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/>.
18 SPDX-License-Identifier: AGPL3.0-or-later
22 * @brief disk IO convenience methods
23 * @author Christian Grothoff
28 #include "gnunet_strings_lib.h"
29 #include "gnunet_disk_lib.h"
31 #define LOG(kind, ...) GNUNET_log_from (kind, "util-disk", __VA_ARGS__)
33 #define LOG_STRERROR(kind, syscall) \
34 GNUNET_log_from_strerror (kind, "util-disk", syscall)
36 #define LOG_STRERROR_FILE(kind, syscall, filename) \
37 GNUNET_log_from_strerror_file (kind, "util-disk", syscall, filename)
40 * Block size for IO for copying files.
42 #define COPY_BLK_SIZE 65536
44 #include <sys/types.h>
49 #include <sys/param.h>
52 #include <sys/mount.h>
54 #if HAVE_SYS_STATVFS_H
55 #include <sys/statvfs.h>
59 #define _IFMT 0170000 /* type of file */
60 #define _IFLNK 0120000 /* symbolic link */
61 #define S_ISLNK(m) (((m) &_IFMT) == _IFLNK)
66 * Handle used to manage a pipe.
68 struct GNUNET_DISK_PipeHandle
71 * File descriptors for the pipe.
72 * One or both of them could be NULL.
74 struct GNUNET_DISK_FileHandle *fd[2];
79 * Closure for the recursion to determine the file size
82 struct GetFileSizeData
85 * Set to the total file size.
90 * GNUNET_YES if symbolic links should be included.
92 int include_sym_links;
95 * GNUNET_YES if mode is file-only (return total == -1 for directories).
103 * Translate GNUnet-internal permission bitmap to UNIX file
104 * access permission bitmap.
106 * @param perm file permissions, GNUnet style
107 * @return file permissions, UNIX style
110 translate_unix_perms (enum GNUNET_DISK_AccessPermissions perm)
115 if (perm & GNUNET_DISK_PERM_USER_READ)
117 if (perm & GNUNET_DISK_PERM_USER_WRITE)
119 if (perm & GNUNET_DISK_PERM_USER_EXEC)
121 if (perm & GNUNET_DISK_PERM_GROUP_READ)
123 if (perm & GNUNET_DISK_PERM_GROUP_WRITE)
125 if (perm & GNUNET_DISK_PERM_GROUP_EXEC)
127 if (perm & GNUNET_DISK_PERM_OTHER_READ)
129 if (perm & GNUNET_DISK_PERM_OTHER_WRITE)
131 if (perm & GNUNET_DISK_PERM_OTHER_EXEC)
140 * Iterate over all files in the given directory and
141 * accumulate their size.
143 * @param cls closure of type `struct GetFileSizeData`
144 * @param fn current filename we are looking at
145 * @return #GNUNET_SYSERR on serious errors, otherwise #GNUNET_OK
148 getSizeRec (void *cls, const char *fn)
150 struct GetFileSizeData *gfsd = cls;
152 #if defined(HAVE_STAT64) && \
153 ! (defined(_FILE_OFFSET_BITS) && _FILE_OFFSET_BITS == 64)
156 if (0 != STAT64 (fn, &buf))
158 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_DEBUG, "stat64", fn);
159 return GNUNET_SYSERR;
164 if (0 != stat (fn, &buf))
166 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_DEBUG, "stat", fn);
167 return GNUNET_SYSERR;
170 if ((S_ISDIR (buf.st_mode)) && (gfsd->single_file_mode == GNUNET_YES))
173 return GNUNET_SYSERR;
175 if ((! S_ISLNK (buf.st_mode)) || (gfsd->include_sym_links == GNUNET_YES))
176 gfsd->total += buf.st_size;
177 if ((S_ISDIR (buf.st_mode)) && (0 == access (fn, X_OK)) &&
178 ((! S_ISLNK (buf.st_mode)) || (gfsd->include_sym_links == GNUNET_YES)))
180 if (GNUNET_SYSERR == GNUNET_DISK_directory_scan (fn, &getSizeRec, gfsd))
181 return GNUNET_SYSERR;
188 * Checks whether a handle is invalid
190 * @param h handle to check
191 * @return #GNUNET_YES if invalid, #GNUNET_NO if valid
194 GNUNET_DISK_handle_invalid (const struct GNUNET_DISK_FileHandle *h)
197 return ((! h) || (h->h == INVALID_HANDLE_VALUE)) ? GNUNET_YES : GNUNET_NO;
199 return ((! h) || (h->fd == -1)) ? GNUNET_YES : GNUNET_NO;
204 * Get the size of an open file.
206 * @param fh open file handle
207 * @param size where to write size of the file
208 * @return #GNUNET_OK on success, #GNUNET_SYSERR on error
211 GNUNET_DISK_file_handle_size (struct GNUNET_DISK_FileHandle *fh, off_t *size)
216 b = GetFileSizeEx (fh->h, &li);
219 SetErrnoFromWinError (GetLastError ());
220 return GNUNET_SYSERR;
222 *size = (off_t) li.QuadPart;
226 if (0 != fstat (fh->fd, &sbuf))
227 return GNUNET_SYSERR;
228 *size = sbuf.st_size;
235 * Move the read/write pointer in a file
237 * @param h handle of an open file
238 * @param offset position to move to
239 * @param whence specification to which position the offset parameter relates to
240 * @return the new position on success, #GNUNET_SYSERR otherwise
243 GNUNET_DISK_file_seek (const struct GNUNET_DISK_FileHandle *h,
245 enum GNUNET_DISK_Seek whence)
250 return GNUNET_SYSERR;
255 LARGE_INTEGER new_pos;
258 static DWORD t[] = {FILE_BEGIN, FILE_CURRENT, FILE_END};
259 li.QuadPart = offset;
261 b = SetFilePointerEx (h->h, li, &new_pos, t[whence]);
264 SetErrnoFromWinError (GetLastError ());
265 return GNUNET_SYSERR;
267 return (off_t) new_pos.QuadPart;
269 static int t[] = {SEEK_SET, SEEK_CUR, SEEK_END};
271 return lseek (h->fd, offset, t[whence]);
277 * Get the size of the file (or directory) of the given file (in
280 * @param filename name of the file or directory
281 * @param size set to the size of the file (or,
282 * in the case of directories, the sum
283 * of all sizes of files in the directory)
284 * @param include_symbolic_links should symbolic links be
286 * @param single_file_mode #GNUNET_YES to only get size of one file
287 * and return #GNUNET_SYSERR for directories.
288 * @return #GNUNET_SYSERR on error, #GNUNET_OK on success
291 GNUNET_DISK_file_size (const char *filename,
293 int include_symbolic_links,
294 int single_file_mode)
296 struct GetFileSizeData gfsd;
299 GNUNET_assert (size != NULL);
301 gfsd.include_sym_links = include_symbolic_links;
302 gfsd.single_file_mode = single_file_mode;
303 ret = getSizeRec (&gfsd, filename);
310 * Obtain some unique identifiers for the given file
311 * that can be used to identify it in the local system.
312 * This function is used between GNUnet processes to
313 * quickly check if two files with the same absolute path
314 * are actually identical. The two processes represent
315 * the same peer but may communicate over the network
316 * (and the file may be on an NFS volume). This function
317 * may not be supported on all operating systems.
319 * @param filename name of the file
320 * @param dev set to the device ID
321 * @param ino set to the inode ID
322 * @return #GNUNET_OK on success
325 GNUNET_DISK_file_get_identifiers (const char *filename,
331 // FIXME NILS: test this
332 struct GNUNET_DISK_FileHandle *fh;
333 BY_HANDLE_FILE_INFORMATION info;
336 fh = GNUNET_DISK_file_open (filename,
337 GNUNET_DISK_OPEN_READ,
338 GNUNET_DISK_PERM_NONE);
340 return GNUNET_SYSERR;
341 succ = GetFileInformationByHandle (fh->h, &info);
342 GNUNET_DISK_file_close (fh);
345 return GNUNET_SYSERR;
347 *dev = info.dwVolumeSerialNumber;
348 *ino = ((((uint64_t) info.nFileIndexHigh) << (sizeof (DWORD) * 8)) |
356 if (0 != stat (filename, &sbuf))
358 return GNUNET_SYSERR;
360 *ino = (uint64_t) sbuf.st_ino;
369 if (0 != statvfs (filename, &fbuf))
371 return GNUNET_SYSERR;
373 *dev = (uint64_t) fbuf.f_fsid;
379 if (0 != statfs (filename, &fbuf))
381 return GNUNET_SYSERR;
384 ((uint64_t) fbuf.f_fsid.val[0]) << 32 || ((uint64_t) fbuf.f_fsid.val[1]);
389 #endif /* !WINDOWS */
395 * Create the name for a temporary file or directory from a template.
397 * @param t template (without XXXXX or "/tmp/")
398 * @return name ready for passing to 'mktemp' or 'mkdtemp', NULL on error
401 mktemp_name (const char *t)
407 if ((t[0] != '/') && (t[0] != '\\')
409 && ! (isalpha ((int) t[0]) && (t[0] != '\0') && (t[1] == ':'))
413 /* FIXME: This uses system codepage on W32, not UTF-8 */
414 tmpdir = getenv ("TMPDIR");
416 tmpdir = getenv ("TMP");
418 tmpdir = getenv ("TEMP");
421 GNUNET_asprintf (&tmpl, "%s/%s%s", tmpdir, t, "XXXXXX");
425 GNUNET_asprintf (&tmpl, "%s%s", t, "XXXXXX");
428 fn = (char *) GNUNET_malloc (MAX_PATH + 1);
429 if (ERROR_SUCCESS != plibc_conv_to_win_path (tmpl, fn))
452 tfn = GNUNET_strdup (fn);
453 random_fn = _mktemp (tfn);
454 if (NULL == random_fn)
459 /* FIXME: assume fn to be UTF-8-encoded and do the right thing */
460 if (0 == CreateDirectoryA (tfn, NULL))
462 DWORD error = GetLastError ();
464 if (ERROR_ALREADY_EXISTS == error)
475 * Update POSIX permissions mask of a file on disk. If both argumets
476 * are #GNUNET_NO, the file is made world-read-write-executable (777).
477 * Does nothing on W32.
479 * @param fn name of the file to update
480 * @param require_uid_match #GNUNET_YES means 700
481 * @param require_gid_match #GNUNET_YES means 770 unless @a require_uid_match is set
484 GNUNET_DISK_fix_permissions (const char *fn,
485 int require_uid_match,
486 int require_gid_match)
494 * Update POSIX permissions mask of a file on disk. If both argumets
495 * are #GNUNET_NO, the file is made world-read-write-executable (777).
497 * @param fn name of the file to update
498 * @param require_uid_match #GNUNET_YES means 700
499 * @param require_gid_match #GNUNET_YES means 770 unless @a require_uid_match is set
502 GNUNET_DISK_fix_permissions (const char *fn,
503 int require_uid_match,
504 int require_gid_match)
508 if (GNUNET_YES == require_uid_match)
509 mode = S_IRUSR | S_IWUSR | S_IXUSR;
510 else if (GNUNET_YES == require_gid_match)
511 mode = S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IWGRP | S_IXGRP;
513 mode = S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IWGRP | S_IXGRP | S_IROTH |
515 if (0 != chmod (fn, mode))
516 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "chmod", fn);
522 * Create an (empty) temporary directory on disk. If the given name is not
523 * an absolute path, the current 'TMPDIR' will be prepended. In any case,
524 * 6 random characters will be appended to the name to create a unique
527 * @param t component to use for the name;
528 * does NOT contain "XXXXXX" or "/tmp/".
529 * @return NULL on error, otherwise name of fresh
530 * file on disk in directory for temporary files
533 GNUNET_DISK_mkdtemp (const char *t)
538 omask = umask (S_IWGRP | S_IWOTH | S_IRGRP | S_IROTH);
539 fn = mktemp_name (t);
540 if (fn != mkdtemp (fn))
542 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_ERROR, "mkdtemp", fn);
553 * Move a file out of the way (create a backup) by
554 * renaming it to "orig.NUM~" where NUM is the smallest
555 * number that is not used yet.
557 * @param fil name of the file to back up
560 GNUNET_DISK_file_backup (const char *fil)
566 slen = strlen (fil) + 20;
567 target = GNUNET_malloc (slen);
571 GNUNET_snprintf (target, slen, "%s.%u~", fil, num++);
572 } while (0 == access (target, F_OK));
573 if (0 != rename (fil, target))
574 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR, "rename", fil);
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_INFO,
641 "A file already exits with the same name %s\n",
645 if (GNUNET_YES == is_readable)
646 ret = access (fil, R_OK | X_OK);
648 ret = access (fil, X_OK);
651 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "access", fil);
659 * Check that fil corresponds to a filename
660 * (of a file that exists and that is not a directory).
662 * @param fil filename to check
663 * @return #GNUNET_YES if yes, #GNUNET_NO if not a file, #GNUNET_SYSERR if something
664 * else (will print an error message in that case, too).
667 GNUNET_DISK_file_test (const char *fil)
669 struct stat filestat;
673 rdir = GNUNET_STRINGS_filename_expand (fil);
675 return GNUNET_SYSERR;
677 ret = stat (rdir, &filestat);
682 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "stat", rdir);
684 return GNUNET_SYSERR;
689 if (! S_ISREG (filestat.st_mode))
694 if (access (rdir, F_OK) < 0)
696 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "access", rdir);
698 return GNUNET_SYSERR;
706 * Implementation of "mkdir -p"
708 * @param dir the directory to create
709 * @return #GNUNET_OK on success, #GNUNET_SYSERR on failure
712 GNUNET_DISK_directory_create (const char *dir)
720 rdir = GNUNET_STRINGS_filename_expand (dir);
724 return GNUNET_SYSERR;
729 pos = 1; /* skip heading '/' */
731 /* Local or Network path? */
732 if (strncmp (rdir, "\\\\", 2) == 0)
737 if (rdir[pos] == '\\')
747 pos = 3; /* strlen("C:\\") */
750 /* Check which low level directories already exist */
752 rdir[len] = DIR_SEPARATOR;
755 if (DIR_SEPARATOR == rdir[pos2])
758 ret = GNUNET_DISK_directory_test (rdir, GNUNET_NO);
759 if (GNUNET_NO == ret)
761 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
762 "Creating directory `%s' failed",
765 return GNUNET_SYSERR;
767 rdir[pos2] = DIR_SEPARATOR;
768 if (GNUNET_YES == ret)
779 /* Start creating directories */
782 if ((rdir[pos] == DIR_SEPARATOR) || (pos == len))
785 ret = GNUNET_DISK_directory_test (rdir, GNUNET_NO);
786 if (GNUNET_NO == ret)
788 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
789 "Creating directory `%s' failed",
792 return GNUNET_SYSERR;
794 if (GNUNET_SYSERR == ret)
798 S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IXGRP | S_IROTH |
801 wchar_t wrdir[MAX_PATH + 1];
802 if (ERROR_SUCCESS == plibc_conv_to_win_pathwconv (rdir, wrdir))
803 ret = ! CreateDirectoryW (wrdir, NULL);
807 if ((ret != 0) && (errno != EEXIST))
809 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_ERROR, "mkdir", rdir);
811 return GNUNET_SYSERR;
814 rdir[pos] = DIR_SEPARATOR;
824 * Create the directory structure for storing a file.
826 * @param filename name of a file in the directory
827 * @returns #GNUNET_OK on success,
828 * #GNUNET_SYSERR on failure,
829 * #GNUNET_NO if the directory
830 * exists but is not writeable for us
833 GNUNET_DISK_directory_create_for_file (const char *filename)
840 rdir = GNUNET_STRINGS_filename_expand (filename);
844 return GNUNET_SYSERR;
846 if (0 == access (rdir, W_OK))
853 while ((len > 0) && (rdir[len] != DIR_SEPARATOR))
856 /* The empty path is invalid and in this case refers to / */
860 rdir = GNUNET_strdup ("/");
862 ret = GNUNET_DISK_directory_create (rdir);
863 if ((GNUNET_OK == ret) && (0 != access (rdir, W_OK)))
873 * Read the contents of a binary file into a buffer.
875 * @param h handle to an open file
876 * @param result the buffer to write the result to
877 * @param len the maximum number of bytes to read
878 * @return the number of bytes read on success, #GNUNET_SYSERR on failure
881 GNUNET_DISK_file_read (const struct GNUNET_DISK_FileHandle *h,
888 return GNUNET_SYSERR;
894 if (h->type == GNUNET_DISK_HANLDE_TYPE_FILE)
896 if (! ReadFile (h->h, result, len, &bytes_read, NULL))
898 SetErrnoFromWinError (GetLastError ());
899 return GNUNET_SYSERR;
902 else if (h->type == GNUNET_DISK_HANLDE_TYPE_PIPE)
904 if (! ReadFile (h->h, result, len, &bytes_read, h->oOverlapRead))
906 if (GetLastError () != ERROR_IO_PENDING)
908 LOG (GNUNET_ERROR_TYPE_DEBUG,
909 "Error reading from pipe: %u\n",
911 SetErrnoFromWinError (GetLastError ());
912 return GNUNET_SYSERR;
914 LOG (GNUNET_ERROR_TYPE_DEBUG, "Will get overlapped result\n");
915 GetOverlappedResult (h->h, h->oOverlapRead, &bytes_read, TRUE);
917 LOG (GNUNET_ERROR_TYPE_DEBUG, "Read %u bytes from pipe\n", bytes_read);
925 return read (h->fd, result, len);
931 * Read the contents of a binary file into a buffer.
932 * Guarantees not to block (returns GNUNET_SYSERR and sets errno to EAGAIN
933 * when no data can be read).
935 * @param h handle to an open file
936 * @param result the buffer to write the result to
937 * @param len the maximum number of bytes to read
938 * @return the number of bytes read on success, #GNUNET_SYSERR on failure
941 GNUNET_DISK_file_read_non_blocking (const struct GNUNET_DISK_FileHandle *h,
948 return GNUNET_SYSERR;
954 if (h->type == GNUNET_DISK_HANLDE_TYPE_FILE)
956 if (! ReadFile (h->h, result, len, &bytes_read, NULL))
958 SetErrnoFromWinError (GetLastError ());
959 return GNUNET_SYSERR;
962 else if (h->type == GNUNET_DISK_HANLDE_TYPE_PIPE)
964 if (! ReadFile (h->h, result, len, &bytes_read, h->oOverlapRead))
966 if (GetLastError () != ERROR_IO_PENDING)
968 LOG (GNUNET_ERROR_TYPE_DEBUG,
969 "Error reading from pipe: %u\n",
971 SetErrnoFromWinError (GetLastError ());
972 return GNUNET_SYSERR;
976 LOG (GNUNET_ERROR_TYPE_DEBUG, "ReadFile() queued a read, cancelling\n");
979 return GNUNET_SYSERR;
982 LOG (GNUNET_ERROR_TYPE_DEBUG, "Read %u bytes\n", bytes_read);
993 /* set to non-blocking, read, then set back */
994 flags = fcntl (h->fd, F_GETFL);
995 if (0 == (flags & O_NONBLOCK))
996 (void) fcntl (h->fd, F_SETFL, flags | O_NONBLOCK);
997 ret = read (h->fd, result, len);
998 if (0 == (flags & O_NONBLOCK))
1001 (void) fcntl (h->fd, F_SETFL, flags);
1010 * Read the contents of a binary file into a buffer.
1012 * @param fn file name
1013 * @param result the buffer to write the result to
1014 * @param len the maximum number of bytes to read
1015 * @return number of bytes read, #GNUNET_SYSERR on failure
1018 GNUNET_DISK_fn_read (const char *fn, void *result, size_t len)
1020 struct GNUNET_DISK_FileHandle *fh;
1024 fh = GNUNET_DISK_file_open (fn, GNUNET_DISK_OPEN_READ, GNUNET_DISK_PERM_NONE);
1026 return GNUNET_SYSERR;
1027 ret = GNUNET_DISK_file_read (fh, result, len);
1029 GNUNET_assert (GNUNET_OK == GNUNET_DISK_file_close (fh));
1036 * Write a buffer to a file.
1038 * @param h handle to open file
1039 * @param buffer the data to write
1040 * @param n number of bytes to write
1041 * @return number of bytes written on success, #GNUNET_SYSERR on error
1044 GNUNET_DISK_file_write (const struct GNUNET_DISK_FileHandle *h,
1051 return GNUNET_SYSERR;
1055 DWORD bytes_written;
1057 if (h->type == GNUNET_DISK_HANLDE_TYPE_FILE)
1059 if (! WriteFile (h->h, buffer, n, &bytes_written, NULL))
1061 SetErrnoFromWinError (GetLastError ());
1062 return GNUNET_SYSERR;
1065 else if (h->type == GNUNET_DISK_HANLDE_TYPE_PIPE)
1067 LOG (GNUNET_ERROR_TYPE_DEBUG, "It is a pipe trying to write %u bytes\n", n);
1068 if (! WriteFile (h->h, buffer, n, &bytes_written, h->oOverlapWrite))
1070 if (GetLastError () != ERROR_IO_PENDING)
1072 SetErrnoFromWinError (GetLastError ());
1073 LOG (GNUNET_ERROR_TYPE_DEBUG,
1074 "Error writing to pipe: %u\n",
1076 return GNUNET_SYSERR;
1078 LOG (GNUNET_ERROR_TYPE_DEBUG, "Will get overlapped result\n");
1079 if (! GetOverlappedResult (h->h, h->oOverlapWrite, &bytes_written, TRUE))
1081 SetErrnoFromWinError (GetLastError ());
1082 LOG (GNUNET_ERROR_TYPE_DEBUG,
1083 "Error getting overlapped result while writing to pipe: %u\n",
1085 return GNUNET_SYSERR;
1091 if (! GetOverlappedResult (h->h, h->oOverlapWrite, &ovr, TRUE))
1093 LOG (GNUNET_ERROR_TYPE_DEBUG,
1094 "Error getting control overlapped result while writing to pipe: %u\n",
1099 LOG (GNUNET_ERROR_TYPE_DEBUG,
1100 "Wrote %u bytes (ovr says %u), picking the greatest\n",
1105 if (bytes_written == 0)
1109 LOG (GNUNET_ERROR_TYPE_DEBUG,
1110 "Wrote %u bytes, returning -1 with EAGAIN\n",
1113 return GNUNET_SYSERR;
1116 LOG (GNUNET_ERROR_TYPE_DEBUG, "Wrote %u bytes\n", bytes_written);
1122 return bytes_written;
1124 return write (h->fd, buffer, n);
1130 * Write a buffer to a file, blocking, if necessary.
1132 * @param h handle to open file
1133 * @param buffer the data to write
1134 * @param n number of bytes to write
1135 * @return number of bytes written on success, #GNUNET_SYSERR on error
1138 GNUNET_DISK_file_write_blocking (const struct GNUNET_DISK_FileHandle *h,
1145 return GNUNET_SYSERR;
1149 DWORD bytes_written;
1150 /* We do a non-overlapped write, which is as blocking as it gets */
1151 LOG (GNUNET_ERROR_TYPE_DEBUG, "Writing %u bytes\n", n);
1152 if (! WriteFile (h->h, buffer, n, &bytes_written, NULL))
1154 SetErrnoFromWinError (GetLastError ());
1155 LOG (GNUNET_ERROR_TYPE_DEBUG,
1156 "Error writing to pipe: %u\n",
1158 return GNUNET_SYSERR;
1160 if (bytes_written == 0 && n > 0)
1162 LOG (GNUNET_ERROR_TYPE_DEBUG, "Waiting for pipe to clean\n");
1163 WaitForSingleObject (h->h, INFINITE);
1164 if (! WriteFile (h->h, buffer, n, &bytes_written, NULL))
1166 SetErrnoFromWinError (GetLastError ());
1167 LOG (GNUNET_ERROR_TYPE_DEBUG,
1168 "Error writing to pipe: %u\n",
1170 return GNUNET_SYSERR;
1173 LOG (GNUNET_ERROR_TYPE_DEBUG, "Wrote %u bytes\n", bytes_written);
1174 return bytes_written;
1179 /* set to blocking, write, then set back */
1180 flags = fcntl (h->fd, F_GETFL);
1181 if (0 != (flags & O_NONBLOCK))
1182 (void) fcntl (h->fd, F_SETFL, flags - O_NONBLOCK);
1183 ret = write (h->fd, buffer, n);
1184 if (0 == (flags & O_NONBLOCK))
1185 (void) fcntl (h->fd, F_SETFL, flags);
1192 * Write a buffer to a file. If the file is longer than the
1193 * number of bytes that will be written, it will be truncated.
1195 * @param fn file name
1196 * @param buffer the data to write
1197 * @param n number of bytes to write
1198 * @param mode file permissions
1199 * @return number of bytes written on success, #GNUNET_SYSERR on error
1202 GNUNET_DISK_fn_write (const char *fn,
1205 enum GNUNET_DISK_AccessPermissions mode)
1207 struct GNUNET_DISK_FileHandle *fh;
1211 GNUNET_DISK_file_open (fn,
1212 GNUNET_DISK_OPEN_WRITE | GNUNET_DISK_OPEN_TRUNCATE |
1213 GNUNET_DISK_OPEN_CREATE,
1216 return GNUNET_SYSERR;
1217 ret = GNUNET_DISK_file_write (fh, buffer, n);
1218 GNUNET_assert (GNUNET_OK == GNUNET_DISK_file_close (fh));
1224 * Scan a directory for files.
1226 * @param dir_name the name of the directory
1227 * @param callback the method to call for each file,
1228 * can be NULL, in that case, we only count
1229 * @param callback_cls closure for @a callback
1230 * @return the number of files found, #GNUNET_SYSERR on error or
1231 * ieration aborted by callback returning #GNUNET_SYSERR
1234 GNUNET_DISK_directory_scan (const char *dir_name,
1235 GNUNET_FileNameCallback callback,
1239 struct dirent *finfo;
1245 unsigned int name_len;
1246 unsigned int n_size;
1248 GNUNET_assert (NULL != dir_name);
1249 dname = GNUNET_STRINGS_filename_expand (dir_name);
1251 return GNUNET_SYSERR;
1252 while ((strlen (dname) > 0) && (dname[strlen (dname) - 1] == DIR_SEPARATOR))
1253 dname[strlen (dname) - 1] = '\0';
1254 if (0 != stat (dname, &istat))
1256 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "stat", dname);
1257 GNUNET_free (dname);
1258 return GNUNET_SYSERR;
1260 if (! S_ISDIR (istat.st_mode))
1262 LOG (GNUNET_ERROR_TYPE_WARNING,
1263 _ ("Expected `%s' to be a directory!\n"),
1265 GNUNET_free (dname);
1266 return GNUNET_SYSERR;
1269 dinfo = opendir (dname);
1270 if ((EACCES == errno) || (NULL == dinfo))
1272 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "opendir", dname);
1275 GNUNET_free (dname);
1276 return GNUNET_SYSERR;
1279 n_size = strlen (dname) + name_len + strlen (DIR_SEPARATOR_STR) + 1;
1280 name = GNUNET_malloc (n_size);
1281 while (NULL != (finfo = readdir (dinfo)))
1283 if ((0 == strcmp (finfo->d_name, ".")) ||
1284 (0 == strcmp (finfo->d_name, "..")))
1286 if (NULL != callback)
1288 if (name_len < strlen (finfo->d_name))
1291 name_len = strlen (finfo->d_name);
1292 n_size = strlen (dname) + name_len + strlen (DIR_SEPARATOR_STR) + 1;
1293 name = GNUNET_malloc (n_size);
1295 /* dname can end in "/" only if dname == "/";
1296 * if dname does not end in "/", we need to add
1297 * a "/" (otherwise, we must not!) */
1298 GNUNET_snprintf (name,
1302 (0 == strcmp (dname, DIR_SEPARATOR_STR))
1304 : DIR_SEPARATOR_STR,
1306 ret = callback (callback_cls, name);
1307 if (GNUNET_OK != ret)
1311 GNUNET_free (dname);
1312 if (GNUNET_NO == ret)
1314 return GNUNET_SYSERR;
1321 GNUNET_free (dname);
1327 * Function that removes the given directory by calling
1328 * #GNUNET_DISK_directory_remove().
1330 * @param unused not used
1331 * @param fn directory to remove
1332 * @return #GNUNET_OK
1335 remove_helper (void *unused, const char *fn)
1338 (void) GNUNET_DISK_directory_remove (fn);
1344 * Remove all files in a directory (rm -r). Call with
1347 * @param filename the file to remove
1348 * @return #GNUNET_OK on success, #GNUNET_SYSERR on error
1351 GNUNET_DISK_directory_remove (const char *filename)
1355 if (NULL == filename)
1358 return GNUNET_SYSERR;
1360 if (0 != lstat (filename, &istat))
1361 return GNUNET_NO; /* file may not exist... */
1362 (void) chmod (filename, S_IWUSR | S_IRUSR | S_IXUSR);
1363 if (0 == unlink (filename))
1365 if ((errno != EISDIR) &&
1366 /* EISDIR is not sufficient in all cases, e.g.
1367 * sticky /tmp directory may result in EPERM on BSD.
1368 * So we also explicitly check "isDirectory" */
1369 (GNUNET_YES != GNUNET_DISK_directory_test (filename, GNUNET_YES)))
1371 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "rmdir", filename);
1372 return GNUNET_SYSERR;
1374 if (GNUNET_SYSERR ==
1375 GNUNET_DISK_directory_scan (filename, &remove_helper, NULL))
1376 return GNUNET_SYSERR;
1377 if (0 != rmdir (filename))
1379 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "rmdir", filename);
1380 return GNUNET_SYSERR;
1389 * @param src file to copy
1390 * @param dst destination file name
1391 * @return #GNUNET_OK on success, #GNUNET_SYSERR on error
1394 GNUNET_DISK_file_copy (const char *src, const char *dst)
1401 struct GNUNET_DISK_FileHandle *in;
1402 struct GNUNET_DISK_FileHandle *out;
1404 if (GNUNET_OK != GNUNET_DISK_file_size (src, &size, GNUNET_YES, GNUNET_YES))
1406 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR, "stat", src);
1407 return GNUNET_SYSERR;
1411 GNUNET_DISK_file_open (src, GNUNET_DISK_OPEN_READ, GNUNET_DISK_PERM_NONE);
1414 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR, "open", src);
1415 return GNUNET_SYSERR;
1418 GNUNET_DISK_file_open (dst,
1419 GNUNET_DISK_OPEN_WRITE | GNUNET_DISK_OPEN_CREATE |
1420 GNUNET_DISK_OPEN_FAILIFEXISTS,
1421 GNUNET_DISK_PERM_USER_READ |
1422 GNUNET_DISK_PERM_USER_WRITE |
1423 GNUNET_DISK_PERM_GROUP_READ |
1424 GNUNET_DISK_PERM_GROUP_WRITE);
1427 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR, "open", dst);
1428 GNUNET_DISK_file_close (in);
1429 return GNUNET_SYSERR;
1431 buf = GNUNET_malloc (COPY_BLK_SIZE);
1434 len = COPY_BLK_SIZE;
1435 if (len > size - pos)
1437 sret = GNUNET_DISK_file_read (in, buf, len);
1438 if ((sret < 0) || (len != (size_t) sret))
1440 sret = GNUNET_DISK_file_write (out, buf, len);
1441 if ((sret < 0) || (len != (size_t) sret))
1446 GNUNET_DISK_file_close (in);
1447 GNUNET_DISK_file_close (out);
1451 GNUNET_DISK_file_close (in);
1452 GNUNET_DISK_file_close (out);
1453 return GNUNET_SYSERR;
1458 * @brief Removes special characters as ':' from a filename.
1459 * @param fn the filename to canonicalize
1462 GNUNET_DISK_filename_canonicalize (char *fn)
1467 for (idx = fn; *idx; idx++)
1471 if (c == '/' || c == '\\' || c == ':' || c == '*' || c == '?' || c == '"' ||
1472 c == '<' || c == '>' || c == '|')
1481 * @brief Change owner of a file
1483 * @param filename name of file to change the owner of
1484 * @param user name of the new owner
1485 * @return #GNUNET_OK on success, #GNUNET_SYSERR on failure
1488 GNUNET_DISK_file_change_owner (const char *filename, const char *user)
1493 pws = getpwnam (user);
1496 LOG (GNUNET_ERROR_TYPE_ERROR,
1497 _ ("Cannot obtain information about user `%s': %s\n"),
1500 return GNUNET_SYSERR;
1502 if (0 != chown (filename, pws->pw_uid, pws->pw_gid))
1504 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "chown", filename);
1505 return GNUNET_SYSERR;
1513 * Lock a part of a file
1515 * @param fh file handle
1516 * @param lock_start absolute position from where to lock
1517 * @param lock_end absolute position until where to lock
1518 * @param excl #GNUNET_YES for an exclusive lock
1519 * @return #GNUNET_OK on success, #GNUNET_SYSERR on error
1522 GNUNET_DISK_file_lock (struct GNUNET_DISK_FileHandle *fh,
1530 return GNUNET_SYSERR;
1536 memset (&fl, 0, sizeof (struct flock));
1537 fl.l_type = excl ? F_WRLCK : F_RDLCK;
1538 fl.l_whence = SEEK_SET;
1539 fl.l_start = lock_start;
1540 fl.l_len = lock_end;
1542 return fcntl (fh->fd, F_SETLK, &fl) != 0 ? GNUNET_SYSERR : GNUNET_OK;
1545 off_t diff = lock_end - lock_start;
1546 DWORD diff_low, diff_high;
1547 diff_low = (DWORD) (diff & 0xFFFFFFFF);
1548 diff_high = (DWORD) ((diff >> (sizeof (DWORD) * 8)) & 0xFFFFFFFF);
1550 memset (&o, 0, sizeof (OVERLAPPED));
1551 o.Offset = (DWORD) (lock_start & 0xFFFFFFFF);
1554 (DWORD) (((lock_start & ~0xFFFFFFFF) >> (sizeof (DWORD) * 8)) & 0xFFFFFFFF);
1556 if (! LockFileEx (fh->h,
1557 (excl ? LOCKFILE_EXCLUSIVE_LOCK : 0) |
1558 LOCKFILE_FAIL_IMMEDIATELY,
1564 SetErrnoFromWinError (GetLastError ());
1565 return GNUNET_SYSERR;
1574 * Unlock a part of a file
1576 * @param fh file handle
1577 * @param unlock_start absolute position from where to unlock
1578 * @param unlock_end absolute position until where to unlock
1579 * @return #GNUNET_OK on success, #GNUNET_SYSERR on error
1582 GNUNET_DISK_file_unlock (struct GNUNET_DISK_FileHandle *fh,
1589 return GNUNET_SYSERR;
1595 memset (&fl, 0, sizeof (struct flock));
1596 fl.l_type = F_UNLCK;
1597 fl.l_whence = SEEK_SET;
1598 fl.l_start = unlock_start;
1599 fl.l_len = unlock_end;
1601 return fcntl (fh->fd, F_SETLK, &fl) != 0 ? GNUNET_SYSERR : GNUNET_OK;
1604 off_t diff = unlock_end - unlock_start;
1605 DWORD diff_low, diff_high;
1606 diff_low = (DWORD) (diff & 0xFFFFFFFF);
1607 diff_high = (DWORD) ((diff >> (sizeof (DWORD) * 8)) & 0xFFFFFFFF);
1609 memset (&o, 0, sizeof (OVERLAPPED));
1610 o.Offset = (DWORD) (unlock_start & 0xFFFFFFFF);
1612 o.OffsetHigh = (DWORD) (
1613 ((unlock_start & ~0xFFFFFFFF) >> (sizeof (DWORD) * 8)) & 0xFFFFFFFF);
1615 if (! UnlockFileEx (fh->h, 0, diff_low, diff_high, &o))
1617 SetErrnoFromWinError (GetLastError ());
1618 return GNUNET_SYSERR;
1627 * Open a file. Note that the access permissions will only be
1628 * used if a new file is created and if the underlying operating
1629 * system supports the given permissions.
1631 * @param fn file name to be opened
1632 * @param flags opening flags, a combination of GNUNET_DISK_OPEN_xxx bit flags
1633 * @param perm permissions for the newly created file, use
1634 * #GNUNET_DISK_PERM_NONE if a file could not be created by this
1635 * call (because of flags)
1636 * @return IO handle on success, NULL on error
1638 struct GNUNET_DISK_FileHandle *
1639 GNUNET_DISK_file_open (const char *fn,
1640 enum GNUNET_DISK_OpenFlags flags,
1641 enum GNUNET_DISK_AccessPermissions perm)
1644 struct GNUNET_DISK_FileHandle *ret;
1650 wchar_t wexpfn[MAX_PATH + 1];
1657 expfn = GNUNET_STRINGS_filename_expand (fn);
1662 if (GNUNET_DISK_OPEN_READWRITE == (flags & GNUNET_DISK_OPEN_READWRITE))
1663 oflags = O_RDWR; /* note: O_RDWR is NOT always O_RDONLY | O_WRONLY */
1664 else if (flags & GNUNET_DISK_OPEN_READ)
1666 else if (flags & GNUNET_DISK_OPEN_WRITE)
1671 GNUNET_free (expfn);
1674 if (flags & GNUNET_DISK_OPEN_FAILIFEXISTS)
1675 oflags |= (O_CREAT | O_EXCL);
1676 if (flags & GNUNET_DISK_OPEN_TRUNCATE)
1678 if (flags & GNUNET_DISK_OPEN_APPEND)
1680 if (GNUNET_NO == GNUNET_DISK_file_test (fn))
1682 if (flags & GNUNET_DISK_OPEN_CREATE)
1684 (void) GNUNET_DISK_directory_create_for_file (expfn);
1686 mode = translate_unix_perms (perm);
1699 if (0 == (flags & GNUNET_DISK_OPEN_FAILIFEXISTS))
1700 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "open", expfn);
1702 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_DEBUG, "open", expfn);
1703 GNUNET_free (expfn);
1710 if (GNUNET_DISK_OPEN_READWRITE == (flags & GNUNET_DISK_OPEN_READWRITE))
1711 access = FILE_READ_DATA | FILE_WRITE_DATA;
1712 else if (flags & GNUNET_DISK_OPEN_READ)
1713 access = FILE_READ_DATA;
1714 else if (flags & GNUNET_DISK_OPEN_WRITE)
1715 access = FILE_WRITE_DATA;
1717 if (flags & GNUNET_DISK_OPEN_FAILIFEXISTS)
1721 else if (flags & GNUNET_DISK_OPEN_CREATE)
1723 (void) GNUNET_DISK_directory_create_for_file (expfn);
1724 if (flags & GNUNET_DISK_OPEN_TRUNCATE)
1725 disp = CREATE_ALWAYS;
1729 else if (flags & GNUNET_DISK_OPEN_TRUNCATE)
1731 disp = TRUNCATE_EXISTING;
1735 disp = OPEN_EXISTING;
1738 if (ERROR_SUCCESS == plibc_conv_to_win_pathwconv (expfn, wexpfn))
1739 h = CreateFileW (wexpfn,
1741 FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE,
1744 FILE_ATTRIBUTE_NORMAL,
1747 h = INVALID_HANDLE_VALUE;
1748 if (h == INVALID_HANDLE_VALUE)
1751 SetErrnoFromWinError (GetLastError ());
1753 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_INFO, "open", expfn);
1754 GNUNET_free (expfn);
1759 if (flags & GNUNET_DISK_OPEN_APPEND)
1760 if (SetFilePointer (h, 0, 0, FILE_END) == INVALID_SET_FILE_POINTER)
1762 SetErrnoFromWinError (GetLastError ());
1763 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "SetFilePointer", expfn);
1765 GNUNET_free (expfn);
1770 ret = GNUNET_new (struct GNUNET_DISK_FileHandle);
1773 ret->type = GNUNET_DISK_HANLDE_TYPE_FILE;
1777 GNUNET_free (expfn);
1783 * Close an open file.
1785 * @param h file handle
1786 * @return #GNUNET_OK on success, #GNUNET_SYSERR otherwise
1789 GNUNET_DISK_file_close (struct GNUNET_DISK_FileHandle *h)
1795 return GNUNET_SYSERR;
1801 if (! CloseHandle (h->h))
1803 SetErrnoFromWinError (GetLastError ());
1804 LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING, "close");
1805 ret = GNUNET_SYSERR;
1807 if (h->oOverlapRead)
1809 if (! CloseHandle (h->oOverlapRead->hEvent))
1811 SetErrnoFromWinError (GetLastError ());
1812 LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING, "close");
1813 ret = GNUNET_SYSERR;
1815 GNUNET_free (h->oOverlapRead);
1817 if (h->oOverlapWrite)
1819 if (! CloseHandle (h->oOverlapWrite->hEvent))
1821 SetErrnoFromWinError (GetLastError ());
1822 LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING, "close");
1823 ret = GNUNET_SYSERR;
1825 GNUNET_free (h->oOverlapWrite);
1828 if (close (h->fd) != 0)
1830 LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING, "close");
1831 ret = GNUNET_SYSERR;
1841 * Get a GNUnet file handle from a W32 handle.
1843 * @param handle native handle
1844 * @return GNUnet file handle corresponding to the W32 handle
1846 struct GNUNET_DISK_FileHandle *
1847 GNUNET_DISK_get_handle_from_w32_handle (HANDLE osfh)
1849 struct GNUNET_DISK_FileHandle *fh;
1851 enum GNUNET_FILE_Type ftype;
1853 dwret = GetFileType (osfh);
1856 case FILE_TYPE_DISK:
1857 ftype = GNUNET_DISK_HANLDE_TYPE_FILE;
1859 case FILE_TYPE_PIPE:
1860 ftype = GNUNET_DISK_HANLDE_TYPE_PIPE;
1862 case FILE_TYPE_UNKNOWN:
1863 if ((GetLastError () == NO_ERROR) ||
1864 (GetLastError () == ERROR_INVALID_HANDLE))
1866 if (0 != ResetEvent (osfh))
1867 ftype = GNUNET_DISK_HANLDE_TYPE_EVENT;
1878 fh = GNUNET_new (struct GNUNET_DISK_FileHandle);
1882 if (ftype == GNUNET_DISK_HANLDE_TYPE_PIPE)
1885 * Note that we can't make it overlapped if it isn't already.
1886 * (ReOpenFile() is only available in 2003/Vista).
1887 * The process that opened this file in the first place (usually a parent
1888 * process, if this is stdin/stdout/stderr) must make it overlapped,
1889 * otherwise we're screwed, as selecting on non-overlapped handle
1892 fh->oOverlapRead = GNUNET_new (OVERLAPPED);
1893 fh->oOverlapWrite = GNUNET_new (OVERLAPPED);
1894 fh->oOverlapRead->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
1895 fh->oOverlapWrite->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
1903 * Get a handle from a native integer FD.
1905 * @param fno native integer file descriptor
1906 * @return file handle corresponding to the descriptor, NULL on error
1908 struct GNUNET_DISK_FileHandle *
1909 GNUNET_DISK_get_handle_from_int_fd (int fno)
1911 struct GNUNET_DISK_FileHandle *fh;
1913 if ((((off_t) -1) == lseek (fno, 0, SEEK_CUR)) && (EBADF == errno))
1914 return NULL; /* invalid FD */
1917 fh = GNUNET_new (struct GNUNET_DISK_FileHandle);
1923 osfh = _get_osfhandle (fno);
1924 if (INVALID_HANDLE_VALUE == (HANDLE) osfh)
1927 fh = GNUNET_DISK_get_handle_from_w32_handle ((HANDLE) osfh);
1935 * Get a handle from a native streaming FD.
1937 * @param fd native streaming file descriptor
1938 * @return file handle corresponding to the descriptor
1940 struct GNUNET_DISK_FileHandle *
1941 GNUNET_DISK_get_handle_from_native (FILE *fd)
1949 return GNUNET_DISK_get_handle_from_int_fd (fno);
1954 * Handle for a memory-mapping operation.
1956 struct GNUNET_DISK_MapHandle
1959 * Address where the map is in memory.
1965 * Underlying OS handle.
1970 * Number of bytes mapped.
1978 #define MAP_FAILED ((void *) -1)
1982 * Map a file into memory
1984 * @param h open file handle
1985 * @param m handle to the new mapping
1986 * @param access access specification, GNUNET_DISK_MAP_TYPE_xxx
1987 * @param len size of the mapping
1988 * @return pointer to the mapped memory region, NULL on failure
1991 GNUNET_DISK_file_map (const struct GNUNET_DISK_FileHandle *h,
1992 struct GNUNET_DISK_MapHandle **m,
1993 enum GNUNET_DISK_MapType access,
2003 DWORD mapAccess, protect;
2005 if ((access & GNUNET_DISK_MAP_TYPE_READ) &&
2006 (access & GNUNET_DISK_MAP_TYPE_WRITE))
2008 protect = PAGE_READWRITE;
2009 mapAccess = FILE_MAP_ALL_ACCESS;
2011 else if (access & GNUNET_DISK_MAP_TYPE_READ)
2013 protect = PAGE_READONLY;
2014 mapAccess = FILE_MAP_READ;
2016 else if (access & GNUNET_DISK_MAP_TYPE_WRITE)
2018 protect = PAGE_READWRITE;
2019 mapAccess = FILE_MAP_WRITE;
2027 *m = GNUNET_new (struct GNUNET_DISK_MapHandle);
2028 (*m)->h = CreateFileMapping (h->h, NULL, protect, 0, 0, NULL);
2029 if ((*m)->h == INVALID_HANDLE_VALUE)
2031 SetErrnoFromWinError (GetLastError ());
2036 (*m)->addr = MapViewOfFile ((*m)->h, mapAccess, 0, 0, len);
2039 SetErrnoFromWinError (GetLastError ());
2040 CloseHandle ((*m)->h);
2049 if (access & GNUNET_DISK_MAP_TYPE_READ)
2051 if (access & GNUNET_DISK_MAP_TYPE_WRITE)
2053 *m = GNUNET_new (struct GNUNET_DISK_MapHandle);
2054 (*m)->addr = mmap (NULL, len, prot, MAP_SHARED, h->fd, 0);
2055 GNUNET_assert (NULL != (*m)->addr);
2056 if (MAP_FAILED == (*m)->addr)
2068 * @param h mapping handle
2069 * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
2072 GNUNET_DISK_file_unmap (struct GNUNET_DISK_MapHandle *h)
2079 return GNUNET_SYSERR;
2083 ret = UnmapViewOfFile (h->addr) ? GNUNET_OK : GNUNET_SYSERR;
2084 if (ret != GNUNET_OK)
2085 SetErrnoFromWinError (GetLastError ());
2086 if (! CloseHandle (h->h) && (ret == GNUNET_OK))
2088 ret = GNUNET_SYSERR;
2089 SetErrnoFromWinError (GetLastError ());
2092 ret = munmap (h->addr, h->len) != -1 ? GNUNET_OK : GNUNET_SYSERR;
2100 * Write file changes to disk
2101 * @param h handle to an open file
2102 * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
2105 GNUNET_DISK_file_sync (const struct GNUNET_DISK_FileHandle *h)
2110 return GNUNET_SYSERR;
2116 ret = FlushFileBuffers (h->h) ? GNUNET_OK : GNUNET_SYSERR;
2117 if (ret != GNUNET_OK)
2118 SetErrnoFromWinError (GetLastError ());
2120 #elif defined(FREEBSD) || defined(OPENBSD) || defined(DARWIN)
2121 return fsync (h->fd) == -1 ? GNUNET_SYSERR : GNUNET_OK;
2123 return fdatasync (h->fd) == -1 ? GNUNET_SYSERR : GNUNET_OK;
2130 #define PIPE_BUF 512
2132 /* Copyright Bob Byrnes <byrnes <at> curl.com>
2133 http://permalink.gmane.org/gmane.os.cygwin.patches/2121
2135 /* Create a pipe, and return handles to the read and write ends,
2136 just like CreatePipe, but ensure that the write end permits
2137 FILE_READ_ATTRIBUTES access, on later versions of win32 where
2138 this is supported. This access is needed by NtQueryInformationFile,
2139 which is used to implement select and nonblocking writes.
2140 Note that the return value is either NO_ERROR or GetLastError,
2141 unlike CreatePipe, which returns a bool for success or failure. */
2143 create_selectable_pipe (PHANDLE read_pipe_ptr,
2144 PHANDLE write_pipe_ptr,
2145 LPSECURITY_ATTRIBUTES sa_ptr,
2150 /* Default to error. */
2151 *read_pipe_ptr = *write_pipe_ptr = INVALID_HANDLE_VALUE;
2156 /* Ensure that there is enough pipe buffer space for atomic writes. */
2157 if (psize < PIPE_BUF)
2160 char pipename[MAX_PATH];
2162 /* Retry CreateNamedPipe as long as the pipe name is in use.
2163 * Retrying will probably never be necessary, but we want
2164 * to be as robust as possible. */
2167 static volatile LONG pipe_unique_id;
2171 "\\\\.\\pipe\\gnunet-%d-%ld",
2173 InterlockedIncrement ((LONG *) &pipe_unique_id));
2174 LOG (GNUNET_ERROR_TYPE_DEBUG,
2175 "CreateNamedPipe: name = %s, size = %lu\n",
2178 /* Use CreateNamedPipe instead of CreatePipe, because the latter
2179 * returns a write handle that does not permit FILE_READ_ATTRIBUTES
2180 * access, on versions of win32 earlier than WinXP SP2.
2181 * CreatePipe also stupidly creates a full duplex pipe, which is
2182 * a waste, since only a single direction is actually used.
2183 * It's important to only allow a single instance, to ensure that
2184 * the pipe was not created earlier by some other process, even if
2185 * the pid has been reused. */
2186 read_pipe = CreateNamedPipeA (pipename,
2187 PIPE_ACCESS_INBOUND |
2188 FILE_FLAG_FIRST_PIPE_INSTANCE | dwReadMode,
2189 PIPE_TYPE_BYTE | PIPE_READMODE_BYTE,
2190 1, /* max instances */
2191 psize, /* output buffer size */
2192 psize, /* input buffer size */
2193 NMPWAIT_USE_DEFAULT_WAIT,
2196 if (read_pipe != INVALID_HANDLE_VALUE)
2198 LOG (GNUNET_ERROR_TYPE_DEBUG, "pipe read handle = %p\n", read_pipe);
2202 DWORD err = GetLastError ();
2206 case ERROR_PIPE_BUSY:
2207 /* The pipe is already open with compatible parameters.
2208 * Pick a new name and retry. */
2209 LOG (GNUNET_ERROR_TYPE_DEBUG, "pipe busy, retrying\n");
2211 case ERROR_ACCESS_DENIED:
2212 /* The pipe is already open with incompatible parameters.
2213 * Pick a new name and retry. */
2214 LOG (GNUNET_ERROR_TYPE_DEBUG, "pipe access denied, retrying\n");
2216 case ERROR_CALL_NOT_IMPLEMENTED:
2217 /* We are on an older Win9x platform without named pipes.
2218 * Return an anonymous pipe as the best approximation. */
2219 LOG (GNUNET_ERROR_TYPE_DEBUG,
2220 "CreateNamedPipe not implemented, resorting to "
2221 "CreatePipe: size = %lu\n",
2223 if (CreatePipe (read_pipe_ptr, write_pipe_ptr, sa_ptr, psize))
2225 LOG (GNUNET_ERROR_TYPE_DEBUG,
2226 "pipe read handle = %p, write handle = %p\n",
2231 err = GetLastError ();
2232 LOG (GNUNET_ERROR_TYPE_ERROR, "CreatePipe failed: %d\n", err);
2235 LOG (GNUNET_ERROR_TYPE_ERROR, "CreateNamedPipe failed: %d\n", err);
2240 LOG (GNUNET_ERROR_TYPE_DEBUG, "CreateFile: name = %s\n", pipename);
2242 /* Open the named pipe for writing.
2243 * Be sure to permit FILE_READ_ATTRIBUTES access. */
2244 write_pipe = CreateFileA (pipename,
2245 GENERIC_WRITE | FILE_READ_ATTRIBUTES,
2249 dwWriteMode, /* flags and attributes */
2250 0); /* handle to template file */
2252 if (write_pipe == INVALID_HANDLE_VALUE)
2255 DWORD err = GetLastError ();
2257 LOG (GNUNET_ERROR_TYPE_DEBUG, "CreateFile failed: %d\n", err);
2258 CloseHandle (read_pipe);
2261 LOG (GNUNET_ERROR_TYPE_DEBUG, "pipe write handle = %p\n", write_pipe);
2263 *read_pipe_ptr = read_pipe;
2264 *write_pipe_ptr = write_pipe;
2271 * Creates an interprocess channel
2273 * @param blocking_read creates an asynchronous pipe for reading if set to GNUNET_NO
2274 * @param blocking_write creates an asynchronous pipe for writing if set to GNUNET_NO
2275 * @param inherit_read inherit the parent processes stdin (only for windows)
2276 * @param inherit_write inherit the parent processes stdout (only for windows)
2277 * @return handle to the new pipe, NULL on error
2279 struct GNUNET_DISK_PipeHandle *
2280 GNUNET_DISK_pipe (int blocking_read,
2290 (void) inherit_read;
2291 (void) inherit_write;
2296 LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR, "pipe");
2300 return GNUNET_DISK_pipe_from_fd (blocking_read, blocking_write, fd);
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.
2320 ret = create_selectable_pipe (&p->fd[0]->h,
2324 FILE_FLAG_OVERLAPPED,
2325 FILE_FLAG_OVERLAPPED);
2328 SetErrnoFromWinError (GetLastError ());
2330 GNUNET_free (p->fd[0]);
2331 GNUNET_free (p->fd[1]);
2336 if (! DuplicateHandle (GetCurrentProcess (),
2338 GetCurrentProcess (),
2341 inherit_read == GNUNET_YES ? TRUE : FALSE,
2342 DUPLICATE_SAME_ACCESS))
2344 SetErrnoFromWinError (GetLastError ());
2346 CloseHandle (p->fd[0]->h);
2347 CloseHandle (p->fd[1]->h);
2348 GNUNET_free (p->fd[0]);
2349 GNUNET_free (p->fd[1]);
2354 CloseHandle (p->fd[0]->h);
2355 p->fd[0]->h = tmp_handle;
2357 if (! DuplicateHandle (GetCurrentProcess (),
2359 GetCurrentProcess (),
2362 inherit_write == GNUNET_YES ? TRUE : FALSE,
2363 DUPLICATE_SAME_ACCESS))
2365 SetErrnoFromWinError (GetLastError ());
2367 CloseHandle (p->fd[0]->h);
2368 CloseHandle (p->fd[1]->h);
2369 GNUNET_free (p->fd[0]);
2370 GNUNET_free (p->fd[1]);
2375 CloseHandle (p->fd[1]->h);
2376 p->fd[1]->h = tmp_handle;
2378 p->fd[0]->type = GNUNET_DISK_HANLDE_TYPE_PIPE;
2379 p->fd[1]->type = GNUNET_DISK_HANLDE_TYPE_PIPE;
2381 p->fd[0]->oOverlapRead = GNUNET_new (OVERLAPPED);
2382 p->fd[0]->oOverlapWrite = GNUNET_new (OVERLAPPED);
2383 p->fd[1]->oOverlapRead = GNUNET_new (OVERLAPPED);
2384 p->fd[1]->oOverlapWrite = GNUNET_new (OVERLAPPED);
2386 p->fd[0]->oOverlapRead->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2387 p->fd[0]->oOverlapWrite->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2389 p->fd[1]->oOverlapRead->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2390 p->fd[1]->oOverlapWrite->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2398 * Creates a pipe object from a couple of file descriptors.
2399 * Useful for wrapping existing pipe FDs.
2401 * @param blocking_read creates an asynchronous pipe for reading if set to GNUNET_NO
2402 * @param blocking_write creates an asynchronous pipe for writing if set to GNUNET_NO
2403 * @param fd an array of two fd values. One of them may be -1 for read-only or write-only pipes
2405 * @return handle to the new pipe, NULL on error
2407 struct GNUNET_DISK_PipeHandle *
2408 GNUNET_DISK_pipe_from_fd (int blocking_read, int blocking_write, int fd[2])
2410 struct GNUNET_DISK_PipeHandle *p;
2412 p = GNUNET_new (struct GNUNET_DISK_PipeHandle);
2417 int eno = 0; /* make gcc happy */
2422 p->fd[0] = GNUNET_new (struct GNUNET_DISK_FileHandle);
2423 p->fd[0]->fd = fd[0];
2424 if (! blocking_read)
2426 flags = fcntl (fd[0], F_GETFL);
2427 flags |= O_NONBLOCK;
2428 if (0 > fcntl (fd[0], F_SETFL, flags))
2434 flags = fcntl (fd[0], F_GETFD);
2435 flags |= FD_CLOEXEC;
2436 if (0 > fcntl (fd[0], F_SETFD, flags))
2445 p->fd[1] = GNUNET_new (struct GNUNET_DISK_FileHandle);
2446 p->fd[1]->fd = fd[1];
2447 if (! blocking_write)
2449 flags = fcntl (fd[1], F_GETFL);
2450 flags |= O_NONBLOCK;
2451 if (0 > fcntl (fd[1], F_SETFL, flags))
2457 flags = fcntl (fd[1], F_GETFD);
2458 flags |= FD_CLOEXEC;
2459 if (0 > fcntl (fd[1], F_SETFD, flags))
2468 LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR, "fcntl");
2469 if (p->fd[0]->fd >= 0)
2470 GNUNET_break (0 == close (p->fd[0]->fd));
2471 if (p->fd[1]->fd >= 0)
2472 GNUNET_break (0 == close (p->fd[1]->fd));
2473 GNUNET_free_non_null (p->fd[0]);
2474 GNUNET_free_non_null (p->fd[1]);
2482 p->fd[0] = GNUNET_new (struct GNUNET_DISK_FileHandle);
2483 p->fd[0]->h = (HANDLE) _get_osfhandle (fd[0]);
2484 if (p->fd[0]->h != INVALID_HANDLE_VALUE)
2486 p->fd[0]->type = GNUNET_DISK_HANLDE_TYPE_PIPE;
2487 p->fd[0]->oOverlapRead = GNUNET_new (OVERLAPPED);
2488 p->fd[0]->oOverlapWrite = GNUNET_new (OVERLAPPED);
2489 p->fd[0]->oOverlapRead->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2490 p->fd[0]->oOverlapWrite->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2494 GNUNET_free (p->fd[0]);
2500 p->fd[1] = GNUNET_new (struct GNUNET_DISK_FileHandle);
2501 p->fd[1]->h = (HANDLE) _get_osfhandle (fd[1]);
2502 if (p->fd[1]->h != INVALID_HANDLE_VALUE)
2504 p->fd[1]->type = GNUNET_DISK_HANLDE_TYPE_PIPE;
2505 p->fd[1]->oOverlapRead = GNUNET_new (OVERLAPPED);
2506 p->fd[1]->oOverlapWrite = GNUNET_new (OVERLAPPED);
2507 p->fd[1]->oOverlapRead->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2508 p->fd[1]->oOverlapWrite->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2512 GNUNET_free (p->fd[1]);
2523 * Closes an interprocess channel
2525 * @param p pipe to close
2526 * @param end which end of the pipe to close
2527 * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
2530 GNUNET_DISK_pipe_close_end (struct GNUNET_DISK_PipeHandle *p,
2531 enum GNUNET_DISK_PipeEnd end)
2533 int ret = GNUNET_OK;
2535 if (end == GNUNET_DISK_PIPE_END_READ)
2539 ret = GNUNET_DISK_file_close (p->fd[0]);
2543 else if (end == GNUNET_DISK_PIPE_END_WRITE)
2547 ret = GNUNET_DISK_file_close (p->fd[1]);
2556 * Detaches one of the ends from the pipe.
2557 * Detached end is a fully-functional FileHandle, it will
2558 * not be affected by anything you do with the pipe afterwards.
2559 * Each end of a pipe can only be detched from it once (i.e.
2560 * it is not duplicated).
2562 * @param p pipe to detach an end from
2563 * @param end which end of the pipe to detach
2564 * @return Detached end on success, NULL on failure
2565 * (or if that end is not present or is closed).
2567 struct GNUNET_DISK_FileHandle *
2568 GNUNET_DISK_pipe_detach_end (struct GNUNET_DISK_PipeHandle *p,
2569 enum GNUNET_DISK_PipeEnd end)
2571 struct GNUNET_DISK_FileHandle *ret = NULL;
2573 if (end == GNUNET_DISK_PIPE_END_READ)
2581 else if (end == GNUNET_DISK_PIPE_END_WRITE)
2595 * Closes an interprocess channel
2597 * @param p pipe to close
2598 * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
2601 GNUNET_DISK_pipe_close (struct GNUNET_DISK_PipeHandle *p)
2603 int ret = GNUNET_OK;
2606 int write_end_close;
2607 int read_end_close_errno;
2608 int write_end_close_errno;
2610 read_end_close = GNUNET_DISK_pipe_close_end (p, GNUNET_DISK_PIPE_END_READ);
2611 read_end_close_errno = errno;
2612 write_end_close = GNUNET_DISK_pipe_close_end (p, GNUNET_DISK_PIPE_END_WRITE);
2613 write_end_close_errno = errno;
2616 if (GNUNET_OK != read_end_close)
2618 errno = read_end_close_errno;
2619 ret = read_end_close;
2621 else if (GNUNET_OK != write_end_close)
2623 errno = write_end_close_errno;
2624 ret = write_end_close;
2632 * Get the handle to a particular pipe end
2635 * @param n end to access
2636 * @return handle for the respective end
2638 const struct GNUNET_DISK_FileHandle *
2639 GNUNET_DISK_pipe_handle (const struct GNUNET_DISK_PipeHandle *p,
2640 enum GNUNET_DISK_PipeEnd n)
2644 case GNUNET_DISK_PIPE_END_READ:
2645 case GNUNET_DISK_PIPE_END_WRITE:
2655 * Retrieve OS file handle
2657 * @param fh GNUnet file descriptor
2658 * @param dst destination buffer
2659 * @param dst_len length of dst
2660 * @return #GNUNET_OK on success, #GNUNET_SYSERR otherwise
2663 GNUNET_DISK_internal_file_handle_ (const struct GNUNET_DISK_FileHandle *fh,
2668 return GNUNET_SYSERR;
2670 if (dst_len < sizeof (HANDLE))
2671 return GNUNET_SYSERR;
2672 *((HANDLE *) dst) = fh->h;
2674 if (dst_len < sizeof (int))
2675 return GNUNET_SYSERR;
2676 *((int *) dst) = fh->fd;
2684 * Helper function for #GNUNET_DISK_purge_cfg_dir.
2686 * @param cls a `const char *` with the option to purge
2687 * @param cfg our configuration
2688 * @return #GNUNET_OK on success
2691 purge_cfg_dir (void *cls, const struct GNUNET_CONFIGURATION_Handle *cfg)
2693 const char *option = cls;
2697 GNUNET_CONFIGURATION_get_value_filename (cfg, "PATHS", option, &tmpname))
2699 GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, "PATHS", option);
2702 if (GNUNET_SYSERR == GNUNET_DISK_directory_remove (tmpname))
2704 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR, "remove", tmpname);
2705 GNUNET_free (tmpname);
2708 GNUNET_free (tmpname);
2714 * Remove the directory given under @a option in
2715 * section [PATHS] in configuration under @a cfg_filename
2717 * @param cfg_filename configuration file to parse
2718 * @param option option with the dir name to purge
2721 GNUNET_DISK_purge_cfg_dir (const char *cfg_filename, const char *option)
2723 GNUNET_break (GNUNET_OK ==
2724 GNUNET_CONFIGURATION_parse_and_run (cfg_filename,