2 This file is part of GNUnet.
3 Copyright (C) 2001--2013 Christian Grothoff (and other contributing authors)
5 GNUnet is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 3, or (at your
8 option) any later version.
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with GNUnet; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 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
806 * @param filename name of a file in the directory
807 * @returns #GNUNET_OK on success,
808 * #GNUNET_SYSERR on failure,
809 * #GNUNET_NO if the directory
810 * exists but is not writeable for us
813 GNUNET_DISK_directory_create_for_file (const char *filename)
819 rdir = GNUNET_STRINGS_filename_expand (filename);
821 return GNUNET_SYSERR;
823 while ((len > 0) && (rdir[len] != DIR_SEPARATOR))
826 ret = GNUNET_DISK_directory_create (rdir);
827 if ((ret == GNUNET_OK) && (0 != ACCESS (rdir, W_OK)))
835 * Read the contents of a binary file into a buffer.
837 * @param h handle to an open file
838 * @param result the buffer to write the result to
839 * @param len the maximum number of bytes to read
840 * @return the number of bytes read on success, #GNUNET_SYSERR on failure
843 GNUNET_DISK_file_read (const struct GNUNET_DISK_FileHandle *h,
850 return GNUNET_SYSERR;
856 if (h->type == GNUNET_DISK_HANLDE_TYPE_FILE)
858 if (!ReadFile (h->h, result, len, &bytes_read, NULL))
860 SetErrnoFromWinError (GetLastError ());
861 return GNUNET_SYSERR;
864 else if (h->type == GNUNET_DISK_HANLDE_TYPE_PIPE)
866 if (!ReadFile (h->h, result, len, &bytes_read, h->oOverlapRead))
868 if (GetLastError () != ERROR_IO_PENDING)
870 LOG (GNUNET_ERROR_TYPE_DEBUG,
871 "Error reading from pipe: %u\n",
873 SetErrnoFromWinError (GetLastError ());
874 return GNUNET_SYSERR;
876 LOG (GNUNET_ERROR_TYPE_DEBUG, "Will get overlapped result\n");
877 GetOverlappedResult (h->h, h->oOverlapRead, &bytes_read, TRUE);
879 LOG (GNUNET_ERROR_TYPE_DEBUG, "Read %u bytes from pipe\n", bytes_read);
887 return read (h->fd, result, len);
893 * Read the contents of a binary file into a buffer.
894 * Guarantees not to block (returns GNUNET_SYSERR and sets errno to EAGAIN
895 * when no data can be read).
897 * @param h handle to an open file
898 * @param result the buffer to write the result to
899 * @param len the maximum number of bytes to read
900 * @return the number of bytes read on success, #GNUNET_SYSERR on failure
903 GNUNET_DISK_file_read_non_blocking (const struct GNUNET_DISK_FileHandle *h,
910 return GNUNET_SYSERR;
916 if (h->type == GNUNET_DISK_HANLDE_TYPE_FILE)
918 if (!ReadFile (h->h, result, len, &bytes_read, NULL))
920 SetErrnoFromWinError (GetLastError ());
921 return GNUNET_SYSERR;
924 else if (h->type == GNUNET_DISK_HANLDE_TYPE_PIPE)
926 if (!ReadFile (h->h, result, len, &bytes_read, h->oOverlapRead))
928 if (GetLastError () != ERROR_IO_PENDING)
930 LOG (GNUNET_ERROR_TYPE_DEBUG, "Error reading from pipe: %u\n", GetLastError ());
931 SetErrnoFromWinError (GetLastError ());
932 return GNUNET_SYSERR;
936 LOG (GNUNET_ERROR_TYPE_DEBUG,
937 "ReadFile() queued a read, cancelling\n");
940 return GNUNET_SYSERR;
943 LOG (GNUNET_ERROR_TYPE_DEBUG,
956 /* set to non-blocking, read, then set back */
957 flags = fcntl (h->fd, F_GETFL);
958 if (0 == (flags & O_NONBLOCK))
959 (void) fcntl (h->fd, F_SETFL, flags | O_NONBLOCK);
960 ret = read (h->fd, result, len);
961 if (0 == (flags & O_NONBLOCK))
964 (void) fcntl (h->fd, F_SETFL, flags);
973 * Read the contents of a binary file into a buffer.
975 * @param fn file name
976 * @param result the buffer to write the result to
977 * @param len the maximum number of bytes to read
978 * @return number of bytes read, #GNUNET_SYSERR on failure
981 GNUNET_DISK_fn_read (const char *fn,
985 struct GNUNET_DISK_FileHandle *fh;
989 fh = GNUNET_DISK_file_open (fn,
990 GNUNET_DISK_OPEN_READ,
991 GNUNET_DISK_PERM_NONE);
993 return GNUNET_SYSERR;
994 ret = GNUNET_DISK_file_read (fh, result, len);
996 GNUNET_assert (GNUNET_OK == GNUNET_DISK_file_close (fh));
1003 * Write a buffer to a file.
1005 * @param h handle to open file
1006 * @param buffer the data to write
1007 * @param n number of bytes to write
1008 * @return number of bytes written on success, #GNUNET_SYSERR on error
1011 GNUNET_DISK_file_write (const struct GNUNET_DISK_FileHandle * h,
1012 const void *buffer, size_t n)
1017 return GNUNET_SYSERR;
1021 DWORD bytes_written;
1023 if (h->type == GNUNET_DISK_HANLDE_TYPE_FILE)
1025 if (!WriteFile (h->h, buffer, n, &bytes_written, NULL))
1027 SetErrnoFromWinError (GetLastError ());
1028 return GNUNET_SYSERR;
1031 else if (h->type == GNUNET_DISK_HANLDE_TYPE_PIPE)
1033 LOG (GNUNET_ERROR_TYPE_DEBUG, "It is a pipe trying to write %u bytes\n", n);
1034 if (!WriteFile (h->h, buffer, n, &bytes_written, h->oOverlapWrite))
1036 if (GetLastError () != ERROR_IO_PENDING)
1038 SetErrnoFromWinError (GetLastError ());
1039 LOG (GNUNET_ERROR_TYPE_DEBUG, "Error writing to pipe: %u\n",
1041 return GNUNET_SYSERR;
1043 LOG (GNUNET_ERROR_TYPE_DEBUG, "Will get overlapped result\n");
1044 if (!GetOverlappedResult (h->h, h->oOverlapWrite, &bytes_written, TRUE))
1046 SetErrnoFromWinError (GetLastError ());
1047 LOG (GNUNET_ERROR_TYPE_DEBUG,
1048 "Error getting overlapped result while writing to pipe: %u\n",
1050 return GNUNET_SYSERR;
1056 if (!GetOverlappedResult (h->h, h->oOverlapWrite, &ovr, TRUE))
1058 LOG (GNUNET_ERROR_TYPE_DEBUG,
1059 "Error getting control overlapped result while writing to pipe: %u\n",
1064 LOG (GNUNET_ERROR_TYPE_DEBUG,
1065 "Wrote %u bytes (ovr says %u), picking the greatest\n",
1066 bytes_written, ovr);
1069 if (bytes_written == 0)
1073 LOG (GNUNET_ERROR_TYPE_DEBUG, "Wrote %u bytes, returning -1 with EAGAIN\n", bytes_written);
1075 return GNUNET_SYSERR;
1078 LOG (GNUNET_ERROR_TYPE_DEBUG, "Wrote %u bytes\n", bytes_written);
1084 return bytes_written;
1086 return write (h->fd, buffer, n);
1092 * Write a buffer to a file, blocking, if necessary.
1094 * @param h handle to open file
1095 * @param buffer the data to write
1096 * @param n number of bytes to write
1097 * @return number of bytes written on success, #GNUNET_SYSERR on error
1100 GNUNET_DISK_file_write_blocking (const struct GNUNET_DISK_FileHandle * h,
1107 return GNUNET_SYSERR;
1111 DWORD bytes_written;
1112 /* We do a non-overlapped write, which is as blocking as it gets */
1113 LOG (GNUNET_ERROR_TYPE_DEBUG, "Writing %u bytes\n", n);
1114 if (!WriteFile (h->h, buffer, n, &bytes_written, NULL))
1116 SetErrnoFromWinError (GetLastError ());
1117 LOG (GNUNET_ERROR_TYPE_DEBUG, "Error writing to pipe: %u\n",
1119 return GNUNET_SYSERR;
1121 if (bytes_written == 0 && n > 0)
1123 LOG (GNUNET_ERROR_TYPE_DEBUG, "Waiting for pipe to clean\n");
1124 WaitForSingleObject (h->h, INFINITE);
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;
1133 LOG (GNUNET_ERROR_TYPE_DEBUG,
1136 return bytes_written;
1141 /* set to blocking, write, then set back */
1142 flags = fcntl (h->fd, F_GETFL);
1143 if (0 != (flags & O_NONBLOCK))
1144 (void) fcntl (h->fd, F_SETFL, flags - O_NONBLOCK);
1145 ret = write (h->fd, buffer, n);
1146 if (0 == (flags & O_NONBLOCK))
1147 (void) fcntl (h->fd, F_SETFL, flags);
1154 * Write a buffer to a file. If the file is longer than the
1155 * number of bytes that will be written, it will be truncated.
1157 * @param fn file name
1158 * @param buffer the data to write
1159 * @param n number of bytes to write
1160 * @param mode file permissions
1161 * @return number of bytes written on success, #GNUNET_SYSERR on error
1164 GNUNET_DISK_fn_write (const char *fn, const void *buffer, size_t n,
1165 enum GNUNET_DISK_AccessPermissions mode)
1167 struct GNUNET_DISK_FileHandle *fh;
1170 fh = GNUNET_DISK_file_open (fn,
1171 GNUNET_DISK_OPEN_WRITE | GNUNET_DISK_OPEN_TRUNCATE
1172 | GNUNET_DISK_OPEN_CREATE, mode);
1174 return GNUNET_SYSERR;
1175 ret = GNUNET_DISK_file_write (fh, buffer, n);
1176 GNUNET_assert (GNUNET_OK == GNUNET_DISK_file_close (fh));
1182 * Scan a directory for files.
1184 * @param dir_name the name of the directory
1185 * @param callback the method to call for each file,
1186 * can be NULL, in that case, we only count
1187 * @param callback_cls closure for @a callback
1188 * @return the number of files found, #GNUNET_SYSERR on error or
1189 * ieration aborted by callback returning #GNUNET_SYSERR
1192 GNUNET_DISK_directory_scan (const char *dir_name,
1193 GNUNET_FileNameCallback callback,
1197 struct dirent *finfo;
1203 unsigned int name_len;
1204 unsigned int n_size;
1206 GNUNET_assert (NULL != dir_name);
1207 dname = GNUNET_STRINGS_filename_expand (dir_name);
1209 return GNUNET_SYSERR;
1210 while ((strlen (dname) > 0) && (dname[strlen (dname) - 1] == DIR_SEPARATOR))
1211 dname[strlen (dname) - 1] = '\0';
1212 if (0 != STAT (dname, &istat))
1214 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "stat", dname);
1215 GNUNET_free (dname);
1216 return GNUNET_SYSERR;
1218 if (! S_ISDIR (istat.st_mode))
1220 LOG (GNUNET_ERROR_TYPE_WARNING,
1221 _("Expected `%s' to be a directory!\n"),
1223 GNUNET_free (dname);
1224 return GNUNET_SYSERR;
1227 dinfo = OPENDIR (dname);
1228 if ((errno == EACCES) || (NULL == dinfo))
1230 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "opendir", dname);
1233 GNUNET_free (dname);
1234 return GNUNET_SYSERR;
1237 n_size = strlen (dname) + name_len + 2;
1238 name = GNUNET_malloc (n_size);
1239 while (NULL != (finfo = READDIR (dinfo)))
1241 if ((0 == strcmp (finfo->d_name, ".")) ||
1242 (0 == strcmp (finfo->d_name, "..")))
1244 if (NULL != callback)
1246 if (name_len < strlen (finfo->d_name))
1249 name_len = strlen (finfo->d_name);
1250 n_size = strlen (dname) + name_len + 2;
1251 name = GNUNET_malloc (n_size);
1253 /* dname can end in "/" only if dname == "/";
1254 * if dname does not end in "/", we need to add
1255 * a "/" (otherwise, we must not!) */
1256 GNUNET_snprintf (name, n_size, "%s%s%s", dname,
1257 (strcmp (dname, DIR_SEPARATOR_STR) ==
1258 0) ? "" : DIR_SEPARATOR_STR, finfo->d_name);
1259 ret = callback (callback_cls, name);
1260 if (GNUNET_OK != ret)
1264 GNUNET_free (dname);
1265 if (GNUNET_NO == ret)
1267 return GNUNET_SYSERR;
1274 GNUNET_free (dname);
1280 * Function that removes the given directory by calling
1281 * "GNUNET_DISK_directory_remove".
1283 * @param unused not used
1284 * @param fn directory to remove
1285 * @return #GNUNET_OK
1288 remove_helper (void *unused, const char *fn)
1290 (void) GNUNET_DISK_directory_remove (fn);
1296 * Remove all files in a directory (rm -rf). Call with
1299 * @param filename the file to remove
1300 * @return #GNUNET_OK on success, #GNUNET_SYSERR on error
1303 GNUNET_DISK_directory_remove (const char *filename)
1307 if (NULL == filename)
1310 return GNUNET_SYSERR;
1312 if (0 != LSTAT (filename, &istat))
1313 return GNUNET_NO; /* file may not exist... */
1314 (void) CHMOD (filename, S_IWUSR | S_IRUSR | S_IXUSR);
1315 if (UNLINK (filename) == 0)
1317 if ((errno != EISDIR) &&
1318 /* EISDIR is not sufficient in all cases, e.g.
1319 * sticky /tmp directory may result in EPERM on BSD.
1320 * So we also explicitly check "isDirectory" */
1321 (GNUNET_YES != GNUNET_DISK_directory_test (filename, GNUNET_YES)))
1323 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "rmdir", filename);
1324 return GNUNET_SYSERR;
1326 if (GNUNET_SYSERR ==
1327 GNUNET_DISK_directory_scan (filename, &remove_helper, NULL))
1328 return GNUNET_SYSERR;
1329 if (0 != RMDIR (filename))
1331 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "rmdir", filename);
1332 return GNUNET_SYSERR;
1341 * @param src file to copy
1342 * @param dst destination file name
1343 * @return #GNUNET_OK on success, #GNUNET_SYSERR on error
1346 GNUNET_DISK_file_copy (const char *src,
1353 struct GNUNET_DISK_FileHandle *in;
1354 struct GNUNET_DISK_FileHandle *out;
1356 if (GNUNET_OK != GNUNET_DISK_file_size (src, &size, GNUNET_YES, GNUNET_YES))
1357 return GNUNET_SYSERR;
1359 in = GNUNET_DISK_file_open (src, GNUNET_DISK_OPEN_READ,
1360 GNUNET_DISK_PERM_NONE);
1362 return GNUNET_SYSERR;
1364 GNUNET_DISK_file_open (dst,
1365 GNUNET_DISK_OPEN_WRITE | GNUNET_DISK_OPEN_CREATE |
1366 GNUNET_DISK_OPEN_FAILIFEXISTS,
1367 GNUNET_DISK_PERM_USER_READ |
1368 GNUNET_DISK_PERM_USER_WRITE |
1369 GNUNET_DISK_PERM_GROUP_READ |
1370 GNUNET_DISK_PERM_GROUP_WRITE);
1373 GNUNET_DISK_file_close (in);
1374 return GNUNET_SYSERR;
1376 buf = GNUNET_malloc (COPY_BLK_SIZE);
1379 len = COPY_BLK_SIZE;
1380 if (len > size - pos)
1382 if (len != GNUNET_DISK_file_read (in, buf, len))
1384 if (len != GNUNET_DISK_file_write (out, buf, len))
1389 GNUNET_DISK_file_close (in);
1390 GNUNET_DISK_file_close (out);
1394 GNUNET_DISK_file_close (in);
1395 GNUNET_DISK_file_close (out);
1396 return GNUNET_SYSERR;
1401 * @brief Removes special characters as ':' from a filename.
1402 * @param fn the filename to canonicalize
1405 GNUNET_DISK_filename_canonicalize (char *fn)
1415 if (c == '/' || c == '\\' || c == ':' || c == '*' || c == '?' || c == '"' ||
1416 c == '<' || c == '>' || c == '|')
1428 * @brief Change owner of a file
1430 * @param filename name of file to change the owner of
1431 * @param user name of the new owner
1432 * @return GNUNET_OK on success, GNUNET_SYSERR on failure
1435 GNUNET_DISK_file_change_owner (const char *filename, const char *user)
1440 pws = getpwnam (user);
1443 LOG (GNUNET_ERROR_TYPE_ERROR,
1444 _("Cannot obtain information about user `%s': %s\n"), user,
1446 return GNUNET_SYSERR;
1448 if (0 != chown (filename, pws->pw_uid, pws->pw_gid))
1449 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "chown", filename);
1456 * Lock a part of a file
1457 * @param fh file handle
1458 * @param lock_start absolute position from where to lock
1459 * @param lock_end absolute position until where to lock
1460 * @param excl GNUNET_YES for an exclusive lock
1461 * @return GNUNET_OK on success, GNUNET_SYSERR on error
1464 GNUNET_DISK_file_lock (struct GNUNET_DISK_FileHandle *fh, off_t lock_start,
1465 off_t lock_end, int excl)
1470 return GNUNET_SYSERR;
1476 memset (&fl, 0, sizeof (struct flock));
1477 fl.l_type = excl ? F_WRLCK : F_RDLCK;
1478 fl.l_whence = SEEK_SET;
1479 fl.l_start = lock_start;
1480 fl.l_len = lock_end;
1482 return fcntl (fh->fd, F_SETLK, &fl) != 0 ? GNUNET_SYSERR : GNUNET_OK;
1485 off_t diff = lock_end - lock_start;
1486 DWORD diff_low, diff_high;
1487 diff_low = (DWORD) (diff & 0xFFFFFFFF);
1488 diff_high = (DWORD) ((diff >> (sizeof (DWORD) * 8)) & 0xFFFFFFFF);
1490 memset (&o, 0, sizeof (OVERLAPPED));
1491 o.Offset = (DWORD) (lock_start & 0xFFFFFFFF);;
1492 o.OffsetHigh = (DWORD) (((lock_start & ~0xFFFFFFFF) >> (sizeof (DWORD) * 8)) & 0xFFFFFFFF);
1495 (fh->h, (excl ? LOCKFILE_EXCLUSIVE_LOCK : 0) | LOCKFILE_FAIL_IMMEDIATELY,
1496 0, diff_low, diff_high, &o))
1498 SetErrnoFromWinError (GetLastError ());
1499 return GNUNET_SYSERR;
1508 * Unlock a part of a file
1509 * @param fh file handle
1510 * @param unlock_start absolute position from where to unlock
1511 * @param unlock_end absolute position until where to unlock
1512 * @return #GNUNET_OK on success, #GNUNET_SYSERR on error
1515 GNUNET_DISK_file_unlock (struct GNUNET_DISK_FileHandle *fh, off_t unlock_start,
1521 return GNUNET_SYSERR;
1527 memset (&fl, 0, sizeof (struct flock));
1528 fl.l_type = F_UNLCK;
1529 fl.l_whence = SEEK_SET;
1530 fl.l_start = unlock_start;
1531 fl.l_len = unlock_end;
1533 return fcntl (fh->fd, F_SETLK, &fl) != 0 ? GNUNET_SYSERR : GNUNET_OK;
1536 off_t diff = unlock_end - unlock_start;
1537 DWORD diff_low, diff_high;
1538 diff_low = (DWORD) (diff & 0xFFFFFFFF);
1539 diff_high = (DWORD) ((diff >> (sizeof (DWORD) * 8)) & 0xFFFFFFFF);
1541 memset (&o, 0, sizeof (OVERLAPPED));
1542 o.Offset = (DWORD) (unlock_start & 0xFFFFFFFF);;
1543 o.OffsetHigh = (DWORD) (((unlock_start & ~0xFFFFFFFF) >> (sizeof (DWORD) * 8)) & 0xFFFFFFFF);
1545 if (!UnlockFileEx (fh->h, 0, diff_low, diff_high, &o))
1547 SetErrnoFromWinError (GetLastError ());
1548 return GNUNET_SYSERR;
1557 * Open a file. Note that the access permissions will only be
1558 * used if a new file is created and if the underlying operating
1559 * system supports the given permissions.
1561 * @param fn file name to be opened
1562 * @param flags opening flags, a combination of GNUNET_DISK_OPEN_xxx bit flags
1563 * @param perm permissions for the newly created file, use
1564 * #GNUNET_DISK_PERM_NONE if a file could not be created by this
1565 * call (because of flags)
1566 * @return IO handle on success, NULL on error
1568 struct GNUNET_DISK_FileHandle *
1569 GNUNET_DISK_file_open (const char *fn,
1570 enum GNUNET_DISK_OpenFlags flags,
1571 enum GNUNET_DISK_AccessPermissions perm)
1574 struct GNUNET_DISK_FileHandle *ret;
1580 wchar_t wexpfn[MAX_PATH + 1];
1587 expfn = GNUNET_STRINGS_filename_expand (fn);
1592 if (GNUNET_DISK_OPEN_READWRITE == (flags & GNUNET_DISK_OPEN_READWRITE))
1593 oflags = O_RDWR; /* note: O_RDWR is NOT always O_RDONLY | O_WRONLY */
1594 else if (flags & GNUNET_DISK_OPEN_READ)
1596 else if (flags & GNUNET_DISK_OPEN_WRITE)
1601 GNUNET_free (expfn);
1604 if (flags & GNUNET_DISK_OPEN_FAILIFEXISTS)
1605 oflags |= (O_CREAT | O_EXCL);
1606 if (flags & GNUNET_DISK_OPEN_TRUNCATE)
1608 if (flags & GNUNET_DISK_OPEN_APPEND)
1610 if (flags & GNUNET_DISK_OPEN_CREATE)
1612 (void) GNUNET_DISK_directory_create_for_file (expfn);
1614 mode = translate_unix_perms (perm);
1617 fd = open (expfn, oflags
1621 | O_LARGEFILE, mode);
1624 if (0 == (flags & GNUNET_DISK_OPEN_FAILIFEXISTS))
1625 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "open", expfn);
1627 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_DEBUG, "open", expfn);
1628 GNUNET_free (expfn);
1635 if (GNUNET_DISK_OPEN_READWRITE == (flags & GNUNET_DISK_OPEN_READWRITE))
1636 access = FILE_READ_DATA | FILE_WRITE_DATA;
1637 else if (flags & GNUNET_DISK_OPEN_READ)
1638 access = FILE_READ_DATA;
1639 else if (flags & GNUNET_DISK_OPEN_WRITE)
1640 access = FILE_WRITE_DATA;
1642 if (flags & GNUNET_DISK_OPEN_FAILIFEXISTS)
1646 else if (flags & GNUNET_DISK_OPEN_CREATE)
1648 (void) GNUNET_DISK_directory_create_for_file (expfn);
1649 if (flags & GNUNET_DISK_OPEN_TRUNCATE)
1650 disp = CREATE_ALWAYS;
1654 else if (flags & GNUNET_DISK_OPEN_TRUNCATE)
1656 disp = TRUNCATE_EXISTING;
1660 disp = OPEN_EXISTING;
1663 if (ERROR_SUCCESS == plibc_conv_to_win_pathwconv(expfn, wexpfn))
1664 h = CreateFileW (wexpfn, access,
1665 FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE, NULL,
1666 disp, FILE_ATTRIBUTE_NORMAL, NULL);
1668 h = INVALID_HANDLE_VALUE;
1669 if (h == INVALID_HANDLE_VALUE)
1672 SetErrnoFromWinError (GetLastError ());
1674 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_INFO, "open", expfn);
1675 GNUNET_free (expfn);
1680 if (flags & GNUNET_DISK_OPEN_APPEND)
1681 if (SetFilePointer (h, 0, 0, FILE_END) == INVALID_SET_FILE_POINTER)
1683 SetErrnoFromWinError (GetLastError ());
1684 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "SetFilePointer", expfn);
1686 GNUNET_free (expfn);
1691 ret = GNUNET_new (struct GNUNET_DISK_FileHandle);
1694 ret->type = GNUNET_DISK_HANLDE_TYPE_FILE;
1698 GNUNET_free (expfn);
1704 * Close an open file
1705 * @param h file handle
1706 * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
1709 GNUNET_DISK_file_close (struct GNUNET_DISK_FileHandle *h)
1715 return GNUNET_SYSERR;
1721 if (!CloseHandle (h->h))
1723 SetErrnoFromWinError (GetLastError ());
1724 LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING, "close");
1725 ret = GNUNET_SYSERR;
1727 if (h->oOverlapRead)
1729 if (!CloseHandle (h->oOverlapRead->hEvent))
1731 SetErrnoFromWinError (GetLastError ());
1732 LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING, "close");
1733 ret = GNUNET_SYSERR;
1735 GNUNET_free (h->oOverlapRead);
1737 if (h->oOverlapWrite)
1739 if (!CloseHandle (h->oOverlapWrite->hEvent))
1741 SetErrnoFromWinError (GetLastError ());
1742 LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING, "close");
1743 ret = GNUNET_SYSERR;
1745 GNUNET_free (h->oOverlapWrite);
1748 if (close (h->fd) != 0)
1750 LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING, "close");
1751 ret = GNUNET_SYSERR;
1760 * Get a GNUnet file handle from a W32 handle.
1762 * @param handle native handle
1763 * @return GNUnet file handle corresponding to the W32 handle
1765 struct GNUNET_DISK_FileHandle *
1766 GNUNET_DISK_get_handle_from_w32_handle (HANDLE osfh)
1768 struct GNUNET_DISK_FileHandle *fh;
1771 enum GNUNET_FILE_Type ftype;
1773 dwret = GetFileType (osfh);
1776 case FILE_TYPE_DISK:
1777 ftype = GNUNET_DISK_HANLDE_TYPE_FILE;
1779 case FILE_TYPE_PIPE:
1780 ftype = GNUNET_DISK_HANLDE_TYPE_PIPE;
1782 case FILE_TYPE_UNKNOWN:
1783 if (GetLastError () == NO_ERROR || GetLastError () == ERROR_INVALID_HANDLE)
1785 if (0 != ResetEvent (osfh))
1786 ftype = GNUNET_DISK_HANLDE_TYPE_EVENT;
1795 fh = GNUNET_new (struct GNUNET_DISK_FileHandle);
1799 if (ftype == GNUNET_DISK_HANLDE_TYPE_PIPE)
1802 * Note that we can't make it overlapped if it isn't already.
1803 * (ReOpenFile() is only available in 2003/Vista).
1804 * The process that opened this file in the first place (usually a parent
1805 * process, if this is stdin/stdout/stderr) must make it overlapped,
1806 * otherwise we're screwed, as selecting on non-overlapped handle
1809 fh->oOverlapRead = GNUNET_new (OVERLAPPED);
1810 fh->oOverlapWrite = GNUNET_new (OVERLAPPED);
1811 fh->oOverlapRead->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
1812 fh->oOverlapWrite->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
1820 * Get a handle from a native integer FD.
1822 * @param fno native integer file descriptor
1823 * @return file handle corresponding to the descriptor, NULL on error
1825 struct GNUNET_DISK_FileHandle *
1826 GNUNET_DISK_get_handle_from_int_fd (int fno)
1828 struct GNUNET_DISK_FileHandle *fh;
1830 if ( (((off_t) -1) == lseek (fno, 0, SEEK_CUR)) &&
1832 return NULL; /* invalid FD */
1835 fh = GNUNET_new (struct GNUNET_DISK_FileHandle);
1841 osfh = _get_osfhandle (fno);
1842 if (INVALID_HANDLE_VALUE == (HANDLE) osfh)
1845 fh = GNUNET_DISK_get_handle_from_w32_handle ((HANDLE) osfh);
1853 * Get a handle from a native streaming FD.
1855 * @param fd native streaming file descriptor
1856 * @return file handle corresponding to the descriptor
1858 struct GNUNET_DISK_FileHandle *
1859 GNUNET_DISK_get_handle_from_native (FILE *fd)
1867 return GNUNET_DISK_get_handle_from_int_fd (fno);
1872 * Handle for a memory-mapping operation.
1874 struct GNUNET_DISK_MapHandle
1877 * Address where the map is in memory.
1883 * Underlying OS handle.
1888 * Number of bytes mapped.
1896 #define MAP_FAILED ((void *) -1)
1900 * Map a file into memory
1902 * @param h open file handle
1903 * @param m handle to the new mapping
1904 * @param access access specification, GNUNET_DISK_MAP_TYPE_xxx
1905 * @param len size of the mapping
1906 * @return pointer to the mapped memory region, NULL on failure
1909 GNUNET_DISK_file_map (const struct GNUNET_DISK_FileHandle *h,
1910 struct GNUNET_DISK_MapHandle **m,
1911 enum GNUNET_DISK_MapType access, size_t len)
1920 DWORD mapAccess, protect;
1922 if ((access & GNUNET_DISK_MAP_TYPE_READ) &&
1923 (access & GNUNET_DISK_MAP_TYPE_WRITE))
1925 protect = PAGE_READWRITE;
1926 mapAccess = FILE_MAP_ALL_ACCESS;
1928 else if (access & GNUNET_DISK_MAP_TYPE_READ)
1930 protect = PAGE_READONLY;
1931 mapAccess = FILE_MAP_READ;
1933 else if (access & GNUNET_DISK_MAP_TYPE_WRITE)
1935 protect = PAGE_READWRITE;
1936 mapAccess = FILE_MAP_WRITE;
1944 *m = GNUNET_new (struct GNUNET_DISK_MapHandle);
1945 (*m)->h = CreateFileMapping (h->h, NULL, protect, 0, 0, NULL);
1946 if ((*m)->h == INVALID_HANDLE_VALUE)
1948 SetErrnoFromWinError (GetLastError ());
1953 (*m)->addr = MapViewOfFile ((*m)->h, mapAccess, 0, 0, len);
1956 SetErrnoFromWinError (GetLastError ());
1957 CloseHandle ((*m)->h);
1966 if (access & GNUNET_DISK_MAP_TYPE_READ)
1968 if (access & GNUNET_DISK_MAP_TYPE_WRITE)
1970 *m = GNUNET_new (struct GNUNET_DISK_MapHandle);
1971 (*m)->addr = mmap (NULL, len, prot, MAP_SHARED, h->fd, 0);
1972 GNUNET_assert (NULL != (*m)->addr);
1973 if (MAP_FAILED == (*m)->addr)
1985 * @param h mapping handle
1986 * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
1989 GNUNET_DISK_file_unmap (struct GNUNET_DISK_MapHandle *h)
1996 return GNUNET_SYSERR;
2000 ret = UnmapViewOfFile (h->addr) ? GNUNET_OK : GNUNET_SYSERR;
2001 if (ret != GNUNET_OK)
2002 SetErrnoFromWinError (GetLastError ());
2003 if (!CloseHandle (h->h) && (ret == GNUNET_OK))
2005 ret = GNUNET_SYSERR;
2006 SetErrnoFromWinError (GetLastError ());
2009 ret = munmap (h->addr, h->len) != -1 ? GNUNET_OK : GNUNET_SYSERR;
2017 * Write file changes to disk
2018 * @param h handle to an open file
2019 * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
2022 GNUNET_DISK_file_sync (const struct GNUNET_DISK_FileHandle *h)
2027 return GNUNET_SYSERR;
2033 ret = FlushFileBuffers (h->h) ? GNUNET_OK : GNUNET_SYSERR;
2034 if (ret != GNUNET_OK)
2035 SetErrnoFromWinError (GetLastError ());
2037 #elif defined(FREEBSD) || defined(OPENBSD) || defined(DARWIN)
2038 return fsync (h->fd) == -1 ? GNUNET_SYSERR : GNUNET_OK;
2040 return fdatasync (h->fd) == -1 ? GNUNET_SYSERR : GNUNET_OK;
2047 #define PIPE_BUF 512
2049 /* Copyright Bob Byrnes <byrnes <at> curl.com>
2050 http://permalink.gmane.org/gmane.os.cygwin.patches/2121
2052 /* Create a pipe, and return handles to the read and write ends,
2053 just like CreatePipe, but ensure that the write end permits
2054 FILE_READ_ATTRIBUTES access, on later versions of win32 where
2055 this is supported. This access is needed by NtQueryInformationFile,
2056 which is used to implement select and nonblocking writes.
2057 Note that the return value is either NO_ERROR or GetLastError,
2058 unlike CreatePipe, which returns a bool for success or failure. */
2060 create_selectable_pipe (PHANDLE read_pipe_ptr, PHANDLE write_pipe_ptr,
2061 LPSECURITY_ATTRIBUTES sa_ptr, DWORD psize,
2062 DWORD dwReadMode, DWORD dwWriteMode)
2064 /* Default to error. */
2065 *read_pipe_ptr = *write_pipe_ptr = INVALID_HANDLE_VALUE;
2070 /* Ensure that there is enough pipe buffer space for atomic writes. */
2071 if (psize < PIPE_BUF)
2074 char pipename[MAX_PATH];
2076 /* Retry CreateNamedPipe as long as the pipe name is in use.
2077 * Retrying will probably never be necessary, but we want
2078 * to be as robust as possible. */
2081 static volatile LONG pipe_unique_id;
2083 snprintf (pipename, sizeof pipename, "\\\\.\\pipe\\gnunet-%d-%ld",
2084 getpid (), InterlockedIncrement ((LONG *) & pipe_unique_id));
2085 LOG (GNUNET_ERROR_TYPE_DEBUG, "CreateNamedPipe: name = %s, size = %lu\n",
2087 /* Use CreateNamedPipe instead of CreatePipe, because the latter
2088 * returns a write handle that does not permit FILE_READ_ATTRIBUTES
2089 * access, on versions of win32 earlier than WinXP SP2.
2090 * CreatePipe also stupidly creates a full duplex pipe, which is
2091 * a waste, since only a single direction is actually used.
2092 * It's important to only allow a single instance, to ensure that
2093 * the pipe was not created earlier by some other process, even if
2094 * the pid has been reused. */
2095 read_pipe = CreateNamedPipeA (pipename, PIPE_ACCESS_INBOUND | FILE_FLAG_FIRST_PIPE_INSTANCE | dwReadMode, PIPE_TYPE_BYTE | PIPE_READMODE_BYTE, 1, /* max instances */
2096 psize, /* output buffer size */
2097 psize, /* input buffer size */
2098 NMPWAIT_USE_DEFAULT_WAIT, sa_ptr);
2100 if (read_pipe != INVALID_HANDLE_VALUE)
2102 LOG (GNUNET_ERROR_TYPE_DEBUG, "pipe read handle = %p\n", read_pipe);
2106 DWORD err = GetLastError ();
2110 case ERROR_PIPE_BUSY:
2111 /* The pipe is already open with compatible parameters.
2112 * Pick a new name and retry. */
2113 LOG (GNUNET_ERROR_TYPE_DEBUG, "pipe busy, retrying\n");
2115 case ERROR_ACCESS_DENIED:
2116 /* The pipe is already open with incompatible parameters.
2117 * Pick a new name and retry. */
2118 LOG (GNUNET_ERROR_TYPE_DEBUG, "pipe access denied, retrying\n");
2120 case ERROR_CALL_NOT_IMPLEMENTED:
2121 /* We are on an older Win9x platform without named pipes.
2122 * Return an anonymous pipe as the best approximation. */
2123 LOG (GNUNET_ERROR_TYPE_DEBUG,
2124 "CreateNamedPipe not implemented, resorting to "
2125 "CreatePipe: size = %lu\n", psize);
2126 if (CreatePipe (read_pipe_ptr, write_pipe_ptr, sa_ptr, psize))
2128 LOG (GNUNET_ERROR_TYPE_DEBUG, "pipe read handle = %p, write handle = %p\n",
2133 err = GetLastError ();
2134 LOG (GNUNET_ERROR_TYPE_ERROR, "CreatePipe failed: %d\n", err);
2137 LOG (GNUNET_ERROR_TYPE_ERROR, "CreateNamedPipe failed: %d\n", err);
2142 LOG (GNUNET_ERROR_TYPE_DEBUG, "CreateFile: name = %s\n", pipename);
2144 /* Open the named pipe for writing.
2145 * Be sure to permit FILE_READ_ATTRIBUTES access. */
2146 write_pipe = CreateFileA (pipename, GENERIC_WRITE | FILE_READ_ATTRIBUTES, 0, /* share mode */
2147 sa_ptr, OPEN_EXISTING, dwWriteMode, /* flags and attributes */
2148 0); /* handle to template file */
2150 if (write_pipe == INVALID_HANDLE_VALUE)
2153 DWORD err = GetLastError ();
2155 LOG (GNUNET_ERROR_TYPE_DEBUG, "CreateFile failed: %d\n", err);
2156 CloseHandle (read_pipe);
2159 LOG (GNUNET_ERROR_TYPE_DEBUG, "pipe write handle = %p\n", write_pipe);
2161 *read_pipe_ptr = read_pipe;
2162 *write_pipe_ptr = write_pipe;
2169 * Creates an interprocess channel
2171 * @param blocking_read creates an asynchronous pipe for reading if set to GNUNET_NO
2172 * @param blocking_write creates an asynchronous pipe for writing if set to GNUNET_NO
2173 * @param inherit_read inherit the parent processes stdin (only for windows)
2174 * @param inherit_write inherit the parent processes stdout (only for windows)
2175 * @return handle to the new pipe, NULL on error
2177 struct GNUNET_DISK_PipeHandle *
2178 GNUNET_DISK_pipe (int blocking_read, int blocking_write, int inherit_read, int inherit_write)
2189 LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR, "pipe");
2193 return GNUNET_DISK_pipe_from_fd (blocking_read,
2197 struct GNUNET_DISK_PipeHandle *p;
2202 p = GNUNET_new (struct GNUNET_DISK_PipeHandle);
2203 p->fd[0] = GNUNET_new (struct GNUNET_DISK_FileHandle);
2204 p->fd[1] = GNUNET_new (struct GNUNET_DISK_FileHandle);
2206 /* All pipes are overlapped. If you want them to block - just
2207 * call WriteFile() and ReadFile() with NULL overlapped pointer.
2208 * NOTE: calling with NULL overlapped pointer works only
2209 * for pipes, and doesn't seem to be a documented feature.
2210 * It will NOT work for files, because overlapped files need
2211 * to read offsets from the overlapped structure, regardless.
2212 * Pipes are not seekable, and need no offsets, which is
2213 * probably why it works for them.
2216 create_selectable_pipe (&p->fd[0]->h, &p->fd[1]->h, NULL, 0,
2217 FILE_FLAG_OVERLAPPED,
2218 FILE_FLAG_OVERLAPPED);
2221 SetErrnoFromWinError (GetLastError ());
2223 GNUNET_free (p->fd[0]);
2224 GNUNET_free (p->fd[1]);
2229 if (!DuplicateHandle
2230 (GetCurrentProcess (), p->fd[0]->h, GetCurrentProcess (), &tmp_handle, 0,
2231 inherit_read == GNUNET_YES ? TRUE : FALSE, DUPLICATE_SAME_ACCESS))
2233 SetErrnoFromWinError (GetLastError ());
2235 CloseHandle (p->fd[0]->h);
2236 CloseHandle (p->fd[1]->h);
2237 GNUNET_free (p->fd[0]);
2238 GNUNET_free (p->fd[1]);
2243 CloseHandle (p->fd[0]->h);
2244 p->fd[0]->h = tmp_handle;
2246 if (!DuplicateHandle
2247 (GetCurrentProcess (), p->fd[1]->h, GetCurrentProcess (), &tmp_handle, 0,
2248 inherit_write == GNUNET_YES ? TRUE : FALSE, DUPLICATE_SAME_ACCESS))
2250 SetErrnoFromWinError (GetLastError ());
2252 CloseHandle (p->fd[0]->h);
2253 CloseHandle (p->fd[1]->h);
2254 GNUNET_free (p->fd[0]);
2255 GNUNET_free (p->fd[1]);
2260 CloseHandle (p->fd[1]->h);
2261 p->fd[1]->h = tmp_handle;
2263 p->fd[0]->type = GNUNET_DISK_HANLDE_TYPE_PIPE;
2264 p->fd[1]->type = GNUNET_DISK_HANLDE_TYPE_PIPE;
2266 p->fd[0]->oOverlapRead = GNUNET_new (OVERLAPPED);
2267 p->fd[0]->oOverlapWrite = GNUNET_new (OVERLAPPED);
2268 p->fd[1]->oOverlapRead = GNUNET_new (OVERLAPPED);
2269 p->fd[1]->oOverlapWrite = GNUNET_new (OVERLAPPED);
2271 p->fd[0]->oOverlapRead->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2272 p->fd[0]->oOverlapWrite->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2274 p->fd[1]->oOverlapRead->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2275 p->fd[1]->oOverlapWrite->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2283 * Creates a pipe object from a couple of file descriptors.
2284 * Useful for wrapping existing pipe FDs.
2286 * @param blocking_read creates an asynchronous pipe for reading if set to GNUNET_NO
2287 * @param blocking_write creates an asynchronous pipe for writing if set to GNUNET_NO
2288 * @param fd an array of two fd values. One of them may be -1 for read-only or write-only pipes
2290 * @return handle to the new pipe, NULL on error
2292 struct GNUNET_DISK_PipeHandle *
2293 GNUNET_DISK_pipe_from_fd (int blocking_read, int blocking_write, int fd[2])
2295 struct GNUNET_DISK_PipeHandle *p;
2297 p = GNUNET_new (struct GNUNET_DISK_PipeHandle);
2302 int eno = 0; /* make gcc happy */
2307 p->fd[0] = GNUNET_new (struct GNUNET_DISK_FileHandle);
2308 p->fd[0]->fd = fd[0];
2311 flags = fcntl (fd[0], F_GETFL);
2312 flags |= O_NONBLOCK;
2313 if (0 > fcntl (fd[0], F_SETFL, flags))
2319 flags = fcntl (fd[0], F_GETFD);
2320 flags |= FD_CLOEXEC;
2321 if (0 > fcntl (fd[0], F_SETFD, flags))
2330 p->fd[1] = GNUNET_new (struct GNUNET_DISK_FileHandle);
2331 p->fd[1]->fd = fd[1];
2332 if (!blocking_write)
2334 flags = fcntl (fd[1], F_GETFL);
2335 flags |= O_NONBLOCK;
2336 if (0 > fcntl (fd[1], F_SETFL, flags))
2342 flags = fcntl (fd[1], F_GETFD);
2343 flags |= FD_CLOEXEC;
2344 if (0 > fcntl (fd[1], F_SETFD, flags))
2353 LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR, "fcntl");
2354 if (p->fd[0]->fd >= 0)
2355 GNUNET_break (0 == close (p->fd[0]->fd));
2356 if (p->fd[1]->fd >= 0)
2357 GNUNET_break (0 == close (p->fd[1]->fd));
2358 GNUNET_free_non_null (p->fd[0]);
2359 GNUNET_free_non_null (p->fd[1]);
2367 p->fd[0] = GNUNET_new (struct GNUNET_DISK_FileHandle);
2368 p->fd[0]->h = (HANDLE) _get_osfhandle (fd[0]);
2369 if (p->fd[0]->h != INVALID_HANDLE_VALUE)
2371 p->fd[0]->type = GNUNET_DISK_HANLDE_TYPE_PIPE;
2372 p->fd[0]->oOverlapRead = GNUNET_new (OVERLAPPED);
2373 p->fd[0]->oOverlapWrite = GNUNET_new (OVERLAPPED);
2374 p->fd[0]->oOverlapRead->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2375 p->fd[0]->oOverlapWrite->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2379 GNUNET_free (p->fd[0]);
2385 p->fd[1] = GNUNET_new (struct GNUNET_DISK_FileHandle);
2386 p->fd[1]->h = (HANDLE) _get_osfhandle (fd[1]);
2387 if (p->fd[1]->h != INVALID_HANDLE_VALUE)
2389 p->fd[1]->type = GNUNET_DISK_HANLDE_TYPE_PIPE;
2390 p->fd[1]->oOverlapRead = GNUNET_new (OVERLAPPED);
2391 p->fd[1]->oOverlapWrite = GNUNET_new (OVERLAPPED);
2392 p->fd[1]->oOverlapRead->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2393 p->fd[1]->oOverlapWrite->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2397 GNUNET_free (p->fd[1]);
2408 * Closes an interprocess channel
2410 * @param p pipe to close
2411 * @param end which end of the pipe to close
2412 * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
2415 GNUNET_DISK_pipe_close_end (struct GNUNET_DISK_PipeHandle *p,
2416 enum GNUNET_DISK_PipeEnd end)
2418 int ret = GNUNET_OK;
2420 if (end == GNUNET_DISK_PIPE_END_READ)
2424 ret = GNUNET_DISK_file_close (p->fd[0]);
2428 else if (end == GNUNET_DISK_PIPE_END_WRITE)
2432 ret = GNUNET_DISK_file_close (p->fd[1]);
2441 * Detaches one of the ends from the pipe.
2442 * Detached end is a fully-functional FileHandle, it will
2443 * not be affected by anything you do with the pipe afterwards.
2444 * Each end of a pipe can only be detched from it once (i.e.
2445 * it is not duplicated).
2447 * @param p pipe to detach an end from
2448 * @param end which end of the pipe to detach
2449 * @return Detached end on success, NULL on failure
2450 * (or if that end is not present or is closed).
2452 struct GNUNET_DISK_FileHandle *
2453 GNUNET_DISK_pipe_detach_end (struct GNUNET_DISK_PipeHandle *p,
2454 enum GNUNET_DISK_PipeEnd end)
2456 struct GNUNET_DISK_FileHandle *ret = NULL;
2458 if (end == GNUNET_DISK_PIPE_END_READ)
2466 else if (end == GNUNET_DISK_PIPE_END_WRITE)
2480 * Closes an interprocess channel
2482 * @param p pipe to close
2483 * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
2486 GNUNET_DISK_pipe_close (struct GNUNET_DISK_PipeHandle *p)
2488 int ret = GNUNET_OK;
2491 int write_end_close;
2492 int read_end_close_errno;
2493 int write_end_close_errno;
2495 read_end_close = GNUNET_DISK_pipe_close_end (p, GNUNET_DISK_PIPE_END_READ);
2496 read_end_close_errno = errno;
2497 write_end_close = GNUNET_DISK_pipe_close_end (p, GNUNET_DISK_PIPE_END_WRITE);
2498 write_end_close_errno = errno;
2501 if (GNUNET_OK != read_end_close)
2503 errno = read_end_close_errno;
2504 ret = read_end_close;
2506 else if (GNUNET_OK != write_end_close)
2508 errno = write_end_close_errno;
2509 ret = write_end_close;
2517 * Get the handle to a particular pipe end
2520 * @param n end to access
2521 * @return handle for the respective end
2523 const struct GNUNET_DISK_FileHandle *
2524 GNUNET_DISK_pipe_handle (const struct GNUNET_DISK_PipeHandle *p,
2525 enum GNUNET_DISK_PipeEnd n)
2529 case GNUNET_DISK_PIPE_END_READ:
2530 case GNUNET_DISK_PIPE_END_WRITE:
2540 * Retrieve OS file handle
2542 * @param fh GNUnet file descriptor
2543 * @param dst destination buffer
2544 * @param dst_len length of dst
2545 * @return #GNUNET_OK on success, #GNUNET_SYSERR otherwise
2548 GNUNET_DISK_internal_file_handle_ (const struct GNUNET_DISK_FileHandle *fh,
2549 void *dst, size_t dst_len)
2552 return GNUNET_SYSERR;
2554 if (dst_len < sizeof (HANDLE))
2555 return GNUNET_SYSERR;
2556 *((HANDLE *) dst) = fh->h;
2558 if (dst_len < sizeof (int))
2559 return GNUNET_SYSERR;
2560 *((int *) dst) = fh->fd;