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) GNUNET_log_from_strerror (kind, "util-disk", syscall)
35 #define LOG_STRERROR_FILE(kind,syscall,filename) GNUNET_log_from_strerror_file (kind, "util-disk", syscall, filename)
38 * Block size for IO for copying files.
40 #define COPY_BLK_SIZE 65536
42 #include <sys/types.h>
47 #include <sys/param.h>
50 #include <sys/mount.h>
52 #if HAVE_SYS_STATVFS_H
53 #include <sys/statvfs.h>
57 #define _IFMT 0170000 /* type of file */
58 #define _IFLNK 0120000 /* symbolic link */
59 #define S_ISLNK(m) (((m)&_IFMT) == _IFLNK)
64 * Handle used to manage a pipe.
66 struct GNUNET_DISK_PipeHandle
69 * File descriptors for the pipe.
70 * One or both of them could be NULL.
72 struct GNUNET_DISK_FileHandle *fd[2];
77 * Closure for the recursion to determine the file size
80 struct GetFileSizeData
83 * Set to the total file size.
88 * GNUNET_YES if symbolic links should be included.
90 int include_sym_links;
93 * GNUNET_YES if mode is file-only (return total == -1 for directories).
101 * Translate GNUnet-internal permission bitmap to UNIX file
102 * access permission bitmap.
104 * @param perm file permissions, GNUnet style
105 * @return file permissions, UNIX style
108 translate_unix_perms (enum GNUNET_DISK_AccessPermissions perm)
113 if (perm & GNUNET_DISK_PERM_USER_READ)
115 if (perm & GNUNET_DISK_PERM_USER_WRITE)
117 if (perm & GNUNET_DISK_PERM_USER_EXEC)
119 if (perm & GNUNET_DISK_PERM_GROUP_READ)
121 if (perm & GNUNET_DISK_PERM_GROUP_WRITE)
123 if (perm & GNUNET_DISK_PERM_GROUP_EXEC)
125 if (perm & GNUNET_DISK_PERM_OTHER_READ)
127 if (perm & GNUNET_DISK_PERM_OTHER_WRITE)
129 if (perm & GNUNET_DISK_PERM_OTHER_EXEC)
138 * Iterate over all files in the given directory and
139 * accumulate their size.
141 * @param cls closure of type `struct GetFileSizeData`
142 * @param fn current filename we are looking at
143 * @return #GNUNET_SYSERR on serious errors, otherwise #GNUNET_OK
146 getSizeRec (void *cls, const char *fn)
148 struct GetFileSizeData *gfsd = cls;
150 #if defined (HAVE_STAT64) && !(defined (_FILE_OFFSET_BITS) && _FILE_OFFSET_BITS == 64)
153 if (0 != STAT64 (fn, &buf))
155 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_DEBUG, "stat64", fn);
156 return GNUNET_SYSERR;
161 if (0 != STAT (fn, &buf))
163 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_DEBUG, "stat", fn);
164 return GNUNET_SYSERR;
167 if ((S_ISDIR (buf.st_mode)) && (gfsd->single_file_mode == GNUNET_YES))
170 return GNUNET_SYSERR;
172 if ((!S_ISLNK (buf.st_mode)) || (gfsd->include_sym_links == GNUNET_YES))
173 gfsd->total += buf.st_size;
174 if ((S_ISDIR (buf.st_mode)) && (0 == ACCESS (fn, X_OK)) &&
175 ((!S_ISLNK (buf.st_mode)) || (gfsd->include_sym_links == GNUNET_YES)))
177 if (GNUNET_SYSERR == GNUNET_DISK_directory_scan (fn, &getSizeRec, gfsd))
178 return GNUNET_SYSERR;
185 * Checks whether a handle is invalid
187 * @param h handle to check
188 * @return #GNUNET_YES if invalid, #GNUNET_NO if valid
191 GNUNET_DISK_handle_invalid (const struct GNUNET_DISK_FileHandle *h)
194 return ((!h) || (h->h == INVALID_HANDLE_VALUE)) ? GNUNET_YES : GNUNET_NO;
196 return ((!h) || (h->fd == -1)) ? GNUNET_YES : GNUNET_NO;
201 * Get the size of an open file.
203 * @param fh open file handle
204 * @param size where to write size of the file
205 * @return #GNUNET_OK on success, #GNUNET_SYSERR on error
208 GNUNET_DISK_file_handle_size (struct GNUNET_DISK_FileHandle *fh,
214 b = GetFileSizeEx (fh->h, &li);
217 SetErrnoFromWinError (GetLastError ());
218 return GNUNET_SYSERR;
220 *size = (off_t) li.QuadPart;
224 if (0 != FSTAT (fh->fd, &sbuf))
225 return GNUNET_SYSERR;
226 *size = sbuf.st_size;
233 * Move the read/write pointer in a file
235 * @param h handle of an open file
236 * @param offset position to move to
237 * @param whence specification to which position the offset parameter relates to
238 * @return the new position on success, #GNUNET_SYSERR otherwise
241 GNUNET_DISK_file_seek (const struct GNUNET_DISK_FileHandle *h,
243 enum GNUNET_DISK_Seek whence)
248 return GNUNET_SYSERR;
253 LARGE_INTEGER new_pos;
256 static DWORD t[] = { FILE_BEGIN, FILE_CURRENT, FILE_END };
257 li.QuadPart = offset;
259 b = SetFilePointerEx (h->h, li, &new_pos, t[whence]);
262 SetErrnoFromWinError (GetLastError ());
263 return GNUNET_SYSERR;
265 return (off_t) new_pos.QuadPart;
267 static int t[] = { SEEK_SET, SEEK_CUR, SEEK_END };
269 return lseek (h->fd, offset, t[whence]);
275 * Get the size of the file (or directory) of the given file (in
278 * @param filename name of the file or directory
279 * @param size set to the size of the file (or,
280 * in the case of directories, the sum
281 * of all sizes of files in the directory)
282 * @param include_symbolic_links should symbolic links be
284 * @param single_file_mode #GNUNET_YES to only get size of one file
285 * and return #GNUNET_SYSERR for directories.
286 * @return #GNUNET_SYSERR on error, #GNUNET_OK on success
289 GNUNET_DISK_file_size (const char *filename,
291 int include_symbolic_links,
292 int single_file_mode)
294 struct GetFileSizeData gfsd;
297 GNUNET_assert (size != NULL);
299 gfsd.include_sym_links = include_symbolic_links;
300 gfsd.single_file_mode = single_file_mode;
301 ret = getSizeRec (&gfsd, filename);
308 * Obtain some unique identifiers for the given file
309 * that can be used to identify it in the local system.
310 * This function is used between GNUnet processes to
311 * quickly check if two files with the same absolute path
312 * are actually identical. The two processes represent
313 * the same peer but may communicate over the network
314 * (and the file may be on an NFS volume). This function
315 * may not be supported on all operating systems.
317 * @param filename name of the file
318 * @param dev set to the device ID
319 * @param ino set to the inode ID
320 * @return #GNUNET_OK on success
323 GNUNET_DISK_file_get_identifiers (const char *filename, uint64_t * dev,
328 // FIXME NILS: test this
329 struct GNUNET_DISK_FileHandle *fh;
330 BY_HANDLE_FILE_INFORMATION info;
333 fh = GNUNET_DISK_file_open (filename,
334 GNUNET_DISK_OPEN_READ,
335 GNUNET_DISK_PERM_NONE);
337 return GNUNET_SYSERR;
338 succ = GetFileInformationByHandle (fh->h, &info);
339 GNUNET_DISK_file_close (fh);
342 return GNUNET_SYSERR;
344 *dev = info.dwVolumeSerialNumber;
345 *ino = ((((uint64_t) info.nFileIndexHigh) << (sizeof (DWORD) * 8)) | info.nFileIndexLow);
352 if (0 != stat (filename, &sbuf))
354 return GNUNET_SYSERR;
356 *ino = (uint64_t) sbuf.st_ino;
365 if (0 != statvfs (filename, &fbuf))
367 return GNUNET_SYSERR;
369 *dev = (uint64_t) fbuf.f_fsid;
375 if (0 != statfs (filename, &fbuf))
377 return GNUNET_SYSERR;
379 *dev = ((uint64_t) fbuf.f_fsid.val[0]) << 32 ||
380 ((uint64_t) fbuf.f_fsid.val[1]);
385 #endif /* !WINDOWS */
391 * Create the name for a temporary file or directory from a template.
393 * @param t template (without XXXXX or "/tmp/")
394 * @return name ready for passing to 'mktemp' or 'mkdtemp', NULL on error
397 mktemp_name (const char *t)
403 if ((t[0] != '/') && (t[0] != '\\')
405 && !(isalpha ((int) t[0]) && (t[0] != '\0') && (t[1] == ':'))
409 /* FIXME: This uses system codepage on W32, not UTF-8 */
410 tmpdir = getenv ("TMPDIR");
412 tmpdir = getenv ("TMP");
414 tmpdir = getenv ("TEMP");
417 GNUNET_asprintf (&tmpl, "%s/%s%s", tmpdir, t, "XXXXXX");
421 GNUNET_asprintf (&tmpl, "%s%s", t, "XXXXXX");
424 fn = (char *) GNUNET_malloc (MAX_PATH + 1);
425 if (ERROR_SUCCESS != plibc_conv_to_win_path (tmpl, fn))
448 tfn = GNUNET_strdup (fn);
449 random_fn = _mktemp (tfn);
450 if (NULL == random_fn)
455 /* FIXME: assume fn to be UTF-8-encoded and do the right thing */
456 if (0 == CreateDirectoryA (tfn, NULL))
458 DWORD error = GetLastError ();
460 if (ERROR_ALREADY_EXISTS == error)
471 * Update POSIX permissions mask of a file on disk. If both argumets
472 * are #GNUNET_NO, the file is made world-read-write-executable (777).
473 * Does nothing on W32.
475 * @param fn name of the file to update
476 * @param require_uid_match #GNUNET_YES means 700
477 * @param require_gid_match #GNUNET_YES means 770 unless @a require_uid_match is set
480 GNUNET_DISK_fix_permissions (const char *fn,
481 int require_uid_match,
482 int require_gid_match)
490 * Update POSIX permissions mask of a file on disk. If both argumets
491 * are #GNUNET_NO, the file is made world-read-write-executable (777).
493 * @param fn name of the file to update
494 * @param require_uid_match #GNUNET_YES means 700
495 * @param require_gid_match #GNUNET_YES means 770 unless @a require_uid_match is set
498 GNUNET_DISK_fix_permissions (const char *fn,
499 int require_uid_match,
500 int require_gid_match)
504 if (GNUNET_YES == require_uid_match)
505 mode = S_IRUSR | S_IWUSR | S_IXUSR;
506 else if (GNUNET_YES == require_gid_match)
507 mode = S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IWGRP | S_IXGRP;
509 mode = S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IWGRP | S_IXGRP | S_IROTH | S_IWOTH | S_IXOTH;
510 if (0 != chmod (fn, mode))
511 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING,
519 * Create an (empty) temporary directory on disk. If the given name is not
520 * an absolute path, the current 'TMPDIR' will be prepended. In any case,
521 * 6 random characters will be appended to the name to create a unique
524 * @param t component to use for the name;
525 * does NOT contain "XXXXXX" or "/tmp/".
526 * @return NULL on error, otherwise name of fresh
527 * file on disk in directory for temporary files
530 GNUNET_DISK_mkdtemp (const char *t)
535 omask = umask (S_IWGRP | S_IWOTH | S_IRGRP | S_IROTH);
536 fn = mktemp_name (t);
537 if (fn != mkdtemp (fn))
539 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_ERROR, "mkdtemp", fn);
550 * Move a file out of the way (create a backup) by
551 * renaming it to "orig.NUM~" where NUM is the smallest
552 * number that is not used yet.
554 * @param fil name of the file to back up
557 GNUNET_DISK_file_backup (const char *fil)
563 slen = strlen (fil) + 20;
564 target = GNUNET_malloc (slen);
568 GNUNET_snprintf (target, slen,
572 } while (0 == access (target, F_OK));
573 if (0 != rename (fil, target))
574 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR,
577 GNUNET_free (target);
582 * Create an (empty) temporary file on disk. If the given name is not
583 * an absolute path, the current 'TMPDIR' will be prepended. In any case,
584 * 6 random characters will be appended to the name to create a unique
587 * @param t component to use for the name;
588 * does NOT contain "XXXXXX" or "/tmp/".
589 * @return NULL on error, otherwise name of fresh
590 * file on disk in directory for temporary files
593 GNUNET_DISK_mktemp (const char *t)
599 omask = umask (S_IWGRP | S_IWOTH | S_IRGRP | S_IROTH);
600 fn = mktemp_name (t);
601 if (-1 == (fd = mkstemp (fn)))
603 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_ERROR, "mkstemp", fn);
610 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "close", fn);
616 * Test if @a fil is a directory and listable. Optionally, also check if the
617 * directory is readable. Will not print an error message if the directory does
618 * not exist. Will log errors if #GNUNET_SYSERR is returned (i.e., a file exists
619 * with the same name).
621 * @param fil filename to test
622 * @param is_readable #GNUNET_YES to additionally check if @a fil is readable;
623 * #GNUNET_NO to disable this check
624 * @return #GNUNET_YES if yes, #GNUNET_NO if not; #GNUNET_SYSERR if it
625 * does not exist or stat'ed
628 GNUNET_DISK_directory_test (const char *fil,
631 struct stat filestat;
634 ret = STAT (fil, &filestat);
638 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "stat", fil);
639 return GNUNET_SYSERR;
641 if (!S_ISDIR (filestat.st_mode))
643 LOG (GNUNET_ERROR_TYPE_INFO,
644 "A file already exits with the same name %s\n", fil);
647 if (GNUNET_YES == is_readable)
648 ret = ACCESS (fil, R_OK | X_OK);
650 ret = ACCESS (fil, X_OK);
653 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "access", fil);
661 * Check that fil corresponds to a filename
662 * (of a file that exists and that is not a directory).
664 * @param fil filename to check
665 * @return #GNUNET_YES if yes, #GNUNET_NO if not a file, #GNUNET_SYSERR if something
666 * else (will print an error message in that case, too).
669 GNUNET_DISK_file_test (const char *fil)
671 struct stat filestat;
675 rdir = GNUNET_STRINGS_filename_expand (fil);
677 return GNUNET_SYSERR;
679 ret = STAT (rdir, &filestat);
684 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "stat", rdir);
686 return GNUNET_SYSERR;
691 if (!S_ISREG (filestat.st_mode))
696 if (ACCESS (rdir, F_OK) < 0)
698 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "access", rdir);
700 return GNUNET_SYSERR;
708 * Implementation of "mkdir -p"
710 * @param dir the directory to create
711 * @return #GNUNET_OK on success, #GNUNET_SYSERR on failure
714 GNUNET_DISK_directory_create (const char *dir)
722 rdir = GNUNET_STRINGS_filename_expand (dir);
726 return GNUNET_SYSERR;
731 pos = 1; /* skip heading '/' */
733 /* Local or Network path? */
734 if (strncmp (rdir, "\\\\", 2) == 0)
739 if (rdir[pos] == '\\')
749 pos = 3; /* strlen("C:\\") */
752 /* Check which low level directories already exist */
754 rdir[len] = DIR_SEPARATOR;
757 if (DIR_SEPARATOR == rdir[pos2])
760 ret = GNUNET_DISK_directory_test (rdir, GNUNET_NO);
761 if (GNUNET_NO == ret)
763 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
764 "Creating directory `%s' failed",
767 return GNUNET_SYSERR;
769 rdir[pos2] = DIR_SEPARATOR;
770 if (GNUNET_YES == ret)
781 /* Start creating directories */
784 if ((rdir[pos] == DIR_SEPARATOR) || (pos == len))
787 ret = GNUNET_DISK_directory_test (rdir, GNUNET_NO);
788 if (GNUNET_NO == ret)
790 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
791 "Creating directory `%s' failed",
794 return GNUNET_SYSERR;
796 if (GNUNET_SYSERR == ret)
799 ret = mkdir (rdir, S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH); /* 755 */
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, "Error reading from pipe: %u\n", GetLastError ());
969 SetErrnoFromWinError (GetLastError ());
970 return GNUNET_SYSERR;
974 LOG (GNUNET_ERROR_TYPE_DEBUG,
975 "ReadFile() queued a read, cancelling\n");
978 return GNUNET_SYSERR;
981 LOG (GNUNET_ERROR_TYPE_DEBUG,
994 /* set to non-blocking, read, then set back */
995 flags = fcntl (h->fd, F_GETFL);
996 if (0 == (flags & O_NONBLOCK))
997 (void) fcntl (h->fd, F_SETFL, flags | O_NONBLOCK);
998 ret = read (h->fd, result, len);
999 if (0 == (flags & O_NONBLOCK))
1002 (void) fcntl (h->fd, F_SETFL, flags);
1011 * Read the contents of a binary file into a buffer.
1013 * @param fn file name
1014 * @param result the buffer to write the result to
1015 * @param len the maximum number of bytes to read
1016 * @return number of bytes read, #GNUNET_SYSERR on failure
1019 GNUNET_DISK_fn_read (const char *fn,
1023 struct GNUNET_DISK_FileHandle *fh;
1027 fh = GNUNET_DISK_file_open (fn,
1028 GNUNET_DISK_OPEN_READ,
1029 GNUNET_DISK_PERM_NONE);
1031 return GNUNET_SYSERR;
1032 ret = GNUNET_DISK_file_read (fh, result, len);
1034 GNUNET_assert (GNUNET_OK == GNUNET_DISK_file_close (fh));
1041 * Write a buffer to a file.
1043 * @param h handle to open file
1044 * @param buffer the data to write
1045 * @param n number of bytes to write
1046 * @return number of bytes written on success, #GNUNET_SYSERR on error
1049 GNUNET_DISK_file_write (const struct GNUNET_DISK_FileHandle * h,
1056 return GNUNET_SYSERR;
1060 DWORD bytes_written;
1062 if (h->type == GNUNET_DISK_HANLDE_TYPE_FILE)
1064 if (!WriteFile (h->h, buffer, n, &bytes_written, NULL))
1066 SetErrnoFromWinError (GetLastError ());
1067 return GNUNET_SYSERR;
1070 else if (h->type == GNUNET_DISK_HANLDE_TYPE_PIPE)
1072 LOG (GNUNET_ERROR_TYPE_DEBUG, "It is a pipe trying to write %u bytes\n", n);
1073 if (!WriteFile (h->h, buffer, n, &bytes_written, h->oOverlapWrite))
1075 if (GetLastError () != ERROR_IO_PENDING)
1077 SetErrnoFromWinError (GetLastError ());
1078 LOG (GNUNET_ERROR_TYPE_DEBUG, "Error writing to pipe: %u\n",
1080 return GNUNET_SYSERR;
1082 LOG (GNUNET_ERROR_TYPE_DEBUG, "Will get overlapped result\n");
1083 if (!GetOverlappedResult (h->h, h->oOverlapWrite, &bytes_written, TRUE))
1085 SetErrnoFromWinError (GetLastError ());
1086 LOG (GNUNET_ERROR_TYPE_DEBUG,
1087 "Error getting overlapped result while writing to pipe: %u\n",
1089 return GNUNET_SYSERR;
1095 if (!GetOverlappedResult (h->h, h->oOverlapWrite, &ovr, TRUE))
1097 LOG (GNUNET_ERROR_TYPE_DEBUG,
1098 "Error getting control overlapped result while writing to pipe: %u\n",
1103 LOG (GNUNET_ERROR_TYPE_DEBUG,
1104 "Wrote %u bytes (ovr says %u), picking the greatest\n",
1105 bytes_written, ovr);
1108 if (bytes_written == 0)
1112 LOG (GNUNET_ERROR_TYPE_DEBUG, "Wrote %u bytes, returning -1 with EAGAIN\n", bytes_written);
1114 return GNUNET_SYSERR;
1117 LOG (GNUNET_ERROR_TYPE_DEBUG, "Wrote %u bytes\n", bytes_written);
1123 return bytes_written;
1125 return write (h->fd, buffer, n);
1131 * Write a buffer to a file, blocking, if necessary.
1133 * @param h handle to open file
1134 * @param buffer the data to write
1135 * @param n number of bytes to write
1136 * @return number of bytes written on success, #GNUNET_SYSERR on error
1139 GNUNET_DISK_file_write_blocking (const struct GNUNET_DISK_FileHandle * h,
1146 return GNUNET_SYSERR;
1150 DWORD bytes_written;
1151 /* We do a non-overlapped write, which is as blocking as it gets */
1152 LOG (GNUNET_ERROR_TYPE_DEBUG, "Writing %u bytes\n", n);
1153 if (!WriteFile (h->h, buffer, n, &bytes_written, NULL))
1155 SetErrnoFromWinError (GetLastError ());
1156 LOG (GNUNET_ERROR_TYPE_DEBUG, "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, "Error writing to pipe: %u\n",
1169 return GNUNET_SYSERR;
1172 LOG (GNUNET_ERROR_TYPE_DEBUG,
1175 return bytes_written;
1180 /* set to blocking, write, then set back */
1181 flags = fcntl (h->fd, F_GETFL);
1182 if (0 != (flags & O_NONBLOCK))
1183 (void) fcntl (h->fd, F_SETFL, flags - O_NONBLOCK);
1184 ret = write (h->fd, buffer, n);
1185 if (0 == (flags & O_NONBLOCK))
1186 (void) fcntl (h->fd, F_SETFL, flags);
1193 * Write a buffer to a file. If the file is longer than the
1194 * number of bytes that will be written, it will be truncated.
1196 * @param fn file name
1197 * @param buffer the data to write
1198 * @param n number of bytes to write
1199 * @param mode file permissions
1200 * @return number of bytes written on success, #GNUNET_SYSERR on error
1203 GNUNET_DISK_fn_write (const char *fn,
1206 enum GNUNET_DISK_AccessPermissions mode)
1208 struct GNUNET_DISK_FileHandle *fh;
1211 fh = GNUNET_DISK_file_open (fn,
1212 GNUNET_DISK_OPEN_WRITE | GNUNET_DISK_OPEN_TRUNCATE
1213 | GNUNET_DISK_OPEN_CREATE, mode);
1215 return GNUNET_SYSERR;
1216 ret = GNUNET_DISK_file_write (fh, buffer, n);
1217 GNUNET_assert (GNUNET_OK == GNUNET_DISK_file_close (fh));
1223 * Scan a directory for files.
1225 * @param dir_name the name of the directory
1226 * @param callback the method to call for each file,
1227 * can be NULL, in that case, we only count
1228 * @param callback_cls closure for @a callback
1229 * @return the number of files found, #GNUNET_SYSERR on error or
1230 * ieration aborted by callback returning #GNUNET_SYSERR
1233 GNUNET_DISK_directory_scan (const char *dir_name,
1234 GNUNET_FileNameCallback callback,
1238 struct dirent *finfo;
1244 unsigned int name_len;
1245 unsigned int n_size;
1247 GNUNET_assert (NULL != dir_name);
1248 dname = GNUNET_STRINGS_filename_expand (dir_name);
1250 return GNUNET_SYSERR;
1251 while ( (strlen (dname) > 0) &&
1252 (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,
1259 GNUNET_free (dname);
1260 return GNUNET_SYSERR;
1262 if (! S_ISDIR (istat.st_mode))
1264 LOG (GNUNET_ERROR_TYPE_WARNING,
1265 _("Expected `%s' to be a directory!\n"),
1267 GNUNET_free (dname);
1268 return GNUNET_SYSERR;
1271 dinfo = OPENDIR (dname);
1272 if ( (EACCES == errno) ||
1275 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING,
1280 GNUNET_free (dname);
1281 return GNUNET_SYSERR;
1284 n_size = strlen (dname) + name_len + strlen (DIR_SEPARATOR_STR) + 1;
1285 name = GNUNET_malloc (n_size);
1286 while (NULL != (finfo = READDIR (dinfo)))
1288 if ( (0 == strcmp (finfo->d_name, ".")) ||
1289 (0 == strcmp (finfo->d_name, "..")) )
1291 if (NULL != callback)
1293 if (name_len < strlen (finfo->d_name))
1296 name_len = strlen (finfo->d_name);
1297 n_size = strlen (dname) + name_len + strlen (DIR_SEPARATOR_STR) + 1;
1298 name = GNUNET_malloc (n_size);
1300 /* dname can end in "/" only if dname == "/";
1301 * if dname does not end in "/", we need to add
1302 * a "/" (otherwise, we must not!) */
1303 GNUNET_snprintf (name,
1307 (0 == strcmp (dname,
1310 : DIR_SEPARATOR_STR,
1312 ret = callback (callback_cls,
1314 if (GNUNET_OK != ret)
1318 GNUNET_free (dname);
1319 if (GNUNET_NO == ret)
1321 return GNUNET_SYSERR;
1328 GNUNET_free (dname);
1334 * Function that removes the given directory by calling
1335 * #GNUNET_DISK_directory_remove().
1337 * @param unused not used
1338 * @param fn directory to remove
1339 * @return #GNUNET_OK
1342 remove_helper (void *unused,
1346 (void) GNUNET_DISK_directory_remove (fn);
1352 * Remove all files in a directory (rm -r). Call with
1355 * @param filename the file to remove
1356 * @return #GNUNET_OK on success, #GNUNET_SYSERR on error
1359 GNUNET_DISK_directory_remove (const char *filename)
1363 if (NULL == filename)
1366 return GNUNET_SYSERR;
1368 if (0 != LSTAT (filename, &istat))
1369 return GNUNET_NO; /* file may not exist... */
1370 (void) CHMOD (filename,
1371 S_IWUSR | S_IRUSR | S_IXUSR);
1372 if (0 == UNLINK (filename))
1374 if ( (errno != EISDIR) &&
1375 /* EISDIR is not sufficient in all cases, e.g.
1376 * sticky /tmp directory may result in EPERM on BSD.
1377 * So we also explicitly check "isDirectory" */
1379 GNUNET_DISK_directory_test (filename,
1382 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING,
1385 return GNUNET_SYSERR;
1387 if (GNUNET_SYSERR ==
1388 GNUNET_DISK_directory_scan (filename,
1391 return GNUNET_SYSERR;
1392 if (0 != RMDIR (filename))
1394 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING,
1397 return GNUNET_SYSERR;
1406 * @param src file to copy
1407 * @param dst destination file name
1408 * @return #GNUNET_OK on success, #GNUNET_SYSERR on error
1411 GNUNET_DISK_file_copy (const char *src,
1419 struct GNUNET_DISK_FileHandle *in;
1420 struct GNUNET_DISK_FileHandle *out;
1423 GNUNET_DISK_file_size (src,
1428 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR,
1431 return GNUNET_SYSERR;
1434 in = GNUNET_DISK_file_open (src,
1435 GNUNET_DISK_OPEN_READ,
1436 GNUNET_DISK_PERM_NONE);
1439 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR,
1442 return GNUNET_SYSERR;
1445 GNUNET_DISK_file_open (dst,
1446 GNUNET_DISK_OPEN_WRITE | GNUNET_DISK_OPEN_CREATE |
1447 GNUNET_DISK_OPEN_FAILIFEXISTS,
1448 GNUNET_DISK_PERM_USER_READ |
1449 GNUNET_DISK_PERM_USER_WRITE |
1450 GNUNET_DISK_PERM_GROUP_READ |
1451 GNUNET_DISK_PERM_GROUP_WRITE);
1454 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR,
1457 GNUNET_DISK_file_close (in);
1458 return GNUNET_SYSERR;
1460 buf = GNUNET_malloc (COPY_BLK_SIZE);
1463 len = COPY_BLK_SIZE;
1464 if (len > size - pos)
1466 sret = GNUNET_DISK_file_read (in,
1470 (len != (size_t) sret) )
1472 sret = GNUNET_DISK_file_write (out,
1476 (len != (size_t) sret) )
1481 GNUNET_DISK_file_close (in);
1482 GNUNET_DISK_file_close (out);
1486 GNUNET_DISK_file_close (in);
1487 GNUNET_DISK_file_close (out);
1488 return GNUNET_SYSERR;
1493 * @brief Removes special characters as ':' from a filename.
1494 * @param fn the filename to canonicalize
1497 GNUNET_DISK_filename_canonicalize (char *fn)
1502 for (idx = fn; *idx; idx++)
1506 if (c == '/' || c == '\\' || c == ':' ||
1507 c == '*' || c == '?' || c == '"' ||
1508 c == '<' || c == '>' || c == '|')
1518 * @brief Change owner of a file
1520 * @param filename name of file to change the owner of
1521 * @param user name of the new owner
1522 * @return #GNUNET_OK on success, #GNUNET_SYSERR on failure
1525 GNUNET_DISK_file_change_owner (const char *filename,
1531 pws = getpwnam (user);
1534 LOG (GNUNET_ERROR_TYPE_ERROR,
1535 _("Cannot obtain information about user `%s': %s\n"),
1538 return GNUNET_SYSERR;
1540 if (0 != chown (filename,
1544 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING,
1547 return GNUNET_SYSERR;
1555 * Lock a part of a file
1557 * @param fh file handle
1558 * @param lock_start absolute position from where to lock
1559 * @param lock_end absolute position until where to lock
1560 * @param excl #GNUNET_YES for an exclusive lock
1561 * @return #GNUNET_OK on success, #GNUNET_SYSERR on error
1564 GNUNET_DISK_file_lock (struct GNUNET_DISK_FileHandle *fh,
1572 return GNUNET_SYSERR;
1578 memset (&fl, 0, sizeof (struct flock));
1579 fl.l_type = excl ? F_WRLCK : F_RDLCK;
1580 fl.l_whence = SEEK_SET;
1581 fl.l_start = lock_start;
1582 fl.l_len = lock_end;
1584 return fcntl (fh->fd, F_SETLK, &fl) != 0 ? GNUNET_SYSERR : GNUNET_OK;
1587 off_t diff = lock_end - lock_start;
1588 DWORD diff_low, diff_high;
1589 diff_low = (DWORD) (diff & 0xFFFFFFFF);
1590 diff_high = (DWORD) ((diff >> (sizeof (DWORD) * 8)) & 0xFFFFFFFF);
1592 memset (&o, 0, sizeof (OVERLAPPED));
1593 o.Offset = (DWORD) (lock_start & 0xFFFFFFFF);;
1594 o.OffsetHigh = (DWORD) (((lock_start & ~0xFFFFFFFF) >> (sizeof (DWORD) * 8)) & 0xFFFFFFFF);
1597 (fh->h, (excl ? LOCKFILE_EXCLUSIVE_LOCK : 0) | LOCKFILE_FAIL_IMMEDIATELY,
1598 0, diff_low, diff_high, &o))
1600 SetErrnoFromWinError (GetLastError ());
1601 return GNUNET_SYSERR;
1610 * Unlock a part of a file
1612 * @param fh file handle
1613 * @param unlock_start absolute position from where to unlock
1614 * @param unlock_end absolute position until where to unlock
1615 * @return #GNUNET_OK on success, #GNUNET_SYSERR on error
1618 GNUNET_DISK_file_unlock (struct GNUNET_DISK_FileHandle *fh,
1625 return GNUNET_SYSERR;
1631 memset (&fl, 0, sizeof (struct flock));
1632 fl.l_type = F_UNLCK;
1633 fl.l_whence = SEEK_SET;
1634 fl.l_start = unlock_start;
1635 fl.l_len = unlock_end;
1637 return fcntl (fh->fd, F_SETLK, &fl) != 0 ? GNUNET_SYSERR : GNUNET_OK;
1640 off_t diff = unlock_end - unlock_start;
1641 DWORD diff_low, diff_high;
1642 diff_low = (DWORD) (diff & 0xFFFFFFFF);
1643 diff_high = (DWORD) ((diff >> (sizeof (DWORD) * 8)) & 0xFFFFFFFF);
1645 memset (&o, 0, sizeof (OVERLAPPED));
1646 o.Offset = (DWORD) (unlock_start & 0xFFFFFFFF);;
1647 o.OffsetHigh = (DWORD) (((unlock_start & ~0xFFFFFFFF) >> (sizeof (DWORD) * 8)) & 0xFFFFFFFF);
1649 if (!UnlockFileEx (fh->h, 0, diff_low, diff_high, &o))
1651 SetErrnoFromWinError (GetLastError ());
1652 return GNUNET_SYSERR;
1661 * Open a file. Note that the access permissions will only be
1662 * used if a new file is created and if the underlying operating
1663 * system supports the given permissions.
1665 * @param fn file name to be opened
1666 * @param flags opening flags, a combination of GNUNET_DISK_OPEN_xxx bit flags
1667 * @param perm permissions for the newly created file, use
1668 * #GNUNET_DISK_PERM_NONE if a file could not be created by this
1669 * call (because of flags)
1670 * @return IO handle on success, NULL on error
1672 struct GNUNET_DISK_FileHandle *
1673 GNUNET_DISK_file_open (const char *fn,
1674 enum GNUNET_DISK_OpenFlags flags,
1675 enum GNUNET_DISK_AccessPermissions perm)
1678 struct GNUNET_DISK_FileHandle *ret;
1684 wchar_t wexpfn[MAX_PATH + 1];
1691 expfn = GNUNET_STRINGS_filename_expand (fn);
1696 if (GNUNET_DISK_OPEN_READWRITE == (flags & GNUNET_DISK_OPEN_READWRITE))
1697 oflags = O_RDWR; /* note: O_RDWR is NOT always O_RDONLY | O_WRONLY */
1698 else if (flags & GNUNET_DISK_OPEN_READ)
1700 else if (flags & GNUNET_DISK_OPEN_WRITE)
1705 GNUNET_free (expfn);
1708 if (flags & GNUNET_DISK_OPEN_FAILIFEXISTS)
1709 oflags |= (O_CREAT | O_EXCL);
1710 if (flags & GNUNET_DISK_OPEN_TRUNCATE)
1712 if (flags & GNUNET_DISK_OPEN_APPEND)
1714 if(GNUNET_NO == GNUNET_DISK_file_test(fn))
1716 if (flags & GNUNET_DISK_OPEN_CREATE )
1718 (void) GNUNET_DISK_directory_create_for_file (expfn);
1720 mode = translate_unix_perms (perm);
1724 fd = open (expfn, oflags
1728 | O_LARGEFILE, mode);
1731 if (0 == (flags & GNUNET_DISK_OPEN_FAILIFEXISTS))
1732 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "open", expfn);
1734 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_DEBUG, "open", expfn);
1735 GNUNET_free (expfn);
1742 if (GNUNET_DISK_OPEN_READWRITE == (flags & GNUNET_DISK_OPEN_READWRITE))
1743 access = FILE_READ_DATA | FILE_WRITE_DATA;
1744 else if (flags & GNUNET_DISK_OPEN_READ)
1745 access = FILE_READ_DATA;
1746 else if (flags & GNUNET_DISK_OPEN_WRITE)
1747 access = FILE_WRITE_DATA;
1749 if (flags & GNUNET_DISK_OPEN_FAILIFEXISTS)
1753 else if (flags & GNUNET_DISK_OPEN_CREATE)
1755 (void) GNUNET_DISK_directory_create_for_file (expfn);
1756 if (flags & GNUNET_DISK_OPEN_TRUNCATE)
1757 disp = CREATE_ALWAYS;
1761 else if (flags & GNUNET_DISK_OPEN_TRUNCATE)
1763 disp = TRUNCATE_EXISTING;
1767 disp = OPEN_EXISTING;
1770 if (ERROR_SUCCESS == plibc_conv_to_win_pathwconv(expfn, wexpfn))
1771 h = CreateFileW (wexpfn, access,
1772 FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE, NULL,
1773 disp, FILE_ATTRIBUTE_NORMAL, NULL);
1775 h = INVALID_HANDLE_VALUE;
1776 if (h == INVALID_HANDLE_VALUE)
1779 SetErrnoFromWinError (GetLastError ());
1781 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_INFO, "open", expfn);
1782 GNUNET_free (expfn);
1787 if (flags & GNUNET_DISK_OPEN_APPEND)
1788 if (SetFilePointer (h, 0, 0, FILE_END) == INVALID_SET_FILE_POINTER)
1790 SetErrnoFromWinError (GetLastError ());
1791 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "SetFilePointer", expfn);
1793 GNUNET_free (expfn);
1798 ret = GNUNET_new (struct GNUNET_DISK_FileHandle);
1801 ret->type = GNUNET_DISK_HANLDE_TYPE_FILE;
1805 GNUNET_free (expfn);
1811 * Close an open file.
1813 * @param h file handle
1814 * @return #GNUNET_OK on success, #GNUNET_SYSERR otherwise
1817 GNUNET_DISK_file_close (struct GNUNET_DISK_FileHandle *h)
1823 return GNUNET_SYSERR;
1829 if (! CloseHandle (h->h))
1831 SetErrnoFromWinError (GetLastError ());
1832 LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING, "close");
1833 ret = GNUNET_SYSERR;
1835 if (h->oOverlapRead)
1837 if (! CloseHandle (h->oOverlapRead->hEvent))
1839 SetErrnoFromWinError (GetLastError ());
1840 LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING, "close");
1841 ret = GNUNET_SYSERR;
1843 GNUNET_free (h->oOverlapRead);
1845 if (h->oOverlapWrite)
1847 if (!CloseHandle (h->oOverlapWrite->hEvent))
1849 SetErrnoFromWinError (GetLastError ());
1850 LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING, "close");
1851 ret = GNUNET_SYSERR;
1853 GNUNET_free (h->oOverlapWrite);
1856 if (close (h->fd) != 0)
1858 LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING, "close");
1859 ret = GNUNET_SYSERR;
1869 * Get a GNUnet file handle from a W32 handle.
1871 * @param handle native handle
1872 * @return GNUnet file handle corresponding to the W32 handle
1874 struct GNUNET_DISK_FileHandle *
1875 GNUNET_DISK_get_handle_from_w32_handle (HANDLE osfh)
1877 struct GNUNET_DISK_FileHandle *fh;
1879 enum GNUNET_FILE_Type ftype;
1881 dwret = GetFileType (osfh);
1884 case FILE_TYPE_DISK:
1885 ftype = GNUNET_DISK_HANLDE_TYPE_FILE;
1887 case FILE_TYPE_PIPE:
1888 ftype = GNUNET_DISK_HANLDE_TYPE_PIPE;
1890 case FILE_TYPE_UNKNOWN:
1891 if ( (GetLastError () == NO_ERROR) ||
1892 (GetLastError () == ERROR_INVALID_HANDLE) )
1894 if (0 != ResetEvent (osfh))
1895 ftype = GNUNET_DISK_HANLDE_TYPE_EVENT;
1906 fh = GNUNET_new (struct GNUNET_DISK_FileHandle);
1910 if (ftype == GNUNET_DISK_HANLDE_TYPE_PIPE)
1913 * Note that we can't make it overlapped if it isn't already.
1914 * (ReOpenFile() is only available in 2003/Vista).
1915 * The process that opened this file in the first place (usually a parent
1916 * process, if this is stdin/stdout/stderr) must make it overlapped,
1917 * otherwise we're screwed, as selecting on non-overlapped handle
1920 fh->oOverlapRead = GNUNET_new (OVERLAPPED);
1921 fh->oOverlapWrite = GNUNET_new (OVERLAPPED);
1922 fh->oOverlapRead->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
1923 fh->oOverlapWrite->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
1931 * Get a handle from a native integer FD.
1933 * @param fno native integer file descriptor
1934 * @return file handle corresponding to the descriptor, NULL on error
1936 struct GNUNET_DISK_FileHandle *
1937 GNUNET_DISK_get_handle_from_int_fd (int fno)
1939 struct GNUNET_DISK_FileHandle *fh;
1941 if ( (((off_t) -1) == lseek (fno, 0, SEEK_CUR)) &&
1943 return NULL; /* invalid FD */
1946 fh = GNUNET_new (struct GNUNET_DISK_FileHandle);
1952 osfh = _get_osfhandle (fno);
1953 if (INVALID_HANDLE_VALUE == (HANDLE) osfh)
1956 fh = GNUNET_DISK_get_handle_from_w32_handle ((HANDLE) osfh);
1964 * Get a handle from a native streaming FD.
1966 * @param fd native streaming file descriptor
1967 * @return file handle corresponding to the descriptor
1969 struct GNUNET_DISK_FileHandle *
1970 GNUNET_DISK_get_handle_from_native (FILE *fd)
1978 return GNUNET_DISK_get_handle_from_int_fd (fno);
1983 * Handle for a memory-mapping operation.
1985 struct GNUNET_DISK_MapHandle
1988 * Address where the map is in memory.
1994 * Underlying OS handle.
1999 * Number of bytes mapped.
2007 #define MAP_FAILED ((void *) -1)
2011 * Map a file into memory
2013 * @param h open file handle
2014 * @param m handle to the new mapping
2015 * @param access access specification, GNUNET_DISK_MAP_TYPE_xxx
2016 * @param len size of the mapping
2017 * @return pointer to the mapped memory region, NULL on failure
2020 GNUNET_DISK_file_map (const struct GNUNET_DISK_FileHandle *h,
2021 struct GNUNET_DISK_MapHandle **m,
2022 enum GNUNET_DISK_MapType access, size_t len)
2031 DWORD mapAccess, protect;
2033 if ((access & GNUNET_DISK_MAP_TYPE_READ) &&
2034 (access & GNUNET_DISK_MAP_TYPE_WRITE))
2036 protect = PAGE_READWRITE;
2037 mapAccess = FILE_MAP_ALL_ACCESS;
2039 else if (access & GNUNET_DISK_MAP_TYPE_READ)
2041 protect = PAGE_READONLY;
2042 mapAccess = FILE_MAP_READ;
2044 else if (access & GNUNET_DISK_MAP_TYPE_WRITE)
2046 protect = PAGE_READWRITE;
2047 mapAccess = FILE_MAP_WRITE;
2055 *m = GNUNET_new (struct GNUNET_DISK_MapHandle);
2056 (*m)->h = CreateFileMapping (h->h, NULL, protect, 0, 0, NULL);
2057 if ((*m)->h == INVALID_HANDLE_VALUE)
2059 SetErrnoFromWinError (GetLastError ());
2064 (*m)->addr = MapViewOfFile ((*m)->h, mapAccess, 0, 0, len);
2067 SetErrnoFromWinError (GetLastError ());
2068 CloseHandle ((*m)->h);
2077 if (access & GNUNET_DISK_MAP_TYPE_READ)
2079 if (access & GNUNET_DISK_MAP_TYPE_WRITE)
2081 *m = GNUNET_new (struct GNUNET_DISK_MapHandle);
2082 (*m)->addr = mmap (NULL, len, prot, MAP_SHARED, h->fd, 0);
2083 GNUNET_assert (NULL != (*m)->addr);
2084 if (MAP_FAILED == (*m)->addr)
2096 * @param h mapping handle
2097 * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
2100 GNUNET_DISK_file_unmap (struct GNUNET_DISK_MapHandle *h)
2107 return GNUNET_SYSERR;
2111 ret = UnmapViewOfFile (h->addr) ? GNUNET_OK : GNUNET_SYSERR;
2112 if (ret != GNUNET_OK)
2113 SetErrnoFromWinError (GetLastError ());
2114 if (!CloseHandle (h->h) && (ret == GNUNET_OK))
2116 ret = GNUNET_SYSERR;
2117 SetErrnoFromWinError (GetLastError ());
2120 ret = munmap (h->addr, h->len) != -1 ? GNUNET_OK : GNUNET_SYSERR;
2128 * Write file changes to disk
2129 * @param h handle to an open file
2130 * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
2133 GNUNET_DISK_file_sync (const struct GNUNET_DISK_FileHandle *h)
2138 return GNUNET_SYSERR;
2144 ret = FlushFileBuffers (h->h) ? GNUNET_OK : GNUNET_SYSERR;
2145 if (ret != GNUNET_OK)
2146 SetErrnoFromWinError (GetLastError ());
2148 #elif defined(FREEBSD) || defined(OPENBSD) || defined(DARWIN)
2149 return fsync (h->fd) == -1 ? GNUNET_SYSERR : GNUNET_OK;
2151 return fdatasync (h->fd) == -1 ? GNUNET_SYSERR : GNUNET_OK;
2158 #define PIPE_BUF 512
2160 /* Copyright Bob Byrnes <byrnes <at> curl.com>
2161 http://permalink.gmane.org/gmane.os.cygwin.patches/2121
2163 /* Create a pipe, and return handles to the read and write ends,
2164 just like CreatePipe, but ensure that the write end permits
2165 FILE_READ_ATTRIBUTES access, on later versions of win32 where
2166 this is supported. This access is needed by NtQueryInformationFile,
2167 which is used to implement select and nonblocking writes.
2168 Note that the return value is either NO_ERROR or GetLastError,
2169 unlike CreatePipe, which returns a bool for success or failure. */
2171 create_selectable_pipe (PHANDLE read_pipe_ptr, PHANDLE write_pipe_ptr,
2172 LPSECURITY_ATTRIBUTES sa_ptr, DWORD psize,
2173 DWORD dwReadMode, DWORD dwWriteMode)
2175 /* Default to error. */
2176 *read_pipe_ptr = *write_pipe_ptr = INVALID_HANDLE_VALUE;
2181 /* Ensure that there is enough pipe buffer space for atomic writes. */
2182 if (psize < PIPE_BUF)
2185 char pipename[MAX_PATH];
2187 /* Retry CreateNamedPipe as long as the pipe name is in use.
2188 * Retrying will probably never be necessary, but we want
2189 * to be as robust as possible. */
2192 static volatile LONG pipe_unique_id;
2194 snprintf (pipename, sizeof pipename, "\\\\.\\pipe\\gnunet-%d-%ld",
2195 getpid (), InterlockedIncrement ((LONG *) & pipe_unique_id));
2196 LOG (GNUNET_ERROR_TYPE_DEBUG, "CreateNamedPipe: name = %s, size = %lu\n",
2198 /* Use CreateNamedPipe instead of CreatePipe, because the latter
2199 * returns a write handle that does not permit FILE_READ_ATTRIBUTES
2200 * access, on versions of win32 earlier than WinXP SP2.
2201 * CreatePipe also stupidly creates a full duplex pipe, which is
2202 * a waste, since only a single direction is actually used.
2203 * It's important to only allow a single instance, to ensure that
2204 * the pipe was not created earlier by some other process, even if
2205 * the pid has been reused. */
2206 read_pipe = CreateNamedPipeA (pipename, PIPE_ACCESS_INBOUND | FILE_FLAG_FIRST_PIPE_INSTANCE | dwReadMode, PIPE_TYPE_BYTE | PIPE_READMODE_BYTE, 1, /* max instances */
2207 psize, /* output buffer size */
2208 psize, /* input buffer size */
2209 NMPWAIT_USE_DEFAULT_WAIT, sa_ptr);
2211 if (read_pipe != INVALID_HANDLE_VALUE)
2213 LOG (GNUNET_ERROR_TYPE_DEBUG, "pipe read handle = %p\n", read_pipe);
2217 DWORD err = GetLastError ();
2221 case ERROR_PIPE_BUSY:
2222 /* The pipe is already open with compatible parameters.
2223 * Pick a new name and retry. */
2224 LOG (GNUNET_ERROR_TYPE_DEBUG, "pipe busy, retrying\n");
2226 case ERROR_ACCESS_DENIED:
2227 /* The pipe is already open with incompatible parameters.
2228 * Pick a new name and retry. */
2229 LOG (GNUNET_ERROR_TYPE_DEBUG, "pipe access denied, retrying\n");
2231 case ERROR_CALL_NOT_IMPLEMENTED:
2232 /* We are on an older Win9x platform without named pipes.
2233 * Return an anonymous pipe as the best approximation. */
2234 LOG (GNUNET_ERROR_TYPE_DEBUG,
2235 "CreateNamedPipe not implemented, resorting to "
2236 "CreatePipe: size = %lu\n", psize);
2237 if (CreatePipe (read_pipe_ptr, write_pipe_ptr, sa_ptr, psize))
2239 LOG (GNUNET_ERROR_TYPE_DEBUG, "pipe read handle = %p, write handle = %p\n",
2244 err = GetLastError ();
2245 LOG (GNUNET_ERROR_TYPE_ERROR, "CreatePipe failed: %d\n", err);
2248 LOG (GNUNET_ERROR_TYPE_ERROR, "CreateNamedPipe failed: %d\n", err);
2253 LOG (GNUNET_ERROR_TYPE_DEBUG, "CreateFile: name = %s\n", pipename);
2255 /* Open the named pipe for writing.
2256 * Be sure to permit FILE_READ_ATTRIBUTES access. */
2257 write_pipe = CreateFileA (pipename, GENERIC_WRITE | FILE_READ_ATTRIBUTES, 0, /* share mode */
2258 sa_ptr, OPEN_EXISTING, dwWriteMode, /* flags and attributes */
2259 0); /* handle to template file */
2261 if (write_pipe == INVALID_HANDLE_VALUE)
2264 DWORD err = GetLastError ();
2266 LOG (GNUNET_ERROR_TYPE_DEBUG, "CreateFile failed: %d\n", err);
2267 CloseHandle (read_pipe);
2270 LOG (GNUNET_ERROR_TYPE_DEBUG, "pipe write handle = %p\n", write_pipe);
2272 *read_pipe_ptr = read_pipe;
2273 *write_pipe_ptr = write_pipe;
2280 * Creates an interprocess channel
2282 * @param blocking_read creates an asynchronous pipe for reading if set to GNUNET_NO
2283 * @param blocking_write creates an asynchronous pipe for writing if set to GNUNET_NO
2284 * @param inherit_read inherit the parent processes stdin (only for windows)
2285 * @param inherit_write inherit the parent processes stdout (only for windows)
2286 * @return handle to the new pipe, NULL on error
2288 struct GNUNET_DISK_PipeHandle *
2289 GNUNET_DISK_pipe (int blocking_read,
2299 (void) inherit_read;
2300 (void) inherit_write;
2305 LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR,
2310 return GNUNET_DISK_pipe_from_fd (blocking_read,
2314 struct GNUNET_DISK_PipeHandle *p;
2319 p = GNUNET_new (struct GNUNET_DISK_PipeHandle);
2320 p->fd[0] = GNUNET_new (struct GNUNET_DISK_FileHandle);
2321 p->fd[1] = GNUNET_new (struct GNUNET_DISK_FileHandle);
2323 /* All pipes are overlapped. If you want them to block - just
2324 * call WriteFile() and ReadFile() with NULL overlapped pointer.
2325 * NOTE: calling with NULL overlapped pointer works only
2326 * for pipes, and doesn't seem to be a documented feature.
2327 * It will NOT work for files, because overlapped files need
2328 * to read offsets from the overlapped structure, regardless.
2329 * Pipes are not seekable, and need no offsets, which is
2330 * probably why it works for them.
2333 create_selectable_pipe (&p->fd[0]->h, &p->fd[1]->h, NULL, 0,
2334 FILE_FLAG_OVERLAPPED,
2335 FILE_FLAG_OVERLAPPED);
2338 SetErrnoFromWinError (GetLastError ());
2340 GNUNET_free (p->fd[0]);
2341 GNUNET_free (p->fd[1]);
2346 if (!DuplicateHandle
2347 (GetCurrentProcess (), p->fd[0]->h, GetCurrentProcess (), &tmp_handle, 0,
2348 inherit_read == GNUNET_YES ? TRUE : FALSE, DUPLICATE_SAME_ACCESS))
2350 SetErrnoFromWinError (GetLastError ());
2352 CloseHandle (p->fd[0]->h);
2353 CloseHandle (p->fd[1]->h);
2354 GNUNET_free (p->fd[0]);
2355 GNUNET_free (p->fd[1]);
2360 CloseHandle (p->fd[0]->h);
2361 p->fd[0]->h = tmp_handle;
2363 if (!DuplicateHandle
2364 (GetCurrentProcess (), p->fd[1]->h, GetCurrentProcess (), &tmp_handle, 0,
2365 inherit_write == GNUNET_YES ? TRUE : FALSE, DUPLICATE_SAME_ACCESS))
2367 SetErrnoFromWinError (GetLastError ());
2369 CloseHandle (p->fd[0]->h);
2370 CloseHandle (p->fd[1]->h);
2371 GNUNET_free (p->fd[0]);
2372 GNUNET_free (p->fd[1]);
2377 CloseHandle (p->fd[1]->h);
2378 p->fd[1]->h = tmp_handle;
2380 p->fd[0]->type = GNUNET_DISK_HANLDE_TYPE_PIPE;
2381 p->fd[1]->type = GNUNET_DISK_HANLDE_TYPE_PIPE;
2383 p->fd[0]->oOverlapRead = GNUNET_new (OVERLAPPED);
2384 p->fd[0]->oOverlapWrite = GNUNET_new (OVERLAPPED);
2385 p->fd[1]->oOverlapRead = GNUNET_new (OVERLAPPED);
2386 p->fd[1]->oOverlapWrite = GNUNET_new (OVERLAPPED);
2388 p->fd[0]->oOverlapRead->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2389 p->fd[0]->oOverlapWrite->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2391 p->fd[1]->oOverlapRead->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2392 p->fd[1]->oOverlapWrite->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2400 * Creates a pipe object from a couple of file descriptors.
2401 * Useful for wrapping existing pipe FDs.
2403 * @param blocking_read creates an asynchronous pipe for reading if set to GNUNET_NO
2404 * @param blocking_write creates an asynchronous pipe for writing if set to GNUNET_NO
2405 * @param fd an array of two fd values. One of them may be -1 for read-only or write-only pipes
2407 * @return handle to the new pipe, NULL on error
2409 struct GNUNET_DISK_PipeHandle *
2410 GNUNET_DISK_pipe_from_fd (int blocking_read, int blocking_write, int fd[2])
2412 struct GNUNET_DISK_PipeHandle *p;
2414 p = GNUNET_new (struct GNUNET_DISK_PipeHandle);
2419 int eno = 0; /* make gcc happy */
2424 p->fd[0] = GNUNET_new (struct GNUNET_DISK_FileHandle);
2425 p->fd[0]->fd = fd[0];
2428 flags = fcntl (fd[0], F_GETFL);
2429 flags |= O_NONBLOCK;
2430 if (0 > fcntl (fd[0], F_SETFL, flags))
2436 flags = fcntl (fd[0], F_GETFD);
2437 flags |= FD_CLOEXEC;
2438 if (0 > fcntl (fd[0], F_SETFD, flags))
2447 p->fd[1] = GNUNET_new (struct GNUNET_DISK_FileHandle);
2448 p->fd[1]->fd = fd[1];
2449 if (!blocking_write)
2451 flags = fcntl (fd[1], F_GETFL);
2452 flags |= O_NONBLOCK;
2453 if (0 > fcntl (fd[1], F_SETFL, flags))
2459 flags = fcntl (fd[1], F_GETFD);
2460 flags |= FD_CLOEXEC;
2461 if (0 > fcntl (fd[1], F_SETFD, flags))
2470 LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR, "fcntl");
2471 if (p->fd[0]->fd >= 0)
2472 GNUNET_break (0 == close (p->fd[0]->fd));
2473 if (p->fd[1]->fd >= 0)
2474 GNUNET_break (0 == close (p->fd[1]->fd));
2475 GNUNET_free_non_null (p->fd[0]);
2476 GNUNET_free_non_null (p->fd[1]);
2484 p->fd[0] = GNUNET_new (struct GNUNET_DISK_FileHandle);
2485 p->fd[0]->h = (HANDLE) _get_osfhandle (fd[0]);
2486 if (p->fd[0]->h != INVALID_HANDLE_VALUE)
2488 p->fd[0]->type = GNUNET_DISK_HANLDE_TYPE_PIPE;
2489 p->fd[0]->oOverlapRead = GNUNET_new (OVERLAPPED);
2490 p->fd[0]->oOverlapWrite = GNUNET_new (OVERLAPPED);
2491 p->fd[0]->oOverlapRead->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2492 p->fd[0]->oOverlapWrite->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2496 GNUNET_free (p->fd[0]);
2502 p->fd[1] = GNUNET_new (struct GNUNET_DISK_FileHandle);
2503 p->fd[1]->h = (HANDLE) _get_osfhandle (fd[1]);
2504 if (p->fd[1]->h != INVALID_HANDLE_VALUE)
2506 p->fd[1]->type = GNUNET_DISK_HANLDE_TYPE_PIPE;
2507 p->fd[1]->oOverlapRead = GNUNET_new (OVERLAPPED);
2508 p->fd[1]->oOverlapWrite = GNUNET_new (OVERLAPPED);
2509 p->fd[1]->oOverlapRead->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2510 p->fd[1]->oOverlapWrite->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2514 GNUNET_free (p->fd[1]);
2525 * Closes an interprocess channel
2527 * @param p pipe to close
2528 * @param end which end of the pipe to close
2529 * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
2532 GNUNET_DISK_pipe_close_end (struct GNUNET_DISK_PipeHandle *p,
2533 enum GNUNET_DISK_PipeEnd end)
2535 int ret = GNUNET_OK;
2537 if (end == GNUNET_DISK_PIPE_END_READ)
2541 ret = GNUNET_DISK_file_close (p->fd[0]);
2545 else if (end == GNUNET_DISK_PIPE_END_WRITE)
2549 ret = GNUNET_DISK_file_close (p->fd[1]);
2558 * Detaches one of the ends from the pipe.
2559 * Detached end is a fully-functional FileHandle, it will
2560 * not be affected by anything you do with the pipe afterwards.
2561 * Each end of a pipe can only be detched from it once (i.e.
2562 * it is not duplicated).
2564 * @param p pipe to detach an end from
2565 * @param end which end of the pipe to detach
2566 * @return Detached end on success, NULL on failure
2567 * (or if that end is not present or is closed).
2569 struct GNUNET_DISK_FileHandle *
2570 GNUNET_DISK_pipe_detach_end (struct GNUNET_DISK_PipeHandle *p,
2571 enum GNUNET_DISK_PipeEnd end)
2573 struct GNUNET_DISK_FileHandle *ret = NULL;
2575 if (end == GNUNET_DISK_PIPE_END_READ)
2583 else if (end == GNUNET_DISK_PIPE_END_WRITE)
2597 * Closes an interprocess channel
2599 * @param p pipe to close
2600 * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
2603 GNUNET_DISK_pipe_close (struct GNUNET_DISK_PipeHandle *p)
2605 int ret = GNUNET_OK;
2608 int write_end_close;
2609 int read_end_close_errno;
2610 int write_end_close_errno;
2612 read_end_close = GNUNET_DISK_pipe_close_end (p, GNUNET_DISK_PIPE_END_READ);
2613 read_end_close_errno = errno;
2614 write_end_close = GNUNET_DISK_pipe_close_end (p, GNUNET_DISK_PIPE_END_WRITE);
2615 write_end_close_errno = errno;
2618 if (GNUNET_OK != read_end_close)
2620 errno = read_end_close_errno;
2621 ret = read_end_close;
2623 else if (GNUNET_OK != write_end_close)
2625 errno = write_end_close_errno;
2626 ret = write_end_close;
2634 * Get the handle to a particular pipe end
2637 * @param n end to access
2638 * @return handle for the respective end
2640 const struct GNUNET_DISK_FileHandle *
2641 GNUNET_DISK_pipe_handle (const struct GNUNET_DISK_PipeHandle *p,
2642 enum GNUNET_DISK_PipeEnd n)
2646 case GNUNET_DISK_PIPE_END_READ:
2647 case GNUNET_DISK_PIPE_END_WRITE:
2657 * Retrieve OS file handle
2659 * @param fh GNUnet file descriptor
2660 * @param dst destination buffer
2661 * @param dst_len length of dst
2662 * @return #GNUNET_OK on success, #GNUNET_SYSERR otherwise
2665 GNUNET_DISK_internal_file_handle_ (const struct GNUNET_DISK_FileHandle *fh,
2666 void *dst, size_t dst_len)
2669 return GNUNET_SYSERR;
2671 if (dst_len < sizeof (HANDLE))
2672 return GNUNET_SYSERR;
2673 *((HANDLE *) dst) = fh->h;
2675 if (dst_len < sizeof (int))
2676 return GNUNET_SYSERR;
2677 *((int *) dst) = fh->fd;
2685 * Helper function for #GNUNET_DISK_purge_cfg_dir.
2687 * @param cls a `const char *` with the option to purge
2688 * @param cfg our configuration
2689 * @return #GNUNET_OK on success
2692 purge_cfg_dir (void *cls,
2693 const struct GNUNET_CONFIGURATION_Handle *cfg)
2695 const char *option = cls;
2699 GNUNET_CONFIGURATION_get_value_filename (cfg,
2704 GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
2709 if (GNUNET_SYSERR ==
2710 GNUNET_DISK_directory_remove (tmpname))
2712 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR,
2715 GNUNET_free (tmpname);
2718 GNUNET_free (tmpname);
2724 * Remove the directory given under @a option in
2725 * section [PATHS] in configuration under @a cfg_filename
2727 * @param cfg_filename configuration file to parse
2728 * @param option option with the dir name to purge
2731 GNUNET_DISK_purge_cfg_dir (const char *cfg_filename,
2734 GNUNET_break (GNUNET_OK ==
2735 GNUNET_CONFIGURATION_parse_and_run (cfg_filename,