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;
834 while ((len > 0) && (rdir[len] != DIR_SEPARATOR))
837 /* The empty path is invalid and in this case refers to / */
841 rdir = GNUNET_strdup ("/");
843 ret = GNUNET_DISK_directory_create (rdir);
844 if ((GNUNET_OK == ret) && (0 != ACCESS (rdir, W_OK)))
854 * Read the contents of a binary file into a buffer.
856 * @param h handle to an open file
857 * @param result the buffer to write the result to
858 * @param len the maximum number of bytes to read
859 * @return the number of bytes read on success, #GNUNET_SYSERR on failure
862 GNUNET_DISK_file_read (const struct GNUNET_DISK_FileHandle *h,
869 return GNUNET_SYSERR;
875 if (h->type == GNUNET_DISK_HANLDE_TYPE_FILE)
877 if (!ReadFile (h->h, result, len, &bytes_read, NULL))
879 SetErrnoFromWinError (GetLastError ());
880 return GNUNET_SYSERR;
883 else if (h->type == GNUNET_DISK_HANLDE_TYPE_PIPE)
885 if (!ReadFile (h->h, result, len, &bytes_read, h->oOverlapRead))
887 if (GetLastError () != ERROR_IO_PENDING)
889 LOG (GNUNET_ERROR_TYPE_DEBUG,
890 "Error reading from pipe: %u\n",
892 SetErrnoFromWinError (GetLastError ());
893 return GNUNET_SYSERR;
895 LOG (GNUNET_ERROR_TYPE_DEBUG, "Will get overlapped result\n");
896 GetOverlappedResult (h->h, h->oOverlapRead, &bytes_read, TRUE);
898 LOG (GNUNET_ERROR_TYPE_DEBUG, "Read %u bytes from pipe\n", bytes_read);
906 return read (h->fd, result, len);
912 * Read the contents of a binary file into a buffer.
913 * Guarantees not to block (returns GNUNET_SYSERR and sets errno to EAGAIN
914 * when no data can be read).
916 * @param h handle to an open file
917 * @param result the buffer to write the result to
918 * @param len the maximum number of bytes to read
919 * @return the number of bytes read on success, #GNUNET_SYSERR on failure
922 GNUNET_DISK_file_read_non_blocking (const struct GNUNET_DISK_FileHandle *h,
929 return GNUNET_SYSERR;
935 if (h->type == GNUNET_DISK_HANLDE_TYPE_FILE)
937 if (!ReadFile (h->h, result, len, &bytes_read, NULL))
939 SetErrnoFromWinError (GetLastError ());
940 return GNUNET_SYSERR;
943 else if (h->type == GNUNET_DISK_HANLDE_TYPE_PIPE)
945 if (!ReadFile (h->h, result, len, &bytes_read, h->oOverlapRead))
947 if (GetLastError () != ERROR_IO_PENDING)
949 LOG (GNUNET_ERROR_TYPE_DEBUG, "Error reading from pipe: %u\n", GetLastError ());
950 SetErrnoFromWinError (GetLastError ());
951 return GNUNET_SYSERR;
955 LOG (GNUNET_ERROR_TYPE_DEBUG,
956 "ReadFile() queued a read, cancelling\n");
959 return GNUNET_SYSERR;
962 LOG (GNUNET_ERROR_TYPE_DEBUG,
975 /* set to non-blocking, read, then set back */
976 flags = fcntl (h->fd, F_GETFL);
977 if (0 == (flags & O_NONBLOCK))
978 (void) fcntl (h->fd, F_SETFL, flags | O_NONBLOCK);
979 ret = read (h->fd, result, len);
980 if (0 == (flags & O_NONBLOCK))
983 (void) fcntl (h->fd, F_SETFL, flags);
992 * Read the contents of a binary file into a buffer.
994 * @param fn file name
995 * @param result the buffer to write the result to
996 * @param len the maximum number of bytes to read
997 * @return number of bytes read, #GNUNET_SYSERR on failure
1000 GNUNET_DISK_fn_read (const char *fn,
1004 struct GNUNET_DISK_FileHandle *fh;
1008 fh = GNUNET_DISK_file_open (fn,
1009 GNUNET_DISK_OPEN_READ,
1010 GNUNET_DISK_PERM_NONE);
1012 return GNUNET_SYSERR;
1013 ret = GNUNET_DISK_file_read (fh, result, len);
1015 GNUNET_assert (GNUNET_OK == GNUNET_DISK_file_close (fh));
1022 * Write a buffer to a file.
1024 * @param h handle to open file
1025 * @param buffer the data to write
1026 * @param n number of bytes to write
1027 * @return number of bytes written on success, #GNUNET_SYSERR on error
1030 GNUNET_DISK_file_write (const struct GNUNET_DISK_FileHandle * h,
1031 const void *buffer, size_t n)
1036 return GNUNET_SYSERR;
1040 DWORD bytes_written;
1042 if (h->type == GNUNET_DISK_HANLDE_TYPE_FILE)
1044 if (!WriteFile (h->h, buffer, n, &bytes_written, NULL))
1046 SetErrnoFromWinError (GetLastError ());
1047 return GNUNET_SYSERR;
1050 else if (h->type == GNUNET_DISK_HANLDE_TYPE_PIPE)
1052 LOG (GNUNET_ERROR_TYPE_DEBUG, "It is a pipe trying to write %u bytes\n", n);
1053 if (!WriteFile (h->h, buffer, n, &bytes_written, h->oOverlapWrite))
1055 if (GetLastError () != ERROR_IO_PENDING)
1057 SetErrnoFromWinError (GetLastError ());
1058 LOG (GNUNET_ERROR_TYPE_DEBUG, "Error writing to pipe: %u\n",
1060 return GNUNET_SYSERR;
1062 LOG (GNUNET_ERROR_TYPE_DEBUG, "Will get overlapped result\n");
1063 if (!GetOverlappedResult (h->h, h->oOverlapWrite, &bytes_written, TRUE))
1065 SetErrnoFromWinError (GetLastError ());
1066 LOG (GNUNET_ERROR_TYPE_DEBUG,
1067 "Error getting overlapped result while writing to pipe: %u\n",
1069 return GNUNET_SYSERR;
1075 if (!GetOverlappedResult (h->h, h->oOverlapWrite, &ovr, TRUE))
1077 LOG (GNUNET_ERROR_TYPE_DEBUG,
1078 "Error getting control overlapped result while writing to pipe: %u\n",
1083 LOG (GNUNET_ERROR_TYPE_DEBUG,
1084 "Wrote %u bytes (ovr says %u), picking the greatest\n",
1085 bytes_written, ovr);
1088 if (bytes_written == 0)
1092 LOG (GNUNET_ERROR_TYPE_DEBUG, "Wrote %u bytes, returning -1 with EAGAIN\n", bytes_written);
1094 return GNUNET_SYSERR;
1097 LOG (GNUNET_ERROR_TYPE_DEBUG, "Wrote %u bytes\n", bytes_written);
1103 return bytes_written;
1105 return write (h->fd, buffer, n);
1111 * Write a buffer to a file, blocking, if necessary.
1113 * @param h handle to open file
1114 * @param buffer the data to write
1115 * @param n number of bytes to write
1116 * @return number of bytes written on success, #GNUNET_SYSERR on error
1119 GNUNET_DISK_file_write_blocking (const struct GNUNET_DISK_FileHandle * h,
1126 return GNUNET_SYSERR;
1130 DWORD bytes_written;
1131 /* We do a non-overlapped write, which is as blocking as it gets */
1132 LOG (GNUNET_ERROR_TYPE_DEBUG, "Writing %u bytes\n", n);
1133 if (!WriteFile (h->h, buffer, n, &bytes_written, NULL))
1135 SetErrnoFromWinError (GetLastError ());
1136 LOG (GNUNET_ERROR_TYPE_DEBUG, "Error writing to pipe: %u\n",
1138 return GNUNET_SYSERR;
1140 if (bytes_written == 0 && n > 0)
1142 LOG (GNUNET_ERROR_TYPE_DEBUG, "Waiting for pipe to clean\n");
1143 WaitForSingleObject (h->h, INFINITE);
1144 if (!WriteFile (h->h, buffer, n, &bytes_written, NULL))
1146 SetErrnoFromWinError (GetLastError ());
1147 LOG (GNUNET_ERROR_TYPE_DEBUG, "Error writing to pipe: %u\n",
1149 return GNUNET_SYSERR;
1152 LOG (GNUNET_ERROR_TYPE_DEBUG,
1155 return bytes_written;
1160 /* set to blocking, write, then set back */
1161 flags = fcntl (h->fd, F_GETFL);
1162 if (0 != (flags & O_NONBLOCK))
1163 (void) fcntl (h->fd, F_SETFL, flags - O_NONBLOCK);
1164 ret = write (h->fd, buffer, n);
1165 if (0 == (flags & O_NONBLOCK))
1166 (void) fcntl (h->fd, F_SETFL, flags);
1173 * Write a buffer to a file. If the file is longer than the
1174 * number of bytes that will be written, it will be truncated.
1176 * @param fn file name
1177 * @param buffer the data to write
1178 * @param n number of bytes to write
1179 * @param mode file permissions
1180 * @return number of bytes written on success, #GNUNET_SYSERR on error
1183 GNUNET_DISK_fn_write (const char *fn,
1186 enum GNUNET_DISK_AccessPermissions mode)
1188 struct GNUNET_DISK_FileHandle *fh;
1191 fh = GNUNET_DISK_file_open (fn,
1192 GNUNET_DISK_OPEN_WRITE | GNUNET_DISK_OPEN_TRUNCATE
1193 | GNUNET_DISK_OPEN_CREATE, mode);
1195 return GNUNET_SYSERR;
1196 ret = GNUNET_DISK_file_write (fh, buffer, n);
1197 GNUNET_assert (GNUNET_OK == GNUNET_DISK_file_close (fh));
1203 * Scan a directory for files.
1205 * @param dir_name the name of the directory
1206 * @param callback the method to call for each file,
1207 * can be NULL, in that case, we only count
1208 * @param callback_cls closure for @a callback
1209 * @return the number of files found, #GNUNET_SYSERR on error or
1210 * ieration aborted by callback returning #GNUNET_SYSERR
1213 GNUNET_DISK_directory_scan (const char *dir_name,
1214 GNUNET_FileNameCallback callback,
1218 struct dirent *finfo;
1224 unsigned int name_len;
1225 unsigned int n_size;
1227 GNUNET_assert (NULL != dir_name);
1228 dname = GNUNET_STRINGS_filename_expand (dir_name);
1230 return GNUNET_SYSERR;
1231 while ( (strlen (dname) > 0) &&
1232 (dname[strlen (dname) - 1] == DIR_SEPARATOR) )
1233 dname[strlen (dname) - 1] = '\0';
1234 if (0 != STAT (dname, &istat))
1236 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING,
1239 GNUNET_free (dname);
1240 return GNUNET_SYSERR;
1242 if (! S_ISDIR (istat.st_mode))
1244 LOG (GNUNET_ERROR_TYPE_WARNING,
1245 _("Expected `%s' to be a directory!\n"),
1247 GNUNET_free (dname);
1248 return GNUNET_SYSERR;
1251 dinfo = OPENDIR (dname);
1252 if ( (EACCES == errno) ||
1255 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING,
1260 GNUNET_free (dname);
1261 return GNUNET_SYSERR;
1264 n_size = strlen (dname) + name_len + strlen (DIR_SEPARATOR_STR) + 1;
1265 name = GNUNET_malloc (n_size);
1266 while (NULL != (finfo = READDIR (dinfo)))
1268 if ( (0 == strcmp (finfo->d_name, ".")) ||
1269 (0 == strcmp (finfo->d_name, "..")) )
1271 if (NULL != callback)
1273 if (name_len < strlen (finfo->d_name))
1276 name_len = strlen (finfo->d_name);
1277 n_size = strlen (dname) + name_len + strlen (DIR_SEPARATOR_STR) + 1;
1278 name = GNUNET_malloc (n_size);
1280 /* dname can end in "/" only if dname == "/";
1281 * if dname does not end in "/", we need to add
1282 * a "/" (otherwise, we must not!) */
1283 GNUNET_snprintf (name,
1287 (0 == strcmp (dname,
1290 : DIR_SEPARATOR_STR,
1292 ret = callback (callback_cls,
1294 if (GNUNET_OK != ret)
1298 GNUNET_free (dname);
1299 if (GNUNET_NO == ret)
1301 return GNUNET_SYSERR;
1308 GNUNET_free (dname);
1314 * Function that removes the given directory by calling
1315 * #GNUNET_DISK_directory_remove().
1317 * @param unused not used
1318 * @param fn directory to remove
1319 * @return #GNUNET_OK
1322 remove_helper (void *unused,
1326 (void) GNUNET_DISK_directory_remove (fn);
1332 * Remove all files in a directory (rm -r). Call with
1335 * @param filename the file to remove
1336 * @return #GNUNET_OK on success, #GNUNET_SYSERR on error
1339 GNUNET_DISK_directory_remove (const char *filename)
1343 if (NULL == filename)
1346 return GNUNET_SYSERR;
1348 if (0 != LSTAT (filename, &istat))
1349 return GNUNET_NO; /* file may not exist... */
1350 (void) CHMOD (filename,
1351 S_IWUSR | S_IRUSR | S_IXUSR);
1352 if (0 == UNLINK (filename))
1354 if ( (errno != EISDIR) &&
1355 /* EISDIR is not sufficient in all cases, e.g.
1356 * sticky /tmp directory may result in EPERM on BSD.
1357 * So we also explicitly check "isDirectory" */
1359 GNUNET_DISK_directory_test (filename,
1362 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING,
1365 return GNUNET_SYSERR;
1367 if (GNUNET_SYSERR ==
1368 GNUNET_DISK_directory_scan (filename,
1371 return GNUNET_SYSERR;
1372 if (0 != RMDIR (filename))
1374 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING,
1377 return GNUNET_SYSERR;
1386 * @param src file to copy
1387 * @param dst destination file name
1388 * @return #GNUNET_OK on success, #GNUNET_SYSERR on error
1391 GNUNET_DISK_file_copy (const char *src,
1399 struct GNUNET_DISK_FileHandle *in;
1400 struct GNUNET_DISK_FileHandle *out;
1403 GNUNET_DISK_file_size (src,
1408 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR,
1411 return GNUNET_SYSERR;
1414 in = GNUNET_DISK_file_open (src,
1415 GNUNET_DISK_OPEN_READ,
1416 GNUNET_DISK_PERM_NONE);
1419 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR,
1422 return GNUNET_SYSERR;
1425 GNUNET_DISK_file_open (dst,
1426 GNUNET_DISK_OPEN_WRITE | GNUNET_DISK_OPEN_CREATE |
1427 GNUNET_DISK_OPEN_FAILIFEXISTS,
1428 GNUNET_DISK_PERM_USER_READ |
1429 GNUNET_DISK_PERM_USER_WRITE |
1430 GNUNET_DISK_PERM_GROUP_READ |
1431 GNUNET_DISK_PERM_GROUP_WRITE);
1434 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR,
1437 GNUNET_DISK_file_close (in);
1438 return GNUNET_SYSERR;
1440 buf = GNUNET_malloc (COPY_BLK_SIZE);
1443 len = COPY_BLK_SIZE;
1444 if (len > size - pos)
1446 sret = GNUNET_DISK_file_read (in,
1450 (len != (size_t) sret) )
1452 sret = GNUNET_DISK_file_write (out,
1456 (len != (size_t) sret) )
1461 GNUNET_DISK_file_close (in);
1462 GNUNET_DISK_file_close (out);
1466 GNUNET_DISK_file_close (in);
1467 GNUNET_DISK_file_close (out);
1468 return GNUNET_SYSERR;
1473 * @brief Removes special characters as ':' from a filename.
1474 * @param fn the filename to canonicalize
1477 GNUNET_DISK_filename_canonicalize (char *fn)
1482 for (idx = fn; *idx; idx++)
1486 if (c == '/' || c == '\\' || c == ':' ||
1487 c == '*' || c == '?' || c == '"' ||
1488 c == '<' || c == '>' || c == '|')
1498 * @brief Change owner of a file
1500 * @param filename name of file to change the owner of
1501 * @param user name of the new owner
1502 * @return #GNUNET_OK on success, #GNUNET_SYSERR on failure
1505 GNUNET_DISK_file_change_owner (const char *filename,
1511 pws = getpwnam (user);
1514 LOG (GNUNET_ERROR_TYPE_ERROR,
1515 _("Cannot obtain information about user `%s': %s\n"),
1518 return GNUNET_SYSERR;
1520 if (0 != chown (filename,
1524 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING,
1527 return GNUNET_SYSERR;
1535 * Lock a part of a file
1537 * @param fh file handle
1538 * @param lock_start absolute position from where to lock
1539 * @param lock_end absolute position until where to lock
1540 * @param excl #GNUNET_YES for an exclusive lock
1541 * @return #GNUNET_OK on success, #GNUNET_SYSERR on error
1544 GNUNET_DISK_file_lock (struct GNUNET_DISK_FileHandle *fh,
1552 return GNUNET_SYSERR;
1558 memset (&fl, 0, sizeof (struct flock));
1559 fl.l_type = excl ? F_WRLCK : F_RDLCK;
1560 fl.l_whence = SEEK_SET;
1561 fl.l_start = lock_start;
1562 fl.l_len = lock_end;
1564 return fcntl (fh->fd, F_SETLK, &fl) != 0 ? GNUNET_SYSERR : GNUNET_OK;
1567 off_t diff = lock_end - lock_start;
1568 DWORD diff_low, diff_high;
1569 diff_low = (DWORD) (diff & 0xFFFFFFFF);
1570 diff_high = (DWORD) ((diff >> (sizeof (DWORD) * 8)) & 0xFFFFFFFF);
1572 memset (&o, 0, sizeof (OVERLAPPED));
1573 o.Offset = (DWORD) (lock_start & 0xFFFFFFFF);;
1574 o.OffsetHigh = (DWORD) (((lock_start & ~0xFFFFFFFF) >> (sizeof (DWORD) * 8)) & 0xFFFFFFFF);
1577 (fh->h, (excl ? LOCKFILE_EXCLUSIVE_LOCK : 0) | LOCKFILE_FAIL_IMMEDIATELY,
1578 0, diff_low, diff_high, &o))
1580 SetErrnoFromWinError (GetLastError ());
1581 return GNUNET_SYSERR;
1590 * Unlock a part of a file
1592 * @param fh file handle
1593 * @param unlock_start absolute position from where to unlock
1594 * @param unlock_end absolute position until where to unlock
1595 * @return #GNUNET_OK on success, #GNUNET_SYSERR on error
1598 GNUNET_DISK_file_unlock (struct GNUNET_DISK_FileHandle *fh,
1605 return GNUNET_SYSERR;
1611 memset (&fl, 0, sizeof (struct flock));
1612 fl.l_type = F_UNLCK;
1613 fl.l_whence = SEEK_SET;
1614 fl.l_start = unlock_start;
1615 fl.l_len = unlock_end;
1617 return fcntl (fh->fd, F_SETLK, &fl) != 0 ? GNUNET_SYSERR : GNUNET_OK;
1620 off_t diff = unlock_end - unlock_start;
1621 DWORD diff_low, diff_high;
1622 diff_low = (DWORD) (diff & 0xFFFFFFFF);
1623 diff_high = (DWORD) ((diff >> (sizeof (DWORD) * 8)) & 0xFFFFFFFF);
1625 memset (&o, 0, sizeof (OVERLAPPED));
1626 o.Offset = (DWORD) (unlock_start & 0xFFFFFFFF);;
1627 o.OffsetHigh = (DWORD) (((unlock_start & ~0xFFFFFFFF) >> (sizeof (DWORD) * 8)) & 0xFFFFFFFF);
1629 if (!UnlockFileEx (fh->h, 0, diff_low, diff_high, &o))
1631 SetErrnoFromWinError (GetLastError ());
1632 return GNUNET_SYSERR;
1641 * Open a file. Note that the access permissions will only be
1642 * used if a new file is created and if the underlying operating
1643 * system supports the given permissions.
1645 * @param fn file name to be opened
1646 * @param flags opening flags, a combination of GNUNET_DISK_OPEN_xxx bit flags
1647 * @param perm permissions for the newly created file, use
1648 * #GNUNET_DISK_PERM_NONE if a file could not be created by this
1649 * call (because of flags)
1650 * @return IO handle on success, NULL on error
1652 struct GNUNET_DISK_FileHandle *
1653 GNUNET_DISK_file_open (const char *fn,
1654 enum GNUNET_DISK_OpenFlags flags,
1655 enum GNUNET_DISK_AccessPermissions perm)
1658 struct GNUNET_DISK_FileHandle *ret;
1664 wchar_t wexpfn[MAX_PATH + 1];
1671 expfn = GNUNET_STRINGS_filename_expand (fn);
1676 if (GNUNET_DISK_OPEN_READWRITE == (flags & GNUNET_DISK_OPEN_READWRITE))
1677 oflags = O_RDWR; /* note: O_RDWR is NOT always O_RDONLY | O_WRONLY */
1678 else if (flags & GNUNET_DISK_OPEN_READ)
1680 else if (flags & GNUNET_DISK_OPEN_WRITE)
1685 GNUNET_free (expfn);
1688 if (flags & GNUNET_DISK_OPEN_FAILIFEXISTS)
1689 oflags |= (O_CREAT | O_EXCL);
1690 if (flags & GNUNET_DISK_OPEN_TRUNCATE)
1692 if (flags & GNUNET_DISK_OPEN_APPEND)
1694 if (flags & GNUNET_DISK_OPEN_CREATE)
1696 (void) GNUNET_DISK_directory_create_for_file (expfn);
1698 mode = translate_unix_perms (perm);
1701 fd = open (expfn, oflags
1705 | O_LARGEFILE, mode);
1708 if (0 == (flags & GNUNET_DISK_OPEN_FAILIFEXISTS))
1709 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "open", expfn);
1711 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_DEBUG, "open", expfn);
1712 GNUNET_free (expfn);
1719 if (GNUNET_DISK_OPEN_READWRITE == (flags & GNUNET_DISK_OPEN_READWRITE))
1720 access = FILE_READ_DATA | FILE_WRITE_DATA;
1721 else if (flags & GNUNET_DISK_OPEN_READ)
1722 access = FILE_READ_DATA;
1723 else if (flags & GNUNET_DISK_OPEN_WRITE)
1724 access = FILE_WRITE_DATA;
1726 if (flags & GNUNET_DISK_OPEN_FAILIFEXISTS)
1730 else if (flags & GNUNET_DISK_OPEN_CREATE)
1732 (void) GNUNET_DISK_directory_create_for_file (expfn);
1733 if (flags & GNUNET_DISK_OPEN_TRUNCATE)
1734 disp = CREATE_ALWAYS;
1738 else if (flags & GNUNET_DISK_OPEN_TRUNCATE)
1740 disp = TRUNCATE_EXISTING;
1744 disp = OPEN_EXISTING;
1747 if (ERROR_SUCCESS == plibc_conv_to_win_pathwconv(expfn, wexpfn))
1748 h = CreateFileW (wexpfn, access,
1749 FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE, NULL,
1750 disp, FILE_ATTRIBUTE_NORMAL, NULL);
1752 h = INVALID_HANDLE_VALUE;
1753 if (h == INVALID_HANDLE_VALUE)
1756 SetErrnoFromWinError (GetLastError ());
1758 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_INFO, "open", expfn);
1759 GNUNET_free (expfn);
1764 if (flags & GNUNET_DISK_OPEN_APPEND)
1765 if (SetFilePointer (h, 0, 0, FILE_END) == INVALID_SET_FILE_POINTER)
1767 SetErrnoFromWinError (GetLastError ());
1768 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "SetFilePointer", expfn);
1770 GNUNET_free (expfn);
1775 ret = GNUNET_new (struct GNUNET_DISK_FileHandle);
1778 ret->type = GNUNET_DISK_HANLDE_TYPE_FILE;
1782 GNUNET_free (expfn);
1788 * Close an open file.
1790 * @param h file handle
1791 * @return #GNUNET_OK on success, #GNUNET_SYSERR otherwise
1794 GNUNET_DISK_file_close (struct GNUNET_DISK_FileHandle *h)
1800 return GNUNET_SYSERR;
1806 if (! CloseHandle (h->h))
1808 SetErrnoFromWinError (GetLastError ());
1809 LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING, "close");
1810 ret = GNUNET_SYSERR;
1812 if (h->oOverlapRead)
1814 if (! CloseHandle (h->oOverlapRead->hEvent))
1816 SetErrnoFromWinError (GetLastError ());
1817 LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING, "close");
1818 ret = GNUNET_SYSERR;
1820 GNUNET_free (h->oOverlapRead);
1822 if (h->oOverlapWrite)
1824 if (!CloseHandle (h->oOverlapWrite->hEvent))
1826 SetErrnoFromWinError (GetLastError ());
1827 LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING, "close");
1828 ret = GNUNET_SYSERR;
1830 GNUNET_free (h->oOverlapWrite);
1833 if (close (h->fd) != 0)
1835 LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING, "close");
1836 ret = GNUNET_SYSERR;
1846 * Get a GNUnet file handle from a W32 handle.
1848 * @param handle native handle
1849 * @return GNUnet file handle corresponding to the W32 handle
1851 struct GNUNET_DISK_FileHandle *
1852 GNUNET_DISK_get_handle_from_w32_handle (HANDLE osfh)
1854 struct GNUNET_DISK_FileHandle *fh;
1856 enum GNUNET_FILE_Type ftype;
1858 dwret = GetFileType (osfh);
1861 case FILE_TYPE_DISK:
1862 ftype = GNUNET_DISK_HANLDE_TYPE_FILE;
1864 case FILE_TYPE_PIPE:
1865 ftype = GNUNET_DISK_HANLDE_TYPE_PIPE;
1867 case FILE_TYPE_UNKNOWN:
1868 if ( (GetLastError () == NO_ERROR) ||
1869 (GetLastError () == ERROR_INVALID_HANDLE) )
1871 if (0 != ResetEvent (osfh))
1872 ftype = GNUNET_DISK_HANLDE_TYPE_EVENT;
1883 fh = GNUNET_new (struct GNUNET_DISK_FileHandle);
1887 if (ftype == GNUNET_DISK_HANLDE_TYPE_PIPE)
1890 * Note that we can't make it overlapped if it isn't already.
1891 * (ReOpenFile() is only available in 2003/Vista).
1892 * The process that opened this file in the first place (usually a parent
1893 * process, if this is stdin/stdout/stderr) must make it overlapped,
1894 * otherwise we're screwed, as selecting on non-overlapped handle
1897 fh->oOverlapRead = GNUNET_new (OVERLAPPED);
1898 fh->oOverlapWrite = GNUNET_new (OVERLAPPED);
1899 fh->oOverlapRead->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
1900 fh->oOverlapWrite->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
1908 * Get a handle from a native integer FD.
1910 * @param fno native integer file descriptor
1911 * @return file handle corresponding to the descriptor, NULL on error
1913 struct GNUNET_DISK_FileHandle *
1914 GNUNET_DISK_get_handle_from_int_fd (int fno)
1916 struct GNUNET_DISK_FileHandle *fh;
1918 if ( (((off_t) -1) == lseek (fno, 0, SEEK_CUR)) &&
1920 return NULL; /* invalid FD */
1923 fh = GNUNET_new (struct GNUNET_DISK_FileHandle);
1929 osfh = _get_osfhandle (fno);
1930 if (INVALID_HANDLE_VALUE == (HANDLE) osfh)
1933 fh = GNUNET_DISK_get_handle_from_w32_handle ((HANDLE) osfh);
1941 * Get a handle from a native streaming FD.
1943 * @param fd native streaming file descriptor
1944 * @return file handle corresponding to the descriptor
1946 struct GNUNET_DISK_FileHandle *
1947 GNUNET_DISK_get_handle_from_native (FILE *fd)
1955 return GNUNET_DISK_get_handle_from_int_fd (fno);
1960 * Handle for a memory-mapping operation.
1962 struct GNUNET_DISK_MapHandle
1965 * Address where the map is in memory.
1971 * Underlying OS handle.
1976 * Number of bytes mapped.
1984 #define MAP_FAILED ((void *) -1)
1988 * Map a file into memory
1990 * @param h open file handle
1991 * @param m handle to the new mapping
1992 * @param access access specification, GNUNET_DISK_MAP_TYPE_xxx
1993 * @param len size of the mapping
1994 * @return pointer to the mapped memory region, NULL on failure
1997 GNUNET_DISK_file_map (const struct GNUNET_DISK_FileHandle *h,
1998 struct GNUNET_DISK_MapHandle **m,
1999 enum GNUNET_DISK_MapType access, size_t len)
2008 DWORD mapAccess, protect;
2010 if ((access & GNUNET_DISK_MAP_TYPE_READ) &&
2011 (access & GNUNET_DISK_MAP_TYPE_WRITE))
2013 protect = PAGE_READWRITE;
2014 mapAccess = FILE_MAP_ALL_ACCESS;
2016 else if (access & GNUNET_DISK_MAP_TYPE_READ)
2018 protect = PAGE_READONLY;
2019 mapAccess = FILE_MAP_READ;
2021 else if (access & GNUNET_DISK_MAP_TYPE_WRITE)
2023 protect = PAGE_READWRITE;
2024 mapAccess = FILE_MAP_WRITE;
2032 *m = GNUNET_new (struct GNUNET_DISK_MapHandle);
2033 (*m)->h = CreateFileMapping (h->h, NULL, protect, 0, 0, NULL);
2034 if ((*m)->h == INVALID_HANDLE_VALUE)
2036 SetErrnoFromWinError (GetLastError ());
2041 (*m)->addr = MapViewOfFile ((*m)->h, mapAccess, 0, 0, len);
2044 SetErrnoFromWinError (GetLastError ());
2045 CloseHandle ((*m)->h);
2054 if (access & GNUNET_DISK_MAP_TYPE_READ)
2056 if (access & GNUNET_DISK_MAP_TYPE_WRITE)
2058 *m = GNUNET_new (struct GNUNET_DISK_MapHandle);
2059 (*m)->addr = mmap (NULL, len, prot, MAP_SHARED, h->fd, 0);
2060 GNUNET_assert (NULL != (*m)->addr);
2061 if (MAP_FAILED == (*m)->addr)
2073 * @param h mapping handle
2074 * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
2077 GNUNET_DISK_file_unmap (struct GNUNET_DISK_MapHandle *h)
2084 return GNUNET_SYSERR;
2088 ret = UnmapViewOfFile (h->addr) ? GNUNET_OK : GNUNET_SYSERR;
2089 if (ret != GNUNET_OK)
2090 SetErrnoFromWinError (GetLastError ());
2091 if (!CloseHandle (h->h) && (ret == GNUNET_OK))
2093 ret = GNUNET_SYSERR;
2094 SetErrnoFromWinError (GetLastError ());
2097 ret = munmap (h->addr, h->len) != -1 ? GNUNET_OK : GNUNET_SYSERR;
2105 * Write file changes to disk
2106 * @param h handle to an open file
2107 * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
2110 GNUNET_DISK_file_sync (const struct GNUNET_DISK_FileHandle *h)
2115 return GNUNET_SYSERR;
2121 ret = FlushFileBuffers (h->h) ? GNUNET_OK : GNUNET_SYSERR;
2122 if (ret != GNUNET_OK)
2123 SetErrnoFromWinError (GetLastError ());
2125 #elif defined(FREEBSD) || defined(OPENBSD) || defined(DARWIN)
2126 return fsync (h->fd) == -1 ? GNUNET_SYSERR : GNUNET_OK;
2128 return fdatasync (h->fd) == -1 ? GNUNET_SYSERR : GNUNET_OK;
2135 #define PIPE_BUF 512
2137 /* Copyright Bob Byrnes <byrnes <at> curl.com>
2138 http://permalink.gmane.org/gmane.os.cygwin.patches/2121
2140 /* Create a pipe, and return handles to the read and write ends,
2141 just like CreatePipe, but ensure that the write end permits
2142 FILE_READ_ATTRIBUTES access, on later versions of win32 where
2143 this is supported. This access is needed by NtQueryInformationFile,
2144 which is used to implement select and nonblocking writes.
2145 Note that the return value is either NO_ERROR or GetLastError,
2146 unlike CreatePipe, which returns a bool for success or failure. */
2148 create_selectable_pipe (PHANDLE read_pipe_ptr, PHANDLE write_pipe_ptr,
2149 LPSECURITY_ATTRIBUTES sa_ptr, DWORD psize,
2150 DWORD dwReadMode, DWORD dwWriteMode)
2152 /* Default to error. */
2153 *read_pipe_ptr = *write_pipe_ptr = INVALID_HANDLE_VALUE;
2158 /* Ensure that there is enough pipe buffer space for atomic writes. */
2159 if (psize < PIPE_BUF)
2162 char pipename[MAX_PATH];
2164 /* Retry CreateNamedPipe as long as the pipe name is in use.
2165 * Retrying will probably never be necessary, but we want
2166 * to be as robust as possible. */
2169 static volatile LONG pipe_unique_id;
2171 snprintf (pipename, sizeof pipename, "\\\\.\\pipe\\gnunet-%d-%ld",
2172 getpid (), InterlockedIncrement ((LONG *) & pipe_unique_id));
2173 LOG (GNUNET_ERROR_TYPE_DEBUG, "CreateNamedPipe: name = %s, size = %lu\n",
2175 /* Use CreateNamedPipe instead of CreatePipe, because the latter
2176 * returns a write handle that does not permit FILE_READ_ATTRIBUTES
2177 * access, on versions of win32 earlier than WinXP SP2.
2178 * CreatePipe also stupidly creates a full duplex pipe, which is
2179 * a waste, since only a single direction is actually used.
2180 * It's important to only allow a single instance, to ensure that
2181 * the pipe was not created earlier by some other process, even if
2182 * the pid has been reused. */
2183 read_pipe = CreateNamedPipeA (pipename, PIPE_ACCESS_INBOUND | FILE_FLAG_FIRST_PIPE_INSTANCE | dwReadMode, PIPE_TYPE_BYTE | PIPE_READMODE_BYTE, 1, /* max instances */
2184 psize, /* output buffer size */
2185 psize, /* input buffer size */
2186 NMPWAIT_USE_DEFAULT_WAIT, sa_ptr);
2188 if (read_pipe != INVALID_HANDLE_VALUE)
2190 LOG (GNUNET_ERROR_TYPE_DEBUG, "pipe read handle = %p\n", read_pipe);
2194 DWORD err = GetLastError ();
2198 case ERROR_PIPE_BUSY:
2199 /* The pipe is already open with compatible parameters.
2200 * Pick a new name and retry. */
2201 LOG (GNUNET_ERROR_TYPE_DEBUG, "pipe busy, retrying\n");
2203 case ERROR_ACCESS_DENIED:
2204 /* The pipe is already open with incompatible parameters.
2205 * Pick a new name and retry. */
2206 LOG (GNUNET_ERROR_TYPE_DEBUG, "pipe access denied, retrying\n");
2208 case ERROR_CALL_NOT_IMPLEMENTED:
2209 /* We are on an older Win9x platform without named pipes.
2210 * Return an anonymous pipe as the best approximation. */
2211 LOG (GNUNET_ERROR_TYPE_DEBUG,
2212 "CreateNamedPipe not implemented, resorting to "
2213 "CreatePipe: size = %lu\n", psize);
2214 if (CreatePipe (read_pipe_ptr, write_pipe_ptr, sa_ptr, psize))
2216 LOG (GNUNET_ERROR_TYPE_DEBUG, "pipe read handle = %p, write handle = %p\n",
2221 err = GetLastError ();
2222 LOG (GNUNET_ERROR_TYPE_ERROR, "CreatePipe failed: %d\n", err);
2225 LOG (GNUNET_ERROR_TYPE_ERROR, "CreateNamedPipe failed: %d\n", err);
2230 LOG (GNUNET_ERROR_TYPE_DEBUG, "CreateFile: name = %s\n", pipename);
2232 /* Open the named pipe for writing.
2233 * Be sure to permit FILE_READ_ATTRIBUTES access. */
2234 write_pipe = CreateFileA (pipename, GENERIC_WRITE | FILE_READ_ATTRIBUTES, 0, /* share mode */
2235 sa_ptr, OPEN_EXISTING, dwWriteMode, /* flags and attributes */
2236 0); /* handle to template file */
2238 if (write_pipe == INVALID_HANDLE_VALUE)
2241 DWORD err = GetLastError ();
2243 LOG (GNUNET_ERROR_TYPE_DEBUG, "CreateFile failed: %d\n", err);
2244 CloseHandle (read_pipe);
2247 LOG (GNUNET_ERROR_TYPE_DEBUG, "pipe write handle = %p\n", write_pipe);
2249 *read_pipe_ptr = read_pipe;
2250 *write_pipe_ptr = write_pipe;
2257 * Creates an interprocess channel
2259 * @param blocking_read creates an asynchronous pipe for reading if set to GNUNET_NO
2260 * @param blocking_write creates an asynchronous pipe for writing if set to GNUNET_NO
2261 * @param inherit_read inherit the parent processes stdin (only for windows)
2262 * @param inherit_write inherit the parent processes stdout (only for windows)
2263 * @return handle to the new pipe, NULL on error
2265 struct GNUNET_DISK_PipeHandle *
2266 GNUNET_DISK_pipe (int blocking_read,
2276 (void) inherit_read;
2277 (void) inherit_write;
2282 LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR,
2287 return GNUNET_DISK_pipe_from_fd (blocking_read,
2291 struct GNUNET_DISK_PipeHandle *p;
2296 p = GNUNET_new (struct GNUNET_DISK_PipeHandle);
2297 p->fd[0] = GNUNET_new (struct GNUNET_DISK_FileHandle);
2298 p->fd[1] = GNUNET_new (struct GNUNET_DISK_FileHandle);
2300 /* All pipes are overlapped. If you want them to block - just
2301 * call WriteFile() and ReadFile() with NULL overlapped pointer.
2302 * NOTE: calling with NULL overlapped pointer works only
2303 * for pipes, and doesn't seem to be a documented feature.
2304 * It will NOT work for files, because overlapped files need
2305 * to read offsets from the overlapped structure, regardless.
2306 * Pipes are not seekable, and need no offsets, which is
2307 * probably why it works for them.
2310 create_selectable_pipe (&p->fd[0]->h, &p->fd[1]->h, NULL, 0,
2311 FILE_FLAG_OVERLAPPED,
2312 FILE_FLAG_OVERLAPPED);
2315 SetErrnoFromWinError (GetLastError ());
2317 GNUNET_free (p->fd[0]);
2318 GNUNET_free (p->fd[1]);
2323 if (!DuplicateHandle
2324 (GetCurrentProcess (), p->fd[0]->h, GetCurrentProcess (), &tmp_handle, 0,
2325 inherit_read == GNUNET_YES ? TRUE : FALSE, DUPLICATE_SAME_ACCESS))
2327 SetErrnoFromWinError (GetLastError ());
2329 CloseHandle (p->fd[0]->h);
2330 CloseHandle (p->fd[1]->h);
2331 GNUNET_free (p->fd[0]);
2332 GNUNET_free (p->fd[1]);
2337 CloseHandle (p->fd[0]->h);
2338 p->fd[0]->h = tmp_handle;
2340 if (!DuplicateHandle
2341 (GetCurrentProcess (), p->fd[1]->h, GetCurrentProcess (), &tmp_handle, 0,
2342 inherit_write == GNUNET_YES ? TRUE : FALSE, DUPLICATE_SAME_ACCESS))
2344 SetErrnoFromWinError (GetLastError ());
2346 CloseHandle (p->fd[0]->h);
2347 CloseHandle (p->fd[1]->h);
2348 GNUNET_free (p->fd[0]);
2349 GNUNET_free (p->fd[1]);
2354 CloseHandle (p->fd[1]->h);
2355 p->fd[1]->h = tmp_handle;
2357 p->fd[0]->type = GNUNET_DISK_HANLDE_TYPE_PIPE;
2358 p->fd[1]->type = GNUNET_DISK_HANLDE_TYPE_PIPE;
2360 p->fd[0]->oOverlapRead = GNUNET_new (OVERLAPPED);
2361 p->fd[0]->oOverlapWrite = GNUNET_new (OVERLAPPED);
2362 p->fd[1]->oOverlapRead = GNUNET_new (OVERLAPPED);
2363 p->fd[1]->oOverlapWrite = GNUNET_new (OVERLAPPED);
2365 p->fd[0]->oOverlapRead->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2366 p->fd[0]->oOverlapWrite->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2368 p->fd[1]->oOverlapRead->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2369 p->fd[1]->oOverlapWrite->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2377 * Creates a pipe object from a couple of file descriptors.
2378 * Useful for wrapping existing pipe FDs.
2380 * @param blocking_read creates an asynchronous pipe for reading if set to GNUNET_NO
2381 * @param blocking_write creates an asynchronous pipe for writing if set to GNUNET_NO
2382 * @param fd an array of two fd values. One of them may be -1 for read-only or write-only pipes
2384 * @return handle to the new pipe, NULL on error
2386 struct GNUNET_DISK_PipeHandle *
2387 GNUNET_DISK_pipe_from_fd (int blocking_read, int blocking_write, int fd[2])
2389 struct GNUNET_DISK_PipeHandle *p;
2391 p = GNUNET_new (struct GNUNET_DISK_PipeHandle);
2396 int eno = 0; /* make gcc happy */
2401 p->fd[0] = GNUNET_new (struct GNUNET_DISK_FileHandle);
2402 p->fd[0]->fd = fd[0];
2405 flags = fcntl (fd[0], F_GETFL);
2406 flags |= O_NONBLOCK;
2407 if (0 > fcntl (fd[0], F_SETFL, flags))
2413 flags = fcntl (fd[0], F_GETFD);
2414 flags |= FD_CLOEXEC;
2415 if (0 > fcntl (fd[0], F_SETFD, flags))
2424 p->fd[1] = GNUNET_new (struct GNUNET_DISK_FileHandle);
2425 p->fd[1]->fd = fd[1];
2426 if (!blocking_write)
2428 flags = fcntl (fd[1], F_GETFL);
2429 flags |= O_NONBLOCK;
2430 if (0 > fcntl (fd[1], F_SETFL, flags))
2436 flags = fcntl (fd[1], F_GETFD);
2437 flags |= FD_CLOEXEC;
2438 if (0 > fcntl (fd[1], F_SETFD, flags))
2447 LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR, "fcntl");
2448 if (p->fd[0]->fd >= 0)
2449 GNUNET_break (0 == close (p->fd[0]->fd));
2450 if (p->fd[1]->fd >= 0)
2451 GNUNET_break (0 == close (p->fd[1]->fd));
2452 GNUNET_free_non_null (p->fd[0]);
2453 GNUNET_free_non_null (p->fd[1]);
2461 p->fd[0] = GNUNET_new (struct GNUNET_DISK_FileHandle);
2462 p->fd[0]->h = (HANDLE) _get_osfhandle (fd[0]);
2463 if (p->fd[0]->h != INVALID_HANDLE_VALUE)
2465 p->fd[0]->type = GNUNET_DISK_HANLDE_TYPE_PIPE;
2466 p->fd[0]->oOverlapRead = GNUNET_new (OVERLAPPED);
2467 p->fd[0]->oOverlapWrite = GNUNET_new (OVERLAPPED);
2468 p->fd[0]->oOverlapRead->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2469 p->fd[0]->oOverlapWrite->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2473 GNUNET_free (p->fd[0]);
2479 p->fd[1] = GNUNET_new (struct GNUNET_DISK_FileHandle);
2480 p->fd[1]->h = (HANDLE) _get_osfhandle (fd[1]);
2481 if (p->fd[1]->h != INVALID_HANDLE_VALUE)
2483 p->fd[1]->type = GNUNET_DISK_HANLDE_TYPE_PIPE;
2484 p->fd[1]->oOverlapRead = GNUNET_new (OVERLAPPED);
2485 p->fd[1]->oOverlapWrite = GNUNET_new (OVERLAPPED);
2486 p->fd[1]->oOverlapRead->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2487 p->fd[1]->oOverlapWrite->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2491 GNUNET_free (p->fd[1]);
2502 * Closes an interprocess channel
2504 * @param p pipe to close
2505 * @param end which end of the pipe to close
2506 * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
2509 GNUNET_DISK_pipe_close_end (struct GNUNET_DISK_PipeHandle *p,
2510 enum GNUNET_DISK_PipeEnd end)
2512 int ret = GNUNET_OK;
2514 if (end == GNUNET_DISK_PIPE_END_READ)
2518 ret = GNUNET_DISK_file_close (p->fd[0]);
2522 else if (end == GNUNET_DISK_PIPE_END_WRITE)
2526 ret = GNUNET_DISK_file_close (p->fd[1]);
2535 * Detaches one of the ends from the pipe.
2536 * Detached end is a fully-functional FileHandle, it will
2537 * not be affected by anything you do with the pipe afterwards.
2538 * Each end of a pipe can only be detched from it once (i.e.
2539 * it is not duplicated).
2541 * @param p pipe to detach an end from
2542 * @param end which end of the pipe to detach
2543 * @return Detached end on success, NULL on failure
2544 * (or if that end is not present or is closed).
2546 struct GNUNET_DISK_FileHandle *
2547 GNUNET_DISK_pipe_detach_end (struct GNUNET_DISK_PipeHandle *p,
2548 enum GNUNET_DISK_PipeEnd end)
2550 struct GNUNET_DISK_FileHandle *ret = NULL;
2552 if (end == GNUNET_DISK_PIPE_END_READ)
2560 else if (end == GNUNET_DISK_PIPE_END_WRITE)
2574 * Closes an interprocess channel
2576 * @param p pipe to close
2577 * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
2580 GNUNET_DISK_pipe_close (struct GNUNET_DISK_PipeHandle *p)
2582 int ret = GNUNET_OK;
2585 int write_end_close;
2586 int read_end_close_errno;
2587 int write_end_close_errno;
2589 read_end_close = GNUNET_DISK_pipe_close_end (p, GNUNET_DISK_PIPE_END_READ);
2590 read_end_close_errno = errno;
2591 write_end_close = GNUNET_DISK_pipe_close_end (p, GNUNET_DISK_PIPE_END_WRITE);
2592 write_end_close_errno = errno;
2595 if (GNUNET_OK != read_end_close)
2597 errno = read_end_close_errno;
2598 ret = read_end_close;
2600 else if (GNUNET_OK != write_end_close)
2602 errno = write_end_close_errno;
2603 ret = write_end_close;
2611 * Get the handle to a particular pipe end
2614 * @param n end to access
2615 * @return handle for the respective end
2617 const struct GNUNET_DISK_FileHandle *
2618 GNUNET_DISK_pipe_handle (const struct GNUNET_DISK_PipeHandle *p,
2619 enum GNUNET_DISK_PipeEnd n)
2623 case GNUNET_DISK_PIPE_END_READ:
2624 case GNUNET_DISK_PIPE_END_WRITE:
2634 * Retrieve OS file handle
2636 * @param fh GNUnet file descriptor
2637 * @param dst destination buffer
2638 * @param dst_len length of dst
2639 * @return #GNUNET_OK on success, #GNUNET_SYSERR otherwise
2642 GNUNET_DISK_internal_file_handle_ (const struct GNUNET_DISK_FileHandle *fh,
2643 void *dst, size_t dst_len)
2646 return GNUNET_SYSERR;
2648 if (dst_len < sizeof (HANDLE))
2649 return GNUNET_SYSERR;
2650 *((HANDLE *) dst) = fh->h;
2652 if (dst_len < sizeof (int))
2653 return GNUNET_SYSERR;
2654 *((int *) dst) = fh->fd;
2662 * Remove the directory given under @a option in
2663 * section [PATHS] in configuration under @a cfg_filename
2665 * @param cfg_filename configuration file to parse
2666 * @param option option with the dir name to purge
2669 GNUNET_DISK_purge_cfg_dir (const char *cfg_filename,
2672 struct GNUNET_CONFIGURATION_Handle *cfg;
2675 cfg = GNUNET_CONFIGURATION_create ();
2677 GNUNET_CONFIGURATION_load (cfg,
2681 GNUNET_CONFIGURATION_destroy (cfg);
2685 GNUNET_CONFIGURATION_get_value_filename (cfg,
2690 GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
2693 GNUNET_CONFIGURATION_destroy (cfg);
2696 GNUNET_CONFIGURATION_destroy (cfg);
2697 if (GNUNET_SYSERR ==
2698 GNUNET_DISK_directory_remove (tmpname))
2700 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR,
2703 GNUNET_free (tmpname);
2706 GNUNET_free (tmpname);