2 This file is part of GNUnet.
3 Copyright (C) 2001--2013, 2016 GNUnet e.V.
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., 51 Franklin Street, Fifth Floor,
18 Boston, MA 02110-1301, USA.
22 * @brief disk IO convenience methods
23 * @author Christian Grothoff
28 #include "gnunet_strings_lib.h"
29 #include "gnunet_disk_lib.h"
31 #define LOG(kind,...) GNUNET_log_from (kind, "util-disk", __VA_ARGS__)
33 #define LOG_STRERROR(kind,syscall) GNUNET_log_from_strerror (kind, "util-disk", syscall)
35 #define LOG_STRERROR_FILE(kind,syscall,filename) GNUNET_log_from_strerror_file (kind, "util-disk", syscall, filename)
38 * Block size for IO for copying files.
40 #define COPY_BLK_SIZE 65536
42 #include <sys/types.h>
47 #include <sys/param.h>
50 #include <sys/mount.h>
52 #if HAVE_SYS_STATVFS_H
53 #include <sys/statvfs.h>
57 #define _IFMT 0170000 /* type of file */
58 #define _IFLNK 0120000 /* symbolic link */
59 #define S_ISLNK(m) (((m)&_IFMT) == _IFLNK)
64 * Handle used to manage a pipe.
66 struct GNUNET_DISK_PipeHandle
69 * File descriptors for the pipe.
70 * One or both of them could be NULL.
72 struct GNUNET_DISK_FileHandle *fd[2];
77 * Closure for the recursion to determine the file size
80 struct GetFileSizeData
83 * Set to the total file size.
88 * GNUNET_YES if symbolic links should be included.
90 int include_sym_links;
93 * GNUNET_YES if mode is file-only (return total == -1 for directories).
101 * Translate GNUnet-internal permission bitmap to UNIX file
102 * access permission bitmap.
104 * @param perm file permissions, GNUnet style
105 * @return file permissions, UNIX style
108 translate_unix_perms (enum GNUNET_DISK_AccessPermissions perm)
113 if (perm & GNUNET_DISK_PERM_USER_READ)
115 if (perm & GNUNET_DISK_PERM_USER_WRITE)
117 if (perm & GNUNET_DISK_PERM_USER_EXEC)
119 if (perm & GNUNET_DISK_PERM_GROUP_READ)
121 if (perm & GNUNET_DISK_PERM_GROUP_WRITE)
123 if (perm & GNUNET_DISK_PERM_GROUP_EXEC)
125 if (perm & GNUNET_DISK_PERM_OTHER_READ)
127 if (perm & GNUNET_DISK_PERM_OTHER_WRITE)
129 if (perm & GNUNET_DISK_PERM_OTHER_EXEC)
138 * Iterate over all files in the given directory and
139 * accumulate their size.
141 * @param cls closure of type `struct GetFileSizeData`
142 * @param fn current filename we are looking at
143 * @return #GNUNET_SYSERR on serious errors, otherwise #GNUNET_OK
146 getSizeRec (void *cls, const char *fn)
148 struct GetFileSizeData *gfsd = cls;
150 #if defined (HAVE_STAT64) && !(defined (_FILE_OFFSET_BITS) && _FILE_OFFSET_BITS == 64)
153 if (0 != STAT64 (fn, &buf))
155 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_DEBUG, "stat64", fn);
156 return GNUNET_SYSERR;
161 if (0 != STAT (fn, &buf))
163 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_DEBUG, "stat", fn);
164 return GNUNET_SYSERR;
167 if ((S_ISDIR (buf.st_mode)) && (gfsd->single_file_mode == GNUNET_YES))
170 return GNUNET_SYSERR;
172 if ((!S_ISLNK (buf.st_mode)) || (gfsd->include_sym_links == GNUNET_YES))
173 gfsd->total += buf.st_size;
174 if ((S_ISDIR (buf.st_mode)) && (0 == ACCESS (fn, X_OK)) &&
175 ((!S_ISLNK (buf.st_mode)) || (gfsd->include_sym_links == GNUNET_YES)))
177 if (GNUNET_SYSERR == GNUNET_DISK_directory_scan (fn, &getSizeRec, gfsd))
178 return GNUNET_SYSERR;
185 * Checks whether a handle is invalid
187 * @param h handle to check
188 * @return #GNUNET_YES if invalid, #GNUNET_NO if valid
191 GNUNET_DISK_handle_invalid (const struct GNUNET_DISK_FileHandle *h)
194 return ((!h) || (h->h == INVALID_HANDLE_VALUE)) ? GNUNET_YES : GNUNET_NO;
196 return ((!h) || (h->fd == -1)) ? GNUNET_YES : GNUNET_NO;
201 * Get the size of an open file.
203 * @param fh open file handle
204 * @param size where to write size of the file
205 * @return #GNUNET_OK on success, #GNUNET_SYSERR on error
208 GNUNET_DISK_file_handle_size (struct GNUNET_DISK_FileHandle *fh,
214 b = GetFileSizeEx (fh->h, &li);
217 SetErrnoFromWinError (GetLastError ());
218 return GNUNET_SYSERR;
220 *size = (off_t) li.QuadPart;
224 if (0 != FSTAT (fh->fd, &sbuf))
225 return GNUNET_SYSERR;
226 *size = sbuf.st_size;
233 * Move the read/write pointer in a file
235 * @param h handle of an open file
236 * @param offset position to move to
237 * @param whence specification to which position the offset parameter relates to
238 * @return the new position on success, #GNUNET_SYSERR otherwise
241 GNUNET_DISK_file_seek (const struct GNUNET_DISK_FileHandle * h, off_t offset,
242 enum GNUNET_DISK_Seek whence)
247 return GNUNET_SYSERR;
252 LARGE_INTEGER new_pos;
255 static DWORD t[] = { FILE_BEGIN, FILE_CURRENT, FILE_END };
256 li.QuadPart = offset;
258 b = SetFilePointerEx (h->h, li, &new_pos, t[whence]);
261 SetErrnoFromWinError (GetLastError ());
262 return GNUNET_SYSERR;
264 return (off_t) new_pos.QuadPart;
266 static int t[] = { SEEK_SET, SEEK_CUR, SEEK_END };
268 return lseek (h->fd, offset, t[whence]);
274 * Get the size of the file (or directory) of the given file (in
277 * @param filename name of the file or directory
278 * @param size set to the size of the file (or,
279 * in the case of directories, the sum
280 * of all sizes of files in the directory)
281 * @param include_symbolic_links should symbolic links be
283 * @param single_file_mode #GNUNET_YES to only get size of one file
284 * and return #GNUNET_SYSERR for directories.
285 * @return #GNUNET_SYSERR on error, #GNUNET_OK on success
288 GNUNET_DISK_file_size (const char *filename,
290 int include_symbolic_links,
291 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,
333 GNUNET_DISK_OPEN_READ,
334 GNUNET_DISK_PERM_NONE);
336 return GNUNET_SYSERR;
337 succ = GetFileInformationByHandle (fh->h, &info);
338 GNUNET_DISK_file_close (fh);
341 return GNUNET_SYSERR;
343 *dev = info.dwVolumeSerialNumber;
344 *ino = ((((uint64_t) info.nFileIndexHigh) << (sizeof (DWORD) * 8)) | info.nFileIndexLow);
351 if (0 != stat (filename, &sbuf))
353 return GNUNET_SYSERR;
355 *ino = (uint64_t) sbuf.st_ino;
364 if (0 != statvfs (filename, &fbuf))
366 return GNUNET_SYSERR;
368 *dev = (uint64_t) fbuf.f_fsid;
374 if (0 != statfs (filename, &fbuf))
376 return GNUNET_SYSERR;
378 *dev = ((uint64_t) fbuf.f_fsid.val[0]) << 32 ||
379 ((uint64_t) fbuf.f_fsid.val[1]);
384 #endif /* !WINDOWS */
390 * Create the name for a temporary file or directory from a template.
392 * @param t template (without XXXXX or "/tmp/")
393 * @return name ready for passing to 'mktemp' or 'mkdtemp', NULL on error
396 mktemp_name (const char *t)
402 if ((t[0] != '/') && (t[0] != '\\')
404 && !(isalpha ((int) t[0]) && (t[0] != '\0') && (t[1] == ':'))
408 /* FIXME: This uses system codepage on W32, not UTF-8 */
409 tmpdir = getenv ("TMPDIR");
411 tmpdir = getenv ("TMP");
413 tmpdir = getenv ("TEMP");
416 GNUNET_asprintf (&tmpl, "%s/%s%s", tmpdir, t, "XXXXXX");
420 GNUNET_asprintf (&tmpl, "%s%s", t, "XXXXXX");
423 fn = (char *) GNUNET_malloc (MAX_PATH + 1);
424 if (ERROR_SUCCESS != plibc_conv_to_win_path (tmpl, fn))
447 tfn = GNUNET_strdup (fn);
448 random_fn = _mktemp (tfn);
449 if (NULL == random_fn)
454 /* FIXME: assume fn to be UTF-8-encoded and do the right thing */
455 if (0 == CreateDirectoryA (tfn, NULL))
457 DWORD error = GetLastError ();
459 if (ERROR_ALREADY_EXISTS == error)
470 * Update POSIX permissions mask of a file on disk. If both argumets
471 * are #GNUNET_NO, the file is made world-read-write-executable (777).
472 * Does nothing on W32.
474 * @param fn name of the file to update
475 * @param require_uid_match #GNUNET_YES means 700
476 * @param require_gid_match #GNUNET_YES means 770 unless @a require_uid_match is set
479 GNUNET_DISK_fix_permissions (const char *fn,
480 int require_uid_match,
481 int require_gid_match)
489 * Update POSIX permissions mask of a file on disk. If both argumets
490 * are #GNUNET_NO, the file is made world-read-write-executable (777).
492 * @param fn name of the file to update
493 * @param require_uid_match #GNUNET_YES means 700
494 * @param require_gid_match #GNUNET_YES means 770 unless @a require_uid_match is set
497 GNUNET_DISK_fix_permissions (const char *fn,
498 int require_uid_match,
499 int require_gid_match)
503 if (GNUNET_YES == require_uid_match)
504 mode = S_IRUSR | S_IWUSR | S_IXUSR;
505 else if (GNUNET_YES == require_gid_match)
506 mode = S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IWGRP | S_IXGRP;
508 mode = S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IWGRP | S_IXGRP | S_IROTH | S_IWOTH | S_IXOTH;
509 if (0 != chmod (fn, mode))
510 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING,
518 * Create an (empty) temporary directory on disk. If the given name is not
519 * an absolute path, the current 'TMPDIR' will be prepended. In any case,
520 * 6 random characters will be appended to the name to create a unique
523 * @param t component to use for the name;
524 * does NOT contain "XXXXXX" or "/tmp/".
525 * @return NULL on error, otherwise name of fresh
526 * file on disk in directory for temporary files
529 GNUNET_DISK_mkdtemp (const char *t)
534 omask = umask (S_IWGRP | S_IWOTH | S_IRGRP | S_IROTH);
535 fn = mktemp_name (t);
536 if (fn != mkdtemp (fn))
538 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_ERROR, "mkdtemp", fn);
549 * Move a file out of the way (create a backup) by
550 * renaming it to "orig.NUM~" where NUM is the smallest
551 * number that is not used yet.
553 * @param fil name of the file to back up
556 GNUNET_DISK_file_backup (const char *fil)
562 slen = strlen (fil) + 20;
563 target = GNUNET_malloc (slen);
567 GNUNET_snprintf (target, slen,
571 } while (0 == access (target, F_OK));
572 if (0 != rename (fil, target))
573 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR,
576 GNUNET_free (target);
581 * Create an (empty) temporary file on disk. If the given name is not
582 * an absolute path, the current 'TMPDIR' will be prepended. In any case,
583 * 6 random characters will be appended to the name to create a unique
586 * @param t component to use for the name;
587 * does NOT contain "XXXXXX" or "/tmp/".
588 * @return NULL on error, otherwise name of fresh
589 * file on disk in directory for temporary files
592 GNUNET_DISK_mktemp (const char *t)
598 omask = umask (S_IWGRP | S_IWOTH | S_IRGRP | S_IROTH);
599 fn = mktemp_name (t);
600 if (-1 == (fd = mkstemp (fn)))
602 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_ERROR, "mkstemp", fn);
609 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "close", fn);
615 * Test if @a fil is a directory and listable. Optionally, also check if the
616 * directory is readable. Will not print an error message if the directory does
617 * not exist. Will log errors if #GNUNET_SYSERR is returned (i.e., a file exists
618 * with the same name).
620 * @param fil filename to test
621 * @param is_readable #GNUNET_YES to additionally check if @a fil is readable;
622 * #GNUNET_NO to disable this check
623 * @return #GNUNET_YES if yes, #GNUNET_NO if not; #GNUNET_SYSERR if it
624 * does not exist or stat'ed
627 GNUNET_DISK_directory_test (const char *fil, int is_readable)
629 struct stat filestat;
632 ret = STAT (fil, &filestat);
636 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "stat", fil);
637 return GNUNET_SYSERR;
639 if (!S_ISDIR (filestat.st_mode))
641 LOG (GNUNET_ERROR_TYPE_DEBUG,
642 "A file already exits with the same name %s\n", fil);
645 if (GNUNET_YES == is_readable)
646 ret = ACCESS (fil, R_OK | X_OK);
648 ret = ACCESS (fil, X_OK);
651 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "access", fil);
659 * Check that fil corresponds to a filename
660 * (of a file that exists and that is not a directory).
662 * @param fil filename to check
663 * @return #GNUNET_YES if yes, #GNUNET_NO if not a file, #GNUNET_SYSERR if something
664 * else (will print an error message in that case, too).
667 GNUNET_DISK_file_test (const char *fil)
669 struct stat filestat;
673 rdir = GNUNET_STRINGS_filename_expand (fil);
675 return GNUNET_SYSERR;
677 ret = STAT (rdir, &filestat);
682 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "stat", rdir);
684 return GNUNET_SYSERR;
689 if (!S_ISREG (filestat.st_mode))
694 if (ACCESS (rdir, F_OK) < 0)
696 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "access", rdir);
698 return GNUNET_SYSERR;
706 * Implementation of "mkdir -p"
708 * @param dir the directory to create
709 * @return #GNUNET_OK on success, #GNUNET_SYSERR on failure
712 GNUNET_DISK_directory_create (const char *dir)
720 rdir = GNUNET_STRINGS_filename_expand (dir);
722 return GNUNET_SYSERR;
726 pos = 1; /* skip heading '/' */
728 /* Local or Network path? */
729 if (strncmp (rdir, "\\\\", 2) == 0)
734 if (rdir[pos] == '\\')
744 pos = 3; /* strlen("C:\\") */
747 /* Check which low level directories already exist */
749 rdir[len] = DIR_SEPARATOR;
752 if (DIR_SEPARATOR == rdir[pos2])
755 ret = GNUNET_DISK_directory_test (rdir, GNUNET_NO);
756 if (GNUNET_NO == ret)
759 return GNUNET_SYSERR;
761 rdir[pos2] = DIR_SEPARATOR;
762 if (GNUNET_YES == ret)
773 /* Start creating directories */
776 if ((rdir[pos] == DIR_SEPARATOR) || (pos == len))
779 ret = GNUNET_DISK_directory_test (rdir, GNUNET_NO);
780 if (GNUNET_NO == ret)
783 return GNUNET_SYSERR;
785 if (GNUNET_SYSERR == ret)
788 ret = mkdir (rdir, S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH); /* 755 */
790 wchar_t wrdir[MAX_PATH + 1];
791 if (ERROR_SUCCESS == plibc_conv_to_win_pathwconv(rdir, wrdir))
792 ret = !CreateDirectoryW (wrdir, NULL);
796 if ((ret != 0) && (errno != EEXIST))
798 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_ERROR, "mkdir", rdir);
800 return GNUNET_SYSERR;
803 rdir[pos] = DIR_SEPARATOR;
813 * Create the directory structure for storing a file.
815 * @param filename name of a file in the directory
816 * @returns #GNUNET_OK on success,
817 * #GNUNET_SYSERR on failure,
818 * #GNUNET_NO if the directory
819 * exists but is not writeable for us
822 GNUNET_DISK_directory_create_for_file (const char *filename)
829 rdir = GNUNET_STRINGS_filename_expand (filename);
833 return GNUNET_SYSERR;
836 while ((len > 0) && (rdir[len] != DIR_SEPARATOR))
839 /* The empty path is invalid and in this case refers to / */
843 rdir = GNUNET_strdup ("/");
845 ret = GNUNET_DISK_directory_create (rdir);
846 if ((GNUNET_OK == ret) && (0 != ACCESS (rdir, W_OK)))
856 * Read the contents of a binary file into a buffer.
858 * @param h handle to an open file
859 * @param result the buffer to write the result to
860 * @param len the maximum number of bytes to read
861 * @return the number of bytes read on success, #GNUNET_SYSERR on failure
864 GNUNET_DISK_file_read (const struct GNUNET_DISK_FileHandle *h,
871 return GNUNET_SYSERR;
877 if (h->type == GNUNET_DISK_HANLDE_TYPE_FILE)
879 if (!ReadFile (h->h, result, len, &bytes_read, NULL))
881 SetErrnoFromWinError (GetLastError ());
882 return GNUNET_SYSERR;
885 else if (h->type == GNUNET_DISK_HANLDE_TYPE_PIPE)
887 if (!ReadFile (h->h, result, len, &bytes_read, h->oOverlapRead))
889 if (GetLastError () != ERROR_IO_PENDING)
891 LOG (GNUNET_ERROR_TYPE_DEBUG,
892 "Error reading from pipe: %u\n",
894 SetErrnoFromWinError (GetLastError ());
895 return GNUNET_SYSERR;
897 LOG (GNUNET_ERROR_TYPE_DEBUG, "Will get overlapped result\n");
898 GetOverlappedResult (h->h, h->oOverlapRead, &bytes_read, TRUE);
900 LOG (GNUNET_ERROR_TYPE_DEBUG, "Read %u bytes from pipe\n", bytes_read);
908 return read (h->fd, result, len);
914 * Read the contents of a binary file into a buffer.
915 * Guarantees not to block (returns GNUNET_SYSERR and sets errno to EAGAIN
916 * when no data can be read).
918 * @param h handle to an open file
919 * @param result the buffer to write the result to
920 * @param len the maximum number of bytes to read
921 * @return the number of bytes read on success, #GNUNET_SYSERR on failure
924 GNUNET_DISK_file_read_non_blocking (const struct GNUNET_DISK_FileHandle *h,
931 return GNUNET_SYSERR;
937 if (h->type == GNUNET_DISK_HANLDE_TYPE_FILE)
939 if (!ReadFile (h->h, result, len, &bytes_read, NULL))
941 SetErrnoFromWinError (GetLastError ());
942 return GNUNET_SYSERR;
945 else if (h->type == GNUNET_DISK_HANLDE_TYPE_PIPE)
947 if (!ReadFile (h->h, result, len, &bytes_read, h->oOverlapRead))
949 if (GetLastError () != ERROR_IO_PENDING)
951 LOG (GNUNET_ERROR_TYPE_DEBUG, "Error reading from pipe: %u\n", GetLastError ());
952 SetErrnoFromWinError (GetLastError ());
953 return GNUNET_SYSERR;
957 LOG (GNUNET_ERROR_TYPE_DEBUG,
958 "ReadFile() queued a read, cancelling\n");
961 return GNUNET_SYSERR;
964 LOG (GNUNET_ERROR_TYPE_DEBUG,
977 /* set to non-blocking, read, then set back */
978 flags = fcntl (h->fd, F_GETFL);
979 if (0 == (flags & O_NONBLOCK))
980 (void) fcntl (h->fd, F_SETFL, flags | O_NONBLOCK);
981 ret = read (h->fd, result, len);
982 if (0 == (flags & O_NONBLOCK))
985 (void) fcntl (h->fd, F_SETFL, flags);
994 * Read the contents of a binary file into a buffer.
996 * @param fn file name
997 * @param result the buffer to write the result to
998 * @param len the maximum number of bytes to read
999 * @return number of bytes read, #GNUNET_SYSERR on failure
1002 GNUNET_DISK_fn_read (const char *fn,
1006 struct GNUNET_DISK_FileHandle *fh;
1010 fh = GNUNET_DISK_file_open (fn,
1011 GNUNET_DISK_OPEN_READ,
1012 GNUNET_DISK_PERM_NONE);
1014 return GNUNET_SYSERR;
1015 ret = GNUNET_DISK_file_read (fh, result, len);
1017 GNUNET_assert (GNUNET_OK == GNUNET_DISK_file_close (fh));
1024 * Write a buffer to a file.
1026 * @param h handle to open file
1027 * @param buffer the data to write
1028 * @param n number of bytes to write
1029 * @return number of bytes written on success, #GNUNET_SYSERR on error
1032 GNUNET_DISK_file_write (const struct GNUNET_DISK_FileHandle * h,
1033 const void *buffer, size_t n)
1038 return GNUNET_SYSERR;
1042 DWORD bytes_written;
1044 if (h->type == GNUNET_DISK_HANLDE_TYPE_FILE)
1046 if (!WriteFile (h->h, buffer, n, &bytes_written, NULL))
1048 SetErrnoFromWinError (GetLastError ());
1049 return GNUNET_SYSERR;
1052 else if (h->type == GNUNET_DISK_HANLDE_TYPE_PIPE)
1054 LOG (GNUNET_ERROR_TYPE_DEBUG, "It is a pipe trying to write %u bytes\n", n);
1055 if (!WriteFile (h->h, buffer, n, &bytes_written, h->oOverlapWrite))
1057 if (GetLastError () != ERROR_IO_PENDING)
1059 SetErrnoFromWinError (GetLastError ());
1060 LOG (GNUNET_ERROR_TYPE_DEBUG, "Error writing to pipe: %u\n",
1062 return GNUNET_SYSERR;
1064 LOG (GNUNET_ERROR_TYPE_DEBUG, "Will get overlapped result\n");
1065 if (!GetOverlappedResult (h->h, h->oOverlapWrite, &bytes_written, TRUE))
1067 SetErrnoFromWinError (GetLastError ());
1068 LOG (GNUNET_ERROR_TYPE_DEBUG,
1069 "Error getting overlapped result while writing to pipe: %u\n",
1071 return GNUNET_SYSERR;
1077 if (!GetOverlappedResult (h->h, h->oOverlapWrite, &ovr, TRUE))
1079 LOG (GNUNET_ERROR_TYPE_DEBUG,
1080 "Error getting control overlapped result while writing to pipe: %u\n",
1085 LOG (GNUNET_ERROR_TYPE_DEBUG,
1086 "Wrote %u bytes (ovr says %u), picking the greatest\n",
1087 bytes_written, ovr);
1090 if (bytes_written == 0)
1094 LOG (GNUNET_ERROR_TYPE_DEBUG, "Wrote %u bytes, returning -1 with EAGAIN\n", bytes_written);
1096 return GNUNET_SYSERR;
1099 LOG (GNUNET_ERROR_TYPE_DEBUG, "Wrote %u bytes\n", bytes_written);
1105 return bytes_written;
1107 return write (h->fd, buffer, n);
1113 * Write a buffer to a file, blocking, if necessary.
1115 * @param h handle to open file
1116 * @param buffer the data to write
1117 * @param n number of bytes to write
1118 * @return number of bytes written on success, #GNUNET_SYSERR on error
1121 GNUNET_DISK_file_write_blocking (const struct GNUNET_DISK_FileHandle * h,
1128 return GNUNET_SYSERR;
1132 DWORD bytes_written;
1133 /* We do a non-overlapped write, which is as blocking as it gets */
1134 LOG (GNUNET_ERROR_TYPE_DEBUG, "Writing %u bytes\n", n);
1135 if (!WriteFile (h->h, buffer, n, &bytes_written, NULL))
1137 SetErrnoFromWinError (GetLastError ());
1138 LOG (GNUNET_ERROR_TYPE_DEBUG, "Error writing to pipe: %u\n",
1140 return GNUNET_SYSERR;
1142 if (bytes_written == 0 && n > 0)
1144 LOG (GNUNET_ERROR_TYPE_DEBUG, "Waiting for pipe to clean\n");
1145 WaitForSingleObject (h->h, INFINITE);
1146 if (!WriteFile (h->h, buffer, n, &bytes_written, NULL))
1148 SetErrnoFromWinError (GetLastError ());
1149 LOG (GNUNET_ERROR_TYPE_DEBUG, "Error writing to pipe: %u\n",
1151 return GNUNET_SYSERR;
1154 LOG (GNUNET_ERROR_TYPE_DEBUG,
1157 return bytes_written;
1162 /* set to blocking, write, then set back */
1163 flags = fcntl (h->fd, F_GETFL);
1164 if (0 != (flags & O_NONBLOCK))
1165 (void) fcntl (h->fd, F_SETFL, flags - O_NONBLOCK);
1166 ret = write (h->fd, buffer, n);
1167 if (0 == (flags & O_NONBLOCK))
1168 (void) fcntl (h->fd, F_SETFL, flags);
1175 * Write a buffer to a file. If the file is longer than the
1176 * number of bytes that will be written, it will be truncated.
1178 * @param fn file name
1179 * @param buffer the data to write
1180 * @param n number of bytes to write
1181 * @param mode file permissions
1182 * @return number of bytes written on success, #GNUNET_SYSERR on error
1185 GNUNET_DISK_fn_write (const char *fn,
1188 enum GNUNET_DISK_AccessPermissions mode)
1190 struct GNUNET_DISK_FileHandle *fh;
1193 fh = GNUNET_DISK_file_open (fn,
1194 GNUNET_DISK_OPEN_WRITE | GNUNET_DISK_OPEN_TRUNCATE
1195 | GNUNET_DISK_OPEN_CREATE, mode);
1197 return GNUNET_SYSERR;
1198 ret = GNUNET_DISK_file_write (fh, buffer, n);
1199 GNUNET_assert (GNUNET_OK == GNUNET_DISK_file_close (fh));
1205 * Scan a directory for files.
1207 * @param dir_name the name of the directory
1208 * @param callback the method to call for each file,
1209 * can be NULL, in that case, we only count
1210 * @param callback_cls closure for @a callback
1211 * @return the number of files found, #GNUNET_SYSERR on error or
1212 * ieration aborted by callback returning #GNUNET_SYSERR
1215 GNUNET_DISK_directory_scan (const char *dir_name,
1216 GNUNET_FileNameCallback callback,
1220 struct dirent *finfo;
1226 unsigned int name_len;
1227 unsigned int n_size;
1229 GNUNET_assert (NULL != dir_name);
1230 dname = GNUNET_STRINGS_filename_expand (dir_name);
1232 return GNUNET_SYSERR;
1233 while ( (strlen (dname) > 0) &&
1234 (dname[strlen (dname) - 1] == DIR_SEPARATOR) )
1235 dname[strlen (dname) - 1] = '\0';
1236 if (0 != STAT (dname, &istat))
1238 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING,
1241 GNUNET_free (dname);
1242 return GNUNET_SYSERR;
1244 if (! S_ISDIR (istat.st_mode))
1246 LOG (GNUNET_ERROR_TYPE_WARNING,
1247 _("Expected `%s' to be a directory!\n"),
1249 GNUNET_free (dname);
1250 return GNUNET_SYSERR;
1253 dinfo = OPENDIR (dname);
1254 if ( (EACCES == errno) ||
1257 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING,
1262 GNUNET_free (dname);
1263 return GNUNET_SYSERR;
1266 n_size = strlen (dname) + name_len + strlen (DIR_SEPARATOR_STR) + 1;
1267 name = GNUNET_malloc (n_size);
1268 while (NULL != (finfo = READDIR (dinfo)))
1270 if ( (0 == strcmp (finfo->d_name, ".")) ||
1271 (0 == strcmp (finfo->d_name, "..")) )
1273 if (NULL != callback)
1275 if (name_len < strlen (finfo->d_name))
1278 name_len = strlen (finfo->d_name);
1279 n_size = strlen (dname) + name_len + strlen (DIR_SEPARATOR_STR) + 1;
1280 name = GNUNET_malloc (n_size);
1282 /* dname can end in "/" only if dname == "/";
1283 * if dname does not end in "/", we need to add
1284 * a "/" (otherwise, we must not!) */
1285 GNUNET_snprintf (name,
1289 (0 == strcmp (dname,
1292 : DIR_SEPARATOR_STR,
1294 ret = callback (callback_cls,
1296 if (GNUNET_OK != ret)
1300 GNUNET_free (dname);
1301 if (GNUNET_NO == ret)
1303 return GNUNET_SYSERR;
1310 GNUNET_free (dname);
1316 * Function that removes the given directory by calling
1317 * #GNUNET_DISK_directory_remove().
1319 * @param unused not used
1320 * @param fn directory to remove
1321 * @return #GNUNET_OK
1324 remove_helper (void *unused,
1328 (void) GNUNET_DISK_directory_remove (fn);
1334 * Remove all files in a directory (rm -r). Call with
1337 * @param filename the file to remove
1338 * @return #GNUNET_OK on success, #GNUNET_SYSERR on error
1341 GNUNET_DISK_directory_remove (const char *filename)
1345 if (NULL == filename)
1348 return GNUNET_SYSERR;
1350 if (0 != LSTAT (filename, &istat))
1351 return GNUNET_NO; /* file may not exist... */
1352 (void) CHMOD (filename,
1353 S_IWUSR | S_IRUSR | S_IXUSR);
1354 if (0 == UNLINK (filename))
1356 if ( (errno != EISDIR) &&
1357 /* EISDIR is not sufficient in all cases, e.g.
1358 * sticky /tmp directory may result in EPERM on BSD.
1359 * So we also explicitly check "isDirectory" */
1361 GNUNET_DISK_directory_test (filename,
1364 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING,
1367 return GNUNET_SYSERR;
1369 if (GNUNET_SYSERR ==
1370 GNUNET_DISK_directory_scan (filename,
1373 return GNUNET_SYSERR;
1374 if (0 != RMDIR (filename))
1376 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING,
1379 return GNUNET_SYSERR;
1388 * @param src file to copy
1389 * @param dst destination file name
1390 * @return #GNUNET_OK on success, #GNUNET_SYSERR on error
1393 GNUNET_DISK_file_copy (const char *src,
1401 struct GNUNET_DISK_FileHandle *in;
1402 struct GNUNET_DISK_FileHandle *out;
1404 if (GNUNET_OK != GNUNET_DISK_file_size (src, &size, GNUNET_YES, GNUNET_YES))
1405 return GNUNET_SYSERR;
1407 in = GNUNET_DISK_file_open (src, GNUNET_DISK_OPEN_READ,
1408 GNUNET_DISK_PERM_NONE);
1410 return GNUNET_SYSERR;
1412 GNUNET_DISK_file_open (dst,
1413 GNUNET_DISK_OPEN_WRITE | GNUNET_DISK_OPEN_CREATE |
1414 GNUNET_DISK_OPEN_FAILIFEXISTS,
1415 GNUNET_DISK_PERM_USER_READ |
1416 GNUNET_DISK_PERM_USER_WRITE |
1417 GNUNET_DISK_PERM_GROUP_READ |
1418 GNUNET_DISK_PERM_GROUP_WRITE);
1421 GNUNET_DISK_file_close (in);
1422 return GNUNET_SYSERR;
1424 buf = GNUNET_malloc (COPY_BLK_SIZE);
1427 len = COPY_BLK_SIZE;
1428 if (len > size - pos)
1430 sret = GNUNET_DISK_file_read (in,
1434 (len != (size_t) sret) )
1436 sret = GNUNET_DISK_file_write (out,
1440 (len != (size_t) sret) )
1445 GNUNET_DISK_file_close (in);
1446 GNUNET_DISK_file_close (out);
1450 GNUNET_DISK_file_close (in);
1451 GNUNET_DISK_file_close (out);
1452 return GNUNET_SYSERR;
1457 * @brief Removes special characters as ':' from a filename.
1458 * @param fn the filename to canonicalize
1461 GNUNET_DISK_filename_canonicalize (char *fn)
1466 for (idx = fn; *idx; idx++)
1470 if (c == '/' || c == '\\' || c == ':' ||
1471 c == '*' || c == '?' || c == '"' ||
1472 c == '<' || c == '>' || c == '|')
1482 * @brief Change owner of a file
1484 * @param filename name of file to change the owner of
1485 * @param user name of the new owner
1486 * @return #GNUNET_OK on success, #GNUNET_SYSERR on failure
1489 GNUNET_DISK_file_change_owner (const char *filename,
1495 pws = getpwnam (user);
1498 LOG (GNUNET_ERROR_TYPE_ERROR,
1499 _("Cannot obtain information about user `%s': %s\n"),
1502 return GNUNET_SYSERR;
1504 if (0 != chown (filename,
1508 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING,
1511 return GNUNET_SYSERR;
1519 * Lock a part of a file
1521 * @param fh file handle
1522 * @param lock_start absolute position from where to lock
1523 * @param lock_end absolute position until where to lock
1524 * @param excl #GNUNET_YES for an exclusive lock
1525 * @return #GNUNET_OK on success, #GNUNET_SYSERR on error
1528 GNUNET_DISK_file_lock (struct GNUNET_DISK_FileHandle *fh,
1536 return GNUNET_SYSERR;
1542 memset (&fl, 0, sizeof (struct flock));
1543 fl.l_type = excl ? F_WRLCK : F_RDLCK;
1544 fl.l_whence = SEEK_SET;
1545 fl.l_start = lock_start;
1546 fl.l_len = lock_end;
1548 return fcntl (fh->fd, F_SETLK, &fl) != 0 ? GNUNET_SYSERR : GNUNET_OK;
1551 off_t diff = lock_end - lock_start;
1552 DWORD diff_low, diff_high;
1553 diff_low = (DWORD) (diff & 0xFFFFFFFF);
1554 diff_high = (DWORD) ((diff >> (sizeof (DWORD) * 8)) & 0xFFFFFFFF);
1556 memset (&o, 0, sizeof (OVERLAPPED));
1557 o.Offset = (DWORD) (lock_start & 0xFFFFFFFF);;
1558 o.OffsetHigh = (DWORD) (((lock_start & ~0xFFFFFFFF) >> (sizeof (DWORD) * 8)) & 0xFFFFFFFF);
1561 (fh->h, (excl ? LOCKFILE_EXCLUSIVE_LOCK : 0) | LOCKFILE_FAIL_IMMEDIATELY,
1562 0, diff_low, diff_high, &o))
1564 SetErrnoFromWinError (GetLastError ());
1565 return GNUNET_SYSERR;
1574 * Unlock a part of a file
1576 * @param fh file handle
1577 * @param unlock_start absolute position from where to unlock
1578 * @param unlock_end absolute position until where to unlock
1579 * @return #GNUNET_OK on success, #GNUNET_SYSERR on error
1582 GNUNET_DISK_file_unlock (struct GNUNET_DISK_FileHandle *fh,
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_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.
1774 * @param h file handle
1775 * @return #GNUNET_OK on success, #GNUNET_SYSERR otherwise
1778 GNUNET_DISK_file_close (struct GNUNET_DISK_FileHandle *h)
1784 return GNUNET_SYSERR;
1790 if (! CloseHandle (h->h))
1792 SetErrnoFromWinError (GetLastError ());
1793 LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING, "close");
1794 ret = GNUNET_SYSERR;
1796 if (h->oOverlapRead)
1798 if (! CloseHandle (h->oOverlapRead->hEvent))
1800 SetErrnoFromWinError (GetLastError ());
1801 LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING, "close");
1802 ret = GNUNET_SYSERR;
1804 GNUNET_free (h->oOverlapRead);
1806 if (h->oOverlapWrite)
1808 if (!CloseHandle (h->oOverlapWrite->hEvent))
1810 SetErrnoFromWinError (GetLastError ());
1811 LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING, "close");
1812 ret = GNUNET_SYSERR;
1814 GNUNET_free (h->oOverlapWrite);
1817 if (close (h->fd) != 0)
1819 LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING, "close");
1820 ret = GNUNET_SYSERR;
1830 * Get a GNUnet file handle from a W32 handle.
1832 * @param handle native handle
1833 * @return GNUnet file handle corresponding to the W32 handle
1835 struct GNUNET_DISK_FileHandle *
1836 GNUNET_DISK_get_handle_from_w32_handle (HANDLE osfh)
1838 struct GNUNET_DISK_FileHandle *fh;
1840 enum GNUNET_FILE_Type ftype;
1842 dwret = GetFileType (osfh);
1845 case FILE_TYPE_DISK:
1846 ftype = GNUNET_DISK_HANLDE_TYPE_FILE;
1848 case FILE_TYPE_PIPE:
1849 ftype = GNUNET_DISK_HANLDE_TYPE_PIPE;
1851 case FILE_TYPE_UNKNOWN:
1852 if ( (GetLastError () == NO_ERROR) ||
1853 (GetLastError () == ERROR_INVALID_HANDLE) )
1855 if (0 != ResetEvent (osfh))
1856 ftype = GNUNET_DISK_HANLDE_TYPE_EVENT;
1867 fh = GNUNET_new (struct GNUNET_DISK_FileHandle);
1871 if (ftype == GNUNET_DISK_HANLDE_TYPE_PIPE)
1874 * Note that we can't make it overlapped if it isn't already.
1875 * (ReOpenFile() is only available in 2003/Vista).
1876 * The process that opened this file in the first place (usually a parent
1877 * process, if this is stdin/stdout/stderr) must make it overlapped,
1878 * otherwise we're screwed, as selecting on non-overlapped handle
1881 fh->oOverlapRead = GNUNET_new (OVERLAPPED);
1882 fh->oOverlapWrite = GNUNET_new (OVERLAPPED);
1883 fh->oOverlapRead->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
1884 fh->oOverlapWrite->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
1892 * Get a handle from a native integer FD.
1894 * @param fno native integer file descriptor
1895 * @return file handle corresponding to the descriptor, NULL on error
1897 struct GNUNET_DISK_FileHandle *
1898 GNUNET_DISK_get_handle_from_int_fd (int fno)
1900 struct GNUNET_DISK_FileHandle *fh;
1902 if ( (((off_t) -1) == lseek (fno, 0, SEEK_CUR)) &&
1904 return NULL; /* invalid FD */
1907 fh = GNUNET_new (struct GNUNET_DISK_FileHandle);
1913 osfh = _get_osfhandle (fno);
1914 if (INVALID_HANDLE_VALUE == (HANDLE) osfh)
1917 fh = GNUNET_DISK_get_handle_from_w32_handle ((HANDLE) osfh);
1925 * Get a handle from a native streaming FD.
1927 * @param fd native streaming file descriptor
1928 * @return file handle corresponding to the descriptor
1930 struct GNUNET_DISK_FileHandle *
1931 GNUNET_DISK_get_handle_from_native (FILE *fd)
1939 return GNUNET_DISK_get_handle_from_int_fd (fno);
1944 * Handle for a memory-mapping operation.
1946 struct GNUNET_DISK_MapHandle
1949 * Address where the map is in memory.
1955 * Underlying OS handle.
1960 * Number of bytes mapped.
1968 #define MAP_FAILED ((void *) -1)
1972 * Map a file into memory
1974 * @param h open file handle
1975 * @param m handle to the new mapping
1976 * @param access access specification, GNUNET_DISK_MAP_TYPE_xxx
1977 * @param len size of the mapping
1978 * @return pointer to the mapped memory region, NULL on failure
1981 GNUNET_DISK_file_map (const struct GNUNET_DISK_FileHandle *h,
1982 struct GNUNET_DISK_MapHandle **m,
1983 enum GNUNET_DISK_MapType access, size_t len)
1992 DWORD mapAccess, protect;
1994 if ((access & GNUNET_DISK_MAP_TYPE_READ) &&
1995 (access & GNUNET_DISK_MAP_TYPE_WRITE))
1997 protect = PAGE_READWRITE;
1998 mapAccess = FILE_MAP_ALL_ACCESS;
2000 else if (access & GNUNET_DISK_MAP_TYPE_READ)
2002 protect = PAGE_READONLY;
2003 mapAccess = FILE_MAP_READ;
2005 else if (access & GNUNET_DISK_MAP_TYPE_WRITE)
2007 protect = PAGE_READWRITE;
2008 mapAccess = FILE_MAP_WRITE;
2016 *m = GNUNET_new (struct GNUNET_DISK_MapHandle);
2017 (*m)->h = CreateFileMapping (h->h, NULL, protect, 0, 0, NULL);
2018 if ((*m)->h == INVALID_HANDLE_VALUE)
2020 SetErrnoFromWinError (GetLastError ());
2025 (*m)->addr = MapViewOfFile ((*m)->h, mapAccess, 0, 0, len);
2028 SetErrnoFromWinError (GetLastError ());
2029 CloseHandle ((*m)->h);
2038 if (access & GNUNET_DISK_MAP_TYPE_READ)
2040 if (access & GNUNET_DISK_MAP_TYPE_WRITE)
2042 *m = GNUNET_new (struct GNUNET_DISK_MapHandle);
2043 (*m)->addr = mmap (NULL, len, prot, MAP_SHARED, h->fd, 0);
2044 GNUNET_assert (NULL != (*m)->addr);
2045 if (MAP_FAILED == (*m)->addr)
2057 * @param h mapping handle
2058 * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
2061 GNUNET_DISK_file_unmap (struct GNUNET_DISK_MapHandle *h)
2068 return GNUNET_SYSERR;
2072 ret = UnmapViewOfFile (h->addr) ? GNUNET_OK : GNUNET_SYSERR;
2073 if (ret != GNUNET_OK)
2074 SetErrnoFromWinError (GetLastError ());
2075 if (!CloseHandle (h->h) && (ret == GNUNET_OK))
2077 ret = GNUNET_SYSERR;
2078 SetErrnoFromWinError (GetLastError ());
2081 ret = munmap (h->addr, h->len) != -1 ? GNUNET_OK : GNUNET_SYSERR;
2089 * Write file changes to disk
2090 * @param h handle to an open file
2091 * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
2094 GNUNET_DISK_file_sync (const struct GNUNET_DISK_FileHandle *h)
2099 return GNUNET_SYSERR;
2105 ret = FlushFileBuffers (h->h) ? GNUNET_OK : GNUNET_SYSERR;
2106 if (ret != GNUNET_OK)
2107 SetErrnoFromWinError (GetLastError ());
2109 #elif defined(FREEBSD) || defined(OPENBSD) || defined(DARWIN)
2110 return fsync (h->fd) == -1 ? GNUNET_SYSERR : GNUNET_OK;
2112 return fdatasync (h->fd) == -1 ? GNUNET_SYSERR : GNUNET_OK;
2119 #define PIPE_BUF 512
2121 /* Copyright Bob Byrnes <byrnes <at> curl.com>
2122 http://permalink.gmane.org/gmane.os.cygwin.patches/2121
2124 /* Create a pipe, and return handles to the read and write ends,
2125 just like CreatePipe, but ensure that the write end permits
2126 FILE_READ_ATTRIBUTES access, on later versions of win32 where
2127 this is supported. This access is needed by NtQueryInformationFile,
2128 which is used to implement select and nonblocking writes.
2129 Note that the return value is either NO_ERROR or GetLastError,
2130 unlike CreatePipe, which returns a bool for success or failure. */
2132 create_selectable_pipe (PHANDLE read_pipe_ptr, PHANDLE write_pipe_ptr,
2133 LPSECURITY_ATTRIBUTES sa_ptr, DWORD psize,
2134 DWORD dwReadMode, DWORD dwWriteMode)
2136 /* Default to error. */
2137 *read_pipe_ptr = *write_pipe_ptr = INVALID_HANDLE_VALUE;
2142 /* Ensure that there is enough pipe buffer space for atomic writes. */
2143 if (psize < PIPE_BUF)
2146 char pipename[MAX_PATH];
2148 /* Retry CreateNamedPipe as long as the pipe name is in use.
2149 * Retrying will probably never be necessary, but we want
2150 * to be as robust as possible. */
2153 static volatile LONG pipe_unique_id;
2155 snprintf (pipename, sizeof pipename, "\\\\.\\pipe\\gnunet-%d-%ld",
2156 getpid (), InterlockedIncrement ((LONG *) & pipe_unique_id));
2157 LOG (GNUNET_ERROR_TYPE_DEBUG, "CreateNamedPipe: name = %s, size = %lu\n",
2159 /* Use CreateNamedPipe instead of CreatePipe, because the latter
2160 * returns a write handle that does not permit FILE_READ_ATTRIBUTES
2161 * access, on versions of win32 earlier than WinXP SP2.
2162 * CreatePipe also stupidly creates a full duplex pipe, which is
2163 * a waste, since only a single direction is actually used.
2164 * It's important to only allow a single instance, to ensure that
2165 * the pipe was not created earlier by some other process, even if
2166 * the pid has been reused. */
2167 read_pipe = CreateNamedPipeA (pipename, PIPE_ACCESS_INBOUND | FILE_FLAG_FIRST_PIPE_INSTANCE | dwReadMode, PIPE_TYPE_BYTE | PIPE_READMODE_BYTE, 1, /* max instances */
2168 psize, /* output buffer size */
2169 psize, /* input buffer size */
2170 NMPWAIT_USE_DEFAULT_WAIT, sa_ptr);
2172 if (read_pipe != INVALID_HANDLE_VALUE)
2174 LOG (GNUNET_ERROR_TYPE_DEBUG, "pipe read handle = %p\n", read_pipe);
2178 DWORD err = GetLastError ();
2182 case ERROR_PIPE_BUSY:
2183 /* The pipe is already open with compatible parameters.
2184 * Pick a new name and retry. */
2185 LOG (GNUNET_ERROR_TYPE_DEBUG, "pipe busy, retrying\n");
2187 case ERROR_ACCESS_DENIED:
2188 /* The pipe is already open with incompatible parameters.
2189 * Pick a new name and retry. */
2190 LOG (GNUNET_ERROR_TYPE_DEBUG, "pipe access denied, retrying\n");
2192 case ERROR_CALL_NOT_IMPLEMENTED:
2193 /* We are on an older Win9x platform without named pipes.
2194 * Return an anonymous pipe as the best approximation. */
2195 LOG (GNUNET_ERROR_TYPE_DEBUG,
2196 "CreateNamedPipe not implemented, resorting to "
2197 "CreatePipe: size = %lu\n", psize);
2198 if (CreatePipe (read_pipe_ptr, write_pipe_ptr, sa_ptr, psize))
2200 LOG (GNUNET_ERROR_TYPE_DEBUG, "pipe read handle = %p, write handle = %p\n",
2205 err = GetLastError ();
2206 LOG (GNUNET_ERROR_TYPE_ERROR, "CreatePipe failed: %d\n", err);
2209 LOG (GNUNET_ERROR_TYPE_ERROR, "CreateNamedPipe failed: %d\n", err);
2214 LOG (GNUNET_ERROR_TYPE_DEBUG, "CreateFile: name = %s\n", pipename);
2216 /* Open the named pipe for writing.
2217 * Be sure to permit FILE_READ_ATTRIBUTES access. */
2218 write_pipe = CreateFileA (pipename, GENERIC_WRITE | FILE_READ_ATTRIBUTES, 0, /* share mode */
2219 sa_ptr, OPEN_EXISTING, dwWriteMode, /* flags and attributes */
2220 0); /* handle to template file */
2222 if (write_pipe == INVALID_HANDLE_VALUE)
2225 DWORD err = GetLastError ();
2227 LOG (GNUNET_ERROR_TYPE_DEBUG, "CreateFile failed: %d\n", err);
2228 CloseHandle (read_pipe);
2231 LOG (GNUNET_ERROR_TYPE_DEBUG, "pipe write handle = %p\n", write_pipe);
2233 *read_pipe_ptr = read_pipe;
2234 *write_pipe_ptr = write_pipe;
2241 * Creates an interprocess channel
2243 * @param blocking_read creates an asynchronous pipe for reading if set to GNUNET_NO
2244 * @param blocking_write creates an asynchronous pipe for writing if set to GNUNET_NO
2245 * @param inherit_read inherit the parent processes stdin (only for windows)
2246 * @param inherit_write inherit the parent processes stdout (only for windows)
2247 * @return handle to the new pipe, NULL on error
2249 struct GNUNET_DISK_PipeHandle *
2250 GNUNET_DISK_pipe (int blocking_read,
2260 (void) inherit_read;
2261 (void) inherit_write;
2266 LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR,
2271 return GNUNET_DISK_pipe_from_fd (blocking_read,
2275 struct GNUNET_DISK_PipeHandle *p;
2280 p = GNUNET_new (struct GNUNET_DISK_PipeHandle);
2281 p->fd[0] = GNUNET_new (struct GNUNET_DISK_FileHandle);
2282 p->fd[1] = GNUNET_new (struct GNUNET_DISK_FileHandle);
2284 /* All pipes are overlapped. If you want them to block - just
2285 * call WriteFile() and ReadFile() with NULL overlapped pointer.
2286 * NOTE: calling with NULL overlapped pointer works only
2287 * for pipes, and doesn't seem to be a documented feature.
2288 * It will NOT work for files, because overlapped files need
2289 * to read offsets from the overlapped structure, regardless.
2290 * Pipes are not seekable, and need no offsets, which is
2291 * probably why it works for them.
2294 create_selectable_pipe (&p->fd[0]->h, &p->fd[1]->h, NULL, 0,
2295 FILE_FLAG_OVERLAPPED,
2296 FILE_FLAG_OVERLAPPED);
2299 SetErrnoFromWinError (GetLastError ());
2301 GNUNET_free (p->fd[0]);
2302 GNUNET_free (p->fd[1]);
2307 if (!DuplicateHandle
2308 (GetCurrentProcess (), p->fd[0]->h, GetCurrentProcess (), &tmp_handle, 0,
2309 inherit_read == GNUNET_YES ? TRUE : FALSE, DUPLICATE_SAME_ACCESS))
2311 SetErrnoFromWinError (GetLastError ());
2313 CloseHandle (p->fd[0]->h);
2314 CloseHandle (p->fd[1]->h);
2315 GNUNET_free (p->fd[0]);
2316 GNUNET_free (p->fd[1]);
2321 CloseHandle (p->fd[0]->h);
2322 p->fd[0]->h = tmp_handle;
2324 if (!DuplicateHandle
2325 (GetCurrentProcess (), p->fd[1]->h, GetCurrentProcess (), &tmp_handle, 0,
2326 inherit_write == GNUNET_YES ? TRUE : FALSE, DUPLICATE_SAME_ACCESS))
2328 SetErrnoFromWinError (GetLastError ());
2330 CloseHandle (p->fd[0]->h);
2331 CloseHandle (p->fd[1]->h);
2332 GNUNET_free (p->fd[0]);
2333 GNUNET_free (p->fd[1]);
2338 CloseHandle (p->fd[1]->h);
2339 p->fd[1]->h = tmp_handle;
2341 p->fd[0]->type = GNUNET_DISK_HANLDE_TYPE_PIPE;
2342 p->fd[1]->type = GNUNET_DISK_HANLDE_TYPE_PIPE;
2344 p->fd[0]->oOverlapRead = GNUNET_new (OVERLAPPED);
2345 p->fd[0]->oOverlapWrite = GNUNET_new (OVERLAPPED);
2346 p->fd[1]->oOverlapRead = GNUNET_new (OVERLAPPED);
2347 p->fd[1]->oOverlapWrite = GNUNET_new (OVERLAPPED);
2349 p->fd[0]->oOverlapRead->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2350 p->fd[0]->oOverlapWrite->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2352 p->fd[1]->oOverlapRead->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2353 p->fd[1]->oOverlapWrite->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2361 * Creates a pipe object from a couple of file descriptors.
2362 * Useful for wrapping existing pipe FDs.
2364 * @param blocking_read creates an asynchronous pipe for reading if set to GNUNET_NO
2365 * @param blocking_write creates an asynchronous pipe for writing if set to GNUNET_NO
2366 * @param fd an array of two fd values. One of them may be -1 for read-only or write-only pipes
2368 * @return handle to the new pipe, NULL on error
2370 struct GNUNET_DISK_PipeHandle *
2371 GNUNET_DISK_pipe_from_fd (int blocking_read, int blocking_write, int fd[2])
2373 struct GNUNET_DISK_PipeHandle *p;
2375 p = GNUNET_new (struct GNUNET_DISK_PipeHandle);
2380 int eno = 0; /* make gcc happy */
2385 p->fd[0] = GNUNET_new (struct GNUNET_DISK_FileHandle);
2386 p->fd[0]->fd = fd[0];
2389 flags = fcntl (fd[0], F_GETFL);
2390 flags |= O_NONBLOCK;
2391 if (0 > fcntl (fd[0], F_SETFL, flags))
2397 flags = fcntl (fd[0], F_GETFD);
2398 flags |= FD_CLOEXEC;
2399 if (0 > fcntl (fd[0], F_SETFD, flags))
2408 p->fd[1] = GNUNET_new (struct GNUNET_DISK_FileHandle);
2409 p->fd[1]->fd = fd[1];
2410 if (!blocking_write)
2412 flags = fcntl (fd[1], F_GETFL);
2413 flags |= O_NONBLOCK;
2414 if (0 > fcntl (fd[1], F_SETFL, flags))
2420 flags = fcntl (fd[1], F_GETFD);
2421 flags |= FD_CLOEXEC;
2422 if (0 > fcntl (fd[1], F_SETFD, flags))
2431 LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR, "fcntl");
2432 if (p->fd[0]->fd >= 0)
2433 GNUNET_break (0 == close (p->fd[0]->fd));
2434 if (p->fd[1]->fd >= 0)
2435 GNUNET_break (0 == close (p->fd[1]->fd));
2436 GNUNET_free_non_null (p->fd[0]);
2437 GNUNET_free_non_null (p->fd[1]);
2445 p->fd[0] = GNUNET_new (struct GNUNET_DISK_FileHandle);
2446 p->fd[0]->h = (HANDLE) _get_osfhandle (fd[0]);
2447 if (p->fd[0]->h != INVALID_HANDLE_VALUE)
2449 p->fd[0]->type = GNUNET_DISK_HANLDE_TYPE_PIPE;
2450 p->fd[0]->oOverlapRead = GNUNET_new (OVERLAPPED);
2451 p->fd[0]->oOverlapWrite = GNUNET_new (OVERLAPPED);
2452 p->fd[0]->oOverlapRead->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2453 p->fd[0]->oOverlapWrite->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2457 GNUNET_free (p->fd[0]);
2463 p->fd[1] = GNUNET_new (struct GNUNET_DISK_FileHandle);
2464 p->fd[1]->h = (HANDLE) _get_osfhandle (fd[1]);
2465 if (p->fd[1]->h != INVALID_HANDLE_VALUE)
2467 p->fd[1]->type = GNUNET_DISK_HANLDE_TYPE_PIPE;
2468 p->fd[1]->oOverlapRead = GNUNET_new (OVERLAPPED);
2469 p->fd[1]->oOverlapWrite = GNUNET_new (OVERLAPPED);
2470 p->fd[1]->oOverlapRead->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2471 p->fd[1]->oOverlapWrite->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2475 GNUNET_free (p->fd[1]);
2486 * Closes an interprocess channel
2488 * @param p pipe to close
2489 * @param end which end of the pipe to close
2490 * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
2493 GNUNET_DISK_pipe_close_end (struct GNUNET_DISK_PipeHandle *p,
2494 enum GNUNET_DISK_PipeEnd end)
2496 int ret = GNUNET_OK;
2498 if (end == GNUNET_DISK_PIPE_END_READ)
2502 ret = GNUNET_DISK_file_close (p->fd[0]);
2506 else if (end == GNUNET_DISK_PIPE_END_WRITE)
2510 ret = GNUNET_DISK_file_close (p->fd[1]);
2519 * Detaches one of the ends from the pipe.
2520 * Detached end is a fully-functional FileHandle, it will
2521 * not be affected by anything you do with the pipe afterwards.
2522 * Each end of a pipe can only be detched from it once (i.e.
2523 * it is not duplicated).
2525 * @param p pipe to detach an end from
2526 * @param end which end of the pipe to detach
2527 * @return Detached end on success, NULL on failure
2528 * (or if that end is not present or is closed).
2530 struct GNUNET_DISK_FileHandle *
2531 GNUNET_DISK_pipe_detach_end (struct GNUNET_DISK_PipeHandle *p,
2532 enum GNUNET_DISK_PipeEnd end)
2534 struct GNUNET_DISK_FileHandle *ret = NULL;
2536 if (end == GNUNET_DISK_PIPE_END_READ)
2544 else if (end == GNUNET_DISK_PIPE_END_WRITE)
2558 * Closes an interprocess channel
2560 * @param p pipe to close
2561 * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
2564 GNUNET_DISK_pipe_close (struct GNUNET_DISK_PipeHandle *p)
2566 int ret = GNUNET_OK;
2569 int write_end_close;
2570 int read_end_close_errno;
2571 int write_end_close_errno;
2573 read_end_close = GNUNET_DISK_pipe_close_end (p, GNUNET_DISK_PIPE_END_READ);
2574 read_end_close_errno = errno;
2575 write_end_close = GNUNET_DISK_pipe_close_end (p, GNUNET_DISK_PIPE_END_WRITE);
2576 write_end_close_errno = errno;
2579 if (GNUNET_OK != read_end_close)
2581 errno = read_end_close_errno;
2582 ret = read_end_close;
2584 else if (GNUNET_OK != write_end_close)
2586 errno = write_end_close_errno;
2587 ret = write_end_close;
2595 * Get the handle to a particular pipe end
2598 * @param n end to access
2599 * @return handle for the respective end
2601 const struct GNUNET_DISK_FileHandle *
2602 GNUNET_DISK_pipe_handle (const struct GNUNET_DISK_PipeHandle *p,
2603 enum GNUNET_DISK_PipeEnd n)
2607 case GNUNET_DISK_PIPE_END_READ:
2608 case GNUNET_DISK_PIPE_END_WRITE:
2618 * Retrieve OS file handle
2620 * @param fh GNUnet file descriptor
2621 * @param dst destination buffer
2622 * @param dst_len length of dst
2623 * @return #GNUNET_OK on success, #GNUNET_SYSERR otherwise
2626 GNUNET_DISK_internal_file_handle_ (const struct GNUNET_DISK_FileHandle *fh,
2627 void *dst, size_t dst_len)
2630 return GNUNET_SYSERR;
2632 if (dst_len < sizeof (HANDLE))
2633 return GNUNET_SYSERR;
2634 *((HANDLE *) dst) = fh->h;
2636 if (dst_len < sizeof (int))
2637 return GNUNET_SYSERR;
2638 *((int *) dst) = fh->fd;