2 This file is part of GNUnet.
3 Copyright (C) 2001--2013, 2016, 2018 GNUnet e.V.
5 GNUnet is free software: you can redistribute it and/or modify it
6 under the terms of the GNU Affero General Public License as published
7 by the Free Software Foundation, either version 3 of the License,
8 or (at your 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 Affero General Public License for more details.
15 You should have received a copy of the GNU Affero General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
20 * @brief disk IO convenience methods
21 * @author Christian Grothoff
26 #include "gnunet_strings_lib.h"
27 #include "gnunet_disk_lib.h"
29 #define LOG(kind,...) GNUNET_log_from (kind, "util-disk", __VA_ARGS__)
31 #define LOG_STRERROR(kind,syscall) GNUNET_log_from_strerror (kind, "util-disk", syscall)
33 #define LOG_STRERROR_FILE(kind,syscall,filename) GNUNET_log_from_strerror_file (kind, "util-disk", syscall, filename)
36 * Block size for IO for copying files.
38 #define COPY_BLK_SIZE 65536
40 #include <sys/types.h>
45 #include <sys/param.h>
48 #include <sys/mount.h>
50 #if HAVE_SYS_STATVFS_H
51 #include <sys/statvfs.h>
55 #define _IFMT 0170000 /* type of file */
56 #define _IFLNK 0120000 /* symbolic link */
57 #define S_ISLNK(m) (((m)&_IFMT) == _IFLNK)
62 * Handle used to manage a pipe.
64 struct GNUNET_DISK_PipeHandle
67 * File descriptors for the pipe.
68 * One or both of them could be NULL.
70 struct GNUNET_DISK_FileHandle *fd[2];
75 * Closure for the recursion to determine the file size
78 struct GetFileSizeData
81 * Set to the total file size.
86 * GNUNET_YES if symbolic links should be included.
88 int include_sym_links;
91 * GNUNET_YES if mode is file-only (return total == -1 for directories).
99 * Translate GNUnet-internal permission bitmap to UNIX file
100 * access permission bitmap.
102 * @param perm file permissions, GNUnet style
103 * @return file permissions, UNIX style
106 translate_unix_perms (enum GNUNET_DISK_AccessPermissions perm)
111 if (perm & GNUNET_DISK_PERM_USER_READ)
113 if (perm & GNUNET_DISK_PERM_USER_WRITE)
115 if (perm & GNUNET_DISK_PERM_USER_EXEC)
117 if (perm & GNUNET_DISK_PERM_GROUP_READ)
119 if (perm & GNUNET_DISK_PERM_GROUP_WRITE)
121 if (perm & GNUNET_DISK_PERM_GROUP_EXEC)
123 if (perm & GNUNET_DISK_PERM_OTHER_READ)
125 if (perm & GNUNET_DISK_PERM_OTHER_WRITE)
127 if (perm & GNUNET_DISK_PERM_OTHER_EXEC)
136 * Iterate over all files in the given directory and
137 * accumulate their size.
139 * @param cls closure of type `struct GetFileSizeData`
140 * @param fn current filename we are looking at
141 * @return #GNUNET_SYSERR on serious errors, otherwise #GNUNET_OK
144 getSizeRec (void *cls, const char *fn)
146 struct GetFileSizeData *gfsd = cls;
148 #if defined (HAVE_STAT64) && !(defined (_FILE_OFFSET_BITS) && _FILE_OFFSET_BITS == 64)
151 if (0 != STAT64 (fn, &buf))
153 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_DEBUG, "stat64", fn);
154 return GNUNET_SYSERR;
159 if (0 != STAT (fn, &buf))
161 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_DEBUG, "stat", fn);
162 return GNUNET_SYSERR;
165 if ((S_ISDIR (buf.st_mode)) && (gfsd->single_file_mode == GNUNET_YES))
168 return GNUNET_SYSERR;
170 if ((!S_ISLNK (buf.st_mode)) || (gfsd->include_sym_links == GNUNET_YES))
171 gfsd->total += buf.st_size;
172 if ((S_ISDIR (buf.st_mode)) && (0 == ACCESS (fn, X_OK)) &&
173 ((!S_ISLNK (buf.st_mode)) || (gfsd->include_sym_links == GNUNET_YES)))
175 if (GNUNET_SYSERR == GNUNET_DISK_directory_scan (fn, &getSizeRec, gfsd))
176 return GNUNET_SYSERR;
183 * Checks whether a handle is invalid
185 * @param h handle to check
186 * @return #GNUNET_YES if invalid, #GNUNET_NO if valid
189 GNUNET_DISK_handle_invalid (const struct GNUNET_DISK_FileHandle *h)
192 return ((!h) || (h->h == INVALID_HANDLE_VALUE)) ? GNUNET_YES : GNUNET_NO;
194 return ((!h) || (h->fd == -1)) ? GNUNET_YES : GNUNET_NO;
199 * Get the size of an open file.
201 * @param fh open file handle
202 * @param size where to write size of the file
203 * @return #GNUNET_OK on success, #GNUNET_SYSERR on error
206 GNUNET_DISK_file_handle_size (struct GNUNET_DISK_FileHandle *fh,
212 b = GetFileSizeEx (fh->h, &li);
215 SetErrnoFromWinError (GetLastError ());
216 return GNUNET_SYSERR;
218 *size = (off_t) li.QuadPart;
222 if (0 != FSTAT (fh->fd, &sbuf))
223 return GNUNET_SYSERR;
224 *size = sbuf.st_size;
231 * Move the read/write pointer in a file
233 * @param h handle of an open file
234 * @param offset position to move to
235 * @param whence specification to which position the offset parameter relates to
236 * @return the new position on success, #GNUNET_SYSERR otherwise
239 GNUNET_DISK_file_seek (const struct GNUNET_DISK_FileHandle *h,
241 enum GNUNET_DISK_Seek whence)
246 return GNUNET_SYSERR;
251 LARGE_INTEGER new_pos;
254 static DWORD t[] = { FILE_BEGIN, FILE_CURRENT, FILE_END };
255 li.QuadPart = offset;
257 b = SetFilePointerEx (h->h, li, &new_pos, t[whence]);
260 SetErrnoFromWinError (GetLastError ());
261 return GNUNET_SYSERR;
263 return (off_t) new_pos.QuadPart;
265 static int t[] = { SEEK_SET, SEEK_CUR, SEEK_END };
267 return lseek (h->fd, offset, t[whence]);
273 * Get the size of the file (or directory) of the given file (in
276 * @param filename name of the file or directory
277 * @param size set to the size of the file (or,
278 * in the case of directories, the sum
279 * of all sizes of files in the directory)
280 * @param include_symbolic_links should symbolic links be
282 * @param single_file_mode #GNUNET_YES to only get size of one file
283 * and return #GNUNET_SYSERR for directories.
284 * @return #GNUNET_SYSERR on error, #GNUNET_OK on success
287 GNUNET_DISK_file_size (const char *filename,
289 int include_symbolic_links,
290 int single_file_mode)
292 struct GetFileSizeData gfsd;
295 GNUNET_assert (size != NULL);
297 gfsd.include_sym_links = include_symbolic_links;
298 gfsd.single_file_mode = single_file_mode;
299 ret = getSizeRec (&gfsd, filename);
306 * Obtain some unique identifiers for the given file
307 * that can be used to identify it in the local system.
308 * This function is used between GNUnet processes to
309 * quickly check if two files with the same absolute path
310 * are actually identical. The two processes represent
311 * the same peer but may communicate over the network
312 * (and the file may be on an NFS volume). This function
313 * may not be supported on all operating systems.
315 * @param filename name of the file
316 * @param dev set to the device ID
317 * @param ino set to the inode ID
318 * @return #GNUNET_OK on success
321 GNUNET_DISK_file_get_identifiers (const char *filename, uint64_t * dev,
326 // FIXME NILS: test this
327 struct GNUNET_DISK_FileHandle *fh;
328 BY_HANDLE_FILE_INFORMATION info;
331 fh = GNUNET_DISK_file_open (filename,
332 GNUNET_DISK_OPEN_READ,
333 GNUNET_DISK_PERM_NONE);
335 return GNUNET_SYSERR;
336 succ = GetFileInformationByHandle (fh->h, &info);
337 GNUNET_DISK_file_close (fh);
340 return GNUNET_SYSERR;
342 *dev = info.dwVolumeSerialNumber;
343 *ino = ((((uint64_t) info.nFileIndexHigh) << (sizeof (DWORD) * 8)) | info.nFileIndexLow);
350 if (0 != stat (filename, &sbuf))
352 return GNUNET_SYSERR;
354 *ino = (uint64_t) sbuf.st_ino;
363 if (0 != statvfs (filename, &fbuf))
365 return GNUNET_SYSERR;
367 *dev = (uint64_t) fbuf.f_fsid;
373 if (0 != statfs (filename, &fbuf))
375 return GNUNET_SYSERR;
377 *dev = ((uint64_t) fbuf.f_fsid.val[0]) << 32 ||
378 ((uint64_t) fbuf.f_fsid.val[1]);
383 #endif /* !WINDOWS */
389 * Create the name for a temporary file or directory from a template.
391 * @param t template (without XXXXX or "/tmp/")
392 * @return name ready for passing to 'mktemp' or 'mkdtemp', NULL on error
395 mktemp_name (const char *t)
401 if ((t[0] != '/') && (t[0] != '\\')
403 && !(isalpha ((int) t[0]) && (t[0] != '\0') && (t[1] == ':'))
407 /* FIXME: This uses system codepage on W32, not UTF-8 */
408 tmpdir = getenv ("TMPDIR");
410 tmpdir = getenv ("TMP");
412 tmpdir = getenv ("TEMP");
415 GNUNET_asprintf (&tmpl, "%s/%s%s", tmpdir, t, "XXXXXX");
419 GNUNET_asprintf (&tmpl, "%s%s", t, "XXXXXX");
422 fn = (char *) GNUNET_malloc (MAX_PATH + 1);
423 if (ERROR_SUCCESS != plibc_conv_to_win_path (tmpl, fn))
446 tfn = GNUNET_strdup (fn);
447 random_fn = _mktemp (tfn);
448 if (NULL == random_fn)
453 /* FIXME: assume fn to be UTF-8-encoded and do the right thing */
454 if (0 == CreateDirectoryA (tfn, NULL))
456 DWORD error = GetLastError ();
458 if (ERROR_ALREADY_EXISTS == error)
469 * Update POSIX permissions mask of a file on disk. If both argumets
470 * are #GNUNET_NO, the file is made world-read-write-executable (777).
471 * Does nothing on W32.
473 * @param fn name of the file to update
474 * @param require_uid_match #GNUNET_YES means 700
475 * @param require_gid_match #GNUNET_YES means 770 unless @a require_uid_match is set
478 GNUNET_DISK_fix_permissions (const char *fn,
479 int require_uid_match,
480 int require_gid_match)
488 * Update POSIX permissions mask of a file on disk. If both argumets
489 * are #GNUNET_NO, the file is made world-read-write-executable (777).
491 * @param fn name of the file to update
492 * @param require_uid_match #GNUNET_YES means 700
493 * @param require_gid_match #GNUNET_YES means 770 unless @a require_uid_match is set
496 GNUNET_DISK_fix_permissions (const char *fn,
497 int require_uid_match,
498 int require_gid_match)
502 if (GNUNET_YES == require_uid_match)
503 mode = S_IRUSR | S_IWUSR | S_IXUSR;
504 else if (GNUNET_YES == require_gid_match)
505 mode = S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IWGRP | S_IXGRP;
507 mode = S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IWGRP | S_IXGRP | S_IROTH | S_IWOTH | S_IXOTH;
508 if (0 != chmod (fn, mode))
509 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING,
517 * Create an (empty) temporary directory on disk. If the given name is not
518 * an absolute path, the current 'TMPDIR' will be prepended. In any case,
519 * 6 random characters will be appended to the name to create a unique
522 * @param t component to use for the name;
523 * does NOT contain "XXXXXX" or "/tmp/".
524 * @return NULL on error, otherwise name of fresh
525 * file on disk in directory for temporary files
528 GNUNET_DISK_mkdtemp (const char *t)
533 omask = umask (S_IWGRP | S_IWOTH | S_IRGRP | S_IROTH);
534 fn = mktemp_name (t);
535 if (fn != mkdtemp (fn))
537 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_ERROR, "mkdtemp", fn);
548 * Move a file out of the way (create a backup) by
549 * renaming it to "orig.NUM~" where NUM is the smallest
550 * number that is not used yet.
552 * @param fil name of the file to back up
555 GNUNET_DISK_file_backup (const char *fil)
561 slen = strlen (fil) + 20;
562 target = GNUNET_malloc (slen);
566 GNUNET_snprintf (target, slen,
570 } while (0 == access (target, F_OK));
571 if (0 != rename (fil, target))
572 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR,
575 GNUNET_free (target);
580 * Create an (empty) temporary file on disk. If the given name is not
581 * an absolute path, the current 'TMPDIR' will be prepended. In any case,
582 * 6 random characters will be appended to the name to create a unique
585 * @param t component to use for the name;
586 * does NOT contain "XXXXXX" or "/tmp/".
587 * @return NULL on error, otherwise name of fresh
588 * file on disk in directory for temporary files
591 GNUNET_DISK_mktemp (const char *t)
597 omask = umask (S_IWGRP | S_IWOTH | S_IRGRP | S_IROTH);
598 fn = mktemp_name (t);
599 if (-1 == (fd = mkstemp (fn)))
601 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_ERROR, "mkstemp", fn);
608 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "close", fn);
614 * Test if @a fil is a directory and listable. Optionally, also check if the
615 * directory is readable. Will not print an error message if the directory does
616 * not exist. Will log errors if #GNUNET_SYSERR is returned (i.e., a file exists
617 * with the same name).
619 * @param fil filename to test
620 * @param is_readable #GNUNET_YES to additionally check if @a fil is readable;
621 * #GNUNET_NO to disable this check
622 * @return #GNUNET_YES if yes, #GNUNET_NO if not; #GNUNET_SYSERR if it
623 * does not exist or stat'ed
626 GNUNET_DISK_directory_test (const char *fil, int is_readable)
628 struct stat filestat;
631 ret = STAT (fil, &filestat);
635 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "stat", fil);
636 return GNUNET_SYSERR;
638 if (!S_ISDIR (filestat.st_mode))
640 LOG (GNUNET_ERROR_TYPE_DEBUG,
641 "A file already exits with the same name %s\n", fil);
644 if (GNUNET_YES == is_readable)
645 ret = ACCESS (fil, R_OK | X_OK);
647 ret = ACCESS (fil, X_OK);
650 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "access", fil);
658 * Check that fil corresponds to a filename
659 * (of a file that exists and that is not a directory).
661 * @param fil filename to check
662 * @return #GNUNET_YES if yes, #GNUNET_NO if not a file, #GNUNET_SYSERR if something
663 * else (will print an error message in that case, too).
666 GNUNET_DISK_file_test (const char *fil)
668 struct stat filestat;
672 rdir = GNUNET_STRINGS_filename_expand (fil);
674 return GNUNET_SYSERR;
676 ret = STAT (rdir, &filestat);
681 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "stat", rdir);
683 return GNUNET_SYSERR;
688 if (!S_ISREG (filestat.st_mode))
693 if (ACCESS (rdir, F_OK) < 0)
695 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "access", rdir);
697 return GNUNET_SYSERR;
705 * Implementation of "mkdir -p"
707 * @param dir the directory to create
708 * @return #GNUNET_OK on success, #GNUNET_SYSERR on failure
711 GNUNET_DISK_directory_create (const char *dir)
719 rdir = GNUNET_STRINGS_filename_expand (dir);
721 return GNUNET_SYSERR;
725 pos = 1; /* skip heading '/' */
727 /* Local or Network path? */
728 if (strncmp (rdir, "\\\\", 2) == 0)
733 if (rdir[pos] == '\\')
743 pos = 3; /* strlen("C:\\") */
746 /* Check which low level directories already exist */
748 rdir[len] = DIR_SEPARATOR;
751 if (DIR_SEPARATOR == rdir[pos2])
754 ret = GNUNET_DISK_directory_test (rdir, GNUNET_NO);
755 if (GNUNET_NO == ret)
758 return GNUNET_SYSERR;
760 rdir[pos2] = DIR_SEPARATOR;
761 if (GNUNET_YES == ret)
772 /* Start creating directories */
775 if ((rdir[pos] == DIR_SEPARATOR) || (pos == len))
778 ret = GNUNET_DISK_directory_test (rdir, GNUNET_NO);
779 if (GNUNET_NO == ret)
782 return GNUNET_SYSERR;
784 if (GNUNET_SYSERR == ret)
787 ret = mkdir (rdir, S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH); /* 755 */
789 wchar_t wrdir[MAX_PATH + 1];
790 if (ERROR_SUCCESS == plibc_conv_to_win_pathwconv(rdir, wrdir))
791 ret = !CreateDirectoryW (wrdir, NULL);
795 if ((ret != 0) && (errno != EEXIST))
797 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_ERROR, "mkdir", rdir);
799 return GNUNET_SYSERR;
802 rdir[pos] = DIR_SEPARATOR;
812 * Create the directory structure for storing a file.
814 * @param filename name of a file in the directory
815 * @returns #GNUNET_OK on success,
816 * #GNUNET_SYSERR on failure,
817 * #GNUNET_NO if the directory
818 * exists but is not writeable for us
821 GNUNET_DISK_directory_create_for_file (const char *filename)
828 rdir = GNUNET_STRINGS_filename_expand (filename);
832 return GNUNET_SYSERR;
834 if (0 == ACCESS (rdir, W_OK))
841 while ((len > 0) && (rdir[len] != DIR_SEPARATOR))
844 /* The empty path is invalid and in this case refers to / */
848 rdir = GNUNET_strdup ("/");
850 ret = GNUNET_DISK_directory_create (rdir);
851 if ((GNUNET_OK == ret) && (0 != ACCESS (rdir, W_OK)))
861 * Read the contents of a binary file into a buffer.
863 * @param h handle to an open file
864 * @param result the buffer to write the result to
865 * @param len the maximum number of bytes to read
866 * @return the number of bytes read on success, #GNUNET_SYSERR on failure
869 GNUNET_DISK_file_read (const struct GNUNET_DISK_FileHandle *h,
876 return GNUNET_SYSERR;
882 if (h->type == GNUNET_DISK_HANLDE_TYPE_FILE)
884 if (!ReadFile (h->h, result, len, &bytes_read, NULL))
886 SetErrnoFromWinError (GetLastError ());
887 return GNUNET_SYSERR;
890 else if (h->type == GNUNET_DISK_HANLDE_TYPE_PIPE)
892 if (!ReadFile (h->h, result, len, &bytes_read, h->oOverlapRead))
894 if (GetLastError () != ERROR_IO_PENDING)
896 LOG (GNUNET_ERROR_TYPE_DEBUG,
897 "Error reading from pipe: %u\n",
899 SetErrnoFromWinError (GetLastError ());
900 return GNUNET_SYSERR;
902 LOG (GNUNET_ERROR_TYPE_DEBUG, "Will get overlapped result\n");
903 GetOverlappedResult (h->h, h->oOverlapRead, &bytes_read, TRUE);
905 LOG (GNUNET_ERROR_TYPE_DEBUG, "Read %u bytes from pipe\n", bytes_read);
913 return read (h->fd, result, len);
919 * Read the contents of a binary file into a buffer.
920 * Guarantees not to block (returns GNUNET_SYSERR and sets errno to EAGAIN
921 * when no data can be read).
923 * @param h handle to an open file
924 * @param result the buffer to write the result to
925 * @param len the maximum number of bytes to read
926 * @return the number of bytes read on success, #GNUNET_SYSERR on failure
929 GNUNET_DISK_file_read_non_blocking (const struct GNUNET_DISK_FileHandle *h,
936 return GNUNET_SYSERR;
942 if (h->type == GNUNET_DISK_HANLDE_TYPE_FILE)
944 if (!ReadFile (h->h, result, len, &bytes_read, NULL))
946 SetErrnoFromWinError (GetLastError ());
947 return GNUNET_SYSERR;
950 else if (h->type == GNUNET_DISK_HANLDE_TYPE_PIPE)
952 if (!ReadFile (h->h, result, len, &bytes_read, h->oOverlapRead))
954 if (GetLastError () != ERROR_IO_PENDING)
956 LOG (GNUNET_ERROR_TYPE_DEBUG, "Error reading from pipe: %u\n", GetLastError ());
957 SetErrnoFromWinError (GetLastError ());
958 return GNUNET_SYSERR;
962 LOG (GNUNET_ERROR_TYPE_DEBUG,
963 "ReadFile() queued a read, cancelling\n");
966 return GNUNET_SYSERR;
969 LOG (GNUNET_ERROR_TYPE_DEBUG,
982 /* set to non-blocking, read, then set back */
983 flags = fcntl (h->fd, F_GETFL);
984 if (0 == (flags & O_NONBLOCK))
985 (void) fcntl (h->fd, F_SETFL, flags | O_NONBLOCK);
986 ret = read (h->fd, result, len);
987 if (0 == (flags & O_NONBLOCK))
990 (void) fcntl (h->fd, F_SETFL, flags);
999 * Read the contents of a binary file into a buffer.
1001 * @param fn file name
1002 * @param result the buffer to write the result to
1003 * @param len the maximum number of bytes to read
1004 * @return number of bytes read, #GNUNET_SYSERR on failure
1007 GNUNET_DISK_fn_read (const char *fn,
1011 struct GNUNET_DISK_FileHandle *fh;
1015 fh = GNUNET_DISK_file_open (fn,
1016 GNUNET_DISK_OPEN_READ,
1017 GNUNET_DISK_PERM_NONE);
1019 return GNUNET_SYSERR;
1020 ret = GNUNET_DISK_file_read (fh, result, len);
1022 GNUNET_assert (GNUNET_OK == GNUNET_DISK_file_close (fh));
1029 * Write a buffer to a file.
1031 * @param h handle to open file
1032 * @param buffer the data to write
1033 * @param n number of bytes to write
1034 * @return number of bytes written on success, #GNUNET_SYSERR on error
1037 GNUNET_DISK_file_write (const struct GNUNET_DISK_FileHandle * h,
1044 return GNUNET_SYSERR;
1048 DWORD bytes_written;
1050 if (h->type == GNUNET_DISK_HANLDE_TYPE_FILE)
1052 if (!WriteFile (h->h, buffer, n, &bytes_written, NULL))
1054 SetErrnoFromWinError (GetLastError ());
1055 return GNUNET_SYSERR;
1058 else if (h->type == GNUNET_DISK_HANLDE_TYPE_PIPE)
1060 LOG (GNUNET_ERROR_TYPE_DEBUG, "It is a pipe trying to write %u bytes\n", n);
1061 if (!WriteFile (h->h, buffer, n, &bytes_written, h->oOverlapWrite))
1063 if (GetLastError () != ERROR_IO_PENDING)
1065 SetErrnoFromWinError (GetLastError ());
1066 LOG (GNUNET_ERROR_TYPE_DEBUG, "Error writing to pipe: %u\n",
1068 return GNUNET_SYSERR;
1070 LOG (GNUNET_ERROR_TYPE_DEBUG, "Will get overlapped result\n");
1071 if (!GetOverlappedResult (h->h, h->oOverlapWrite, &bytes_written, TRUE))
1073 SetErrnoFromWinError (GetLastError ());
1074 LOG (GNUNET_ERROR_TYPE_DEBUG,
1075 "Error getting overlapped result while writing to pipe: %u\n",
1077 return GNUNET_SYSERR;
1083 if (!GetOverlappedResult (h->h, h->oOverlapWrite, &ovr, TRUE))
1085 LOG (GNUNET_ERROR_TYPE_DEBUG,
1086 "Error getting control overlapped result while writing to pipe: %u\n",
1091 LOG (GNUNET_ERROR_TYPE_DEBUG,
1092 "Wrote %u bytes (ovr says %u), picking the greatest\n",
1093 bytes_written, ovr);
1096 if (bytes_written == 0)
1100 LOG (GNUNET_ERROR_TYPE_DEBUG, "Wrote %u bytes, returning -1 with EAGAIN\n", bytes_written);
1102 return GNUNET_SYSERR;
1105 LOG (GNUNET_ERROR_TYPE_DEBUG, "Wrote %u bytes\n", bytes_written);
1111 return bytes_written;
1113 return write (h->fd, buffer, n);
1119 * Write a buffer to a file, blocking, if necessary.
1121 * @param h handle to open file
1122 * @param buffer the data to write
1123 * @param n number of bytes to write
1124 * @return number of bytes written on success, #GNUNET_SYSERR on error
1127 GNUNET_DISK_file_write_blocking (const struct GNUNET_DISK_FileHandle * h,
1134 return GNUNET_SYSERR;
1138 DWORD bytes_written;
1139 /* We do a non-overlapped write, which is as blocking as it gets */
1140 LOG (GNUNET_ERROR_TYPE_DEBUG, "Writing %u bytes\n", n);
1141 if (!WriteFile (h->h, buffer, n, &bytes_written, NULL))
1143 SetErrnoFromWinError (GetLastError ());
1144 LOG (GNUNET_ERROR_TYPE_DEBUG, "Error writing to pipe: %u\n",
1146 return GNUNET_SYSERR;
1148 if (bytes_written == 0 && n > 0)
1150 LOG (GNUNET_ERROR_TYPE_DEBUG, "Waiting for pipe to clean\n");
1151 WaitForSingleObject (h->h, INFINITE);
1152 if (!WriteFile (h->h, buffer, n, &bytes_written, NULL))
1154 SetErrnoFromWinError (GetLastError ());
1155 LOG (GNUNET_ERROR_TYPE_DEBUG, "Error writing to pipe: %u\n",
1157 return GNUNET_SYSERR;
1160 LOG (GNUNET_ERROR_TYPE_DEBUG,
1163 return bytes_written;
1168 /* set to blocking, write, then set back */
1169 flags = fcntl (h->fd, F_GETFL);
1170 if (0 != (flags & O_NONBLOCK))
1171 (void) fcntl (h->fd, F_SETFL, flags - O_NONBLOCK);
1172 ret = write (h->fd, buffer, n);
1173 if (0 == (flags & O_NONBLOCK))
1174 (void) fcntl (h->fd, F_SETFL, flags);
1181 * Write a buffer to a file. If the file is longer than the
1182 * number of bytes that will be written, it will be truncated.
1184 * @param fn file name
1185 * @param buffer the data to write
1186 * @param n number of bytes to write
1187 * @param mode file permissions
1188 * @return number of bytes written on success, #GNUNET_SYSERR on error
1191 GNUNET_DISK_fn_write (const char *fn,
1194 enum GNUNET_DISK_AccessPermissions mode)
1196 struct GNUNET_DISK_FileHandle *fh;
1199 fh = GNUNET_DISK_file_open (fn,
1200 GNUNET_DISK_OPEN_WRITE | GNUNET_DISK_OPEN_TRUNCATE
1201 | GNUNET_DISK_OPEN_CREATE, mode);
1203 return GNUNET_SYSERR;
1204 ret = GNUNET_DISK_file_write (fh, buffer, n);
1205 GNUNET_assert (GNUNET_OK == GNUNET_DISK_file_close (fh));
1211 * Scan a directory for files.
1213 * @param dir_name the name of the directory
1214 * @param callback the method to call for each file,
1215 * can be NULL, in that case, we only count
1216 * @param callback_cls closure for @a callback
1217 * @return the number of files found, #GNUNET_SYSERR on error or
1218 * ieration aborted by callback returning #GNUNET_SYSERR
1221 GNUNET_DISK_directory_scan (const char *dir_name,
1222 GNUNET_FileNameCallback callback,
1226 struct dirent *finfo;
1232 unsigned int name_len;
1233 unsigned int n_size;
1235 GNUNET_assert (NULL != dir_name);
1236 dname = GNUNET_STRINGS_filename_expand (dir_name);
1238 return GNUNET_SYSERR;
1239 while ( (strlen (dname) > 0) &&
1240 (dname[strlen (dname) - 1] == DIR_SEPARATOR) )
1241 dname[strlen (dname) - 1] = '\0';
1242 if (0 != STAT (dname, &istat))
1244 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING,
1247 GNUNET_free (dname);
1248 return GNUNET_SYSERR;
1250 if (! S_ISDIR (istat.st_mode))
1252 LOG (GNUNET_ERROR_TYPE_WARNING,
1253 _("Expected `%s' to be a directory!\n"),
1255 GNUNET_free (dname);
1256 return GNUNET_SYSERR;
1259 dinfo = OPENDIR (dname);
1260 if ( (EACCES == errno) ||
1263 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING,
1268 GNUNET_free (dname);
1269 return GNUNET_SYSERR;
1272 n_size = strlen (dname) + name_len + strlen (DIR_SEPARATOR_STR) + 1;
1273 name = GNUNET_malloc (n_size);
1274 while (NULL != (finfo = READDIR (dinfo)))
1276 if ( (0 == strcmp (finfo->d_name, ".")) ||
1277 (0 == strcmp (finfo->d_name, "..")) )
1279 if (NULL != callback)
1281 if (name_len < strlen (finfo->d_name))
1284 name_len = strlen (finfo->d_name);
1285 n_size = strlen (dname) + name_len + strlen (DIR_SEPARATOR_STR) + 1;
1286 name = GNUNET_malloc (n_size);
1288 /* dname can end in "/" only if dname == "/";
1289 * if dname does not end in "/", we need to add
1290 * a "/" (otherwise, we must not!) */
1291 GNUNET_snprintf (name,
1295 (0 == strcmp (dname,
1298 : DIR_SEPARATOR_STR,
1300 ret = callback (callback_cls,
1302 if (GNUNET_OK != ret)
1306 GNUNET_free (dname);
1307 if (GNUNET_NO == ret)
1309 return GNUNET_SYSERR;
1316 GNUNET_free (dname);
1322 * Function that removes the given directory by calling
1323 * #GNUNET_DISK_directory_remove().
1325 * @param unused not used
1326 * @param fn directory to remove
1327 * @return #GNUNET_OK
1330 remove_helper (void *unused,
1334 (void) GNUNET_DISK_directory_remove (fn);
1340 * Remove all files in a directory (rm -r). Call with
1343 * @param filename the file to remove
1344 * @return #GNUNET_OK on success, #GNUNET_SYSERR on error
1347 GNUNET_DISK_directory_remove (const char *filename)
1351 if (NULL == filename)
1354 return GNUNET_SYSERR;
1356 if (0 != LSTAT (filename, &istat))
1357 return GNUNET_NO; /* file may not exist... */
1358 (void) CHMOD (filename,
1359 S_IWUSR | S_IRUSR | S_IXUSR);
1360 if (0 == UNLINK (filename))
1362 if ( (errno != EISDIR) &&
1363 /* EISDIR is not sufficient in all cases, e.g.
1364 * sticky /tmp directory may result in EPERM on BSD.
1365 * So we also explicitly check "isDirectory" */
1367 GNUNET_DISK_directory_test (filename,
1370 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING,
1373 return GNUNET_SYSERR;
1375 if (GNUNET_SYSERR ==
1376 GNUNET_DISK_directory_scan (filename,
1379 return GNUNET_SYSERR;
1380 if (0 != RMDIR (filename))
1382 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING,
1385 return GNUNET_SYSERR;
1394 * @param src file to copy
1395 * @param dst destination file name
1396 * @return #GNUNET_OK on success, #GNUNET_SYSERR on error
1399 GNUNET_DISK_file_copy (const char *src,
1407 struct GNUNET_DISK_FileHandle *in;
1408 struct GNUNET_DISK_FileHandle *out;
1411 GNUNET_DISK_file_size (src,
1416 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR,
1419 return GNUNET_SYSERR;
1422 in = GNUNET_DISK_file_open (src,
1423 GNUNET_DISK_OPEN_READ,
1424 GNUNET_DISK_PERM_NONE);
1427 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR,
1430 return GNUNET_SYSERR;
1433 GNUNET_DISK_file_open (dst,
1434 GNUNET_DISK_OPEN_WRITE | GNUNET_DISK_OPEN_CREATE |
1435 GNUNET_DISK_OPEN_FAILIFEXISTS,
1436 GNUNET_DISK_PERM_USER_READ |
1437 GNUNET_DISK_PERM_USER_WRITE |
1438 GNUNET_DISK_PERM_GROUP_READ |
1439 GNUNET_DISK_PERM_GROUP_WRITE);
1442 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR,
1445 GNUNET_DISK_file_close (in);
1446 return GNUNET_SYSERR;
1448 buf = GNUNET_malloc (COPY_BLK_SIZE);
1451 len = COPY_BLK_SIZE;
1452 if (len > size - pos)
1454 sret = GNUNET_DISK_file_read (in,
1458 (len != (size_t) sret) )
1460 sret = GNUNET_DISK_file_write (out,
1464 (len != (size_t) sret) )
1469 GNUNET_DISK_file_close (in);
1470 GNUNET_DISK_file_close (out);
1474 GNUNET_DISK_file_close (in);
1475 GNUNET_DISK_file_close (out);
1476 return GNUNET_SYSERR;
1481 * @brief Removes special characters as ':' from a filename.
1482 * @param fn the filename to canonicalize
1485 GNUNET_DISK_filename_canonicalize (char *fn)
1490 for (idx = fn; *idx; idx++)
1494 if (c == '/' || c == '\\' || c == ':' ||
1495 c == '*' || c == '?' || c == '"' ||
1496 c == '<' || c == '>' || c == '|')
1506 * @brief Change owner of a file
1508 * @param filename name of file to change the owner of
1509 * @param user name of the new owner
1510 * @return #GNUNET_OK on success, #GNUNET_SYSERR on failure
1513 GNUNET_DISK_file_change_owner (const char *filename,
1519 pws = getpwnam (user);
1522 LOG (GNUNET_ERROR_TYPE_ERROR,
1523 _("Cannot obtain information about user `%s': %s\n"),
1526 return GNUNET_SYSERR;
1528 if (0 != chown (filename,
1532 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING,
1535 return GNUNET_SYSERR;
1543 * Lock a part of a file
1545 * @param fh file handle
1546 * @param lock_start absolute position from where to lock
1547 * @param lock_end absolute position until where to lock
1548 * @param excl #GNUNET_YES for an exclusive lock
1549 * @return #GNUNET_OK on success, #GNUNET_SYSERR on error
1552 GNUNET_DISK_file_lock (struct GNUNET_DISK_FileHandle *fh,
1560 return GNUNET_SYSERR;
1566 memset (&fl, 0, sizeof (struct flock));
1567 fl.l_type = excl ? F_WRLCK : F_RDLCK;
1568 fl.l_whence = SEEK_SET;
1569 fl.l_start = lock_start;
1570 fl.l_len = lock_end;
1572 return fcntl (fh->fd, F_SETLK, &fl) != 0 ? GNUNET_SYSERR : GNUNET_OK;
1575 off_t diff = lock_end - lock_start;
1576 DWORD diff_low, diff_high;
1577 diff_low = (DWORD) (diff & 0xFFFFFFFF);
1578 diff_high = (DWORD) ((diff >> (sizeof (DWORD) * 8)) & 0xFFFFFFFF);
1580 memset (&o, 0, sizeof (OVERLAPPED));
1581 o.Offset = (DWORD) (lock_start & 0xFFFFFFFF);;
1582 o.OffsetHigh = (DWORD) (((lock_start & ~0xFFFFFFFF) >> (sizeof (DWORD) * 8)) & 0xFFFFFFFF);
1585 (fh->h, (excl ? LOCKFILE_EXCLUSIVE_LOCK : 0) | LOCKFILE_FAIL_IMMEDIATELY,
1586 0, diff_low, diff_high, &o))
1588 SetErrnoFromWinError (GetLastError ());
1589 return GNUNET_SYSERR;
1598 * Unlock a part of a file
1600 * @param fh file handle
1601 * @param unlock_start absolute position from where to unlock
1602 * @param unlock_end absolute position until where to unlock
1603 * @return #GNUNET_OK on success, #GNUNET_SYSERR on error
1606 GNUNET_DISK_file_unlock (struct GNUNET_DISK_FileHandle *fh,
1613 return GNUNET_SYSERR;
1619 memset (&fl, 0, sizeof (struct flock));
1620 fl.l_type = F_UNLCK;
1621 fl.l_whence = SEEK_SET;
1622 fl.l_start = unlock_start;
1623 fl.l_len = unlock_end;
1625 return fcntl (fh->fd, F_SETLK, &fl) != 0 ? GNUNET_SYSERR : GNUNET_OK;
1628 off_t diff = unlock_end - unlock_start;
1629 DWORD diff_low, diff_high;
1630 diff_low = (DWORD) (diff & 0xFFFFFFFF);
1631 diff_high = (DWORD) ((diff >> (sizeof (DWORD) * 8)) & 0xFFFFFFFF);
1633 memset (&o, 0, sizeof (OVERLAPPED));
1634 o.Offset = (DWORD) (unlock_start & 0xFFFFFFFF);;
1635 o.OffsetHigh = (DWORD) (((unlock_start & ~0xFFFFFFFF) >> (sizeof (DWORD) * 8)) & 0xFFFFFFFF);
1637 if (!UnlockFileEx (fh->h, 0, diff_low, diff_high, &o))
1639 SetErrnoFromWinError (GetLastError ());
1640 return GNUNET_SYSERR;
1649 * Open a file. Note that the access permissions will only be
1650 * used if a new file is created and if the underlying operating
1651 * system supports the given permissions.
1653 * @param fn file name to be opened
1654 * @param flags opening flags, a combination of GNUNET_DISK_OPEN_xxx bit flags
1655 * @param perm permissions for the newly created file, use
1656 * #GNUNET_DISK_PERM_NONE if a file could not be created by this
1657 * call (because of flags)
1658 * @return IO handle on success, NULL on error
1660 struct GNUNET_DISK_FileHandle *
1661 GNUNET_DISK_file_open (const char *fn,
1662 enum GNUNET_DISK_OpenFlags flags,
1663 enum GNUNET_DISK_AccessPermissions perm)
1666 struct GNUNET_DISK_FileHandle *ret;
1672 wchar_t wexpfn[MAX_PATH + 1];
1679 expfn = GNUNET_STRINGS_filename_expand (fn);
1684 if (GNUNET_DISK_OPEN_READWRITE == (flags & GNUNET_DISK_OPEN_READWRITE))
1685 oflags = O_RDWR; /* note: O_RDWR is NOT always O_RDONLY | O_WRONLY */
1686 else if (flags & GNUNET_DISK_OPEN_READ)
1688 else if (flags & GNUNET_DISK_OPEN_WRITE)
1693 GNUNET_free (expfn);
1696 if (flags & GNUNET_DISK_OPEN_FAILIFEXISTS)
1697 oflags |= (O_CREAT | O_EXCL);
1698 if (flags & GNUNET_DISK_OPEN_TRUNCATE)
1700 if (flags & GNUNET_DISK_OPEN_APPEND)
1702 if(GNUNET_NO == GNUNET_DISK_file_test(fn))
1704 if (flags & GNUNET_DISK_OPEN_CREATE )
1706 (void) GNUNET_DISK_directory_create_for_file (expfn);
1708 mode = translate_unix_perms (perm);
1712 fd = open (expfn, oflags
1716 | O_LARGEFILE, mode);
1719 if (0 == (flags & GNUNET_DISK_OPEN_FAILIFEXISTS))
1720 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "open", expfn);
1722 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_DEBUG, "open", expfn);
1723 GNUNET_free (expfn);
1730 if (GNUNET_DISK_OPEN_READWRITE == (flags & GNUNET_DISK_OPEN_READWRITE))
1731 access = FILE_READ_DATA | FILE_WRITE_DATA;
1732 else if (flags & GNUNET_DISK_OPEN_READ)
1733 access = FILE_READ_DATA;
1734 else if (flags & GNUNET_DISK_OPEN_WRITE)
1735 access = FILE_WRITE_DATA;
1737 if (flags & GNUNET_DISK_OPEN_FAILIFEXISTS)
1741 else if (flags & GNUNET_DISK_OPEN_CREATE)
1743 (void) GNUNET_DISK_directory_create_for_file (expfn);
1744 if (flags & GNUNET_DISK_OPEN_TRUNCATE)
1745 disp = CREATE_ALWAYS;
1749 else if (flags & GNUNET_DISK_OPEN_TRUNCATE)
1751 disp = TRUNCATE_EXISTING;
1755 disp = OPEN_EXISTING;
1758 if (ERROR_SUCCESS == plibc_conv_to_win_pathwconv(expfn, wexpfn))
1759 h = CreateFileW (wexpfn, access,
1760 FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE, NULL,
1761 disp, FILE_ATTRIBUTE_NORMAL, NULL);
1763 h = INVALID_HANDLE_VALUE;
1764 if (h == INVALID_HANDLE_VALUE)
1767 SetErrnoFromWinError (GetLastError ());
1769 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_INFO, "open", expfn);
1770 GNUNET_free (expfn);
1775 if (flags & GNUNET_DISK_OPEN_APPEND)
1776 if (SetFilePointer (h, 0, 0, FILE_END) == INVALID_SET_FILE_POINTER)
1778 SetErrnoFromWinError (GetLastError ());
1779 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "SetFilePointer", expfn);
1781 GNUNET_free (expfn);
1786 ret = GNUNET_new (struct GNUNET_DISK_FileHandle);
1789 ret->type = GNUNET_DISK_HANLDE_TYPE_FILE;
1793 GNUNET_free (expfn);
1799 * Close an open file.
1801 * @param h file handle
1802 * @return #GNUNET_OK on success, #GNUNET_SYSERR otherwise
1805 GNUNET_DISK_file_close (struct GNUNET_DISK_FileHandle *h)
1811 return GNUNET_SYSERR;
1817 if (! CloseHandle (h->h))
1819 SetErrnoFromWinError (GetLastError ());
1820 LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING, "close");
1821 ret = GNUNET_SYSERR;
1823 if (h->oOverlapRead)
1825 if (! CloseHandle (h->oOverlapRead->hEvent))
1827 SetErrnoFromWinError (GetLastError ());
1828 LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING, "close");
1829 ret = GNUNET_SYSERR;
1831 GNUNET_free (h->oOverlapRead);
1833 if (h->oOverlapWrite)
1835 if (!CloseHandle (h->oOverlapWrite->hEvent))
1837 SetErrnoFromWinError (GetLastError ());
1838 LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING, "close");
1839 ret = GNUNET_SYSERR;
1841 GNUNET_free (h->oOverlapWrite);
1844 if (close (h->fd) != 0)
1846 LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING, "close");
1847 ret = GNUNET_SYSERR;
1857 * Get a GNUnet file handle from a W32 handle.
1859 * @param handle native handle
1860 * @return GNUnet file handle corresponding to the W32 handle
1862 struct GNUNET_DISK_FileHandle *
1863 GNUNET_DISK_get_handle_from_w32_handle (HANDLE osfh)
1865 struct GNUNET_DISK_FileHandle *fh;
1867 enum GNUNET_FILE_Type ftype;
1869 dwret = GetFileType (osfh);
1872 case FILE_TYPE_DISK:
1873 ftype = GNUNET_DISK_HANLDE_TYPE_FILE;
1875 case FILE_TYPE_PIPE:
1876 ftype = GNUNET_DISK_HANLDE_TYPE_PIPE;
1878 case FILE_TYPE_UNKNOWN:
1879 if ( (GetLastError () == NO_ERROR) ||
1880 (GetLastError () == ERROR_INVALID_HANDLE) )
1882 if (0 != ResetEvent (osfh))
1883 ftype = GNUNET_DISK_HANLDE_TYPE_EVENT;
1894 fh = GNUNET_new (struct GNUNET_DISK_FileHandle);
1898 if (ftype == GNUNET_DISK_HANLDE_TYPE_PIPE)
1901 * Note that we can't make it overlapped if it isn't already.
1902 * (ReOpenFile() is only available in 2003/Vista).
1903 * The process that opened this file in the first place (usually a parent
1904 * process, if this is stdin/stdout/stderr) must make it overlapped,
1905 * otherwise we're screwed, as selecting on non-overlapped handle
1908 fh->oOverlapRead = GNUNET_new (OVERLAPPED);
1909 fh->oOverlapWrite = GNUNET_new (OVERLAPPED);
1910 fh->oOverlapRead->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
1911 fh->oOverlapWrite->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
1919 * Get a handle from a native integer FD.
1921 * @param fno native integer file descriptor
1922 * @return file handle corresponding to the descriptor, NULL on error
1924 struct GNUNET_DISK_FileHandle *
1925 GNUNET_DISK_get_handle_from_int_fd (int fno)
1927 struct GNUNET_DISK_FileHandle *fh;
1929 if ( (((off_t) -1) == lseek (fno, 0, SEEK_CUR)) &&
1931 return NULL; /* invalid FD */
1934 fh = GNUNET_new (struct GNUNET_DISK_FileHandle);
1940 osfh = _get_osfhandle (fno);
1941 if (INVALID_HANDLE_VALUE == (HANDLE) osfh)
1944 fh = GNUNET_DISK_get_handle_from_w32_handle ((HANDLE) osfh);
1952 * Get a handle from a native streaming FD.
1954 * @param fd native streaming file descriptor
1955 * @return file handle corresponding to the descriptor
1957 struct GNUNET_DISK_FileHandle *
1958 GNUNET_DISK_get_handle_from_native (FILE *fd)
1966 return GNUNET_DISK_get_handle_from_int_fd (fno);
1971 * Handle for a memory-mapping operation.
1973 struct GNUNET_DISK_MapHandle
1976 * Address where the map is in memory.
1982 * Underlying OS handle.
1987 * Number of bytes mapped.
1995 #define MAP_FAILED ((void *) -1)
1999 * Map a file into memory
2001 * @param h open file handle
2002 * @param m handle to the new mapping
2003 * @param access access specification, GNUNET_DISK_MAP_TYPE_xxx
2004 * @param len size of the mapping
2005 * @return pointer to the mapped memory region, NULL on failure
2008 GNUNET_DISK_file_map (const struct GNUNET_DISK_FileHandle *h,
2009 struct GNUNET_DISK_MapHandle **m,
2010 enum GNUNET_DISK_MapType access, size_t len)
2019 DWORD mapAccess, protect;
2021 if ((access & GNUNET_DISK_MAP_TYPE_READ) &&
2022 (access & GNUNET_DISK_MAP_TYPE_WRITE))
2024 protect = PAGE_READWRITE;
2025 mapAccess = FILE_MAP_ALL_ACCESS;
2027 else if (access & GNUNET_DISK_MAP_TYPE_READ)
2029 protect = PAGE_READONLY;
2030 mapAccess = FILE_MAP_READ;
2032 else if (access & GNUNET_DISK_MAP_TYPE_WRITE)
2034 protect = PAGE_READWRITE;
2035 mapAccess = FILE_MAP_WRITE;
2043 *m = GNUNET_new (struct GNUNET_DISK_MapHandle);
2044 (*m)->h = CreateFileMapping (h->h, NULL, protect, 0, 0, NULL);
2045 if ((*m)->h == INVALID_HANDLE_VALUE)
2047 SetErrnoFromWinError (GetLastError ());
2052 (*m)->addr = MapViewOfFile ((*m)->h, mapAccess, 0, 0, len);
2055 SetErrnoFromWinError (GetLastError ());
2056 CloseHandle ((*m)->h);
2065 if (access & GNUNET_DISK_MAP_TYPE_READ)
2067 if (access & GNUNET_DISK_MAP_TYPE_WRITE)
2069 *m = GNUNET_new (struct GNUNET_DISK_MapHandle);
2070 (*m)->addr = mmap (NULL, len, prot, MAP_SHARED, h->fd, 0);
2071 GNUNET_assert (NULL != (*m)->addr);
2072 if (MAP_FAILED == (*m)->addr)
2084 * @param h mapping handle
2085 * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
2088 GNUNET_DISK_file_unmap (struct GNUNET_DISK_MapHandle *h)
2095 return GNUNET_SYSERR;
2099 ret = UnmapViewOfFile (h->addr) ? GNUNET_OK : GNUNET_SYSERR;
2100 if (ret != GNUNET_OK)
2101 SetErrnoFromWinError (GetLastError ());
2102 if (!CloseHandle (h->h) && (ret == GNUNET_OK))
2104 ret = GNUNET_SYSERR;
2105 SetErrnoFromWinError (GetLastError ());
2108 ret = munmap (h->addr, h->len) != -1 ? GNUNET_OK : GNUNET_SYSERR;
2116 * Write file changes to disk
2117 * @param h handle to an open file
2118 * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
2121 GNUNET_DISK_file_sync (const struct GNUNET_DISK_FileHandle *h)
2126 return GNUNET_SYSERR;
2132 ret = FlushFileBuffers (h->h) ? GNUNET_OK : GNUNET_SYSERR;
2133 if (ret != GNUNET_OK)
2134 SetErrnoFromWinError (GetLastError ());
2136 #elif defined(FREEBSD) || defined(OPENBSD) || defined(DARWIN)
2137 return fsync (h->fd) == -1 ? GNUNET_SYSERR : GNUNET_OK;
2139 return fdatasync (h->fd) == -1 ? GNUNET_SYSERR : GNUNET_OK;
2146 #define PIPE_BUF 512
2148 /* Copyright Bob Byrnes <byrnes <at> curl.com>
2149 http://permalink.gmane.org/gmane.os.cygwin.patches/2121
2151 /* Create a pipe, and return handles to the read and write ends,
2152 just like CreatePipe, but ensure that the write end permits
2153 FILE_READ_ATTRIBUTES access, on later versions of win32 where
2154 this is supported. This access is needed by NtQueryInformationFile,
2155 which is used to implement select and nonblocking writes.
2156 Note that the return value is either NO_ERROR or GetLastError,
2157 unlike CreatePipe, which returns a bool for success or failure. */
2159 create_selectable_pipe (PHANDLE read_pipe_ptr, PHANDLE write_pipe_ptr,
2160 LPSECURITY_ATTRIBUTES sa_ptr, DWORD psize,
2161 DWORD dwReadMode, DWORD dwWriteMode)
2163 /* Default to error. */
2164 *read_pipe_ptr = *write_pipe_ptr = INVALID_HANDLE_VALUE;
2169 /* Ensure that there is enough pipe buffer space for atomic writes. */
2170 if (psize < PIPE_BUF)
2173 char pipename[MAX_PATH];
2175 /* Retry CreateNamedPipe as long as the pipe name is in use.
2176 * Retrying will probably never be necessary, but we want
2177 * to be as robust as possible. */
2180 static volatile LONG pipe_unique_id;
2182 snprintf (pipename, sizeof pipename, "\\\\.\\pipe\\gnunet-%d-%ld",
2183 getpid (), InterlockedIncrement ((LONG *) & pipe_unique_id));
2184 LOG (GNUNET_ERROR_TYPE_DEBUG, "CreateNamedPipe: name = %s, size = %lu\n",
2186 /* Use CreateNamedPipe instead of CreatePipe, because the latter
2187 * returns a write handle that does not permit FILE_READ_ATTRIBUTES
2188 * access, on versions of win32 earlier than WinXP SP2.
2189 * CreatePipe also stupidly creates a full duplex pipe, which is
2190 * a waste, since only a single direction is actually used.
2191 * It's important to only allow a single instance, to ensure that
2192 * the pipe was not created earlier by some other process, even if
2193 * the pid has been reused. */
2194 read_pipe = CreateNamedPipeA (pipename, PIPE_ACCESS_INBOUND | FILE_FLAG_FIRST_PIPE_INSTANCE | dwReadMode, PIPE_TYPE_BYTE | PIPE_READMODE_BYTE, 1, /* max instances */
2195 psize, /* output buffer size */
2196 psize, /* input buffer size */
2197 NMPWAIT_USE_DEFAULT_WAIT, sa_ptr);
2199 if (read_pipe != INVALID_HANDLE_VALUE)
2201 LOG (GNUNET_ERROR_TYPE_DEBUG, "pipe read handle = %p\n", read_pipe);
2205 DWORD err = GetLastError ();
2209 case ERROR_PIPE_BUSY:
2210 /* The pipe is already open with compatible parameters.
2211 * Pick a new name and retry. */
2212 LOG (GNUNET_ERROR_TYPE_DEBUG, "pipe busy, retrying\n");
2214 case ERROR_ACCESS_DENIED:
2215 /* The pipe is already open with incompatible parameters.
2216 * Pick a new name and retry. */
2217 LOG (GNUNET_ERROR_TYPE_DEBUG, "pipe access denied, retrying\n");
2219 case ERROR_CALL_NOT_IMPLEMENTED:
2220 /* We are on an older Win9x platform without named pipes.
2221 * Return an anonymous pipe as the best approximation. */
2222 LOG (GNUNET_ERROR_TYPE_DEBUG,
2223 "CreateNamedPipe not implemented, resorting to "
2224 "CreatePipe: size = %lu\n", psize);
2225 if (CreatePipe (read_pipe_ptr, write_pipe_ptr, sa_ptr, psize))
2227 LOG (GNUNET_ERROR_TYPE_DEBUG, "pipe read handle = %p, write handle = %p\n",
2232 err = GetLastError ();
2233 LOG (GNUNET_ERROR_TYPE_ERROR, "CreatePipe failed: %d\n", err);
2236 LOG (GNUNET_ERROR_TYPE_ERROR, "CreateNamedPipe failed: %d\n", err);
2241 LOG (GNUNET_ERROR_TYPE_DEBUG, "CreateFile: name = %s\n", pipename);
2243 /* Open the named pipe for writing.
2244 * Be sure to permit FILE_READ_ATTRIBUTES access. */
2245 write_pipe = CreateFileA (pipename, GENERIC_WRITE | FILE_READ_ATTRIBUTES, 0, /* share mode */
2246 sa_ptr, OPEN_EXISTING, dwWriteMode, /* flags and attributes */
2247 0); /* handle to template file */
2249 if (write_pipe == INVALID_HANDLE_VALUE)
2252 DWORD err = GetLastError ();
2254 LOG (GNUNET_ERROR_TYPE_DEBUG, "CreateFile failed: %d\n", err);
2255 CloseHandle (read_pipe);
2258 LOG (GNUNET_ERROR_TYPE_DEBUG, "pipe write handle = %p\n", write_pipe);
2260 *read_pipe_ptr = read_pipe;
2261 *write_pipe_ptr = write_pipe;
2268 * Creates an interprocess channel
2270 * @param blocking_read creates an asynchronous pipe for reading if set to GNUNET_NO
2271 * @param blocking_write creates an asynchronous pipe for writing if set to GNUNET_NO
2272 * @param inherit_read inherit the parent processes stdin (only for windows)
2273 * @param inherit_write inherit the parent processes stdout (only for windows)
2274 * @return handle to the new pipe, NULL on error
2276 struct GNUNET_DISK_PipeHandle *
2277 GNUNET_DISK_pipe (int blocking_read,
2287 (void) inherit_read;
2288 (void) inherit_write;
2293 LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR,
2298 return GNUNET_DISK_pipe_from_fd (blocking_read,
2302 struct GNUNET_DISK_PipeHandle *p;
2307 p = GNUNET_new (struct GNUNET_DISK_PipeHandle);
2308 p->fd[0] = GNUNET_new (struct GNUNET_DISK_FileHandle);
2309 p->fd[1] = GNUNET_new (struct GNUNET_DISK_FileHandle);
2311 /* All pipes are overlapped. If you want them to block - just
2312 * call WriteFile() and ReadFile() with NULL overlapped pointer.
2313 * NOTE: calling with NULL overlapped pointer works only
2314 * for pipes, and doesn't seem to be a documented feature.
2315 * It will NOT work for files, because overlapped files need
2316 * to read offsets from the overlapped structure, regardless.
2317 * Pipes are not seekable, and need no offsets, which is
2318 * probably why it works for them.
2321 create_selectable_pipe (&p->fd[0]->h, &p->fd[1]->h, NULL, 0,
2322 FILE_FLAG_OVERLAPPED,
2323 FILE_FLAG_OVERLAPPED);
2326 SetErrnoFromWinError (GetLastError ());
2328 GNUNET_free (p->fd[0]);
2329 GNUNET_free (p->fd[1]);
2334 if (!DuplicateHandle
2335 (GetCurrentProcess (), p->fd[0]->h, GetCurrentProcess (), &tmp_handle, 0,
2336 inherit_read == GNUNET_YES ? TRUE : FALSE, DUPLICATE_SAME_ACCESS))
2338 SetErrnoFromWinError (GetLastError ());
2340 CloseHandle (p->fd[0]->h);
2341 CloseHandle (p->fd[1]->h);
2342 GNUNET_free (p->fd[0]);
2343 GNUNET_free (p->fd[1]);
2348 CloseHandle (p->fd[0]->h);
2349 p->fd[0]->h = tmp_handle;
2351 if (!DuplicateHandle
2352 (GetCurrentProcess (), p->fd[1]->h, GetCurrentProcess (), &tmp_handle, 0,
2353 inherit_write == GNUNET_YES ? TRUE : FALSE, DUPLICATE_SAME_ACCESS))
2355 SetErrnoFromWinError (GetLastError ());
2357 CloseHandle (p->fd[0]->h);
2358 CloseHandle (p->fd[1]->h);
2359 GNUNET_free (p->fd[0]);
2360 GNUNET_free (p->fd[1]);
2365 CloseHandle (p->fd[1]->h);
2366 p->fd[1]->h = tmp_handle;
2368 p->fd[0]->type = GNUNET_DISK_HANLDE_TYPE_PIPE;
2369 p->fd[1]->type = GNUNET_DISK_HANLDE_TYPE_PIPE;
2371 p->fd[0]->oOverlapRead = GNUNET_new (OVERLAPPED);
2372 p->fd[0]->oOverlapWrite = GNUNET_new (OVERLAPPED);
2373 p->fd[1]->oOverlapRead = GNUNET_new (OVERLAPPED);
2374 p->fd[1]->oOverlapWrite = GNUNET_new (OVERLAPPED);
2376 p->fd[0]->oOverlapRead->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2377 p->fd[0]->oOverlapWrite->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2379 p->fd[1]->oOverlapRead->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2380 p->fd[1]->oOverlapWrite->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2388 * Creates a pipe object from a couple of file descriptors.
2389 * Useful for wrapping existing pipe FDs.
2391 * @param blocking_read creates an asynchronous pipe for reading if set to GNUNET_NO
2392 * @param blocking_write creates an asynchronous pipe for writing if set to GNUNET_NO
2393 * @param fd an array of two fd values. One of them may be -1 for read-only or write-only pipes
2395 * @return handle to the new pipe, NULL on error
2397 struct GNUNET_DISK_PipeHandle *
2398 GNUNET_DISK_pipe_from_fd (int blocking_read, int blocking_write, int fd[2])
2400 struct GNUNET_DISK_PipeHandle *p;
2402 p = GNUNET_new (struct GNUNET_DISK_PipeHandle);
2407 int eno = 0; /* make gcc happy */
2412 p->fd[0] = GNUNET_new (struct GNUNET_DISK_FileHandle);
2413 p->fd[0]->fd = fd[0];
2416 flags = fcntl (fd[0], F_GETFL);
2417 flags |= O_NONBLOCK;
2418 if (0 > fcntl (fd[0], F_SETFL, flags))
2424 flags = fcntl (fd[0], F_GETFD);
2425 flags |= FD_CLOEXEC;
2426 if (0 > fcntl (fd[0], F_SETFD, flags))
2435 p->fd[1] = GNUNET_new (struct GNUNET_DISK_FileHandle);
2436 p->fd[1]->fd = fd[1];
2437 if (!blocking_write)
2439 flags = fcntl (fd[1], F_GETFL);
2440 flags |= O_NONBLOCK;
2441 if (0 > fcntl (fd[1], F_SETFL, flags))
2447 flags = fcntl (fd[1], F_GETFD);
2448 flags |= FD_CLOEXEC;
2449 if (0 > fcntl (fd[1], F_SETFD, flags))
2458 LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR, "fcntl");
2459 if (p->fd[0]->fd >= 0)
2460 GNUNET_break (0 == close (p->fd[0]->fd));
2461 if (p->fd[1]->fd >= 0)
2462 GNUNET_break (0 == close (p->fd[1]->fd));
2463 GNUNET_free_non_null (p->fd[0]);
2464 GNUNET_free_non_null (p->fd[1]);
2472 p->fd[0] = GNUNET_new (struct GNUNET_DISK_FileHandle);
2473 p->fd[0]->h = (HANDLE) _get_osfhandle (fd[0]);
2474 if (p->fd[0]->h != INVALID_HANDLE_VALUE)
2476 p->fd[0]->type = GNUNET_DISK_HANLDE_TYPE_PIPE;
2477 p->fd[0]->oOverlapRead = GNUNET_new (OVERLAPPED);
2478 p->fd[0]->oOverlapWrite = GNUNET_new (OVERLAPPED);
2479 p->fd[0]->oOverlapRead->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2480 p->fd[0]->oOverlapWrite->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2484 GNUNET_free (p->fd[0]);
2490 p->fd[1] = GNUNET_new (struct GNUNET_DISK_FileHandle);
2491 p->fd[1]->h = (HANDLE) _get_osfhandle (fd[1]);
2492 if (p->fd[1]->h != INVALID_HANDLE_VALUE)
2494 p->fd[1]->type = GNUNET_DISK_HANLDE_TYPE_PIPE;
2495 p->fd[1]->oOverlapRead = GNUNET_new (OVERLAPPED);
2496 p->fd[1]->oOverlapWrite = GNUNET_new (OVERLAPPED);
2497 p->fd[1]->oOverlapRead->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2498 p->fd[1]->oOverlapWrite->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2502 GNUNET_free (p->fd[1]);
2513 * Closes an interprocess channel
2515 * @param p pipe to close
2516 * @param end which end of the pipe to close
2517 * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
2520 GNUNET_DISK_pipe_close_end (struct GNUNET_DISK_PipeHandle *p,
2521 enum GNUNET_DISK_PipeEnd end)
2523 int ret = GNUNET_OK;
2525 if (end == GNUNET_DISK_PIPE_END_READ)
2529 ret = GNUNET_DISK_file_close (p->fd[0]);
2533 else if (end == GNUNET_DISK_PIPE_END_WRITE)
2537 ret = GNUNET_DISK_file_close (p->fd[1]);
2546 * Detaches one of the ends from the pipe.
2547 * Detached end is a fully-functional FileHandle, it will
2548 * not be affected by anything you do with the pipe afterwards.
2549 * Each end of a pipe can only be detched from it once (i.e.
2550 * it is not duplicated).
2552 * @param p pipe to detach an end from
2553 * @param end which end of the pipe to detach
2554 * @return Detached end on success, NULL on failure
2555 * (or if that end is not present or is closed).
2557 struct GNUNET_DISK_FileHandle *
2558 GNUNET_DISK_pipe_detach_end (struct GNUNET_DISK_PipeHandle *p,
2559 enum GNUNET_DISK_PipeEnd end)
2561 struct GNUNET_DISK_FileHandle *ret = NULL;
2563 if (end == GNUNET_DISK_PIPE_END_READ)
2571 else if (end == GNUNET_DISK_PIPE_END_WRITE)
2585 * Closes an interprocess channel
2587 * @param p pipe to close
2588 * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
2591 GNUNET_DISK_pipe_close (struct GNUNET_DISK_PipeHandle *p)
2593 int ret = GNUNET_OK;
2596 int write_end_close;
2597 int read_end_close_errno;
2598 int write_end_close_errno;
2600 read_end_close = GNUNET_DISK_pipe_close_end (p, GNUNET_DISK_PIPE_END_READ);
2601 read_end_close_errno = errno;
2602 write_end_close = GNUNET_DISK_pipe_close_end (p, GNUNET_DISK_PIPE_END_WRITE);
2603 write_end_close_errno = errno;
2606 if (GNUNET_OK != read_end_close)
2608 errno = read_end_close_errno;
2609 ret = read_end_close;
2611 else if (GNUNET_OK != write_end_close)
2613 errno = write_end_close_errno;
2614 ret = write_end_close;
2622 * Get the handle to a particular pipe end
2625 * @param n end to access
2626 * @return handle for the respective end
2628 const struct GNUNET_DISK_FileHandle *
2629 GNUNET_DISK_pipe_handle (const struct GNUNET_DISK_PipeHandle *p,
2630 enum GNUNET_DISK_PipeEnd n)
2634 case GNUNET_DISK_PIPE_END_READ:
2635 case GNUNET_DISK_PIPE_END_WRITE:
2645 * Retrieve OS file handle
2647 * @param fh GNUnet file descriptor
2648 * @param dst destination buffer
2649 * @param dst_len length of dst
2650 * @return #GNUNET_OK on success, #GNUNET_SYSERR otherwise
2653 GNUNET_DISK_internal_file_handle_ (const struct GNUNET_DISK_FileHandle *fh,
2654 void *dst, size_t dst_len)
2657 return GNUNET_SYSERR;
2659 if (dst_len < sizeof (HANDLE))
2660 return GNUNET_SYSERR;
2661 *((HANDLE *) dst) = fh->h;
2663 if (dst_len < sizeof (int))
2664 return GNUNET_SYSERR;
2665 *((int *) dst) = fh->fd;
2673 * Helper function for #GNUNET_DISK_purge_cfg_dir.
2675 * @param cls a `const char *` with the option to purge
2676 * @param cfg our configuration
2677 * @return #GNUNET_OK on success
2680 purge_cfg_dir (void *cls,
2681 const struct GNUNET_CONFIGURATION_Handle *cfg)
2683 const char *option = cls;
2687 GNUNET_CONFIGURATION_get_value_filename (cfg,
2692 GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
2697 if (GNUNET_SYSERR ==
2698 GNUNET_DISK_directory_remove (tmpname))
2700 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR,
2703 GNUNET_free (tmpname);
2706 GNUNET_free (tmpname);
2712 * Remove the directory given under @a option in
2713 * section [PATHS] in configuration under @a cfg_filename
2715 * @param cfg_filename configuration file to parse
2716 * @param option option with the dir name to purge
2719 GNUNET_DISK_purge_cfg_dir (const char *cfg_filename,
2722 GNUNET_break (GNUNET_OK ==
2723 GNUNET_CONFIGURATION_parse_and_run (cfg_filename,