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[] = {[GNUNET_DISK_SEEK_SET] = FILE_BEGIN,
278 [GNUNET_DISK_SEEK_CUR] = FILE_CURRENT,[GNUNET_DISK_SEEK_END] = FILE_END
280 li.QuadPart = offset;
282 b = SetFilePointerEx (h->h, li, &new_pos, t[whence]);
285 SetErrnoFromWinError (GetLastError ());
286 return GNUNET_SYSERR;
288 return (OFF_T) new_pos.QuadPart;
290 static int t[] = {[GNUNET_DISK_SEEK_SET] = SEEK_SET,
291 [GNUNET_DISK_SEEK_CUR] = SEEK_CUR,[GNUNET_DISK_SEEK_END] = SEEK_END
294 return lseek (h->fd, offset, t[whence]);
300 * Get the size of the file (or directory) of the given file (in
303 * @param filename name of the file or directory
304 * @param size set to the size of the file (or,
305 * in the case of directories, the sum
306 * of all sizes of files in the directory)
307 * @param includeSymLinks should symbolic links be
309 * @param singleFileMode GNUNET_YES to only get size of one file
310 * and return GNUNET_SYSERR for directories.
311 * @return GNUNET_SYSERR on error, GNUNET_OK on success
314 GNUNET_DISK_file_size (const char *filename, uint64_t * size,
315 int includeSymLinks, int singleFileMode)
317 struct GetFileSizeData gfsd;
320 GNUNET_assert (size != NULL);
322 gfsd.include_sym_links = includeSymLinks;
323 gfsd.single_file_mode = singleFileMode;
324 ret = getSizeRec (&gfsd, filename);
331 * Obtain some unique identifiers for the given file
332 * that can be used to identify it in the local system.
333 * This function is used between GNUnet processes to
334 * quickly check if two files with the same absolute path
335 * are actually identical. The two processes represent
336 * the same peer but may communicate over the network
337 * (and the file may be on an NFS volume). This function
338 * may not be supported on all operating systems.
340 * @param filename name of the file
341 * @param dev set to the device ID
342 * @param ino set to the inode ID
343 * @return GNUNET_OK on success
346 GNUNET_DISK_file_get_identifiers (const char *filename, uint64_t * dev,
353 if ((0 == stat (filename, &sbuf)) && (0 == statvfs (filename, &fbuf)))
355 *dev = (uint64_t) fbuf.f_fsid;
356 *ino = (uint64_t) sbuf.st_ino;
363 if ((0 == stat (filename, &sbuf)) && (0 == statfs (filename, &fbuf)))
365 *dev = ((uint64_t) fbuf.f_fsid.val[0]) << 32 ||
366 ((uint64_t) fbuf.f_fsid.val[1]);
367 *ino = (uint64_t) sbuf.st_ino;
371 // FIXME NILS: test this
372 struct GNUNET_DISK_FileHandle *fh;
373 BY_HANDLE_FILE_INFORMATION info;
376 fh = GNUNET_DISK_file_open (filename, GNUNET_DISK_OPEN_READ, 0);
378 return GNUNET_SYSERR;
379 succ = GetFileInformationByHandle (fh->h, &info);
380 GNUNET_DISK_file_close (fh);
383 *dev = info.dwVolumeSerialNumber;
384 *ino = ((((uint64_t) info.nFileIndexHigh) << (sizeof (DWORD) * 8)) | info.nFileIndexLow);
388 return GNUNET_SYSERR;
391 return GNUNET_SYSERR;
396 * Create the name for a temporary file or directory from a template.
398 * @param t template (without XXXXX or "/tmp/")
399 * @return name ready for passing to 'mktemp' or 'mkdtemp', NULL on error
402 mktemp_name (const char *t)
408 if ((t[0] != '/') && (t[0] != '\\')
410 && !(isalpha ((int) t[0]) && (t[0] != '\0') && (t[1] == ':'))
414 /* FIXME: This uses system codepage on W32, not UTF-8 */
415 tmpdir = getenv ("TMPDIR");
417 tmpdir = getenv ("TMP");
419 tmpdir = getenv ("TEMP");
422 GNUNET_asprintf (&tmpl, "%s/%s%s", tmpdir, t, "XXXXXX");
426 GNUNET_asprintf (&tmpl, "%s%s", t, "XXXXXX");
429 fn = (char *) GNUNET_malloc (MAX_PATH + 1);
430 if (ERROR_SUCCESS != plibc_conv_to_win_path (tmpl, fn))
451 tfn = GNUNET_strdup (fn);
452 random_fn = _mktemp (tfn);
453 if (NULL == random_fn)
458 /* FIXME: assume fn to be UTF-8-encoded and do the right thing */
459 if (0 == CreateDirectoryA (tfn, NULL))
461 DWORD error = GetLastError ();
463 if (ERROR_ALREADY_EXISTS == error)
475 * Create an (empty) temporary directory on disk. If the given name is not
476 * an absolute path, the current 'TMPDIR' will be prepended. In any case,
477 * 6 random characters will be appended to the name to create a unique
480 * @param t component to use for the name;
481 * does NOT contain "XXXXXX" or "/tmp/".
482 * @return NULL on error, otherwise name of fresh
483 * file on disk in directory for temporary files
486 GNUNET_DISK_mkdtemp (const char *t)
490 fn = mktemp_name (t);
491 if (fn != mkdtemp (fn))
493 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_ERROR, "mkstemp", fn);
502 * Create an (empty) temporary file on disk. If the given name is not
503 * an absolute path, the current 'TMPDIR' will be prepended. In any case,
504 * 6 random characters will be appended to the name to create a unique
507 * @param t component to use for the name;
508 * does NOT contain "XXXXXX" or "/tmp/".
509 * @return NULL on error, otherwise name of fresh
510 * file on disk in directory for temporary files
513 GNUNET_DISK_mktemp (const char *t)
518 fn = mktemp_name (t);
519 if (-1 == (fd = mkstemp (fn)))
521 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_ERROR, "mkstemp", fn);
526 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "close", fn);
532 * Get the number of blocks that are left on the partition that
533 * contains the given file (for normal users).
535 * @param part a file on the partition to check
536 * @return -1 on errors, otherwise the number of free blocks
539 GNUNET_DISK_get_blocks_available (const char *part)
544 if (0 != statvfs (part, &buf))
546 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "statfs", part);
554 wchar_t wpath[MAX_PATH + 1];
557 path = GNUNET_STRINGS_filename_expand (part);
560 /* "part" was in UTF-8, and so is "path" */
561 if (ERROR_SUCCESS != plibc_conv_to_win_pathwconv(path, wpath))
567 wcsncpy (szDrive, wpath, 3);
569 if (!GetDiskFreeSpaceW (szDrive, &dwDummy, &dwDummy, &dwBlocks, &dwDummy))
571 LOG (GNUNET_ERROR_TYPE_WARNING, _("`%s' failed for drive `%S': %u\n"),
572 "GetDiskFreeSpace", szDrive, GetLastError ());
580 if (0 != statfs (part, &s))
582 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "statfs", part);
591 * Test if "fil" is a directory.
592 * Will not print an error message if the directory
593 * does not exist. Will log errors if GNUNET_SYSERR is
594 * returned (i.e., a file exists with the same name).
596 * @param fil filename to test
597 * @return GNUNET_YES if yes, GNUNET_NO if not, GNUNET_SYSERR if it
601 GNUNET_DISK_directory_test (const char *fil)
603 struct stat filestat;
606 ret = STAT (fil, &filestat);
611 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "stat", fil);
612 return GNUNET_SYSERR;
616 if (!S_ISDIR (filestat.st_mode))
618 if (ACCESS (fil, R_OK | X_OK) < 0)
620 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "access", fil);
621 return GNUNET_SYSERR;
628 * Check that fil corresponds to a filename
629 * (of a file that exists and that is not a directory).
631 * @param fil filename to check
632 * @return GNUNET_YES if yes, GNUNET_NO if not a file, GNUNET_SYSERR if something
633 * else (will print an error message in that case, too).
636 GNUNET_DISK_file_test (const char *fil)
638 struct stat filestat;
642 rdir = GNUNET_STRINGS_filename_expand (fil);
644 return GNUNET_SYSERR;
646 ret = STAT (rdir, &filestat);
651 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "stat", rdir);
653 return GNUNET_SYSERR;
658 if (!S_ISREG (filestat.st_mode))
663 if (ACCESS (rdir, R_OK) < 0)
665 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "access", rdir);
667 return GNUNET_SYSERR;
675 * Implementation of "mkdir -p"
676 * @param dir the directory to create
677 * @returns GNUNET_OK on success, GNUNET_SYSERR on failure
680 GNUNET_DISK_directory_create (const char *dir)
687 rdir = GNUNET_STRINGS_filename_expand (dir);
689 return GNUNET_SYSERR;
693 pos = 1; /* skip heading '/' */
695 /* Local or Network path? */
696 if (strncmp (rdir, "\\\\", 2) == 0)
701 if (rdir[pos] == '\\')
711 pos = 3; /* strlen("C:\\") */
716 if ((rdir[pos] == DIR_SEPARATOR) || (pos == len))
719 ret = GNUNET_DISK_directory_test (rdir);
720 if (ret == GNUNET_SYSERR)
723 return GNUNET_SYSERR;
725 if (ret == GNUNET_NO)
728 ret = mkdir (rdir, S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH); /* 755 */
730 wchar_t wrdir[MAX_PATH + 1];
731 if (ERROR_SUCCESS == plibc_conv_to_win_pathwconv(rdir, wrdir))
732 ret = !CreateDirectoryW (wrdir, NULL);
736 if ((ret != 0) && (errno != EEXIST))
738 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_ERROR, "mkdir", rdir);
740 return GNUNET_SYSERR;
743 rdir[pos] = DIR_SEPARATOR;
753 * Create the directory structure for storing
756 * @param filename name of a file in the directory
757 * @returns GNUNET_OK on success,
758 * GNUNET_SYSERR on failure,
759 * GNUNET_NO if the directory
760 * exists but is not writeable for us
763 GNUNET_DISK_directory_create_for_file (const char *filename)
769 rdir = GNUNET_STRINGS_filename_expand (filename);
771 return GNUNET_SYSERR;
773 while ((len > 0) && (rdir[len] != DIR_SEPARATOR))
776 ret = GNUNET_DISK_directory_create (rdir);
777 if ((ret == GNUNET_OK) && (0 != ACCESS (rdir, W_OK)))
785 * Read the contents of a binary file into a buffer.
786 * @param h handle to an open file
787 * @param result the buffer to write the result to
788 * @param len the maximum number of bytes to read
789 * @return the number of bytes read on success, GNUNET_SYSERR on failure
792 GNUNET_DISK_file_read (const struct GNUNET_DISK_FileHandle * h, void *result,
798 return GNUNET_SYSERR;
804 if (h->type != GNUNET_PIPE)
806 if (!ReadFile (h->h, result, len, &bytesRead, NULL))
808 SetErrnoFromWinError (GetLastError ());
809 return GNUNET_SYSERR;
814 if (!ReadFile (h->h, result, len, &bytesRead, h->oOverlapRead))
816 if (GetLastError () != ERROR_IO_PENDING)
818 LOG (GNUNET_ERROR_TYPE_DEBUG, "Error reading from pipe: %u\n", GetLastError ());
819 SetErrnoFromWinError (GetLastError ());
820 return GNUNET_SYSERR;
822 LOG (GNUNET_ERROR_TYPE_DEBUG, "Will get overlapped result\n");
823 GetOverlappedResult (h->h, h->oOverlapRead, &bytesRead, TRUE);
825 LOG (GNUNET_ERROR_TYPE_DEBUG, "Read %u bytes from pipe\n", bytesRead);
829 return read (h->fd, result, len);
835 * Read the contents of a binary file into a buffer.
836 * Guarantees not to block (returns GNUNET_SYSERR and sets errno to EAGAIN
837 * when no data can be read).
839 * @param h handle to an open file
840 * @param result the buffer to write the result to
841 * @param len the maximum number of bytes to read
842 * @return the number of bytes read on success, GNUNET_SYSERR on failure
845 GNUNET_DISK_file_read_non_blocking (const struct GNUNET_DISK_FileHandle * h,
846 void *result, size_t len)
851 return GNUNET_SYSERR;
857 if (h->type != GNUNET_PIPE)
859 if (!ReadFile (h->h, result, len, &bytesRead, NULL))
861 SetErrnoFromWinError (GetLastError ());
862 return GNUNET_SYSERR;
867 if (!ReadFile (h->h, result, len, &bytesRead, h->oOverlapRead))
869 if (GetLastError () != ERROR_IO_PENDING)
871 LOG (GNUNET_ERROR_TYPE_DEBUG, "Error reading from pipe: %u\n", GetLastError ());
872 SetErrnoFromWinError (GetLastError ());
873 return GNUNET_SYSERR;
877 LOG (GNUNET_ERROR_TYPE_DEBUG,
878 "ReadFile() queued a read, cancelling\n");
881 return GNUNET_SYSERR;
884 LOG (GNUNET_ERROR_TYPE_DEBUG, "Read %u bytes\n", bytesRead);
891 /* set to non-blocking, read, then set back */
892 flags = fcntl (h->fd, F_GETFL);
893 if (0 == (flags & O_NONBLOCK))
894 fcntl (h->fd, F_SETFL, flags | O_NONBLOCK);
895 ret = read (h->fd, result, len);
896 if (0 == (flags & O_NONBLOCK))
897 fcntl (h->fd, F_SETFL, flags);
904 * Read the contents of a binary file into a buffer.
906 * @param fn file name
907 * @param result the buffer to write the result to
908 * @param len the maximum number of bytes to read
909 * @return number of bytes read, GNUNET_SYSERR on failure
912 GNUNET_DISK_fn_read (const char *fn, void *result, size_t len)
914 struct GNUNET_DISK_FileHandle *fh;
917 fh = GNUNET_DISK_file_open (fn, GNUNET_DISK_OPEN_READ, GNUNET_DISK_PERM_NONE);
919 return GNUNET_SYSERR;
920 ret = GNUNET_DISK_file_read (fh, result, len);
921 GNUNET_assert (GNUNET_OK == GNUNET_DISK_file_close (fh));
928 * Write a buffer to a file.
929 * @param h handle to open file
930 * @param buffer the data to write
931 * @param n number of bytes to write
932 * @return number of bytes written on success, GNUNET_SYSERR on error
935 GNUNET_DISK_file_write (const struct GNUNET_DISK_FileHandle * h,
936 const void *buffer, size_t n)
941 return GNUNET_SYSERR;
947 if (h->type != GNUNET_PIPE)
949 if (!WriteFile (h->h, buffer, n, &bytesWritten, NULL))
951 SetErrnoFromWinError (GetLastError ());
952 return GNUNET_SYSERR;
957 LOG (GNUNET_ERROR_TYPE_DEBUG, "It is a pipe trying to write %u bytes\n", n);
958 if (!WriteFile (h->h, buffer, n, &bytesWritten, h->oOverlapWrite))
960 if (GetLastError () != ERROR_IO_PENDING)
962 SetErrnoFromWinError (GetLastError ());
963 LOG (GNUNET_ERROR_TYPE_DEBUG, "Error writing to pipe: %u\n",
965 return GNUNET_SYSERR;
967 LOG (GNUNET_ERROR_TYPE_DEBUG, "Will get overlapped result\n");
968 if (!GetOverlappedResult (h->h, h->oOverlapWrite, &bytesWritten, TRUE))
970 SetErrnoFromWinError (GetLastError ());
971 LOG (GNUNET_ERROR_TYPE_DEBUG,
972 "Error getting overlapped result while writing to pipe: %u\n",
974 return GNUNET_SYSERR;
980 if (!GetOverlappedResult (h->h, h->oOverlapWrite, &ovr, TRUE))
982 LOG (GNUNET_ERROR_TYPE_DEBUG,
983 "Error getting control overlapped result while writing to pipe: %u\n",
988 LOG (GNUNET_ERROR_TYPE_DEBUG,
989 "Wrote %u bytes (ovr says %u), picking the greatest\n",
993 if (bytesWritten == 0)
997 LOG (GNUNET_ERROR_TYPE_DEBUG, "Wrote %u bytes, returning -1 with EAGAIN\n", bytesWritten);
999 return GNUNET_SYSERR;
1002 LOG (GNUNET_ERROR_TYPE_DEBUG, "Wrote %u bytes\n", bytesWritten);
1004 return bytesWritten;
1006 return write (h->fd, buffer, n);
1012 * Write a buffer to a file, blocking, if necessary.
1013 * @param h handle to open file
1014 * @param buffer the data to write
1015 * @param n number of bytes to write
1016 * @return number of bytes written on success, GNUNET_SYSERR on error
1019 GNUNET_DISK_file_write_blocking (const struct GNUNET_DISK_FileHandle * h,
1020 const void *buffer, size_t n)
1025 return GNUNET_SYSERR;
1030 /* We do a non-overlapped write, which is as blocking as it gets */
1031 LOG (GNUNET_ERROR_TYPE_DEBUG, "Writing %u bytes\n", n);
1032 if (!WriteFile (h->h, buffer, n, &bytesWritten, NULL))
1034 SetErrnoFromWinError (GetLastError ());
1035 LOG (GNUNET_ERROR_TYPE_DEBUG, "Error writing to pipe: %u\n",
1037 return GNUNET_SYSERR;
1039 if (bytesWritten == 0 && n > 0)
1041 LOG (GNUNET_ERROR_TYPE_DEBUG, "Waiting for pipe to clean\n");
1042 WaitForSingleObject (h->h, INFINITE);
1043 if (!WriteFile (h->h, buffer, n, &bytesWritten, NULL))
1045 SetErrnoFromWinError (GetLastError ());
1046 LOG (GNUNET_ERROR_TYPE_DEBUG, "Error writing to pipe: %u\n",
1048 return GNUNET_SYSERR;
1051 LOG (GNUNET_ERROR_TYPE_DEBUG, "Wrote %u bytes\n", bytesWritten);
1052 return bytesWritten;
1057 /* set to blocking, write, then set back */
1058 flags = fcntl (h->fd, F_GETFL);
1059 if (0 != (flags & O_NONBLOCK))
1060 fcntl (h->fd, F_SETFL, flags - O_NONBLOCK);
1061 ret = write (h->fd, buffer, n);
1062 if (0 == (flags & O_NONBLOCK))
1063 fcntl (h->fd, F_SETFL, flags);
1070 * Write a buffer to a file. If the file is longer than the
1071 * number of bytes that will be written, it will be truncated.
1073 * @param fn file name
1074 * @param buffer the data to write
1075 * @param n number of bytes to write
1076 * @param mode file permissions
1077 * @return number of bytes written on success, GNUNET_SYSERR on error
1080 GNUNET_DISK_fn_write (const char *fn, const void *buffer, size_t n,
1081 enum GNUNET_DISK_AccessPermissions mode)
1083 struct GNUNET_DISK_FileHandle *fh;
1086 fh = GNUNET_DISK_file_open (fn,
1087 GNUNET_DISK_OPEN_WRITE | GNUNET_DISK_OPEN_TRUNCATE
1088 | GNUNET_DISK_OPEN_CREATE, mode);
1090 return GNUNET_SYSERR;
1091 ret = GNUNET_DISK_file_write (fh, buffer, n);
1092 GNUNET_assert (GNUNET_OK == GNUNET_DISK_file_close (fh));
1098 * Scan a directory for files.
1100 * @param dirName the name of the directory
1101 * @param callback the method to call for each file,
1102 * can be NULL, in that case, we only count
1103 * @param callback_cls closure for callback
1104 * @return the number of files found, GNUNET_SYSERR on error or
1105 * ieration aborted by callback returning GNUNET_SYSERR
1108 GNUNET_DISK_directory_scan (const char *dirName,
1109 GNUNET_FileNameCallback callback,
1113 struct dirent *finfo;
1118 unsigned int name_len;
1119 unsigned int n_size;
1121 GNUNET_assert (dirName != NULL);
1122 dname = GNUNET_STRINGS_filename_expand (dirName);
1124 return GNUNET_SYSERR;
1125 while ((strlen (dname) > 0) && (dname[strlen (dname) - 1] == DIR_SEPARATOR))
1126 dname[strlen (dname) - 1] = '\0';
1127 if (0 != STAT (dname, &istat))
1129 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "stat", dname);
1130 GNUNET_free (dname);
1131 return GNUNET_SYSERR;
1133 if (!S_ISDIR (istat.st_mode))
1135 LOG (GNUNET_ERROR_TYPE_WARNING, _("Expected `%s' to be a directory!\n"),
1137 GNUNET_free (dname);
1138 return GNUNET_SYSERR;
1141 dinfo = OPENDIR (dname);
1142 if ((errno == EACCES) || (dinfo == NULL))
1144 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "opendir", dname);
1147 GNUNET_free (dname);
1148 return GNUNET_SYSERR;
1151 n_size = strlen (dname) + name_len + 2;
1152 name = GNUNET_malloc (n_size);
1153 while ((finfo = READDIR (dinfo)) != NULL)
1155 if ((0 == strcmp (finfo->d_name, ".")) ||
1156 (0 == strcmp (finfo->d_name, "..")))
1158 if (callback != NULL)
1160 if (name_len < strlen (finfo->d_name))
1163 name_len = strlen (finfo->d_name);
1164 n_size = strlen (dname) + name_len + 2;
1165 name = GNUNET_malloc (n_size);
1167 /* dname can end in "/" only if dname == "/";
1168 * if dname does not end in "/", we need to add
1169 * a "/" (otherwise, we must not!) */
1170 GNUNET_snprintf (name, n_size, "%s%s%s", dname,
1171 (strcmp (dname, DIR_SEPARATOR_STR) ==
1172 0) ? "" : DIR_SEPARATOR_STR, finfo->d_name);
1173 if (GNUNET_OK != callback (callback_cls, name))
1177 GNUNET_free (dname);
1178 return GNUNET_SYSERR;
1185 GNUNET_free (dname);
1191 * Opaque handle used for iterating over a directory.
1193 struct GNUNET_DISK_DirectoryIterator
1197 * Function to call on directory entries.
1199 GNUNET_DISK_DirectoryIteratorCallback callback;
1202 * Closure for callback.
1207 * Reference to directory.
1217 * Next filename to process.
1224 enum GNUNET_SCHEDULER_Priority priority;
1230 * Task used by the directory iterator.
1233 directory_iterator_task (void *cls,
1234 const struct GNUNET_SCHEDULER_TaskContext *tc)
1236 struct GNUNET_DISK_DirectoryIterator *iter = cls;
1239 name = iter->next_name;
1240 GNUNET_assert (name != NULL);
1241 iter->next_name = NULL;
1242 iter->callback (iter->callback_cls, iter, name, iter->dirname);
1248 * This function must be called during the DiskIteratorCallback
1249 * (exactly once) to schedule the task to process the next
1250 * filename in the directory (if there is one).
1252 * @param iter opaque handle for the iterator
1253 * @param can set to GNUNET_YES to terminate the iteration early
1254 * @return GNUNET_YES if iteration will continue,
1255 * GNUNET_NO if this was the last entry (and iteration is complete),
1256 * GNUNET_SYSERR if abort was YES
1259 GNUNET_DISK_directory_iterator_next (struct GNUNET_DISK_DirectoryIterator *iter,
1262 struct dirent *finfo;
1264 GNUNET_assert (iter->next_name == NULL);
1265 if (can == GNUNET_YES)
1267 CLOSEDIR (iter->directory);
1268 GNUNET_free (iter->dirname);
1270 return GNUNET_SYSERR;
1272 while (NULL != (finfo = READDIR (iter->directory)))
1274 if ((0 == strcmp (finfo->d_name, ".")) ||
1275 (0 == strcmp (finfo->d_name, "..")))
1277 GNUNET_asprintf (&iter->next_name, "%s%s%s", iter->dirname,
1278 DIR_SEPARATOR_STR, finfo->d_name);
1283 GNUNET_DISK_directory_iterator_next (iter, GNUNET_YES);
1286 GNUNET_SCHEDULER_add_with_priority (iter->priority, &directory_iterator_task,
1293 * Scan a directory for files using the scheduler to run a task for
1294 * each entry. The name of the directory must be expanded first (!).
1295 * If a scheduler does not need to be used, GNUNET_DISK_directory_scan
1296 * may provide a simpler API.
1298 * @param prio priority to use
1299 * @param dirName the name of the directory
1300 * @param callback the method to call for each file
1301 * @param callback_cls closure for callback
1302 * @return GNUNET_YES if directory is not empty and 'callback'
1303 * will be called later, GNUNET_NO otherwise, GNUNET_SYSERR on error.
1306 GNUNET_DISK_directory_iterator_start (enum GNUNET_SCHEDULER_Priority prio,
1307 const char *dirName,
1308 GNUNET_DISK_DirectoryIteratorCallback
1309 callback, void *callback_cls)
1311 struct GNUNET_DISK_DirectoryIterator *di;
1313 di = GNUNET_malloc (sizeof (struct GNUNET_DISK_DirectoryIterator));
1314 di->callback = callback;
1315 di->callback_cls = callback_cls;
1316 di->directory = OPENDIR (dirName);
1317 if (di->directory == NULL)
1320 callback (callback_cls, NULL, NULL, NULL);
1321 return GNUNET_SYSERR;
1323 di->dirname = GNUNET_strdup (dirName);
1324 di->priority = prio;
1325 return GNUNET_DISK_directory_iterator_next (di, GNUNET_NO);
1330 * Function that removes the given directory by calling
1331 * "GNUNET_DISK_directory_remove".
1333 * @param unused not used
1334 * @param fn directory to remove
1338 remove_helper (void *unused, const char *fn)
1340 (void) GNUNET_DISK_directory_remove (fn);
1346 * Remove all files in a directory (rm -rf). Call with
1350 * @param fileName the file to remove
1351 * @return GNUNET_OK on success, GNUNET_SYSERR on error
1354 GNUNET_DISK_directory_remove (const char *fileName)
1358 if (0 != LSTAT (fileName, &istat))
1359 return GNUNET_NO; /* file may not exist... */
1360 CHMOD (fileName, S_IWUSR | S_IRUSR | S_IXUSR);
1361 if (UNLINK (fileName) == 0)
1363 if ((errno != EISDIR) &&
1364 /* EISDIR is not sufficient in all cases, e.g.
1365 * sticky /tmp directory may result in EPERM on BSD.
1366 * So we also explicitly check "isDirectory" */
1367 (GNUNET_YES != GNUNET_DISK_directory_test (fileName)))
1369 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "rmdir", fileName);
1370 return GNUNET_SYSERR;
1372 if (GNUNET_SYSERR ==
1373 GNUNET_DISK_directory_scan (fileName, &remove_helper, NULL))
1374 return GNUNET_SYSERR;
1375 if (0 != RMDIR (fileName))
1377 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "rmdir", fileName);
1378 return GNUNET_SYSERR;
1387 * @param src file to copy
1388 * @param dst destination file name
1389 * @return GNUNET_OK on success, GNUNET_SYSERR on error
1392 GNUNET_DISK_file_copy (const char *src, const char *dst)
1398 struct GNUNET_DISK_FileHandle *in;
1399 struct GNUNET_DISK_FileHandle *out;
1401 if (GNUNET_OK != GNUNET_DISK_file_size (src, &size, GNUNET_YES, GNUNET_YES))
1402 return GNUNET_SYSERR;
1404 in = GNUNET_DISK_file_open (src, GNUNET_DISK_OPEN_READ,
1405 GNUNET_DISK_PERM_NONE);
1407 return GNUNET_SYSERR;
1409 GNUNET_DISK_file_open (dst,
1410 GNUNET_DISK_OPEN_WRITE | GNUNET_DISK_OPEN_CREATE |
1411 GNUNET_DISK_OPEN_FAILIFEXISTS,
1412 GNUNET_DISK_PERM_USER_READ |
1413 GNUNET_DISK_PERM_USER_WRITE |
1414 GNUNET_DISK_PERM_GROUP_READ |
1415 GNUNET_DISK_PERM_GROUP_WRITE);
1418 GNUNET_DISK_file_close (in);
1419 return GNUNET_SYSERR;
1421 buf = GNUNET_malloc (COPY_BLK_SIZE);
1424 len = COPY_BLK_SIZE;
1425 if (len > size - pos)
1427 if (len != GNUNET_DISK_file_read (in, buf, len))
1429 if (len != GNUNET_DISK_file_write (out, buf, len))
1434 GNUNET_DISK_file_close (in);
1435 GNUNET_DISK_file_close (out);
1439 GNUNET_DISK_file_close (in);
1440 GNUNET_DISK_file_close (out);
1441 return GNUNET_SYSERR;
1446 * @brief Removes special characters as ':' from a filename.
1447 * @param fn the filename to canonicalize
1450 GNUNET_DISK_filename_canonicalize (char *fn)
1460 if (c == '/' || c == '\\' || c == ':' || c == '*' || c == '?' || c == '"' ||
1461 c == '<' || c == '>' || c == '|')
1473 * @brief Change owner of a file
1475 * @param filename name of file to change the owner of
1476 * @param user name of the new owner
1477 * @return GNUNET_OK on success, GNUNET_SYSERR on failure
1480 GNUNET_DISK_file_change_owner (const char *filename, const char *user)
1485 pws = getpwnam (user);
1488 LOG (GNUNET_ERROR_TYPE_ERROR,
1489 _("Cannot obtain information about user `%s': %s\n"), user,
1491 return GNUNET_SYSERR;
1493 if (0 != chown (filename, pws->pw_uid, pws->pw_gid))
1494 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "chown", filename);
1501 * Lock a part of a file
1502 * @param fh file handle
1503 * @param lockStart absolute position from where to lock
1504 * @param lockEnd absolute position until where to lock
1505 * @param excl GNUNET_YES for an exclusive lock
1506 * @return GNUNET_OK on success, GNUNET_SYSERR on error
1509 GNUNET_DISK_file_lock (struct GNUNET_DISK_FileHandle *fh, OFF_T lockStart,
1510 OFF_T lockEnd, int excl)
1515 return GNUNET_SYSERR;
1521 memset (&fl, 0, sizeof (struct flock));
1522 fl.l_type = excl ? F_WRLCK : F_RDLCK;
1523 fl.l_whence = SEEK_SET;
1524 fl.l_start = lockStart;
1527 return fcntl (fh->fd, F_SETLK, &fl) != 0 ? GNUNET_SYSERR : GNUNET_OK;
1530 OFF_T diff = lockEnd - lockStart;
1531 DWORD diff_low, diff_high;
1532 diff_low = (DWORD) (diff & 0xFFFFFFFF);
1533 diff_high = (DWORD) ((diff >> (sizeof (DWORD) * 8)) & 0xFFFFFFFF);
1535 memset (&o, 0, sizeof (OVERLAPPED));
1536 o.Offset = (DWORD) (lockStart & 0xFFFFFFFF);;
1537 o.OffsetHigh = (DWORD) (((lockStart & ~0xFFFFFFFF) >> (sizeof (DWORD) * 8)) & 0xFFFFFFFF);
1540 (fh->h, (excl ? LOCKFILE_EXCLUSIVE_LOCK : 0) | LOCKFILE_FAIL_IMMEDIATELY,
1541 0, diff_low, diff_high, &o))
1543 SetErrnoFromWinError (GetLastError ());
1544 return GNUNET_SYSERR;
1553 * Unlock a part of a file
1554 * @param fh file handle
1555 * @param unlockStart absolute position from where to unlock
1556 * @param unlockEnd absolute position until where to unlock
1557 * @return GNUNET_OK on success, GNUNET_SYSERR on error
1560 GNUNET_DISK_file_unlock (struct GNUNET_DISK_FileHandle *fh, OFF_T unlockStart,
1566 return GNUNET_SYSERR;
1572 memset (&fl, 0, sizeof (struct flock));
1573 fl.l_type = F_UNLCK;
1574 fl.l_whence = SEEK_SET;
1575 fl.l_start = unlockStart;
1576 fl.l_len = unlockEnd;
1578 return fcntl (fh->fd, F_SETLK, &fl) != 0 ? GNUNET_SYSERR : GNUNET_OK;
1581 OFF_T diff = unlockEnd - unlockStart;
1582 DWORD diff_low, diff_high;
1583 diff_low = (DWORD) (diff & 0xFFFFFFFF);
1584 diff_high = (DWORD) ((diff >> (sizeof (DWORD) * 8)) & 0xFFFFFFFF);
1586 memset (&o, 0, sizeof (OVERLAPPED));
1587 o.Offset = (DWORD) (unlockStart & 0xFFFFFFFF);;
1588 o.OffsetHigh = (DWORD) (((unlockStart & ~0xFFFFFFFF) >> (sizeof (DWORD) * 8)) & 0xFFFFFFFF);
1590 if (!UnlockFileEx (fh->h, 0, diff_low, diff_high, &o))
1592 SetErrnoFromWinError (GetLastError ());
1593 return GNUNET_SYSERR;
1602 * Open a file. Note that the access permissions will only be
1603 * used if a new file is created and if the underlying operating
1604 * system supports the given permissions.
1606 * @param fn file name to be opened
1607 * @param flags opening flags, a combination of GNUNET_DISK_OPEN_xxx bit flags
1608 * @param perm permissions for the newly created file, use
1609 * GNUNET_DISK_PERM_USER_NONE if a file could not be created by this
1610 * call (because of flags)
1611 * @return IO handle on success, NULL on error
1613 struct GNUNET_DISK_FileHandle *
1614 GNUNET_DISK_file_open (const char *fn, enum GNUNET_DISK_OpenFlags flags,
1615 enum GNUNET_DISK_AccessPermissions perm)
1618 struct GNUNET_DISK_FileHandle *ret;
1624 wchar_t wexpfn[MAX_PATH + 1];
1631 expfn = GNUNET_STRINGS_filename_expand (fn);
1636 if (GNUNET_DISK_OPEN_READWRITE == (flags & GNUNET_DISK_OPEN_READWRITE))
1637 oflags = O_RDWR; /* note: O_RDWR is NOT always O_RDONLY | O_WRONLY */
1638 else if (flags & GNUNET_DISK_OPEN_READ)
1640 else if (flags & GNUNET_DISK_OPEN_WRITE)
1645 GNUNET_free (expfn);
1648 if (flags & GNUNET_DISK_OPEN_FAILIFEXISTS)
1649 oflags |= (O_CREAT | O_EXCL);
1650 if (flags & GNUNET_DISK_OPEN_TRUNCATE)
1652 if (flags & GNUNET_DISK_OPEN_APPEND)
1654 if (flags & GNUNET_DISK_OPEN_CREATE)
1656 (void) GNUNET_DISK_directory_create_for_file (expfn);
1658 mode = translate_unix_perms (perm);
1661 fd = open (expfn, oflags | O_LARGEFILE, mode);
1664 if (0 == (flags & GNUNET_DISK_OPEN_FAILIFEXISTS))
1665 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "open", expfn);
1667 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_DEBUG, "open", expfn);
1668 GNUNET_free (expfn);
1675 if (GNUNET_DISK_OPEN_READWRITE == (flags & GNUNET_DISK_OPEN_READWRITE))
1676 access = FILE_READ_DATA | FILE_WRITE_DATA;
1677 else if (flags & GNUNET_DISK_OPEN_READ)
1678 access = FILE_READ_DATA;
1679 else if (flags & GNUNET_DISK_OPEN_WRITE)
1680 access = FILE_WRITE_DATA;
1682 if (flags & GNUNET_DISK_OPEN_FAILIFEXISTS)
1686 else if (flags & GNUNET_DISK_OPEN_CREATE)
1688 (void) GNUNET_DISK_directory_create_for_file (expfn);
1689 if (flags & GNUNET_DISK_OPEN_TRUNCATE)
1690 disp = CREATE_ALWAYS;
1694 else if (flags & GNUNET_DISK_OPEN_TRUNCATE)
1696 disp = TRUNCATE_EXISTING;
1700 disp = OPEN_EXISTING;
1703 if (ERROR_SUCCESS == plibc_conv_to_win_pathwconv(expfn, wexpfn))
1704 h = CreateFileW (wexpfn, access,
1705 FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE, NULL,
1706 disp, FILE_ATTRIBUTE_NORMAL, NULL);
1708 h = INVALID_HANDLE_VALUE;
1709 if (h == INVALID_HANDLE_VALUE)
1711 SetErrnoFromWinError (GetLastError ());
1712 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "open", expfn);
1713 GNUNET_free (expfn);
1717 if (flags & GNUNET_DISK_OPEN_APPEND)
1718 if (SetFilePointer (h, 0, 0, FILE_END) == INVALID_SET_FILE_POINTER)
1720 SetErrnoFromWinError (GetLastError ());
1721 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "SetFilePointer", expfn);
1723 GNUNET_free (expfn);
1728 ret = GNUNET_malloc (sizeof (struct GNUNET_DISK_FileHandle));
1731 ret->type = GNUNET_DISK_FILE;
1735 GNUNET_free (expfn);
1741 * Close an open file
1742 * @param h file handle
1743 * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
1746 GNUNET_DISK_file_close (struct GNUNET_DISK_FileHandle *h)
1751 return GNUNET_SYSERR;
1755 if (!CloseHandle (h->h))
1757 SetErrnoFromWinError (GetLastError ());
1758 LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING, "close");
1759 GNUNET_free (h->oOverlapRead);
1760 GNUNET_free (h->oOverlapWrite);
1762 return GNUNET_SYSERR;
1765 if (close (h->fd) != 0)
1767 LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING, "close");
1769 return GNUNET_SYSERR;
1778 * Get a handle from a native FD.
1780 * @param fd native file descriptor
1781 * @return file handle corresponding to the descriptor
1783 struct GNUNET_DISK_FileHandle *
1784 GNUNET_DISK_get_handle_from_native (FILE *fd)
1786 struct GNUNET_DISK_FileHandle *fh;
1797 osfh = _get_osfhandle (fno);
1798 if (osfh == INVALID_HANDLE_VALUE)
1802 fh = GNUNET_malloc (sizeof (struct GNUNET_DISK_FileHandle));
1806 /* Assume it to be a pipe. TODO: use some kind of detection
1807 * function to figure out handle type.
1808 * Note that we can't make it overlapped if it isn't already.
1809 * (ReOpenFile() is only available in 2003/Vista).
1810 * The process that opened this file in the first place (usually a parent
1811 * process, if this is stdin/stdout/stderr) must make it overlapped,
1812 * otherwise we're screwed, as selecting on non-overlapped handle
1815 fh->type = GNUNET_PIPE;
1816 fh->oOverlapRead = GNUNET_malloc (sizeof (OVERLAPPED));
1817 fh->oOverlapWrite = GNUNET_malloc (sizeof (OVERLAPPED));
1818 fh->oOverlapRead->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
1819 fh->oOverlapWrite->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
1829 * Construct full path to a file inside of the private
1830 * directory used by GNUnet. Also creates the corresponding
1831 * directory. If the resulting name is supposed to be
1832 * a directory, end the last argument in '/' (or pass
1833 * DIR_SEPARATOR_STR as the last argument before NULL).
1835 * @param cfg configuration to use (determines HOME)
1836 * @param serviceName name of the service
1837 * @param ... is NULL-terminated list of
1838 * path components to append to the
1839 * private directory name.
1840 * @return the constructed filename
1843 GNUNET_DISK_get_home_filename (const struct GNUNET_CONFIGURATION_Handle *cfg,
1844 const char *serviceName, ...)
1850 unsigned int needed;
1853 GNUNET_CONFIGURATION_get_value_filename (cfg, serviceName, "HOME", &pfx))
1857 LOG (GNUNET_ERROR_TYPE_WARNING,
1858 _("No `%s' specified for service `%s' in configuration.\n"), "HOME",
1862 needed = strlen (pfx) + 2;
1863 if ((pfx[strlen (pfx) - 1] != '/') && (pfx[strlen (pfx) - 1] != '\\'))
1865 va_start (ap, serviceName);
1868 c = va_arg (ap, const char *);
1872 needed += strlen (c);
1873 if ((c[strlen (c) - 1] != '/') && (c[strlen (c) - 1] != '\\'))
1877 ret = GNUNET_malloc (needed);
1880 va_start (ap, serviceName);
1883 c = va_arg (ap, const char *);
1887 if ((c[strlen (c) - 1] != '/') && (c[strlen (c) - 1] != '\\'))
1888 strcat (ret, DIR_SEPARATOR_STR);
1892 if ((ret[strlen (ret) - 1] != '/') && (ret[strlen (ret) - 1] != '\\'))
1893 (void) GNUNET_DISK_directory_create_for_file (ret);
1895 (void) GNUNET_DISK_directory_create (ret);
1901 * Handle for a memory-mapping operation.
1903 struct GNUNET_DISK_MapHandle
1906 * Address where the map is in memory.
1912 * Underlying OS handle.
1917 * Number of bytes mapped.
1925 #define MAP_FAILED ((void *) -1)
1929 * Map a file into memory
1931 * @param h open file handle
1932 * @param m handle to the new mapping
1933 * @param access access specification, GNUNET_DISK_MAP_TYPE_xxx
1934 * @param len size of the mapping
1935 * @return pointer to the mapped memory region, NULL on failure
1938 GNUNET_DISK_file_map (const struct GNUNET_DISK_FileHandle *h,
1939 struct GNUNET_DISK_MapHandle **m,
1940 enum GNUNET_DISK_MapType access, size_t len)
1949 DWORD mapAccess, protect;
1951 if ((access & GNUNET_DISK_MAP_TYPE_READ) &&
1952 (access & GNUNET_DISK_MAP_TYPE_WRITE))
1954 protect = PAGE_READWRITE;
1955 mapAccess = FILE_MAP_ALL_ACCESS;
1957 else if (access & GNUNET_DISK_MAP_TYPE_READ)
1959 protect = PAGE_READONLY;
1960 mapAccess = FILE_MAP_READ;
1962 else if (access & GNUNET_DISK_MAP_TYPE_WRITE)
1964 protect = PAGE_READWRITE;
1965 mapAccess = FILE_MAP_WRITE;
1973 *m = GNUNET_malloc (sizeof (struct GNUNET_DISK_MapHandle));
1974 (*m)->h = CreateFileMapping (h->h, NULL, protect, 0, 0, NULL);
1975 if ((*m)->h == INVALID_HANDLE_VALUE)
1977 SetErrnoFromWinError (GetLastError ());
1982 (*m)->addr = MapViewOfFile ((*m)->h, mapAccess, 0, 0, len);
1985 SetErrnoFromWinError (GetLastError ());
1986 CloseHandle ((*m)->h);
1995 if (access & GNUNET_DISK_MAP_TYPE_READ)
1997 if (access & GNUNET_DISK_MAP_TYPE_WRITE)
1999 *m = GNUNET_malloc (sizeof (struct GNUNET_DISK_MapHandle));
2000 (*m)->addr = mmap (NULL, len, prot, MAP_SHARED, h->fd, 0);
2001 GNUNET_assert (NULL != (*m)->addr);
2002 if (MAP_FAILED == (*m)->addr)
2014 * @param h mapping handle
2015 * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
2018 GNUNET_DISK_file_unmap (struct GNUNET_DISK_MapHandle *h)
2025 return GNUNET_SYSERR;
2029 ret = UnmapViewOfFile (h->addr) ? GNUNET_OK : GNUNET_SYSERR;
2030 if (ret != GNUNET_OK)
2031 SetErrnoFromWinError (GetLastError ());
2032 if (!CloseHandle (h->h) && (ret == GNUNET_OK))
2034 ret = GNUNET_SYSERR;
2035 SetErrnoFromWinError (GetLastError ());
2038 ret = munmap (h->addr, h->len) != -1 ? GNUNET_OK : GNUNET_SYSERR;
2046 * Write file changes to disk
2047 * @param h handle to an open file
2048 * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
2051 GNUNET_DISK_file_sync (const struct GNUNET_DISK_FileHandle *h)
2056 return GNUNET_SYSERR;
2062 ret = FlushFileBuffers (h->h) ? GNUNET_OK : GNUNET_SYSERR;
2063 if (ret != GNUNET_OK)
2064 SetErrnoFromWinError (GetLastError ());
2066 #elif defined(FREEBSD) || defined(OPENBSD) || defined(DARWIN)
2067 return fsync (h->fd) == -1 ? GNUNET_SYSERR : GNUNET_OK;
2069 return fdatasync (h->fd) == -1 ? GNUNET_SYSERR : GNUNET_OK;
2075 /* Copyright Bob Byrnes <byrnes <at> curl.com>
2076 http://permalink.gmane.org/gmane.os.cygwin.patches/2121
2078 /* Create a pipe, and return handles to the read and write ends,
2079 just like CreatePipe, but ensure that the write end permits
2080 FILE_READ_ATTRIBUTES access, on later versions of win32 where
2081 this is supported. This access is needed by NtQueryInformationFile,
2082 which is used to implement select and nonblocking writes.
2083 Note that the return value is either NO_ERROR or GetLastError,
2084 unlike CreatePipe, which returns a bool for success or failure. */
2086 create_selectable_pipe (PHANDLE read_pipe_ptr, PHANDLE write_pipe_ptr,
2087 LPSECURITY_ATTRIBUTES sa_ptr, DWORD psize,
2088 DWORD dwReadMode, DWORD dwWriteMode)
2090 /* Default to error. */
2091 *read_pipe_ptr = *write_pipe_ptr = INVALID_HANDLE_VALUE;
2093 HANDLE read_pipe = INVALID_HANDLE_VALUE, write_pipe = INVALID_HANDLE_VALUE;
2095 /* Ensure that there is enough pipe buffer space for atomic writes. */
2096 if (psize < PIPE_BUF)
2099 char pipename[MAX_PATH];
2101 /* Retry CreateNamedPipe as long as the pipe name is in use.
2102 * Retrying will probably never be necessary, but we want
2103 * to be as robust as possible. */
2106 static volatile LONG pipe_unique_id;
2108 snprintf (pipename, sizeof pipename, "\\\\.\\pipe\\gnunet-%d-%ld",
2109 getpid (), InterlockedIncrement ((LONG *) & pipe_unique_id));
2110 LOG (GNUNET_ERROR_TYPE_DEBUG, "CreateNamedPipe: name = %s, size = %lu\n",
2112 /* Use CreateNamedPipe instead of CreatePipe, because the latter
2113 * returns a write handle that does not permit FILE_READ_ATTRIBUTES
2114 * access, on versions of win32 earlier than WinXP SP2.
2115 * CreatePipe also stupidly creates a full duplex pipe, which is
2116 * a waste, since only a single direction is actually used.
2117 * It's important to only allow a single instance, to ensure that
2118 * the pipe was not created earlier by some other process, even if
2119 * the pid has been reused. We avoid FILE_FLAG_FIRST_PIPE_INSTANCE
2120 * because that is only available for Win2k SP2 and WinXP. */
2121 read_pipe = CreateNamedPipeA (pipename, PIPE_ACCESS_INBOUND | dwReadMode, PIPE_TYPE_BYTE | PIPE_READMODE_BYTE, 1, /* max instances */
2122 psize, /* output buffer size */
2123 psize, /* input buffer size */
2124 NMPWAIT_USE_DEFAULT_WAIT, sa_ptr);
2126 if (read_pipe != INVALID_HANDLE_VALUE)
2128 LOG (GNUNET_ERROR_TYPE_DEBUG, "pipe read handle = %p\n", read_pipe);
2132 DWORD err = GetLastError ();
2136 case ERROR_PIPE_BUSY:
2137 /* The pipe is already open with compatible parameters.
2138 * Pick a new name and retry. */
2139 LOG (GNUNET_ERROR_TYPE_DEBUG, "pipe busy, retrying\n");
2141 case ERROR_ACCESS_DENIED:
2142 /* The pipe is already open with incompatible parameters.
2143 * Pick a new name and retry. */
2144 LOG (GNUNET_ERROR_TYPE_DEBUG, "pipe access denied, retrying\n");
2146 case ERROR_CALL_NOT_IMPLEMENTED:
2147 /* We are on an older Win9x platform without named pipes.
2148 * Return an anonymous pipe as the best approximation. */
2149 LOG (GNUNET_ERROR_TYPE_DEBUG,
2150 "CreateNamedPipe not implemented, resorting to "
2151 "CreatePipe: size = %lu\n", psize);
2152 if (CreatePipe (read_pipe_ptr, write_pipe_ptr, sa_ptr, psize))
2154 LOG (GNUNET_ERROR_TYPE_DEBUG, "pipe read handle = %p, write handle = %p\n",
2159 err = GetLastError ();
2160 LOG (GNUNET_ERROR_TYPE_ERROR, "CreatePipe failed: %d\n", err);
2163 LOG (GNUNET_ERROR_TYPE_ERROR, "CreateNamedPipe failed: %d\n", err);
2168 LOG (GNUNET_ERROR_TYPE_DEBUG, "CreateFile: name = %s\n", pipename);
2170 /* Open the named pipe for writing.
2171 * Be sure to permit FILE_READ_ATTRIBUTES access. */
2172 write_pipe = CreateFileA (pipename, GENERIC_WRITE | FILE_READ_ATTRIBUTES, 0, /* share mode */
2173 sa_ptr, OPEN_EXISTING, dwWriteMode, /* flags and attributes */
2174 0); /* handle to template file */
2176 if (write_pipe == INVALID_HANDLE_VALUE)
2179 DWORD err = GetLastError ();
2181 LOG (GNUNET_ERROR_TYPE_DEBUG, "CreateFile failed: %d\n", err);
2182 CloseHandle (read_pipe);
2185 LOG (GNUNET_ERROR_TYPE_DEBUG, "pipe write handle = %p\n", write_pipe);
2187 *read_pipe_ptr = read_pipe;
2188 *write_pipe_ptr = write_pipe;
2195 * Creates an interprocess channel
2197 * @param blocking_read creates an asynchronous pipe for reading if set to GNUNET_NO
2198 * @param blocking_write creates an asynchronous pipe for writing if set to GNUNET_NO
2199 * @param inherit_read inherit the parent processes stdin (only for windows)
2200 * @param inherit_write inherit the parent processes stdout (only for windows)
2201 * @return handle to the new pipe, NULL on error
2203 struct GNUNET_DISK_PipeHandle *
2204 GNUNET_DISK_pipe (int blocking_read, int blocking_write, int inherit_read, int inherit_write)
2215 LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR, "pipe");
2219 return GNUNET_DISK_pipe_from_fd (blocking_read,
2223 struct GNUNET_DISK_PipeHandle *p;
2224 struct GNUNET_DISK_FileHandle *fds;
2229 p = GNUNET_malloc (sizeof (struct GNUNET_DISK_PipeHandle) +
2230 2 * sizeof (struct GNUNET_DISK_FileHandle));
2231 fds = (struct GNUNET_DISK_FileHandle *) &p[1];
2235 /* All pipes are overlapped. If you want them to block - just
2236 * call WriteFile() and ReadFile() with NULL overlapped pointer.
2239 create_selectable_pipe (&p->fd[0]->h, &p->fd[1]->h, NULL, 0,
2240 FILE_FLAG_OVERLAPPED,
2241 FILE_FLAG_OVERLAPPED);
2245 SetErrnoFromWinError (GetLastError ());
2248 if (!DuplicateHandle
2249 (GetCurrentProcess (), p->fd[0]->h, GetCurrentProcess (), &tmp_handle, 0,
2250 inherit_read == GNUNET_YES ? TRUE : FALSE, DUPLICATE_SAME_ACCESS))
2252 SetErrnoFromWinError (GetLastError ());
2253 CloseHandle (p->fd[0]->h);
2254 CloseHandle (p->fd[1]->h);
2258 CloseHandle (p->fd[0]->h);
2259 p->fd[0]->h = tmp_handle;
2261 if (!DuplicateHandle
2262 (GetCurrentProcess (), p->fd[1]->h, GetCurrentProcess (), &tmp_handle, 0,
2263 inherit_write == GNUNET_YES ? TRUE : FALSE, DUPLICATE_SAME_ACCESS))
2265 SetErrnoFromWinError (GetLastError ());
2266 CloseHandle (p->fd[0]->h);
2267 CloseHandle (p->fd[1]->h);
2271 CloseHandle (p->fd[1]->h);
2272 p->fd[1]->h = tmp_handle;
2274 p->fd[0]->type = GNUNET_PIPE;
2275 p->fd[1]->type = GNUNET_PIPE;
2277 p->fd[0]->oOverlapRead = GNUNET_malloc (sizeof (OVERLAPPED));
2278 p->fd[0]->oOverlapWrite = GNUNET_malloc (sizeof (OVERLAPPED));
2279 p->fd[1]->oOverlapRead = GNUNET_malloc (sizeof (OVERLAPPED));
2280 p->fd[1]->oOverlapWrite = GNUNET_malloc (sizeof (OVERLAPPED));
2282 p->fd[0]->oOverlapRead->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2283 p->fd[0]->oOverlapWrite->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2285 p->fd[1]->oOverlapRead->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2286 p->fd[1]->oOverlapWrite->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2294 * Creates a pipe object from a couple of file descriptors.
2295 * Useful for wrapping existing pipe FDs.
2297 * @param blocking_read creates an asynchronous pipe for reading if set to GNUNET_NO
2298 * @param blocking_write creates an asynchronous pipe for writing if set to GNUNET_NO
2299 * @param fd an array of two fd values. One of them may be -1 for read-only or write-only pipes
2301 * @return handle to the new pipe, NULL on error
2303 struct GNUNET_DISK_PipeHandle *
2304 GNUNET_DISK_pipe_from_fd (int blocking_read, int blocking_write, int fd[2])
2306 struct GNUNET_DISK_PipeHandle *p;
2307 struct GNUNET_DISK_FileHandle *fds;
2309 p = GNUNET_malloc (sizeof (struct GNUNET_DISK_PipeHandle) +
2310 2 * sizeof (struct GNUNET_DISK_FileHandle));
2311 fds = (struct GNUNET_DISK_FileHandle *) &p[1];
2317 int eno = 0; /* make gcc happy */
2319 p->fd[0]->fd = fd[0];
2320 p->fd[1]->fd = fd[1];
2326 flags = fcntl (fd[0], F_GETFL);
2327 flags |= O_NONBLOCK;
2328 if (0 > fcntl (fd[0], F_SETFL, flags))
2334 flags = fcntl (fd[0], F_GETFD);
2335 flags |= FD_CLOEXEC;
2336 if (0 > fcntl (fd[0], F_SETFD, flags))
2345 if (!blocking_write)
2347 flags = fcntl (fd[1], F_GETFL);
2348 flags |= O_NONBLOCK;
2349 if (0 > fcntl (fd[1], F_SETFL, flags))
2355 flags = fcntl (fd[1], F_GETFD);
2356 flags |= FD_CLOEXEC;
2357 if (0 > fcntl (fd[1], F_SETFD, flags))
2366 LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR, "fcntl");
2367 if (p->fd[0]->fd >= 0)
2368 GNUNET_break (0 == close (p->fd[0]->fd));
2369 if (p->fd[1]->fd >= 0)
2370 GNUNET_break (0 == close (p->fd[1]->fd));
2377 p->fd[0]->h = (HANDLE) _get_osfhandle (fd[0]);
2379 p->fd[0]->h = INVALID_HANDLE_VALUE;
2381 p->fd[1]->h = (HANDLE) _get_osfhandle (fd[1]);
2383 p->fd[1]->h = INVALID_HANDLE_VALUE;
2385 if (p->fd[0]->h != INVALID_HANDLE_VALUE)
2387 p->fd[0]->type = GNUNET_PIPE;
2388 p->fd[0]->oOverlapRead = GNUNET_malloc (sizeof (OVERLAPPED));
2389 p->fd[0]->oOverlapWrite = GNUNET_malloc (sizeof (OVERLAPPED));
2390 p->fd[0]->oOverlapRead->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2391 p->fd[0]->oOverlapWrite->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2394 if (p->fd[1]->h != INVALID_HANDLE_VALUE)
2396 p->fd[1]->type = GNUNET_PIPE;
2397 p->fd[1]->oOverlapRead = GNUNET_malloc (sizeof (OVERLAPPED));
2398 p->fd[1]->oOverlapWrite = GNUNET_malloc (sizeof (OVERLAPPED));
2399 p->fd[1]->oOverlapRead->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2400 p->fd[1]->oOverlapWrite->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2408 * Closes an interprocess channel
2410 * @param p pipe to close
2411 * @param end which end of the pipe to close
2412 * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
2415 GNUNET_DISK_pipe_close_end (struct GNUNET_DISK_PipeHandle *p,
2416 enum GNUNET_DISK_PipeEnd end)
2418 int ret = GNUNET_OK;
2422 if (end == GNUNET_DISK_PIPE_END_READ)
2424 if (p->fd[0]->h != INVALID_HANDLE_VALUE)
2426 if (!CloseHandle (p->fd[0]->h))
2428 SetErrnoFromWinError (GetLastError ());
2429 ret = GNUNET_SYSERR;
2431 GNUNET_free (p->fd[0]->oOverlapRead);
2432 GNUNET_free (p->fd[0]->oOverlapWrite);
2433 p->fd[0]->h = INVALID_HANDLE_VALUE;
2436 else if (end == GNUNET_DISK_PIPE_END_WRITE)
2438 if (p->fd[0]->h != INVALID_HANDLE_VALUE)
2440 if (!CloseHandle (p->fd[1]->h))
2442 SetErrnoFromWinError (GetLastError ());
2443 ret = GNUNET_SYSERR;
2445 GNUNET_free (p->fd[1]->oOverlapRead);
2446 GNUNET_free (p->fd[1]->oOverlapWrite);
2447 p->fd[1]->h = INVALID_HANDLE_VALUE;
2453 if (end == GNUNET_DISK_PIPE_END_READ)
2455 if (0 != close (p->fd[0]->fd))
2457 ret = GNUNET_SYSERR;
2462 else if (end == GNUNET_DISK_PIPE_END_WRITE)
2464 if (0 != close (p->fd[1]->fd))
2466 ret = GNUNET_SYSERR;
2478 * Closes an interprocess channel
2480 * @param p pipe to close
2481 * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
2484 GNUNET_DISK_pipe_close (struct GNUNET_DISK_PipeHandle *p)
2486 int ret = GNUNET_OK;
2490 if (p->fd[0]->h != INVALID_HANDLE_VALUE)
2492 if (!CloseHandle (p->fd[0]->h))
2494 SetErrnoFromWinError (GetLastError ());
2495 ret = GNUNET_SYSERR;
2497 GNUNET_free (p->fd[0]->oOverlapRead);
2498 GNUNET_free (p->fd[0]->oOverlapWrite);
2500 if (p->fd[1]->h != INVALID_HANDLE_VALUE)
2502 if (!CloseHandle (p->fd[1]->h))
2504 SetErrnoFromWinError (GetLastError ());
2505 ret = GNUNET_SYSERR;
2507 GNUNET_free (p->fd[1]->oOverlapRead);
2508 GNUNET_free (p->fd[1]->oOverlapWrite);
2513 if (p->fd[0]->fd != -1)
2515 if (0 != close (p->fd[0]->fd))
2517 ret = GNUNET_SYSERR;
2522 if (p->fd[1]->fd != -1)
2524 if (0 != close (p->fd[1]->fd))
2526 ret = GNUNET_SYSERR;
2538 * Get the handle to a particular pipe end
2541 * @param n end to access
2542 * @return handle for the respective end
2544 const struct GNUNET_DISK_FileHandle *
2545 GNUNET_DISK_pipe_handle (const struct GNUNET_DISK_PipeHandle *p,
2546 enum GNUNET_DISK_PipeEnd n)
2550 case GNUNET_DISK_PIPE_END_READ:
2551 case GNUNET_DISK_PIPE_END_WRITE:
2561 * Retrieve OS file handle
2563 * @param fh GNUnet file descriptor
2564 * @param dst destination buffer
2565 * @param dst_len length of dst
2566 * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
2569 GNUNET_DISK_internal_file_handle_ (const struct GNUNET_DISK_FileHandle *fh,
2570 void *dst, size_t dst_len)
2573 if (dst_len < sizeof (HANDLE))
2574 return GNUNET_SYSERR;
2575 *((HANDLE *) dst) = fh->h;
2577 if (dst_len < sizeof (int))
2578 return GNUNET_SYSERR;
2579 *((int *) dst) = fh->fd;