2 This file is part of GNUnet.
3 Copyright (C) 2001--2013 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", __VA_ARGS__)
33 #define LOG_STRERROR(kind,syscall) GNUNET_log_from_strerror (kind, "util", syscall)
35 #define LOG_STRERROR_FILE(kind,syscall,filename) GNUNET_log_from_strerror_file (kind, "util", 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, 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)
468 * Update POSIX permissions mask of a file on disk. If both argumets
469 * are #GNUNET_NO, the file is made world-read-write-executable (777).
470 * Does nothing on W32.
472 * @param fn name of the file to update
473 * @param require_uid_match #GNUNET_YES means 700
474 * @param require_gid_match #GNUNET_YES means 770 unless @a require_uid_match is set
477 GNUNET_DISK_fix_permissions (const char *fn,
478 int require_uid_match,
479 int require_gid_match)
487 * Update POSIX permissions mask of a file on disk. If both argumets
488 * are #GNUNET_NO, the file is made world-read-write-executable (777).
490 * @param fn name of the file to update
491 * @param require_uid_match #GNUNET_YES means 700
492 * @param require_gid_match #GNUNET_YES means 770 unless @a require_uid_match is set
495 GNUNET_DISK_fix_permissions (const char *fn,
496 int require_uid_match,
497 int require_gid_match)
501 if (GNUNET_YES == require_uid_match)
502 mode = S_IRUSR | S_IWUSR | S_IXUSR;
503 else if (GNUNET_YES == require_gid_match)
504 mode = S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IWGRP | S_IXGRP;
506 mode = S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IWGRP | S_IXGRP | S_IROTH | S_IWOTH | S_IXOTH;
507 if (0 != chmod (fn, mode))
508 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING,
516 * Create an (empty) temporary directory on disk. If the given name is not
517 * an absolute path, the current 'TMPDIR' will be prepended. In any case,
518 * 6 random characters will be appended to the name to create a unique
521 * @param t component to use for the name;
522 * does NOT contain "XXXXXX" or "/tmp/".
523 * @return NULL on error, otherwise name of fresh
524 * file on disk in directory for temporary files
527 GNUNET_DISK_mkdtemp (const char *t)
531 fn = mktemp_name (t);
532 if (fn != mkdtemp (fn))
534 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_ERROR, "mkstemp", fn);
543 * Move a file out of the way (create a backup) by
544 * renaming it to "orig.NUM~" where NUM is the smallest
545 * number that is not used yet.
547 * @param fil name of the file to back up
550 GNUNET_DISK_file_backup (const char *fil)
556 slen = strlen (fil) + 20;
557 target = GNUNET_malloc (slen);
561 GNUNET_snprintf (target, slen,
565 } while (0 == access (target, F_OK));
566 if (0 != rename (fil, target))
567 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR,
570 GNUNET_free (target);
575 * Create an (empty) temporary file on disk. If the given name is not
576 * an absolute path, the current 'TMPDIR' will be prepended. In any case,
577 * 6 random characters will be appended to the name to create a unique
580 * @param t component to use for the name;
581 * does NOT contain "XXXXXX" or "/tmp/".
582 * @return NULL on error, otherwise name of fresh
583 * file on disk in directory for temporary files
586 GNUNET_DISK_mktemp (const char *t)
591 fn = mktemp_name (t);
592 if (-1 == (fd = mkstemp (fn)))
594 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_ERROR, "mkstemp", fn);
599 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "close", fn);
605 * Test if @a fil is a directory and listable. Optionally, also check if the
606 * directory is readable. Will not print an error message if the directory does
607 * not exist. Will log errors if #GNUNET_SYSERR is returned (i.e., a file exists
608 * with the same name).
610 * @param fil filename to test
611 * @param is_readable #GNUNET_YES to additionally check if @a fil is readable;
612 * #GNUNET_NO to disable this check
613 * @return #GNUNET_YES if yes, #GNUNET_NO if not; #GNUNET_SYSERR if it
614 * does not exist or stat'ed
617 GNUNET_DISK_directory_test (const char *fil, int is_readable)
619 struct stat filestat;
622 ret = STAT (fil, &filestat);
626 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "stat", fil);
627 return GNUNET_SYSERR;
629 if (!S_ISDIR (filestat.st_mode))
631 LOG (GNUNET_ERROR_TYPE_DEBUG,
632 "A file already exits with the same name %s\n", fil);
635 if (GNUNET_YES == is_readable)
636 ret = ACCESS (fil, R_OK | X_OK);
638 ret = ACCESS (fil, X_OK);
641 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "access", fil);
649 * Check that fil corresponds to a filename
650 * (of a file that exists and that is not a directory).
652 * @param fil filename to check
653 * @return #GNUNET_YES if yes, #GNUNET_NO if not a file, #GNUNET_SYSERR if something
654 * else (will print an error message in that case, too).
657 GNUNET_DISK_file_test (const char *fil)
659 struct stat filestat;
663 rdir = GNUNET_STRINGS_filename_expand (fil);
665 return GNUNET_SYSERR;
667 ret = STAT (rdir, &filestat);
672 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "stat", rdir);
674 return GNUNET_SYSERR;
679 if (!S_ISREG (filestat.st_mode))
684 if (ACCESS (rdir, F_OK) < 0)
686 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "access", rdir);
688 return GNUNET_SYSERR;
696 * Implementation of "mkdir -p"
698 * @param dir the directory to create
699 * @return #GNUNET_OK on success, #GNUNET_SYSERR on failure
702 GNUNET_DISK_directory_create (const char *dir)
710 rdir = GNUNET_STRINGS_filename_expand (dir);
712 return GNUNET_SYSERR;
716 pos = 1; /* skip heading '/' */
718 /* Local or Network path? */
719 if (strncmp (rdir, "\\\\", 2) == 0)
724 if (rdir[pos] == '\\')
734 pos = 3; /* strlen("C:\\") */
737 /* Check which low level directories already exist */
739 rdir[len] = DIR_SEPARATOR;
742 if (DIR_SEPARATOR == rdir[pos2])
745 ret = GNUNET_DISK_directory_test (rdir, GNUNET_NO);
746 if (GNUNET_NO == ret)
749 return GNUNET_SYSERR;
751 rdir[pos2] = DIR_SEPARATOR;
752 if (GNUNET_YES == ret)
763 /* Start creating directories */
766 if ((rdir[pos] == DIR_SEPARATOR) || (pos == len))
769 ret = GNUNET_DISK_directory_test (rdir, GNUNET_NO);
770 if (GNUNET_NO == ret)
773 return GNUNET_SYSERR;
775 if (GNUNET_SYSERR == ret)
778 ret = mkdir (rdir, S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH); /* 755 */
780 wchar_t wrdir[MAX_PATH + 1];
781 if (ERROR_SUCCESS == plibc_conv_to_win_pathwconv(rdir, wrdir))
782 ret = !CreateDirectoryW (wrdir, NULL);
786 if ((ret != 0) && (errno != EEXIST))
788 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_ERROR, "mkdir", rdir);
790 return GNUNET_SYSERR;
793 rdir[pos] = DIR_SEPARATOR;
803 * Create the directory structure for storing a file.
805 * @param filename name of a file in the directory
806 * @returns #GNUNET_OK on success,
807 * #GNUNET_SYSERR on failure,
808 * #GNUNET_NO if the directory
809 * exists but is not writeable for us
812 GNUNET_DISK_directory_create_for_file (const char *filename)
819 rdir = GNUNET_STRINGS_filename_expand (filename);
823 return GNUNET_SYSERR;
826 while ((len > 0) && (rdir[len] != DIR_SEPARATOR))
829 /* The empty path is invalid and in this case refers to / */
833 rdir = GNUNET_strdup ("/");
835 ret = GNUNET_DISK_directory_create (rdir);
836 if ((GNUNET_OK == ret) && (0 != ACCESS (rdir, W_OK)))
846 * Read the contents of a binary file into a buffer.
848 * @param h handle to an open file
849 * @param result the buffer to write the result to
850 * @param len the maximum number of bytes to read
851 * @return the number of bytes read on success, #GNUNET_SYSERR on failure
854 GNUNET_DISK_file_read (const struct GNUNET_DISK_FileHandle *h,
861 return GNUNET_SYSERR;
867 if (h->type == GNUNET_DISK_HANLDE_TYPE_FILE)
869 if (!ReadFile (h->h, result, len, &bytes_read, NULL))
871 SetErrnoFromWinError (GetLastError ());
872 return GNUNET_SYSERR;
875 else if (h->type == GNUNET_DISK_HANLDE_TYPE_PIPE)
877 if (!ReadFile (h->h, result, len, &bytes_read, h->oOverlapRead))
879 if (GetLastError () != ERROR_IO_PENDING)
881 LOG (GNUNET_ERROR_TYPE_DEBUG,
882 "Error reading from pipe: %u\n",
884 SetErrnoFromWinError (GetLastError ());
885 return GNUNET_SYSERR;
887 LOG (GNUNET_ERROR_TYPE_DEBUG, "Will get overlapped result\n");
888 GetOverlappedResult (h->h, h->oOverlapRead, &bytes_read, TRUE);
890 LOG (GNUNET_ERROR_TYPE_DEBUG, "Read %u bytes from pipe\n", bytes_read);
898 return read (h->fd, result, len);
904 * Read the contents of a binary file into a buffer.
905 * Guarantees not to block (returns GNUNET_SYSERR and sets errno to EAGAIN
906 * when no data can be read).
908 * @param h handle to an open file
909 * @param result the buffer to write the result to
910 * @param len the maximum number of bytes to read
911 * @return the number of bytes read on success, #GNUNET_SYSERR on failure
914 GNUNET_DISK_file_read_non_blocking (const struct GNUNET_DISK_FileHandle *h,
921 return GNUNET_SYSERR;
927 if (h->type == GNUNET_DISK_HANLDE_TYPE_FILE)
929 if (!ReadFile (h->h, result, len, &bytes_read, NULL))
931 SetErrnoFromWinError (GetLastError ());
932 return GNUNET_SYSERR;
935 else if (h->type == GNUNET_DISK_HANLDE_TYPE_PIPE)
937 if (!ReadFile (h->h, result, len, &bytes_read, h->oOverlapRead))
939 if (GetLastError () != ERROR_IO_PENDING)
941 LOG (GNUNET_ERROR_TYPE_DEBUG, "Error reading from pipe: %u\n", GetLastError ());
942 SetErrnoFromWinError (GetLastError ());
943 return GNUNET_SYSERR;
947 LOG (GNUNET_ERROR_TYPE_DEBUG,
948 "ReadFile() queued a read, cancelling\n");
951 return GNUNET_SYSERR;
954 LOG (GNUNET_ERROR_TYPE_DEBUG,
967 /* set to non-blocking, read, then set back */
968 flags = fcntl (h->fd, F_GETFL);
969 if (0 == (flags & O_NONBLOCK))
970 (void) fcntl (h->fd, F_SETFL, flags | O_NONBLOCK);
971 ret = read (h->fd, result, len);
972 if (0 == (flags & O_NONBLOCK))
975 (void) fcntl (h->fd, F_SETFL, flags);
984 * Read the contents of a binary file into a buffer.
986 * @param fn file name
987 * @param result the buffer to write the result to
988 * @param len the maximum number of bytes to read
989 * @return number of bytes read, #GNUNET_SYSERR on failure
992 GNUNET_DISK_fn_read (const char *fn,
996 struct GNUNET_DISK_FileHandle *fh;
1000 fh = GNUNET_DISK_file_open (fn,
1001 GNUNET_DISK_OPEN_READ,
1002 GNUNET_DISK_PERM_NONE);
1004 return GNUNET_SYSERR;
1005 ret = GNUNET_DISK_file_read (fh, result, len);
1007 GNUNET_assert (GNUNET_OK == GNUNET_DISK_file_close (fh));
1014 * Write a buffer to a file.
1016 * @param h handle to open file
1017 * @param buffer the data to write
1018 * @param n number of bytes to write
1019 * @return number of bytes written on success, #GNUNET_SYSERR on error
1022 GNUNET_DISK_file_write (const struct GNUNET_DISK_FileHandle * h,
1023 const void *buffer, size_t n)
1028 return GNUNET_SYSERR;
1032 DWORD bytes_written;
1034 if (h->type == GNUNET_DISK_HANLDE_TYPE_FILE)
1036 if (!WriteFile (h->h, buffer, n, &bytes_written, NULL))
1038 SetErrnoFromWinError (GetLastError ());
1039 return GNUNET_SYSERR;
1042 else if (h->type == GNUNET_DISK_HANLDE_TYPE_PIPE)
1044 LOG (GNUNET_ERROR_TYPE_DEBUG, "It is a pipe trying to write %u bytes\n", n);
1045 if (!WriteFile (h->h, buffer, n, &bytes_written, h->oOverlapWrite))
1047 if (GetLastError () != ERROR_IO_PENDING)
1049 SetErrnoFromWinError (GetLastError ());
1050 LOG (GNUNET_ERROR_TYPE_DEBUG, "Error writing to pipe: %u\n",
1052 return GNUNET_SYSERR;
1054 LOG (GNUNET_ERROR_TYPE_DEBUG, "Will get overlapped result\n");
1055 if (!GetOverlappedResult (h->h, h->oOverlapWrite, &bytes_written, TRUE))
1057 SetErrnoFromWinError (GetLastError ());
1058 LOG (GNUNET_ERROR_TYPE_DEBUG,
1059 "Error getting overlapped result while writing to pipe: %u\n",
1061 return GNUNET_SYSERR;
1067 if (!GetOverlappedResult (h->h, h->oOverlapWrite, &ovr, TRUE))
1069 LOG (GNUNET_ERROR_TYPE_DEBUG,
1070 "Error getting control overlapped result while writing to pipe: %u\n",
1075 LOG (GNUNET_ERROR_TYPE_DEBUG,
1076 "Wrote %u bytes (ovr says %u), picking the greatest\n",
1077 bytes_written, ovr);
1080 if (bytes_written == 0)
1084 LOG (GNUNET_ERROR_TYPE_DEBUG, "Wrote %u bytes, returning -1 with EAGAIN\n", bytes_written);
1086 return GNUNET_SYSERR;
1089 LOG (GNUNET_ERROR_TYPE_DEBUG, "Wrote %u bytes\n", bytes_written);
1095 return bytes_written;
1097 return write (h->fd, buffer, n);
1103 * Write a buffer to a file, blocking, if necessary.
1105 * @param h handle to open file
1106 * @param buffer the data to write
1107 * @param n number of bytes to write
1108 * @return number of bytes written on success, #GNUNET_SYSERR on error
1111 GNUNET_DISK_file_write_blocking (const struct GNUNET_DISK_FileHandle * h,
1118 return GNUNET_SYSERR;
1122 DWORD bytes_written;
1123 /* We do a non-overlapped write, which is as blocking as it gets */
1124 LOG (GNUNET_ERROR_TYPE_DEBUG, "Writing %u bytes\n", n);
1125 if (!WriteFile (h->h, buffer, n, &bytes_written, NULL))
1127 SetErrnoFromWinError (GetLastError ());
1128 LOG (GNUNET_ERROR_TYPE_DEBUG, "Error writing to pipe: %u\n",
1130 return GNUNET_SYSERR;
1132 if (bytes_written == 0 && n > 0)
1134 LOG (GNUNET_ERROR_TYPE_DEBUG, "Waiting for pipe to clean\n");
1135 WaitForSingleObject (h->h, INFINITE);
1136 if (!WriteFile (h->h, buffer, n, &bytes_written, NULL))
1138 SetErrnoFromWinError (GetLastError ());
1139 LOG (GNUNET_ERROR_TYPE_DEBUG, "Error writing to pipe: %u\n",
1141 return GNUNET_SYSERR;
1144 LOG (GNUNET_ERROR_TYPE_DEBUG,
1147 return bytes_written;
1152 /* set to blocking, write, then set back */
1153 flags = fcntl (h->fd, F_GETFL);
1154 if (0 != (flags & O_NONBLOCK))
1155 (void) fcntl (h->fd, F_SETFL, flags - O_NONBLOCK);
1156 ret = write (h->fd, buffer, n);
1157 if (0 == (flags & O_NONBLOCK))
1158 (void) fcntl (h->fd, F_SETFL, flags);
1165 * Write a buffer to a file. If the file is longer than the
1166 * number of bytes that will be written, it will be truncated.
1168 * @param fn file name
1169 * @param buffer the data to write
1170 * @param n number of bytes to write
1171 * @param mode file permissions
1172 * @return number of bytes written on success, #GNUNET_SYSERR on error
1175 GNUNET_DISK_fn_write (const char *fn,
1178 enum GNUNET_DISK_AccessPermissions mode)
1180 struct GNUNET_DISK_FileHandle *fh;
1183 fh = GNUNET_DISK_file_open (fn,
1184 GNUNET_DISK_OPEN_WRITE | GNUNET_DISK_OPEN_TRUNCATE
1185 | GNUNET_DISK_OPEN_CREATE, mode);
1187 return GNUNET_SYSERR;
1188 ret = GNUNET_DISK_file_write (fh, buffer, n);
1189 GNUNET_assert (GNUNET_OK == GNUNET_DISK_file_close (fh));
1195 * Scan a directory for files.
1197 * @param dir_name the name of the directory
1198 * @param callback the method to call for each file,
1199 * can be NULL, in that case, we only count
1200 * @param callback_cls closure for @a callback
1201 * @return the number of files found, #GNUNET_SYSERR on error or
1202 * ieration aborted by callback returning #GNUNET_SYSERR
1205 GNUNET_DISK_directory_scan (const char *dir_name,
1206 GNUNET_FileNameCallback callback,
1210 struct dirent *finfo;
1216 unsigned int name_len;
1217 unsigned int n_size;
1219 GNUNET_assert (NULL != dir_name);
1220 dname = GNUNET_STRINGS_filename_expand (dir_name);
1222 return GNUNET_SYSERR;
1223 while ((strlen (dname) > 0) && (dname[strlen (dname) - 1] == DIR_SEPARATOR))
1224 dname[strlen (dname) - 1] = '\0';
1225 if (0 != STAT (dname, &istat))
1227 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "stat", dname);
1228 GNUNET_free (dname);
1229 return GNUNET_SYSERR;
1231 if (! S_ISDIR (istat.st_mode))
1233 LOG (GNUNET_ERROR_TYPE_WARNING,
1234 _("Expected `%s' to be a directory!\n"),
1236 GNUNET_free (dname);
1237 return GNUNET_SYSERR;
1240 dinfo = OPENDIR (dname);
1241 if ((errno == EACCES) || (NULL == dinfo))
1243 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "opendir", dname);
1246 GNUNET_free (dname);
1247 return GNUNET_SYSERR;
1250 n_size = strlen (dname) + name_len + 2;
1251 name = GNUNET_malloc (n_size);
1252 while (NULL != (finfo = READDIR (dinfo)))
1254 if ((0 == strcmp (finfo->d_name, ".")) ||
1255 (0 == strcmp (finfo->d_name, "..")))
1257 if (NULL != callback)
1259 if (name_len < strlen (finfo->d_name))
1262 name_len = strlen (finfo->d_name);
1263 n_size = strlen (dname) + name_len + 2;
1264 name = GNUNET_malloc (n_size);
1266 /* dname can end in "/" only if dname == "/";
1267 * if dname does not end in "/", we need to add
1268 * a "/" (otherwise, we must not!) */
1269 GNUNET_snprintf (name, n_size, "%s%s%s", dname,
1270 (strcmp (dname, DIR_SEPARATOR_STR) ==
1271 0) ? "" : DIR_SEPARATOR_STR, finfo->d_name);
1272 ret = callback (callback_cls, name);
1273 if (GNUNET_OK != ret)
1277 GNUNET_free (dname);
1278 if (GNUNET_NO == ret)
1280 return GNUNET_SYSERR;
1287 GNUNET_free (dname);
1293 * Function that removes the given directory by calling
1294 * "GNUNET_DISK_directory_remove".
1296 * @param unused not used
1297 * @param fn directory to remove
1298 * @return #GNUNET_OK
1301 remove_helper (void *unused, const char *fn)
1303 (void) GNUNET_DISK_directory_remove (fn);
1309 * Remove all files in a directory (rm -rf). Call with
1312 * @param filename the file to remove
1313 * @return #GNUNET_OK on success, #GNUNET_SYSERR on error
1316 GNUNET_DISK_directory_remove (const char *filename)
1320 if (NULL == filename)
1323 return GNUNET_SYSERR;
1325 if (0 != LSTAT (filename, &istat))
1326 return GNUNET_NO; /* file may not exist... */
1327 (void) CHMOD (filename, S_IWUSR | S_IRUSR | S_IXUSR);
1328 if (UNLINK (filename) == 0)
1330 if ((errno != EISDIR) &&
1331 /* EISDIR is not sufficient in all cases, e.g.
1332 * sticky /tmp directory may result in EPERM on BSD.
1333 * So we also explicitly check "isDirectory" */
1334 (GNUNET_YES != GNUNET_DISK_directory_test (filename, GNUNET_YES)))
1336 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "rmdir", filename);
1337 return GNUNET_SYSERR;
1339 if (GNUNET_SYSERR ==
1340 GNUNET_DISK_directory_scan (filename, &remove_helper, NULL))
1341 return GNUNET_SYSERR;
1342 if (0 != RMDIR (filename))
1344 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "rmdir", filename);
1345 return GNUNET_SYSERR;
1354 * @param src file to copy
1355 * @param dst destination file name
1356 * @return #GNUNET_OK on success, #GNUNET_SYSERR on error
1359 GNUNET_DISK_file_copy (const char *src,
1366 struct GNUNET_DISK_FileHandle *in;
1367 struct GNUNET_DISK_FileHandle *out;
1369 if (GNUNET_OK != GNUNET_DISK_file_size (src, &size, GNUNET_YES, GNUNET_YES))
1370 return GNUNET_SYSERR;
1372 in = GNUNET_DISK_file_open (src, GNUNET_DISK_OPEN_READ,
1373 GNUNET_DISK_PERM_NONE);
1375 return GNUNET_SYSERR;
1377 GNUNET_DISK_file_open (dst,
1378 GNUNET_DISK_OPEN_WRITE | GNUNET_DISK_OPEN_CREATE |
1379 GNUNET_DISK_OPEN_FAILIFEXISTS,
1380 GNUNET_DISK_PERM_USER_READ |
1381 GNUNET_DISK_PERM_USER_WRITE |
1382 GNUNET_DISK_PERM_GROUP_READ |
1383 GNUNET_DISK_PERM_GROUP_WRITE);
1386 GNUNET_DISK_file_close (in);
1387 return GNUNET_SYSERR;
1389 buf = GNUNET_malloc (COPY_BLK_SIZE);
1392 len = COPY_BLK_SIZE;
1393 if (len > size - pos)
1395 if (len != GNUNET_DISK_file_read (in, buf, len))
1397 if (len != GNUNET_DISK_file_write (out, buf, len))
1402 GNUNET_DISK_file_close (in);
1403 GNUNET_DISK_file_close (out);
1407 GNUNET_DISK_file_close (in);
1408 GNUNET_DISK_file_close (out);
1409 return GNUNET_SYSERR;
1414 * @brief Removes special characters as ':' from a filename.
1415 * @param fn the filename to canonicalize
1418 GNUNET_DISK_filename_canonicalize (char *fn)
1428 if (c == '/' || c == '\\' || c == ':' || c == '*' || c == '?' || c == '"' ||
1429 c == '<' || c == '>' || c == '|')
1441 * @brief Change owner of a file
1443 * @param filename name of file to change the owner of
1444 * @param user name of the new owner
1445 * @return GNUNET_OK on success, GNUNET_SYSERR on failure
1448 GNUNET_DISK_file_change_owner (const char *filename, const char *user)
1453 pws = getpwnam (user);
1456 LOG (GNUNET_ERROR_TYPE_ERROR,
1457 _("Cannot obtain information about user `%s': %s\n"), user,
1459 return GNUNET_SYSERR;
1461 if (0 != chown (filename, pws->pw_uid, pws->pw_gid))
1462 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "chown", filename);
1469 * Lock a part of a file
1470 * @param fh file handle
1471 * @param lock_start absolute position from where to lock
1472 * @param lock_end absolute position until where to lock
1473 * @param excl GNUNET_YES for an exclusive lock
1474 * @return GNUNET_OK on success, GNUNET_SYSERR on error
1477 GNUNET_DISK_file_lock (struct GNUNET_DISK_FileHandle *fh, off_t lock_start,
1478 off_t lock_end, int excl)
1483 return GNUNET_SYSERR;
1489 memset (&fl, 0, sizeof (struct flock));
1490 fl.l_type = excl ? F_WRLCK : F_RDLCK;
1491 fl.l_whence = SEEK_SET;
1492 fl.l_start = lock_start;
1493 fl.l_len = lock_end;
1495 return fcntl (fh->fd, F_SETLK, &fl) != 0 ? GNUNET_SYSERR : GNUNET_OK;
1498 off_t diff = lock_end - lock_start;
1499 DWORD diff_low, diff_high;
1500 diff_low = (DWORD) (diff & 0xFFFFFFFF);
1501 diff_high = (DWORD) ((diff >> (sizeof (DWORD) * 8)) & 0xFFFFFFFF);
1503 memset (&o, 0, sizeof (OVERLAPPED));
1504 o.Offset = (DWORD) (lock_start & 0xFFFFFFFF);;
1505 o.OffsetHigh = (DWORD) (((lock_start & ~0xFFFFFFFF) >> (sizeof (DWORD) * 8)) & 0xFFFFFFFF);
1508 (fh->h, (excl ? LOCKFILE_EXCLUSIVE_LOCK : 0) | LOCKFILE_FAIL_IMMEDIATELY,
1509 0, diff_low, diff_high, &o))
1511 SetErrnoFromWinError (GetLastError ());
1512 return GNUNET_SYSERR;
1521 * Unlock a part of a file
1522 * @param fh file handle
1523 * @param unlock_start absolute position from where to unlock
1524 * @param unlock_end absolute position until where to unlock
1525 * @return #GNUNET_OK on success, #GNUNET_SYSERR on error
1528 GNUNET_DISK_file_unlock (struct GNUNET_DISK_FileHandle *fh, off_t unlock_start,
1534 return GNUNET_SYSERR;
1540 memset (&fl, 0, sizeof (struct flock));
1541 fl.l_type = F_UNLCK;
1542 fl.l_whence = SEEK_SET;
1543 fl.l_start = unlock_start;
1544 fl.l_len = unlock_end;
1546 return fcntl (fh->fd, F_SETLK, &fl) != 0 ? GNUNET_SYSERR : GNUNET_OK;
1549 off_t diff = unlock_end - unlock_start;
1550 DWORD diff_low, diff_high;
1551 diff_low = (DWORD) (diff & 0xFFFFFFFF);
1552 diff_high = (DWORD) ((diff >> (sizeof (DWORD) * 8)) & 0xFFFFFFFF);
1554 memset (&o, 0, sizeof (OVERLAPPED));
1555 o.Offset = (DWORD) (unlock_start & 0xFFFFFFFF);;
1556 o.OffsetHigh = (DWORD) (((unlock_start & ~0xFFFFFFFF) >> (sizeof (DWORD) * 8)) & 0xFFFFFFFF);
1558 if (!UnlockFileEx (fh->h, 0, diff_low, diff_high, &o))
1560 SetErrnoFromWinError (GetLastError ());
1561 return GNUNET_SYSERR;
1570 * Open a file. Note that the access permissions will only be
1571 * used if a new file is created and if the underlying operating
1572 * system supports the given permissions.
1574 * @param fn file name to be opened
1575 * @param flags opening flags, a combination of GNUNET_DISK_OPEN_xxx bit flags
1576 * @param perm permissions for the newly created file, use
1577 * #GNUNET_DISK_PERM_NONE if a file could not be created by this
1578 * call (because of flags)
1579 * @return IO handle on success, NULL on error
1581 struct GNUNET_DISK_FileHandle *
1582 GNUNET_DISK_file_open (const char *fn,
1583 enum GNUNET_DISK_OpenFlags flags,
1584 enum GNUNET_DISK_AccessPermissions perm)
1587 struct GNUNET_DISK_FileHandle *ret;
1593 wchar_t wexpfn[MAX_PATH + 1];
1600 expfn = GNUNET_STRINGS_filename_expand (fn);
1605 if (GNUNET_DISK_OPEN_READWRITE == (flags & GNUNET_DISK_OPEN_READWRITE))
1606 oflags = O_RDWR; /* note: O_RDWR is NOT always O_RDONLY | O_WRONLY */
1607 else if (flags & GNUNET_DISK_OPEN_READ)
1609 else if (flags & GNUNET_DISK_OPEN_WRITE)
1614 GNUNET_free (expfn);
1617 if (flags & GNUNET_DISK_OPEN_FAILIFEXISTS)
1618 oflags |= (O_CREAT | O_EXCL);
1619 if (flags & GNUNET_DISK_OPEN_TRUNCATE)
1621 if (flags & GNUNET_DISK_OPEN_APPEND)
1623 if (flags & GNUNET_DISK_OPEN_CREATE)
1625 (void) GNUNET_DISK_directory_create_for_file (expfn);
1627 mode = translate_unix_perms (perm);
1630 fd = open (expfn, oflags
1634 | O_LARGEFILE, mode);
1637 if (0 == (flags & GNUNET_DISK_OPEN_FAILIFEXISTS))
1638 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "open", expfn);
1640 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_DEBUG, "open", expfn);
1641 GNUNET_free (expfn);
1648 if (GNUNET_DISK_OPEN_READWRITE == (flags & GNUNET_DISK_OPEN_READWRITE))
1649 access = FILE_READ_DATA | FILE_WRITE_DATA;
1650 else if (flags & GNUNET_DISK_OPEN_READ)
1651 access = FILE_READ_DATA;
1652 else if (flags & GNUNET_DISK_OPEN_WRITE)
1653 access = FILE_WRITE_DATA;
1655 if (flags & GNUNET_DISK_OPEN_FAILIFEXISTS)
1659 else if (flags & GNUNET_DISK_OPEN_CREATE)
1661 (void) GNUNET_DISK_directory_create_for_file (expfn);
1662 if (flags & GNUNET_DISK_OPEN_TRUNCATE)
1663 disp = CREATE_ALWAYS;
1667 else if (flags & GNUNET_DISK_OPEN_TRUNCATE)
1669 disp = TRUNCATE_EXISTING;
1673 disp = OPEN_EXISTING;
1676 if (ERROR_SUCCESS == plibc_conv_to_win_pathwconv(expfn, wexpfn))
1677 h = CreateFileW (wexpfn, access,
1678 FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE, NULL,
1679 disp, FILE_ATTRIBUTE_NORMAL, NULL);
1681 h = INVALID_HANDLE_VALUE;
1682 if (h == INVALID_HANDLE_VALUE)
1685 SetErrnoFromWinError (GetLastError ());
1687 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_INFO, "open", expfn);
1688 GNUNET_free (expfn);
1693 if (flags & GNUNET_DISK_OPEN_APPEND)
1694 if (SetFilePointer (h, 0, 0, FILE_END) == INVALID_SET_FILE_POINTER)
1696 SetErrnoFromWinError (GetLastError ());
1697 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "SetFilePointer", expfn);
1699 GNUNET_free (expfn);
1704 ret = GNUNET_new (struct GNUNET_DISK_FileHandle);
1707 ret->type = GNUNET_DISK_HANLDE_TYPE_FILE;
1711 GNUNET_free (expfn);
1717 * Close an open file
1718 * @param h file handle
1719 * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
1722 GNUNET_DISK_file_close (struct GNUNET_DISK_FileHandle *h)
1728 return GNUNET_SYSERR;
1734 if (!CloseHandle (h->h))
1736 SetErrnoFromWinError (GetLastError ());
1737 LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING, "close");
1738 ret = GNUNET_SYSERR;
1740 if (h->oOverlapRead)
1742 if (!CloseHandle (h->oOverlapRead->hEvent))
1744 SetErrnoFromWinError (GetLastError ());
1745 LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING, "close");
1746 ret = GNUNET_SYSERR;
1748 GNUNET_free (h->oOverlapRead);
1750 if (h->oOverlapWrite)
1752 if (!CloseHandle (h->oOverlapWrite->hEvent))
1754 SetErrnoFromWinError (GetLastError ());
1755 LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING, "close");
1756 ret = GNUNET_SYSERR;
1758 GNUNET_free (h->oOverlapWrite);
1761 if (close (h->fd) != 0)
1763 LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING, "close");
1764 ret = GNUNET_SYSERR;
1773 * Get a GNUnet file handle from a W32 handle.
1775 * @param handle native handle
1776 * @return GNUnet file handle corresponding to the W32 handle
1778 struct GNUNET_DISK_FileHandle *
1779 GNUNET_DISK_get_handle_from_w32_handle (HANDLE osfh)
1781 struct GNUNET_DISK_FileHandle *fh;
1784 enum GNUNET_FILE_Type ftype;
1786 dwret = GetFileType (osfh);
1789 case FILE_TYPE_DISK:
1790 ftype = GNUNET_DISK_HANLDE_TYPE_FILE;
1792 case FILE_TYPE_PIPE:
1793 ftype = GNUNET_DISK_HANLDE_TYPE_PIPE;
1795 case FILE_TYPE_UNKNOWN:
1796 if (GetLastError () == NO_ERROR || GetLastError () == ERROR_INVALID_HANDLE)
1798 if (0 != ResetEvent (osfh))
1799 ftype = GNUNET_DISK_HANLDE_TYPE_EVENT;
1808 fh = GNUNET_new (struct GNUNET_DISK_FileHandle);
1812 if (ftype == GNUNET_DISK_HANLDE_TYPE_PIPE)
1815 * Note that we can't make it overlapped if it isn't already.
1816 * (ReOpenFile() is only available in 2003/Vista).
1817 * The process that opened this file in the first place (usually a parent
1818 * process, if this is stdin/stdout/stderr) must make it overlapped,
1819 * otherwise we're screwed, as selecting on non-overlapped handle
1822 fh->oOverlapRead = GNUNET_new (OVERLAPPED);
1823 fh->oOverlapWrite = GNUNET_new (OVERLAPPED);
1824 fh->oOverlapRead->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
1825 fh->oOverlapWrite->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
1833 * Get a handle from a native integer FD.
1835 * @param fno native integer file descriptor
1836 * @return file handle corresponding to the descriptor, NULL on error
1838 struct GNUNET_DISK_FileHandle *
1839 GNUNET_DISK_get_handle_from_int_fd (int fno)
1841 struct GNUNET_DISK_FileHandle *fh;
1843 if ( (((off_t) -1) == lseek (fno, 0, SEEK_CUR)) &&
1845 return NULL; /* invalid FD */
1848 fh = GNUNET_new (struct GNUNET_DISK_FileHandle);
1854 osfh = _get_osfhandle (fno);
1855 if (INVALID_HANDLE_VALUE == (HANDLE) osfh)
1858 fh = GNUNET_DISK_get_handle_from_w32_handle ((HANDLE) osfh);
1866 * Get a handle from a native streaming FD.
1868 * @param fd native streaming file descriptor
1869 * @return file handle corresponding to the descriptor
1871 struct GNUNET_DISK_FileHandle *
1872 GNUNET_DISK_get_handle_from_native (FILE *fd)
1880 return GNUNET_DISK_get_handle_from_int_fd (fno);
1885 * Handle for a memory-mapping operation.
1887 struct GNUNET_DISK_MapHandle
1890 * Address where the map is in memory.
1896 * Underlying OS handle.
1901 * Number of bytes mapped.
1909 #define MAP_FAILED ((void *) -1)
1913 * Map a file into memory
1915 * @param h open file handle
1916 * @param m handle to the new mapping
1917 * @param access access specification, GNUNET_DISK_MAP_TYPE_xxx
1918 * @param len size of the mapping
1919 * @return pointer to the mapped memory region, NULL on failure
1922 GNUNET_DISK_file_map (const struct GNUNET_DISK_FileHandle *h,
1923 struct GNUNET_DISK_MapHandle **m,
1924 enum GNUNET_DISK_MapType access, size_t len)
1933 DWORD mapAccess, protect;
1935 if ((access & GNUNET_DISK_MAP_TYPE_READ) &&
1936 (access & GNUNET_DISK_MAP_TYPE_WRITE))
1938 protect = PAGE_READWRITE;
1939 mapAccess = FILE_MAP_ALL_ACCESS;
1941 else if (access & GNUNET_DISK_MAP_TYPE_READ)
1943 protect = PAGE_READONLY;
1944 mapAccess = FILE_MAP_READ;
1946 else if (access & GNUNET_DISK_MAP_TYPE_WRITE)
1948 protect = PAGE_READWRITE;
1949 mapAccess = FILE_MAP_WRITE;
1957 *m = GNUNET_new (struct GNUNET_DISK_MapHandle);
1958 (*m)->h = CreateFileMapping (h->h, NULL, protect, 0, 0, NULL);
1959 if ((*m)->h == INVALID_HANDLE_VALUE)
1961 SetErrnoFromWinError (GetLastError ());
1966 (*m)->addr = MapViewOfFile ((*m)->h, mapAccess, 0, 0, len);
1969 SetErrnoFromWinError (GetLastError ());
1970 CloseHandle ((*m)->h);
1979 if (access & GNUNET_DISK_MAP_TYPE_READ)
1981 if (access & GNUNET_DISK_MAP_TYPE_WRITE)
1983 *m = GNUNET_new (struct GNUNET_DISK_MapHandle);
1984 (*m)->addr = mmap (NULL, len, prot, MAP_SHARED, h->fd, 0);
1985 GNUNET_assert (NULL != (*m)->addr);
1986 if (MAP_FAILED == (*m)->addr)
1998 * @param h mapping handle
1999 * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
2002 GNUNET_DISK_file_unmap (struct GNUNET_DISK_MapHandle *h)
2009 return GNUNET_SYSERR;
2013 ret = UnmapViewOfFile (h->addr) ? GNUNET_OK : GNUNET_SYSERR;
2014 if (ret != GNUNET_OK)
2015 SetErrnoFromWinError (GetLastError ());
2016 if (!CloseHandle (h->h) && (ret == GNUNET_OK))
2018 ret = GNUNET_SYSERR;
2019 SetErrnoFromWinError (GetLastError ());
2022 ret = munmap (h->addr, h->len) != -1 ? GNUNET_OK : GNUNET_SYSERR;
2030 * Write file changes to disk
2031 * @param h handle to an open file
2032 * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
2035 GNUNET_DISK_file_sync (const struct GNUNET_DISK_FileHandle *h)
2040 return GNUNET_SYSERR;
2046 ret = FlushFileBuffers (h->h) ? GNUNET_OK : GNUNET_SYSERR;
2047 if (ret != GNUNET_OK)
2048 SetErrnoFromWinError (GetLastError ());
2050 #elif defined(FREEBSD) || defined(OPENBSD) || defined(DARWIN)
2051 return fsync (h->fd) == -1 ? GNUNET_SYSERR : GNUNET_OK;
2053 return fdatasync (h->fd) == -1 ? GNUNET_SYSERR : GNUNET_OK;
2060 #define PIPE_BUF 512
2062 /* Copyright Bob Byrnes <byrnes <at> curl.com>
2063 http://permalink.gmane.org/gmane.os.cygwin.patches/2121
2065 /* Create a pipe, and return handles to the read and write ends,
2066 just like CreatePipe, but ensure that the write end permits
2067 FILE_READ_ATTRIBUTES access, on later versions of win32 where
2068 this is supported. This access is needed by NtQueryInformationFile,
2069 which is used to implement select and nonblocking writes.
2070 Note that the return value is either NO_ERROR or GetLastError,
2071 unlike CreatePipe, which returns a bool for success or failure. */
2073 create_selectable_pipe (PHANDLE read_pipe_ptr, PHANDLE write_pipe_ptr,
2074 LPSECURITY_ATTRIBUTES sa_ptr, DWORD psize,
2075 DWORD dwReadMode, DWORD dwWriteMode)
2077 /* Default to error. */
2078 *read_pipe_ptr = *write_pipe_ptr = INVALID_HANDLE_VALUE;
2083 /* Ensure that there is enough pipe buffer space for atomic writes. */
2084 if (psize < PIPE_BUF)
2087 char pipename[MAX_PATH];
2089 /* Retry CreateNamedPipe as long as the pipe name is in use.
2090 * Retrying will probably never be necessary, but we want
2091 * to be as robust as possible. */
2094 static volatile LONG pipe_unique_id;
2096 snprintf (pipename, sizeof pipename, "\\\\.\\pipe\\gnunet-%d-%ld",
2097 getpid (), InterlockedIncrement ((LONG *) & pipe_unique_id));
2098 LOG (GNUNET_ERROR_TYPE_DEBUG, "CreateNamedPipe: name = %s, size = %lu\n",
2100 /* Use CreateNamedPipe instead of CreatePipe, because the latter
2101 * returns a write handle that does not permit FILE_READ_ATTRIBUTES
2102 * access, on versions of win32 earlier than WinXP SP2.
2103 * CreatePipe also stupidly creates a full duplex pipe, which is
2104 * a waste, since only a single direction is actually used.
2105 * It's important to only allow a single instance, to ensure that
2106 * the pipe was not created earlier by some other process, even if
2107 * the pid has been reused. */
2108 read_pipe = CreateNamedPipeA (pipename, PIPE_ACCESS_INBOUND | FILE_FLAG_FIRST_PIPE_INSTANCE | dwReadMode, PIPE_TYPE_BYTE | PIPE_READMODE_BYTE, 1, /* max instances */
2109 psize, /* output buffer size */
2110 psize, /* input buffer size */
2111 NMPWAIT_USE_DEFAULT_WAIT, sa_ptr);
2113 if (read_pipe != INVALID_HANDLE_VALUE)
2115 LOG (GNUNET_ERROR_TYPE_DEBUG, "pipe read handle = %p\n", read_pipe);
2119 DWORD err = GetLastError ();
2123 case ERROR_PIPE_BUSY:
2124 /* The pipe is already open with compatible parameters.
2125 * Pick a new name and retry. */
2126 LOG (GNUNET_ERROR_TYPE_DEBUG, "pipe busy, retrying\n");
2128 case ERROR_ACCESS_DENIED:
2129 /* The pipe is already open with incompatible parameters.
2130 * Pick a new name and retry. */
2131 LOG (GNUNET_ERROR_TYPE_DEBUG, "pipe access denied, retrying\n");
2133 case ERROR_CALL_NOT_IMPLEMENTED:
2134 /* We are on an older Win9x platform without named pipes.
2135 * Return an anonymous pipe as the best approximation. */
2136 LOG (GNUNET_ERROR_TYPE_DEBUG,
2137 "CreateNamedPipe not implemented, resorting to "
2138 "CreatePipe: size = %lu\n", psize);
2139 if (CreatePipe (read_pipe_ptr, write_pipe_ptr, sa_ptr, psize))
2141 LOG (GNUNET_ERROR_TYPE_DEBUG, "pipe read handle = %p, write handle = %p\n",
2146 err = GetLastError ();
2147 LOG (GNUNET_ERROR_TYPE_ERROR, "CreatePipe failed: %d\n", err);
2150 LOG (GNUNET_ERROR_TYPE_ERROR, "CreateNamedPipe failed: %d\n", err);
2155 LOG (GNUNET_ERROR_TYPE_DEBUG, "CreateFile: name = %s\n", pipename);
2157 /* Open the named pipe for writing.
2158 * Be sure to permit FILE_READ_ATTRIBUTES access. */
2159 write_pipe = CreateFileA (pipename, GENERIC_WRITE | FILE_READ_ATTRIBUTES, 0, /* share mode */
2160 sa_ptr, OPEN_EXISTING, dwWriteMode, /* flags and attributes */
2161 0); /* handle to template file */
2163 if (write_pipe == INVALID_HANDLE_VALUE)
2166 DWORD err = GetLastError ();
2168 LOG (GNUNET_ERROR_TYPE_DEBUG, "CreateFile failed: %d\n", err);
2169 CloseHandle (read_pipe);
2172 LOG (GNUNET_ERROR_TYPE_DEBUG, "pipe write handle = %p\n", write_pipe);
2174 *read_pipe_ptr = read_pipe;
2175 *write_pipe_ptr = write_pipe;
2182 * Creates an interprocess channel
2184 * @param blocking_read creates an asynchronous pipe for reading if set to GNUNET_NO
2185 * @param blocking_write creates an asynchronous pipe for writing if set to GNUNET_NO
2186 * @param inherit_read inherit the parent processes stdin (only for windows)
2187 * @param inherit_write inherit the parent processes stdout (only for windows)
2188 * @return handle to the new pipe, NULL on error
2190 struct GNUNET_DISK_PipeHandle *
2191 GNUNET_DISK_pipe (int blocking_read, int blocking_write, int inherit_read, int inherit_write)
2202 LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR, "pipe");
2206 return GNUNET_DISK_pipe_from_fd (blocking_read,
2210 struct GNUNET_DISK_PipeHandle *p;
2215 p = GNUNET_new (struct GNUNET_DISK_PipeHandle);
2216 p->fd[0] = GNUNET_new (struct GNUNET_DISK_FileHandle);
2217 p->fd[1] = GNUNET_new (struct GNUNET_DISK_FileHandle);
2219 /* All pipes are overlapped. If you want them to block - just
2220 * call WriteFile() and ReadFile() with NULL overlapped pointer.
2221 * NOTE: calling with NULL overlapped pointer works only
2222 * for pipes, and doesn't seem to be a documented feature.
2223 * It will NOT work for files, because overlapped files need
2224 * to read offsets from the overlapped structure, regardless.
2225 * Pipes are not seekable, and need no offsets, which is
2226 * probably why it works for them.
2229 create_selectable_pipe (&p->fd[0]->h, &p->fd[1]->h, NULL, 0,
2230 FILE_FLAG_OVERLAPPED,
2231 FILE_FLAG_OVERLAPPED);
2234 SetErrnoFromWinError (GetLastError ());
2236 GNUNET_free (p->fd[0]);
2237 GNUNET_free (p->fd[1]);
2242 if (!DuplicateHandle
2243 (GetCurrentProcess (), p->fd[0]->h, GetCurrentProcess (), &tmp_handle, 0,
2244 inherit_read == GNUNET_YES ? TRUE : FALSE, DUPLICATE_SAME_ACCESS))
2246 SetErrnoFromWinError (GetLastError ());
2248 CloseHandle (p->fd[0]->h);
2249 CloseHandle (p->fd[1]->h);
2250 GNUNET_free (p->fd[0]);
2251 GNUNET_free (p->fd[1]);
2256 CloseHandle (p->fd[0]->h);
2257 p->fd[0]->h = tmp_handle;
2259 if (!DuplicateHandle
2260 (GetCurrentProcess (), p->fd[1]->h, GetCurrentProcess (), &tmp_handle, 0,
2261 inherit_write == GNUNET_YES ? TRUE : FALSE, DUPLICATE_SAME_ACCESS))
2263 SetErrnoFromWinError (GetLastError ());
2265 CloseHandle (p->fd[0]->h);
2266 CloseHandle (p->fd[1]->h);
2267 GNUNET_free (p->fd[0]);
2268 GNUNET_free (p->fd[1]);
2273 CloseHandle (p->fd[1]->h);
2274 p->fd[1]->h = tmp_handle;
2276 p->fd[0]->type = GNUNET_DISK_HANLDE_TYPE_PIPE;
2277 p->fd[1]->type = GNUNET_DISK_HANLDE_TYPE_PIPE;
2279 p->fd[0]->oOverlapRead = GNUNET_new (OVERLAPPED);
2280 p->fd[0]->oOverlapWrite = GNUNET_new (OVERLAPPED);
2281 p->fd[1]->oOverlapRead = GNUNET_new (OVERLAPPED);
2282 p->fd[1]->oOverlapWrite = GNUNET_new (OVERLAPPED);
2284 p->fd[0]->oOverlapRead->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2285 p->fd[0]->oOverlapWrite->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2287 p->fd[1]->oOverlapRead->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2288 p->fd[1]->oOverlapWrite->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2296 * Creates a pipe object from a couple of file descriptors.
2297 * Useful for wrapping existing pipe FDs.
2299 * @param blocking_read creates an asynchronous pipe for reading if set to GNUNET_NO
2300 * @param blocking_write creates an asynchronous pipe for writing if set to GNUNET_NO
2301 * @param fd an array of two fd values. One of them may be -1 for read-only or write-only pipes
2303 * @return handle to the new pipe, NULL on error
2305 struct GNUNET_DISK_PipeHandle *
2306 GNUNET_DISK_pipe_from_fd (int blocking_read, int blocking_write, int fd[2])
2308 struct GNUNET_DISK_PipeHandle *p;
2310 p = GNUNET_new (struct GNUNET_DISK_PipeHandle);
2315 int eno = 0; /* make gcc happy */
2320 p->fd[0] = GNUNET_new (struct GNUNET_DISK_FileHandle);
2321 p->fd[0]->fd = fd[0];
2324 flags = fcntl (fd[0], F_GETFL);
2325 flags |= O_NONBLOCK;
2326 if (0 > fcntl (fd[0], F_SETFL, flags))
2332 flags = fcntl (fd[0], F_GETFD);
2333 flags |= FD_CLOEXEC;
2334 if (0 > fcntl (fd[0], F_SETFD, flags))
2343 p->fd[1] = GNUNET_new (struct GNUNET_DISK_FileHandle);
2344 p->fd[1]->fd = fd[1];
2345 if (!blocking_write)
2347 flags = fcntl (fd[1], F_GETFL);
2348 flags |= O_NONBLOCK;
2349 if (0 > fcntl (fd[1], F_SETFL, flags))
2355 flags = fcntl (fd[1], F_GETFD);
2356 flags |= FD_CLOEXEC;
2357 if (0 > fcntl (fd[1], F_SETFD, flags))
2366 LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR, "fcntl");
2367 if (p->fd[0]->fd >= 0)
2368 GNUNET_break (0 == close (p->fd[0]->fd));
2369 if (p->fd[1]->fd >= 0)
2370 GNUNET_break (0 == close (p->fd[1]->fd));
2371 GNUNET_free_non_null (p->fd[0]);
2372 GNUNET_free_non_null (p->fd[1]);
2380 p->fd[0] = GNUNET_new (struct GNUNET_DISK_FileHandle);
2381 p->fd[0]->h = (HANDLE) _get_osfhandle (fd[0]);
2382 if (p->fd[0]->h != INVALID_HANDLE_VALUE)
2384 p->fd[0]->type = GNUNET_DISK_HANLDE_TYPE_PIPE;
2385 p->fd[0]->oOverlapRead = GNUNET_new (OVERLAPPED);
2386 p->fd[0]->oOverlapWrite = GNUNET_new (OVERLAPPED);
2387 p->fd[0]->oOverlapRead->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2388 p->fd[0]->oOverlapWrite->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2392 GNUNET_free (p->fd[0]);
2398 p->fd[1] = GNUNET_new (struct GNUNET_DISK_FileHandle);
2399 p->fd[1]->h = (HANDLE) _get_osfhandle (fd[1]);
2400 if (p->fd[1]->h != INVALID_HANDLE_VALUE)
2402 p->fd[1]->type = GNUNET_DISK_HANLDE_TYPE_PIPE;
2403 p->fd[1]->oOverlapRead = GNUNET_new (OVERLAPPED);
2404 p->fd[1]->oOverlapWrite = GNUNET_new (OVERLAPPED);
2405 p->fd[1]->oOverlapRead->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2406 p->fd[1]->oOverlapWrite->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2410 GNUNET_free (p->fd[1]);
2421 * Closes an interprocess channel
2423 * @param p pipe to close
2424 * @param end which end of the pipe to close
2425 * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
2428 GNUNET_DISK_pipe_close_end (struct GNUNET_DISK_PipeHandle *p,
2429 enum GNUNET_DISK_PipeEnd end)
2431 int ret = GNUNET_OK;
2433 if (end == GNUNET_DISK_PIPE_END_READ)
2437 ret = GNUNET_DISK_file_close (p->fd[0]);
2441 else if (end == GNUNET_DISK_PIPE_END_WRITE)
2445 ret = GNUNET_DISK_file_close (p->fd[1]);
2454 * Detaches one of the ends from the pipe.
2455 * Detached end is a fully-functional FileHandle, it will
2456 * not be affected by anything you do with the pipe afterwards.
2457 * Each end of a pipe can only be detched from it once (i.e.
2458 * it is not duplicated).
2460 * @param p pipe to detach an end from
2461 * @param end which end of the pipe to detach
2462 * @return Detached end on success, NULL on failure
2463 * (or if that end is not present or is closed).
2465 struct GNUNET_DISK_FileHandle *
2466 GNUNET_DISK_pipe_detach_end (struct GNUNET_DISK_PipeHandle *p,
2467 enum GNUNET_DISK_PipeEnd end)
2469 struct GNUNET_DISK_FileHandle *ret = NULL;
2471 if (end == GNUNET_DISK_PIPE_END_READ)
2479 else if (end == GNUNET_DISK_PIPE_END_WRITE)
2493 * Closes an interprocess channel
2495 * @param p pipe to close
2496 * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
2499 GNUNET_DISK_pipe_close (struct GNUNET_DISK_PipeHandle *p)
2501 int ret = GNUNET_OK;
2504 int write_end_close;
2505 int read_end_close_errno;
2506 int write_end_close_errno;
2508 read_end_close = GNUNET_DISK_pipe_close_end (p, GNUNET_DISK_PIPE_END_READ);
2509 read_end_close_errno = errno;
2510 write_end_close = GNUNET_DISK_pipe_close_end (p, GNUNET_DISK_PIPE_END_WRITE);
2511 write_end_close_errno = errno;
2514 if (GNUNET_OK != read_end_close)
2516 errno = read_end_close_errno;
2517 ret = read_end_close;
2519 else if (GNUNET_OK != write_end_close)
2521 errno = write_end_close_errno;
2522 ret = write_end_close;
2530 * Get the handle to a particular pipe end
2533 * @param n end to access
2534 * @return handle for the respective end
2536 const struct GNUNET_DISK_FileHandle *
2537 GNUNET_DISK_pipe_handle (const struct GNUNET_DISK_PipeHandle *p,
2538 enum GNUNET_DISK_PipeEnd n)
2542 case GNUNET_DISK_PIPE_END_READ:
2543 case GNUNET_DISK_PIPE_END_WRITE:
2553 * Retrieve OS file handle
2555 * @param fh GNUnet file descriptor
2556 * @param dst destination buffer
2557 * @param dst_len length of dst
2558 * @return #GNUNET_OK on success, #GNUNET_SYSERR otherwise
2561 GNUNET_DISK_internal_file_handle_ (const struct GNUNET_DISK_FileHandle *fh,
2562 void *dst, size_t dst_len)
2565 return GNUNET_SYSERR;
2567 if (dst_len < sizeof (HANDLE))
2568 return GNUNET_SYSERR;
2569 *((HANDLE *) dst) = fh->h;
2571 if (dst_len < sizeof (int))
2572 return GNUNET_SYSERR;
2573 *((int *) dst) = fh->fd;