2 This file is part of GNUnet.
3 (C) 2001--2013 Christian Grothoff (and other contributing authors)
5 GNUnet is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 3, or (at your
8 option) any later version.
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with GNUnet; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
23 * @brief disk IO convenience methods
24 * @author Christian Grothoff
29 #include "gnunet_directories.h"
30 #include "gnunet_util_lib.h"
33 #define LOG(kind,...) GNUNET_log_from (kind, "util", __VA_ARGS__)
35 #define LOG_STRERROR(kind,syscall) GNUNET_log_from_strerror (kind, "util", syscall)
37 #define LOG_STRERROR_FILE(kind,syscall,filename) GNUNET_log_from_strerror_file (kind, "util", syscall, filename)
40 * Block size for IO for copying files.
42 #define COPY_BLK_SIZE 65536
44 #include <sys/types.h>
49 #include <sys/param.h>
52 #include <sys/mount.h>
54 #if HAVE_SYS_STATVFS_H
55 #include <sys/statvfs.h>
59 #define _IFMT 0170000 /* type of file */
60 #define _IFLNK 0120000 /* symbolic link */
61 #define S_ISLNK(m) (((m)&_IFMT) == _IFLNK)
66 * Handle used to manage a pipe.
68 struct GNUNET_DISK_PipeHandle
71 * File descriptors for the pipe.
72 * One or both of them could be NULL.
74 struct GNUNET_DISK_FileHandle *fd[2];
79 * Closure for the recursion to determine the file size
82 struct GetFileSizeData
85 * Set to the total file size.
90 * GNUNET_YES if symbolic links should be included.
92 int include_sym_links;
95 * GNUNET_YES if mode is file-only (return total == -1 for directories).
103 * Translate GNUnet-internal permission bitmap to UNIX file
104 * access permission bitmap.
106 * @param perm file permissions, GNUnet style
107 * @return file permissions, UNIX style
110 translate_unix_perms (enum GNUNET_DISK_AccessPermissions perm)
115 if (perm & GNUNET_DISK_PERM_USER_READ)
117 if (perm & GNUNET_DISK_PERM_USER_WRITE)
119 if (perm & GNUNET_DISK_PERM_USER_EXEC)
121 if (perm & GNUNET_DISK_PERM_GROUP_READ)
123 if (perm & GNUNET_DISK_PERM_GROUP_WRITE)
125 if (perm & GNUNET_DISK_PERM_GROUP_EXEC)
127 if (perm & GNUNET_DISK_PERM_OTHER_READ)
129 if (perm & GNUNET_DISK_PERM_OTHER_WRITE)
131 if (perm & GNUNET_DISK_PERM_OTHER_EXEC)
140 * Iterate over all files in the given directory and
141 * accumulate their size.
143 * @param cls closure of type "struct GetFileSizeData"
144 * @param fn current filename we are looking at
145 * @return GNUNET_SYSERR on serious errors, otherwise GNUNET_OK
148 getSizeRec (void *cls, const char *fn)
150 struct GetFileSizeData *gfsd = cls;
152 #if defined (HAVE_STAT64) && !(defined (_FILE_OFFSET_BITS) && _FILE_OFFSET_BITS == 64)
155 if (0 != STAT64 (fn, &buf))
157 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_DEBUG, "stat64", fn);
158 return GNUNET_SYSERR;
163 if (0 != STAT (fn, &buf))
165 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_DEBUG, "stat", fn);
166 return GNUNET_SYSERR;
169 if ((S_ISDIR (buf.st_mode)) && (gfsd->single_file_mode == GNUNET_YES))
172 return GNUNET_SYSERR;
174 if ((!S_ISLNK (buf.st_mode)) || (gfsd->include_sym_links == GNUNET_YES))
175 gfsd->total += buf.st_size;
176 if ((S_ISDIR (buf.st_mode)) && (0 == ACCESS (fn, X_OK)) &&
177 ((!S_ISLNK (buf.st_mode)) || (gfsd->include_sym_links == GNUNET_YES)))
179 if (GNUNET_SYSERR == GNUNET_DISK_directory_scan (fn, &getSizeRec, gfsd))
180 return GNUNET_SYSERR;
187 * Checks whether a handle is invalid
189 * @param h handle to check
190 * @return GNUNET_YES if invalid, GNUNET_NO if valid
193 GNUNET_DISK_handle_invalid (const struct GNUNET_DISK_FileHandle *h)
196 return ((!h) || (h->h == INVALID_HANDLE_VALUE)) ? GNUNET_YES : GNUNET_NO;
198 return ((!h) || (h->fd == -1)) ? GNUNET_YES : GNUNET_NO;
203 * Get the size of an open file.
205 * @param fh open file handle
206 * @param size where to write size of the file
207 * @return GNUNET_OK on success, GNUNET_SYSERR on error
210 GNUNET_DISK_file_handle_size (struct GNUNET_DISK_FileHandle *fh,
216 b = GetFileSizeEx (fh->h, &li);
219 SetErrnoFromWinError (GetLastError ());
220 return GNUNET_SYSERR;
222 *size = (off_t) li.QuadPart;
226 if (0 != FSTAT (fh->fd, &sbuf))
227 return GNUNET_SYSERR;
228 *size = sbuf.st_size;
235 * Move the read/write pointer in a file
237 * @param h handle of an open file
238 * @param offset position to move to
239 * @param whence specification to which position the offset parameter relates to
240 * @return the new position on success, GNUNET_SYSERR otherwise
243 GNUNET_DISK_file_seek (const struct GNUNET_DISK_FileHandle * h, off_t offset,
244 enum GNUNET_DISK_Seek whence)
249 return GNUNET_SYSERR;
254 LARGE_INTEGER new_pos;
257 static DWORD t[] = { FILE_BEGIN, FILE_CURRENT, FILE_END };
258 li.QuadPart = offset;
260 b = SetFilePointerEx (h->h, li, &new_pos, t[whence]);
263 SetErrnoFromWinError (GetLastError ());
264 return GNUNET_SYSERR;
266 return (off_t) new_pos.QuadPart;
268 static int t[] = { SEEK_SET, SEEK_CUR, SEEK_END };
270 return lseek (h->fd, offset, t[whence]);
276 * Get the size of the file (or directory) of the given file (in
279 * @param filename name of the file or directory
280 * @param size set to the size of the file (or,
281 * in the case of directories, the sum
282 * of all sizes of files in the directory)
283 * @param include_symbolic_links should symbolic links be
285 * @param single_file_mode GNUNET_YES to only get size of one file
286 * and return GNUNET_SYSERR for directories.
287 * @return GNUNET_SYSERR on error, GNUNET_OK on success
290 GNUNET_DISK_file_size (const char *filename, uint64_t * size,
291 int include_symbolic_links, int single_file_mode)
293 struct GetFileSizeData gfsd;
296 GNUNET_assert (size != NULL);
298 gfsd.include_sym_links = include_symbolic_links;
299 gfsd.single_file_mode = single_file_mode;
300 ret = getSizeRec (&gfsd, filename);
307 * Obtain some unique identifiers for the given file
308 * that can be used to identify it in the local system.
309 * This function is used between GNUnet processes to
310 * quickly check if two files with the same absolute path
311 * are actually identical. The two processes represent
312 * the same peer but may communicate over the network
313 * (and the file may be on an NFS volume). This function
314 * may not be supported on all operating systems.
316 * @param filename name of the file
317 * @param dev set to the device ID
318 * @param ino set to the inode ID
319 * @return #GNUNET_OK on success
322 GNUNET_DISK_file_get_identifiers (const char *filename, uint64_t * dev,
327 // FIXME NILS: test this
328 struct GNUNET_DISK_FileHandle *fh;
329 BY_HANDLE_FILE_INFORMATION info;
332 fh = GNUNET_DISK_file_open (filename, GNUNET_DISK_OPEN_READ, 0);
334 return GNUNET_SYSERR;
335 succ = GetFileInformationByHandle (fh->h, &info);
336 GNUNET_DISK_file_close (fh);
339 return GNUNET_SYSERR;
341 *dev = info.dwVolumeSerialNumber;
342 *ino = ((((uint64_t) info.nFileIndexHigh) << (sizeof (DWORD) * 8)) | info.nFileIndexLow);
349 if (0 != stat (filename, &sbuf))
351 return GNUNET_SYSERR;
353 *ino = (uint64_t) sbuf.st_ino;
362 if (0 != statvfs (filename, &fbuf))
364 return GNUNET_SYSERR;
366 *dev = (uint64_t) fbuf.f_fsid;
372 if (0 != statfs (filename, &fbuf))
374 return GNUNET_SYSERR;
376 *dev = ((uint64_t) fbuf.f_fsid.val[0]) << 32 ||
377 ((uint64_t) fbuf.f_fsid.val[1]);
382 #endif /* !WINDOWS */
388 * Create the name for a temporary file or directory from a template.
390 * @param t template (without XXXXX or "/tmp/")
391 * @return name ready for passing to 'mktemp' or 'mkdtemp', NULL on error
394 mktemp_name (const char *t)
400 if ((t[0] != '/') && (t[0] != '\\')
402 && !(isalpha ((int) t[0]) && (t[0] != '\0') && (t[1] == ':'))
406 /* FIXME: This uses system codepage on W32, not UTF-8 */
407 tmpdir = getenv ("TMPDIR");
409 tmpdir = getenv ("TMP");
411 tmpdir = getenv ("TEMP");
414 GNUNET_asprintf (&tmpl, "%s/%s%s", tmpdir, t, "XXXXXX");
418 GNUNET_asprintf (&tmpl, "%s%s", t, "XXXXXX");
421 fn = (char *) GNUNET_malloc (MAX_PATH + 1);
422 if (ERROR_SUCCESS != plibc_conv_to_win_path (tmpl, fn))
445 tfn = GNUNET_strdup (fn);
446 random_fn = _mktemp (tfn);
447 if (NULL == random_fn)
452 /* FIXME: assume fn to be UTF-8-encoded and do the right thing */
453 if (0 == CreateDirectoryA (tfn, NULL))
455 DWORD error = GetLastError ();
457 if (ERROR_ALREADY_EXISTS == error)
469 * Create an (empty) temporary directory on disk. If the given name is not
470 * an absolute path, the current 'TMPDIR' will be prepended. In any case,
471 * 6 random characters will be appended to the name to create a unique
474 * @param t component to use for the name;
475 * does NOT contain "XXXXXX" or "/tmp/".
476 * @return NULL on error, otherwise name of fresh
477 * file on disk in directory for temporary files
480 GNUNET_DISK_mkdtemp (const char *t)
484 fn = mktemp_name (t);
485 if (fn != mkdtemp (fn))
487 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_ERROR, "mkstemp", fn);
496 * Move a file out of the way (create a backup) by
497 * renaming it to "orig.NUM~" where NUM is the smallest
498 * number that is not used yet.
500 * @param fil name of the file to back up
503 GNUNET_DISK_file_backup (const char *fil)
509 slen = strlen (fil) + 20;
510 target = GNUNET_malloc (slen);
514 GNUNET_snprintf (target, slen,
518 } while (0 == access (target, F_OK));
519 if (0 != rename (fil, target))
520 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR,
523 GNUNET_free (target);
528 * Create an (empty) temporary file on disk. If the given name is not
529 * an absolute path, the current 'TMPDIR' will be prepended. In any case,
530 * 6 random characters will be appended to the name to create a unique
533 * @param t component to use for the name;
534 * does NOT contain "XXXXXX" or "/tmp/".
535 * @return NULL on error, otherwise name of fresh
536 * file on disk in directory for temporary files
539 GNUNET_DISK_mktemp (const char *t)
544 fn = mktemp_name (t);
545 if (-1 == (fd = mkstemp (fn)))
547 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_ERROR, "mkstemp", fn);
552 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "close", fn);
558 * Test if @a fil is a directory and listable. Optionally, also check if the
559 * directory is readable. Will not print an error message if the directory does
560 * not exist. Will log errors if #GNUNET_SYSERR is returned (i.e., a file exists
561 * with the same name).
563 * @param fil filename to test
564 * @param is_readable GNUNET_YES to additionally check if @a fil is readable;
565 * #GNUNET_NO to disable this check
566 * @return #GNUNET_YES if yes, #GNUNET_NO if not; #GNUNET_SYSERR if it
567 * does not exist or stat'ed
570 GNUNET_DISK_directory_test (const char *fil, int is_readable)
572 struct stat filestat;
575 ret = STAT (fil, &filestat);
579 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "stat", fil);
580 return GNUNET_SYSERR;
582 if (!S_ISDIR (filestat.st_mode))
584 LOG (GNUNET_ERROR_TYPE_DEBUG,
585 "A file already exits with the same name %s\n", fil);
588 if (GNUNET_YES == is_readable)
589 ret = ACCESS (fil, R_OK | X_OK);
591 ret = ACCESS (fil, X_OK);
594 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "access", fil);
602 * Check that fil corresponds to a filename
603 * (of a file that exists and that is not a directory).
605 * @param fil filename to check
606 * @return #GNUNET_YES if yes, GNUNET_NO if not a file, #GNUNET_SYSERR if something
607 * else (will print an error message in that case, too).
610 GNUNET_DISK_file_test (const char *fil)
612 struct stat filestat;
616 rdir = GNUNET_STRINGS_filename_expand (fil);
618 return GNUNET_SYSERR;
620 ret = STAT (rdir, &filestat);
625 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "stat", rdir);
627 return GNUNET_SYSERR;
632 if (!S_ISREG (filestat.st_mode))
637 if (ACCESS (rdir, F_OK) < 0)
639 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "access", rdir);
641 return GNUNET_SYSERR;
649 * Implementation of "mkdir -p"
651 * @param dir the directory to create
652 * @return #GNUNET_OK on success, #GNUNET_SYSERR on failure
655 GNUNET_DISK_directory_create (const char *dir)
663 rdir = GNUNET_STRINGS_filename_expand (dir);
665 return GNUNET_SYSERR;
669 pos = 1; /* skip heading '/' */
671 /* Local or Network path? */
672 if (strncmp (rdir, "\\\\", 2) == 0)
677 if (rdir[pos] == '\\')
687 pos = 3; /* strlen("C:\\") */
690 /* Check which low level directories already exist */
692 rdir[len] = DIR_SEPARATOR;
695 if (DIR_SEPARATOR == rdir[pos2])
698 ret = GNUNET_DISK_directory_test (rdir, GNUNET_NO);
699 if (GNUNET_NO == ret)
702 return GNUNET_SYSERR;
704 rdir[pos2] = DIR_SEPARATOR;
705 if (GNUNET_YES == ret)
716 /* Start creating directories */
719 if ((rdir[pos] == DIR_SEPARATOR) || (pos == len))
722 ret = GNUNET_DISK_directory_test (rdir, GNUNET_NO);
723 if (GNUNET_NO == ret)
726 return GNUNET_SYSERR;
728 if (GNUNET_SYSERR == ret)
731 ret = mkdir (rdir, S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH); /* 755 */
733 wchar_t wrdir[MAX_PATH + 1];
734 if (ERROR_SUCCESS == plibc_conv_to_win_pathwconv(rdir, wrdir))
735 ret = !CreateDirectoryW (wrdir, NULL);
739 if ((ret != 0) && (errno != EEXIST))
741 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_ERROR, "mkdir", rdir);
743 return GNUNET_SYSERR;
746 rdir[pos] = DIR_SEPARATOR;
756 * Create the directory structure for storing
759 * @param filename name of a file in the directory
760 * @returns #GNUNET_OK on success,
761 * #GNUNET_SYSERR on failure,
762 * #GNUNET_NO if the directory
763 * exists but is not writeable for us
766 GNUNET_DISK_directory_create_for_file (const char *filename)
772 rdir = GNUNET_STRINGS_filename_expand (filename);
774 return GNUNET_SYSERR;
776 while ((len > 0) && (rdir[len] != DIR_SEPARATOR))
779 ret = GNUNET_DISK_directory_create (rdir);
780 if ((ret == GNUNET_OK) && (0 != ACCESS (rdir, W_OK)))
788 * Read the contents of a binary file into a buffer.
790 * @param h handle to an open file
791 * @param result the buffer to write the result to
792 * @param len the maximum number of bytes to read
793 * @return the number of bytes read on success, #GNUNET_SYSERR on failure
796 GNUNET_DISK_file_read (const struct GNUNET_DISK_FileHandle *h,
803 return GNUNET_SYSERR;
809 if (h->type != GNUNET_DISK_HANLDE_TYPE_PIPE)
811 if (!ReadFile (h->h, result, len, &bytes_read, NULL))
813 SetErrnoFromWinError (GetLastError ());
814 return GNUNET_SYSERR;
819 if (!ReadFile (h->h, result, len, &bytes_read, h->oOverlapRead))
821 if (GetLastError () != ERROR_IO_PENDING)
823 LOG (GNUNET_ERROR_TYPE_DEBUG, "Error reading from pipe: %u\n", GetLastError ());
824 SetErrnoFromWinError (GetLastError ());
825 return GNUNET_SYSERR;
827 LOG (GNUNET_ERROR_TYPE_DEBUG, "Will get overlapped result\n");
828 GetOverlappedResult (h->h, h->oOverlapRead, &bytes_read, TRUE);
830 LOG (GNUNET_ERROR_TYPE_DEBUG, "Read %u bytes from pipe\n", bytes_read);
834 return read (h->fd, result, len);
840 * Read the contents of a binary file into a buffer.
841 * Guarantees not to block (returns GNUNET_SYSERR and sets errno to EAGAIN
842 * when no data can be read).
844 * @param h handle to an open file
845 * @param result the buffer to write the result to
846 * @param len the maximum number of bytes to read
847 * @return the number of bytes read on success, #GNUNET_SYSERR on failure
850 GNUNET_DISK_file_read_non_blocking (const struct GNUNET_DISK_FileHandle *h,
857 return GNUNET_SYSERR;
863 if (h->type != GNUNET_DISK_HANLDE_TYPE_PIPE)
865 if (!ReadFile (h->h, result, len, &bytes_read, NULL))
867 SetErrnoFromWinError (GetLastError ());
868 return GNUNET_SYSERR;
873 if (!ReadFile (h->h, result, len, &bytes_read, h->oOverlapRead))
875 if (GetLastError () != ERROR_IO_PENDING)
877 LOG (GNUNET_ERROR_TYPE_DEBUG, "Error reading from pipe: %u\n", GetLastError ());
878 SetErrnoFromWinError (GetLastError ());
879 return GNUNET_SYSERR;
883 LOG (GNUNET_ERROR_TYPE_DEBUG,
884 "ReadFile() queued a read, cancelling\n");
887 return GNUNET_SYSERR;
890 LOG (GNUNET_ERROR_TYPE_DEBUG, "Read %u bytes\n", bytes_read);
897 /* set to non-blocking, read, then set back */
898 flags = fcntl (h->fd, F_GETFL);
899 if (0 == (flags & O_NONBLOCK))
900 (void) fcntl (h->fd, F_SETFL, flags | O_NONBLOCK);
901 ret = read (h->fd, result, len);
902 if (0 == (flags & O_NONBLOCK))
905 (void) fcntl (h->fd, F_SETFL, flags);
914 * Read the contents of a binary file into a buffer.
916 * @param fn file name
917 * @param result the buffer to write the result to
918 * @param len the maximum number of bytes to read
919 * @return number of bytes read, #GNUNET_SYSERR on failure
922 GNUNET_DISK_fn_read (const char *fn,
926 struct GNUNET_DISK_FileHandle *fh;
929 fh = GNUNET_DISK_file_open (fn, GNUNET_DISK_OPEN_READ, GNUNET_DISK_PERM_NONE);
931 return GNUNET_SYSERR;
932 ret = GNUNET_DISK_file_read (fh, result, len);
933 GNUNET_assert (GNUNET_OK == GNUNET_DISK_file_close (fh));
940 * Write a buffer to a file.
942 * @param h handle to open file
943 * @param buffer the data to write
944 * @param n number of bytes to write
945 * @return number of bytes written on success, #GNUNET_SYSERR on error
948 GNUNET_DISK_file_write (const struct GNUNET_DISK_FileHandle * h,
949 const void *buffer, size_t n)
954 return GNUNET_SYSERR;
960 if (h->type != GNUNET_DISK_HANLDE_TYPE_PIPE)
962 if (!WriteFile (h->h, buffer, n, &bytes_written, NULL))
964 SetErrnoFromWinError (GetLastError ());
965 return GNUNET_SYSERR;
970 LOG (GNUNET_ERROR_TYPE_DEBUG, "It is a pipe trying to write %u bytes\n", n);
971 if (!WriteFile (h->h, buffer, n, &bytes_written, h->oOverlapWrite))
973 if (GetLastError () != ERROR_IO_PENDING)
975 SetErrnoFromWinError (GetLastError ());
976 LOG (GNUNET_ERROR_TYPE_DEBUG, "Error writing to pipe: %u\n",
978 return GNUNET_SYSERR;
980 LOG (GNUNET_ERROR_TYPE_DEBUG, "Will get overlapped result\n");
981 if (!GetOverlappedResult (h->h, h->oOverlapWrite, &bytes_written, TRUE))
983 SetErrnoFromWinError (GetLastError ());
984 LOG (GNUNET_ERROR_TYPE_DEBUG,
985 "Error getting overlapped result while writing to pipe: %u\n",
987 return GNUNET_SYSERR;
993 if (!GetOverlappedResult (h->h, h->oOverlapWrite, &ovr, TRUE))
995 LOG (GNUNET_ERROR_TYPE_DEBUG,
996 "Error getting control overlapped result while writing to pipe: %u\n",
1001 LOG (GNUNET_ERROR_TYPE_DEBUG,
1002 "Wrote %u bytes (ovr says %u), picking the greatest\n",
1003 bytes_written, ovr);
1006 if (bytes_written == 0)
1010 LOG (GNUNET_ERROR_TYPE_DEBUG, "Wrote %u bytes, returning -1 with EAGAIN\n", bytes_written);
1012 return GNUNET_SYSERR;
1015 LOG (GNUNET_ERROR_TYPE_DEBUG, "Wrote %u bytes\n", bytes_written);
1017 return bytes_written;
1019 return write (h->fd, buffer, n);
1025 * Write a buffer to a file, blocking, if necessary.
1027 * @param h handle to open file
1028 * @param buffer the data to write
1029 * @param n number of bytes to write
1030 * @return number of bytes written on success, #GNUNET_SYSERR on error
1033 GNUNET_DISK_file_write_blocking (const struct GNUNET_DISK_FileHandle * h,
1040 return GNUNET_SYSERR;
1044 DWORD bytes_written;
1045 /* We do a non-overlapped write, which is as blocking as it gets */
1046 LOG (GNUNET_ERROR_TYPE_DEBUG, "Writing %u bytes\n", n);
1047 if (!WriteFile (h->h, buffer, n, &bytes_written, NULL))
1049 SetErrnoFromWinError (GetLastError ());
1050 LOG (GNUNET_ERROR_TYPE_DEBUG, "Error writing to pipe: %u\n",
1052 return GNUNET_SYSERR;
1054 if (bytes_written == 0 && n > 0)
1056 LOG (GNUNET_ERROR_TYPE_DEBUG, "Waiting for pipe to clean\n");
1057 WaitForSingleObject (h->h, INFINITE);
1058 if (!WriteFile (h->h, buffer, n, &bytes_written, NULL))
1060 SetErrnoFromWinError (GetLastError ());
1061 LOG (GNUNET_ERROR_TYPE_DEBUG, "Error writing to pipe: %u\n",
1063 return GNUNET_SYSERR;
1066 LOG (GNUNET_ERROR_TYPE_DEBUG,
1069 return bytes_written;
1074 /* set to blocking, write, then set back */
1075 flags = fcntl (h->fd, F_GETFL);
1076 if (0 != (flags & O_NONBLOCK))
1077 (void) fcntl (h->fd, F_SETFL, flags - O_NONBLOCK);
1078 ret = write (h->fd, buffer, n);
1079 if (0 == (flags & O_NONBLOCK))
1080 (void) fcntl (h->fd, F_SETFL, flags);
1087 * Write a buffer to a file. If the file is longer than the
1088 * number of bytes that will be written, it will be truncated.
1090 * @param fn file name
1091 * @param buffer the data to write
1092 * @param n number of bytes to write
1093 * @param mode file permissions
1094 * @return number of bytes written on success, #GNUNET_SYSERR on error
1097 GNUNET_DISK_fn_write (const char *fn, const void *buffer, size_t n,
1098 enum GNUNET_DISK_AccessPermissions mode)
1100 struct GNUNET_DISK_FileHandle *fh;
1103 fh = GNUNET_DISK_file_open (fn,
1104 GNUNET_DISK_OPEN_WRITE | GNUNET_DISK_OPEN_TRUNCATE
1105 | GNUNET_DISK_OPEN_CREATE, mode);
1107 return GNUNET_SYSERR;
1108 ret = GNUNET_DISK_file_write (fh, buffer, n);
1109 GNUNET_assert (GNUNET_OK == GNUNET_DISK_file_close (fh));
1115 * Scan a directory for files.
1117 * @param dir_name the name of the directory
1118 * @param callback the method to call for each file,
1119 * can be NULL, in that case, we only count
1120 * @param callback_cls closure for @a callback
1121 * @return the number of files found, #GNUNET_SYSERR on error or
1122 * ieration aborted by callback returning #GNUNET_SYSERR
1125 GNUNET_DISK_directory_scan (const char *dir_name,
1126 GNUNET_FileNameCallback callback,
1130 struct dirent *finfo;
1136 unsigned int name_len;
1137 unsigned int n_size;
1139 GNUNET_assert (dir_name != NULL);
1140 dname = GNUNET_STRINGS_filename_expand (dir_name);
1142 return GNUNET_SYSERR;
1143 while ((strlen (dname) > 0) && (dname[strlen (dname) - 1] == DIR_SEPARATOR))
1144 dname[strlen (dname) - 1] = '\0';
1145 if (0 != STAT (dname, &istat))
1147 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "stat", dname);
1148 GNUNET_free (dname);
1149 return GNUNET_SYSERR;
1151 if (!S_ISDIR (istat.st_mode))
1153 LOG (GNUNET_ERROR_TYPE_WARNING,
1154 _("Expected `%s' to be a directory!\n"),
1156 GNUNET_free (dname);
1157 return GNUNET_SYSERR;
1160 dinfo = OPENDIR (dname);
1161 if ((errno == EACCES) || (dinfo == NULL))
1163 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "opendir", dname);
1166 GNUNET_free (dname);
1167 return GNUNET_SYSERR;
1170 n_size = strlen (dname) + name_len + 2;
1171 name = GNUNET_malloc (n_size);
1172 while ((finfo = READDIR (dinfo)) != NULL)
1174 if ((0 == strcmp (finfo->d_name, ".")) ||
1175 (0 == strcmp (finfo->d_name, "..")))
1177 if (callback != NULL)
1179 if (name_len < strlen (finfo->d_name))
1182 name_len = strlen (finfo->d_name);
1183 n_size = strlen (dname) + name_len + 2;
1184 name = GNUNET_malloc (n_size);
1186 /* dname can end in "/" only if dname == "/";
1187 * if dname does not end in "/", we need to add
1188 * a "/" (otherwise, we must not!) */
1189 GNUNET_snprintf (name, n_size, "%s%s%s", dname,
1190 (strcmp (dname, DIR_SEPARATOR_STR) ==
1191 0) ? "" : DIR_SEPARATOR_STR, finfo->d_name);
1192 ret = callback (callback_cls, name);
1193 if (GNUNET_OK != ret)
1197 GNUNET_free (dname);
1198 if (GNUNET_NO == ret)
1200 return GNUNET_SYSERR;
1207 GNUNET_free (dname);
1213 * Opaque handle used for iterating over a directory.
1215 struct GNUNET_DISK_DirectoryIterator
1219 * Function to call on directory entries.
1221 GNUNET_DISK_DirectoryIteratorCallback callback;
1224 * Closure for callback.
1229 * Reference to directory.
1239 * Next filename to process.
1246 enum GNUNET_SCHEDULER_Priority priority;
1252 * Task used by the directory iterator.
1255 directory_iterator_task (void *cls,
1256 const struct GNUNET_SCHEDULER_TaskContext *tc)
1258 struct GNUNET_DISK_DirectoryIterator *iter = cls;
1261 name = iter->next_name;
1262 GNUNET_assert (name != NULL);
1263 iter->next_name = NULL;
1264 iter->callback (iter->callback_cls, iter, name, iter->dirname);
1270 * This function must be called during the DiskIteratorCallback
1271 * (exactly once) to schedule the task to process the next
1272 * filename in the directory (if there is one).
1274 * @param iter opaque handle for the iterator
1275 * @param can set to GNUNET_YES to terminate the iteration early
1276 * @return GNUNET_YES if iteration will continue,
1277 * GNUNET_NO if this was the last entry (and iteration is complete),
1278 * GNUNET_SYSERR if abort was YES
1281 GNUNET_DISK_directory_iterator_next (struct GNUNET_DISK_DirectoryIterator *iter,
1284 struct dirent *finfo;
1286 GNUNET_assert (iter->next_name == NULL);
1287 if (can == GNUNET_YES)
1289 CLOSEDIR (iter->directory);
1290 GNUNET_free (iter->dirname);
1292 return GNUNET_SYSERR;
1294 while (NULL != (finfo = READDIR (iter->directory)))
1296 if ((0 == strcmp (finfo->d_name, ".")) ||
1297 (0 == strcmp (finfo->d_name, "..")))
1299 GNUNET_asprintf (&iter->next_name, "%s%s%s", iter->dirname,
1300 DIR_SEPARATOR_STR, finfo->d_name);
1305 GNUNET_DISK_directory_iterator_next (iter, GNUNET_YES);
1308 GNUNET_SCHEDULER_add_with_priority (iter->priority, &directory_iterator_task,
1315 * Scan a directory for files using the scheduler to run a task for
1316 * each entry. The name of the directory must be expanded first (!).
1317 * If a scheduler does not need to be used, GNUNET_DISK_directory_scan
1318 * may provide a simpler API.
1320 * @param prio priority to use
1321 * @param dir_name the name of the directory
1322 * @param callback the method to call for each file
1323 * @param callback_cls closure for callback
1324 * @return GNUNET_YES if directory is not empty and 'callback'
1325 * will be called later, GNUNET_NO otherwise, GNUNET_SYSERR on error.
1328 GNUNET_DISK_directory_iterator_start (enum GNUNET_SCHEDULER_Priority prio,
1329 const char *dir_name,
1330 GNUNET_DISK_DirectoryIteratorCallback
1331 callback, void *callback_cls)
1333 struct GNUNET_DISK_DirectoryIterator *di;
1335 di = GNUNET_new (struct GNUNET_DISK_DirectoryIterator);
1336 di->callback = callback;
1337 di->callback_cls = callback_cls;
1338 di->directory = OPENDIR (dir_name);
1339 if (di->directory == NULL)
1342 callback (callback_cls, NULL, NULL, NULL);
1343 return GNUNET_SYSERR;
1345 di->dirname = GNUNET_strdup (dir_name);
1346 di->priority = prio;
1347 return GNUNET_DISK_directory_iterator_next (di, GNUNET_NO);
1352 * Function that removes the given directory by calling
1353 * "GNUNET_DISK_directory_remove".
1355 * @param unused not used
1356 * @param fn directory to remove
1360 remove_helper (void *unused, const char *fn)
1362 (void) GNUNET_DISK_directory_remove (fn);
1368 * Remove all files in a directory (rm -rf). Call with
1372 * @param filename the file to remove
1373 * @return GNUNET_OK on success, GNUNET_SYSERR on error
1376 GNUNET_DISK_directory_remove (const char *filename)
1380 if (0 != LSTAT (filename, &istat))
1381 return GNUNET_NO; /* file may not exist... */
1382 (void) CHMOD (filename, S_IWUSR | S_IRUSR | S_IXUSR);
1383 if (UNLINK (filename) == 0)
1385 if ((errno != EISDIR) &&
1386 /* EISDIR is not sufficient in all cases, e.g.
1387 * sticky /tmp directory may result in EPERM on BSD.
1388 * So we also explicitly check "isDirectory" */
1389 (GNUNET_YES != GNUNET_DISK_directory_test (filename, GNUNET_YES)))
1391 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "rmdir", filename);
1392 return GNUNET_SYSERR;
1394 if (GNUNET_SYSERR ==
1395 GNUNET_DISK_directory_scan (filename, &remove_helper, NULL))
1396 return GNUNET_SYSERR;
1397 if (0 != RMDIR (filename))
1399 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "rmdir", filename);
1400 return GNUNET_SYSERR;
1409 * @param src file to copy
1410 * @param dst destination file name
1411 * @return #GNUNET_OK on success, #GNUNET_SYSERR on error
1414 GNUNET_DISK_file_copy (const char *src,
1421 struct GNUNET_DISK_FileHandle *in;
1422 struct GNUNET_DISK_FileHandle *out;
1424 if (GNUNET_OK != GNUNET_DISK_file_size (src, &size, GNUNET_YES, GNUNET_YES))
1425 return GNUNET_SYSERR;
1427 in = GNUNET_DISK_file_open (src, GNUNET_DISK_OPEN_READ,
1428 GNUNET_DISK_PERM_NONE);
1430 return GNUNET_SYSERR;
1432 GNUNET_DISK_file_open (dst,
1433 GNUNET_DISK_OPEN_WRITE | GNUNET_DISK_OPEN_CREATE |
1434 GNUNET_DISK_OPEN_FAILIFEXISTS,
1435 GNUNET_DISK_PERM_USER_READ |
1436 GNUNET_DISK_PERM_USER_WRITE |
1437 GNUNET_DISK_PERM_GROUP_READ |
1438 GNUNET_DISK_PERM_GROUP_WRITE);
1441 GNUNET_DISK_file_close (in);
1442 return GNUNET_SYSERR;
1444 buf = GNUNET_malloc (COPY_BLK_SIZE);
1447 len = COPY_BLK_SIZE;
1448 if (len > size - pos)
1450 if (len != GNUNET_DISK_file_read (in, buf, len))
1452 if (len != GNUNET_DISK_file_write (out, buf, len))
1457 GNUNET_DISK_file_close (in);
1458 GNUNET_DISK_file_close (out);
1462 GNUNET_DISK_file_close (in);
1463 GNUNET_DISK_file_close (out);
1464 return GNUNET_SYSERR;
1469 * @brief Removes special characters as ':' from a filename.
1470 * @param fn the filename to canonicalize
1473 GNUNET_DISK_filename_canonicalize (char *fn)
1483 if (c == '/' || c == '\\' || c == ':' || c == '*' || c == '?' || c == '"' ||
1484 c == '<' || c == '>' || c == '|')
1496 * @brief Change owner of a file
1498 * @param filename name of file to change the owner of
1499 * @param user name of the new owner
1500 * @return GNUNET_OK on success, GNUNET_SYSERR on failure
1503 GNUNET_DISK_file_change_owner (const char *filename, const char *user)
1508 pws = getpwnam (user);
1511 LOG (GNUNET_ERROR_TYPE_ERROR,
1512 _("Cannot obtain information about user `%s': %s\n"), user,
1514 return GNUNET_SYSERR;
1516 if (0 != chown (filename, pws->pw_uid, pws->pw_gid))
1517 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "chown", filename);
1524 * Lock a part of a file
1525 * @param fh file handle
1526 * @param lock_start absolute position from where to lock
1527 * @param lock_end absolute position until where to lock
1528 * @param excl GNUNET_YES for an exclusive lock
1529 * @return GNUNET_OK on success, GNUNET_SYSERR on error
1532 GNUNET_DISK_file_lock (struct GNUNET_DISK_FileHandle *fh, off_t lock_start,
1533 off_t lock_end, int excl)
1538 return GNUNET_SYSERR;
1544 memset (&fl, 0, sizeof (struct flock));
1545 fl.l_type = excl ? F_WRLCK : F_RDLCK;
1546 fl.l_whence = SEEK_SET;
1547 fl.l_start = lock_start;
1548 fl.l_len = lock_end;
1550 return fcntl (fh->fd, F_SETLK, &fl) != 0 ? GNUNET_SYSERR : GNUNET_OK;
1553 off_t diff = lock_end - lock_start;
1554 DWORD diff_low, diff_high;
1555 diff_low = (DWORD) (diff & 0xFFFFFFFF);
1556 diff_high = (DWORD) ((diff >> (sizeof (DWORD) * 8)) & 0xFFFFFFFF);
1558 memset (&o, 0, sizeof (OVERLAPPED));
1559 o.Offset = (DWORD) (lock_start & 0xFFFFFFFF);;
1560 o.OffsetHigh = (DWORD) (((lock_start & ~0xFFFFFFFF) >> (sizeof (DWORD) * 8)) & 0xFFFFFFFF);
1563 (fh->h, (excl ? LOCKFILE_EXCLUSIVE_LOCK : 0) | LOCKFILE_FAIL_IMMEDIATELY,
1564 0, diff_low, diff_high, &o))
1566 SetErrnoFromWinError (GetLastError ());
1567 return GNUNET_SYSERR;
1576 * Unlock a part of a file
1577 * @param fh file handle
1578 * @param unlock_start absolute position from where to unlock
1579 * @param unlock_end absolute position until where to unlock
1580 * @return GNUNET_OK on success, GNUNET_SYSERR on error
1583 GNUNET_DISK_file_unlock (struct GNUNET_DISK_FileHandle *fh, off_t unlock_start,
1589 return GNUNET_SYSERR;
1595 memset (&fl, 0, sizeof (struct flock));
1596 fl.l_type = F_UNLCK;
1597 fl.l_whence = SEEK_SET;
1598 fl.l_start = unlock_start;
1599 fl.l_len = unlock_end;
1601 return fcntl (fh->fd, F_SETLK, &fl) != 0 ? GNUNET_SYSERR : GNUNET_OK;
1604 off_t diff = unlock_end - unlock_start;
1605 DWORD diff_low, diff_high;
1606 diff_low = (DWORD) (diff & 0xFFFFFFFF);
1607 diff_high = (DWORD) ((diff >> (sizeof (DWORD) * 8)) & 0xFFFFFFFF);
1609 memset (&o, 0, sizeof (OVERLAPPED));
1610 o.Offset = (DWORD) (unlock_start & 0xFFFFFFFF);;
1611 o.OffsetHigh = (DWORD) (((unlock_start & ~0xFFFFFFFF) >> (sizeof (DWORD) * 8)) & 0xFFFFFFFF);
1613 if (!UnlockFileEx (fh->h, 0, diff_low, diff_high, &o))
1615 SetErrnoFromWinError (GetLastError ());
1616 return GNUNET_SYSERR;
1625 * Open a file. Note that the access permissions will only be
1626 * used if a new file is created and if the underlying operating
1627 * system supports the given permissions.
1629 * @param fn file name to be opened
1630 * @param flags opening flags, a combination of GNUNET_DISK_OPEN_xxx bit flags
1631 * @param perm permissions for the newly created file, use
1632 * #GNUNET_DISK_PERM_USER_NONE if a file could not be created by this
1633 * call (because of flags)
1634 * @return IO handle on success, NULL on error
1636 struct GNUNET_DISK_FileHandle *
1637 GNUNET_DISK_file_open (const char *fn,
1638 enum GNUNET_DISK_OpenFlags flags,
1639 enum GNUNET_DISK_AccessPermissions perm)
1642 struct GNUNET_DISK_FileHandle *ret;
1648 wchar_t wexpfn[MAX_PATH + 1];
1655 expfn = GNUNET_STRINGS_filename_expand (fn);
1660 if (GNUNET_DISK_OPEN_READWRITE == (flags & GNUNET_DISK_OPEN_READWRITE))
1661 oflags = O_RDWR; /* note: O_RDWR is NOT always O_RDONLY | O_WRONLY */
1662 else if (flags & GNUNET_DISK_OPEN_READ)
1664 else if (flags & GNUNET_DISK_OPEN_WRITE)
1669 GNUNET_free (expfn);
1672 if (flags & GNUNET_DISK_OPEN_FAILIFEXISTS)
1673 oflags |= (O_CREAT | O_EXCL);
1674 if (flags & GNUNET_DISK_OPEN_TRUNCATE)
1676 if (flags & GNUNET_DISK_OPEN_APPEND)
1678 if (flags & GNUNET_DISK_OPEN_CREATE)
1680 (void) GNUNET_DISK_directory_create_for_file (expfn);
1682 mode = translate_unix_perms (perm);
1685 fd = open (expfn, oflags
1689 | O_LARGEFILE, mode);
1692 if (0 == (flags & GNUNET_DISK_OPEN_FAILIFEXISTS))
1693 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "open", expfn);
1695 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_DEBUG, "open", expfn);
1696 GNUNET_free (expfn);
1703 if (GNUNET_DISK_OPEN_READWRITE == (flags & GNUNET_DISK_OPEN_READWRITE))
1704 access = FILE_READ_DATA | FILE_WRITE_DATA;
1705 else if (flags & GNUNET_DISK_OPEN_READ)
1706 access = FILE_READ_DATA;
1707 else if (flags & GNUNET_DISK_OPEN_WRITE)
1708 access = FILE_WRITE_DATA;
1710 if (flags & GNUNET_DISK_OPEN_FAILIFEXISTS)
1714 else if (flags & GNUNET_DISK_OPEN_CREATE)
1716 (void) GNUNET_DISK_directory_create_for_file (expfn);
1717 if (flags & GNUNET_DISK_OPEN_TRUNCATE)
1718 disp = CREATE_ALWAYS;
1722 else if (flags & GNUNET_DISK_OPEN_TRUNCATE)
1724 disp = TRUNCATE_EXISTING;
1728 disp = OPEN_EXISTING;
1731 if (ERROR_SUCCESS == plibc_conv_to_win_pathwconv(expfn, wexpfn))
1732 h = CreateFileW (wexpfn, access,
1733 FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE, NULL,
1734 disp, FILE_ATTRIBUTE_NORMAL, NULL);
1736 h = INVALID_HANDLE_VALUE;
1737 if (h == INVALID_HANDLE_VALUE)
1740 SetErrnoFromWinError (GetLastError ());
1742 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_INFO, "open", expfn);
1743 GNUNET_free (expfn);
1748 if (flags & GNUNET_DISK_OPEN_APPEND)
1749 if (SetFilePointer (h, 0, 0, FILE_END) == INVALID_SET_FILE_POINTER)
1751 SetErrnoFromWinError (GetLastError ());
1752 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "SetFilePointer", expfn);
1754 GNUNET_free (expfn);
1759 ret = GNUNET_new (struct GNUNET_DISK_FileHandle);
1762 ret->type = GNUNET_DISK_HANLDE_TYPE_FILE;
1766 GNUNET_free (expfn);
1772 * Close an open file
1773 * @param h file handle
1774 * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
1777 GNUNET_DISK_file_close (struct GNUNET_DISK_FileHandle *h)
1783 return GNUNET_SYSERR;
1789 if (!CloseHandle (h->h))
1791 SetErrnoFromWinError (GetLastError ());
1792 LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING, "close");
1793 ret = GNUNET_SYSERR;
1795 if (h->oOverlapRead)
1797 if (!CloseHandle (h->oOverlapRead->hEvent))
1799 SetErrnoFromWinError (GetLastError ());
1800 LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING, "close");
1801 ret = GNUNET_SYSERR;
1803 GNUNET_free (h->oOverlapRead);
1805 if (h->oOverlapWrite)
1807 if (!CloseHandle (h->oOverlapWrite->hEvent))
1809 SetErrnoFromWinError (GetLastError ());
1810 LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING, "close");
1811 ret = GNUNET_SYSERR;
1813 GNUNET_free (h->oOverlapWrite);
1816 if (close (h->fd) != 0)
1818 LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING, "close");
1819 ret = GNUNET_SYSERR;
1828 * Get a GNUnet file handle from a W32 handle.
1830 * @param handle native handle
1831 * @return GNUnet file handle corresponding to the W32 handle
1833 struct GNUNET_DISK_FileHandle *
1834 GNUNET_DISK_get_handle_from_w32_handle (HANDLE osfh)
1836 struct GNUNET_DISK_FileHandle *fh;
1839 enum GNUNET_FILE_Type ftype;
1841 dwret = GetFileType (osfh);
1844 case FILE_TYPE_DISK:
1845 ftype = GNUNET_DISK_HANLDE_TYPE_FILE;
1847 case FILE_TYPE_PIPE:
1848 ftype = GNUNET_DISK_HANLDE_TYPE_PIPE;
1854 fh = GNUNET_new (struct GNUNET_DISK_FileHandle);
1858 if (ftype == GNUNET_DISK_HANLDE_TYPE_PIPE)
1861 * Note that we can't make it overlapped if it isn't already.
1862 * (ReOpenFile() is only available in 2003/Vista).
1863 * The process that opened this file in the first place (usually a parent
1864 * process, if this is stdin/stdout/stderr) must make it overlapped,
1865 * otherwise we're screwed, as selecting on non-overlapped handle
1868 fh->oOverlapRead = GNUNET_malloc (sizeof (OVERLAPPED));
1869 fh->oOverlapWrite = GNUNET_malloc (sizeof (OVERLAPPED));
1870 fh->oOverlapRead->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
1871 fh->oOverlapWrite->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
1879 * Get a handle from a native integer FD.
1881 * @param fno native integer file descriptor
1882 * @return file handle corresponding to the descriptor, NULL on error
1884 struct GNUNET_DISK_FileHandle *
1885 GNUNET_DISK_get_handle_from_int_fd (int fno)
1887 struct GNUNET_DISK_FileHandle *fh;
1889 if ( (((off_t) -1) == lseek (fno, 0, SEEK_CUR)) &&
1891 return NULL; /* invalid FD */
1894 fh = GNUNET_new (struct GNUNET_DISK_FileHandle);
1900 osfh = _get_osfhandle (fno);
1901 if (INVALID_HANDLE_VALUE == (HANDLE) osfh)
1904 fh = GNUNET_DISK_get_handle_from_w32_handle ((HANDLE) osfh);
1912 * Get a handle from a native streaming FD.
1914 * @param fd native streaming file descriptor
1915 * @return file handle corresponding to the descriptor
1917 struct GNUNET_DISK_FileHandle *
1918 GNUNET_DISK_get_handle_from_native (FILE *fd)
1926 return GNUNET_DISK_get_handle_from_int_fd (fno);
1931 * Handle for a memory-mapping operation.
1933 struct GNUNET_DISK_MapHandle
1936 * Address where the map is in memory.
1942 * Underlying OS handle.
1947 * Number of bytes mapped.
1955 #define MAP_FAILED ((void *) -1)
1959 * Map a file into memory
1961 * @param h open file handle
1962 * @param m handle to the new mapping
1963 * @param access access specification, GNUNET_DISK_MAP_TYPE_xxx
1964 * @param len size of the mapping
1965 * @return pointer to the mapped memory region, NULL on failure
1968 GNUNET_DISK_file_map (const struct GNUNET_DISK_FileHandle *h,
1969 struct GNUNET_DISK_MapHandle **m,
1970 enum GNUNET_DISK_MapType access, size_t len)
1979 DWORD mapAccess, protect;
1981 if ((access & GNUNET_DISK_MAP_TYPE_READ) &&
1982 (access & GNUNET_DISK_MAP_TYPE_WRITE))
1984 protect = PAGE_READWRITE;
1985 mapAccess = FILE_MAP_ALL_ACCESS;
1987 else if (access & GNUNET_DISK_MAP_TYPE_READ)
1989 protect = PAGE_READONLY;
1990 mapAccess = FILE_MAP_READ;
1992 else if (access & GNUNET_DISK_MAP_TYPE_WRITE)
1994 protect = PAGE_READWRITE;
1995 mapAccess = FILE_MAP_WRITE;
2003 *m = GNUNET_new (struct GNUNET_DISK_MapHandle);
2004 (*m)->h = CreateFileMapping (h->h, NULL, protect, 0, 0, NULL);
2005 if ((*m)->h == INVALID_HANDLE_VALUE)
2007 SetErrnoFromWinError (GetLastError ());
2012 (*m)->addr = MapViewOfFile ((*m)->h, mapAccess, 0, 0, len);
2015 SetErrnoFromWinError (GetLastError ());
2016 CloseHandle ((*m)->h);
2025 if (access & GNUNET_DISK_MAP_TYPE_READ)
2027 if (access & GNUNET_DISK_MAP_TYPE_WRITE)
2029 *m = GNUNET_new (struct GNUNET_DISK_MapHandle);
2030 (*m)->addr = mmap (NULL, len, prot, MAP_SHARED, h->fd, 0);
2031 GNUNET_assert (NULL != (*m)->addr);
2032 if (MAP_FAILED == (*m)->addr)
2044 * @param h mapping handle
2045 * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
2048 GNUNET_DISK_file_unmap (struct GNUNET_DISK_MapHandle *h)
2055 return GNUNET_SYSERR;
2059 ret = UnmapViewOfFile (h->addr) ? GNUNET_OK : GNUNET_SYSERR;
2060 if (ret != GNUNET_OK)
2061 SetErrnoFromWinError (GetLastError ());
2062 if (!CloseHandle (h->h) && (ret == GNUNET_OK))
2064 ret = GNUNET_SYSERR;
2065 SetErrnoFromWinError (GetLastError ());
2068 ret = munmap (h->addr, h->len) != -1 ? GNUNET_OK : GNUNET_SYSERR;
2076 * Write file changes to disk
2077 * @param h handle to an open file
2078 * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
2081 GNUNET_DISK_file_sync (const struct GNUNET_DISK_FileHandle *h)
2086 return GNUNET_SYSERR;
2092 ret = FlushFileBuffers (h->h) ? GNUNET_OK : GNUNET_SYSERR;
2093 if (ret != GNUNET_OK)
2094 SetErrnoFromWinError (GetLastError ());
2096 #elif defined(FREEBSD) || defined(OPENBSD) || defined(DARWIN)
2097 return fsync (h->fd) == -1 ? GNUNET_SYSERR : GNUNET_OK;
2099 return fdatasync (h->fd) == -1 ? GNUNET_SYSERR : GNUNET_OK;
2106 #define PIPE_BUF 512
2108 /* Copyright Bob Byrnes <byrnes <at> curl.com>
2109 http://permalink.gmane.org/gmane.os.cygwin.patches/2121
2111 /* Create a pipe, and return handles to the read and write ends,
2112 just like CreatePipe, but ensure that the write end permits
2113 FILE_READ_ATTRIBUTES access, on later versions of win32 where
2114 this is supported. This access is needed by NtQueryInformationFile,
2115 which is used to implement select and nonblocking writes.
2116 Note that the return value is either NO_ERROR or GetLastError,
2117 unlike CreatePipe, which returns a bool for success or failure. */
2119 create_selectable_pipe (PHANDLE read_pipe_ptr, PHANDLE write_pipe_ptr,
2120 LPSECURITY_ATTRIBUTES sa_ptr, DWORD psize,
2121 DWORD dwReadMode, DWORD dwWriteMode)
2123 /* Default to error. */
2124 *read_pipe_ptr = *write_pipe_ptr = INVALID_HANDLE_VALUE;
2129 /* Ensure that there is enough pipe buffer space for atomic writes. */
2130 if (psize < PIPE_BUF)
2133 char pipename[MAX_PATH];
2135 /* Retry CreateNamedPipe as long as the pipe name is in use.
2136 * Retrying will probably never be necessary, but we want
2137 * to be as robust as possible. */
2140 static volatile LONG pipe_unique_id;
2142 snprintf (pipename, sizeof pipename, "\\\\.\\pipe\\gnunet-%d-%ld",
2143 getpid (), InterlockedIncrement ((LONG *) & pipe_unique_id));
2144 LOG (GNUNET_ERROR_TYPE_DEBUG, "CreateNamedPipe: name = %s, size = %lu\n",
2146 /* Use CreateNamedPipe instead of CreatePipe, because the latter
2147 * returns a write handle that does not permit FILE_READ_ATTRIBUTES
2148 * access, on versions of win32 earlier than WinXP SP2.
2149 * CreatePipe also stupidly creates a full duplex pipe, which is
2150 * a waste, since only a single direction is actually used.
2151 * It's important to only allow a single instance, to ensure that
2152 * the pipe was not created earlier by some other process, even if
2153 * the pid has been reused. */
2154 read_pipe = CreateNamedPipeA (pipename, PIPE_ACCESS_INBOUND | FILE_FLAG_FIRST_PIPE_INSTANCE | dwReadMode, PIPE_TYPE_BYTE | PIPE_READMODE_BYTE, 1, /* max instances */
2155 psize, /* output buffer size */
2156 psize, /* input buffer size */
2157 NMPWAIT_USE_DEFAULT_WAIT, sa_ptr);
2159 if (read_pipe != INVALID_HANDLE_VALUE)
2161 LOG (GNUNET_ERROR_TYPE_DEBUG, "pipe read handle = %p\n", read_pipe);
2165 DWORD err = GetLastError ();
2169 case ERROR_PIPE_BUSY:
2170 /* The pipe is already open with compatible parameters.
2171 * Pick a new name and retry. */
2172 LOG (GNUNET_ERROR_TYPE_DEBUG, "pipe busy, retrying\n");
2174 case ERROR_ACCESS_DENIED:
2175 /* The pipe is already open with incompatible parameters.
2176 * Pick a new name and retry. */
2177 LOG (GNUNET_ERROR_TYPE_DEBUG, "pipe access denied, retrying\n");
2179 case ERROR_CALL_NOT_IMPLEMENTED:
2180 /* We are on an older Win9x platform without named pipes.
2181 * Return an anonymous pipe as the best approximation. */
2182 LOG (GNUNET_ERROR_TYPE_DEBUG,
2183 "CreateNamedPipe not implemented, resorting to "
2184 "CreatePipe: size = %lu\n", psize);
2185 if (CreatePipe (read_pipe_ptr, write_pipe_ptr, sa_ptr, psize))
2187 LOG (GNUNET_ERROR_TYPE_DEBUG, "pipe read handle = %p, write handle = %p\n",
2192 err = GetLastError ();
2193 LOG (GNUNET_ERROR_TYPE_ERROR, "CreatePipe failed: %d\n", err);
2196 LOG (GNUNET_ERROR_TYPE_ERROR, "CreateNamedPipe failed: %d\n", err);
2201 LOG (GNUNET_ERROR_TYPE_DEBUG, "CreateFile: name = %s\n", pipename);
2203 /* Open the named pipe for writing.
2204 * Be sure to permit FILE_READ_ATTRIBUTES access. */
2205 write_pipe = CreateFileA (pipename, GENERIC_WRITE | FILE_READ_ATTRIBUTES, 0, /* share mode */
2206 sa_ptr, OPEN_EXISTING, dwWriteMode, /* flags and attributes */
2207 0); /* handle to template file */
2209 if (write_pipe == INVALID_HANDLE_VALUE)
2212 DWORD err = GetLastError ();
2214 LOG (GNUNET_ERROR_TYPE_DEBUG, "CreateFile failed: %d\n", err);
2215 CloseHandle (read_pipe);
2218 LOG (GNUNET_ERROR_TYPE_DEBUG, "pipe write handle = %p\n", write_pipe);
2220 *read_pipe_ptr = read_pipe;
2221 *write_pipe_ptr = write_pipe;
2228 * Creates an interprocess channel
2230 * @param blocking_read creates an asynchronous pipe for reading if set to GNUNET_NO
2231 * @param blocking_write creates an asynchronous pipe for writing if set to GNUNET_NO
2232 * @param inherit_read inherit the parent processes stdin (only for windows)
2233 * @param inherit_write inherit the parent processes stdout (only for windows)
2234 * @return handle to the new pipe, NULL on error
2236 struct GNUNET_DISK_PipeHandle *
2237 GNUNET_DISK_pipe (int blocking_read, int blocking_write, int inherit_read, int inherit_write)
2248 LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR, "pipe");
2252 return GNUNET_DISK_pipe_from_fd (blocking_read,
2256 struct GNUNET_DISK_PipeHandle *p;
2261 p = GNUNET_new (struct GNUNET_DISK_PipeHandle);
2262 p->fd[0] = GNUNET_new (struct GNUNET_DISK_FileHandle);
2263 p->fd[1] = GNUNET_new (struct GNUNET_DISK_FileHandle);
2265 /* All pipes are overlapped. If you want them to block - just
2266 * call WriteFile() and ReadFile() with NULL overlapped pointer.
2267 * NOTE: calling with NULL overlapped pointer works only
2268 * for pipes, and doesn't seem to be a documented feature.
2269 * It will NOT work for files, because overlapped files need
2270 * to read offsets from the overlapped structure, regardless.
2271 * Pipes are not seekable, and need no offsets, which is
2272 * probably why it works for them.
2275 create_selectable_pipe (&p->fd[0]->h, &p->fd[1]->h, NULL, 0,
2276 FILE_FLAG_OVERLAPPED,
2277 FILE_FLAG_OVERLAPPED);
2280 SetErrnoFromWinError (GetLastError ());
2282 GNUNET_free (p->fd[0]);
2283 GNUNET_free (p->fd[1]);
2288 if (!DuplicateHandle
2289 (GetCurrentProcess (), p->fd[0]->h, GetCurrentProcess (), &tmp_handle, 0,
2290 inherit_read == GNUNET_YES ? TRUE : FALSE, DUPLICATE_SAME_ACCESS))
2292 SetErrnoFromWinError (GetLastError ());
2294 CloseHandle (p->fd[0]->h);
2295 CloseHandle (p->fd[1]->h);
2296 GNUNET_free (p->fd[0]);
2297 GNUNET_free (p->fd[1]);
2302 CloseHandle (p->fd[0]->h);
2303 p->fd[0]->h = tmp_handle;
2305 if (!DuplicateHandle
2306 (GetCurrentProcess (), p->fd[1]->h, GetCurrentProcess (), &tmp_handle, 0,
2307 inherit_write == GNUNET_YES ? TRUE : FALSE, DUPLICATE_SAME_ACCESS))
2309 SetErrnoFromWinError (GetLastError ());
2311 CloseHandle (p->fd[0]->h);
2312 CloseHandle (p->fd[1]->h);
2313 GNUNET_free (p->fd[0]);
2314 GNUNET_free (p->fd[1]);
2319 CloseHandle (p->fd[1]->h);
2320 p->fd[1]->h = tmp_handle;
2322 p->fd[0]->type = GNUNET_DISK_HANLDE_TYPE_PIPE;
2323 p->fd[1]->type = GNUNET_DISK_HANLDE_TYPE_PIPE;
2325 p->fd[0]->oOverlapRead = GNUNET_malloc (sizeof (OVERLAPPED));
2326 p->fd[0]->oOverlapWrite = GNUNET_malloc (sizeof (OVERLAPPED));
2327 p->fd[1]->oOverlapRead = GNUNET_malloc (sizeof (OVERLAPPED));
2328 p->fd[1]->oOverlapWrite = GNUNET_malloc (sizeof (OVERLAPPED));
2330 p->fd[0]->oOverlapRead->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2331 p->fd[0]->oOverlapWrite->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2333 p->fd[1]->oOverlapRead->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2334 p->fd[1]->oOverlapWrite->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2342 * Creates a pipe object from a couple of file descriptors.
2343 * Useful for wrapping existing pipe FDs.
2345 * @param blocking_read creates an asynchronous pipe for reading if set to GNUNET_NO
2346 * @param blocking_write creates an asynchronous pipe for writing if set to GNUNET_NO
2347 * @param fd an array of two fd values. One of them may be -1 for read-only or write-only pipes
2349 * @return handle to the new pipe, NULL on error
2351 struct GNUNET_DISK_PipeHandle *
2352 GNUNET_DISK_pipe_from_fd (int blocking_read, int blocking_write, int fd[2])
2354 struct GNUNET_DISK_PipeHandle *p;
2356 p = GNUNET_new (struct GNUNET_DISK_PipeHandle);
2361 int eno = 0; /* make gcc happy */
2366 p->fd[0] = GNUNET_new (struct GNUNET_DISK_FileHandle);
2367 p->fd[0]->fd = fd[0];
2370 flags = fcntl (fd[0], F_GETFL);
2371 flags |= O_NONBLOCK;
2372 if (0 > fcntl (fd[0], F_SETFL, flags))
2378 flags = fcntl (fd[0], F_GETFD);
2379 flags |= FD_CLOEXEC;
2380 if (0 > fcntl (fd[0], F_SETFD, flags))
2389 p->fd[1] = GNUNET_new (struct GNUNET_DISK_FileHandle);
2390 p->fd[1]->fd = fd[1];
2391 if (!blocking_write)
2393 flags = fcntl (fd[1], F_GETFL);
2394 flags |= O_NONBLOCK;
2395 if (0 > fcntl (fd[1], F_SETFL, flags))
2401 flags = fcntl (fd[1], F_GETFD);
2402 flags |= FD_CLOEXEC;
2403 if (0 > fcntl (fd[1], F_SETFD, flags))
2412 LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR, "fcntl");
2413 if (p->fd[0]->fd >= 0)
2414 GNUNET_break (0 == close (p->fd[0]->fd));
2415 if (p->fd[1]->fd >= 0)
2416 GNUNET_break (0 == close (p->fd[1]->fd));
2417 GNUNET_free_non_null (p->fd[0]);
2418 GNUNET_free_non_null (p->fd[1]);
2426 p->fd[0] = GNUNET_new (struct GNUNET_DISK_FileHandle);
2427 p->fd[0]->h = (HANDLE) _get_osfhandle (fd[0]);
2428 if (p->fd[0]->h != INVALID_HANDLE_VALUE)
2430 p->fd[0]->type = GNUNET_DISK_HANLDE_TYPE_PIPE;
2431 p->fd[0]->oOverlapRead = GNUNET_malloc (sizeof (OVERLAPPED));
2432 p->fd[0]->oOverlapWrite = GNUNET_malloc (sizeof (OVERLAPPED));
2433 p->fd[0]->oOverlapRead->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2434 p->fd[0]->oOverlapWrite->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2438 GNUNET_free (p->fd[0]);
2444 p->fd[1] = GNUNET_new (struct GNUNET_DISK_FileHandle);
2445 p->fd[1]->h = (HANDLE) _get_osfhandle (fd[1]);
2446 if (p->fd[1]->h != INVALID_HANDLE_VALUE)
2448 p->fd[1]->type = GNUNET_DISK_HANLDE_TYPE_PIPE;
2449 p->fd[1]->oOverlapRead = GNUNET_malloc (sizeof (OVERLAPPED));
2450 p->fd[1]->oOverlapWrite = GNUNET_malloc (sizeof (OVERLAPPED));
2451 p->fd[1]->oOverlapRead->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2452 p->fd[1]->oOverlapWrite->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2456 GNUNET_free (p->fd[1]);
2467 * Closes an interprocess channel
2469 * @param p pipe to close
2470 * @param end which end of the pipe to close
2471 * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
2474 GNUNET_DISK_pipe_close_end (struct GNUNET_DISK_PipeHandle *p,
2475 enum GNUNET_DISK_PipeEnd end)
2477 int ret = GNUNET_OK;
2479 if (end == GNUNET_DISK_PIPE_END_READ)
2483 ret = GNUNET_DISK_file_close (p->fd[0]);
2487 else if (end == GNUNET_DISK_PIPE_END_WRITE)
2491 ret = GNUNET_DISK_file_close (p->fd[1]);
2500 * Detaches one of the ends from the pipe.
2501 * Detached end is a fully-functional FileHandle, it will
2502 * not be affected by anything you do with the pipe afterwards.
2503 * Each end of a pipe can only be detched from it once (i.e.
2504 * it is not duplicated).
2506 * @param p pipe to detach an end from
2507 * @param end which end of the pipe to detach
2508 * @return Detached end on success, NULL on failure
2509 * (or if that end is not present or is closed).
2511 struct GNUNET_DISK_FileHandle *
2512 GNUNET_DISK_pipe_detach_end (struct GNUNET_DISK_PipeHandle *p,
2513 enum GNUNET_DISK_PipeEnd end)
2515 struct GNUNET_DISK_FileHandle *ret = NULL;
2517 if (end == GNUNET_DISK_PIPE_END_READ)
2525 else if (end == GNUNET_DISK_PIPE_END_WRITE)
2539 * Closes an interprocess channel
2541 * @param p pipe to close
2542 * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
2545 GNUNET_DISK_pipe_close (struct GNUNET_DISK_PipeHandle *p)
2547 int ret = GNUNET_OK;
2550 int write_end_close;
2551 int read_end_close_errno;
2552 int write_end_close_errno;
2554 read_end_close = GNUNET_DISK_pipe_close_end (p, GNUNET_DISK_PIPE_END_READ);
2555 read_end_close_errno = errno;
2556 write_end_close = GNUNET_DISK_pipe_close_end (p, GNUNET_DISK_PIPE_END_WRITE);
2557 write_end_close_errno = errno;
2560 if (GNUNET_OK != read_end_close)
2562 errno = read_end_close_errno;
2563 ret = read_end_close;
2565 else if (GNUNET_OK != write_end_close)
2567 errno = write_end_close_errno;
2568 ret = write_end_close;
2576 * Get the handle to a particular pipe end
2579 * @param n end to access
2580 * @return handle for the respective end
2582 const struct GNUNET_DISK_FileHandle *
2583 GNUNET_DISK_pipe_handle (const struct GNUNET_DISK_PipeHandle *p,
2584 enum GNUNET_DISK_PipeEnd n)
2588 case GNUNET_DISK_PIPE_END_READ:
2589 case GNUNET_DISK_PIPE_END_WRITE:
2599 * Retrieve OS file handle
2601 * @param fh GNUnet file descriptor
2602 * @param dst destination buffer
2603 * @param dst_len length of dst
2604 * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
2607 GNUNET_DISK_internal_file_handle_ (const struct GNUNET_DISK_FileHandle *fh,
2608 void *dst, size_t dst_len)
2611 if (dst_len < sizeof (HANDLE))
2612 return GNUNET_SYSERR;
2613 *((HANDLE *) dst) = fh->h;
2615 if (dst_len < sizeof (int))
2616 return GNUNET_SYSERR;
2617 *((int *) dst) = fh->fd;