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)
818 rdir = GNUNET_STRINGS_filename_expand (filename);
820 return GNUNET_SYSERR;
822 while ((len > 0) && (rdir[len] != DIR_SEPARATOR))
825 /* The empty path is invalid and in this case refers to / */
828 rdir = GNUNET_strdup ("/");
830 ret = GNUNET_DISK_directory_create (rdir);
831 if ((ret == GNUNET_OK) && (0 != ACCESS (rdir, W_OK)))
839 * Read the contents of a binary file into a buffer.
841 * @param h handle to an open file
842 * @param result the buffer to write the result to
843 * @param len the maximum number of bytes to read
844 * @return the number of bytes read on success, #GNUNET_SYSERR on failure
847 GNUNET_DISK_file_read (const struct GNUNET_DISK_FileHandle *h,
854 return GNUNET_SYSERR;
860 if (h->type == GNUNET_DISK_HANLDE_TYPE_FILE)
862 if (!ReadFile (h->h, result, len, &bytes_read, NULL))
864 SetErrnoFromWinError (GetLastError ());
865 return GNUNET_SYSERR;
868 else if (h->type == GNUNET_DISK_HANLDE_TYPE_PIPE)
870 if (!ReadFile (h->h, result, len, &bytes_read, h->oOverlapRead))
872 if (GetLastError () != ERROR_IO_PENDING)
874 LOG (GNUNET_ERROR_TYPE_DEBUG,
875 "Error reading from pipe: %u\n",
877 SetErrnoFromWinError (GetLastError ());
878 return GNUNET_SYSERR;
880 LOG (GNUNET_ERROR_TYPE_DEBUG, "Will get overlapped result\n");
881 GetOverlappedResult (h->h, h->oOverlapRead, &bytes_read, TRUE);
883 LOG (GNUNET_ERROR_TYPE_DEBUG, "Read %u bytes from pipe\n", bytes_read);
891 return read (h->fd, result, len);
897 * Read the contents of a binary file into a buffer.
898 * Guarantees not to block (returns GNUNET_SYSERR and sets errno to EAGAIN
899 * when no data can be read).
901 * @param h handle to an open file
902 * @param result the buffer to write the result to
903 * @param len the maximum number of bytes to read
904 * @return the number of bytes read on success, #GNUNET_SYSERR on failure
907 GNUNET_DISK_file_read_non_blocking (const struct GNUNET_DISK_FileHandle *h,
914 return GNUNET_SYSERR;
920 if (h->type == GNUNET_DISK_HANLDE_TYPE_FILE)
922 if (!ReadFile (h->h, result, len, &bytes_read, NULL))
924 SetErrnoFromWinError (GetLastError ());
925 return GNUNET_SYSERR;
928 else if (h->type == GNUNET_DISK_HANLDE_TYPE_PIPE)
930 if (!ReadFile (h->h, result, len, &bytes_read, h->oOverlapRead))
932 if (GetLastError () != ERROR_IO_PENDING)
934 LOG (GNUNET_ERROR_TYPE_DEBUG, "Error reading from pipe: %u\n", GetLastError ());
935 SetErrnoFromWinError (GetLastError ());
936 return GNUNET_SYSERR;
940 LOG (GNUNET_ERROR_TYPE_DEBUG,
941 "ReadFile() queued a read, cancelling\n");
944 return GNUNET_SYSERR;
947 LOG (GNUNET_ERROR_TYPE_DEBUG,
960 /* set to non-blocking, read, then set back */
961 flags = fcntl (h->fd, F_GETFL);
962 if (0 == (flags & O_NONBLOCK))
963 (void) fcntl (h->fd, F_SETFL, flags | O_NONBLOCK);
964 ret = read (h->fd, result, len);
965 if (0 == (flags & O_NONBLOCK))
968 (void) fcntl (h->fd, F_SETFL, flags);
977 * Read the contents of a binary file into a buffer.
979 * @param fn file name
980 * @param result the buffer to write the result to
981 * @param len the maximum number of bytes to read
982 * @return number of bytes read, #GNUNET_SYSERR on failure
985 GNUNET_DISK_fn_read (const char *fn,
989 struct GNUNET_DISK_FileHandle *fh;
993 fh = GNUNET_DISK_file_open (fn,
994 GNUNET_DISK_OPEN_READ,
995 GNUNET_DISK_PERM_NONE);
997 return GNUNET_SYSERR;
998 ret = GNUNET_DISK_file_read (fh, result, len);
1000 GNUNET_assert (GNUNET_OK == GNUNET_DISK_file_close (fh));
1007 * Write a buffer to a file.
1009 * @param h handle to open file
1010 * @param buffer the data to write
1011 * @param n number of bytes to write
1012 * @return number of bytes written on success, #GNUNET_SYSERR on error
1015 GNUNET_DISK_file_write (const struct GNUNET_DISK_FileHandle * h,
1016 const void *buffer, size_t n)
1021 return GNUNET_SYSERR;
1025 DWORD bytes_written;
1027 if (h->type == GNUNET_DISK_HANLDE_TYPE_FILE)
1029 if (!WriteFile (h->h, buffer, n, &bytes_written, NULL))
1031 SetErrnoFromWinError (GetLastError ());
1032 return GNUNET_SYSERR;
1035 else if (h->type == GNUNET_DISK_HANLDE_TYPE_PIPE)
1037 LOG (GNUNET_ERROR_TYPE_DEBUG, "It is a pipe trying to write %u bytes\n", n);
1038 if (!WriteFile (h->h, buffer, n, &bytes_written, h->oOverlapWrite))
1040 if (GetLastError () != ERROR_IO_PENDING)
1042 SetErrnoFromWinError (GetLastError ());
1043 LOG (GNUNET_ERROR_TYPE_DEBUG, "Error writing to pipe: %u\n",
1045 return GNUNET_SYSERR;
1047 LOG (GNUNET_ERROR_TYPE_DEBUG, "Will get overlapped result\n");
1048 if (!GetOverlappedResult (h->h, h->oOverlapWrite, &bytes_written, TRUE))
1050 SetErrnoFromWinError (GetLastError ());
1051 LOG (GNUNET_ERROR_TYPE_DEBUG,
1052 "Error getting overlapped result while writing to pipe: %u\n",
1054 return GNUNET_SYSERR;
1060 if (!GetOverlappedResult (h->h, h->oOverlapWrite, &ovr, TRUE))
1062 LOG (GNUNET_ERROR_TYPE_DEBUG,
1063 "Error getting control overlapped result while writing to pipe: %u\n",
1068 LOG (GNUNET_ERROR_TYPE_DEBUG,
1069 "Wrote %u bytes (ovr says %u), picking the greatest\n",
1070 bytes_written, ovr);
1073 if (bytes_written == 0)
1077 LOG (GNUNET_ERROR_TYPE_DEBUG, "Wrote %u bytes, returning -1 with EAGAIN\n", bytes_written);
1079 return GNUNET_SYSERR;
1082 LOG (GNUNET_ERROR_TYPE_DEBUG, "Wrote %u bytes\n", bytes_written);
1088 return bytes_written;
1090 return write (h->fd, buffer, n);
1096 * Write a buffer to a file, blocking, if necessary.
1098 * @param h handle to open file
1099 * @param buffer the data to write
1100 * @param n number of bytes to write
1101 * @return number of bytes written on success, #GNUNET_SYSERR on error
1104 GNUNET_DISK_file_write_blocking (const struct GNUNET_DISK_FileHandle * h,
1111 return GNUNET_SYSERR;
1115 DWORD bytes_written;
1116 /* We do a non-overlapped write, which is as blocking as it gets */
1117 LOG (GNUNET_ERROR_TYPE_DEBUG, "Writing %u bytes\n", n);
1118 if (!WriteFile (h->h, buffer, n, &bytes_written, NULL))
1120 SetErrnoFromWinError (GetLastError ());
1121 LOG (GNUNET_ERROR_TYPE_DEBUG, "Error writing to pipe: %u\n",
1123 return GNUNET_SYSERR;
1125 if (bytes_written == 0 && n > 0)
1127 LOG (GNUNET_ERROR_TYPE_DEBUG, "Waiting for pipe to clean\n");
1128 WaitForSingleObject (h->h, INFINITE);
1129 if (!WriteFile (h->h, buffer, n, &bytes_written, NULL))
1131 SetErrnoFromWinError (GetLastError ());
1132 LOG (GNUNET_ERROR_TYPE_DEBUG, "Error writing to pipe: %u\n",
1134 return GNUNET_SYSERR;
1137 LOG (GNUNET_ERROR_TYPE_DEBUG,
1140 return bytes_written;
1145 /* set to blocking, write, then set back */
1146 flags = fcntl (h->fd, F_GETFL);
1147 if (0 != (flags & O_NONBLOCK))
1148 (void) fcntl (h->fd, F_SETFL, flags - O_NONBLOCK);
1149 ret = write (h->fd, buffer, n);
1150 if (0 == (flags & O_NONBLOCK))
1151 (void) fcntl (h->fd, F_SETFL, flags);
1158 * Write a buffer to a file. If the file is longer than the
1159 * number of bytes that will be written, it will be truncated.
1161 * @param fn file name
1162 * @param buffer the data to write
1163 * @param n number of bytes to write
1164 * @param mode file permissions
1165 * @return number of bytes written on success, #GNUNET_SYSERR on error
1168 GNUNET_DISK_fn_write (const char *fn,
1171 enum GNUNET_DISK_AccessPermissions mode)
1173 struct GNUNET_DISK_FileHandle *fh;
1176 fh = GNUNET_DISK_file_open (fn,
1177 GNUNET_DISK_OPEN_WRITE | GNUNET_DISK_OPEN_TRUNCATE
1178 | GNUNET_DISK_OPEN_CREATE, mode);
1180 return GNUNET_SYSERR;
1181 ret = GNUNET_DISK_file_write (fh, buffer, n);
1182 GNUNET_assert (GNUNET_OK == GNUNET_DISK_file_close (fh));
1188 * Scan a directory for files.
1190 * @param dir_name the name of the directory
1191 * @param callback the method to call for each file,
1192 * can be NULL, in that case, we only count
1193 * @param callback_cls closure for @a callback
1194 * @return the number of files found, #GNUNET_SYSERR on error or
1195 * ieration aborted by callback returning #GNUNET_SYSERR
1198 GNUNET_DISK_directory_scan (const char *dir_name,
1199 GNUNET_FileNameCallback callback,
1203 struct dirent *finfo;
1209 unsigned int name_len;
1210 unsigned int n_size;
1212 GNUNET_assert (NULL != dir_name);
1213 dname = GNUNET_STRINGS_filename_expand (dir_name);
1215 return GNUNET_SYSERR;
1216 while ((strlen (dname) > 0) && (dname[strlen (dname) - 1] == DIR_SEPARATOR))
1217 dname[strlen (dname) - 1] = '\0';
1218 if (0 != STAT (dname, &istat))
1220 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "stat", dname);
1221 GNUNET_free (dname);
1222 return GNUNET_SYSERR;
1224 if (! S_ISDIR (istat.st_mode))
1226 LOG (GNUNET_ERROR_TYPE_WARNING,
1227 _("Expected `%s' to be a directory!\n"),
1229 GNUNET_free (dname);
1230 return GNUNET_SYSERR;
1233 dinfo = OPENDIR (dname);
1234 if ((errno == EACCES) || (NULL == dinfo))
1236 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "opendir", dname);
1239 GNUNET_free (dname);
1240 return GNUNET_SYSERR;
1243 n_size = strlen (dname) + name_len + 2;
1244 name = GNUNET_malloc (n_size);
1245 while (NULL != (finfo = READDIR (dinfo)))
1247 if ((0 == strcmp (finfo->d_name, ".")) ||
1248 (0 == strcmp (finfo->d_name, "..")))
1250 if (NULL != callback)
1252 if (name_len < strlen (finfo->d_name))
1255 name_len = strlen (finfo->d_name);
1256 n_size = strlen (dname) + name_len + 2;
1257 name = GNUNET_malloc (n_size);
1259 /* dname can end in "/" only if dname == "/";
1260 * if dname does not end in "/", we need to add
1261 * a "/" (otherwise, we must not!) */
1262 GNUNET_snprintf (name, n_size, "%s%s%s", dname,
1263 (strcmp (dname, DIR_SEPARATOR_STR) ==
1264 0) ? "" : DIR_SEPARATOR_STR, finfo->d_name);
1265 ret = callback (callback_cls, name);
1266 if (GNUNET_OK != ret)
1270 GNUNET_free (dname);
1271 if (GNUNET_NO == ret)
1273 return GNUNET_SYSERR;
1280 GNUNET_free (dname);
1286 * Function that removes the given directory by calling
1287 * "GNUNET_DISK_directory_remove".
1289 * @param unused not used
1290 * @param fn directory to remove
1291 * @return #GNUNET_OK
1294 remove_helper (void *unused, const char *fn)
1296 (void) GNUNET_DISK_directory_remove (fn);
1302 * Remove all files in a directory (rm -rf). Call with
1305 * @param filename the file to remove
1306 * @return #GNUNET_OK on success, #GNUNET_SYSERR on error
1309 GNUNET_DISK_directory_remove (const char *filename)
1313 if (NULL == filename)
1316 return GNUNET_SYSERR;
1318 if (0 != LSTAT (filename, &istat))
1319 return GNUNET_NO; /* file may not exist... */
1320 (void) CHMOD (filename, S_IWUSR | S_IRUSR | S_IXUSR);
1321 if (UNLINK (filename) == 0)
1323 if ((errno != EISDIR) &&
1324 /* EISDIR is not sufficient in all cases, e.g.
1325 * sticky /tmp directory may result in EPERM on BSD.
1326 * So we also explicitly check "isDirectory" */
1327 (GNUNET_YES != GNUNET_DISK_directory_test (filename, GNUNET_YES)))
1329 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "rmdir", filename);
1330 return GNUNET_SYSERR;
1332 if (GNUNET_SYSERR ==
1333 GNUNET_DISK_directory_scan (filename, &remove_helper, NULL))
1334 return GNUNET_SYSERR;
1335 if (0 != RMDIR (filename))
1337 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "rmdir", filename);
1338 return GNUNET_SYSERR;
1347 * @param src file to copy
1348 * @param dst destination file name
1349 * @return #GNUNET_OK on success, #GNUNET_SYSERR on error
1352 GNUNET_DISK_file_copy (const char *src,
1359 struct GNUNET_DISK_FileHandle *in;
1360 struct GNUNET_DISK_FileHandle *out;
1362 if (GNUNET_OK != GNUNET_DISK_file_size (src, &size, GNUNET_YES, GNUNET_YES))
1363 return GNUNET_SYSERR;
1365 in = GNUNET_DISK_file_open (src, GNUNET_DISK_OPEN_READ,
1366 GNUNET_DISK_PERM_NONE);
1368 return GNUNET_SYSERR;
1370 GNUNET_DISK_file_open (dst,
1371 GNUNET_DISK_OPEN_WRITE | GNUNET_DISK_OPEN_CREATE |
1372 GNUNET_DISK_OPEN_FAILIFEXISTS,
1373 GNUNET_DISK_PERM_USER_READ |
1374 GNUNET_DISK_PERM_USER_WRITE |
1375 GNUNET_DISK_PERM_GROUP_READ |
1376 GNUNET_DISK_PERM_GROUP_WRITE);
1379 GNUNET_DISK_file_close (in);
1380 return GNUNET_SYSERR;
1382 buf = GNUNET_malloc (COPY_BLK_SIZE);
1385 len = COPY_BLK_SIZE;
1386 if (len > size - pos)
1388 if (len != GNUNET_DISK_file_read (in, buf, len))
1390 if (len != GNUNET_DISK_file_write (out, buf, len))
1395 GNUNET_DISK_file_close (in);
1396 GNUNET_DISK_file_close (out);
1400 GNUNET_DISK_file_close (in);
1401 GNUNET_DISK_file_close (out);
1402 return GNUNET_SYSERR;
1407 * @brief Removes special characters as ':' from a filename.
1408 * @param fn the filename to canonicalize
1411 GNUNET_DISK_filename_canonicalize (char *fn)
1421 if (c == '/' || c == '\\' || c == ':' || c == '*' || c == '?' || c == '"' ||
1422 c == '<' || c == '>' || c == '|')
1434 * @brief Change owner of a file
1436 * @param filename name of file to change the owner of
1437 * @param user name of the new owner
1438 * @return GNUNET_OK on success, GNUNET_SYSERR on failure
1441 GNUNET_DISK_file_change_owner (const char *filename, const char *user)
1446 pws = getpwnam (user);
1449 LOG (GNUNET_ERROR_TYPE_ERROR,
1450 _("Cannot obtain information about user `%s': %s\n"), user,
1452 return GNUNET_SYSERR;
1454 if (0 != chown (filename, pws->pw_uid, pws->pw_gid))
1455 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "chown", filename);
1462 * Lock a part of a file
1463 * @param fh file handle
1464 * @param lock_start absolute position from where to lock
1465 * @param lock_end absolute position until where to lock
1466 * @param excl GNUNET_YES for an exclusive lock
1467 * @return GNUNET_OK on success, GNUNET_SYSERR on error
1470 GNUNET_DISK_file_lock (struct GNUNET_DISK_FileHandle *fh, off_t lock_start,
1471 off_t lock_end, int excl)
1476 return GNUNET_SYSERR;
1482 memset (&fl, 0, sizeof (struct flock));
1483 fl.l_type = excl ? F_WRLCK : F_RDLCK;
1484 fl.l_whence = SEEK_SET;
1485 fl.l_start = lock_start;
1486 fl.l_len = lock_end;
1488 return fcntl (fh->fd, F_SETLK, &fl) != 0 ? GNUNET_SYSERR : GNUNET_OK;
1491 off_t diff = lock_end - lock_start;
1492 DWORD diff_low, diff_high;
1493 diff_low = (DWORD) (diff & 0xFFFFFFFF);
1494 diff_high = (DWORD) ((diff >> (sizeof (DWORD) * 8)) & 0xFFFFFFFF);
1496 memset (&o, 0, sizeof (OVERLAPPED));
1497 o.Offset = (DWORD) (lock_start & 0xFFFFFFFF);;
1498 o.OffsetHigh = (DWORD) (((lock_start & ~0xFFFFFFFF) >> (sizeof (DWORD) * 8)) & 0xFFFFFFFF);
1501 (fh->h, (excl ? LOCKFILE_EXCLUSIVE_LOCK : 0) | LOCKFILE_FAIL_IMMEDIATELY,
1502 0, diff_low, diff_high, &o))
1504 SetErrnoFromWinError (GetLastError ());
1505 return GNUNET_SYSERR;
1514 * Unlock a part of a file
1515 * @param fh file handle
1516 * @param unlock_start absolute position from where to unlock
1517 * @param unlock_end absolute position until where to unlock
1518 * @return #GNUNET_OK on success, #GNUNET_SYSERR on error
1521 GNUNET_DISK_file_unlock (struct GNUNET_DISK_FileHandle *fh, off_t unlock_start,
1527 return GNUNET_SYSERR;
1533 memset (&fl, 0, sizeof (struct flock));
1534 fl.l_type = F_UNLCK;
1535 fl.l_whence = SEEK_SET;
1536 fl.l_start = unlock_start;
1537 fl.l_len = unlock_end;
1539 return fcntl (fh->fd, F_SETLK, &fl) != 0 ? GNUNET_SYSERR : GNUNET_OK;
1542 off_t diff = unlock_end - unlock_start;
1543 DWORD diff_low, diff_high;
1544 diff_low = (DWORD) (diff & 0xFFFFFFFF);
1545 diff_high = (DWORD) ((diff >> (sizeof (DWORD) * 8)) & 0xFFFFFFFF);
1547 memset (&o, 0, sizeof (OVERLAPPED));
1548 o.Offset = (DWORD) (unlock_start & 0xFFFFFFFF);;
1549 o.OffsetHigh = (DWORD) (((unlock_start & ~0xFFFFFFFF) >> (sizeof (DWORD) * 8)) & 0xFFFFFFFF);
1551 if (!UnlockFileEx (fh->h, 0, diff_low, diff_high, &o))
1553 SetErrnoFromWinError (GetLastError ());
1554 return GNUNET_SYSERR;
1563 * Open a file. Note that the access permissions will only be
1564 * used if a new file is created and if the underlying operating
1565 * system supports the given permissions.
1567 * @param fn file name to be opened
1568 * @param flags opening flags, a combination of GNUNET_DISK_OPEN_xxx bit flags
1569 * @param perm permissions for the newly created file, use
1570 * #GNUNET_DISK_PERM_NONE if a file could not be created by this
1571 * call (because of flags)
1572 * @return IO handle on success, NULL on error
1574 struct GNUNET_DISK_FileHandle *
1575 GNUNET_DISK_file_open (const char *fn,
1576 enum GNUNET_DISK_OpenFlags flags,
1577 enum GNUNET_DISK_AccessPermissions perm)
1580 struct GNUNET_DISK_FileHandle *ret;
1586 wchar_t wexpfn[MAX_PATH + 1];
1593 expfn = GNUNET_STRINGS_filename_expand (fn);
1598 if (GNUNET_DISK_OPEN_READWRITE == (flags & GNUNET_DISK_OPEN_READWRITE))
1599 oflags = O_RDWR; /* note: O_RDWR is NOT always O_RDONLY | O_WRONLY */
1600 else if (flags & GNUNET_DISK_OPEN_READ)
1602 else if (flags & GNUNET_DISK_OPEN_WRITE)
1607 GNUNET_free (expfn);
1610 if (flags & GNUNET_DISK_OPEN_FAILIFEXISTS)
1611 oflags |= (O_CREAT | O_EXCL);
1612 if (flags & GNUNET_DISK_OPEN_TRUNCATE)
1614 if (flags & GNUNET_DISK_OPEN_APPEND)
1616 if (flags & GNUNET_DISK_OPEN_CREATE)
1618 (void) GNUNET_DISK_directory_create_for_file (expfn);
1620 mode = translate_unix_perms (perm);
1623 fd = open (expfn, oflags
1627 | O_LARGEFILE, mode);
1630 if (0 == (flags & GNUNET_DISK_OPEN_FAILIFEXISTS))
1631 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "open", expfn);
1633 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_DEBUG, "open", expfn);
1634 GNUNET_free (expfn);
1641 if (GNUNET_DISK_OPEN_READWRITE == (flags & GNUNET_DISK_OPEN_READWRITE))
1642 access = FILE_READ_DATA | FILE_WRITE_DATA;
1643 else if (flags & GNUNET_DISK_OPEN_READ)
1644 access = FILE_READ_DATA;
1645 else if (flags & GNUNET_DISK_OPEN_WRITE)
1646 access = FILE_WRITE_DATA;
1648 if (flags & GNUNET_DISK_OPEN_FAILIFEXISTS)
1652 else if (flags & GNUNET_DISK_OPEN_CREATE)
1654 (void) GNUNET_DISK_directory_create_for_file (expfn);
1655 if (flags & GNUNET_DISK_OPEN_TRUNCATE)
1656 disp = CREATE_ALWAYS;
1660 else if (flags & GNUNET_DISK_OPEN_TRUNCATE)
1662 disp = TRUNCATE_EXISTING;
1666 disp = OPEN_EXISTING;
1669 if (ERROR_SUCCESS == plibc_conv_to_win_pathwconv(expfn, wexpfn))
1670 h = CreateFileW (wexpfn, access,
1671 FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE, NULL,
1672 disp, FILE_ATTRIBUTE_NORMAL, NULL);
1674 h = INVALID_HANDLE_VALUE;
1675 if (h == INVALID_HANDLE_VALUE)
1678 SetErrnoFromWinError (GetLastError ());
1680 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_INFO, "open", expfn);
1681 GNUNET_free (expfn);
1686 if (flags & GNUNET_DISK_OPEN_APPEND)
1687 if (SetFilePointer (h, 0, 0, FILE_END) == INVALID_SET_FILE_POINTER)
1689 SetErrnoFromWinError (GetLastError ());
1690 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "SetFilePointer", expfn);
1692 GNUNET_free (expfn);
1697 ret = GNUNET_new (struct GNUNET_DISK_FileHandle);
1700 ret->type = GNUNET_DISK_HANLDE_TYPE_FILE;
1704 GNUNET_free (expfn);
1710 * Close an open file
1711 * @param h file handle
1712 * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
1715 GNUNET_DISK_file_close (struct GNUNET_DISK_FileHandle *h)
1721 return GNUNET_SYSERR;
1727 if (!CloseHandle (h->h))
1729 SetErrnoFromWinError (GetLastError ());
1730 LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING, "close");
1731 ret = GNUNET_SYSERR;
1733 if (h->oOverlapRead)
1735 if (!CloseHandle (h->oOverlapRead->hEvent))
1737 SetErrnoFromWinError (GetLastError ());
1738 LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING, "close");
1739 ret = GNUNET_SYSERR;
1741 GNUNET_free (h->oOverlapRead);
1743 if (h->oOverlapWrite)
1745 if (!CloseHandle (h->oOverlapWrite->hEvent))
1747 SetErrnoFromWinError (GetLastError ());
1748 LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING, "close");
1749 ret = GNUNET_SYSERR;
1751 GNUNET_free (h->oOverlapWrite);
1754 if (close (h->fd) != 0)
1756 LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING, "close");
1757 ret = GNUNET_SYSERR;
1766 * Get a GNUnet file handle from a W32 handle.
1768 * @param handle native handle
1769 * @return GNUnet file handle corresponding to the W32 handle
1771 struct GNUNET_DISK_FileHandle *
1772 GNUNET_DISK_get_handle_from_w32_handle (HANDLE osfh)
1774 struct GNUNET_DISK_FileHandle *fh;
1777 enum GNUNET_FILE_Type ftype;
1779 dwret = GetFileType (osfh);
1782 case FILE_TYPE_DISK:
1783 ftype = GNUNET_DISK_HANLDE_TYPE_FILE;
1785 case FILE_TYPE_PIPE:
1786 ftype = GNUNET_DISK_HANLDE_TYPE_PIPE;
1788 case FILE_TYPE_UNKNOWN:
1789 if (GetLastError () == NO_ERROR || GetLastError () == ERROR_INVALID_HANDLE)
1791 if (0 != ResetEvent (osfh))
1792 ftype = GNUNET_DISK_HANLDE_TYPE_EVENT;
1801 fh = GNUNET_new (struct GNUNET_DISK_FileHandle);
1805 if (ftype == GNUNET_DISK_HANLDE_TYPE_PIPE)
1808 * Note that we can't make it overlapped if it isn't already.
1809 * (ReOpenFile() is only available in 2003/Vista).
1810 * The process that opened this file in the first place (usually a parent
1811 * process, if this is stdin/stdout/stderr) must make it overlapped,
1812 * otherwise we're screwed, as selecting on non-overlapped handle
1815 fh->oOverlapRead = GNUNET_new (OVERLAPPED);
1816 fh->oOverlapWrite = GNUNET_new (OVERLAPPED);
1817 fh->oOverlapRead->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
1818 fh->oOverlapWrite->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
1826 * Get a handle from a native integer FD.
1828 * @param fno native integer file descriptor
1829 * @return file handle corresponding to the descriptor, NULL on error
1831 struct GNUNET_DISK_FileHandle *
1832 GNUNET_DISK_get_handle_from_int_fd (int fno)
1834 struct GNUNET_DISK_FileHandle *fh;
1836 if ( (((off_t) -1) == lseek (fno, 0, SEEK_CUR)) &&
1838 return NULL; /* invalid FD */
1841 fh = GNUNET_new (struct GNUNET_DISK_FileHandle);
1847 osfh = _get_osfhandle (fno);
1848 if (INVALID_HANDLE_VALUE == (HANDLE) osfh)
1851 fh = GNUNET_DISK_get_handle_from_w32_handle ((HANDLE) osfh);
1859 * Get a handle from a native streaming FD.
1861 * @param fd native streaming file descriptor
1862 * @return file handle corresponding to the descriptor
1864 struct GNUNET_DISK_FileHandle *
1865 GNUNET_DISK_get_handle_from_native (FILE *fd)
1873 return GNUNET_DISK_get_handle_from_int_fd (fno);
1878 * Handle for a memory-mapping operation.
1880 struct GNUNET_DISK_MapHandle
1883 * Address where the map is in memory.
1889 * Underlying OS handle.
1894 * Number of bytes mapped.
1902 #define MAP_FAILED ((void *) -1)
1906 * Map a file into memory
1908 * @param h open file handle
1909 * @param m handle to the new mapping
1910 * @param access access specification, GNUNET_DISK_MAP_TYPE_xxx
1911 * @param len size of the mapping
1912 * @return pointer to the mapped memory region, NULL on failure
1915 GNUNET_DISK_file_map (const struct GNUNET_DISK_FileHandle *h,
1916 struct GNUNET_DISK_MapHandle **m,
1917 enum GNUNET_DISK_MapType access, size_t len)
1926 DWORD mapAccess, protect;
1928 if ((access & GNUNET_DISK_MAP_TYPE_READ) &&
1929 (access & GNUNET_DISK_MAP_TYPE_WRITE))
1931 protect = PAGE_READWRITE;
1932 mapAccess = FILE_MAP_ALL_ACCESS;
1934 else if (access & GNUNET_DISK_MAP_TYPE_READ)
1936 protect = PAGE_READONLY;
1937 mapAccess = FILE_MAP_READ;
1939 else if (access & GNUNET_DISK_MAP_TYPE_WRITE)
1941 protect = PAGE_READWRITE;
1942 mapAccess = FILE_MAP_WRITE;
1950 *m = GNUNET_new (struct GNUNET_DISK_MapHandle);
1951 (*m)->h = CreateFileMapping (h->h, NULL, protect, 0, 0, NULL);
1952 if ((*m)->h == INVALID_HANDLE_VALUE)
1954 SetErrnoFromWinError (GetLastError ());
1959 (*m)->addr = MapViewOfFile ((*m)->h, mapAccess, 0, 0, len);
1962 SetErrnoFromWinError (GetLastError ());
1963 CloseHandle ((*m)->h);
1972 if (access & GNUNET_DISK_MAP_TYPE_READ)
1974 if (access & GNUNET_DISK_MAP_TYPE_WRITE)
1976 *m = GNUNET_new (struct GNUNET_DISK_MapHandle);
1977 (*m)->addr = mmap (NULL, len, prot, MAP_SHARED, h->fd, 0);
1978 GNUNET_assert (NULL != (*m)->addr);
1979 if (MAP_FAILED == (*m)->addr)
1991 * @param h mapping handle
1992 * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
1995 GNUNET_DISK_file_unmap (struct GNUNET_DISK_MapHandle *h)
2002 return GNUNET_SYSERR;
2006 ret = UnmapViewOfFile (h->addr) ? GNUNET_OK : GNUNET_SYSERR;
2007 if (ret != GNUNET_OK)
2008 SetErrnoFromWinError (GetLastError ());
2009 if (!CloseHandle (h->h) && (ret == GNUNET_OK))
2011 ret = GNUNET_SYSERR;
2012 SetErrnoFromWinError (GetLastError ());
2015 ret = munmap (h->addr, h->len) != -1 ? GNUNET_OK : GNUNET_SYSERR;
2023 * Write file changes to disk
2024 * @param h handle to an open file
2025 * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
2028 GNUNET_DISK_file_sync (const struct GNUNET_DISK_FileHandle *h)
2033 return GNUNET_SYSERR;
2039 ret = FlushFileBuffers (h->h) ? GNUNET_OK : GNUNET_SYSERR;
2040 if (ret != GNUNET_OK)
2041 SetErrnoFromWinError (GetLastError ());
2043 #elif defined(FREEBSD) || defined(OPENBSD) || defined(DARWIN)
2044 return fsync (h->fd) == -1 ? GNUNET_SYSERR : GNUNET_OK;
2046 return fdatasync (h->fd) == -1 ? GNUNET_SYSERR : GNUNET_OK;
2053 #define PIPE_BUF 512
2055 /* Copyright Bob Byrnes <byrnes <at> curl.com>
2056 http://permalink.gmane.org/gmane.os.cygwin.patches/2121
2058 /* Create a pipe, and return handles to the read and write ends,
2059 just like CreatePipe, but ensure that the write end permits
2060 FILE_READ_ATTRIBUTES access, on later versions of win32 where
2061 this is supported. This access is needed by NtQueryInformationFile,
2062 which is used to implement select and nonblocking writes.
2063 Note that the return value is either NO_ERROR or GetLastError,
2064 unlike CreatePipe, which returns a bool for success or failure. */
2066 create_selectable_pipe (PHANDLE read_pipe_ptr, PHANDLE write_pipe_ptr,
2067 LPSECURITY_ATTRIBUTES sa_ptr, DWORD psize,
2068 DWORD dwReadMode, DWORD dwWriteMode)
2070 /* Default to error. */
2071 *read_pipe_ptr = *write_pipe_ptr = INVALID_HANDLE_VALUE;
2076 /* Ensure that there is enough pipe buffer space for atomic writes. */
2077 if (psize < PIPE_BUF)
2080 char pipename[MAX_PATH];
2082 /* Retry CreateNamedPipe as long as the pipe name is in use.
2083 * Retrying will probably never be necessary, but we want
2084 * to be as robust as possible. */
2087 static volatile LONG pipe_unique_id;
2089 snprintf (pipename, sizeof pipename, "\\\\.\\pipe\\gnunet-%d-%ld",
2090 getpid (), InterlockedIncrement ((LONG *) & pipe_unique_id));
2091 LOG (GNUNET_ERROR_TYPE_DEBUG, "CreateNamedPipe: name = %s, size = %lu\n",
2093 /* Use CreateNamedPipe instead of CreatePipe, because the latter
2094 * returns a write handle that does not permit FILE_READ_ATTRIBUTES
2095 * access, on versions of win32 earlier than WinXP SP2.
2096 * CreatePipe also stupidly creates a full duplex pipe, which is
2097 * a waste, since only a single direction is actually used.
2098 * It's important to only allow a single instance, to ensure that
2099 * the pipe was not created earlier by some other process, even if
2100 * the pid has been reused. */
2101 read_pipe = CreateNamedPipeA (pipename, PIPE_ACCESS_INBOUND | FILE_FLAG_FIRST_PIPE_INSTANCE | dwReadMode, PIPE_TYPE_BYTE | PIPE_READMODE_BYTE, 1, /* max instances */
2102 psize, /* output buffer size */
2103 psize, /* input buffer size */
2104 NMPWAIT_USE_DEFAULT_WAIT, sa_ptr);
2106 if (read_pipe != INVALID_HANDLE_VALUE)
2108 LOG (GNUNET_ERROR_TYPE_DEBUG, "pipe read handle = %p\n", read_pipe);
2112 DWORD err = GetLastError ();
2116 case ERROR_PIPE_BUSY:
2117 /* The pipe is already open with compatible parameters.
2118 * Pick a new name and retry. */
2119 LOG (GNUNET_ERROR_TYPE_DEBUG, "pipe busy, retrying\n");
2121 case ERROR_ACCESS_DENIED:
2122 /* The pipe is already open with incompatible parameters.
2123 * Pick a new name and retry. */
2124 LOG (GNUNET_ERROR_TYPE_DEBUG, "pipe access denied, retrying\n");
2126 case ERROR_CALL_NOT_IMPLEMENTED:
2127 /* We are on an older Win9x platform without named pipes.
2128 * Return an anonymous pipe as the best approximation. */
2129 LOG (GNUNET_ERROR_TYPE_DEBUG,
2130 "CreateNamedPipe not implemented, resorting to "
2131 "CreatePipe: size = %lu\n", psize);
2132 if (CreatePipe (read_pipe_ptr, write_pipe_ptr, sa_ptr, psize))
2134 LOG (GNUNET_ERROR_TYPE_DEBUG, "pipe read handle = %p, write handle = %p\n",
2139 err = GetLastError ();
2140 LOG (GNUNET_ERROR_TYPE_ERROR, "CreatePipe failed: %d\n", err);
2143 LOG (GNUNET_ERROR_TYPE_ERROR, "CreateNamedPipe failed: %d\n", err);
2148 LOG (GNUNET_ERROR_TYPE_DEBUG, "CreateFile: name = %s\n", pipename);
2150 /* Open the named pipe for writing.
2151 * Be sure to permit FILE_READ_ATTRIBUTES access. */
2152 write_pipe = CreateFileA (pipename, GENERIC_WRITE | FILE_READ_ATTRIBUTES, 0, /* share mode */
2153 sa_ptr, OPEN_EXISTING, dwWriteMode, /* flags and attributes */
2154 0); /* handle to template file */
2156 if (write_pipe == INVALID_HANDLE_VALUE)
2159 DWORD err = GetLastError ();
2161 LOG (GNUNET_ERROR_TYPE_DEBUG, "CreateFile failed: %d\n", err);
2162 CloseHandle (read_pipe);
2165 LOG (GNUNET_ERROR_TYPE_DEBUG, "pipe write handle = %p\n", write_pipe);
2167 *read_pipe_ptr = read_pipe;
2168 *write_pipe_ptr = write_pipe;
2175 * Creates an interprocess channel
2177 * @param blocking_read creates an asynchronous pipe for reading if set to GNUNET_NO
2178 * @param blocking_write creates an asynchronous pipe for writing if set to GNUNET_NO
2179 * @param inherit_read inherit the parent processes stdin (only for windows)
2180 * @param inherit_write inherit the parent processes stdout (only for windows)
2181 * @return handle to the new pipe, NULL on error
2183 struct GNUNET_DISK_PipeHandle *
2184 GNUNET_DISK_pipe (int blocking_read, int blocking_write, int inherit_read, int inherit_write)
2195 LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR, "pipe");
2199 return GNUNET_DISK_pipe_from_fd (blocking_read,
2203 struct GNUNET_DISK_PipeHandle *p;
2208 p = GNUNET_new (struct GNUNET_DISK_PipeHandle);
2209 p->fd[0] = GNUNET_new (struct GNUNET_DISK_FileHandle);
2210 p->fd[1] = GNUNET_new (struct GNUNET_DISK_FileHandle);
2212 /* All pipes are overlapped. If you want them to block - just
2213 * call WriteFile() and ReadFile() with NULL overlapped pointer.
2214 * NOTE: calling with NULL overlapped pointer works only
2215 * for pipes, and doesn't seem to be a documented feature.
2216 * It will NOT work for files, because overlapped files need
2217 * to read offsets from the overlapped structure, regardless.
2218 * Pipes are not seekable, and need no offsets, which is
2219 * probably why it works for them.
2222 create_selectable_pipe (&p->fd[0]->h, &p->fd[1]->h, NULL, 0,
2223 FILE_FLAG_OVERLAPPED,
2224 FILE_FLAG_OVERLAPPED);
2227 SetErrnoFromWinError (GetLastError ());
2229 GNUNET_free (p->fd[0]);
2230 GNUNET_free (p->fd[1]);
2235 if (!DuplicateHandle
2236 (GetCurrentProcess (), p->fd[0]->h, GetCurrentProcess (), &tmp_handle, 0,
2237 inherit_read == GNUNET_YES ? TRUE : FALSE, DUPLICATE_SAME_ACCESS))
2239 SetErrnoFromWinError (GetLastError ());
2241 CloseHandle (p->fd[0]->h);
2242 CloseHandle (p->fd[1]->h);
2243 GNUNET_free (p->fd[0]);
2244 GNUNET_free (p->fd[1]);
2249 CloseHandle (p->fd[0]->h);
2250 p->fd[0]->h = tmp_handle;
2252 if (!DuplicateHandle
2253 (GetCurrentProcess (), p->fd[1]->h, GetCurrentProcess (), &tmp_handle, 0,
2254 inherit_write == GNUNET_YES ? TRUE : FALSE, DUPLICATE_SAME_ACCESS))
2256 SetErrnoFromWinError (GetLastError ());
2258 CloseHandle (p->fd[0]->h);
2259 CloseHandle (p->fd[1]->h);
2260 GNUNET_free (p->fd[0]);
2261 GNUNET_free (p->fd[1]);
2266 CloseHandle (p->fd[1]->h);
2267 p->fd[1]->h = tmp_handle;
2269 p->fd[0]->type = GNUNET_DISK_HANLDE_TYPE_PIPE;
2270 p->fd[1]->type = GNUNET_DISK_HANLDE_TYPE_PIPE;
2272 p->fd[0]->oOverlapRead = GNUNET_new (OVERLAPPED);
2273 p->fd[0]->oOverlapWrite = GNUNET_new (OVERLAPPED);
2274 p->fd[1]->oOverlapRead = GNUNET_new (OVERLAPPED);
2275 p->fd[1]->oOverlapWrite = GNUNET_new (OVERLAPPED);
2277 p->fd[0]->oOverlapRead->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2278 p->fd[0]->oOverlapWrite->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2280 p->fd[1]->oOverlapRead->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2281 p->fd[1]->oOverlapWrite->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2289 * Creates a pipe object from a couple of file descriptors.
2290 * Useful for wrapping existing pipe FDs.
2292 * @param blocking_read creates an asynchronous pipe for reading if set to GNUNET_NO
2293 * @param blocking_write creates an asynchronous pipe for writing if set to GNUNET_NO
2294 * @param fd an array of two fd values. One of them may be -1 for read-only or write-only pipes
2296 * @return handle to the new pipe, NULL on error
2298 struct GNUNET_DISK_PipeHandle *
2299 GNUNET_DISK_pipe_from_fd (int blocking_read, int blocking_write, int fd[2])
2301 struct GNUNET_DISK_PipeHandle *p;
2303 p = GNUNET_new (struct GNUNET_DISK_PipeHandle);
2308 int eno = 0; /* make gcc happy */
2313 p->fd[0] = GNUNET_new (struct GNUNET_DISK_FileHandle);
2314 p->fd[0]->fd = fd[0];
2317 flags = fcntl (fd[0], F_GETFL);
2318 flags |= O_NONBLOCK;
2319 if (0 > fcntl (fd[0], F_SETFL, flags))
2325 flags = fcntl (fd[0], F_GETFD);
2326 flags |= FD_CLOEXEC;
2327 if (0 > fcntl (fd[0], F_SETFD, flags))
2336 p->fd[1] = GNUNET_new (struct GNUNET_DISK_FileHandle);
2337 p->fd[1]->fd = fd[1];
2338 if (!blocking_write)
2340 flags = fcntl (fd[1], F_GETFL);
2341 flags |= O_NONBLOCK;
2342 if (0 > fcntl (fd[1], F_SETFL, flags))
2348 flags = fcntl (fd[1], F_GETFD);
2349 flags |= FD_CLOEXEC;
2350 if (0 > fcntl (fd[1], F_SETFD, flags))
2359 LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR, "fcntl");
2360 if (p->fd[0]->fd >= 0)
2361 GNUNET_break (0 == close (p->fd[0]->fd));
2362 if (p->fd[1]->fd >= 0)
2363 GNUNET_break (0 == close (p->fd[1]->fd));
2364 GNUNET_free_non_null (p->fd[0]);
2365 GNUNET_free_non_null (p->fd[1]);
2373 p->fd[0] = GNUNET_new (struct GNUNET_DISK_FileHandle);
2374 p->fd[0]->h = (HANDLE) _get_osfhandle (fd[0]);
2375 if (p->fd[0]->h != INVALID_HANDLE_VALUE)
2377 p->fd[0]->type = GNUNET_DISK_HANLDE_TYPE_PIPE;
2378 p->fd[0]->oOverlapRead = GNUNET_new (OVERLAPPED);
2379 p->fd[0]->oOverlapWrite = GNUNET_new (OVERLAPPED);
2380 p->fd[0]->oOverlapRead->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2381 p->fd[0]->oOverlapWrite->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2385 GNUNET_free (p->fd[0]);
2391 p->fd[1] = GNUNET_new (struct GNUNET_DISK_FileHandle);
2392 p->fd[1]->h = (HANDLE) _get_osfhandle (fd[1]);
2393 if (p->fd[1]->h != INVALID_HANDLE_VALUE)
2395 p->fd[1]->type = GNUNET_DISK_HANLDE_TYPE_PIPE;
2396 p->fd[1]->oOverlapRead = GNUNET_new (OVERLAPPED);
2397 p->fd[1]->oOverlapWrite = GNUNET_new (OVERLAPPED);
2398 p->fd[1]->oOverlapRead->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2399 p->fd[1]->oOverlapWrite->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2403 GNUNET_free (p->fd[1]);
2414 * Closes an interprocess channel
2416 * @param p pipe to close
2417 * @param end which end of the pipe to close
2418 * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
2421 GNUNET_DISK_pipe_close_end (struct GNUNET_DISK_PipeHandle *p,
2422 enum GNUNET_DISK_PipeEnd end)
2424 int ret = GNUNET_OK;
2426 if (end == GNUNET_DISK_PIPE_END_READ)
2430 ret = GNUNET_DISK_file_close (p->fd[0]);
2434 else if (end == GNUNET_DISK_PIPE_END_WRITE)
2438 ret = GNUNET_DISK_file_close (p->fd[1]);
2447 * Detaches one of the ends from the pipe.
2448 * Detached end is a fully-functional FileHandle, it will
2449 * not be affected by anything you do with the pipe afterwards.
2450 * Each end of a pipe can only be detched from it once (i.e.
2451 * it is not duplicated).
2453 * @param p pipe to detach an end from
2454 * @param end which end of the pipe to detach
2455 * @return Detached end on success, NULL on failure
2456 * (or if that end is not present or is closed).
2458 struct GNUNET_DISK_FileHandle *
2459 GNUNET_DISK_pipe_detach_end (struct GNUNET_DISK_PipeHandle *p,
2460 enum GNUNET_DISK_PipeEnd end)
2462 struct GNUNET_DISK_FileHandle *ret = NULL;
2464 if (end == GNUNET_DISK_PIPE_END_READ)
2472 else if (end == GNUNET_DISK_PIPE_END_WRITE)
2486 * Closes an interprocess channel
2488 * @param p pipe to close
2489 * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
2492 GNUNET_DISK_pipe_close (struct GNUNET_DISK_PipeHandle *p)
2494 int ret = GNUNET_OK;
2497 int write_end_close;
2498 int read_end_close_errno;
2499 int write_end_close_errno;
2501 read_end_close = GNUNET_DISK_pipe_close_end (p, GNUNET_DISK_PIPE_END_READ);
2502 read_end_close_errno = errno;
2503 write_end_close = GNUNET_DISK_pipe_close_end (p, GNUNET_DISK_PIPE_END_WRITE);
2504 write_end_close_errno = errno;
2507 if (GNUNET_OK != read_end_close)
2509 errno = read_end_close_errno;
2510 ret = read_end_close;
2512 else if (GNUNET_OK != write_end_close)
2514 errno = write_end_close_errno;
2515 ret = write_end_close;
2523 * Get the handle to a particular pipe end
2526 * @param n end to access
2527 * @return handle for the respective end
2529 const struct GNUNET_DISK_FileHandle *
2530 GNUNET_DISK_pipe_handle (const struct GNUNET_DISK_PipeHandle *p,
2531 enum GNUNET_DISK_PipeEnd n)
2535 case GNUNET_DISK_PIPE_END_READ:
2536 case GNUNET_DISK_PIPE_END_WRITE:
2546 * Retrieve OS file handle
2548 * @param fh GNUnet file descriptor
2549 * @param dst destination buffer
2550 * @param dst_len length of dst
2551 * @return #GNUNET_OK on success, #GNUNET_SYSERR otherwise
2554 GNUNET_DISK_internal_file_handle_ (const struct GNUNET_DISK_FileHandle *fh,
2555 void *dst, size_t dst_len)
2558 return GNUNET_SYSERR;
2560 if (dst_len < sizeof (HANDLE))
2561 return GNUNET_SYSERR;
2562 *((HANDLE *) dst) = fh->h;
2564 if (dst_len < sizeof (int))
2565 return GNUNET_SYSERR;
2566 *((int *) dst) = fh->fd;