2 This file is part of GNUnet.
3 (C) 2001--2012 Christian Grothoff (and other contributing authors)
5 GNUnet is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 2, or (at your
8 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 General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with GNUnet; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
23 * @brief disk IO convenience methods
24 * @author Christian Grothoff
29 #include "gnunet_common.h"
30 #include "gnunet_directories.h"
31 #include "gnunet_disk_lib.h"
32 #include "gnunet_scheduler_lib.h"
33 #include "gnunet_strings_lib.h"
34 #include "gnunet_crypto_lib.h"
37 #define LOG(kind,...) GNUNET_log_from (kind, "util", __VA_ARGS__)
39 #define LOG_STRERROR(kind,syscall) GNUNET_log_from_strerror (kind, "util", syscall)
41 #define LOG_STRERROR_FILE(kind,syscall,filename) GNUNET_log_from_strerror_file (kind, "util", syscall, filename)
44 * Block size for IO for copying files.
46 #define COPY_BLK_SIZE 65536
50 #if defined(LINUX) || defined(CYGWIN) || defined(GNU)
53 #if defined(SOMEBSD) || defined(DARWIN)
54 #include <sys/param.h>
55 #include <sys/mount.h>
58 #include <sys/types.h>
59 #include <sys/statvfs.h>
64 ULONG PipeSerialNumber;
66 #define _IFMT 0170000 /* type of file */
67 #define _IFLNK 0120000 /* symbolic link */
68 #define S_ISLNK(m) (((m)&_IFMT) == _IFLNK)
70 #error PORT-ME: need to port statfs (how much space is left on the drive?)
76 #if !defined(SOMEBSD) && !defined(DARWIN) && !defined(WINDOWS)
80 #include <sys/statvfs.h>
85 * Handle used to manage a pipe.
87 struct GNUNET_DISK_PipeHandle
90 * File descriptors for the pipe.
92 struct GNUNET_DISK_FileHandle *fd[2];
97 * Closure for the recursion to determine the file size
100 struct GetFileSizeData
103 * Set to the total file size.
108 * GNUNET_YES if symbolic links should be included.
110 int include_sym_links;
113 * GNUNET_YES if mode is file-only (return total == -1 for directories).
115 int single_file_mode;
121 * Translate GNUnet-internal permission bitmap to UNIX file
122 * access permission bitmap.
124 * @param perm file permissions, GNUnet style
125 * @return file permissions, UNIX style
128 translate_unix_perms (enum GNUNET_DISK_AccessPermissions perm)
133 if (perm & GNUNET_DISK_PERM_USER_READ)
135 if (perm & GNUNET_DISK_PERM_USER_WRITE)
137 if (perm & GNUNET_DISK_PERM_USER_EXEC)
139 if (perm & GNUNET_DISK_PERM_GROUP_READ)
141 if (perm & GNUNET_DISK_PERM_GROUP_WRITE)
143 if (perm & GNUNET_DISK_PERM_GROUP_EXEC)
145 if (perm & GNUNET_DISK_PERM_OTHER_READ)
147 if (perm & GNUNET_DISK_PERM_OTHER_WRITE)
149 if (perm & GNUNET_DISK_PERM_OTHER_EXEC)
158 * Iterate over all files in the given directory and
159 * accumulate their size.
161 * @param cls closure of type "struct GetFileSizeData"
162 * @param fn current filename we are looking at
163 * @return GNUNET_SYSERR on serious errors, otherwise GNUNET_OK
166 getSizeRec (void *cls, const char *fn)
168 struct GetFileSizeData *gfsd = cls;
177 if (0 != STAT64 (fn, &buf))
179 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_DEBUG, "stat64", fn);
180 return GNUNET_SYSERR;
183 if (0 != STAT (fn, &buf))
185 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_DEBUG, "stat", fn);
186 return GNUNET_SYSERR;
189 if ((S_ISDIR (buf.st_mode)) && (gfsd->single_file_mode == GNUNET_YES))
192 return GNUNET_SYSERR;
194 if ((!S_ISLNK (buf.st_mode)) || (gfsd->include_sym_links == GNUNET_YES))
195 gfsd->total += buf.st_size;
196 if ((S_ISDIR (buf.st_mode)) && (0 == ACCESS (fn, X_OK)) &&
197 ((!S_ISLNK (buf.st_mode)) || (gfsd->include_sym_links == GNUNET_YES)))
199 if (GNUNET_SYSERR == GNUNET_DISK_directory_scan (fn, &getSizeRec, gfsd))
200 return GNUNET_SYSERR;
207 * Checks whether a handle is invalid
209 * @param h handle to check
210 * @return GNUNET_YES if invalid, GNUNET_NO if valid
213 GNUNET_DISK_handle_invalid (const struct GNUNET_DISK_FileHandle *h)
216 return ((!h) || (h->h == INVALID_HANDLE_VALUE)) ? GNUNET_YES : GNUNET_NO;
218 return ((!h) || (h->fd == -1)) ? GNUNET_YES : GNUNET_NO;
223 * Get the size of an open file.
225 * @param fh open file handle
226 * @param size where to write size of the file
227 * @return GNUNET_OK on success, GNUNET_SYSERR on error
230 GNUNET_DISK_file_handle_size (struct GNUNET_DISK_FileHandle *fh,
236 b = GetFileSizeEx (fh->h, &li);
239 SetErrnoFromWinError (GetLastError ());
240 return GNUNET_SYSERR;
242 *size = (OFF_T) li.QuadPart;
246 if (0 != FSTAT (fh->fd, &sbuf))
247 return GNUNET_SYSERR;
248 *size = sbuf.st_size;
255 * Move the read/write pointer in a file
257 * @param h handle of an open file
258 * @param offset position to move to
259 * @param whence specification to which position the offset parameter relates to
260 * @return the new position on success, GNUNET_SYSERR otherwise
263 GNUNET_DISK_file_seek (const struct GNUNET_DISK_FileHandle * h, OFF_T offset,
264 enum GNUNET_DISK_Seek whence)
269 return GNUNET_SYSERR;
274 LARGE_INTEGER new_pos;
277 static DWORD t[] = { FILE_BEGIN, FILE_CURRENT, FILE_END };
278 li.QuadPart = offset;
280 b = SetFilePointerEx (h->h, li, &new_pos, t[whence]);
283 SetErrnoFromWinError (GetLastError ());
284 return GNUNET_SYSERR;
286 return (OFF_T) new_pos.QuadPart;
288 static int t[] = { SEEK_SET, SEEK_CUR, SEEK_END };
290 return lseek (h->fd, offset, t[whence]);
296 * Get the size of the file (or directory) of the given file (in
299 * @param filename name of the file or directory
300 * @param size set to the size of the file (or,
301 * in the case of directories, the sum
302 * of all sizes of files in the directory)
303 * @param includeSymLinks should symbolic links be
305 * @param singleFileMode GNUNET_YES to only get size of one file
306 * and return GNUNET_SYSERR for directories.
307 * @return GNUNET_SYSERR on error, GNUNET_OK on success
310 GNUNET_DISK_file_size (const char *filename, uint64_t * size,
311 int includeSymLinks, int singleFileMode)
313 struct GetFileSizeData gfsd;
316 GNUNET_assert (size != NULL);
318 gfsd.include_sym_links = includeSymLinks;
319 gfsd.single_file_mode = singleFileMode;
320 ret = getSizeRec (&gfsd, filename);
327 * Obtain some unique identifiers for the given file
328 * that can be used to identify it in the local system.
329 * This function is used between GNUnet processes to
330 * quickly check if two files with the same absolute path
331 * are actually identical. The two processes represent
332 * the same peer but may communicate over the network
333 * (and the file may be on an NFS volume). This function
334 * may not be supported on all operating systems.
336 * @param filename name of the file
337 * @param dev set to the device ID
338 * @param ino set to the inode ID
339 * @return GNUNET_OK on success
342 GNUNET_DISK_file_get_identifiers (const char *filename, uint64_t * dev,
349 if ((0 == stat (filename, &sbuf)) && (0 == statvfs (filename, &fbuf)))
351 *dev = (uint64_t) fbuf.f_fsid;
352 *ino = (uint64_t) sbuf.st_ino;
359 if ((0 == stat (filename, &sbuf)) && (0 == statfs (filename, &fbuf)))
361 *dev = ((uint64_t) fbuf.f_fsid.val[0]) << 32 ||
362 ((uint64_t) fbuf.f_fsid.val[1]);
363 *ino = (uint64_t) sbuf.st_ino;
367 // FIXME NILS: test this
368 struct GNUNET_DISK_FileHandle *fh;
369 BY_HANDLE_FILE_INFORMATION info;
372 fh = GNUNET_DISK_file_open (filename, GNUNET_DISK_OPEN_READ, 0);
374 return GNUNET_SYSERR;
375 succ = GetFileInformationByHandle (fh->h, &info);
376 GNUNET_DISK_file_close (fh);
379 *dev = info.dwVolumeSerialNumber;
380 *ino = ((((uint64_t) info.nFileIndexHigh) << (sizeof (DWORD) * 8)) | info.nFileIndexLow);
384 return GNUNET_SYSERR;
387 return GNUNET_SYSERR;
392 * Create the name for a temporary file or directory from a template.
394 * @param t template (without XXXXX or "/tmp/")
395 * @return name ready for passing to 'mktemp' or 'mkdtemp', NULL on error
398 mktemp_name (const char *t)
404 if ((t[0] != '/') && (t[0] != '\\')
406 && !(isalpha ((int) t[0]) && (t[0] != '\0') && (t[1] == ':'))
410 /* FIXME: This uses system codepage on W32, not UTF-8 */
411 tmpdir = getenv ("TMPDIR");
413 tmpdir = getenv ("TMP");
415 tmpdir = getenv ("TEMP");
418 GNUNET_asprintf (&tmpl, "%s/%s%s", tmpdir, t, "XXXXXX");
422 GNUNET_asprintf (&tmpl, "%s%s", t, "XXXXXX");
425 fn = (char *) GNUNET_malloc (MAX_PATH + 1);
426 if (ERROR_SUCCESS != plibc_conv_to_win_path (tmpl, fn))
447 tfn = GNUNET_strdup (fn);
448 random_fn = _mktemp (tfn);
449 if (NULL == random_fn)
454 /* FIXME: assume fn to be UTF-8-encoded and do the right thing */
455 if (0 == CreateDirectoryA (tfn, NULL))
457 DWORD error = GetLastError ();
459 if (ERROR_ALREADY_EXISTS == error)
471 * Create an (empty) temporary directory on disk. If the given name is not
472 * an absolute path, the current 'TMPDIR' will be prepended. In any case,
473 * 6 random characters will be appended to the name to create a unique
476 * @param t component to use for the name;
477 * does NOT contain "XXXXXX" or "/tmp/".
478 * @return NULL on error, otherwise name of fresh
479 * file on disk in directory for temporary files
482 GNUNET_DISK_mkdtemp (const char *t)
486 fn = mktemp_name (t);
487 if (fn != mkdtemp (fn))
489 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_ERROR, "mkstemp", fn);
498 * Create an (empty) temporary file on disk. If the given name is not
499 * an absolute path, the current 'TMPDIR' will be prepended. In any case,
500 * 6 random characters will be appended to the name to create a unique
503 * @param t component to use for the name;
504 * does NOT contain "XXXXXX" or "/tmp/".
505 * @return NULL on error, otherwise name of fresh
506 * file on disk in directory for temporary files
509 GNUNET_DISK_mktemp (const char *t)
514 fn = mktemp_name (t);
515 if (-1 == (fd = mkstemp (fn)))
517 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_ERROR, "mkstemp", fn);
522 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "close", fn);
528 * Get the number of blocks that are left on the partition that
529 * contains the given file (for normal users).
531 * @param part a file on the partition to check
532 * @return -1 on errors, otherwise the number of free blocks
535 GNUNET_DISK_get_blocks_available (const char *part)
540 if (0 != statvfs (part, &buf))
542 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "statfs", part);
550 wchar_t wpath[MAX_PATH + 1];
553 path = GNUNET_STRINGS_filename_expand (part);
556 /* "part" was in UTF-8, and so is "path" */
557 if (ERROR_SUCCESS != plibc_conv_to_win_pathwconv(path, wpath))
563 wcsncpy (szDrive, wpath, 3);
565 if (!GetDiskFreeSpaceW (szDrive, &dwDummy, &dwDummy, &dwBlocks, &dwDummy))
567 LOG (GNUNET_ERROR_TYPE_WARNING, _("`%s' failed for drive `%S': %u\n"),
568 "GetDiskFreeSpace", szDrive, GetLastError ());
576 if (0 != statfs (part, &s))
578 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "statfs", part);
587 * Test if "fil" is a directory.
588 * Will not print an error message if the directory
589 * does not exist. Will log errors if GNUNET_SYSERR is
590 * returned (i.e., a file exists with the same name).
592 * @param fil filename to test
593 * @return GNUNET_YES if yes, GNUNET_NO if not, GNUNET_SYSERR if it
597 GNUNET_DISK_directory_test (const char *fil)
599 struct stat filestat;
602 ret = STAT (fil, &filestat);
607 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "stat", fil);
608 return GNUNET_SYSERR;
612 if (!S_ISDIR (filestat.st_mode))
614 if (ACCESS (fil, R_OK | X_OK) < 0)
616 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "access", fil);
617 return GNUNET_SYSERR;
624 * Check that fil corresponds to a filename
625 * (of a file that exists and that is not a directory).
627 * @param fil filename to check
628 * @return GNUNET_YES if yes, GNUNET_NO if not a file, GNUNET_SYSERR if something
629 * else (will print an error message in that case, too).
632 GNUNET_DISK_file_test (const char *fil)
634 struct stat filestat;
638 rdir = GNUNET_STRINGS_filename_expand (fil);
640 return GNUNET_SYSERR;
642 ret = STAT (rdir, &filestat);
647 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "stat", rdir);
649 return GNUNET_SYSERR;
654 if (!S_ISREG (filestat.st_mode))
659 if (ACCESS (rdir, F_OK) < 0)
661 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "access", rdir);
663 return GNUNET_SYSERR;
671 * Implementation of "mkdir -p"
672 * @param dir the directory to create
673 * @returns GNUNET_OK on success, GNUNET_SYSERR on failure
676 GNUNET_DISK_directory_create (const char *dir)
683 rdir = GNUNET_STRINGS_filename_expand (dir);
685 return GNUNET_SYSERR;
689 pos = 1; /* skip heading '/' */
691 /* Local or Network path? */
692 if (strncmp (rdir, "\\\\", 2) == 0)
697 if (rdir[pos] == '\\')
707 pos = 3; /* strlen("C:\\") */
712 if ((rdir[pos] == DIR_SEPARATOR) || (pos == len))
715 ret = GNUNET_DISK_directory_test (rdir);
716 if (ret == GNUNET_SYSERR)
719 return GNUNET_SYSERR;
721 if (ret == GNUNET_NO)
724 ret = mkdir (rdir, S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH); /* 755 */
726 wchar_t wrdir[MAX_PATH + 1];
727 if (ERROR_SUCCESS == plibc_conv_to_win_pathwconv(rdir, wrdir))
728 ret = !CreateDirectoryW (wrdir, NULL);
732 if ((ret != 0) && (errno != EEXIST))
734 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_ERROR, "mkdir", rdir);
736 return GNUNET_SYSERR;
739 rdir[pos] = DIR_SEPARATOR;
749 * Create the directory structure for storing
752 * @param filename name of a file in the directory
753 * @returns GNUNET_OK on success,
754 * GNUNET_SYSERR on failure,
755 * GNUNET_NO if the directory
756 * exists but is not writeable for us
759 GNUNET_DISK_directory_create_for_file (const char *filename)
765 rdir = GNUNET_STRINGS_filename_expand (filename);
767 return GNUNET_SYSERR;
769 while ((len > 0) && (rdir[len] != DIR_SEPARATOR))
772 ret = GNUNET_DISK_directory_create (rdir);
773 if ((ret == GNUNET_OK) && (0 != ACCESS (rdir, W_OK)))
781 * Read the contents of a binary file into a buffer.
782 * @param h handle to an open file
783 * @param result the buffer to write the result to
784 * @param len the maximum number of bytes to read
785 * @return the number of bytes read on success, GNUNET_SYSERR on failure
788 GNUNET_DISK_file_read (const struct GNUNET_DISK_FileHandle * h, void *result,
794 return GNUNET_SYSERR;
800 if (h->type != GNUNET_DISK_HANLDE_TYPE_PIPE)
802 if (!ReadFile (h->h, result, len, &bytesRead, NULL))
804 SetErrnoFromWinError (GetLastError ());
805 return GNUNET_SYSERR;
810 if (!ReadFile (h->h, result, len, &bytesRead, h->oOverlapRead))
812 if (GetLastError () != ERROR_IO_PENDING)
814 LOG (GNUNET_ERROR_TYPE_DEBUG, "Error reading from pipe: %u\n", GetLastError ());
815 SetErrnoFromWinError (GetLastError ());
816 return GNUNET_SYSERR;
818 LOG (GNUNET_ERROR_TYPE_DEBUG, "Will get overlapped result\n");
819 GetOverlappedResult (h->h, h->oOverlapRead, &bytesRead, TRUE);
821 LOG (GNUNET_ERROR_TYPE_DEBUG, "Read %u bytes from pipe\n", bytesRead);
825 return read (h->fd, result, len);
831 * Read the contents of a binary file into a buffer.
832 * Guarantees not to block (returns GNUNET_SYSERR and sets errno to EAGAIN
833 * when no data can be read).
835 * @param h handle to an open file
836 * @param result the buffer to write the result to
837 * @param len the maximum number of bytes to read
838 * @return the number of bytes read on success, GNUNET_SYSERR on failure
841 GNUNET_DISK_file_read_non_blocking (const struct GNUNET_DISK_FileHandle * h,
848 return GNUNET_SYSERR;
854 if (h->type != GNUNET_DISK_HANLDE_TYPE_PIPE)
856 if (!ReadFile (h->h, result, len, &bytesRead, NULL))
858 SetErrnoFromWinError (GetLastError ());
859 return GNUNET_SYSERR;
864 if (!ReadFile (h->h, result, len, &bytesRead, h->oOverlapRead))
866 if (GetLastError () != ERROR_IO_PENDING)
868 LOG (GNUNET_ERROR_TYPE_DEBUG, "Error reading from pipe: %u\n", GetLastError ());
869 SetErrnoFromWinError (GetLastError ());
870 return GNUNET_SYSERR;
874 LOG (GNUNET_ERROR_TYPE_DEBUG,
875 "ReadFile() queued a read, cancelling\n");
878 return GNUNET_SYSERR;
881 LOG (GNUNET_ERROR_TYPE_DEBUG, "Read %u bytes\n", bytesRead);
888 /* set to non-blocking, read, then set back */
889 flags = fcntl (h->fd, F_GETFL);
890 if (0 == (flags & O_NONBLOCK))
891 (void) fcntl (h->fd, F_SETFL, flags | O_NONBLOCK);
892 ret = read (h->fd, result, len);
893 if (0 == (flags & O_NONBLOCK))
896 (void) fcntl (h->fd, F_SETFL, flags);
905 * Read the contents of a binary file into a buffer.
907 * @param fn file name
908 * @param result the buffer to write the result to
909 * @param len the maximum number of bytes to read
910 * @return number of bytes read, GNUNET_SYSERR on failure
913 GNUNET_DISK_fn_read (const char *fn, void *result, size_t len)
915 struct GNUNET_DISK_FileHandle *fh;
918 fh = GNUNET_DISK_file_open (fn, GNUNET_DISK_OPEN_READ, GNUNET_DISK_PERM_NONE);
920 return GNUNET_SYSERR;
921 ret = GNUNET_DISK_file_read (fh, result, len);
922 GNUNET_assert (GNUNET_OK == GNUNET_DISK_file_close (fh));
929 * Write a buffer to a file.
930 * @param h handle to open file
931 * @param buffer the data to write
932 * @param n number of bytes to write
933 * @return number of bytes written on success, GNUNET_SYSERR on error
936 GNUNET_DISK_file_write (const struct GNUNET_DISK_FileHandle * h,
937 const void *buffer, size_t n)
942 return GNUNET_SYSERR;
948 if (h->type != GNUNET_DISK_HANLDE_TYPE_PIPE)
950 if (!WriteFile (h->h, buffer, n, &bytesWritten, NULL))
952 SetErrnoFromWinError (GetLastError ());
953 return GNUNET_SYSERR;
958 LOG (GNUNET_ERROR_TYPE_DEBUG, "It is a pipe trying to write %u bytes\n", n);
959 if (!WriteFile (h->h, buffer, n, &bytesWritten, h->oOverlapWrite))
961 if (GetLastError () != ERROR_IO_PENDING)
963 SetErrnoFromWinError (GetLastError ());
964 LOG (GNUNET_ERROR_TYPE_DEBUG, "Error writing to pipe: %u\n",
966 return GNUNET_SYSERR;
968 LOG (GNUNET_ERROR_TYPE_DEBUG, "Will get overlapped result\n");
969 if (!GetOverlappedResult (h->h, h->oOverlapWrite, &bytesWritten, TRUE))
971 SetErrnoFromWinError (GetLastError ());
972 LOG (GNUNET_ERROR_TYPE_DEBUG,
973 "Error getting overlapped result while writing to pipe: %u\n",
975 return GNUNET_SYSERR;
981 if (!GetOverlappedResult (h->h, h->oOverlapWrite, &ovr, TRUE))
983 LOG (GNUNET_ERROR_TYPE_DEBUG,
984 "Error getting control overlapped result while writing to pipe: %u\n",
989 LOG (GNUNET_ERROR_TYPE_DEBUG,
990 "Wrote %u bytes (ovr says %u), picking the greatest\n",
994 if (bytesWritten == 0)
998 LOG (GNUNET_ERROR_TYPE_DEBUG, "Wrote %u bytes, returning -1 with EAGAIN\n", bytesWritten);
1000 return GNUNET_SYSERR;
1003 LOG (GNUNET_ERROR_TYPE_DEBUG, "Wrote %u bytes\n", bytesWritten);
1005 return bytesWritten;
1007 return write (h->fd, buffer, n);
1013 * Write a buffer to a file, blocking, if necessary.
1014 * @param h handle to open file
1015 * @param buffer the data to write
1016 * @param n number of bytes to write
1017 * @return number of bytes written on success, GNUNET_SYSERR on error
1020 GNUNET_DISK_file_write_blocking (const struct GNUNET_DISK_FileHandle * h,
1021 const void *buffer, size_t n)
1026 return GNUNET_SYSERR;
1031 /* We do a non-overlapped write, which is as blocking as it gets */
1032 LOG (GNUNET_ERROR_TYPE_DEBUG, "Writing %u bytes\n", n);
1033 if (!WriteFile (h->h, buffer, n, &bytesWritten, NULL))
1035 SetErrnoFromWinError (GetLastError ());
1036 LOG (GNUNET_ERROR_TYPE_DEBUG, "Error writing to pipe: %u\n",
1038 return GNUNET_SYSERR;
1040 if (bytesWritten == 0 && n > 0)
1042 LOG (GNUNET_ERROR_TYPE_DEBUG, "Waiting for pipe to clean\n");
1043 WaitForSingleObject (h->h, INFINITE);
1044 if (!WriteFile (h->h, buffer, n, &bytesWritten, NULL))
1046 SetErrnoFromWinError (GetLastError ());
1047 LOG (GNUNET_ERROR_TYPE_DEBUG, "Error writing to pipe: %u\n",
1049 return GNUNET_SYSERR;
1052 LOG (GNUNET_ERROR_TYPE_DEBUG, "Wrote %u bytes\n", bytesWritten);
1053 return bytesWritten;
1058 /* set to blocking, write, then set back */
1059 flags = fcntl (h->fd, F_GETFL);
1060 if (0 != (flags & O_NONBLOCK))
1061 (void) fcntl (h->fd, F_SETFL, flags - O_NONBLOCK);
1062 ret = write (h->fd, buffer, n);
1063 if (0 == (flags & O_NONBLOCK))
1064 (void) fcntl (h->fd, F_SETFL, flags);
1071 * Write a buffer to a file. If the file is longer than the
1072 * number of bytes that will be written, it will be truncated.
1074 * @param fn file name
1075 * @param buffer the data to write
1076 * @param n number of bytes to write
1077 * @param mode file permissions
1078 * @return number of bytes written on success, GNUNET_SYSERR on error
1081 GNUNET_DISK_fn_write (const char *fn, const void *buffer, size_t n,
1082 enum GNUNET_DISK_AccessPermissions mode)
1084 struct GNUNET_DISK_FileHandle *fh;
1087 fh = GNUNET_DISK_file_open (fn,
1088 GNUNET_DISK_OPEN_WRITE | GNUNET_DISK_OPEN_TRUNCATE
1089 | GNUNET_DISK_OPEN_CREATE, mode);
1091 return GNUNET_SYSERR;
1092 ret = GNUNET_DISK_file_write (fh, buffer, n);
1093 GNUNET_assert (GNUNET_OK == GNUNET_DISK_file_close (fh));
1099 * Scan a directory for files.
1101 * @param dirName the name of the directory
1102 * @param callback the method to call for each file,
1103 * can be NULL, in that case, we only count
1104 * @param callback_cls closure for callback
1105 * @return the number of files found, GNUNET_SYSERR on error or
1106 * ieration aborted by callback returning GNUNET_SYSERR
1109 GNUNET_DISK_directory_scan (const char *dirName,
1110 GNUNET_FileNameCallback callback,
1114 struct dirent *finfo;
1119 unsigned int name_len;
1120 unsigned int n_size;
1122 GNUNET_assert (dirName != NULL);
1123 dname = GNUNET_STRINGS_filename_expand (dirName);
1125 return GNUNET_SYSERR;
1126 while ((strlen (dname) > 0) && (dname[strlen (dname) - 1] == DIR_SEPARATOR))
1127 dname[strlen (dname) - 1] = '\0';
1128 if (0 != STAT (dname, &istat))
1130 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "stat", dname);
1131 GNUNET_free (dname);
1132 return GNUNET_SYSERR;
1134 if (!S_ISDIR (istat.st_mode))
1136 LOG (GNUNET_ERROR_TYPE_WARNING, _("Expected `%s' to be a directory!\n"),
1138 GNUNET_free (dname);
1139 return GNUNET_SYSERR;
1142 dinfo = OPENDIR (dname);
1143 if ((errno == EACCES) || (dinfo == NULL))
1145 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "opendir", dname);
1148 GNUNET_free (dname);
1149 return GNUNET_SYSERR;
1152 n_size = strlen (dname) + name_len + 2;
1153 name = GNUNET_malloc (n_size);
1154 while ((finfo = READDIR (dinfo)) != NULL)
1156 if ((0 == strcmp (finfo->d_name, ".")) ||
1157 (0 == strcmp (finfo->d_name, "..")))
1159 if (callback != NULL)
1161 if (name_len < strlen (finfo->d_name))
1164 name_len = strlen (finfo->d_name);
1165 n_size = strlen (dname) + name_len + 2;
1166 name = GNUNET_malloc (n_size);
1168 /* dname can end in "/" only if dname == "/";
1169 * if dname does not end in "/", we need to add
1170 * a "/" (otherwise, we must not!) */
1171 GNUNET_snprintf (name, n_size, "%s%s%s", dname,
1172 (strcmp (dname, DIR_SEPARATOR_STR) ==
1173 0) ? "" : DIR_SEPARATOR_STR, finfo->d_name);
1174 if (GNUNET_OK != callback (callback_cls, name))
1178 GNUNET_free (dname);
1179 return GNUNET_SYSERR;
1186 GNUNET_free (dname);
1192 * Opaque handle used for iterating over a directory.
1194 struct GNUNET_DISK_DirectoryIterator
1198 * Function to call on directory entries.
1200 GNUNET_DISK_DirectoryIteratorCallback callback;
1203 * Closure for callback.
1208 * Reference to directory.
1218 * Next filename to process.
1225 enum GNUNET_SCHEDULER_Priority priority;
1231 * Task used by the directory iterator.
1234 directory_iterator_task (void *cls,
1235 const struct GNUNET_SCHEDULER_TaskContext *tc)
1237 struct GNUNET_DISK_DirectoryIterator *iter = cls;
1240 name = iter->next_name;
1241 GNUNET_assert (name != NULL);
1242 iter->next_name = NULL;
1243 iter->callback (iter->callback_cls, iter, name, iter->dirname);
1249 * This function must be called during the DiskIteratorCallback
1250 * (exactly once) to schedule the task to process the next
1251 * filename in the directory (if there is one).
1253 * @param iter opaque handle for the iterator
1254 * @param can set to GNUNET_YES to terminate the iteration early
1255 * @return GNUNET_YES if iteration will continue,
1256 * GNUNET_NO if this was the last entry (and iteration is complete),
1257 * GNUNET_SYSERR if abort was YES
1260 GNUNET_DISK_directory_iterator_next (struct GNUNET_DISK_DirectoryIterator *iter,
1263 struct dirent *finfo;
1265 GNUNET_assert (iter->next_name == NULL);
1266 if (can == GNUNET_YES)
1268 CLOSEDIR (iter->directory);
1269 GNUNET_free (iter->dirname);
1271 return GNUNET_SYSERR;
1273 while (NULL != (finfo = READDIR (iter->directory)))
1275 if ((0 == strcmp (finfo->d_name, ".")) ||
1276 (0 == strcmp (finfo->d_name, "..")))
1278 GNUNET_asprintf (&iter->next_name, "%s%s%s", iter->dirname,
1279 DIR_SEPARATOR_STR, finfo->d_name);
1284 GNUNET_DISK_directory_iterator_next (iter, GNUNET_YES);
1287 GNUNET_SCHEDULER_add_with_priority (iter->priority, &directory_iterator_task,
1294 * Scan a directory for files using the scheduler to run a task for
1295 * each entry. The name of the directory must be expanded first (!).
1296 * If a scheduler does not need to be used, GNUNET_DISK_directory_scan
1297 * may provide a simpler API.
1299 * @param prio priority to use
1300 * @param dirName the name of the directory
1301 * @param callback the method to call for each file
1302 * @param callback_cls closure for callback
1303 * @return GNUNET_YES if directory is not empty and 'callback'
1304 * will be called later, GNUNET_NO otherwise, GNUNET_SYSERR on error.
1307 GNUNET_DISK_directory_iterator_start (enum GNUNET_SCHEDULER_Priority prio,
1308 const char *dirName,
1309 GNUNET_DISK_DirectoryIteratorCallback
1310 callback, void *callback_cls)
1312 struct GNUNET_DISK_DirectoryIterator *di;
1314 di = GNUNET_malloc (sizeof (struct GNUNET_DISK_DirectoryIterator));
1315 di->callback = callback;
1316 di->callback_cls = callback_cls;
1317 di->directory = OPENDIR (dirName);
1318 if (di->directory == NULL)
1321 callback (callback_cls, NULL, NULL, NULL);
1322 return GNUNET_SYSERR;
1324 di->dirname = GNUNET_strdup (dirName);
1325 di->priority = prio;
1326 return GNUNET_DISK_directory_iterator_next (di, GNUNET_NO);
1331 * Function that removes the given directory by calling
1332 * "GNUNET_DISK_directory_remove".
1334 * @param unused not used
1335 * @param fn directory to remove
1339 remove_helper (void *unused, const char *fn)
1341 (void) GNUNET_DISK_directory_remove (fn);
1347 * Remove all files in a directory (rm -rf). Call with
1351 * @param filename the file to remove
1352 * @return GNUNET_OK on success, GNUNET_SYSERR on error
1355 GNUNET_DISK_directory_remove (const char *filename)
1359 if (0 != LSTAT (filename, &istat))
1360 return GNUNET_NO; /* file may not exist... */
1361 (void) CHMOD (filename, S_IWUSR | S_IRUSR | S_IXUSR);
1362 if (UNLINK (filename) == 0)
1364 if ((errno != EISDIR) &&
1365 /* EISDIR is not sufficient in all cases, e.g.
1366 * sticky /tmp directory may result in EPERM on BSD.
1367 * So we also explicitly check "isDirectory" */
1368 (GNUNET_YES != GNUNET_DISK_directory_test (filename)))
1370 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "rmdir", filename);
1371 return GNUNET_SYSERR;
1373 if (GNUNET_SYSERR ==
1374 GNUNET_DISK_directory_scan (filename, &remove_helper, NULL))
1375 return GNUNET_SYSERR;
1376 if (0 != RMDIR (filename))
1378 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "rmdir", filename);
1379 return GNUNET_SYSERR;
1388 * @param src file to copy
1389 * @param dst destination file name
1390 * @return GNUNET_OK on success, GNUNET_SYSERR on error
1393 GNUNET_DISK_file_copy (const char *src, const char *dst)
1399 struct GNUNET_DISK_FileHandle *in;
1400 struct GNUNET_DISK_FileHandle *out;
1402 if (GNUNET_OK != GNUNET_DISK_file_size (src, &size, GNUNET_YES, GNUNET_YES))
1403 return GNUNET_SYSERR;
1405 in = GNUNET_DISK_file_open (src, GNUNET_DISK_OPEN_READ,
1406 GNUNET_DISK_PERM_NONE);
1408 return GNUNET_SYSERR;
1410 GNUNET_DISK_file_open (dst,
1411 GNUNET_DISK_OPEN_WRITE | GNUNET_DISK_OPEN_CREATE |
1412 GNUNET_DISK_OPEN_FAILIFEXISTS,
1413 GNUNET_DISK_PERM_USER_READ |
1414 GNUNET_DISK_PERM_USER_WRITE |
1415 GNUNET_DISK_PERM_GROUP_READ |
1416 GNUNET_DISK_PERM_GROUP_WRITE);
1419 GNUNET_DISK_file_close (in);
1420 return GNUNET_SYSERR;
1422 buf = GNUNET_malloc (COPY_BLK_SIZE);
1425 len = COPY_BLK_SIZE;
1426 if (len > size - pos)
1428 if (len != GNUNET_DISK_file_read (in, buf, len))
1430 if (len != GNUNET_DISK_file_write (out, buf, len))
1435 GNUNET_DISK_file_close (in);
1436 GNUNET_DISK_file_close (out);
1440 GNUNET_DISK_file_close (in);
1441 GNUNET_DISK_file_close (out);
1442 return GNUNET_SYSERR;
1447 * @brief Removes special characters as ':' from a filename.
1448 * @param fn the filename to canonicalize
1451 GNUNET_DISK_filename_canonicalize (char *fn)
1461 if (c == '/' || c == '\\' || c == ':' || c == '*' || c == '?' || c == '"' ||
1462 c == '<' || c == '>' || c == '|')
1474 * @brief Change owner of a file
1476 * @param filename name of file to change the owner of
1477 * @param user name of the new owner
1478 * @return GNUNET_OK on success, GNUNET_SYSERR on failure
1481 GNUNET_DISK_file_change_owner (const char *filename, const char *user)
1486 pws = getpwnam (user);
1489 LOG (GNUNET_ERROR_TYPE_ERROR,
1490 _("Cannot obtain information about user `%s': %s\n"), user,
1492 return GNUNET_SYSERR;
1494 if (0 != chown (filename, pws->pw_uid, pws->pw_gid))
1495 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "chown", filename);
1502 * Lock a part of a file
1503 * @param fh file handle
1504 * @param lockStart absolute position from where to lock
1505 * @param lockEnd absolute position until where to lock
1506 * @param excl GNUNET_YES for an exclusive lock
1507 * @return GNUNET_OK on success, GNUNET_SYSERR on error
1510 GNUNET_DISK_file_lock (struct GNUNET_DISK_FileHandle *fh, OFF_T lockStart,
1511 OFF_T lockEnd, int excl)
1516 return GNUNET_SYSERR;
1522 memset (&fl, 0, sizeof (struct flock));
1523 fl.l_type = excl ? F_WRLCK : F_RDLCK;
1524 fl.l_whence = SEEK_SET;
1525 fl.l_start = lockStart;
1528 return fcntl (fh->fd, F_SETLK, &fl) != 0 ? GNUNET_SYSERR : GNUNET_OK;
1531 OFF_T diff = lockEnd - lockStart;
1532 DWORD diff_low, diff_high;
1533 diff_low = (DWORD) (diff & 0xFFFFFFFF);
1534 diff_high = (DWORD) ((diff >> (sizeof (DWORD) * 8)) & 0xFFFFFFFF);
1536 memset (&o, 0, sizeof (OVERLAPPED));
1537 o.Offset = (DWORD) (lockStart & 0xFFFFFFFF);;
1538 o.OffsetHigh = (DWORD) (((lockStart & ~0xFFFFFFFF) >> (sizeof (DWORD) * 8)) & 0xFFFFFFFF);
1541 (fh->h, (excl ? LOCKFILE_EXCLUSIVE_LOCK : 0) | LOCKFILE_FAIL_IMMEDIATELY,
1542 0, diff_low, diff_high, &o))
1544 SetErrnoFromWinError (GetLastError ());
1545 return GNUNET_SYSERR;
1554 * Unlock a part of a file
1555 * @param fh file handle
1556 * @param unlockStart absolute position from where to unlock
1557 * @param unlockEnd absolute position until where to unlock
1558 * @return GNUNET_OK on success, GNUNET_SYSERR on error
1561 GNUNET_DISK_file_unlock (struct GNUNET_DISK_FileHandle *fh, OFF_T unlockStart,
1567 return GNUNET_SYSERR;
1573 memset (&fl, 0, sizeof (struct flock));
1574 fl.l_type = F_UNLCK;
1575 fl.l_whence = SEEK_SET;
1576 fl.l_start = unlockStart;
1577 fl.l_len = unlockEnd;
1579 return fcntl (fh->fd, F_SETLK, &fl) != 0 ? GNUNET_SYSERR : GNUNET_OK;
1582 OFF_T diff = unlockEnd - unlockStart;
1583 DWORD diff_low, diff_high;
1584 diff_low = (DWORD) (diff & 0xFFFFFFFF);
1585 diff_high = (DWORD) ((diff >> (sizeof (DWORD) * 8)) & 0xFFFFFFFF);
1587 memset (&o, 0, sizeof (OVERLAPPED));
1588 o.Offset = (DWORD) (unlockStart & 0xFFFFFFFF);;
1589 o.OffsetHigh = (DWORD) (((unlockStart & ~0xFFFFFFFF) >> (sizeof (DWORD) * 8)) & 0xFFFFFFFF);
1591 if (!UnlockFileEx (fh->h, 0, diff_low, diff_high, &o))
1593 SetErrnoFromWinError (GetLastError ());
1594 return GNUNET_SYSERR;
1603 * Open a file. Note that the access permissions will only be
1604 * used if a new file is created and if the underlying operating
1605 * system supports the given permissions.
1607 * @param fn file name to be opened
1608 * @param flags opening flags, a combination of GNUNET_DISK_OPEN_xxx bit flags
1609 * @param perm permissions for the newly created file, use
1610 * GNUNET_DISK_PERM_USER_NONE if a file could not be created by this
1611 * call (because of flags)
1612 * @return IO handle on success, NULL on error
1614 struct GNUNET_DISK_FileHandle *
1615 GNUNET_DISK_file_open (const char *fn, enum GNUNET_DISK_OpenFlags flags,
1616 enum GNUNET_DISK_AccessPermissions perm)
1619 struct GNUNET_DISK_FileHandle *ret;
1625 wchar_t wexpfn[MAX_PATH + 1];
1632 expfn = GNUNET_STRINGS_filename_expand (fn);
1637 if (GNUNET_DISK_OPEN_READWRITE == (flags & GNUNET_DISK_OPEN_READWRITE))
1638 oflags = O_RDWR; /* note: O_RDWR is NOT always O_RDONLY | O_WRONLY */
1639 else if (flags & GNUNET_DISK_OPEN_READ)
1641 else if (flags & GNUNET_DISK_OPEN_WRITE)
1646 GNUNET_free (expfn);
1649 if (flags & GNUNET_DISK_OPEN_FAILIFEXISTS)
1650 oflags |= (O_CREAT | O_EXCL);
1651 if (flags & GNUNET_DISK_OPEN_TRUNCATE)
1653 if (flags & GNUNET_DISK_OPEN_APPEND)
1655 if (flags & GNUNET_DISK_OPEN_CREATE)
1657 (void) GNUNET_DISK_directory_create_for_file (expfn);
1659 mode = translate_unix_perms (perm);
1662 fd = open (expfn, oflags | O_LARGEFILE, mode);
1665 if (0 == (flags & GNUNET_DISK_OPEN_FAILIFEXISTS))
1666 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "open", expfn);
1668 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_DEBUG, "open", expfn);
1669 GNUNET_free (expfn);
1676 if (GNUNET_DISK_OPEN_READWRITE == (flags & GNUNET_DISK_OPEN_READWRITE))
1677 access = FILE_READ_DATA | FILE_WRITE_DATA;
1678 else if (flags & GNUNET_DISK_OPEN_READ)
1679 access = FILE_READ_DATA;
1680 else if (flags & GNUNET_DISK_OPEN_WRITE)
1681 access = FILE_WRITE_DATA;
1683 if (flags & GNUNET_DISK_OPEN_FAILIFEXISTS)
1687 else if (flags & GNUNET_DISK_OPEN_CREATE)
1689 (void) GNUNET_DISK_directory_create_for_file (expfn);
1690 if (flags & GNUNET_DISK_OPEN_TRUNCATE)
1691 disp = CREATE_ALWAYS;
1695 else if (flags & GNUNET_DISK_OPEN_TRUNCATE)
1697 disp = TRUNCATE_EXISTING;
1701 disp = OPEN_EXISTING;
1704 if (ERROR_SUCCESS == plibc_conv_to_win_pathwconv(expfn, wexpfn))
1705 h = CreateFileW (wexpfn, access,
1706 FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE, NULL,
1707 disp, FILE_ATTRIBUTE_NORMAL, NULL);
1709 h = INVALID_HANDLE_VALUE;
1710 if (h == INVALID_HANDLE_VALUE)
1713 SetErrnoFromWinError (GetLastError ());
1715 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_INFO, "open", expfn);
1716 GNUNET_free (expfn);
1721 if (flags & GNUNET_DISK_OPEN_APPEND)
1722 if (SetFilePointer (h, 0, 0, FILE_END) == INVALID_SET_FILE_POINTER)
1724 SetErrnoFromWinError (GetLastError ());
1725 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "SetFilePointer", expfn);
1727 GNUNET_free (expfn);
1732 ret = GNUNET_malloc (sizeof (struct GNUNET_DISK_FileHandle));
1735 ret->type = GNUNET_DISK_HANLDE_TYPE_FILE;
1739 GNUNET_free (expfn);
1745 * Close an open file
1746 * @param h file handle
1747 * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
1750 GNUNET_DISK_file_close (struct GNUNET_DISK_FileHandle *h)
1755 return GNUNET_SYSERR;
1759 if (!CloseHandle (h->h))
1761 SetErrnoFromWinError (GetLastError ());
1762 LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING, "close");
1763 GNUNET_free (h->oOverlapRead);
1764 GNUNET_free (h->oOverlapWrite);
1766 return GNUNET_SYSERR;
1769 if (close (h->fd) != 0)
1771 LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING, "close");
1773 return GNUNET_SYSERR;
1782 * Get a handle from a native FD.
1784 * @param fd native file descriptor
1785 * @return file handle corresponding to the descriptor
1787 struct GNUNET_DISK_FileHandle *
1788 GNUNET_DISK_get_handle_from_native (FILE *fd)
1790 struct GNUNET_DISK_FileHandle *fh;
1801 osfh = _get_osfhandle (fno);
1802 if (INVALID_HANDLE_VALUE == (HANDLE) osfh)
1806 fh = GNUNET_malloc (sizeof (struct GNUNET_DISK_FileHandle));
1809 fh->h = (HANDLE) osfh;
1810 /* Assume it to be a pipe. TODO: use some kind of detection
1811 * function to figure out handle type.
1812 * Note that we can't make it overlapped if it isn't already.
1813 * (ReOpenFile() is only available in 2003/Vista).
1814 * The process that opened this file in the first place (usually a parent
1815 * process, if this is stdin/stdout/stderr) must make it overlapped,
1816 * otherwise we're screwed, as selecting on non-overlapped handle
1819 fh->type = GNUNET_DISK_HANLDE_TYPE_PIPE;
1820 fh->oOverlapRead = GNUNET_malloc (sizeof (OVERLAPPED));
1821 fh->oOverlapWrite = GNUNET_malloc (sizeof (OVERLAPPED));
1822 fh->oOverlapRead->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
1823 fh->oOverlapWrite->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
1833 * Construct full path to a file inside of the private
1834 * directory used by GNUnet. Also creates the corresponding
1835 * directory. If the resulting name is supposed to be
1836 * a directory, end the last argument in '/' (or pass
1837 * DIR_SEPARATOR_STR as the last argument before NULL).
1839 * @param cfg configuration to use (determines HOME)
1840 * @param serviceName name of the service
1841 * @param ... is NULL-terminated list of
1842 * path components to append to the
1843 * private directory name.
1844 * @return the constructed filename
1847 GNUNET_DISK_get_home_filename (const struct GNUNET_CONFIGURATION_Handle *cfg,
1848 const char *serviceName, ...)
1854 unsigned int needed;
1857 GNUNET_CONFIGURATION_get_value_filename (cfg, serviceName, "HOME", &pfx))
1861 LOG (GNUNET_ERROR_TYPE_WARNING,
1862 _("No `%s' specified for service `%s' in configuration.\n"), "HOME",
1866 needed = strlen (pfx) + 2;
1867 if ((pfx[strlen (pfx) - 1] != '/') && (pfx[strlen (pfx) - 1] != '\\'))
1869 va_start (ap, serviceName);
1872 c = va_arg (ap, const char *);
1876 needed += strlen (c);
1877 if ((c[strlen (c) - 1] != '/') && (c[strlen (c) - 1] != '\\'))
1881 ret = GNUNET_malloc (needed);
1884 va_start (ap, serviceName);
1887 c = va_arg (ap, const char *);
1891 if ((c[strlen (c) - 1] != '/') && (c[strlen (c) - 1] != '\\'))
1892 strcat (ret, DIR_SEPARATOR_STR);
1896 if ((ret[strlen (ret) - 1] != '/') && (ret[strlen (ret) - 1] != '\\'))
1897 (void) GNUNET_DISK_directory_create_for_file (ret);
1899 (void) GNUNET_DISK_directory_create (ret);
1905 * Handle for a memory-mapping operation.
1907 struct GNUNET_DISK_MapHandle
1910 * Address where the map is in memory.
1916 * Underlying OS handle.
1921 * Number of bytes mapped.
1929 #define MAP_FAILED ((void *) -1)
1933 * Map a file into memory
1935 * @param h open file handle
1936 * @param m handle to the new mapping
1937 * @param access access specification, GNUNET_DISK_MAP_TYPE_xxx
1938 * @param len size of the mapping
1939 * @return pointer to the mapped memory region, NULL on failure
1942 GNUNET_DISK_file_map (const struct GNUNET_DISK_FileHandle *h,
1943 struct GNUNET_DISK_MapHandle **m,
1944 enum GNUNET_DISK_MapType access, size_t len)
1953 DWORD mapAccess, protect;
1955 if ((access & GNUNET_DISK_MAP_TYPE_READ) &&
1956 (access & GNUNET_DISK_MAP_TYPE_WRITE))
1958 protect = PAGE_READWRITE;
1959 mapAccess = FILE_MAP_ALL_ACCESS;
1961 else if (access & GNUNET_DISK_MAP_TYPE_READ)
1963 protect = PAGE_READONLY;
1964 mapAccess = FILE_MAP_READ;
1966 else if (access & GNUNET_DISK_MAP_TYPE_WRITE)
1968 protect = PAGE_READWRITE;
1969 mapAccess = FILE_MAP_WRITE;
1977 *m = GNUNET_malloc (sizeof (struct GNUNET_DISK_MapHandle));
1978 (*m)->h = CreateFileMapping (h->h, NULL, protect, 0, 0, NULL);
1979 if ((*m)->h == INVALID_HANDLE_VALUE)
1981 SetErrnoFromWinError (GetLastError ());
1986 (*m)->addr = MapViewOfFile ((*m)->h, mapAccess, 0, 0, len);
1989 SetErrnoFromWinError (GetLastError ());
1990 CloseHandle ((*m)->h);
1999 if (access & GNUNET_DISK_MAP_TYPE_READ)
2001 if (access & GNUNET_DISK_MAP_TYPE_WRITE)
2003 *m = GNUNET_malloc (sizeof (struct GNUNET_DISK_MapHandle));
2004 (*m)->addr = mmap (NULL, len, prot, MAP_SHARED, h->fd, 0);
2005 GNUNET_assert (NULL != (*m)->addr);
2006 if (MAP_FAILED == (*m)->addr)
2018 * @param h mapping handle
2019 * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
2022 GNUNET_DISK_file_unmap (struct GNUNET_DISK_MapHandle *h)
2029 return GNUNET_SYSERR;
2033 ret = UnmapViewOfFile (h->addr) ? GNUNET_OK : GNUNET_SYSERR;
2034 if (ret != GNUNET_OK)
2035 SetErrnoFromWinError (GetLastError ());
2036 if (!CloseHandle (h->h) && (ret == GNUNET_OK))
2038 ret = GNUNET_SYSERR;
2039 SetErrnoFromWinError (GetLastError ());
2042 ret = munmap (h->addr, h->len) != -1 ? GNUNET_OK : GNUNET_SYSERR;
2050 * Write file changes to disk
2051 * @param h handle to an open file
2052 * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
2055 GNUNET_DISK_file_sync (const struct GNUNET_DISK_FileHandle *h)
2060 return GNUNET_SYSERR;
2066 ret = FlushFileBuffers (h->h) ? GNUNET_OK : GNUNET_SYSERR;
2067 if (ret != GNUNET_OK)
2068 SetErrnoFromWinError (GetLastError ());
2070 #elif defined(FREEBSD) || defined(OPENBSD) || defined(DARWIN)
2071 return fsync (h->fd) == -1 ? GNUNET_SYSERR : GNUNET_OK;
2073 return fdatasync (h->fd) == -1 ? GNUNET_SYSERR : GNUNET_OK;
2079 /* Copyright Bob Byrnes <byrnes <at> curl.com>
2080 http://permalink.gmane.org/gmane.os.cygwin.patches/2121
2082 /* Create a pipe, and return handles to the read and write ends,
2083 just like CreatePipe, but ensure that the write end permits
2084 FILE_READ_ATTRIBUTES access, on later versions of win32 where
2085 this is supported. This access is needed by NtQueryInformationFile,
2086 which is used to implement select and nonblocking writes.
2087 Note that the return value is either NO_ERROR or GetLastError,
2088 unlike CreatePipe, which returns a bool for success or failure. */
2090 create_selectable_pipe (PHANDLE read_pipe_ptr, PHANDLE write_pipe_ptr,
2091 LPSECURITY_ATTRIBUTES sa_ptr, DWORD psize,
2092 DWORD dwReadMode, DWORD dwWriteMode)
2094 /* Default to error. */
2095 *read_pipe_ptr = *write_pipe_ptr = INVALID_HANDLE_VALUE;
2097 HANDLE read_pipe = INVALID_HANDLE_VALUE, write_pipe = INVALID_HANDLE_VALUE;
2099 /* Ensure that there is enough pipe buffer space for atomic writes. */
2100 if (psize < PIPE_BUF)
2103 char pipename[MAX_PATH];
2105 /* Retry CreateNamedPipe as long as the pipe name is in use.
2106 * Retrying will probably never be necessary, but we want
2107 * to be as robust as possible. */
2110 static volatile LONG pipe_unique_id;
2112 snprintf (pipename, sizeof pipename, "\\\\.\\pipe\\gnunet-%d-%ld",
2113 getpid (), InterlockedIncrement ((LONG *) & pipe_unique_id));
2114 LOG (GNUNET_ERROR_TYPE_DEBUG, "CreateNamedPipe: name = %s, size = %lu\n",
2116 /* Use CreateNamedPipe instead of CreatePipe, because the latter
2117 * returns a write handle that does not permit FILE_READ_ATTRIBUTES
2118 * access, on versions of win32 earlier than WinXP SP2.
2119 * CreatePipe also stupidly creates a full duplex pipe, which is
2120 * a waste, since only a single direction is actually used.
2121 * It's important to only allow a single instance, to ensure that
2122 * the pipe was not created earlier by some other process, even if
2123 * the pid has been reused. We avoid FILE_FLAG_FIRST_PIPE_INSTANCE
2124 * because that is only available for Win2k SP2 and WinXP. */
2125 read_pipe = CreateNamedPipeA (pipename, PIPE_ACCESS_INBOUND | dwReadMode, PIPE_TYPE_BYTE | PIPE_READMODE_BYTE, 1, /* max instances */
2126 psize, /* output buffer size */
2127 psize, /* input buffer size */
2128 NMPWAIT_USE_DEFAULT_WAIT, sa_ptr);
2130 if (read_pipe != INVALID_HANDLE_VALUE)
2132 LOG (GNUNET_ERROR_TYPE_DEBUG, "pipe read handle = %p\n", read_pipe);
2136 DWORD err = GetLastError ();
2140 case ERROR_PIPE_BUSY:
2141 /* The pipe is already open with compatible parameters.
2142 * Pick a new name and retry. */
2143 LOG (GNUNET_ERROR_TYPE_DEBUG, "pipe busy, retrying\n");
2145 case ERROR_ACCESS_DENIED:
2146 /* The pipe is already open with incompatible parameters.
2147 * Pick a new name and retry. */
2148 LOG (GNUNET_ERROR_TYPE_DEBUG, "pipe access denied, retrying\n");
2150 case ERROR_CALL_NOT_IMPLEMENTED:
2151 /* We are on an older Win9x platform without named pipes.
2152 * Return an anonymous pipe as the best approximation. */
2153 LOG (GNUNET_ERROR_TYPE_DEBUG,
2154 "CreateNamedPipe not implemented, resorting to "
2155 "CreatePipe: size = %lu\n", psize);
2156 if (CreatePipe (read_pipe_ptr, write_pipe_ptr, sa_ptr, psize))
2158 LOG (GNUNET_ERROR_TYPE_DEBUG, "pipe read handle = %p, write handle = %p\n",
2163 err = GetLastError ();
2164 LOG (GNUNET_ERROR_TYPE_ERROR, "CreatePipe failed: %d\n", err);
2167 LOG (GNUNET_ERROR_TYPE_ERROR, "CreateNamedPipe failed: %d\n", err);
2172 LOG (GNUNET_ERROR_TYPE_DEBUG, "CreateFile: name = %s\n", pipename);
2174 /* Open the named pipe for writing.
2175 * Be sure to permit FILE_READ_ATTRIBUTES access. */
2176 write_pipe = CreateFileA (pipename, GENERIC_WRITE | FILE_READ_ATTRIBUTES, 0, /* share mode */
2177 sa_ptr, OPEN_EXISTING, dwWriteMode, /* flags and attributes */
2178 0); /* handle to template file */
2180 if (write_pipe == INVALID_HANDLE_VALUE)
2183 DWORD err = GetLastError ();
2185 LOG (GNUNET_ERROR_TYPE_DEBUG, "CreateFile failed: %d\n", err);
2186 CloseHandle (read_pipe);
2189 LOG (GNUNET_ERROR_TYPE_DEBUG, "pipe write handle = %p\n", write_pipe);
2191 *read_pipe_ptr = read_pipe;
2192 *write_pipe_ptr = write_pipe;
2199 * Creates an interprocess channel
2201 * @param blocking_read creates an asynchronous pipe for reading if set to GNUNET_NO
2202 * @param blocking_write creates an asynchronous pipe for writing if set to GNUNET_NO
2203 * @param inherit_read inherit the parent processes stdin (only for windows)
2204 * @param inherit_write inherit the parent processes stdout (only for windows)
2205 * @return handle to the new pipe, NULL on error
2207 struct GNUNET_DISK_PipeHandle *
2208 GNUNET_DISK_pipe (int blocking_read, int blocking_write, int inherit_read, int inherit_write)
2219 LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR, "pipe");
2223 return GNUNET_DISK_pipe_from_fd (blocking_read,
2227 struct GNUNET_DISK_PipeHandle *p;
2228 struct GNUNET_DISK_FileHandle *fds;
2233 p = GNUNET_malloc (sizeof (struct GNUNET_DISK_PipeHandle) +
2234 2 * sizeof (struct GNUNET_DISK_FileHandle));
2235 fds = (struct GNUNET_DISK_FileHandle *) &p[1];
2239 /* All pipes are overlapped. If you want them to block - just
2240 * call WriteFile() and ReadFile() with NULL overlapped pointer.
2243 create_selectable_pipe (&p->fd[0]->h, &p->fd[1]->h, NULL, 0,
2244 FILE_FLAG_OVERLAPPED,
2245 FILE_FLAG_OVERLAPPED);
2249 SetErrnoFromWinError (GetLastError ());
2252 if (!DuplicateHandle
2253 (GetCurrentProcess (), p->fd[0]->h, GetCurrentProcess (), &tmp_handle, 0,
2254 inherit_read == GNUNET_YES ? TRUE : FALSE, DUPLICATE_SAME_ACCESS))
2256 SetErrnoFromWinError (GetLastError ());
2257 CloseHandle (p->fd[0]->h);
2258 CloseHandle (p->fd[1]->h);
2262 CloseHandle (p->fd[0]->h);
2263 p->fd[0]->h = tmp_handle;
2265 if (!DuplicateHandle
2266 (GetCurrentProcess (), p->fd[1]->h, GetCurrentProcess (), &tmp_handle, 0,
2267 inherit_write == GNUNET_YES ? TRUE : FALSE, DUPLICATE_SAME_ACCESS))
2269 SetErrnoFromWinError (GetLastError ());
2270 CloseHandle (p->fd[0]->h);
2271 CloseHandle (p->fd[1]->h);
2275 CloseHandle (p->fd[1]->h);
2276 p->fd[1]->h = tmp_handle;
2278 p->fd[0]->type = GNUNET_DISK_HANLDE_TYPE_PIPE;
2279 p->fd[1]->type = GNUNET_DISK_HANLDE_TYPE_PIPE;
2281 p->fd[0]->oOverlapRead = GNUNET_malloc (sizeof (OVERLAPPED));
2282 p->fd[0]->oOverlapWrite = GNUNET_malloc (sizeof (OVERLAPPED));
2283 p->fd[1]->oOverlapRead = GNUNET_malloc (sizeof (OVERLAPPED));
2284 p->fd[1]->oOverlapWrite = GNUNET_malloc (sizeof (OVERLAPPED));
2286 p->fd[0]->oOverlapRead->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2287 p->fd[0]->oOverlapWrite->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2289 p->fd[1]->oOverlapRead->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2290 p->fd[1]->oOverlapWrite->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2298 * Creates a pipe object from a couple of file descriptors.
2299 * Useful for wrapping existing pipe FDs.
2301 * @param blocking_read creates an asynchronous pipe for reading if set to GNUNET_NO
2302 * @param blocking_write creates an asynchronous pipe for writing if set to GNUNET_NO
2303 * @param fd an array of two fd values. One of them may be -1 for read-only or write-only pipes
2305 * @return handle to the new pipe, NULL on error
2307 struct GNUNET_DISK_PipeHandle *
2308 GNUNET_DISK_pipe_from_fd (int blocking_read, int blocking_write, int fd[2])
2310 struct GNUNET_DISK_PipeHandle *p;
2311 struct GNUNET_DISK_FileHandle *fds;
2313 p = GNUNET_malloc (sizeof (struct GNUNET_DISK_PipeHandle) +
2314 2 * sizeof (struct GNUNET_DISK_FileHandle));
2315 fds = (struct GNUNET_DISK_FileHandle *) &p[1];
2321 int eno = 0; /* make gcc happy */
2323 p->fd[0]->fd = fd[0];
2324 p->fd[1]->fd = fd[1];
2330 flags = fcntl (fd[0], F_GETFL);
2331 flags |= O_NONBLOCK;
2332 if (0 > fcntl (fd[0], F_SETFL, flags))
2338 flags = fcntl (fd[0], F_GETFD);
2339 flags |= FD_CLOEXEC;
2340 if (0 > fcntl (fd[0], F_SETFD, flags))
2349 if (!blocking_write)
2351 flags = fcntl (fd[1], F_GETFL);
2352 flags |= O_NONBLOCK;
2353 if (0 > fcntl (fd[1], F_SETFL, flags))
2359 flags = fcntl (fd[1], F_GETFD);
2360 flags |= FD_CLOEXEC;
2361 if (0 > fcntl (fd[1], F_SETFD, flags))
2370 LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR, "fcntl");
2371 if (p->fd[0]->fd >= 0)
2372 GNUNET_break (0 == close (p->fd[0]->fd));
2373 if (p->fd[1]->fd >= 0)
2374 GNUNET_break (0 == close (p->fd[1]->fd));
2381 p->fd[0]->h = (HANDLE) _get_osfhandle (fd[0]);
2383 p->fd[0]->h = INVALID_HANDLE_VALUE;
2385 p->fd[1]->h = (HANDLE) _get_osfhandle (fd[1]);
2387 p->fd[1]->h = INVALID_HANDLE_VALUE;
2389 if (p->fd[0]->h != INVALID_HANDLE_VALUE)
2391 p->fd[0]->type = GNUNET_DISK_HANLDE_TYPE_PIPE;
2392 p->fd[0]->oOverlapRead = GNUNET_malloc (sizeof (OVERLAPPED));
2393 p->fd[0]->oOverlapWrite = GNUNET_malloc (sizeof (OVERLAPPED));
2394 p->fd[0]->oOverlapRead->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2395 p->fd[0]->oOverlapWrite->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2398 if (p->fd[1]->h != INVALID_HANDLE_VALUE)
2400 p->fd[1]->type = GNUNET_DISK_HANLDE_TYPE_PIPE;
2401 p->fd[1]->oOverlapRead = GNUNET_malloc (sizeof (OVERLAPPED));
2402 p->fd[1]->oOverlapWrite = GNUNET_malloc (sizeof (OVERLAPPED));
2403 p->fd[1]->oOverlapRead->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2404 p->fd[1]->oOverlapWrite->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2412 * Closes an interprocess channel
2414 * @param p pipe to close
2415 * @param end which end of the pipe to close
2416 * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
2419 GNUNET_DISK_pipe_close_end (struct GNUNET_DISK_PipeHandle *p,
2420 enum GNUNET_DISK_PipeEnd end)
2422 int ret = GNUNET_OK;
2426 if (end == GNUNET_DISK_PIPE_END_READ)
2428 if (p->fd[0]->h != INVALID_HANDLE_VALUE)
2430 if (!CloseHandle (p->fd[0]->h))
2432 SetErrnoFromWinError (GetLastError ());
2433 ret = GNUNET_SYSERR;
2435 GNUNET_free (p->fd[0]->oOverlapRead);
2436 GNUNET_free (p->fd[0]->oOverlapWrite);
2437 p->fd[0]->h = INVALID_HANDLE_VALUE;
2440 else if (end == GNUNET_DISK_PIPE_END_WRITE)
2442 if (p->fd[0]->h != INVALID_HANDLE_VALUE)
2444 if (!CloseHandle (p->fd[1]->h))
2446 SetErrnoFromWinError (GetLastError ());
2447 ret = GNUNET_SYSERR;
2449 GNUNET_free (p->fd[1]->oOverlapRead);
2450 GNUNET_free (p->fd[1]->oOverlapWrite);
2451 p->fd[1]->h = INVALID_HANDLE_VALUE;
2457 if (end == GNUNET_DISK_PIPE_END_READ)
2459 if (0 != close (p->fd[0]->fd))
2461 ret = GNUNET_SYSERR;
2466 else if (end == GNUNET_DISK_PIPE_END_WRITE)
2468 if (0 != close (p->fd[1]->fd))
2470 ret = GNUNET_SYSERR;
2482 * Closes an interprocess channel
2484 * @param p pipe to close
2485 * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
2488 GNUNET_DISK_pipe_close (struct GNUNET_DISK_PipeHandle *p)
2490 int ret = GNUNET_OK;
2494 if (p->fd[0]->h != INVALID_HANDLE_VALUE)
2496 if (!CloseHandle (p->fd[0]->h))
2498 SetErrnoFromWinError (GetLastError ());
2499 ret = GNUNET_SYSERR;
2501 GNUNET_free (p->fd[0]->oOverlapRead);
2502 GNUNET_free (p->fd[0]->oOverlapWrite);
2504 if (p->fd[1]->h != INVALID_HANDLE_VALUE)
2506 if (!CloseHandle (p->fd[1]->h))
2508 SetErrnoFromWinError (GetLastError ());
2509 ret = GNUNET_SYSERR;
2511 GNUNET_free (p->fd[1]->oOverlapRead);
2512 GNUNET_free (p->fd[1]->oOverlapWrite);
2517 if (p->fd[0]->fd != -1)
2519 if (0 != close (p->fd[0]->fd))
2521 ret = GNUNET_SYSERR;
2526 if (p->fd[1]->fd != -1)
2528 if (0 != close (p->fd[1]->fd))
2530 ret = GNUNET_SYSERR;
2542 * Get the handle to a particular pipe end
2545 * @param n end to access
2546 * @return handle for the respective end
2548 const struct GNUNET_DISK_FileHandle *
2549 GNUNET_DISK_pipe_handle (const struct GNUNET_DISK_PipeHandle *p,
2550 enum GNUNET_DISK_PipeEnd n)
2554 case GNUNET_DISK_PIPE_END_READ:
2555 case GNUNET_DISK_PIPE_END_WRITE:
2565 * Retrieve OS file handle
2567 * @param fh GNUnet file descriptor
2568 * @param dst destination buffer
2569 * @param dst_len length of dst
2570 * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
2573 GNUNET_DISK_internal_file_handle_ (const struct GNUNET_DISK_FileHandle *fh,
2574 void *dst, size_t dst_len)
2577 if (dst_len < sizeof (HANDLE))
2578 return GNUNET_SYSERR;
2579 *((HANDLE *) dst) = fh->h;
2581 if (dst_len < sizeof (int))
2582 return GNUNET_SYSERR;
2583 *((int *) dst) = fh->fd;