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, int is_readable)
630 struct stat filestat;
633 ret = STAT (fil, &filestat);
637 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "stat", fil);
638 return GNUNET_SYSERR;
640 if (!S_ISDIR (filestat.st_mode))
642 LOG (GNUNET_ERROR_TYPE_DEBUG,
643 "A file already exits with the same name %s\n", fil);
646 if (GNUNET_YES == is_readable)
647 ret = ACCESS (fil, R_OK | X_OK);
649 ret = ACCESS (fil, X_OK);
652 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "access", fil);
660 * Check that fil corresponds to a filename
661 * (of a file that exists and that is not a directory).
663 * @param fil filename to check
664 * @return #GNUNET_YES if yes, #GNUNET_NO if not a file, #GNUNET_SYSERR if something
665 * else (will print an error message in that case, too).
668 GNUNET_DISK_file_test (const char *fil)
670 struct stat filestat;
674 rdir = GNUNET_STRINGS_filename_expand (fil);
676 return GNUNET_SYSERR;
678 ret = STAT (rdir, &filestat);
683 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "stat", rdir);
685 return GNUNET_SYSERR;
690 if (!S_ISREG (filestat.st_mode))
695 if (ACCESS (rdir, F_OK) < 0)
697 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "access", rdir);
699 return GNUNET_SYSERR;
707 * Implementation of "mkdir -p"
709 * @param dir the directory to create
710 * @return #GNUNET_OK on success, #GNUNET_SYSERR on failure
713 GNUNET_DISK_directory_create (const char *dir)
721 rdir = GNUNET_STRINGS_filename_expand (dir);
723 return GNUNET_SYSERR;
727 pos = 1; /* skip heading '/' */
729 /* Local or Network path? */
730 if (strncmp (rdir, "\\\\", 2) == 0)
735 if (rdir[pos] == '\\')
745 pos = 3; /* strlen("C:\\") */
748 /* Check which low level directories already exist */
750 rdir[len] = DIR_SEPARATOR;
753 if (DIR_SEPARATOR == rdir[pos2])
756 ret = GNUNET_DISK_directory_test (rdir, GNUNET_NO);
757 if (GNUNET_NO == ret)
760 return GNUNET_SYSERR;
762 rdir[pos2] = DIR_SEPARATOR;
763 if (GNUNET_YES == ret)
774 /* Start creating directories */
777 if ((rdir[pos] == DIR_SEPARATOR) || (pos == len))
780 ret = GNUNET_DISK_directory_test (rdir, GNUNET_NO);
781 if (GNUNET_NO == ret)
784 return GNUNET_SYSERR;
786 if (GNUNET_SYSERR == ret)
789 ret = mkdir (rdir, S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH); /* 755 */
791 wchar_t wrdir[MAX_PATH + 1];
792 if (ERROR_SUCCESS == plibc_conv_to_win_pathwconv(rdir, wrdir))
793 ret = !CreateDirectoryW (wrdir, NULL);
797 if ((ret != 0) && (errno != EEXIST))
799 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_ERROR, "mkdir", rdir);
801 return GNUNET_SYSERR;
804 rdir[pos] = DIR_SEPARATOR;
814 * Create the directory structure for storing a file.
816 * @param filename name of a file in the directory
817 * @returns #GNUNET_OK on success,
818 * #GNUNET_SYSERR on failure,
819 * #GNUNET_NO if the directory
820 * exists but is not writeable for us
823 GNUNET_DISK_directory_create_for_file (const char *filename)
830 rdir = GNUNET_STRINGS_filename_expand (filename);
834 return GNUNET_SYSERR;
836 if (0 == ACCESS (rdir, W_OK))
843 while ((len > 0) && (rdir[len] != DIR_SEPARATOR))
846 /* The empty path is invalid and in this case refers to / */
850 rdir = GNUNET_strdup ("/");
852 ret = GNUNET_DISK_directory_create (rdir);
853 if ((GNUNET_OK == ret) && (0 != ACCESS (rdir, W_OK)))
863 * Read the contents of a binary file into a buffer.
865 * @param h handle to an open file
866 * @param result the buffer to write the result to
867 * @param len the maximum number of bytes to read
868 * @return the number of bytes read on success, #GNUNET_SYSERR on failure
871 GNUNET_DISK_file_read (const struct GNUNET_DISK_FileHandle *h,
878 return GNUNET_SYSERR;
884 if (h->type == GNUNET_DISK_HANLDE_TYPE_FILE)
886 if (!ReadFile (h->h, result, len, &bytes_read, NULL))
888 SetErrnoFromWinError (GetLastError ());
889 return GNUNET_SYSERR;
892 else if (h->type == GNUNET_DISK_HANLDE_TYPE_PIPE)
894 if (!ReadFile (h->h, result, len, &bytes_read, h->oOverlapRead))
896 if (GetLastError () != ERROR_IO_PENDING)
898 LOG (GNUNET_ERROR_TYPE_DEBUG,
899 "Error reading from pipe: %u\n",
901 SetErrnoFromWinError (GetLastError ());
902 return GNUNET_SYSERR;
904 LOG (GNUNET_ERROR_TYPE_DEBUG, "Will get overlapped result\n");
905 GetOverlappedResult (h->h, h->oOverlapRead, &bytes_read, TRUE);
907 LOG (GNUNET_ERROR_TYPE_DEBUG, "Read %u bytes from pipe\n", bytes_read);
915 return read (h->fd, result, len);
921 * Read the contents of a binary file into a buffer.
922 * Guarantees not to block (returns GNUNET_SYSERR and sets errno to EAGAIN
923 * when no data can be read).
925 * @param h handle to an open file
926 * @param result the buffer to write the result to
927 * @param len the maximum number of bytes to read
928 * @return the number of bytes read on success, #GNUNET_SYSERR on failure
931 GNUNET_DISK_file_read_non_blocking (const struct GNUNET_DISK_FileHandle *h,
938 return GNUNET_SYSERR;
944 if (h->type == GNUNET_DISK_HANLDE_TYPE_FILE)
946 if (!ReadFile (h->h, result, len, &bytes_read, NULL))
948 SetErrnoFromWinError (GetLastError ());
949 return GNUNET_SYSERR;
952 else if (h->type == GNUNET_DISK_HANLDE_TYPE_PIPE)
954 if (!ReadFile (h->h, result, len, &bytes_read, h->oOverlapRead))
956 if (GetLastError () != ERROR_IO_PENDING)
958 LOG (GNUNET_ERROR_TYPE_DEBUG, "Error reading from pipe: %u\n", GetLastError ());
959 SetErrnoFromWinError (GetLastError ());
960 return GNUNET_SYSERR;
964 LOG (GNUNET_ERROR_TYPE_DEBUG,
965 "ReadFile() queued a read, cancelling\n");
968 return GNUNET_SYSERR;
971 LOG (GNUNET_ERROR_TYPE_DEBUG,
984 /* set to non-blocking, read, then set back */
985 flags = fcntl (h->fd, F_GETFL);
986 if (0 == (flags & O_NONBLOCK))
987 (void) fcntl (h->fd, F_SETFL, flags | O_NONBLOCK);
988 ret = read (h->fd, result, len);
989 if (0 == (flags & O_NONBLOCK))
992 (void) fcntl (h->fd, F_SETFL, flags);
1001 * Read the contents of a binary file into a buffer.
1003 * @param fn file name
1004 * @param result the buffer to write the result to
1005 * @param len the maximum number of bytes to read
1006 * @return number of bytes read, #GNUNET_SYSERR on failure
1009 GNUNET_DISK_fn_read (const char *fn,
1013 struct GNUNET_DISK_FileHandle *fh;
1017 fh = GNUNET_DISK_file_open (fn,
1018 GNUNET_DISK_OPEN_READ,
1019 GNUNET_DISK_PERM_NONE);
1021 return GNUNET_SYSERR;
1022 ret = GNUNET_DISK_file_read (fh, result, len);
1024 GNUNET_assert (GNUNET_OK == GNUNET_DISK_file_close (fh));
1031 * Write a buffer to a file.
1033 * @param h handle to open file
1034 * @param buffer the data to write
1035 * @param n number of bytes to write
1036 * @return number of bytes written on success, #GNUNET_SYSERR on error
1039 GNUNET_DISK_file_write (const struct GNUNET_DISK_FileHandle * h,
1046 return GNUNET_SYSERR;
1050 DWORD bytes_written;
1052 if (h->type == GNUNET_DISK_HANLDE_TYPE_FILE)
1054 if (!WriteFile (h->h, buffer, n, &bytes_written, NULL))
1056 SetErrnoFromWinError (GetLastError ());
1057 return GNUNET_SYSERR;
1060 else if (h->type == GNUNET_DISK_HANLDE_TYPE_PIPE)
1062 LOG (GNUNET_ERROR_TYPE_DEBUG, "It is a pipe trying to write %u bytes\n", n);
1063 if (!WriteFile (h->h, buffer, n, &bytes_written, h->oOverlapWrite))
1065 if (GetLastError () != ERROR_IO_PENDING)
1067 SetErrnoFromWinError (GetLastError ());
1068 LOG (GNUNET_ERROR_TYPE_DEBUG, "Error writing to pipe: %u\n",
1070 return GNUNET_SYSERR;
1072 LOG (GNUNET_ERROR_TYPE_DEBUG, "Will get overlapped result\n");
1073 if (!GetOverlappedResult (h->h, h->oOverlapWrite, &bytes_written, TRUE))
1075 SetErrnoFromWinError (GetLastError ());
1076 LOG (GNUNET_ERROR_TYPE_DEBUG,
1077 "Error getting overlapped result while writing to pipe: %u\n",
1079 return GNUNET_SYSERR;
1085 if (!GetOverlappedResult (h->h, h->oOverlapWrite, &ovr, TRUE))
1087 LOG (GNUNET_ERROR_TYPE_DEBUG,
1088 "Error getting control overlapped result while writing to pipe: %u\n",
1093 LOG (GNUNET_ERROR_TYPE_DEBUG,
1094 "Wrote %u bytes (ovr says %u), picking the greatest\n",
1095 bytes_written, ovr);
1098 if (bytes_written == 0)
1102 LOG (GNUNET_ERROR_TYPE_DEBUG, "Wrote %u bytes, returning -1 with EAGAIN\n", bytes_written);
1104 return GNUNET_SYSERR;
1107 LOG (GNUNET_ERROR_TYPE_DEBUG, "Wrote %u bytes\n", bytes_written);
1113 return bytes_written;
1115 return write (h->fd, buffer, n);
1121 * Write a buffer to a file, blocking, if necessary.
1123 * @param h handle to open file
1124 * @param buffer the data to write
1125 * @param n number of bytes to write
1126 * @return number of bytes written on success, #GNUNET_SYSERR on error
1129 GNUNET_DISK_file_write_blocking (const struct GNUNET_DISK_FileHandle * h,
1136 return GNUNET_SYSERR;
1140 DWORD bytes_written;
1141 /* We do a non-overlapped write, which is as blocking as it gets */
1142 LOG (GNUNET_ERROR_TYPE_DEBUG, "Writing %u bytes\n", n);
1143 if (!WriteFile (h->h, buffer, n, &bytes_written, NULL))
1145 SetErrnoFromWinError (GetLastError ());
1146 LOG (GNUNET_ERROR_TYPE_DEBUG, "Error writing to pipe: %u\n",
1148 return GNUNET_SYSERR;
1150 if (bytes_written == 0 && n > 0)
1152 LOG (GNUNET_ERROR_TYPE_DEBUG, "Waiting for pipe to clean\n");
1153 WaitForSingleObject (h->h, INFINITE);
1154 if (!WriteFile (h->h, buffer, n, &bytes_written, NULL))
1156 SetErrnoFromWinError (GetLastError ());
1157 LOG (GNUNET_ERROR_TYPE_DEBUG, "Error writing to pipe: %u\n",
1159 return GNUNET_SYSERR;
1162 LOG (GNUNET_ERROR_TYPE_DEBUG,
1165 return bytes_written;
1170 /* set to blocking, write, then set back */
1171 flags = fcntl (h->fd, F_GETFL);
1172 if (0 != (flags & O_NONBLOCK))
1173 (void) fcntl (h->fd, F_SETFL, flags - O_NONBLOCK);
1174 ret = write (h->fd, buffer, n);
1175 if (0 == (flags & O_NONBLOCK))
1176 (void) fcntl (h->fd, F_SETFL, flags);
1183 * Write a buffer to a file. If the file is longer than the
1184 * number of bytes that will be written, it will be truncated.
1186 * @param fn file name
1187 * @param buffer the data to write
1188 * @param n number of bytes to write
1189 * @param mode file permissions
1190 * @return number of bytes written on success, #GNUNET_SYSERR on error
1193 GNUNET_DISK_fn_write (const char *fn,
1196 enum GNUNET_DISK_AccessPermissions mode)
1198 struct GNUNET_DISK_FileHandle *fh;
1201 fh = GNUNET_DISK_file_open (fn,
1202 GNUNET_DISK_OPEN_WRITE | GNUNET_DISK_OPEN_TRUNCATE
1203 | GNUNET_DISK_OPEN_CREATE, mode);
1205 return GNUNET_SYSERR;
1206 ret = GNUNET_DISK_file_write (fh, buffer, n);
1207 GNUNET_assert (GNUNET_OK == GNUNET_DISK_file_close (fh));
1213 * Scan a directory for files.
1215 * @param dir_name the name of the directory
1216 * @param callback the method to call for each file,
1217 * can be NULL, in that case, we only count
1218 * @param callback_cls closure for @a callback
1219 * @return the number of files found, #GNUNET_SYSERR on error or
1220 * ieration aborted by callback returning #GNUNET_SYSERR
1223 GNUNET_DISK_directory_scan (const char *dir_name,
1224 GNUNET_FileNameCallback callback,
1228 struct dirent *finfo;
1234 unsigned int name_len;
1235 unsigned int n_size;
1237 GNUNET_assert (NULL != dir_name);
1238 dname = GNUNET_STRINGS_filename_expand (dir_name);
1240 return GNUNET_SYSERR;
1241 while ( (strlen (dname) > 0) &&
1242 (dname[strlen (dname) - 1] == DIR_SEPARATOR) )
1243 dname[strlen (dname) - 1] = '\0';
1244 if (0 != STAT (dname, &istat))
1246 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING,
1249 GNUNET_free (dname);
1250 return GNUNET_SYSERR;
1252 if (! S_ISDIR (istat.st_mode))
1254 LOG (GNUNET_ERROR_TYPE_WARNING,
1255 _("Expected `%s' to be a directory!\n"),
1257 GNUNET_free (dname);
1258 return GNUNET_SYSERR;
1261 dinfo = OPENDIR (dname);
1262 if ( (EACCES == errno) ||
1265 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING,
1270 GNUNET_free (dname);
1271 return GNUNET_SYSERR;
1274 n_size = strlen (dname) + name_len + strlen (DIR_SEPARATOR_STR) + 1;
1275 name = GNUNET_malloc (n_size);
1276 while (NULL != (finfo = READDIR (dinfo)))
1278 if ( (0 == strcmp (finfo->d_name, ".")) ||
1279 (0 == strcmp (finfo->d_name, "..")) )
1281 if (NULL != callback)
1283 if (name_len < strlen (finfo->d_name))
1286 name_len = strlen (finfo->d_name);
1287 n_size = strlen (dname) + name_len + strlen (DIR_SEPARATOR_STR) + 1;
1288 name = GNUNET_malloc (n_size);
1290 /* dname can end in "/" only if dname == "/";
1291 * if dname does not end in "/", we need to add
1292 * a "/" (otherwise, we must not!) */
1293 GNUNET_snprintf (name,
1297 (0 == strcmp (dname,
1300 : DIR_SEPARATOR_STR,
1302 ret = callback (callback_cls,
1304 if (GNUNET_OK != ret)
1308 GNUNET_free (dname);
1309 if (GNUNET_NO == ret)
1311 return GNUNET_SYSERR;
1318 GNUNET_free (dname);
1324 * Function that removes the given directory by calling
1325 * #GNUNET_DISK_directory_remove().
1327 * @param unused not used
1328 * @param fn directory to remove
1329 * @return #GNUNET_OK
1332 remove_helper (void *unused,
1336 (void) GNUNET_DISK_directory_remove (fn);
1342 * Remove all files in a directory (rm -r). Call with
1345 * @param filename the file to remove
1346 * @return #GNUNET_OK on success, #GNUNET_SYSERR on error
1349 GNUNET_DISK_directory_remove (const char *filename)
1353 if (NULL == filename)
1356 return GNUNET_SYSERR;
1358 if (0 != LSTAT (filename, &istat))
1359 return GNUNET_NO; /* file may not exist... */
1360 (void) CHMOD (filename,
1361 S_IWUSR | S_IRUSR | S_IXUSR);
1362 if (0 == UNLINK (filename))
1364 if ( (errno != EISDIR) &&
1365 /* EISDIR is not sufficient in all cases, e.g.
1366 * sticky /tmp directory may result in EPERM on BSD.
1367 * So we also explicitly check "isDirectory" */
1369 GNUNET_DISK_directory_test (filename,
1372 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING,
1375 return GNUNET_SYSERR;
1377 if (GNUNET_SYSERR ==
1378 GNUNET_DISK_directory_scan (filename,
1381 return GNUNET_SYSERR;
1382 if (0 != RMDIR (filename))
1384 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING,
1387 return GNUNET_SYSERR;
1396 * @param src file to copy
1397 * @param dst destination file name
1398 * @return #GNUNET_OK on success, #GNUNET_SYSERR on error
1401 GNUNET_DISK_file_copy (const char *src,
1409 struct GNUNET_DISK_FileHandle *in;
1410 struct GNUNET_DISK_FileHandle *out;
1413 GNUNET_DISK_file_size (src,
1418 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR,
1421 return GNUNET_SYSERR;
1424 in = GNUNET_DISK_file_open (src,
1425 GNUNET_DISK_OPEN_READ,
1426 GNUNET_DISK_PERM_NONE);
1429 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR,
1432 return GNUNET_SYSERR;
1435 GNUNET_DISK_file_open (dst,
1436 GNUNET_DISK_OPEN_WRITE | GNUNET_DISK_OPEN_CREATE |
1437 GNUNET_DISK_OPEN_FAILIFEXISTS,
1438 GNUNET_DISK_PERM_USER_READ |
1439 GNUNET_DISK_PERM_USER_WRITE |
1440 GNUNET_DISK_PERM_GROUP_READ |
1441 GNUNET_DISK_PERM_GROUP_WRITE);
1444 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR,
1447 GNUNET_DISK_file_close (in);
1448 return GNUNET_SYSERR;
1450 buf = GNUNET_malloc (COPY_BLK_SIZE);
1453 len = COPY_BLK_SIZE;
1454 if (len > size - pos)
1456 sret = GNUNET_DISK_file_read (in,
1460 (len != (size_t) sret) )
1462 sret = GNUNET_DISK_file_write (out,
1466 (len != (size_t) sret) )
1471 GNUNET_DISK_file_close (in);
1472 GNUNET_DISK_file_close (out);
1476 GNUNET_DISK_file_close (in);
1477 GNUNET_DISK_file_close (out);
1478 return GNUNET_SYSERR;
1483 * @brief Removes special characters as ':' from a filename.
1484 * @param fn the filename to canonicalize
1487 GNUNET_DISK_filename_canonicalize (char *fn)
1492 for (idx = fn; *idx; idx++)
1496 if (c == '/' || c == '\\' || c == ':' ||
1497 c == '*' || c == '?' || c == '"' ||
1498 c == '<' || c == '>' || c == '|')
1508 * @brief Change owner of a file
1510 * @param filename name of file to change the owner of
1511 * @param user name of the new owner
1512 * @return #GNUNET_OK on success, #GNUNET_SYSERR on failure
1515 GNUNET_DISK_file_change_owner (const char *filename,
1521 pws = getpwnam (user);
1524 LOG (GNUNET_ERROR_TYPE_ERROR,
1525 _("Cannot obtain information about user `%s': %s\n"),
1528 return GNUNET_SYSERR;
1530 if (0 != chown (filename,
1534 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING,
1537 return GNUNET_SYSERR;
1545 * Lock a part of a file
1547 * @param fh file handle
1548 * @param lock_start absolute position from where to lock
1549 * @param lock_end absolute position until where to lock
1550 * @param excl #GNUNET_YES for an exclusive lock
1551 * @return #GNUNET_OK on success, #GNUNET_SYSERR on error
1554 GNUNET_DISK_file_lock (struct GNUNET_DISK_FileHandle *fh,
1562 return GNUNET_SYSERR;
1568 memset (&fl, 0, sizeof (struct flock));
1569 fl.l_type = excl ? F_WRLCK : F_RDLCK;
1570 fl.l_whence = SEEK_SET;
1571 fl.l_start = lock_start;
1572 fl.l_len = lock_end;
1574 return fcntl (fh->fd, F_SETLK, &fl) != 0 ? GNUNET_SYSERR : GNUNET_OK;
1577 off_t diff = lock_end - lock_start;
1578 DWORD diff_low, diff_high;
1579 diff_low = (DWORD) (diff & 0xFFFFFFFF);
1580 diff_high = (DWORD) ((diff >> (sizeof (DWORD) * 8)) & 0xFFFFFFFF);
1582 memset (&o, 0, sizeof (OVERLAPPED));
1583 o.Offset = (DWORD) (lock_start & 0xFFFFFFFF);;
1584 o.OffsetHigh = (DWORD) (((lock_start & ~0xFFFFFFFF) >> (sizeof (DWORD) * 8)) & 0xFFFFFFFF);
1587 (fh->h, (excl ? LOCKFILE_EXCLUSIVE_LOCK : 0) | LOCKFILE_FAIL_IMMEDIATELY,
1588 0, diff_low, diff_high, &o))
1590 SetErrnoFromWinError (GetLastError ());
1591 return GNUNET_SYSERR;
1600 * Unlock a part of a file
1602 * @param fh file handle
1603 * @param unlock_start absolute position from where to unlock
1604 * @param unlock_end absolute position until where to unlock
1605 * @return #GNUNET_OK on success, #GNUNET_SYSERR on error
1608 GNUNET_DISK_file_unlock (struct GNUNET_DISK_FileHandle *fh,
1615 return GNUNET_SYSERR;
1621 memset (&fl, 0, sizeof (struct flock));
1622 fl.l_type = F_UNLCK;
1623 fl.l_whence = SEEK_SET;
1624 fl.l_start = unlock_start;
1625 fl.l_len = unlock_end;
1627 return fcntl (fh->fd, F_SETLK, &fl) != 0 ? GNUNET_SYSERR : GNUNET_OK;
1630 off_t diff = unlock_end - unlock_start;
1631 DWORD diff_low, diff_high;
1632 diff_low = (DWORD) (diff & 0xFFFFFFFF);
1633 diff_high = (DWORD) ((diff >> (sizeof (DWORD) * 8)) & 0xFFFFFFFF);
1635 memset (&o, 0, sizeof (OVERLAPPED));
1636 o.Offset = (DWORD) (unlock_start & 0xFFFFFFFF);;
1637 o.OffsetHigh = (DWORD) (((unlock_start & ~0xFFFFFFFF) >> (sizeof (DWORD) * 8)) & 0xFFFFFFFF);
1639 if (!UnlockFileEx (fh->h, 0, diff_low, diff_high, &o))
1641 SetErrnoFromWinError (GetLastError ());
1642 return GNUNET_SYSERR;
1651 * Open a file. Note that the access permissions will only be
1652 * used if a new file is created and if the underlying operating
1653 * system supports the given permissions.
1655 * @param fn file name to be opened
1656 * @param flags opening flags, a combination of GNUNET_DISK_OPEN_xxx bit flags
1657 * @param perm permissions for the newly created file, use
1658 * #GNUNET_DISK_PERM_NONE if a file could not be created by this
1659 * call (because of flags)
1660 * @return IO handle on success, NULL on error
1662 struct GNUNET_DISK_FileHandle *
1663 GNUNET_DISK_file_open (const char *fn,
1664 enum GNUNET_DISK_OpenFlags flags,
1665 enum GNUNET_DISK_AccessPermissions perm)
1668 struct GNUNET_DISK_FileHandle *ret;
1674 wchar_t wexpfn[MAX_PATH + 1];
1681 expfn = GNUNET_STRINGS_filename_expand (fn);
1686 if (GNUNET_DISK_OPEN_READWRITE == (flags & GNUNET_DISK_OPEN_READWRITE))
1687 oflags = O_RDWR; /* note: O_RDWR is NOT always O_RDONLY | O_WRONLY */
1688 else if (flags & GNUNET_DISK_OPEN_READ)
1690 else if (flags & GNUNET_DISK_OPEN_WRITE)
1695 GNUNET_free (expfn);
1698 if (flags & GNUNET_DISK_OPEN_FAILIFEXISTS)
1699 oflags |= (O_CREAT | O_EXCL);
1700 if (flags & GNUNET_DISK_OPEN_TRUNCATE)
1702 if (flags & GNUNET_DISK_OPEN_APPEND)
1704 if(GNUNET_NO == GNUNET_DISK_file_test(fn))
1706 if (flags & GNUNET_DISK_OPEN_CREATE )
1708 (void) GNUNET_DISK_directory_create_for_file (expfn);
1710 mode = translate_unix_perms (perm);
1714 fd = open (expfn, oflags
1718 | O_LARGEFILE, mode);
1721 if (0 == (flags & GNUNET_DISK_OPEN_FAILIFEXISTS))
1722 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "open", expfn);
1724 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_DEBUG, "open", expfn);
1725 GNUNET_free (expfn);
1732 if (GNUNET_DISK_OPEN_READWRITE == (flags & GNUNET_DISK_OPEN_READWRITE))
1733 access = FILE_READ_DATA | FILE_WRITE_DATA;
1734 else if (flags & GNUNET_DISK_OPEN_READ)
1735 access = FILE_READ_DATA;
1736 else if (flags & GNUNET_DISK_OPEN_WRITE)
1737 access = FILE_WRITE_DATA;
1739 if (flags & GNUNET_DISK_OPEN_FAILIFEXISTS)
1743 else if (flags & GNUNET_DISK_OPEN_CREATE)
1745 (void) GNUNET_DISK_directory_create_for_file (expfn);
1746 if (flags & GNUNET_DISK_OPEN_TRUNCATE)
1747 disp = CREATE_ALWAYS;
1751 else if (flags & GNUNET_DISK_OPEN_TRUNCATE)
1753 disp = TRUNCATE_EXISTING;
1757 disp = OPEN_EXISTING;
1760 if (ERROR_SUCCESS == plibc_conv_to_win_pathwconv(expfn, wexpfn))
1761 h = CreateFileW (wexpfn, access,
1762 FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE, NULL,
1763 disp, FILE_ATTRIBUTE_NORMAL, NULL);
1765 h = INVALID_HANDLE_VALUE;
1766 if (h == INVALID_HANDLE_VALUE)
1769 SetErrnoFromWinError (GetLastError ());
1771 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_INFO, "open", expfn);
1772 GNUNET_free (expfn);
1777 if (flags & GNUNET_DISK_OPEN_APPEND)
1778 if (SetFilePointer (h, 0, 0, FILE_END) == INVALID_SET_FILE_POINTER)
1780 SetErrnoFromWinError (GetLastError ());
1781 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "SetFilePointer", expfn);
1783 GNUNET_free (expfn);
1788 ret = GNUNET_new (struct GNUNET_DISK_FileHandle);
1791 ret->type = GNUNET_DISK_HANLDE_TYPE_FILE;
1795 GNUNET_free (expfn);
1801 * Close an open file.
1803 * @param h file handle
1804 * @return #GNUNET_OK on success, #GNUNET_SYSERR otherwise
1807 GNUNET_DISK_file_close (struct GNUNET_DISK_FileHandle *h)
1813 return GNUNET_SYSERR;
1819 if (! CloseHandle (h->h))
1821 SetErrnoFromWinError (GetLastError ());
1822 LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING, "close");
1823 ret = GNUNET_SYSERR;
1825 if (h->oOverlapRead)
1827 if (! CloseHandle (h->oOverlapRead->hEvent))
1829 SetErrnoFromWinError (GetLastError ());
1830 LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING, "close");
1831 ret = GNUNET_SYSERR;
1833 GNUNET_free (h->oOverlapRead);
1835 if (h->oOverlapWrite)
1837 if (!CloseHandle (h->oOverlapWrite->hEvent))
1839 SetErrnoFromWinError (GetLastError ());
1840 LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING, "close");
1841 ret = GNUNET_SYSERR;
1843 GNUNET_free (h->oOverlapWrite);
1846 if (close (h->fd) != 0)
1848 LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING, "close");
1849 ret = GNUNET_SYSERR;
1859 * Get a GNUnet file handle from a W32 handle.
1861 * @param handle native handle
1862 * @return GNUnet file handle corresponding to the W32 handle
1864 struct GNUNET_DISK_FileHandle *
1865 GNUNET_DISK_get_handle_from_w32_handle (HANDLE osfh)
1867 struct GNUNET_DISK_FileHandle *fh;
1869 enum GNUNET_FILE_Type ftype;
1871 dwret = GetFileType (osfh);
1874 case FILE_TYPE_DISK:
1875 ftype = GNUNET_DISK_HANLDE_TYPE_FILE;
1877 case FILE_TYPE_PIPE:
1878 ftype = GNUNET_DISK_HANLDE_TYPE_PIPE;
1880 case FILE_TYPE_UNKNOWN:
1881 if ( (GetLastError () == NO_ERROR) ||
1882 (GetLastError () == ERROR_INVALID_HANDLE) )
1884 if (0 != ResetEvent (osfh))
1885 ftype = GNUNET_DISK_HANLDE_TYPE_EVENT;
1896 fh = GNUNET_new (struct GNUNET_DISK_FileHandle);
1900 if (ftype == GNUNET_DISK_HANLDE_TYPE_PIPE)
1903 * Note that we can't make it overlapped if it isn't already.
1904 * (ReOpenFile() is only available in 2003/Vista).
1905 * The process that opened this file in the first place (usually a parent
1906 * process, if this is stdin/stdout/stderr) must make it overlapped,
1907 * otherwise we're screwed, as selecting on non-overlapped handle
1910 fh->oOverlapRead = GNUNET_new (OVERLAPPED);
1911 fh->oOverlapWrite = GNUNET_new (OVERLAPPED);
1912 fh->oOverlapRead->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
1913 fh->oOverlapWrite->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
1921 * Get a handle from a native integer FD.
1923 * @param fno native integer file descriptor
1924 * @return file handle corresponding to the descriptor, NULL on error
1926 struct GNUNET_DISK_FileHandle *
1927 GNUNET_DISK_get_handle_from_int_fd (int fno)
1929 struct GNUNET_DISK_FileHandle *fh;
1931 if ( (((off_t) -1) == lseek (fno, 0, SEEK_CUR)) &&
1933 return NULL; /* invalid FD */
1936 fh = GNUNET_new (struct GNUNET_DISK_FileHandle);
1942 osfh = _get_osfhandle (fno);
1943 if (INVALID_HANDLE_VALUE == (HANDLE) osfh)
1946 fh = GNUNET_DISK_get_handle_from_w32_handle ((HANDLE) osfh);
1954 * Get a handle from a native streaming FD.
1956 * @param fd native streaming file descriptor
1957 * @return file handle corresponding to the descriptor
1959 struct GNUNET_DISK_FileHandle *
1960 GNUNET_DISK_get_handle_from_native (FILE *fd)
1968 return GNUNET_DISK_get_handle_from_int_fd (fno);
1973 * Handle for a memory-mapping operation.
1975 struct GNUNET_DISK_MapHandle
1978 * Address where the map is in memory.
1984 * Underlying OS handle.
1989 * Number of bytes mapped.
1997 #define MAP_FAILED ((void *) -1)
2001 * Map a file into memory
2003 * @param h open file handle
2004 * @param m handle to the new mapping
2005 * @param access access specification, GNUNET_DISK_MAP_TYPE_xxx
2006 * @param len size of the mapping
2007 * @return pointer to the mapped memory region, NULL on failure
2010 GNUNET_DISK_file_map (const struct GNUNET_DISK_FileHandle *h,
2011 struct GNUNET_DISK_MapHandle **m,
2012 enum GNUNET_DISK_MapType access, size_t len)
2021 DWORD mapAccess, protect;
2023 if ((access & GNUNET_DISK_MAP_TYPE_READ) &&
2024 (access & GNUNET_DISK_MAP_TYPE_WRITE))
2026 protect = PAGE_READWRITE;
2027 mapAccess = FILE_MAP_ALL_ACCESS;
2029 else if (access & GNUNET_DISK_MAP_TYPE_READ)
2031 protect = PAGE_READONLY;
2032 mapAccess = FILE_MAP_READ;
2034 else if (access & GNUNET_DISK_MAP_TYPE_WRITE)
2036 protect = PAGE_READWRITE;
2037 mapAccess = FILE_MAP_WRITE;
2045 *m = GNUNET_new (struct GNUNET_DISK_MapHandle);
2046 (*m)->h = CreateFileMapping (h->h, NULL, protect, 0, 0, NULL);
2047 if ((*m)->h == INVALID_HANDLE_VALUE)
2049 SetErrnoFromWinError (GetLastError ());
2054 (*m)->addr = MapViewOfFile ((*m)->h, mapAccess, 0, 0, len);
2057 SetErrnoFromWinError (GetLastError ());
2058 CloseHandle ((*m)->h);
2067 if (access & GNUNET_DISK_MAP_TYPE_READ)
2069 if (access & GNUNET_DISK_MAP_TYPE_WRITE)
2071 *m = GNUNET_new (struct GNUNET_DISK_MapHandle);
2072 (*m)->addr = mmap (NULL, len, prot, MAP_SHARED, h->fd, 0);
2073 GNUNET_assert (NULL != (*m)->addr);
2074 if (MAP_FAILED == (*m)->addr)
2086 * @param h mapping handle
2087 * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
2090 GNUNET_DISK_file_unmap (struct GNUNET_DISK_MapHandle *h)
2097 return GNUNET_SYSERR;
2101 ret = UnmapViewOfFile (h->addr) ? GNUNET_OK : GNUNET_SYSERR;
2102 if (ret != GNUNET_OK)
2103 SetErrnoFromWinError (GetLastError ());
2104 if (!CloseHandle (h->h) && (ret == GNUNET_OK))
2106 ret = GNUNET_SYSERR;
2107 SetErrnoFromWinError (GetLastError ());
2110 ret = munmap (h->addr, h->len) != -1 ? GNUNET_OK : GNUNET_SYSERR;
2118 * Write file changes to disk
2119 * @param h handle to an open file
2120 * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
2123 GNUNET_DISK_file_sync (const struct GNUNET_DISK_FileHandle *h)
2128 return GNUNET_SYSERR;
2134 ret = FlushFileBuffers (h->h) ? GNUNET_OK : GNUNET_SYSERR;
2135 if (ret != GNUNET_OK)
2136 SetErrnoFromWinError (GetLastError ());
2138 #elif defined(FREEBSD) || defined(OPENBSD) || defined(DARWIN)
2139 return fsync (h->fd) == -1 ? GNUNET_SYSERR : GNUNET_OK;
2141 return fdatasync (h->fd) == -1 ? GNUNET_SYSERR : GNUNET_OK;
2148 #define PIPE_BUF 512
2150 /* Copyright Bob Byrnes <byrnes <at> curl.com>
2151 http://permalink.gmane.org/gmane.os.cygwin.patches/2121
2153 /* Create a pipe, and return handles to the read and write ends,
2154 just like CreatePipe, but ensure that the write end permits
2155 FILE_READ_ATTRIBUTES access, on later versions of win32 where
2156 this is supported. This access is needed by NtQueryInformationFile,
2157 which is used to implement select and nonblocking writes.
2158 Note that the return value is either NO_ERROR or GetLastError,
2159 unlike CreatePipe, which returns a bool for success or failure. */
2161 create_selectable_pipe (PHANDLE read_pipe_ptr, PHANDLE write_pipe_ptr,
2162 LPSECURITY_ATTRIBUTES sa_ptr, DWORD psize,
2163 DWORD dwReadMode, DWORD dwWriteMode)
2165 /* Default to error. */
2166 *read_pipe_ptr = *write_pipe_ptr = INVALID_HANDLE_VALUE;
2171 /* Ensure that there is enough pipe buffer space for atomic writes. */
2172 if (psize < PIPE_BUF)
2175 char pipename[MAX_PATH];
2177 /* Retry CreateNamedPipe as long as the pipe name is in use.
2178 * Retrying will probably never be necessary, but we want
2179 * to be as robust as possible. */
2182 static volatile LONG pipe_unique_id;
2184 snprintf (pipename, sizeof pipename, "\\\\.\\pipe\\gnunet-%d-%ld",
2185 getpid (), InterlockedIncrement ((LONG *) & pipe_unique_id));
2186 LOG (GNUNET_ERROR_TYPE_DEBUG, "CreateNamedPipe: name = %s, size = %lu\n",
2188 /* Use CreateNamedPipe instead of CreatePipe, because the latter
2189 * returns a write handle that does not permit FILE_READ_ATTRIBUTES
2190 * access, on versions of win32 earlier than WinXP SP2.
2191 * CreatePipe also stupidly creates a full duplex pipe, which is
2192 * a waste, since only a single direction is actually used.
2193 * It's important to only allow a single instance, to ensure that
2194 * the pipe was not created earlier by some other process, even if
2195 * the pid has been reused. */
2196 read_pipe = CreateNamedPipeA (pipename, PIPE_ACCESS_INBOUND | FILE_FLAG_FIRST_PIPE_INSTANCE | dwReadMode, PIPE_TYPE_BYTE | PIPE_READMODE_BYTE, 1, /* max instances */
2197 psize, /* output buffer size */
2198 psize, /* input buffer size */
2199 NMPWAIT_USE_DEFAULT_WAIT, sa_ptr);
2201 if (read_pipe != INVALID_HANDLE_VALUE)
2203 LOG (GNUNET_ERROR_TYPE_DEBUG, "pipe read handle = %p\n", read_pipe);
2207 DWORD err = GetLastError ();
2211 case ERROR_PIPE_BUSY:
2212 /* The pipe is already open with compatible parameters.
2213 * Pick a new name and retry. */
2214 LOG (GNUNET_ERROR_TYPE_DEBUG, "pipe busy, retrying\n");
2216 case ERROR_ACCESS_DENIED:
2217 /* The pipe is already open with incompatible parameters.
2218 * Pick a new name and retry. */
2219 LOG (GNUNET_ERROR_TYPE_DEBUG, "pipe access denied, retrying\n");
2221 case ERROR_CALL_NOT_IMPLEMENTED:
2222 /* We are on an older Win9x platform without named pipes.
2223 * Return an anonymous pipe as the best approximation. */
2224 LOG (GNUNET_ERROR_TYPE_DEBUG,
2225 "CreateNamedPipe not implemented, resorting to "
2226 "CreatePipe: size = %lu\n", psize);
2227 if (CreatePipe (read_pipe_ptr, write_pipe_ptr, sa_ptr, psize))
2229 LOG (GNUNET_ERROR_TYPE_DEBUG, "pipe read handle = %p, write handle = %p\n",
2234 err = GetLastError ();
2235 LOG (GNUNET_ERROR_TYPE_ERROR, "CreatePipe failed: %d\n", err);
2238 LOG (GNUNET_ERROR_TYPE_ERROR, "CreateNamedPipe failed: %d\n", err);
2243 LOG (GNUNET_ERROR_TYPE_DEBUG, "CreateFile: name = %s\n", pipename);
2245 /* Open the named pipe for writing.
2246 * Be sure to permit FILE_READ_ATTRIBUTES access. */
2247 write_pipe = CreateFileA (pipename, GENERIC_WRITE | FILE_READ_ATTRIBUTES, 0, /* share mode */
2248 sa_ptr, OPEN_EXISTING, dwWriteMode, /* flags and attributes */
2249 0); /* handle to template file */
2251 if (write_pipe == INVALID_HANDLE_VALUE)
2254 DWORD err = GetLastError ();
2256 LOG (GNUNET_ERROR_TYPE_DEBUG, "CreateFile failed: %d\n", err);
2257 CloseHandle (read_pipe);
2260 LOG (GNUNET_ERROR_TYPE_DEBUG, "pipe write handle = %p\n", write_pipe);
2262 *read_pipe_ptr = read_pipe;
2263 *write_pipe_ptr = write_pipe;
2270 * Creates an interprocess channel
2272 * @param blocking_read creates an asynchronous pipe for reading if set to GNUNET_NO
2273 * @param blocking_write creates an asynchronous pipe for writing if set to GNUNET_NO
2274 * @param inherit_read inherit the parent processes stdin (only for windows)
2275 * @param inherit_write inherit the parent processes stdout (only for windows)
2276 * @return handle to the new pipe, NULL on error
2278 struct GNUNET_DISK_PipeHandle *
2279 GNUNET_DISK_pipe (int blocking_read,
2289 (void) inherit_read;
2290 (void) inherit_write;
2295 LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR,
2300 return GNUNET_DISK_pipe_from_fd (blocking_read,
2304 struct GNUNET_DISK_PipeHandle *p;
2309 p = GNUNET_new (struct GNUNET_DISK_PipeHandle);
2310 p->fd[0] = GNUNET_new (struct GNUNET_DISK_FileHandle);
2311 p->fd[1] = GNUNET_new (struct GNUNET_DISK_FileHandle);
2313 /* All pipes are overlapped. If you want them to block - just
2314 * call WriteFile() and ReadFile() with NULL overlapped pointer.
2315 * NOTE: calling with NULL overlapped pointer works only
2316 * for pipes, and doesn't seem to be a documented feature.
2317 * It will NOT work for files, because overlapped files need
2318 * to read offsets from the overlapped structure, regardless.
2319 * Pipes are not seekable, and need no offsets, which is
2320 * probably why it works for them.
2323 create_selectable_pipe (&p->fd[0]->h, &p->fd[1]->h, NULL, 0,
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
2337 (GetCurrentProcess (), p->fd[0]->h, GetCurrentProcess (), &tmp_handle, 0,
2338 inherit_read == GNUNET_YES ? TRUE : FALSE, DUPLICATE_SAME_ACCESS))
2340 SetErrnoFromWinError (GetLastError ());
2342 CloseHandle (p->fd[0]->h);
2343 CloseHandle (p->fd[1]->h);
2344 GNUNET_free (p->fd[0]);
2345 GNUNET_free (p->fd[1]);
2350 CloseHandle (p->fd[0]->h);
2351 p->fd[0]->h = tmp_handle;
2353 if (!DuplicateHandle
2354 (GetCurrentProcess (), p->fd[1]->h, GetCurrentProcess (), &tmp_handle, 0,
2355 inherit_write == GNUNET_YES ? TRUE : FALSE, DUPLICATE_SAME_ACCESS))
2357 SetErrnoFromWinError (GetLastError ());
2359 CloseHandle (p->fd[0]->h);
2360 CloseHandle (p->fd[1]->h);
2361 GNUNET_free (p->fd[0]);
2362 GNUNET_free (p->fd[1]);
2367 CloseHandle (p->fd[1]->h);
2368 p->fd[1]->h = tmp_handle;
2370 p->fd[0]->type = GNUNET_DISK_HANLDE_TYPE_PIPE;
2371 p->fd[1]->type = GNUNET_DISK_HANLDE_TYPE_PIPE;
2373 p->fd[0]->oOverlapRead = GNUNET_new (OVERLAPPED);
2374 p->fd[0]->oOverlapWrite = GNUNET_new (OVERLAPPED);
2375 p->fd[1]->oOverlapRead = GNUNET_new (OVERLAPPED);
2376 p->fd[1]->oOverlapWrite = GNUNET_new (OVERLAPPED);
2378 p->fd[0]->oOverlapRead->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2379 p->fd[0]->oOverlapWrite->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2381 p->fd[1]->oOverlapRead->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2382 p->fd[1]->oOverlapWrite->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2390 * Creates a pipe object from a couple of file descriptors.
2391 * Useful for wrapping existing pipe FDs.
2393 * @param blocking_read creates an asynchronous pipe for reading if set to GNUNET_NO
2394 * @param blocking_write creates an asynchronous pipe for writing if set to GNUNET_NO
2395 * @param fd an array of two fd values. One of them may be -1 for read-only or write-only pipes
2397 * @return handle to the new pipe, NULL on error
2399 struct GNUNET_DISK_PipeHandle *
2400 GNUNET_DISK_pipe_from_fd (int blocking_read, int blocking_write, int fd[2])
2402 struct GNUNET_DISK_PipeHandle *p;
2404 p = GNUNET_new (struct GNUNET_DISK_PipeHandle);
2409 int eno = 0; /* make gcc happy */
2414 p->fd[0] = GNUNET_new (struct GNUNET_DISK_FileHandle);
2415 p->fd[0]->fd = fd[0];
2418 flags = fcntl (fd[0], F_GETFL);
2419 flags |= O_NONBLOCK;
2420 if (0 > fcntl (fd[0], F_SETFL, flags))
2426 flags = fcntl (fd[0], F_GETFD);
2427 flags |= FD_CLOEXEC;
2428 if (0 > fcntl (fd[0], F_SETFD, flags))
2437 p->fd[1] = GNUNET_new (struct GNUNET_DISK_FileHandle);
2438 p->fd[1]->fd = fd[1];
2439 if (!blocking_write)
2441 flags = fcntl (fd[1], F_GETFL);
2442 flags |= O_NONBLOCK;
2443 if (0 > fcntl (fd[1], F_SETFL, flags))
2449 flags = fcntl (fd[1], F_GETFD);
2450 flags |= FD_CLOEXEC;
2451 if (0 > fcntl (fd[1], F_SETFD, flags))
2460 LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR, "fcntl");
2461 if (p->fd[0]->fd >= 0)
2462 GNUNET_break (0 == close (p->fd[0]->fd));
2463 if (p->fd[1]->fd >= 0)
2464 GNUNET_break (0 == close (p->fd[1]->fd));
2465 GNUNET_free_non_null (p->fd[0]);
2466 GNUNET_free_non_null (p->fd[1]);
2474 p->fd[0] = GNUNET_new (struct GNUNET_DISK_FileHandle);
2475 p->fd[0]->h = (HANDLE) _get_osfhandle (fd[0]);
2476 if (p->fd[0]->h != INVALID_HANDLE_VALUE)
2478 p->fd[0]->type = GNUNET_DISK_HANLDE_TYPE_PIPE;
2479 p->fd[0]->oOverlapRead = GNUNET_new (OVERLAPPED);
2480 p->fd[0]->oOverlapWrite = GNUNET_new (OVERLAPPED);
2481 p->fd[0]->oOverlapRead->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2482 p->fd[0]->oOverlapWrite->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2486 GNUNET_free (p->fd[0]);
2492 p->fd[1] = GNUNET_new (struct GNUNET_DISK_FileHandle);
2493 p->fd[1]->h = (HANDLE) _get_osfhandle (fd[1]);
2494 if (p->fd[1]->h != INVALID_HANDLE_VALUE)
2496 p->fd[1]->type = GNUNET_DISK_HANLDE_TYPE_PIPE;
2497 p->fd[1]->oOverlapRead = GNUNET_new (OVERLAPPED);
2498 p->fd[1]->oOverlapWrite = GNUNET_new (OVERLAPPED);
2499 p->fd[1]->oOverlapRead->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2500 p->fd[1]->oOverlapWrite->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2504 GNUNET_free (p->fd[1]);
2515 * Closes an interprocess channel
2517 * @param p pipe to close
2518 * @param end which end of the pipe to close
2519 * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
2522 GNUNET_DISK_pipe_close_end (struct GNUNET_DISK_PipeHandle *p,
2523 enum GNUNET_DISK_PipeEnd end)
2525 int ret = GNUNET_OK;
2527 if (end == GNUNET_DISK_PIPE_END_READ)
2531 ret = GNUNET_DISK_file_close (p->fd[0]);
2535 else if (end == GNUNET_DISK_PIPE_END_WRITE)
2539 ret = GNUNET_DISK_file_close (p->fd[1]);
2548 * Detaches one of the ends from the pipe.
2549 * Detached end is a fully-functional FileHandle, it will
2550 * not be affected by anything you do with the pipe afterwards.
2551 * Each end of a pipe can only be detched from it once (i.e.
2552 * it is not duplicated).
2554 * @param p pipe to detach an end from
2555 * @param end which end of the pipe to detach
2556 * @return Detached end on success, NULL on failure
2557 * (or if that end is not present or is closed).
2559 struct GNUNET_DISK_FileHandle *
2560 GNUNET_DISK_pipe_detach_end (struct GNUNET_DISK_PipeHandle *p,
2561 enum GNUNET_DISK_PipeEnd end)
2563 struct GNUNET_DISK_FileHandle *ret = NULL;
2565 if (end == GNUNET_DISK_PIPE_END_READ)
2573 else if (end == GNUNET_DISK_PIPE_END_WRITE)
2587 * Closes an interprocess channel
2589 * @param p pipe to close
2590 * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
2593 GNUNET_DISK_pipe_close (struct GNUNET_DISK_PipeHandle *p)
2595 int ret = GNUNET_OK;
2598 int write_end_close;
2599 int read_end_close_errno;
2600 int write_end_close_errno;
2602 read_end_close = GNUNET_DISK_pipe_close_end (p, GNUNET_DISK_PIPE_END_READ);
2603 read_end_close_errno = errno;
2604 write_end_close = GNUNET_DISK_pipe_close_end (p, GNUNET_DISK_PIPE_END_WRITE);
2605 write_end_close_errno = errno;
2608 if (GNUNET_OK != read_end_close)
2610 errno = read_end_close_errno;
2611 ret = read_end_close;
2613 else if (GNUNET_OK != write_end_close)
2615 errno = write_end_close_errno;
2616 ret = write_end_close;
2624 * Get the handle to a particular pipe end
2627 * @param n end to access
2628 * @return handle for the respective end
2630 const struct GNUNET_DISK_FileHandle *
2631 GNUNET_DISK_pipe_handle (const struct GNUNET_DISK_PipeHandle *p,
2632 enum GNUNET_DISK_PipeEnd n)
2636 case GNUNET_DISK_PIPE_END_READ:
2637 case GNUNET_DISK_PIPE_END_WRITE:
2647 * Retrieve OS file handle
2649 * @param fh GNUnet file descriptor
2650 * @param dst destination buffer
2651 * @param dst_len length of dst
2652 * @return #GNUNET_OK on success, #GNUNET_SYSERR otherwise
2655 GNUNET_DISK_internal_file_handle_ (const struct GNUNET_DISK_FileHandle *fh,
2656 void *dst, size_t dst_len)
2659 return GNUNET_SYSERR;
2661 if (dst_len < sizeof (HANDLE))
2662 return GNUNET_SYSERR;
2663 *((HANDLE *) dst) = fh->h;
2665 if (dst_len < sizeof (int))
2666 return GNUNET_SYSERR;
2667 *((int *) dst) = fh->fd;
2675 * Helper function for #GNUNET_DISK_purge_cfg_dir.
2677 * @param cls a `const char *` with the option to purge
2678 * @param cfg our configuration
2679 * @return #GNUNET_OK on success
2682 purge_cfg_dir (void *cls,
2683 const struct GNUNET_CONFIGURATION_Handle *cfg)
2685 const char *option = cls;
2689 GNUNET_CONFIGURATION_get_value_filename (cfg,
2694 GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
2699 if (GNUNET_SYSERR ==
2700 GNUNET_DISK_directory_remove (tmpname))
2702 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR,
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,
2724 GNUNET_break (GNUNET_OK ==
2725 GNUNET_CONFIGURATION_parse_and_run (cfg_filename,