2 This file is part of GNUnet.
3 Copyright (C) 2001--2013, 2016, 2018 GNUnet e.V.
5 GNUnet is free software: you can redistribute it and/or modify it
6 under the terms of the GNU Affero General Public License as published
7 by the Free Software Foundation, either version 3 of the License,
8 or (at your option) any later version.
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Affero General Public License for more details.
15 You should have received a copy of the GNU Affero General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
20 * @brief disk IO convenience methods
21 * @author Christian Grothoff
26 #include "gnunet_strings_lib.h"
27 #include "gnunet_disk_lib.h"
29 #define LOG(kind,...) GNUNET_log_from (kind, "util-disk", __VA_ARGS__)
31 #define LOG_STRERROR(kind,syscall) GNUNET_log_from_strerror (kind, "util-disk", syscall)
33 #define LOG_STRERROR_FILE(kind,syscall,filename) GNUNET_log_from_strerror_file (kind, "util-disk", syscall, filename)
36 * Block size for IO for copying files.
38 #define COPY_BLK_SIZE 65536
40 #include <sys/types.h>
45 #include <sys/param.h>
48 #include <sys/mount.h>
50 #if HAVE_SYS_STATVFS_H
51 #include <sys/statvfs.h>
55 #define _IFMT 0170000 /* type of file */
56 #define _IFLNK 0120000 /* symbolic link */
57 #define S_ISLNK(m) (((m)&_IFMT) == _IFLNK)
62 * Handle used to manage a pipe.
64 struct GNUNET_DISK_PipeHandle
67 * File descriptors for the pipe.
68 * One or both of them could be NULL.
70 struct GNUNET_DISK_FileHandle *fd[2];
75 * Closure for the recursion to determine the file size
78 struct GetFileSizeData
81 * Set to the total file size.
86 * GNUNET_YES if symbolic links should be included.
88 int include_sym_links;
91 * GNUNET_YES if mode is file-only (return total == -1 for directories).
99 * Translate GNUnet-internal permission bitmap to UNIX file
100 * access permission bitmap.
102 * @param perm file permissions, GNUnet style
103 * @return file permissions, UNIX style
106 translate_unix_perms (enum GNUNET_DISK_AccessPermissions perm)
111 if (perm & GNUNET_DISK_PERM_USER_READ)
113 if (perm & GNUNET_DISK_PERM_USER_WRITE)
115 if (perm & GNUNET_DISK_PERM_USER_EXEC)
117 if (perm & GNUNET_DISK_PERM_GROUP_READ)
119 if (perm & GNUNET_DISK_PERM_GROUP_WRITE)
121 if (perm & GNUNET_DISK_PERM_GROUP_EXEC)
123 if (perm & GNUNET_DISK_PERM_OTHER_READ)
125 if (perm & GNUNET_DISK_PERM_OTHER_WRITE)
127 if (perm & GNUNET_DISK_PERM_OTHER_EXEC)
136 * Iterate over all files in the given directory and
137 * accumulate their size.
139 * @param cls closure of type `struct GetFileSizeData`
140 * @param fn current filename we are looking at
141 * @return #GNUNET_SYSERR on serious errors, otherwise #GNUNET_OK
144 getSizeRec (void *cls, const char *fn)
146 struct GetFileSizeData *gfsd = cls;
148 #if defined (HAVE_STAT64) && !(defined (_FILE_OFFSET_BITS) && _FILE_OFFSET_BITS == 64)
151 if (0 != STAT64 (fn, &buf))
153 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_DEBUG, "stat64", fn);
154 return GNUNET_SYSERR;
159 if (0 != STAT (fn, &buf))
161 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_DEBUG, "stat", fn);
162 return GNUNET_SYSERR;
165 if ((S_ISDIR (buf.st_mode)) && (gfsd->single_file_mode == GNUNET_YES))
168 return GNUNET_SYSERR;
170 if ((!S_ISLNK (buf.st_mode)) || (gfsd->include_sym_links == GNUNET_YES))
171 gfsd->total += buf.st_size;
172 if ((S_ISDIR (buf.st_mode)) && (0 == ACCESS (fn, X_OK)) &&
173 ((!S_ISLNK (buf.st_mode)) || (gfsd->include_sym_links == GNUNET_YES)))
175 if (GNUNET_SYSERR == GNUNET_DISK_directory_scan (fn, &getSizeRec, gfsd))
176 return GNUNET_SYSERR;
183 * Checks whether a handle is invalid
185 * @param h handle to check
186 * @return #GNUNET_YES if invalid, #GNUNET_NO if valid
189 GNUNET_DISK_handle_invalid (const struct GNUNET_DISK_FileHandle *h)
192 return ((!h) || (h->h == INVALID_HANDLE_VALUE)) ? GNUNET_YES : GNUNET_NO;
194 return ((!h) || (h->fd == -1)) ? GNUNET_YES : GNUNET_NO;
199 * Get the size of an open file.
201 * @param fh open file handle
202 * @param size where to write size of the file
203 * @return #GNUNET_OK on success, #GNUNET_SYSERR on error
206 GNUNET_DISK_file_handle_size (struct GNUNET_DISK_FileHandle *fh,
212 b = GetFileSizeEx (fh->h, &li);
215 SetErrnoFromWinError (GetLastError ());
216 return GNUNET_SYSERR;
218 *size = (off_t) li.QuadPart;
222 if (0 != FSTAT (fh->fd, &sbuf))
223 return GNUNET_SYSERR;
224 *size = sbuf.st_size;
231 * Move the read/write pointer in a file
233 * @param h handle of an open file
234 * @param offset position to move to
235 * @param whence specification to which position the offset parameter relates to
236 * @return the new position on success, #GNUNET_SYSERR otherwise
239 GNUNET_DISK_file_seek (const struct GNUNET_DISK_FileHandle * h, off_t offset,
240 enum GNUNET_DISK_Seek whence)
245 return GNUNET_SYSERR;
250 LARGE_INTEGER new_pos;
253 static DWORD t[] = { FILE_BEGIN, FILE_CURRENT, FILE_END };
254 li.QuadPart = offset;
256 b = SetFilePointerEx (h->h, li, &new_pos, t[whence]);
259 SetErrnoFromWinError (GetLastError ());
260 return GNUNET_SYSERR;
262 return (off_t) new_pos.QuadPart;
264 static int t[] = { SEEK_SET, SEEK_CUR, SEEK_END };
266 return lseek (h->fd, offset, t[whence]);
272 * Get the size of the file (or directory) of the given file (in
275 * @param filename name of the file or directory
276 * @param size set to the size of the file (or,
277 * in the case of directories, the sum
278 * of all sizes of files in the directory)
279 * @param include_symbolic_links should symbolic links be
281 * @param single_file_mode #GNUNET_YES to only get size of one file
282 * and return #GNUNET_SYSERR for directories.
283 * @return #GNUNET_SYSERR on error, #GNUNET_OK on success
286 GNUNET_DISK_file_size (const char *filename,
288 int include_symbolic_links,
289 int single_file_mode)
291 struct GetFileSizeData gfsd;
294 GNUNET_assert (size != NULL);
296 gfsd.include_sym_links = include_symbolic_links;
297 gfsd.single_file_mode = single_file_mode;
298 ret = getSizeRec (&gfsd, filename);
305 * Obtain some unique identifiers for the given file
306 * that can be used to identify it in the local system.
307 * This function is used between GNUnet processes to
308 * quickly check if two files with the same absolute path
309 * are actually identical. The two processes represent
310 * the same peer but may communicate over the network
311 * (and the file may be on an NFS volume). This function
312 * may not be supported on all operating systems.
314 * @param filename name of the file
315 * @param dev set to the device ID
316 * @param ino set to the inode ID
317 * @return #GNUNET_OK on success
320 GNUNET_DISK_file_get_identifiers (const char *filename, uint64_t * dev,
325 // FIXME NILS: test this
326 struct GNUNET_DISK_FileHandle *fh;
327 BY_HANDLE_FILE_INFORMATION info;
330 fh = GNUNET_DISK_file_open (filename,
331 GNUNET_DISK_OPEN_READ,
332 GNUNET_DISK_PERM_NONE);
334 return GNUNET_SYSERR;
335 succ = GetFileInformationByHandle (fh->h, &info);
336 GNUNET_DISK_file_close (fh);
339 return GNUNET_SYSERR;
341 *dev = info.dwVolumeSerialNumber;
342 *ino = ((((uint64_t) info.nFileIndexHigh) << (sizeof (DWORD) * 8)) | info.nFileIndexLow);
349 if (0 != stat (filename, &sbuf))
351 return GNUNET_SYSERR;
353 *ino = (uint64_t) sbuf.st_ino;
362 if (0 != statvfs (filename, &fbuf))
364 return GNUNET_SYSERR;
366 *dev = (uint64_t) fbuf.f_fsid;
372 if (0 != statfs (filename, &fbuf))
374 return GNUNET_SYSERR;
376 *dev = ((uint64_t) fbuf.f_fsid.val[0]) << 32 ||
377 ((uint64_t) fbuf.f_fsid.val[1]);
382 #endif /* !WINDOWS */
388 * Create the name for a temporary file or directory from a template.
390 * @param t template (without XXXXX or "/tmp/")
391 * @return name ready for passing to 'mktemp' or 'mkdtemp', NULL on error
394 mktemp_name (const char *t)
400 if ((t[0] != '/') && (t[0] != '\\')
402 && !(isalpha ((int) t[0]) && (t[0] != '\0') && (t[1] == ':'))
406 /* FIXME: This uses system codepage on W32, not UTF-8 */
407 tmpdir = getenv ("TMPDIR");
409 tmpdir = getenv ("TMP");
411 tmpdir = getenv ("TEMP");
414 GNUNET_asprintf (&tmpl, "%s/%s%s", tmpdir, t, "XXXXXX");
418 GNUNET_asprintf (&tmpl, "%s%s", t, "XXXXXX");
421 fn = (char *) GNUNET_malloc (MAX_PATH + 1);
422 if (ERROR_SUCCESS != plibc_conv_to_win_path (tmpl, fn))
445 tfn = GNUNET_strdup (fn);
446 random_fn = _mktemp (tfn);
447 if (NULL == random_fn)
452 /* FIXME: assume fn to be UTF-8-encoded and do the right thing */
453 if (0 == CreateDirectoryA (tfn, NULL))
455 DWORD error = GetLastError ();
457 if (ERROR_ALREADY_EXISTS == error)
468 * Update POSIX permissions mask of a file on disk. If both argumets
469 * are #GNUNET_NO, the file is made world-read-write-executable (777).
470 * Does nothing on W32.
472 * @param fn name of the file to update
473 * @param require_uid_match #GNUNET_YES means 700
474 * @param require_gid_match #GNUNET_YES means 770 unless @a require_uid_match is set
477 GNUNET_DISK_fix_permissions (const char *fn,
478 int require_uid_match,
479 int require_gid_match)
487 * Update POSIX permissions mask of a file on disk. If both argumets
488 * are #GNUNET_NO, the file is made world-read-write-executable (777).
490 * @param fn name of the file to update
491 * @param require_uid_match #GNUNET_YES means 700
492 * @param require_gid_match #GNUNET_YES means 770 unless @a require_uid_match is set
495 GNUNET_DISK_fix_permissions (const char *fn,
496 int require_uid_match,
497 int require_gid_match)
501 if (GNUNET_YES == require_uid_match)
502 mode = S_IRUSR | S_IWUSR | S_IXUSR;
503 else if (GNUNET_YES == require_gid_match)
504 mode = S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IWGRP | S_IXGRP;
506 mode = S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IWGRP | S_IXGRP | S_IROTH | S_IWOTH | S_IXOTH;
507 if (0 != chmod (fn, mode))
508 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING,
516 * Create an (empty) temporary directory on disk. If the given name is not
517 * an absolute path, the current 'TMPDIR' will be prepended. In any case,
518 * 6 random characters will be appended to the name to create a unique
521 * @param t component to use for the name;
522 * does NOT contain "XXXXXX" or "/tmp/".
523 * @return NULL on error, otherwise name of fresh
524 * file on disk in directory for temporary files
527 GNUNET_DISK_mkdtemp (const char *t)
532 omask = umask (S_IWGRP | S_IWOTH | S_IRGRP | S_IROTH);
533 fn = mktemp_name (t);
534 if (fn != mkdtemp (fn))
536 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_ERROR, "mkdtemp", fn);
547 * Move a file out of the way (create a backup) by
548 * renaming it to "orig.NUM~" where NUM is the smallest
549 * number that is not used yet.
551 * @param fil name of the file to back up
554 GNUNET_DISK_file_backup (const char *fil)
560 slen = strlen (fil) + 20;
561 target = GNUNET_malloc (slen);
565 GNUNET_snprintf (target, slen,
569 } while (0 == access (target, F_OK));
570 if (0 != rename (fil, target))
571 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR,
574 GNUNET_free (target);
579 * Create an (empty) temporary file on disk. If the given name is not
580 * an absolute path, the current 'TMPDIR' will be prepended. In any case,
581 * 6 random characters will be appended to the name to create a unique
584 * @param t component to use for the name;
585 * does NOT contain "XXXXXX" or "/tmp/".
586 * @return NULL on error, otherwise name of fresh
587 * file on disk in directory for temporary files
590 GNUNET_DISK_mktemp (const char *t)
596 omask = umask (S_IWGRP | S_IWOTH | S_IRGRP | S_IROTH);
597 fn = mktemp_name (t);
598 if (-1 == (fd = mkstemp (fn)))
600 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_ERROR, "mkstemp", fn);
607 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "close", fn);
614 * Test if @a fil is a directory and listable. Optionally, also check if the
615 * directory is readable. Will not print an error message if the directory does
616 * not exist. Will log errors if #GNUNET_SYSERR is returned (i.e., a file exists
617 * with the same name).
619 * @param fil filename to test
620 * @param is_readable #GNUNET_YES to additionally check if @a fil is readable;
621 * #GNUNET_NO to disable this check
622 * @return #GNUNET_YES if yes, #GNUNET_NO if not; #GNUNET_SYSERR if it
623 * does not exist or stat'ed
626 GNUNET_DISK_directory_test (const char *fil, int is_readable)
628 struct stat filestat;
631 ret = STAT (fil, &filestat);
635 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "stat", fil);
636 return GNUNET_SYSERR;
638 if (!S_ISDIR (filestat.st_mode))
640 LOG (GNUNET_ERROR_TYPE_DEBUG,
641 "A file already exits with the same name %s\n", fil);
644 if (GNUNET_YES == is_readable)
645 ret = ACCESS (fil, R_OK | X_OK);
647 ret = ACCESS (fil, X_OK);
650 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "access", fil);
658 * Check that fil corresponds to a filename
659 * (of a file that exists and that is not a directory).
661 * @param fil filename to check
662 * @return #GNUNET_YES if yes, #GNUNET_NO if not a file, #GNUNET_SYSERR if something
663 * else (will print an error message in that case, too).
666 GNUNET_DISK_file_test (const char *fil)
668 struct stat filestat;
672 rdir = GNUNET_STRINGS_filename_expand (fil);
674 return GNUNET_SYSERR;
676 ret = STAT (rdir, &filestat);
681 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "stat", rdir);
683 return GNUNET_SYSERR;
688 if (!S_ISREG (filestat.st_mode))
693 if (ACCESS (rdir, F_OK) < 0)
695 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "access", rdir);
697 return GNUNET_SYSERR;
705 * Implementation of "mkdir -p"
707 * @param dir the directory to create
708 * @return #GNUNET_OK on success, #GNUNET_SYSERR on failure
711 GNUNET_DISK_directory_create (const char *dir)
719 rdir = GNUNET_STRINGS_filename_expand (dir);
721 return GNUNET_SYSERR;
725 pos = 1; /* skip heading '/' */
727 /* Local or Network path? */
728 if (strncmp (rdir, "\\\\", 2) == 0)
733 if (rdir[pos] == '\\')
743 pos = 3; /* strlen("C:\\") */
746 /* Check which low level directories already exist */
748 rdir[len] = DIR_SEPARATOR;
751 if (DIR_SEPARATOR == rdir[pos2])
754 ret = GNUNET_DISK_directory_test (rdir, GNUNET_NO);
755 if (GNUNET_NO == ret)
758 return GNUNET_SYSERR;
760 rdir[pos2] = DIR_SEPARATOR;
761 if (GNUNET_YES == ret)
772 /* Start creating directories */
775 if ((rdir[pos] == DIR_SEPARATOR) || (pos == len))
778 ret = GNUNET_DISK_directory_test (rdir, GNUNET_NO);
779 if (GNUNET_NO == ret)
782 return GNUNET_SYSERR;
784 if (GNUNET_SYSERR == ret)
787 ret = mkdir (rdir, S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH); /* 755 */
789 wchar_t wrdir[MAX_PATH + 1];
790 if (ERROR_SUCCESS == plibc_conv_to_win_pathwconv(rdir, wrdir))
791 ret = !CreateDirectoryW (wrdir, NULL);
795 if ((ret != 0) && (errno != EEXIST))
797 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_ERROR, "mkdir", rdir);
799 return GNUNET_SYSERR;
802 rdir[pos] = DIR_SEPARATOR;
812 * Create the directory structure for storing a file.
814 * @param filename name of a file in the directory
815 * @returns #GNUNET_OK on success,
816 * #GNUNET_SYSERR on failure,
817 * #GNUNET_NO if the directory
818 * exists but is not writeable for us
821 GNUNET_DISK_directory_create_for_file (const char *filename)
828 rdir = GNUNET_STRINGS_filename_expand (filename);
832 return GNUNET_SYSERR;
834 if (0 == ACCESS (rdir, W_OK))
841 while ((len > 0) && (rdir[len] != DIR_SEPARATOR))
844 /* The empty path is invalid and in this case refers to / */
848 rdir = GNUNET_strdup ("/");
850 ret = GNUNET_DISK_directory_create (rdir);
851 if ((GNUNET_OK == ret) && (0 != ACCESS (rdir, W_OK)))
861 * Read the contents of a binary file into a buffer.
863 * @param h handle to an open file
864 * @param result the buffer to write the result to
865 * @param len the maximum number of bytes to read
866 * @return the number of bytes read on success, #GNUNET_SYSERR on failure
869 GNUNET_DISK_file_read (const struct GNUNET_DISK_FileHandle *h,
876 return GNUNET_SYSERR;
882 if (h->type == GNUNET_DISK_HANLDE_TYPE_FILE)
884 if (!ReadFile (h->h, result, len, &bytes_read, NULL))
886 SetErrnoFromWinError (GetLastError ());
887 return GNUNET_SYSERR;
890 else if (h->type == GNUNET_DISK_HANLDE_TYPE_PIPE)
892 if (!ReadFile (h->h, result, len, &bytes_read, h->oOverlapRead))
894 if (GetLastError () != ERROR_IO_PENDING)
896 LOG (GNUNET_ERROR_TYPE_DEBUG,
897 "Error reading from pipe: %u\n",
899 SetErrnoFromWinError (GetLastError ());
900 return GNUNET_SYSERR;
902 LOG (GNUNET_ERROR_TYPE_DEBUG, "Will get overlapped result\n");
903 GetOverlappedResult (h->h, h->oOverlapRead, &bytes_read, TRUE);
905 LOG (GNUNET_ERROR_TYPE_DEBUG, "Read %u bytes from pipe\n", bytes_read);
913 return read (h->fd, result, len);
919 * Read the contents of a binary file into a buffer.
920 * Guarantees not to block (returns GNUNET_SYSERR and sets errno to EAGAIN
921 * when no data can be read).
923 * @param h handle to an open file
924 * @param result the buffer to write the result to
925 * @param len the maximum number of bytes to read
926 * @return the number of bytes read on success, #GNUNET_SYSERR on failure
929 GNUNET_DISK_file_read_non_blocking (const struct GNUNET_DISK_FileHandle *h,
936 return GNUNET_SYSERR;
942 if (h->type == GNUNET_DISK_HANLDE_TYPE_FILE)
944 if (!ReadFile (h->h, result, len, &bytes_read, NULL))
946 SetErrnoFromWinError (GetLastError ());
947 return GNUNET_SYSERR;
950 else if (h->type == GNUNET_DISK_HANLDE_TYPE_PIPE)
952 if (!ReadFile (h->h, result, len, &bytes_read, h->oOverlapRead))
954 if (GetLastError () != ERROR_IO_PENDING)
956 LOG (GNUNET_ERROR_TYPE_DEBUG, "Error reading from pipe: %u\n", GetLastError ());
957 SetErrnoFromWinError (GetLastError ());
958 return GNUNET_SYSERR;
962 LOG (GNUNET_ERROR_TYPE_DEBUG,
963 "ReadFile() queued a read, cancelling\n");
966 return GNUNET_SYSERR;
969 LOG (GNUNET_ERROR_TYPE_DEBUG,
982 /* set to non-blocking, read, then set back */
983 flags = fcntl (h->fd, F_GETFL);
984 if (0 == (flags & O_NONBLOCK))
985 (void) fcntl (h->fd, F_SETFL, flags | O_NONBLOCK);
986 ret = read (h->fd, result, len);
987 if (0 == (flags & O_NONBLOCK))
990 (void) fcntl (h->fd, F_SETFL, flags);
999 * Read the contents of a binary file into a buffer.
1001 * @param fn file name
1002 * @param result the buffer to write the result to
1003 * @param len the maximum number of bytes to read
1004 * @return number of bytes read, #GNUNET_SYSERR on failure
1007 GNUNET_DISK_fn_read (const char *fn,
1011 struct GNUNET_DISK_FileHandle *fh;
1015 fh = GNUNET_DISK_file_open (fn,
1016 GNUNET_DISK_OPEN_READ,
1017 GNUNET_DISK_PERM_NONE);
1019 return GNUNET_SYSERR;
1020 ret = GNUNET_DISK_file_read (fh, result, len);
1022 GNUNET_assert (GNUNET_OK == GNUNET_DISK_file_close (fh));
1029 * Write a buffer to a file.
1031 * @param h handle to open file
1032 * @param buffer the data to write
1033 * @param n number of bytes to write
1034 * @return number of bytes written on success, #GNUNET_SYSERR on error
1037 GNUNET_DISK_file_write (const struct GNUNET_DISK_FileHandle * h,
1038 const void *buffer, size_t n)
1043 return GNUNET_SYSERR;
1047 DWORD bytes_written;
1049 if (h->type == GNUNET_DISK_HANLDE_TYPE_FILE)
1051 if (!WriteFile (h->h, buffer, n, &bytes_written, NULL))
1053 SetErrnoFromWinError (GetLastError ());
1054 return GNUNET_SYSERR;
1057 else if (h->type == GNUNET_DISK_HANLDE_TYPE_PIPE)
1059 LOG (GNUNET_ERROR_TYPE_DEBUG, "It is a pipe trying to write %u bytes\n", n);
1060 if (!WriteFile (h->h, buffer, n, &bytes_written, h->oOverlapWrite))
1062 if (GetLastError () != ERROR_IO_PENDING)
1064 SetErrnoFromWinError (GetLastError ());
1065 LOG (GNUNET_ERROR_TYPE_DEBUG, "Error writing to pipe: %u\n",
1067 return GNUNET_SYSERR;
1069 LOG (GNUNET_ERROR_TYPE_DEBUG, "Will get overlapped result\n");
1070 if (!GetOverlappedResult (h->h, h->oOverlapWrite, &bytes_written, TRUE))
1072 SetErrnoFromWinError (GetLastError ());
1073 LOG (GNUNET_ERROR_TYPE_DEBUG,
1074 "Error getting overlapped result while writing to pipe: %u\n",
1076 return GNUNET_SYSERR;
1082 if (!GetOverlappedResult (h->h, h->oOverlapWrite, &ovr, TRUE))
1084 LOG (GNUNET_ERROR_TYPE_DEBUG,
1085 "Error getting control overlapped result while writing to pipe: %u\n",
1090 LOG (GNUNET_ERROR_TYPE_DEBUG,
1091 "Wrote %u bytes (ovr says %u), picking the greatest\n",
1092 bytes_written, ovr);
1095 if (bytes_written == 0)
1099 LOG (GNUNET_ERROR_TYPE_DEBUG, "Wrote %u bytes, returning -1 with EAGAIN\n", bytes_written);
1101 return GNUNET_SYSERR;
1104 LOG (GNUNET_ERROR_TYPE_DEBUG, "Wrote %u bytes\n", bytes_written);
1110 return bytes_written;
1112 return write (h->fd, buffer, n);
1118 * Write a buffer to a file, blocking, if necessary.
1120 * @param h handle to open file
1121 * @param buffer the data to write
1122 * @param n number of bytes to write
1123 * @return number of bytes written on success, #GNUNET_SYSERR on error
1126 GNUNET_DISK_file_write_blocking (const struct GNUNET_DISK_FileHandle * h,
1133 return GNUNET_SYSERR;
1137 DWORD bytes_written;
1138 /* We do a non-overlapped write, which is as blocking as it gets */
1139 LOG (GNUNET_ERROR_TYPE_DEBUG, "Writing %u bytes\n", n);
1140 if (!WriteFile (h->h, buffer, n, &bytes_written, NULL))
1142 SetErrnoFromWinError (GetLastError ());
1143 LOG (GNUNET_ERROR_TYPE_DEBUG, "Error writing to pipe: %u\n",
1145 return GNUNET_SYSERR;
1147 if (bytes_written == 0 && n > 0)
1149 LOG (GNUNET_ERROR_TYPE_DEBUG, "Waiting for pipe to clean\n");
1150 WaitForSingleObject (h->h, INFINITE);
1151 if (!WriteFile (h->h, buffer, n, &bytes_written, NULL))
1153 SetErrnoFromWinError (GetLastError ());
1154 LOG (GNUNET_ERROR_TYPE_DEBUG, "Error writing to pipe: %u\n",
1156 return GNUNET_SYSERR;
1159 LOG (GNUNET_ERROR_TYPE_DEBUG,
1162 return bytes_written;
1167 /* set to blocking, write, then set back */
1168 flags = fcntl (h->fd, F_GETFL);
1169 if (0 != (flags & O_NONBLOCK))
1170 (void) fcntl (h->fd, F_SETFL, flags - O_NONBLOCK);
1171 ret = write (h->fd, buffer, n);
1172 if (0 == (flags & O_NONBLOCK))
1173 (void) fcntl (h->fd, F_SETFL, flags);
1180 * Write a buffer to a file. If the file is longer than the
1181 * number of bytes that will be written, it will be truncated.
1183 * @param fn file name
1184 * @param buffer the data to write
1185 * @param n number of bytes to write
1186 * @param mode file permissions
1187 * @return number of bytes written on success, #GNUNET_SYSERR on error
1190 GNUNET_DISK_fn_write (const char *fn,
1193 enum GNUNET_DISK_AccessPermissions mode)
1195 struct GNUNET_DISK_FileHandle *fh;
1198 fh = GNUNET_DISK_file_open (fn,
1199 GNUNET_DISK_OPEN_WRITE | GNUNET_DISK_OPEN_TRUNCATE
1200 | GNUNET_DISK_OPEN_CREATE, mode);
1202 return GNUNET_SYSERR;
1203 ret = GNUNET_DISK_file_write (fh, buffer, n);
1204 GNUNET_assert (GNUNET_OK == GNUNET_DISK_file_close (fh));
1210 * Scan a directory for files.
1212 * @param dir_name the name of the directory
1213 * @param callback the method to call for each file,
1214 * can be NULL, in that case, we only count
1215 * @param callback_cls closure for @a callback
1216 * @return the number of files found, #GNUNET_SYSERR on error or
1217 * ieration aborted by callback returning #GNUNET_SYSERR
1220 GNUNET_DISK_directory_scan (const char *dir_name,
1221 GNUNET_FileNameCallback callback,
1225 struct dirent *finfo;
1231 unsigned int name_len;
1232 unsigned int n_size;
1234 GNUNET_assert (NULL != dir_name);
1235 dname = GNUNET_STRINGS_filename_expand (dir_name);
1237 return GNUNET_SYSERR;
1238 while ( (strlen (dname) > 0) &&
1239 (dname[strlen (dname) - 1] == DIR_SEPARATOR) )
1240 dname[strlen (dname) - 1] = '\0';
1241 if (0 != STAT (dname, &istat))
1243 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING,
1246 GNUNET_free (dname);
1247 return GNUNET_SYSERR;
1249 if (! S_ISDIR (istat.st_mode))
1251 LOG (GNUNET_ERROR_TYPE_WARNING,
1252 _("Expected `%s' to be a directory!\n"),
1254 GNUNET_free (dname);
1255 return GNUNET_SYSERR;
1258 dinfo = OPENDIR (dname);
1259 if ( (EACCES == errno) ||
1262 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING,
1267 GNUNET_free (dname);
1268 return GNUNET_SYSERR;
1271 n_size = strlen (dname) + name_len + strlen (DIR_SEPARATOR_STR) + 1;
1272 name = GNUNET_malloc (n_size);
1273 while (NULL != (finfo = READDIR (dinfo)))
1275 if ( (0 == strcmp (finfo->d_name, ".")) ||
1276 (0 == strcmp (finfo->d_name, "..")) )
1278 if (NULL != callback)
1280 if (name_len < strlen (finfo->d_name))
1283 name_len = strlen (finfo->d_name);
1284 n_size = strlen (dname) + name_len + strlen (DIR_SEPARATOR_STR) + 1;
1285 name = GNUNET_malloc (n_size);
1287 /* dname can end in "/" only if dname == "/";
1288 * if dname does not end in "/", we need to add
1289 * a "/" (otherwise, we must not!) */
1290 GNUNET_snprintf (name,
1294 (0 == strcmp (dname,
1297 : DIR_SEPARATOR_STR,
1299 ret = callback (callback_cls,
1301 if (GNUNET_OK != ret)
1305 GNUNET_free (dname);
1306 if (GNUNET_NO == ret)
1308 return GNUNET_SYSERR;
1315 GNUNET_free (dname);
1321 * Function that removes the given directory by calling
1322 * #GNUNET_DISK_directory_remove().
1324 * @param unused not used
1325 * @param fn directory to remove
1326 * @return #GNUNET_OK
1329 remove_helper (void *unused,
1333 (void) GNUNET_DISK_directory_remove (fn);
1339 * Remove all files in a directory (rm -r). Call with
1342 * @param filename the file to remove
1343 * @return #GNUNET_OK on success, #GNUNET_SYSERR on error
1346 GNUNET_DISK_directory_remove (const char *filename)
1350 if (NULL == filename)
1353 return GNUNET_SYSERR;
1355 if (0 != LSTAT (filename, &istat))
1356 return GNUNET_NO; /* file may not exist... */
1357 (void) CHMOD (filename,
1358 S_IWUSR | S_IRUSR | S_IXUSR);
1359 if (0 == UNLINK (filename))
1361 if ( (errno != EISDIR) &&
1362 /* EISDIR is not sufficient in all cases, e.g.
1363 * sticky /tmp directory may result in EPERM on BSD.
1364 * So we also explicitly check "isDirectory" */
1366 GNUNET_DISK_directory_test (filename,
1369 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING,
1372 return GNUNET_SYSERR;
1374 if (GNUNET_SYSERR ==
1375 GNUNET_DISK_directory_scan (filename,
1378 return GNUNET_SYSERR;
1379 if (0 != RMDIR (filename))
1381 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING,
1384 return GNUNET_SYSERR;
1393 * @param src file to copy
1394 * @param dst destination file name
1395 * @return #GNUNET_OK on success, #GNUNET_SYSERR on error
1398 GNUNET_DISK_file_copy (const char *src,
1406 struct GNUNET_DISK_FileHandle *in;
1407 struct GNUNET_DISK_FileHandle *out;
1410 GNUNET_DISK_file_size (src,
1415 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR,
1418 return GNUNET_SYSERR;
1421 in = GNUNET_DISK_file_open (src,
1422 GNUNET_DISK_OPEN_READ,
1423 GNUNET_DISK_PERM_NONE);
1426 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR,
1429 return GNUNET_SYSERR;
1432 GNUNET_DISK_file_open (dst,
1433 GNUNET_DISK_OPEN_WRITE | GNUNET_DISK_OPEN_CREATE |
1434 GNUNET_DISK_OPEN_FAILIFEXISTS,
1435 GNUNET_DISK_PERM_USER_READ |
1436 GNUNET_DISK_PERM_USER_WRITE |
1437 GNUNET_DISK_PERM_GROUP_READ |
1438 GNUNET_DISK_PERM_GROUP_WRITE);
1441 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR,
1444 GNUNET_DISK_file_close (in);
1445 return GNUNET_SYSERR;
1447 buf = GNUNET_malloc (COPY_BLK_SIZE);
1450 len = COPY_BLK_SIZE;
1451 if (len > size - pos)
1453 sret = GNUNET_DISK_file_read (in,
1457 (len != (size_t) sret) )
1459 sret = GNUNET_DISK_file_write (out,
1463 (len != (size_t) sret) )
1468 GNUNET_DISK_file_close (in);
1469 GNUNET_DISK_file_close (out);
1473 GNUNET_DISK_file_close (in);
1474 GNUNET_DISK_file_close (out);
1475 return GNUNET_SYSERR;
1480 * @brief Removes special characters as ':' from a filename.
1481 * @param fn the filename to canonicalize
1484 GNUNET_DISK_filename_canonicalize (char *fn)
1489 for (idx = fn; *idx; idx++)
1493 if (c == '/' || c == '\\' || c == ':' ||
1494 c == '*' || c == '?' || c == '"' ||
1495 c == '<' || c == '>' || c == '|')
1505 * @brief Change owner of a file
1507 * @param filename name of file to change the owner of
1508 * @param user name of the new owner
1509 * @return #GNUNET_OK on success, #GNUNET_SYSERR on failure
1512 GNUNET_DISK_file_change_owner (const char *filename,
1518 pws = getpwnam (user);
1521 LOG (GNUNET_ERROR_TYPE_ERROR,
1522 _("Cannot obtain information about user `%s': %s\n"),
1525 return GNUNET_SYSERR;
1527 if (0 != chown (filename,
1531 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING,
1534 return GNUNET_SYSERR;
1542 * Lock a part of a file
1544 * @param fh file handle
1545 * @param lock_start absolute position from where to lock
1546 * @param lock_end absolute position until where to lock
1547 * @param excl #GNUNET_YES for an exclusive lock
1548 * @return #GNUNET_OK on success, #GNUNET_SYSERR on error
1551 GNUNET_DISK_file_lock (struct GNUNET_DISK_FileHandle *fh,
1559 return GNUNET_SYSERR;
1565 memset (&fl, 0, sizeof (struct flock));
1566 fl.l_type = excl ? F_WRLCK : F_RDLCK;
1567 fl.l_whence = SEEK_SET;
1568 fl.l_start = lock_start;
1569 fl.l_len = lock_end;
1571 return fcntl (fh->fd, F_SETLK, &fl) != 0 ? GNUNET_SYSERR : GNUNET_OK;
1574 off_t diff = lock_end - lock_start;
1575 DWORD diff_low, diff_high;
1576 diff_low = (DWORD) (diff & 0xFFFFFFFF);
1577 diff_high = (DWORD) ((diff >> (sizeof (DWORD) * 8)) & 0xFFFFFFFF);
1579 memset (&o, 0, sizeof (OVERLAPPED));
1580 o.Offset = (DWORD) (lock_start & 0xFFFFFFFF);;
1581 o.OffsetHigh = (DWORD) (((lock_start & ~0xFFFFFFFF) >> (sizeof (DWORD) * 8)) & 0xFFFFFFFF);
1584 (fh->h, (excl ? LOCKFILE_EXCLUSIVE_LOCK : 0) | LOCKFILE_FAIL_IMMEDIATELY,
1585 0, diff_low, diff_high, &o))
1587 SetErrnoFromWinError (GetLastError ());
1588 return GNUNET_SYSERR;
1597 * Unlock a part of a file
1599 * @param fh file handle
1600 * @param unlock_start absolute position from where to unlock
1601 * @param unlock_end absolute position until where to unlock
1602 * @return #GNUNET_OK on success, #GNUNET_SYSERR on error
1605 GNUNET_DISK_file_unlock (struct GNUNET_DISK_FileHandle *fh,
1612 return GNUNET_SYSERR;
1618 memset (&fl, 0, sizeof (struct flock));
1619 fl.l_type = F_UNLCK;
1620 fl.l_whence = SEEK_SET;
1621 fl.l_start = unlock_start;
1622 fl.l_len = unlock_end;
1624 return fcntl (fh->fd, F_SETLK, &fl) != 0 ? GNUNET_SYSERR : GNUNET_OK;
1627 off_t diff = unlock_end - unlock_start;
1628 DWORD diff_low, diff_high;
1629 diff_low = (DWORD) (diff & 0xFFFFFFFF);
1630 diff_high = (DWORD) ((diff >> (sizeof (DWORD) * 8)) & 0xFFFFFFFF);
1632 memset (&o, 0, sizeof (OVERLAPPED));
1633 o.Offset = (DWORD) (unlock_start & 0xFFFFFFFF);;
1634 o.OffsetHigh = (DWORD) (((unlock_start & ~0xFFFFFFFF) >> (sizeof (DWORD) * 8)) & 0xFFFFFFFF);
1636 if (!UnlockFileEx (fh->h, 0, diff_low, diff_high, &o))
1638 SetErrnoFromWinError (GetLastError ());
1639 return GNUNET_SYSERR;
1648 * Open a file. Note that the access permissions will only be
1649 * used if a new file is created and if the underlying operating
1650 * system supports the given permissions.
1652 * @param fn file name to be opened
1653 * @param flags opening flags, a combination of GNUNET_DISK_OPEN_xxx bit flags
1654 * @param perm permissions for the newly created file, use
1655 * #GNUNET_DISK_PERM_NONE if a file could not be created by this
1656 * call (because of flags)
1657 * @return IO handle on success, NULL on error
1659 struct GNUNET_DISK_FileHandle *
1660 GNUNET_DISK_file_open (const char *fn,
1661 enum GNUNET_DISK_OpenFlags flags,
1662 enum GNUNET_DISK_AccessPermissions perm)
1665 struct GNUNET_DISK_FileHandle *ret;
1671 wchar_t wexpfn[MAX_PATH + 1];
1678 expfn = GNUNET_STRINGS_filename_expand (fn);
1683 if (GNUNET_DISK_OPEN_READWRITE == (flags & GNUNET_DISK_OPEN_READWRITE))
1684 oflags = O_RDWR; /* note: O_RDWR is NOT always O_RDONLY | O_WRONLY */
1685 else if (flags & GNUNET_DISK_OPEN_READ)
1687 else if (flags & GNUNET_DISK_OPEN_WRITE)
1692 GNUNET_free (expfn);
1695 if (flags & GNUNET_DISK_OPEN_FAILIFEXISTS)
1696 oflags |= (O_CREAT | O_EXCL);
1697 if (flags & GNUNET_DISK_OPEN_TRUNCATE)
1699 if (flags & GNUNET_DISK_OPEN_APPEND)
1701 if(GNUNET_NO == GNUNET_DISK_file_test(fn))
1703 if (flags & GNUNET_DISK_OPEN_CREATE )
1705 (void) GNUNET_DISK_directory_create_for_file (expfn);
1707 mode = translate_unix_perms (perm);
1711 fd = open (expfn, oflags
1715 | O_LARGEFILE, mode);
1718 if (0 == (flags & GNUNET_DISK_OPEN_FAILIFEXISTS))
1719 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "open", expfn);
1721 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_DEBUG, "open", expfn);
1722 GNUNET_free (expfn);
1729 if (GNUNET_DISK_OPEN_READWRITE == (flags & GNUNET_DISK_OPEN_READWRITE))
1730 access = FILE_READ_DATA | FILE_WRITE_DATA;
1731 else if (flags & GNUNET_DISK_OPEN_READ)
1732 access = FILE_READ_DATA;
1733 else if (flags & GNUNET_DISK_OPEN_WRITE)
1734 access = FILE_WRITE_DATA;
1736 if (flags & GNUNET_DISK_OPEN_FAILIFEXISTS)
1740 else if (flags & GNUNET_DISK_OPEN_CREATE)
1742 (void) GNUNET_DISK_directory_create_for_file (expfn);
1743 if (flags & GNUNET_DISK_OPEN_TRUNCATE)
1744 disp = CREATE_ALWAYS;
1748 else if (flags & GNUNET_DISK_OPEN_TRUNCATE)
1750 disp = TRUNCATE_EXISTING;
1754 disp = OPEN_EXISTING;
1757 if (ERROR_SUCCESS == plibc_conv_to_win_pathwconv(expfn, wexpfn))
1758 h = CreateFileW (wexpfn, access,
1759 FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE, NULL,
1760 disp, FILE_ATTRIBUTE_NORMAL, NULL);
1762 h = INVALID_HANDLE_VALUE;
1763 if (h == INVALID_HANDLE_VALUE)
1766 SetErrnoFromWinError (GetLastError ());
1768 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_INFO, "open", expfn);
1769 GNUNET_free (expfn);
1774 if (flags & GNUNET_DISK_OPEN_APPEND)
1775 if (SetFilePointer (h, 0, 0, FILE_END) == INVALID_SET_FILE_POINTER)
1777 SetErrnoFromWinError (GetLastError ());
1778 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "SetFilePointer", expfn);
1780 GNUNET_free (expfn);
1785 ret = GNUNET_new (struct GNUNET_DISK_FileHandle);
1788 ret->type = GNUNET_DISK_HANLDE_TYPE_FILE;
1792 GNUNET_free (expfn);
1798 * Close an open file.
1800 * @param h file handle
1801 * @return #GNUNET_OK on success, #GNUNET_SYSERR otherwise
1804 GNUNET_DISK_file_close (struct GNUNET_DISK_FileHandle *h)
1810 return GNUNET_SYSERR;
1816 if (! CloseHandle (h->h))
1818 SetErrnoFromWinError (GetLastError ());
1819 LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING, "close");
1820 ret = GNUNET_SYSERR;
1822 if (h->oOverlapRead)
1824 if (! CloseHandle (h->oOverlapRead->hEvent))
1826 SetErrnoFromWinError (GetLastError ());
1827 LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING, "close");
1828 ret = GNUNET_SYSERR;
1830 GNUNET_free (h->oOverlapRead);
1832 if (h->oOverlapWrite)
1834 if (!CloseHandle (h->oOverlapWrite->hEvent))
1836 SetErrnoFromWinError (GetLastError ());
1837 LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING, "close");
1838 ret = GNUNET_SYSERR;
1840 GNUNET_free (h->oOverlapWrite);
1843 if (close (h->fd) != 0)
1845 LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING, "close");
1846 ret = GNUNET_SYSERR;
1856 * Get a GNUnet file handle from a W32 handle.
1858 * @param handle native handle
1859 * @return GNUnet file handle corresponding to the W32 handle
1861 struct GNUNET_DISK_FileHandle *
1862 GNUNET_DISK_get_handle_from_w32_handle (HANDLE osfh)
1864 struct GNUNET_DISK_FileHandle *fh;
1866 enum GNUNET_FILE_Type ftype;
1868 dwret = GetFileType (osfh);
1871 case FILE_TYPE_DISK:
1872 ftype = GNUNET_DISK_HANLDE_TYPE_FILE;
1874 case FILE_TYPE_PIPE:
1875 ftype = GNUNET_DISK_HANLDE_TYPE_PIPE;
1877 case FILE_TYPE_UNKNOWN:
1878 if ( (GetLastError () == NO_ERROR) ||
1879 (GetLastError () == ERROR_INVALID_HANDLE) )
1881 if (0 != ResetEvent (osfh))
1882 ftype = GNUNET_DISK_HANLDE_TYPE_EVENT;
1893 fh = GNUNET_new (struct GNUNET_DISK_FileHandle);
1897 if (ftype == GNUNET_DISK_HANLDE_TYPE_PIPE)
1900 * Note that we can't make it overlapped if it isn't already.
1901 * (ReOpenFile() is only available in 2003/Vista).
1902 * The process that opened this file in the first place (usually a parent
1903 * process, if this is stdin/stdout/stderr) must make it overlapped,
1904 * otherwise we're screwed, as selecting on non-overlapped handle
1907 fh->oOverlapRead = GNUNET_new (OVERLAPPED);
1908 fh->oOverlapWrite = GNUNET_new (OVERLAPPED);
1909 fh->oOverlapRead->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
1910 fh->oOverlapWrite->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
1918 * Get a handle from a native integer FD.
1920 * @param fno native integer file descriptor
1921 * @return file handle corresponding to the descriptor, NULL on error
1923 struct GNUNET_DISK_FileHandle *
1924 GNUNET_DISK_get_handle_from_int_fd (int fno)
1926 struct GNUNET_DISK_FileHandle *fh;
1928 if ( (((off_t) -1) == lseek (fno, 0, SEEK_CUR)) &&
1930 return NULL; /* invalid FD */
1933 fh = GNUNET_new (struct GNUNET_DISK_FileHandle);
1939 osfh = _get_osfhandle (fno);
1940 if (INVALID_HANDLE_VALUE == (HANDLE) osfh)
1943 fh = GNUNET_DISK_get_handle_from_w32_handle ((HANDLE) osfh);
1951 * Get a handle from a native streaming FD.
1953 * @param fd native streaming file descriptor
1954 * @return file handle corresponding to the descriptor
1956 struct GNUNET_DISK_FileHandle *
1957 GNUNET_DISK_get_handle_from_native (FILE *fd)
1965 return GNUNET_DISK_get_handle_from_int_fd (fno);
1970 * Handle for a memory-mapping operation.
1972 struct GNUNET_DISK_MapHandle
1975 * Address where the map is in memory.
1981 * Underlying OS handle.
1986 * Number of bytes mapped.
1994 #define MAP_FAILED ((void *) -1)
1998 * Map a file into memory
2000 * @param h open file handle
2001 * @param m handle to the new mapping
2002 * @param access access specification, GNUNET_DISK_MAP_TYPE_xxx
2003 * @param len size of the mapping
2004 * @return pointer to the mapped memory region, NULL on failure
2007 GNUNET_DISK_file_map (const struct GNUNET_DISK_FileHandle *h,
2008 struct GNUNET_DISK_MapHandle **m,
2009 enum GNUNET_DISK_MapType access, size_t len)
2018 DWORD mapAccess, protect;
2020 if ((access & GNUNET_DISK_MAP_TYPE_READ) &&
2021 (access & GNUNET_DISK_MAP_TYPE_WRITE))
2023 protect = PAGE_READWRITE;
2024 mapAccess = FILE_MAP_ALL_ACCESS;
2026 else if (access & GNUNET_DISK_MAP_TYPE_READ)
2028 protect = PAGE_READONLY;
2029 mapAccess = FILE_MAP_READ;
2031 else if (access & GNUNET_DISK_MAP_TYPE_WRITE)
2033 protect = PAGE_READWRITE;
2034 mapAccess = FILE_MAP_WRITE;
2042 *m = GNUNET_new (struct GNUNET_DISK_MapHandle);
2043 (*m)->h = CreateFileMapping (h->h, NULL, protect, 0, 0, NULL);
2044 if ((*m)->h == INVALID_HANDLE_VALUE)
2046 SetErrnoFromWinError (GetLastError ());
2051 (*m)->addr = MapViewOfFile ((*m)->h, mapAccess, 0, 0, len);
2054 SetErrnoFromWinError (GetLastError ());
2055 CloseHandle ((*m)->h);
2064 if (access & GNUNET_DISK_MAP_TYPE_READ)
2066 if (access & GNUNET_DISK_MAP_TYPE_WRITE)
2068 *m = GNUNET_new (struct GNUNET_DISK_MapHandle);
2069 (*m)->addr = mmap (NULL, len, prot, MAP_SHARED, h->fd, 0);
2070 GNUNET_assert (NULL != (*m)->addr);
2071 if (MAP_FAILED == (*m)->addr)
2083 * @param h mapping handle
2084 * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
2087 GNUNET_DISK_file_unmap (struct GNUNET_DISK_MapHandle *h)
2094 return GNUNET_SYSERR;
2098 ret = UnmapViewOfFile (h->addr) ? GNUNET_OK : GNUNET_SYSERR;
2099 if (ret != GNUNET_OK)
2100 SetErrnoFromWinError (GetLastError ());
2101 if (!CloseHandle (h->h) && (ret == GNUNET_OK))
2103 ret = GNUNET_SYSERR;
2104 SetErrnoFromWinError (GetLastError ());
2107 ret = munmap (h->addr, h->len) != -1 ? GNUNET_OK : GNUNET_SYSERR;
2115 * Write file changes to disk
2116 * @param h handle to an open file
2117 * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
2120 GNUNET_DISK_file_sync (const struct GNUNET_DISK_FileHandle *h)
2125 return GNUNET_SYSERR;
2131 ret = FlushFileBuffers (h->h) ? GNUNET_OK : GNUNET_SYSERR;
2132 if (ret != GNUNET_OK)
2133 SetErrnoFromWinError (GetLastError ());
2135 #elif defined(FREEBSD) || defined(OPENBSD) || defined(DARWIN)
2136 return fsync (h->fd) == -1 ? GNUNET_SYSERR : GNUNET_OK;
2138 return fdatasync (h->fd) == -1 ? GNUNET_SYSERR : GNUNET_OK;
2145 #define PIPE_BUF 512
2147 /* Copyright Bob Byrnes <byrnes <at> curl.com>
2148 http://permalink.gmane.org/gmane.os.cygwin.patches/2121
2150 /* Create a pipe, and return handles to the read and write ends,
2151 just like CreatePipe, but ensure that the write end permits
2152 FILE_READ_ATTRIBUTES access, on later versions of win32 where
2153 this is supported. This access is needed by NtQueryInformationFile,
2154 which is used to implement select and nonblocking writes.
2155 Note that the return value is either NO_ERROR or GetLastError,
2156 unlike CreatePipe, which returns a bool for success or failure. */
2158 create_selectable_pipe (PHANDLE read_pipe_ptr, PHANDLE write_pipe_ptr,
2159 LPSECURITY_ATTRIBUTES sa_ptr, DWORD psize,
2160 DWORD dwReadMode, DWORD dwWriteMode)
2162 /* Default to error. */
2163 *read_pipe_ptr = *write_pipe_ptr = INVALID_HANDLE_VALUE;
2168 /* Ensure that there is enough pipe buffer space for atomic writes. */
2169 if (psize < PIPE_BUF)
2172 char pipename[MAX_PATH];
2174 /* Retry CreateNamedPipe as long as the pipe name is in use.
2175 * Retrying will probably never be necessary, but we want
2176 * to be as robust as possible. */
2179 static volatile LONG pipe_unique_id;
2181 snprintf (pipename, sizeof pipename, "\\\\.\\pipe\\gnunet-%d-%ld",
2182 getpid (), InterlockedIncrement ((LONG *) & pipe_unique_id));
2183 LOG (GNUNET_ERROR_TYPE_DEBUG, "CreateNamedPipe: name = %s, size = %lu\n",
2185 /* Use CreateNamedPipe instead of CreatePipe, because the latter
2186 * returns a write handle that does not permit FILE_READ_ATTRIBUTES
2187 * access, on versions of win32 earlier than WinXP SP2.
2188 * CreatePipe also stupidly creates a full duplex pipe, which is
2189 * a waste, since only a single direction is actually used.
2190 * It's important to only allow a single instance, to ensure that
2191 * the pipe was not created earlier by some other process, even if
2192 * the pid has been reused. */
2193 read_pipe = CreateNamedPipeA (pipename, PIPE_ACCESS_INBOUND | FILE_FLAG_FIRST_PIPE_INSTANCE | dwReadMode, PIPE_TYPE_BYTE | PIPE_READMODE_BYTE, 1, /* max instances */
2194 psize, /* output buffer size */
2195 psize, /* input buffer size */
2196 NMPWAIT_USE_DEFAULT_WAIT, sa_ptr);
2198 if (read_pipe != INVALID_HANDLE_VALUE)
2200 LOG (GNUNET_ERROR_TYPE_DEBUG, "pipe read handle = %p\n", read_pipe);
2204 DWORD err = GetLastError ();
2208 case ERROR_PIPE_BUSY:
2209 /* The pipe is already open with compatible parameters.
2210 * Pick a new name and retry. */
2211 LOG (GNUNET_ERROR_TYPE_DEBUG, "pipe busy, retrying\n");
2213 case ERROR_ACCESS_DENIED:
2214 /* The pipe is already open with incompatible parameters.
2215 * Pick a new name and retry. */
2216 LOG (GNUNET_ERROR_TYPE_DEBUG, "pipe access denied, retrying\n");
2218 case ERROR_CALL_NOT_IMPLEMENTED:
2219 /* We are on an older Win9x platform without named pipes.
2220 * Return an anonymous pipe as the best approximation. */
2221 LOG (GNUNET_ERROR_TYPE_DEBUG,
2222 "CreateNamedPipe not implemented, resorting to "
2223 "CreatePipe: size = %lu\n", psize);
2224 if (CreatePipe (read_pipe_ptr, write_pipe_ptr, sa_ptr, psize))
2226 LOG (GNUNET_ERROR_TYPE_DEBUG, "pipe read handle = %p, write handle = %p\n",
2231 err = GetLastError ();
2232 LOG (GNUNET_ERROR_TYPE_ERROR, "CreatePipe failed: %d\n", err);
2235 LOG (GNUNET_ERROR_TYPE_ERROR, "CreateNamedPipe failed: %d\n", err);
2240 LOG (GNUNET_ERROR_TYPE_DEBUG, "CreateFile: name = %s\n", pipename);
2242 /* Open the named pipe for writing.
2243 * Be sure to permit FILE_READ_ATTRIBUTES access. */
2244 write_pipe = CreateFileA (pipename, GENERIC_WRITE | FILE_READ_ATTRIBUTES, 0, /* share mode */
2245 sa_ptr, OPEN_EXISTING, dwWriteMode, /* flags and attributes */
2246 0); /* handle to template file */
2248 if (write_pipe == INVALID_HANDLE_VALUE)
2251 DWORD err = GetLastError ();
2253 LOG (GNUNET_ERROR_TYPE_DEBUG, "CreateFile failed: %d\n", err);
2254 CloseHandle (read_pipe);
2257 LOG (GNUNET_ERROR_TYPE_DEBUG, "pipe write handle = %p\n", write_pipe);
2259 *read_pipe_ptr = read_pipe;
2260 *write_pipe_ptr = write_pipe;
2267 * Creates an interprocess channel
2269 * @param blocking_read creates an asynchronous pipe for reading if set to GNUNET_NO
2270 * @param blocking_write creates an asynchronous pipe for writing if set to GNUNET_NO
2271 * @param inherit_read inherit the parent processes stdin (only for windows)
2272 * @param inherit_write inherit the parent processes stdout (only for windows)
2273 * @return handle to the new pipe, NULL on error
2275 struct GNUNET_DISK_PipeHandle *
2276 GNUNET_DISK_pipe (int blocking_read,
2286 (void) inherit_read;
2287 (void) inherit_write;
2292 LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR,
2297 return GNUNET_DISK_pipe_from_fd (blocking_read,
2301 struct GNUNET_DISK_PipeHandle *p;
2306 p = GNUNET_new (struct GNUNET_DISK_PipeHandle);
2307 p->fd[0] = GNUNET_new (struct GNUNET_DISK_FileHandle);
2308 p->fd[1] = GNUNET_new (struct GNUNET_DISK_FileHandle);
2310 /* All pipes are overlapped. If you want them to block - just
2311 * call WriteFile() and ReadFile() with NULL overlapped pointer.
2312 * NOTE: calling with NULL overlapped pointer works only
2313 * for pipes, and doesn't seem to be a documented feature.
2314 * It will NOT work for files, because overlapped files need
2315 * to read offsets from the overlapped structure, regardless.
2316 * Pipes are not seekable, and need no offsets, which is
2317 * probably why it works for them.
2320 create_selectable_pipe (&p->fd[0]->h, &p->fd[1]->h, NULL, 0,
2321 FILE_FLAG_OVERLAPPED,
2322 FILE_FLAG_OVERLAPPED);
2325 SetErrnoFromWinError (GetLastError ());
2327 GNUNET_free (p->fd[0]);
2328 GNUNET_free (p->fd[1]);
2333 if (!DuplicateHandle
2334 (GetCurrentProcess (), p->fd[0]->h, GetCurrentProcess (), &tmp_handle, 0,
2335 inherit_read == GNUNET_YES ? TRUE : FALSE, DUPLICATE_SAME_ACCESS))
2337 SetErrnoFromWinError (GetLastError ());
2339 CloseHandle (p->fd[0]->h);
2340 CloseHandle (p->fd[1]->h);
2341 GNUNET_free (p->fd[0]);
2342 GNUNET_free (p->fd[1]);
2347 CloseHandle (p->fd[0]->h);
2348 p->fd[0]->h = tmp_handle;
2350 if (!DuplicateHandle
2351 (GetCurrentProcess (), p->fd[1]->h, GetCurrentProcess (), &tmp_handle, 0,
2352 inherit_write == GNUNET_YES ? TRUE : FALSE, DUPLICATE_SAME_ACCESS))
2354 SetErrnoFromWinError (GetLastError ());
2356 CloseHandle (p->fd[0]->h);
2357 CloseHandle (p->fd[1]->h);
2358 GNUNET_free (p->fd[0]);
2359 GNUNET_free (p->fd[1]);
2364 CloseHandle (p->fd[1]->h);
2365 p->fd[1]->h = tmp_handle;
2367 p->fd[0]->type = GNUNET_DISK_HANLDE_TYPE_PIPE;
2368 p->fd[1]->type = GNUNET_DISK_HANLDE_TYPE_PIPE;
2370 p->fd[0]->oOverlapRead = GNUNET_new (OVERLAPPED);
2371 p->fd[0]->oOverlapWrite = GNUNET_new (OVERLAPPED);
2372 p->fd[1]->oOverlapRead = GNUNET_new (OVERLAPPED);
2373 p->fd[1]->oOverlapWrite = GNUNET_new (OVERLAPPED);
2375 p->fd[0]->oOverlapRead->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2376 p->fd[0]->oOverlapWrite->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2378 p->fd[1]->oOverlapRead->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2379 p->fd[1]->oOverlapWrite->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2387 * Creates a pipe object from a couple of file descriptors.
2388 * Useful for wrapping existing pipe FDs.
2390 * @param blocking_read creates an asynchronous pipe for reading if set to GNUNET_NO
2391 * @param blocking_write creates an asynchronous pipe for writing if set to GNUNET_NO
2392 * @param fd an array of two fd values. One of them may be -1 for read-only or write-only pipes
2394 * @return handle to the new pipe, NULL on error
2396 struct GNUNET_DISK_PipeHandle *
2397 GNUNET_DISK_pipe_from_fd (int blocking_read, int blocking_write, int fd[2])
2399 struct GNUNET_DISK_PipeHandle *p;
2401 p = GNUNET_new (struct GNUNET_DISK_PipeHandle);
2406 int eno = 0; /* make gcc happy */
2411 p->fd[0] = GNUNET_new (struct GNUNET_DISK_FileHandle);
2412 p->fd[0]->fd = fd[0];
2415 flags = fcntl (fd[0], F_GETFL);
2416 flags |= O_NONBLOCK;
2417 if (0 > fcntl (fd[0], F_SETFL, flags))
2423 flags = fcntl (fd[0], F_GETFD);
2424 flags |= FD_CLOEXEC;
2425 if (0 > fcntl (fd[0], F_SETFD, flags))
2434 p->fd[1] = GNUNET_new (struct GNUNET_DISK_FileHandle);
2435 p->fd[1]->fd = fd[1];
2436 if (!blocking_write)
2438 flags = fcntl (fd[1], F_GETFL);
2439 flags |= O_NONBLOCK;
2440 if (0 > fcntl (fd[1], F_SETFL, flags))
2446 flags = fcntl (fd[1], F_GETFD);
2447 flags |= FD_CLOEXEC;
2448 if (0 > fcntl (fd[1], F_SETFD, flags))
2457 LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR, "fcntl");
2458 if (p->fd[0]->fd >= 0)
2459 GNUNET_break (0 == close (p->fd[0]->fd));
2460 if (p->fd[1]->fd >= 0)
2461 GNUNET_break (0 == close (p->fd[1]->fd));
2462 GNUNET_free_non_null (p->fd[0]);
2463 GNUNET_free_non_null (p->fd[1]);
2471 p->fd[0] = GNUNET_new (struct GNUNET_DISK_FileHandle);
2472 p->fd[0]->h = (HANDLE) _get_osfhandle (fd[0]);
2473 if (p->fd[0]->h != INVALID_HANDLE_VALUE)
2475 p->fd[0]->type = GNUNET_DISK_HANLDE_TYPE_PIPE;
2476 p->fd[0]->oOverlapRead = GNUNET_new (OVERLAPPED);
2477 p->fd[0]->oOverlapWrite = GNUNET_new (OVERLAPPED);
2478 p->fd[0]->oOverlapRead->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2479 p->fd[0]->oOverlapWrite->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2483 GNUNET_free (p->fd[0]);
2489 p->fd[1] = GNUNET_new (struct GNUNET_DISK_FileHandle);
2490 p->fd[1]->h = (HANDLE) _get_osfhandle (fd[1]);
2491 if (p->fd[1]->h != INVALID_HANDLE_VALUE)
2493 p->fd[1]->type = GNUNET_DISK_HANLDE_TYPE_PIPE;
2494 p->fd[1]->oOverlapRead = GNUNET_new (OVERLAPPED);
2495 p->fd[1]->oOverlapWrite = GNUNET_new (OVERLAPPED);
2496 p->fd[1]->oOverlapRead->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2497 p->fd[1]->oOverlapWrite->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2501 GNUNET_free (p->fd[1]);
2512 * Closes an interprocess channel
2514 * @param p pipe to close
2515 * @param end which end of the pipe to close
2516 * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
2519 GNUNET_DISK_pipe_close_end (struct GNUNET_DISK_PipeHandle *p,
2520 enum GNUNET_DISK_PipeEnd end)
2522 int ret = GNUNET_OK;
2524 if (end == GNUNET_DISK_PIPE_END_READ)
2528 ret = GNUNET_DISK_file_close (p->fd[0]);
2532 else if (end == GNUNET_DISK_PIPE_END_WRITE)
2536 ret = GNUNET_DISK_file_close (p->fd[1]);
2545 * Detaches one of the ends from the pipe.
2546 * Detached end is a fully-functional FileHandle, it will
2547 * not be affected by anything you do with the pipe afterwards.
2548 * Each end of a pipe can only be detched from it once (i.e.
2549 * it is not duplicated).
2551 * @param p pipe to detach an end from
2552 * @param end which end of the pipe to detach
2553 * @return Detached end on success, NULL on failure
2554 * (or if that end is not present or is closed).
2556 struct GNUNET_DISK_FileHandle *
2557 GNUNET_DISK_pipe_detach_end (struct GNUNET_DISK_PipeHandle *p,
2558 enum GNUNET_DISK_PipeEnd end)
2560 struct GNUNET_DISK_FileHandle *ret = NULL;
2562 if (end == GNUNET_DISK_PIPE_END_READ)
2570 else if (end == GNUNET_DISK_PIPE_END_WRITE)
2584 * Closes an interprocess channel
2586 * @param p pipe to close
2587 * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
2590 GNUNET_DISK_pipe_close (struct GNUNET_DISK_PipeHandle *p)
2592 int ret = GNUNET_OK;
2595 int write_end_close;
2596 int read_end_close_errno;
2597 int write_end_close_errno;
2599 read_end_close = GNUNET_DISK_pipe_close_end (p, GNUNET_DISK_PIPE_END_READ);
2600 read_end_close_errno = errno;
2601 write_end_close = GNUNET_DISK_pipe_close_end (p, GNUNET_DISK_PIPE_END_WRITE);
2602 write_end_close_errno = errno;
2605 if (GNUNET_OK != read_end_close)
2607 errno = read_end_close_errno;
2608 ret = read_end_close;
2610 else if (GNUNET_OK != write_end_close)
2612 errno = write_end_close_errno;
2613 ret = write_end_close;
2621 * Get the handle to a particular pipe end
2624 * @param n end to access
2625 * @return handle for the respective end
2627 const struct GNUNET_DISK_FileHandle *
2628 GNUNET_DISK_pipe_handle (const struct GNUNET_DISK_PipeHandle *p,
2629 enum GNUNET_DISK_PipeEnd n)
2633 case GNUNET_DISK_PIPE_END_READ:
2634 case GNUNET_DISK_PIPE_END_WRITE:
2644 * Retrieve OS file handle
2646 * @param fh GNUnet file descriptor
2647 * @param dst destination buffer
2648 * @param dst_len length of dst
2649 * @return #GNUNET_OK on success, #GNUNET_SYSERR otherwise
2652 GNUNET_DISK_internal_file_handle_ (const struct GNUNET_DISK_FileHandle *fh,
2653 void *dst, size_t dst_len)
2656 return GNUNET_SYSERR;
2658 if (dst_len < sizeof (HANDLE))
2659 return GNUNET_SYSERR;
2660 *((HANDLE *) dst) = fh->h;
2662 if (dst_len < sizeof (int))
2663 return GNUNET_SYSERR;
2664 *((int *) dst) = fh->fd;
2672 * Remove the directory given under @a option in
2673 * section [PATHS] in configuration under @a cfg_filename
2675 * @param cfg_filename configuration file to parse
2676 * @param option option with the dir name to purge
2679 GNUNET_DISK_purge_cfg_dir (const char *cfg_filename,
2682 struct GNUNET_CONFIGURATION_Handle *cfg;
2685 cfg = GNUNET_CONFIGURATION_create ();
2687 GNUNET_CONFIGURATION_load (cfg,
2691 GNUNET_CONFIGURATION_destroy (cfg);
2695 GNUNET_CONFIGURATION_get_value_filename (cfg,
2700 GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
2703 GNUNET_CONFIGURATION_destroy (cfg);
2706 GNUNET_CONFIGURATION_destroy (cfg);
2707 if (GNUNET_SYSERR ==
2708 GNUNET_DISK_directory_remove (tmpname))
2710 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR,
2713 GNUNET_free (tmpname);
2716 GNUNET_free (tmpname);