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);
613 * Test if @a fil is a directory and listable. Optionally, also check if the
614 * directory is readable. Will not print an error message if the directory does
615 * not exist. Will log errors if #GNUNET_SYSERR is returned (i.e., a file exists
616 * with the same name).
618 * @param fil filename to test
619 * @param is_readable #GNUNET_YES to additionally check if @a fil is readable;
620 * #GNUNET_NO to disable this check
621 * @return #GNUNET_YES if yes, #GNUNET_NO if not; #GNUNET_SYSERR if it
622 * does not exist or stat'ed
625 GNUNET_DISK_directory_test (const char *fil, int is_readable)
627 struct stat filestat;
630 ret = STAT (fil, &filestat);
634 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "stat", fil);
635 return GNUNET_SYSERR;
637 if (!S_ISDIR (filestat.st_mode))
639 LOG (GNUNET_ERROR_TYPE_DEBUG,
640 "A file already exits with the same name %s\n", fil);
643 if (GNUNET_YES == is_readable)
644 ret = ACCESS (fil, R_OK | X_OK);
646 ret = ACCESS (fil, X_OK);
649 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "access", fil);
657 * Check that fil corresponds to a filename
658 * (of a file that exists and that is not a directory).
660 * @param fil filename to check
661 * @return #GNUNET_YES if yes, #GNUNET_NO if not a file, #GNUNET_SYSERR if something
662 * else (will print an error message in that case, too).
665 GNUNET_DISK_file_test (const char *fil)
667 struct stat filestat;
671 rdir = GNUNET_STRINGS_filename_expand (fil);
673 return GNUNET_SYSERR;
675 ret = STAT (rdir, &filestat);
680 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "stat", rdir);
682 return GNUNET_SYSERR;
687 if (!S_ISREG (filestat.st_mode))
692 if (ACCESS (rdir, F_OK) < 0)
694 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "access", rdir);
696 return GNUNET_SYSERR;
704 * Implementation of "mkdir -p"
706 * @param dir the directory to create
707 * @return #GNUNET_OK on success, #GNUNET_SYSERR on failure
710 GNUNET_DISK_directory_create (const char *dir)
718 rdir = GNUNET_STRINGS_filename_expand (dir);
720 return GNUNET_SYSERR;
724 pos = 1; /* skip heading '/' */
726 /* Local or Network path? */
727 if (strncmp (rdir, "\\\\", 2) == 0)
732 if (rdir[pos] == '\\')
742 pos = 3; /* strlen("C:\\") */
745 /* Check which low level directories already exist */
747 rdir[len] = DIR_SEPARATOR;
750 if (DIR_SEPARATOR == rdir[pos2])
753 ret = GNUNET_DISK_directory_test (rdir, GNUNET_NO);
754 if (GNUNET_NO == ret)
757 return GNUNET_SYSERR;
759 rdir[pos2] = DIR_SEPARATOR;
760 if (GNUNET_YES == ret)
771 /* Start creating directories */
774 if ((rdir[pos] == DIR_SEPARATOR) || (pos == len))
777 ret = GNUNET_DISK_directory_test (rdir, GNUNET_NO);
778 if (GNUNET_NO == ret)
781 return GNUNET_SYSERR;
783 if (GNUNET_SYSERR == ret)
786 ret = mkdir (rdir, S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH); /* 755 */
788 wchar_t wrdir[MAX_PATH + 1];
789 if (ERROR_SUCCESS == plibc_conv_to_win_pathwconv(rdir, wrdir))
790 ret = !CreateDirectoryW (wrdir, NULL);
794 if ((ret != 0) && (errno != EEXIST))
796 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_ERROR, "mkdir", rdir);
798 return GNUNET_SYSERR;
801 rdir[pos] = DIR_SEPARATOR;
811 * Create the directory structure for storing a file.
813 * @param filename name of a file in the directory
814 * @returns #GNUNET_OK on success,
815 * #GNUNET_SYSERR on failure,
816 * #GNUNET_NO if the directory
817 * exists but is not writeable for us
820 GNUNET_DISK_directory_create_for_file (const char *filename)
827 rdir = GNUNET_STRINGS_filename_expand (filename);
831 return GNUNET_SYSERR;
833 if (0 == ACCESS (rdir, W_OK))
840 while ((len > 0) && (rdir[len] != DIR_SEPARATOR))
843 /* The empty path is invalid and in this case refers to / */
847 rdir = GNUNET_strdup ("/");
849 ret = GNUNET_DISK_directory_create (rdir);
850 if ((GNUNET_OK == ret) && (0 != ACCESS (rdir, W_OK)))
860 * Read the contents of a binary file into a buffer.
862 * @param h handle to an open file
863 * @param result the buffer to write the result to
864 * @param len the maximum number of bytes to read
865 * @return the number of bytes read on success, #GNUNET_SYSERR on failure
868 GNUNET_DISK_file_read (const struct GNUNET_DISK_FileHandle *h,
875 return GNUNET_SYSERR;
881 if (h->type == GNUNET_DISK_HANLDE_TYPE_FILE)
883 if (!ReadFile (h->h, result, len, &bytes_read, NULL))
885 SetErrnoFromWinError (GetLastError ());
886 return GNUNET_SYSERR;
889 else if (h->type == GNUNET_DISK_HANLDE_TYPE_PIPE)
891 if (!ReadFile (h->h, result, len, &bytes_read, h->oOverlapRead))
893 if (GetLastError () != ERROR_IO_PENDING)
895 LOG (GNUNET_ERROR_TYPE_DEBUG,
896 "Error reading from pipe: %u\n",
898 SetErrnoFromWinError (GetLastError ());
899 return GNUNET_SYSERR;
901 LOG (GNUNET_ERROR_TYPE_DEBUG, "Will get overlapped result\n");
902 GetOverlappedResult (h->h, h->oOverlapRead, &bytes_read, TRUE);
904 LOG (GNUNET_ERROR_TYPE_DEBUG, "Read %u bytes from pipe\n", bytes_read);
912 return read (h->fd, result, len);
918 * Read the contents of a binary file into a buffer.
919 * Guarantees not to block (returns GNUNET_SYSERR and sets errno to EAGAIN
920 * when no data can be read).
922 * @param h handle to an open file
923 * @param result the buffer to write the result to
924 * @param len the maximum number of bytes to read
925 * @return the number of bytes read on success, #GNUNET_SYSERR on failure
928 GNUNET_DISK_file_read_non_blocking (const struct GNUNET_DISK_FileHandle *h,
935 return GNUNET_SYSERR;
941 if (h->type == GNUNET_DISK_HANLDE_TYPE_FILE)
943 if (!ReadFile (h->h, result, len, &bytes_read, NULL))
945 SetErrnoFromWinError (GetLastError ());
946 return GNUNET_SYSERR;
949 else if (h->type == GNUNET_DISK_HANLDE_TYPE_PIPE)
951 if (!ReadFile (h->h, result, len, &bytes_read, h->oOverlapRead))
953 if (GetLastError () != ERROR_IO_PENDING)
955 LOG (GNUNET_ERROR_TYPE_DEBUG, "Error reading from pipe: %u\n", GetLastError ());
956 SetErrnoFromWinError (GetLastError ());
957 return GNUNET_SYSERR;
961 LOG (GNUNET_ERROR_TYPE_DEBUG,
962 "ReadFile() queued a read, cancelling\n");
965 return GNUNET_SYSERR;
968 LOG (GNUNET_ERROR_TYPE_DEBUG,
981 /* set to non-blocking, read, then set back */
982 flags = fcntl (h->fd, F_GETFL);
983 if (0 == (flags & O_NONBLOCK))
984 (void) fcntl (h->fd, F_SETFL, flags | O_NONBLOCK);
985 ret = read (h->fd, result, len);
986 if (0 == (flags & O_NONBLOCK))
989 (void) fcntl (h->fd, F_SETFL, flags);
998 * Read the contents of a binary file into a buffer.
1000 * @param fn file name
1001 * @param result the buffer to write the result to
1002 * @param len the maximum number of bytes to read
1003 * @return number of bytes read, #GNUNET_SYSERR on failure
1006 GNUNET_DISK_fn_read (const char *fn,
1010 struct GNUNET_DISK_FileHandle *fh;
1014 fh = GNUNET_DISK_file_open (fn,
1015 GNUNET_DISK_OPEN_READ,
1016 GNUNET_DISK_PERM_NONE);
1018 return GNUNET_SYSERR;
1019 ret = GNUNET_DISK_file_read (fh, result, len);
1021 GNUNET_assert (GNUNET_OK == GNUNET_DISK_file_close (fh));
1028 * Write a buffer to a file.
1030 * @param h handle to open file
1031 * @param buffer the data to write
1032 * @param n number of bytes to write
1033 * @return number of bytes written on success, #GNUNET_SYSERR on error
1036 GNUNET_DISK_file_write (const struct GNUNET_DISK_FileHandle * h,
1037 const void *buffer, size_t n)
1042 return GNUNET_SYSERR;
1046 DWORD bytes_written;
1048 if (h->type == GNUNET_DISK_HANLDE_TYPE_FILE)
1050 if (!WriteFile (h->h, buffer, n, &bytes_written, NULL))
1052 SetErrnoFromWinError (GetLastError ());
1053 return GNUNET_SYSERR;
1056 else if (h->type == GNUNET_DISK_HANLDE_TYPE_PIPE)
1058 LOG (GNUNET_ERROR_TYPE_DEBUG, "It is a pipe trying to write %u bytes\n", n);
1059 if (!WriteFile (h->h, buffer, n, &bytes_written, h->oOverlapWrite))
1061 if (GetLastError () != ERROR_IO_PENDING)
1063 SetErrnoFromWinError (GetLastError ());
1064 LOG (GNUNET_ERROR_TYPE_DEBUG, "Error writing to pipe: %u\n",
1066 return GNUNET_SYSERR;
1068 LOG (GNUNET_ERROR_TYPE_DEBUG, "Will get overlapped result\n");
1069 if (!GetOverlappedResult (h->h, h->oOverlapWrite, &bytes_written, TRUE))
1071 SetErrnoFromWinError (GetLastError ());
1072 LOG (GNUNET_ERROR_TYPE_DEBUG,
1073 "Error getting overlapped result while writing to pipe: %u\n",
1075 return GNUNET_SYSERR;
1081 if (!GetOverlappedResult (h->h, h->oOverlapWrite, &ovr, TRUE))
1083 LOG (GNUNET_ERROR_TYPE_DEBUG,
1084 "Error getting control overlapped result while writing to pipe: %u\n",
1089 LOG (GNUNET_ERROR_TYPE_DEBUG,
1090 "Wrote %u bytes (ovr says %u), picking the greatest\n",
1091 bytes_written, ovr);
1094 if (bytes_written == 0)
1098 LOG (GNUNET_ERROR_TYPE_DEBUG, "Wrote %u bytes, returning -1 with EAGAIN\n", bytes_written);
1100 return GNUNET_SYSERR;
1103 LOG (GNUNET_ERROR_TYPE_DEBUG, "Wrote %u bytes\n", bytes_written);
1109 return bytes_written;
1111 return write (h->fd, buffer, n);
1117 * Write a buffer to a file, blocking, if necessary.
1119 * @param h handle to open file
1120 * @param buffer the data to write
1121 * @param n number of bytes to write
1122 * @return number of bytes written on success, #GNUNET_SYSERR on error
1125 GNUNET_DISK_file_write_blocking (const struct GNUNET_DISK_FileHandle * h,
1132 return GNUNET_SYSERR;
1136 DWORD bytes_written;
1137 /* We do a non-overlapped write, which is as blocking as it gets */
1138 LOG (GNUNET_ERROR_TYPE_DEBUG, "Writing %u bytes\n", n);
1139 if (!WriteFile (h->h, buffer, n, &bytes_written, NULL))
1141 SetErrnoFromWinError (GetLastError ());
1142 LOG (GNUNET_ERROR_TYPE_DEBUG, "Error writing to pipe: %u\n",
1144 return GNUNET_SYSERR;
1146 if (bytes_written == 0 && n > 0)
1148 LOG (GNUNET_ERROR_TYPE_DEBUG, "Waiting for pipe to clean\n");
1149 WaitForSingleObject (h->h, INFINITE);
1150 if (!WriteFile (h->h, buffer, n, &bytes_written, NULL))
1152 SetErrnoFromWinError (GetLastError ());
1153 LOG (GNUNET_ERROR_TYPE_DEBUG, "Error writing to pipe: %u\n",
1155 return GNUNET_SYSERR;
1158 LOG (GNUNET_ERROR_TYPE_DEBUG,
1161 return bytes_written;
1166 /* set to blocking, write, then set back */
1167 flags = fcntl (h->fd, F_GETFL);
1168 if (0 != (flags & O_NONBLOCK))
1169 (void) fcntl (h->fd, F_SETFL, flags - O_NONBLOCK);
1170 ret = write (h->fd, buffer, n);
1171 if (0 == (flags & O_NONBLOCK))
1172 (void) fcntl (h->fd, F_SETFL, flags);
1179 * Write a buffer to a file. If the file is longer than the
1180 * number of bytes that will be written, it will be truncated.
1182 * @param fn file name
1183 * @param buffer the data to write
1184 * @param n number of bytes to write
1185 * @param mode file permissions
1186 * @return number of bytes written on success, #GNUNET_SYSERR on error
1189 GNUNET_DISK_fn_write (const char *fn,
1192 enum GNUNET_DISK_AccessPermissions mode)
1194 struct GNUNET_DISK_FileHandle *fh;
1197 fh = GNUNET_DISK_file_open (fn,
1198 GNUNET_DISK_OPEN_WRITE | GNUNET_DISK_OPEN_TRUNCATE
1199 | GNUNET_DISK_OPEN_CREATE, mode);
1201 return GNUNET_SYSERR;
1202 ret = GNUNET_DISK_file_write (fh, buffer, n);
1203 GNUNET_assert (GNUNET_OK == GNUNET_DISK_file_close (fh));
1209 * Scan a directory for files.
1211 * @param dir_name the name of the directory
1212 * @param callback the method to call for each file,
1213 * can be NULL, in that case, we only count
1214 * @param callback_cls closure for @a callback
1215 * @return the number of files found, #GNUNET_SYSERR on error or
1216 * ieration aborted by callback returning #GNUNET_SYSERR
1219 GNUNET_DISK_directory_scan (const char *dir_name,
1220 GNUNET_FileNameCallback callback,
1224 struct dirent *finfo;
1230 unsigned int name_len;
1231 unsigned int n_size;
1233 GNUNET_assert (NULL != dir_name);
1234 dname = GNUNET_STRINGS_filename_expand (dir_name);
1236 return GNUNET_SYSERR;
1237 while ( (strlen (dname) > 0) &&
1238 (dname[strlen (dname) - 1] == DIR_SEPARATOR) )
1239 dname[strlen (dname) - 1] = '\0';
1240 if (0 != STAT (dname, &istat))
1242 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING,
1245 GNUNET_free (dname);
1246 return GNUNET_SYSERR;
1248 if (! S_ISDIR (istat.st_mode))
1250 LOG (GNUNET_ERROR_TYPE_WARNING,
1251 _("Expected `%s' to be a directory!\n"),
1253 GNUNET_free (dname);
1254 return GNUNET_SYSERR;
1257 dinfo = OPENDIR (dname);
1258 if ( (EACCES == errno) ||
1261 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING,
1266 GNUNET_free (dname);
1267 return GNUNET_SYSERR;
1270 n_size = strlen (dname) + name_len + strlen (DIR_SEPARATOR_STR) + 1;
1271 name = GNUNET_malloc (n_size);
1272 while (NULL != (finfo = READDIR (dinfo)))
1274 if ( (0 == strcmp (finfo->d_name, ".")) ||
1275 (0 == strcmp (finfo->d_name, "..")) )
1277 if (NULL != callback)
1279 if (name_len < strlen (finfo->d_name))
1282 name_len = strlen (finfo->d_name);
1283 n_size = strlen (dname) + name_len + strlen (DIR_SEPARATOR_STR) + 1;
1284 name = GNUNET_malloc (n_size);
1286 /* dname can end in "/" only if dname == "/";
1287 * if dname does not end in "/", we need to add
1288 * a "/" (otherwise, we must not!) */
1289 GNUNET_snprintf (name,
1293 (0 == strcmp (dname,
1296 : DIR_SEPARATOR_STR,
1298 ret = callback (callback_cls,
1300 if (GNUNET_OK != ret)
1304 GNUNET_free (dname);
1305 if (GNUNET_NO == ret)
1307 return GNUNET_SYSERR;
1314 GNUNET_free (dname);
1320 * Function that removes the given directory by calling
1321 * #GNUNET_DISK_directory_remove().
1323 * @param unused not used
1324 * @param fn directory to remove
1325 * @return #GNUNET_OK
1328 remove_helper (void *unused,
1332 (void) GNUNET_DISK_directory_remove (fn);
1338 * Remove all files in a directory (rm -r). Call with
1341 * @param filename the file to remove
1342 * @return #GNUNET_OK on success, #GNUNET_SYSERR on error
1345 GNUNET_DISK_directory_remove (const char *filename)
1349 if (NULL == filename)
1352 return GNUNET_SYSERR;
1354 if (0 != LSTAT (filename, &istat))
1355 return GNUNET_NO; /* file may not exist... */
1356 (void) CHMOD (filename,
1357 S_IWUSR | S_IRUSR | S_IXUSR);
1358 if (0 == UNLINK (filename))
1360 if ( (errno != EISDIR) &&
1361 /* EISDIR is not sufficient in all cases, e.g.
1362 * sticky /tmp directory may result in EPERM on BSD.
1363 * So we also explicitly check "isDirectory" */
1365 GNUNET_DISK_directory_test (filename,
1368 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING,
1371 return GNUNET_SYSERR;
1373 if (GNUNET_SYSERR ==
1374 GNUNET_DISK_directory_scan (filename,
1377 return GNUNET_SYSERR;
1378 if (0 != RMDIR (filename))
1380 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING,
1383 return GNUNET_SYSERR;
1392 * @param src file to copy
1393 * @param dst destination file name
1394 * @return #GNUNET_OK on success, #GNUNET_SYSERR on error
1397 GNUNET_DISK_file_copy (const char *src,
1405 struct GNUNET_DISK_FileHandle *in;
1406 struct GNUNET_DISK_FileHandle *out;
1409 GNUNET_DISK_file_size (src,
1414 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR,
1417 return GNUNET_SYSERR;
1420 in = GNUNET_DISK_file_open (src,
1421 GNUNET_DISK_OPEN_READ,
1422 GNUNET_DISK_PERM_NONE);
1425 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR,
1428 return GNUNET_SYSERR;
1431 GNUNET_DISK_file_open (dst,
1432 GNUNET_DISK_OPEN_WRITE | GNUNET_DISK_OPEN_CREATE |
1433 GNUNET_DISK_OPEN_FAILIFEXISTS,
1434 GNUNET_DISK_PERM_USER_READ |
1435 GNUNET_DISK_PERM_USER_WRITE |
1436 GNUNET_DISK_PERM_GROUP_READ |
1437 GNUNET_DISK_PERM_GROUP_WRITE);
1440 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR,
1443 GNUNET_DISK_file_close (in);
1444 return GNUNET_SYSERR;
1446 buf = GNUNET_malloc (COPY_BLK_SIZE);
1449 len = COPY_BLK_SIZE;
1450 if (len > size - pos)
1452 sret = GNUNET_DISK_file_read (in,
1456 (len != (size_t) sret) )
1458 sret = GNUNET_DISK_file_write (out,
1462 (len != (size_t) sret) )
1467 GNUNET_DISK_file_close (in);
1468 GNUNET_DISK_file_close (out);
1472 GNUNET_DISK_file_close (in);
1473 GNUNET_DISK_file_close (out);
1474 return GNUNET_SYSERR;
1479 * @brief Removes special characters as ':' from a filename.
1480 * @param fn the filename to canonicalize
1483 GNUNET_DISK_filename_canonicalize (char *fn)
1488 for (idx = fn; *idx; idx++)
1492 if (c == '/' || c == '\\' || c == ':' ||
1493 c == '*' || c == '?' || c == '"' ||
1494 c == '<' || c == '>' || c == '|')
1504 * @brief Change owner of a file
1506 * @param filename name of file to change the owner of
1507 * @param user name of the new owner
1508 * @return #GNUNET_OK on success, #GNUNET_SYSERR on failure
1511 GNUNET_DISK_file_change_owner (const char *filename,
1517 pws = getpwnam (user);
1520 LOG (GNUNET_ERROR_TYPE_ERROR,
1521 _("Cannot obtain information about user `%s': %s\n"),
1524 return GNUNET_SYSERR;
1526 if (0 != chown (filename,
1530 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING,
1533 return GNUNET_SYSERR;
1541 * Lock a part of a file
1543 * @param fh file handle
1544 * @param lock_start absolute position from where to lock
1545 * @param lock_end absolute position until where to lock
1546 * @param excl #GNUNET_YES for an exclusive lock
1547 * @return #GNUNET_OK on success, #GNUNET_SYSERR on error
1550 GNUNET_DISK_file_lock (struct GNUNET_DISK_FileHandle *fh,
1558 return GNUNET_SYSERR;
1564 memset (&fl, 0, sizeof (struct flock));
1565 fl.l_type = excl ? F_WRLCK : F_RDLCK;
1566 fl.l_whence = SEEK_SET;
1567 fl.l_start = lock_start;
1568 fl.l_len = lock_end;
1570 return fcntl (fh->fd, F_SETLK, &fl) != 0 ? GNUNET_SYSERR : GNUNET_OK;
1573 off_t diff = lock_end - lock_start;
1574 DWORD diff_low, diff_high;
1575 diff_low = (DWORD) (diff & 0xFFFFFFFF);
1576 diff_high = (DWORD) ((diff >> (sizeof (DWORD) * 8)) & 0xFFFFFFFF);
1578 memset (&o, 0, sizeof (OVERLAPPED));
1579 o.Offset = (DWORD) (lock_start & 0xFFFFFFFF);;
1580 o.OffsetHigh = (DWORD) (((lock_start & ~0xFFFFFFFF) >> (sizeof (DWORD) * 8)) & 0xFFFFFFFF);
1583 (fh->h, (excl ? LOCKFILE_EXCLUSIVE_LOCK : 0) | LOCKFILE_FAIL_IMMEDIATELY,
1584 0, diff_low, diff_high, &o))
1586 SetErrnoFromWinError (GetLastError ());
1587 return GNUNET_SYSERR;
1596 * Unlock a part of a file
1598 * @param fh file handle
1599 * @param unlock_start absolute position from where to unlock
1600 * @param unlock_end absolute position until where to unlock
1601 * @return #GNUNET_OK on success, #GNUNET_SYSERR on error
1604 GNUNET_DISK_file_unlock (struct GNUNET_DISK_FileHandle *fh,
1611 return GNUNET_SYSERR;
1617 memset (&fl, 0, sizeof (struct flock));
1618 fl.l_type = F_UNLCK;
1619 fl.l_whence = SEEK_SET;
1620 fl.l_start = unlock_start;
1621 fl.l_len = unlock_end;
1623 return fcntl (fh->fd, F_SETLK, &fl) != 0 ? GNUNET_SYSERR : GNUNET_OK;
1626 off_t diff = unlock_end - unlock_start;
1627 DWORD diff_low, diff_high;
1628 diff_low = (DWORD) (diff & 0xFFFFFFFF);
1629 diff_high = (DWORD) ((diff >> (sizeof (DWORD) * 8)) & 0xFFFFFFFF);
1631 memset (&o, 0, sizeof (OVERLAPPED));
1632 o.Offset = (DWORD) (unlock_start & 0xFFFFFFFF);;
1633 o.OffsetHigh = (DWORD) (((unlock_start & ~0xFFFFFFFF) >> (sizeof (DWORD) * 8)) & 0xFFFFFFFF);
1635 if (!UnlockFileEx (fh->h, 0, diff_low, diff_high, &o))
1637 SetErrnoFromWinError (GetLastError ());
1638 return GNUNET_SYSERR;
1647 * Open a file. Note that the access permissions will only be
1648 * used if a new file is created and if the underlying operating
1649 * system supports the given permissions.
1651 * @param fn file name to be opened
1652 * @param flags opening flags, a combination of GNUNET_DISK_OPEN_xxx bit flags
1653 * @param perm permissions for the newly created file, use
1654 * #GNUNET_DISK_PERM_NONE if a file could not be created by this
1655 * call (because of flags)
1656 * @return IO handle on success, NULL on error
1658 struct GNUNET_DISK_FileHandle *
1659 GNUNET_DISK_file_open (const char *fn,
1660 enum GNUNET_DISK_OpenFlags flags,
1661 enum GNUNET_DISK_AccessPermissions perm)
1664 struct GNUNET_DISK_FileHandle *ret;
1670 wchar_t wexpfn[MAX_PATH + 1];
1677 expfn = GNUNET_STRINGS_filename_expand (fn);
1682 if (GNUNET_DISK_OPEN_READWRITE == (flags & GNUNET_DISK_OPEN_READWRITE))
1683 oflags = O_RDWR; /* note: O_RDWR is NOT always O_RDONLY | O_WRONLY */
1684 else if (flags & GNUNET_DISK_OPEN_READ)
1686 else if (flags & GNUNET_DISK_OPEN_WRITE)
1691 GNUNET_free (expfn);
1694 if (flags & GNUNET_DISK_OPEN_FAILIFEXISTS)
1695 oflags |= (O_CREAT | O_EXCL);
1696 if (flags & GNUNET_DISK_OPEN_TRUNCATE)
1698 if (flags & GNUNET_DISK_OPEN_APPEND)
1700 if(GNUNET_NO == GNUNET_DISK_file_test(fn))
1702 if (flags & GNUNET_DISK_OPEN_CREATE )
1704 (void) GNUNET_DISK_directory_create_for_file (expfn);
1706 mode = translate_unix_perms (perm);
1710 fd = open (expfn, oflags
1714 | O_LARGEFILE, mode);
1717 if (0 == (flags & GNUNET_DISK_OPEN_FAILIFEXISTS))
1718 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "open", expfn);
1720 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_DEBUG, "open", expfn);
1721 GNUNET_free (expfn);
1728 if (GNUNET_DISK_OPEN_READWRITE == (flags & GNUNET_DISK_OPEN_READWRITE))
1729 access = FILE_READ_DATA | FILE_WRITE_DATA;
1730 else if (flags & GNUNET_DISK_OPEN_READ)
1731 access = FILE_READ_DATA;
1732 else if (flags & GNUNET_DISK_OPEN_WRITE)
1733 access = FILE_WRITE_DATA;
1735 if (flags & GNUNET_DISK_OPEN_FAILIFEXISTS)
1739 else if (flags & GNUNET_DISK_OPEN_CREATE)
1741 (void) GNUNET_DISK_directory_create_for_file (expfn);
1742 if (flags & GNUNET_DISK_OPEN_TRUNCATE)
1743 disp = CREATE_ALWAYS;
1747 else if (flags & GNUNET_DISK_OPEN_TRUNCATE)
1749 disp = TRUNCATE_EXISTING;
1753 disp = OPEN_EXISTING;
1756 if (ERROR_SUCCESS == plibc_conv_to_win_pathwconv(expfn, wexpfn))
1757 h = CreateFileW (wexpfn, access,
1758 FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE, NULL,
1759 disp, FILE_ATTRIBUTE_NORMAL, NULL);
1761 h = INVALID_HANDLE_VALUE;
1762 if (h == INVALID_HANDLE_VALUE)
1765 SetErrnoFromWinError (GetLastError ());
1767 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_INFO, "open", expfn);
1768 GNUNET_free (expfn);
1773 if (flags & GNUNET_DISK_OPEN_APPEND)
1774 if (SetFilePointer (h, 0, 0, FILE_END) == INVALID_SET_FILE_POINTER)
1776 SetErrnoFromWinError (GetLastError ());
1777 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "SetFilePointer", expfn);
1779 GNUNET_free (expfn);
1784 ret = GNUNET_new (struct GNUNET_DISK_FileHandle);
1787 ret->type = GNUNET_DISK_HANLDE_TYPE_FILE;
1791 GNUNET_free (expfn);
1797 * Close an open file.
1799 * @param h file handle
1800 * @return #GNUNET_OK on success, #GNUNET_SYSERR otherwise
1803 GNUNET_DISK_file_close (struct GNUNET_DISK_FileHandle *h)
1809 return GNUNET_SYSERR;
1815 if (! CloseHandle (h->h))
1817 SetErrnoFromWinError (GetLastError ());
1818 LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING, "close");
1819 ret = GNUNET_SYSERR;
1821 if (h->oOverlapRead)
1823 if (! CloseHandle (h->oOverlapRead->hEvent))
1825 SetErrnoFromWinError (GetLastError ());
1826 LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING, "close");
1827 ret = GNUNET_SYSERR;
1829 GNUNET_free (h->oOverlapRead);
1831 if (h->oOverlapWrite)
1833 if (!CloseHandle (h->oOverlapWrite->hEvent))
1835 SetErrnoFromWinError (GetLastError ());
1836 LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING, "close");
1837 ret = GNUNET_SYSERR;
1839 GNUNET_free (h->oOverlapWrite);
1842 if (close (h->fd) != 0)
1844 LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING, "close");
1845 ret = GNUNET_SYSERR;
1855 * Get a GNUnet file handle from a W32 handle.
1857 * @param handle native handle
1858 * @return GNUnet file handle corresponding to the W32 handle
1860 struct GNUNET_DISK_FileHandle *
1861 GNUNET_DISK_get_handle_from_w32_handle (HANDLE osfh)
1863 struct GNUNET_DISK_FileHandle *fh;
1865 enum GNUNET_FILE_Type ftype;
1867 dwret = GetFileType (osfh);
1870 case FILE_TYPE_DISK:
1871 ftype = GNUNET_DISK_HANLDE_TYPE_FILE;
1873 case FILE_TYPE_PIPE:
1874 ftype = GNUNET_DISK_HANLDE_TYPE_PIPE;
1876 case FILE_TYPE_UNKNOWN:
1877 if ( (GetLastError () == NO_ERROR) ||
1878 (GetLastError () == ERROR_INVALID_HANDLE) )
1880 if (0 != ResetEvent (osfh))
1881 ftype = GNUNET_DISK_HANLDE_TYPE_EVENT;
1892 fh = GNUNET_new (struct GNUNET_DISK_FileHandle);
1896 if (ftype == GNUNET_DISK_HANLDE_TYPE_PIPE)
1899 * Note that we can't make it overlapped if it isn't already.
1900 * (ReOpenFile() is only available in 2003/Vista).
1901 * The process that opened this file in the first place (usually a parent
1902 * process, if this is stdin/stdout/stderr) must make it overlapped,
1903 * otherwise we're screwed, as selecting on non-overlapped handle
1906 fh->oOverlapRead = GNUNET_new (OVERLAPPED);
1907 fh->oOverlapWrite = GNUNET_new (OVERLAPPED);
1908 fh->oOverlapRead->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
1909 fh->oOverlapWrite->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
1917 * Get a handle from a native integer FD.
1919 * @param fno native integer file descriptor
1920 * @return file handle corresponding to the descriptor, NULL on error
1922 struct GNUNET_DISK_FileHandle *
1923 GNUNET_DISK_get_handle_from_int_fd (int fno)
1925 struct GNUNET_DISK_FileHandle *fh;
1927 if ( (((off_t) -1) == lseek (fno, 0, SEEK_CUR)) &&
1929 return NULL; /* invalid FD */
1932 fh = GNUNET_new (struct GNUNET_DISK_FileHandle);
1938 osfh = _get_osfhandle (fno);
1939 if (INVALID_HANDLE_VALUE == (HANDLE) osfh)
1942 fh = GNUNET_DISK_get_handle_from_w32_handle ((HANDLE) osfh);
1950 * Get a handle from a native streaming FD.
1952 * @param fd native streaming file descriptor
1953 * @return file handle corresponding to the descriptor
1955 struct GNUNET_DISK_FileHandle *
1956 GNUNET_DISK_get_handle_from_native (FILE *fd)
1964 return GNUNET_DISK_get_handle_from_int_fd (fno);
1969 * Handle for a memory-mapping operation.
1971 struct GNUNET_DISK_MapHandle
1974 * Address where the map is in memory.
1980 * Underlying OS handle.
1985 * Number of bytes mapped.
1993 #define MAP_FAILED ((void *) -1)
1997 * Map a file into memory
1999 * @param h open file handle
2000 * @param m handle to the new mapping
2001 * @param access access specification, GNUNET_DISK_MAP_TYPE_xxx
2002 * @param len size of the mapping
2003 * @return pointer to the mapped memory region, NULL on failure
2006 GNUNET_DISK_file_map (const struct GNUNET_DISK_FileHandle *h,
2007 struct GNUNET_DISK_MapHandle **m,
2008 enum GNUNET_DISK_MapType access, size_t len)
2017 DWORD mapAccess, protect;
2019 if ((access & GNUNET_DISK_MAP_TYPE_READ) &&
2020 (access & GNUNET_DISK_MAP_TYPE_WRITE))
2022 protect = PAGE_READWRITE;
2023 mapAccess = FILE_MAP_ALL_ACCESS;
2025 else if (access & GNUNET_DISK_MAP_TYPE_READ)
2027 protect = PAGE_READONLY;
2028 mapAccess = FILE_MAP_READ;
2030 else if (access & GNUNET_DISK_MAP_TYPE_WRITE)
2032 protect = PAGE_READWRITE;
2033 mapAccess = FILE_MAP_WRITE;
2041 *m = GNUNET_new (struct GNUNET_DISK_MapHandle);
2042 (*m)->h = CreateFileMapping (h->h, NULL, protect, 0, 0, NULL);
2043 if ((*m)->h == INVALID_HANDLE_VALUE)
2045 SetErrnoFromWinError (GetLastError ());
2050 (*m)->addr = MapViewOfFile ((*m)->h, mapAccess, 0, 0, len);
2053 SetErrnoFromWinError (GetLastError ());
2054 CloseHandle ((*m)->h);
2063 if (access & GNUNET_DISK_MAP_TYPE_READ)
2065 if (access & GNUNET_DISK_MAP_TYPE_WRITE)
2067 *m = GNUNET_new (struct GNUNET_DISK_MapHandle);
2068 (*m)->addr = mmap (NULL, len, prot, MAP_SHARED, h->fd, 0);
2069 GNUNET_assert (NULL != (*m)->addr);
2070 if (MAP_FAILED == (*m)->addr)
2082 * @param h mapping handle
2083 * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
2086 GNUNET_DISK_file_unmap (struct GNUNET_DISK_MapHandle *h)
2093 return GNUNET_SYSERR;
2097 ret = UnmapViewOfFile (h->addr) ? GNUNET_OK : GNUNET_SYSERR;
2098 if (ret != GNUNET_OK)
2099 SetErrnoFromWinError (GetLastError ());
2100 if (!CloseHandle (h->h) && (ret == GNUNET_OK))
2102 ret = GNUNET_SYSERR;
2103 SetErrnoFromWinError (GetLastError ());
2106 ret = munmap (h->addr, h->len) != -1 ? GNUNET_OK : GNUNET_SYSERR;
2114 * Write file changes to disk
2115 * @param h handle to an open file
2116 * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
2119 GNUNET_DISK_file_sync (const struct GNUNET_DISK_FileHandle *h)
2124 return GNUNET_SYSERR;
2130 ret = FlushFileBuffers (h->h) ? GNUNET_OK : GNUNET_SYSERR;
2131 if (ret != GNUNET_OK)
2132 SetErrnoFromWinError (GetLastError ());
2134 #elif defined(FREEBSD) || defined(OPENBSD) || defined(DARWIN)
2135 return fsync (h->fd) == -1 ? GNUNET_SYSERR : GNUNET_OK;
2137 return fdatasync (h->fd) == -1 ? GNUNET_SYSERR : GNUNET_OK;
2144 #define PIPE_BUF 512
2146 /* Copyright Bob Byrnes <byrnes <at> curl.com>
2147 http://permalink.gmane.org/gmane.os.cygwin.patches/2121
2149 /* Create a pipe, and return handles to the read and write ends,
2150 just like CreatePipe, but ensure that the write end permits
2151 FILE_READ_ATTRIBUTES access, on later versions of win32 where
2152 this is supported. This access is needed by NtQueryInformationFile,
2153 which is used to implement select and nonblocking writes.
2154 Note that the return value is either NO_ERROR or GetLastError,
2155 unlike CreatePipe, which returns a bool for success or failure. */
2157 create_selectable_pipe (PHANDLE read_pipe_ptr, PHANDLE write_pipe_ptr,
2158 LPSECURITY_ATTRIBUTES sa_ptr, DWORD psize,
2159 DWORD dwReadMode, DWORD dwWriteMode)
2161 /* Default to error. */
2162 *read_pipe_ptr = *write_pipe_ptr = INVALID_HANDLE_VALUE;
2167 /* Ensure that there is enough pipe buffer space for atomic writes. */
2168 if (psize < PIPE_BUF)
2171 char pipename[MAX_PATH];
2173 /* Retry CreateNamedPipe as long as the pipe name is in use.
2174 * Retrying will probably never be necessary, but we want
2175 * to be as robust as possible. */
2178 static volatile LONG pipe_unique_id;
2180 snprintf (pipename, sizeof pipename, "\\\\.\\pipe\\gnunet-%d-%ld",
2181 getpid (), InterlockedIncrement ((LONG *) & pipe_unique_id));
2182 LOG (GNUNET_ERROR_TYPE_DEBUG, "CreateNamedPipe: name = %s, size = %lu\n",
2184 /* Use CreateNamedPipe instead of CreatePipe, because the latter
2185 * returns a write handle that does not permit FILE_READ_ATTRIBUTES
2186 * access, on versions of win32 earlier than WinXP SP2.
2187 * CreatePipe also stupidly creates a full duplex pipe, which is
2188 * a waste, since only a single direction is actually used.
2189 * It's important to only allow a single instance, to ensure that
2190 * the pipe was not created earlier by some other process, even if
2191 * the pid has been reused. */
2192 read_pipe = CreateNamedPipeA (pipename, PIPE_ACCESS_INBOUND | FILE_FLAG_FIRST_PIPE_INSTANCE | dwReadMode, PIPE_TYPE_BYTE | PIPE_READMODE_BYTE, 1, /* max instances */
2193 psize, /* output buffer size */
2194 psize, /* input buffer size */
2195 NMPWAIT_USE_DEFAULT_WAIT, sa_ptr);
2197 if (read_pipe != INVALID_HANDLE_VALUE)
2199 LOG (GNUNET_ERROR_TYPE_DEBUG, "pipe read handle = %p\n", read_pipe);
2203 DWORD err = GetLastError ();
2207 case ERROR_PIPE_BUSY:
2208 /* The pipe is already open with compatible parameters.
2209 * Pick a new name and retry. */
2210 LOG (GNUNET_ERROR_TYPE_DEBUG, "pipe busy, retrying\n");
2212 case ERROR_ACCESS_DENIED:
2213 /* The pipe is already open with incompatible parameters.
2214 * Pick a new name and retry. */
2215 LOG (GNUNET_ERROR_TYPE_DEBUG, "pipe access denied, retrying\n");
2217 case ERROR_CALL_NOT_IMPLEMENTED:
2218 /* We are on an older Win9x platform without named pipes.
2219 * Return an anonymous pipe as the best approximation. */
2220 LOG (GNUNET_ERROR_TYPE_DEBUG,
2221 "CreateNamedPipe not implemented, resorting to "
2222 "CreatePipe: size = %lu\n", psize);
2223 if (CreatePipe (read_pipe_ptr, write_pipe_ptr, sa_ptr, psize))
2225 LOG (GNUNET_ERROR_TYPE_DEBUG, "pipe read handle = %p, write handle = %p\n",
2230 err = GetLastError ();
2231 LOG (GNUNET_ERROR_TYPE_ERROR, "CreatePipe failed: %d\n", err);
2234 LOG (GNUNET_ERROR_TYPE_ERROR, "CreateNamedPipe failed: %d\n", err);
2239 LOG (GNUNET_ERROR_TYPE_DEBUG, "CreateFile: name = %s\n", pipename);
2241 /* Open the named pipe for writing.
2242 * Be sure to permit FILE_READ_ATTRIBUTES access. */
2243 write_pipe = CreateFileA (pipename, GENERIC_WRITE | FILE_READ_ATTRIBUTES, 0, /* share mode */
2244 sa_ptr, OPEN_EXISTING, dwWriteMode, /* flags and attributes */
2245 0); /* handle to template file */
2247 if (write_pipe == INVALID_HANDLE_VALUE)
2250 DWORD err = GetLastError ();
2252 LOG (GNUNET_ERROR_TYPE_DEBUG, "CreateFile failed: %d\n", err);
2253 CloseHandle (read_pipe);
2256 LOG (GNUNET_ERROR_TYPE_DEBUG, "pipe write handle = %p\n", write_pipe);
2258 *read_pipe_ptr = read_pipe;
2259 *write_pipe_ptr = write_pipe;
2266 * Creates an interprocess channel
2268 * @param blocking_read creates an asynchronous pipe for reading if set to GNUNET_NO
2269 * @param blocking_write creates an asynchronous pipe for writing if set to GNUNET_NO
2270 * @param inherit_read inherit the parent processes stdin (only for windows)
2271 * @param inherit_write inherit the parent processes stdout (only for windows)
2272 * @return handle to the new pipe, NULL on error
2274 struct GNUNET_DISK_PipeHandle *
2275 GNUNET_DISK_pipe (int blocking_read,
2285 (void) inherit_read;
2286 (void) inherit_write;
2291 LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR,
2296 return GNUNET_DISK_pipe_from_fd (blocking_read,
2300 struct GNUNET_DISK_PipeHandle *p;
2305 p = GNUNET_new (struct GNUNET_DISK_PipeHandle);
2306 p->fd[0] = GNUNET_new (struct GNUNET_DISK_FileHandle);
2307 p->fd[1] = GNUNET_new (struct GNUNET_DISK_FileHandle);
2309 /* All pipes are overlapped. If you want them to block - just
2310 * call WriteFile() and ReadFile() with NULL overlapped pointer.
2311 * NOTE: calling with NULL overlapped pointer works only
2312 * for pipes, and doesn't seem to be a documented feature.
2313 * It will NOT work for files, because overlapped files need
2314 * to read offsets from the overlapped structure, regardless.
2315 * Pipes are not seekable, and need no offsets, which is
2316 * probably why it works for them.
2319 create_selectable_pipe (&p->fd[0]->h, &p->fd[1]->h, NULL, 0,
2320 FILE_FLAG_OVERLAPPED,
2321 FILE_FLAG_OVERLAPPED);
2324 SetErrnoFromWinError (GetLastError ());
2326 GNUNET_free (p->fd[0]);
2327 GNUNET_free (p->fd[1]);
2332 if (!DuplicateHandle
2333 (GetCurrentProcess (), p->fd[0]->h, GetCurrentProcess (), &tmp_handle, 0,
2334 inherit_read == GNUNET_YES ? TRUE : FALSE, DUPLICATE_SAME_ACCESS))
2336 SetErrnoFromWinError (GetLastError ());
2338 CloseHandle (p->fd[0]->h);
2339 CloseHandle (p->fd[1]->h);
2340 GNUNET_free (p->fd[0]);
2341 GNUNET_free (p->fd[1]);
2346 CloseHandle (p->fd[0]->h);
2347 p->fd[0]->h = tmp_handle;
2349 if (!DuplicateHandle
2350 (GetCurrentProcess (), p->fd[1]->h, GetCurrentProcess (), &tmp_handle, 0,
2351 inherit_write == GNUNET_YES ? TRUE : FALSE, DUPLICATE_SAME_ACCESS))
2353 SetErrnoFromWinError (GetLastError ());
2355 CloseHandle (p->fd[0]->h);
2356 CloseHandle (p->fd[1]->h);
2357 GNUNET_free (p->fd[0]);
2358 GNUNET_free (p->fd[1]);
2363 CloseHandle (p->fd[1]->h);
2364 p->fd[1]->h = tmp_handle;
2366 p->fd[0]->type = GNUNET_DISK_HANLDE_TYPE_PIPE;
2367 p->fd[1]->type = GNUNET_DISK_HANLDE_TYPE_PIPE;
2369 p->fd[0]->oOverlapRead = GNUNET_new (OVERLAPPED);
2370 p->fd[0]->oOverlapWrite = GNUNET_new (OVERLAPPED);
2371 p->fd[1]->oOverlapRead = GNUNET_new (OVERLAPPED);
2372 p->fd[1]->oOverlapWrite = GNUNET_new (OVERLAPPED);
2374 p->fd[0]->oOverlapRead->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2375 p->fd[0]->oOverlapWrite->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2377 p->fd[1]->oOverlapRead->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2378 p->fd[1]->oOverlapWrite->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2386 * Creates a pipe object from a couple of file descriptors.
2387 * Useful for wrapping existing pipe FDs.
2389 * @param blocking_read creates an asynchronous pipe for reading if set to GNUNET_NO
2390 * @param blocking_write creates an asynchronous pipe for writing if set to GNUNET_NO
2391 * @param fd an array of two fd values. One of them may be -1 for read-only or write-only pipes
2393 * @return handle to the new pipe, NULL on error
2395 struct GNUNET_DISK_PipeHandle *
2396 GNUNET_DISK_pipe_from_fd (int blocking_read, int blocking_write, int fd[2])
2398 struct GNUNET_DISK_PipeHandle *p;
2400 p = GNUNET_new (struct GNUNET_DISK_PipeHandle);
2405 int eno = 0; /* make gcc happy */
2410 p->fd[0] = GNUNET_new (struct GNUNET_DISK_FileHandle);
2411 p->fd[0]->fd = fd[0];
2414 flags = fcntl (fd[0], F_GETFL);
2415 flags |= O_NONBLOCK;
2416 if (0 > fcntl (fd[0], F_SETFL, flags))
2422 flags = fcntl (fd[0], F_GETFD);
2423 flags |= FD_CLOEXEC;
2424 if (0 > fcntl (fd[0], F_SETFD, flags))
2433 p->fd[1] = GNUNET_new (struct GNUNET_DISK_FileHandle);
2434 p->fd[1]->fd = fd[1];
2435 if (!blocking_write)
2437 flags = fcntl (fd[1], F_GETFL);
2438 flags |= O_NONBLOCK;
2439 if (0 > fcntl (fd[1], F_SETFL, flags))
2445 flags = fcntl (fd[1], F_GETFD);
2446 flags |= FD_CLOEXEC;
2447 if (0 > fcntl (fd[1], F_SETFD, flags))
2456 LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR, "fcntl");
2457 if (p->fd[0]->fd >= 0)
2458 GNUNET_break (0 == close (p->fd[0]->fd));
2459 if (p->fd[1]->fd >= 0)
2460 GNUNET_break (0 == close (p->fd[1]->fd));
2461 GNUNET_free_non_null (p->fd[0]);
2462 GNUNET_free_non_null (p->fd[1]);
2470 p->fd[0] = GNUNET_new (struct GNUNET_DISK_FileHandle);
2471 p->fd[0]->h = (HANDLE) _get_osfhandle (fd[0]);
2472 if (p->fd[0]->h != INVALID_HANDLE_VALUE)
2474 p->fd[0]->type = GNUNET_DISK_HANLDE_TYPE_PIPE;
2475 p->fd[0]->oOverlapRead = GNUNET_new (OVERLAPPED);
2476 p->fd[0]->oOverlapWrite = GNUNET_new (OVERLAPPED);
2477 p->fd[0]->oOverlapRead->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2478 p->fd[0]->oOverlapWrite->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2482 GNUNET_free (p->fd[0]);
2488 p->fd[1] = GNUNET_new (struct GNUNET_DISK_FileHandle);
2489 p->fd[1]->h = (HANDLE) _get_osfhandle (fd[1]);
2490 if (p->fd[1]->h != INVALID_HANDLE_VALUE)
2492 p->fd[1]->type = GNUNET_DISK_HANLDE_TYPE_PIPE;
2493 p->fd[1]->oOverlapRead = GNUNET_new (OVERLAPPED);
2494 p->fd[1]->oOverlapWrite = GNUNET_new (OVERLAPPED);
2495 p->fd[1]->oOverlapRead->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2496 p->fd[1]->oOverlapWrite->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2500 GNUNET_free (p->fd[1]);
2511 * Closes an interprocess channel
2513 * @param p pipe to close
2514 * @param end which end of the pipe to close
2515 * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
2518 GNUNET_DISK_pipe_close_end (struct GNUNET_DISK_PipeHandle *p,
2519 enum GNUNET_DISK_PipeEnd end)
2521 int ret = GNUNET_OK;
2523 if (end == GNUNET_DISK_PIPE_END_READ)
2527 ret = GNUNET_DISK_file_close (p->fd[0]);
2531 else if (end == GNUNET_DISK_PIPE_END_WRITE)
2535 ret = GNUNET_DISK_file_close (p->fd[1]);
2544 * Detaches one of the ends from the pipe.
2545 * Detached end is a fully-functional FileHandle, it will
2546 * not be affected by anything you do with the pipe afterwards.
2547 * Each end of a pipe can only be detched from it once (i.e.
2548 * it is not duplicated).
2550 * @param p pipe to detach an end from
2551 * @param end which end of the pipe to detach
2552 * @return Detached end on success, NULL on failure
2553 * (or if that end is not present or is closed).
2555 struct GNUNET_DISK_FileHandle *
2556 GNUNET_DISK_pipe_detach_end (struct GNUNET_DISK_PipeHandle *p,
2557 enum GNUNET_DISK_PipeEnd end)
2559 struct GNUNET_DISK_FileHandle *ret = NULL;
2561 if (end == GNUNET_DISK_PIPE_END_READ)
2569 else if (end == GNUNET_DISK_PIPE_END_WRITE)
2583 * Closes an interprocess channel
2585 * @param p pipe to close
2586 * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
2589 GNUNET_DISK_pipe_close (struct GNUNET_DISK_PipeHandle *p)
2591 int ret = GNUNET_OK;
2594 int write_end_close;
2595 int read_end_close_errno;
2596 int write_end_close_errno;
2598 read_end_close = GNUNET_DISK_pipe_close_end (p, GNUNET_DISK_PIPE_END_READ);
2599 read_end_close_errno = errno;
2600 write_end_close = GNUNET_DISK_pipe_close_end (p, GNUNET_DISK_PIPE_END_WRITE);
2601 write_end_close_errno = errno;
2604 if (GNUNET_OK != read_end_close)
2606 errno = read_end_close_errno;
2607 ret = read_end_close;
2609 else if (GNUNET_OK != write_end_close)
2611 errno = write_end_close_errno;
2612 ret = write_end_close;
2620 * Get the handle to a particular pipe end
2623 * @param n end to access
2624 * @return handle for the respective end
2626 const struct GNUNET_DISK_FileHandle *
2627 GNUNET_DISK_pipe_handle (const struct GNUNET_DISK_PipeHandle *p,
2628 enum GNUNET_DISK_PipeEnd n)
2632 case GNUNET_DISK_PIPE_END_READ:
2633 case GNUNET_DISK_PIPE_END_WRITE:
2643 * Retrieve OS file handle
2645 * @param fh GNUnet file descriptor
2646 * @param dst destination buffer
2647 * @param dst_len length of dst
2648 * @return #GNUNET_OK on success, #GNUNET_SYSERR otherwise
2651 GNUNET_DISK_internal_file_handle_ (const struct GNUNET_DISK_FileHandle *fh,
2652 void *dst, size_t dst_len)
2655 return GNUNET_SYSERR;
2657 if (dst_len < sizeof (HANDLE))
2658 return GNUNET_SYSERR;
2659 *((HANDLE *) dst) = fh->h;
2661 if (dst_len < sizeof (int))
2662 return GNUNET_SYSERR;
2663 *((int *) dst) = fh->fd;
2671 * Remove the directory given under @a option in
2672 * section [PATHS] in configuration under @a cfg_filename
2674 * @param cfg_filename configuration file to parse
2675 * @param option option with the dir name to purge
2678 GNUNET_DISK_purge_cfg_dir (const char *cfg_filename,
2681 struct GNUNET_CONFIGURATION_Handle *cfg;
2684 cfg = GNUNET_CONFIGURATION_create ();
2686 GNUNET_CONFIGURATION_load (cfg,
2690 GNUNET_CONFIGURATION_destroy (cfg);
2694 GNUNET_CONFIGURATION_get_value_filename (cfg,
2699 GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
2702 GNUNET_CONFIGURATION_destroy (cfg);
2705 GNUNET_CONFIGURATION_destroy (cfg);
2706 if (GNUNET_SYSERR ==
2707 GNUNET_DISK_directory_remove (tmpname))
2709 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR,
2712 GNUNET_free (tmpname);
2715 GNUNET_free (tmpname);