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, F_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_DISK_HANLDE_TYPE_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,
852 return GNUNET_SYSERR;
858 if (h->type != GNUNET_DISK_HANLDE_TYPE_PIPE)
860 if (!ReadFile (h->h, result, len, &bytesRead, NULL))
862 SetErrnoFromWinError (GetLastError ());
863 return GNUNET_SYSERR;
868 if (!ReadFile (h->h, result, len, &bytesRead, h->oOverlapRead))
870 if (GetLastError () != ERROR_IO_PENDING)
872 LOG (GNUNET_ERROR_TYPE_DEBUG, "Error reading from pipe: %u\n", GetLastError ());
873 SetErrnoFromWinError (GetLastError ());
874 return GNUNET_SYSERR;
878 LOG (GNUNET_ERROR_TYPE_DEBUG,
879 "ReadFile() queued a read, cancelling\n");
882 return GNUNET_SYSERR;
885 LOG (GNUNET_ERROR_TYPE_DEBUG, "Read %u bytes\n", bytesRead);
892 /* set to non-blocking, read, then set back */
893 flags = fcntl (h->fd, F_GETFL);
894 if (0 == (flags & O_NONBLOCK))
895 (void) fcntl (h->fd, F_SETFL, flags | O_NONBLOCK);
896 ret = read (h->fd, result, len);
897 if (0 == (flags & O_NONBLOCK))
900 (void) fcntl (h->fd, F_SETFL, flags);
909 * Read the contents of a binary file into a buffer.
911 * @param fn file name
912 * @param result the buffer to write the result to
913 * @param len the maximum number of bytes to read
914 * @return number of bytes read, GNUNET_SYSERR on failure
917 GNUNET_DISK_fn_read (const char *fn, void *result, size_t len)
919 struct GNUNET_DISK_FileHandle *fh;
922 fh = GNUNET_DISK_file_open (fn, GNUNET_DISK_OPEN_READ, GNUNET_DISK_PERM_NONE);
924 return GNUNET_SYSERR;
925 ret = GNUNET_DISK_file_read (fh, result, len);
926 GNUNET_assert (GNUNET_OK == GNUNET_DISK_file_close (fh));
933 * Write a buffer to a file.
934 * @param h handle to open file
935 * @param buffer the data to write
936 * @param n number of bytes to write
937 * @return number of bytes written on success, GNUNET_SYSERR on error
940 GNUNET_DISK_file_write (const struct GNUNET_DISK_FileHandle * h,
941 const void *buffer, size_t n)
946 return GNUNET_SYSERR;
952 if (h->type != GNUNET_DISK_HANLDE_TYPE_PIPE)
954 if (!WriteFile (h->h, buffer, n, &bytesWritten, NULL))
956 SetErrnoFromWinError (GetLastError ());
957 return GNUNET_SYSERR;
962 LOG (GNUNET_ERROR_TYPE_DEBUG, "It is a pipe trying to write %u bytes\n", n);
963 if (!WriteFile (h->h, buffer, n, &bytesWritten, h->oOverlapWrite))
965 if (GetLastError () != ERROR_IO_PENDING)
967 SetErrnoFromWinError (GetLastError ());
968 LOG (GNUNET_ERROR_TYPE_DEBUG, "Error writing to pipe: %u\n",
970 return GNUNET_SYSERR;
972 LOG (GNUNET_ERROR_TYPE_DEBUG, "Will get overlapped result\n");
973 if (!GetOverlappedResult (h->h, h->oOverlapWrite, &bytesWritten, TRUE))
975 SetErrnoFromWinError (GetLastError ());
976 LOG (GNUNET_ERROR_TYPE_DEBUG,
977 "Error getting overlapped result while writing to pipe: %u\n",
979 return GNUNET_SYSERR;
985 if (!GetOverlappedResult (h->h, h->oOverlapWrite, &ovr, TRUE))
987 LOG (GNUNET_ERROR_TYPE_DEBUG,
988 "Error getting control overlapped result while writing to pipe: %u\n",
993 LOG (GNUNET_ERROR_TYPE_DEBUG,
994 "Wrote %u bytes (ovr says %u), picking the greatest\n",
998 if (bytesWritten == 0)
1002 LOG (GNUNET_ERROR_TYPE_DEBUG, "Wrote %u bytes, returning -1 with EAGAIN\n", bytesWritten);
1004 return GNUNET_SYSERR;
1007 LOG (GNUNET_ERROR_TYPE_DEBUG, "Wrote %u bytes\n", bytesWritten);
1009 return bytesWritten;
1011 return write (h->fd, buffer, n);
1017 * Write a buffer to a file, blocking, if necessary.
1018 * @param h handle to open file
1019 * @param buffer the data to write
1020 * @param n number of bytes to write
1021 * @return number of bytes written on success, GNUNET_SYSERR on error
1024 GNUNET_DISK_file_write_blocking (const struct GNUNET_DISK_FileHandle * h,
1025 const void *buffer, size_t n)
1030 return GNUNET_SYSERR;
1035 /* We do a non-overlapped write, which is as blocking as it gets */
1036 LOG (GNUNET_ERROR_TYPE_DEBUG, "Writing %u bytes\n", n);
1037 if (!WriteFile (h->h, buffer, n, &bytesWritten, NULL))
1039 SetErrnoFromWinError (GetLastError ());
1040 LOG (GNUNET_ERROR_TYPE_DEBUG, "Error writing to pipe: %u\n",
1042 return GNUNET_SYSERR;
1044 if (bytesWritten == 0 && n > 0)
1046 LOG (GNUNET_ERROR_TYPE_DEBUG, "Waiting for pipe to clean\n");
1047 WaitForSingleObject (h->h, INFINITE);
1048 if (!WriteFile (h->h, buffer, n, &bytesWritten, NULL))
1050 SetErrnoFromWinError (GetLastError ());
1051 LOG (GNUNET_ERROR_TYPE_DEBUG, "Error writing to pipe: %u\n",
1053 return GNUNET_SYSERR;
1056 LOG (GNUNET_ERROR_TYPE_DEBUG, "Wrote %u bytes\n", bytesWritten);
1057 return bytesWritten;
1062 /* set to blocking, write, then set back */
1063 flags = fcntl (h->fd, F_GETFL);
1064 if (0 != (flags & O_NONBLOCK))
1065 fcntl (h->fd, F_SETFL, flags - O_NONBLOCK);
1066 ret = write (h->fd, buffer, n);
1067 if (0 == (flags & O_NONBLOCK))
1068 fcntl (h->fd, F_SETFL, flags);
1075 * Write a buffer to a file. If the file is longer than the
1076 * number of bytes that will be written, it will be truncated.
1078 * @param fn file name
1079 * @param buffer the data to write
1080 * @param n number of bytes to write
1081 * @param mode file permissions
1082 * @return number of bytes written on success, GNUNET_SYSERR on error
1085 GNUNET_DISK_fn_write (const char *fn, const void *buffer, size_t n,
1086 enum GNUNET_DISK_AccessPermissions mode)
1088 struct GNUNET_DISK_FileHandle *fh;
1091 fh = GNUNET_DISK_file_open (fn,
1092 GNUNET_DISK_OPEN_WRITE | GNUNET_DISK_OPEN_TRUNCATE
1093 | GNUNET_DISK_OPEN_CREATE, mode);
1095 return GNUNET_SYSERR;
1096 ret = GNUNET_DISK_file_write (fh, buffer, n);
1097 GNUNET_assert (GNUNET_OK == GNUNET_DISK_file_close (fh));
1103 * Scan a directory for files.
1105 * @param dirName the name of the directory
1106 * @param callback the method to call for each file,
1107 * can be NULL, in that case, we only count
1108 * @param callback_cls closure for callback
1109 * @return the number of files found, GNUNET_SYSERR on error or
1110 * ieration aborted by callback returning GNUNET_SYSERR
1113 GNUNET_DISK_directory_scan (const char *dirName,
1114 GNUNET_FileNameCallback callback,
1118 struct dirent *finfo;
1123 unsigned int name_len;
1124 unsigned int n_size;
1126 GNUNET_assert (dirName != NULL);
1127 dname = GNUNET_STRINGS_filename_expand (dirName);
1129 return GNUNET_SYSERR;
1130 while ((strlen (dname) > 0) && (dname[strlen (dname) - 1] == DIR_SEPARATOR))
1131 dname[strlen (dname) - 1] = '\0';
1132 if (0 != STAT (dname, &istat))
1134 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "stat", dname);
1135 GNUNET_free (dname);
1136 return GNUNET_SYSERR;
1138 if (!S_ISDIR (istat.st_mode))
1140 LOG (GNUNET_ERROR_TYPE_WARNING, _("Expected `%s' to be a directory!\n"),
1142 GNUNET_free (dname);
1143 return GNUNET_SYSERR;
1146 dinfo = OPENDIR (dname);
1147 if ((errno == EACCES) || (dinfo == NULL))
1149 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "opendir", dname);
1152 GNUNET_free (dname);
1153 return GNUNET_SYSERR;
1156 n_size = strlen (dname) + name_len + 2;
1157 name = GNUNET_malloc (n_size);
1158 while ((finfo = READDIR (dinfo)) != NULL)
1160 if ((0 == strcmp (finfo->d_name, ".")) ||
1161 (0 == strcmp (finfo->d_name, "..")))
1163 if (callback != NULL)
1165 if (name_len < strlen (finfo->d_name))
1168 name_len = strlen (finfo->d_name);
1169 n_size = strlen (dname) + name_len + 2;
1170 name = GNUNET_malloc (n_size);
1172 /* dname can end in "/" only if dname == "/";
1173 * if dname does not end in "/", we need to add
1174 * a "/" (otherwise, we must not!) */
1175 GNUNET_snprintf (name, n_size, "%s%s%s", dname,
1176 (strcmp (dname, DIR_SEPARATOR_STR) ==
1177 0) ? "" : DIR_SEPARATOR_STR, finfo->d_name);
1178 if (GNUNET_OK != callback (callback_cls, name))
1182 GNUNET_free (dname);
1183 return GNUNET_SYSERR;
1190 GNUNET_free (dname);
1196 * Opaque handle used for iterating over a directory.
1198 struct GNUNET_DISK_DirectoryIterator
1202 * Function to call on directory entries.
1204 GNUNET_DISK_DirectoryIteratorCallback callback;
1207 * Closure for callback.
1212 * Reference to directory.
1222 * Next filename to process.
1229 enum GNUNET_SCHEDULER_Priority priority;
1235 * Task used by the directory iterator.
1238 directory_iterator_task (void *cls,
1239 const struct GNUNET_SCHEDULER_TaskContext *tc)
1241 struct GNUNET_DISK_DirectoryIterator *iter = cls;
1244 name = iter->next_name;
1245 GNUNET_assert (name != NULL);
1246 iter->next_name = NULL;
1247 iter->callback (iter->callback_cls, iter, name, iter->dirname);
1253 * This function must be called during the DiskIteratorCallback
1254 * (exactly once) to schedule the task to process the next
1255 * filename in the directory (if there is one).
1257 * @param iter opaque handle for the iterator
1258 * @param can set to GNUNET_YES to terminate the iteration early
1259 * @return GNUNET_YES if iteration will continue,
1260 * GNUNET_NO if this was the last entry (and iteration is complete),
1261 * GNUNET_SYSERR if abort was YES
1264 GNUNET_DISK_directory_iterator_next (struct GNUNET_DISK_DirectoryIterator *iter,
1267 struct dirent *finfo;
1269 GNUNET_assert (iter->next_name == NULL);
1270 if (can == GNUNET_YES)
1272 CLOSEDIR (iter->directory);
1273 GNUNET_free (iter->dirname);
1275 return GNUNET_SYSERR;
1277 while (NULL != (finfo = READDIR (iter->directory)))
1279 if ((0 == strcmp (finfo->d_name, ".")) ||
1280 (0 == strcmp (finfo->d_name, "..")))
1282 GNUNET_asprintf (&iter->next_name, "%s%s%s", iter->dirname,
1283 DIR_SEPARATOR_STR, finfo->d_name);
1288 GNUNET_DISK_directory_iterator_next (iter, GNUNET_YES);
1291 GNUNET_SCHEDULER_add_with_priority (iter->priority, &directory_iterator_task,
1298 * Scan a directory for files using the scheduler to run a task for
1299 * each entry. The name of the directory must be expanded first (!).
1300 * If a scheduler does not need to be used, GNUNET_DISK_directory_scan
1301 * may provide a simpler API.
1303 * @param prio priority to use
1304 * @param dirName the name of the directory
1305 * @param callback the method to call for each file
1306 * @param callback_cls closure for callback
1307 * @return GNUNET_YES if directory is not empty and 'callback'
1308 * will be called later, GNUNET_NO otherwise, GNUNET_SYSERR on error.
1311 GNUNET_DISK_directory_iterator_start (enum GNUNET_SCHEDULER_Priority prio,
1312 const char *dirName,
1313 GNUNET_DISK_DirectoryIteratorCallback
1314 callback, void *callback_cls)
1316 struct GNUNET_DISK_DirectoryIterator *di;
1318 di = GNUNET_malloc (sizeof (struct GNUNET_DISK_DirectoryIterator));
1319 di->callback = callback;
1320 di->callback_cls = callback_cls;
1321 di->directory = OPENDIR (dirName);
1322 if (di->directory == NULL)
1325 callback (callback_cls, NULL, NULL, NULL);
1326 return GNUNET_SYSERR;
1328 di->dirname = GNUNET_strdup (dirName);
1329 di->priority = prio;
1330 return GNUNET_DISK_directory_iterator_next (di, GNUNET_NO);
1335 * Function that removes the given directory by calling
1336 * "GNUNET_DISK_directory_remove".
1338 * @param unused not used
1339 * @param fn directory to remove
1343 remove_helper (void *unused, const char *fn)
1345 (void) GNUNET_DISK_directory_remove (fn);
1351 * Remove all files in a directory (rm -rf). Call with
1355 * @param filename the file to remove
1356 * @return GNUNET_OK on success, GNUNET_SYSERR on error
1359 GNUNET_DISK_directory_remove (const char *filename)
1363 if (0 != LSTAT (filename, &istat))
1364 return GNUNET_NO; /* file may not exist... */
1365 (void) CHMOD (filename, S_IWUSR | S_IRUSR | S_IXUSR);
1366 if (UNLINK (filename) == 0)
1368 if ((errno != EISDIR) &&
1369 /* EISDIR is not sufficient in all cases, e.g.
1370 * sticky /tmp directory may result in EPERM on BSD.
1371 * So we also explicitly check "isDirectory" */
1372 (GNUNET_YES != GNUNET_DISK_directory_test (filename)))
1374 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "rmdir", filename);
1375 return GNUNET_SYSERR;
1377 if (GNUNET_SYSERR ==
1378 GNUNET_DISK_directory_scan (filename, &remove_helper, NULL))
1379 return GNUNET_SYSERR;
1380 if (0 != RMDIR (filename))
1382 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "rmdir", filename);
1383 return GNUNET_SYSERR;
1392 * @param src file to copy
1393 * @param dst destination file name
1394 * @return GNUNET_OK on success, GNUNET_SYSERR on error
1397 GNUNET_DISK_file_copy (const char *src, const char *dst)
1403 struct GNUNET_DISK_FileHandle *in;
1404 struct GNUNET_DISK_FileHandle *out;
1406 if (GNUNET_OK != GNUNET_DISK_file_size (src, &size, GNUNET_YES, GNUNET_YES))
1407 return GNUNET_SYSERR;
1409 in = GNUNET_DISK_file_open (src, GNUNET_DISK_OPEN_READ,
1410 GNUNET_DISK_PERM_NONE);
1412 return GNUNET_SYSERR;
1414 GNUNET_DISK_file_open (dst,
1415 GNUNET_DISK_OPEN_WRITE | GNUNET_DISK_OPEN_CREATE |
1416 GNUNET_DISK_OPEN_FAILIFEXISTS,
1417 GNUNET_DISK_PERM_USER_READ |
1418 GNUNET_DISK_PERM_USER_WRITE |
1419 GNUNET_DISK_PERM_GROUP_READ |
1420 GNUNET_DISK_PERM_GROUP_WRITE);
1423 GNUNET_DISK_file_close (in);
1424 return GNUNET_SYSERR;
1426 buf = GNUNET_malloc (COPY_BLK_SIZE);
1429 len = COPY_BLK_SIZE;
1430 if (len > size - pos)
1432 if (len != GNUNET_DISK_file_read (in, buf, len))
1434 if (len != GNUNET_DISK_file_write (out, buf, len))
1439 GNUNET_DISK_file_close (in);
1440 GNUNET_DISK_file_close (out);
1444 GNUNET_DISK_file_close (in);
1445 GNUNET_DISK_file_close (out);
1446 return GNUNET_SYSERR;
1451 * @brief Removes special characters as ':' from a filename.
1452 * @param fn the filename to canonicalize
1455 GNUNET_DISK_filename_canonicalize (char *fn)
1465 if (c == '/' || c == '\\' || c == ':' || c == '*' || c == '?' || c == '"' ||
1466 c == '<' || c == '>' || c == '|')
1478 * @brief Change owner of a file
1480 * @param filename name of file to change the owner of
1481 * @param user name of the new owner
1482 * @return GNUNET_OK on success, GNUNET_SYSERR on failure
1485 GNUNET_DISK_file_change_owner (const char *filename, const char *user)
1490 pws = getpwnam (user);
1493 LOG (GNUNET_ERROR_TYPE_ERROR,
1494 _("Cannot obtain information about user `%s': %s\n"), user,
1496 return GNUNET_SYSERR;
1498 if (0 != chown (filename, pws->pw_uid, pws->pw_gid))
1499 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "chown", filename);
1506 * Lock a part of a file
1507 * @param fh file handle
1508 * @param lockStart absolute position from where to lock
1509 * @param lockEnd absolute position until where to lock
1510 * @param excl GNUNET_YES for an exclusive lock
1511 * @return GNUNET_OK on success, GNUNET_SYSERR on error
1514 GNUNET_DISK_file_lock (struct GNUNET_DISK_FileHandle *fh, OFF_T lockStart,
1515 OFF_T lockEnd, int excl)
1520 return GNUNET_SYSERR;
1526 memset (&fl, 0, sizeof (struct flock));
1527 fl.l_type = excl ? F_WRLCK : F_RDLCK;
1528 fl.l_whence = SEEK_SET;
1529 fl.l_start = lockStart;
1532 return fcntl (fh->fd, F_SETLK, &fl) != 0 ? GNUNET_SYSERR : GNUNET_OK;
1535 OFF_T diff = lockEnd - lockStart;
1536 DWORD diff_low, diff_high;
1537 diff_low = (DWORD) (diff & 0xFFFFFFFF);
1538 diff_high = (DWORD) ((diff >> (sizeof (DWORD) * 8)) & 0xFFFFFFFF);
1540 memset (&o, 0, sizeof (OVERLAPPED));
1541 o.Offset = (DWORD) (lockStart & 0xFFFFFFFF);;
1542 o.OffsetHigh = (DWORD) (((lockStart & ~0xFFFFFFFF) >> (sizeof (DWORD) * 8)) & 0xFFFFFFFF);
1545 (fh->h, (excl ? LOCKFILE_EXCLUSIVE_LOCK : 0) | LOCKFILE_FAIL_IMMEDIATELY,
1546 0, diff_low, diff_high, &o))
1548 SetErrnoFromWinError (GetLastError ());
1549 return GNUNET_SYSERR;
1558 * Unlock a part of a file
1559 * @param fh file handle
1560 * @param unlockStart absolute position from where to unlock
1561 * @param unlockEnd absolute position until where to unlock
1562 * @return GNUNET_OK on success, GNUNET_SYSERR on error
1565 GNUNET_DISK_file_unlock (struct GNUNET_DISK_FileHandle *fh, OFF_T unlockStart,
1571 return GNUNET_SYSERR;
1577 memset (&fl, 0, sizeof (struct flock));
1578 fl.l_type = F_UNLCK;
1579 fl.l_whence = SEEK_SET;
1580 fl.l_start = unlockStart;
1581 fl.l_len = unlockEnd;
1583 return fcntl (fh->fd, F_SETLK, &fl) != 0 ? GNUNET_SYSERR : GNUNET_OK;
1586 OFF_T diff = unlockEnd - unlockStart;
1587 DWORD diff_low, diff_high;
1588 diff_low = (DWORD) (diff & 0xFFFFFFFF);
1589 diff_high = (DWORD) ((diff >> (sizeof (DWORD) * 8)) & 0xFFFFFFFF);
1591 memset (&o, 0, sizeof (OVERLAPPED));
1592 o.Offset = (DWORD) (unlockStart & 0xFFFFFFFF);;
1593 o.OffsetHigh = (DWORD) (((unlockStart & ~0xFFFFFFFF) >> (sizeof (DWORD) * 8)) & 0xFFFFFFFF);
1595 if (!UnlockFileEx (fh->h, 0, diff_low, diff_high, &o))
1597 SetErrnoFromWinError (GetLastError ());
1598 return GNUNET_SYSERR;
1607 * Open a file. Note that the access permissions will only be
1608 * used if a new file is created and if the underlying operating
1609 * system supports the given permissions.
1611 * @param fn file name to be opened
1612 * @param flags opening flags, a combination of GNUNET_DISK_OPEN_xxx bit flags
1613 * @param perm permissions for the newly created file, use
1614 * GNUNET_DISK_PERM_USER_NONE if a file could not be created by this
1615 * call (because of flags)
1616 * @return IO handle on success, NULL on error
1618 struct GNUNET_DISK_FileHandle *
1619 GNUNET_DISK_file_open (const char *fn, enum GNUNET_DISK_OpenFlags flags,
1620 enum GNUNET_DISK_AccessPermissions perm)
1623 struct GNUNET_DISK_FileHandle *ret;
1629 wchar_t wexpfn[MAX_PATH + 1];
1636 expfn = GNUNET_STRINGS_filename_expand (fn);
1641 if (GNUNET_DISK_OPEN_READWRITE == (flags & GNUNET_DISK_OPEN_READWRITE))
1642 oflags = O_RDWR; /* note: O_RDWR is NOT always O_RDONLY | O_WRONLY */
1643 else if (flags & GNUNET_DISK_OPEN_READ)
1645 else if (flags & GNUNET_DISK_OPEN_WRITE)
1650 GNUNET_free (expfn);
1653 if (flags & GNUNET_DISK_OPEN_FAILIFEXISTS)
1654 oflags |= (O_CREAT | O_EXCL);
1655 if (flags & GNUNET_DISK_OPEN_TRUNCATE)
1657 if (flags & GNUNET_DISK_OPEN_APPEND)
1659 if (flags & GNUNET_DISK_OPEN_CREATE)
1661 (void) GNUNET_DISK_directory_create_for_file (expfn);
1663 mode = translate_unix_perms (perm);
1666 fd = open (expfn, oflags | O_LARGEFILE, mode);
1669 if (0 == (flags & GNUNET_DISK_OPEN_FAILIFEXISTS))
1670 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "open", expfn);
1672 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_DEBUG, "open", expfn);
1673 GNUNET_free (expfn);
1680 if (GNUNET_DISK_OPEN_READWRITE == (flags & GNUNET_DISK_OPEN_READWRITE))
1681 access = FILE_READ_DATA | FILE_WRITE_DATA;
1682 else if (flags & GNUNET_DISK_OPEN_READ)
1683 access = FILE_READ_DATA;
1684 else if (flags & GNUNET_DISK_OPEN_WRITE)
1685 access = FILE_WRITE_DATA;
1687 if (flags & GNUNET_DISK_OPEN_FAILIFEXISTS)
1691 else if (flags & GNUNET_DISK_OPEN_CREATE)
1693 (void) GNUNET_DISK_directory_create_for_file (expfn);
1694 if (flags & GNUNET_DISK_OPEN_TRUNCATE)
1695 disp = CREATE_ALWAYS;
1699 else if (flags & GNUNET_DISK_OPEN_TRUNCATE)
1701 disp = TRUNCATE_EXISTING;
1705 disp = OPEN_EXISTING;
1708 if (ERROR_SUCCESS == plibc_conv_to_win_pathwconv(expfn, wexpfn))
1709 h = CreateFileW (wexpfn, access,
1710 FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE, NULL,
1711 disp, FILE_ATTRIBUTE_NORMAL, NULL);
1713 h = INVALID_HANDLE_VALUE;
1714 if (h == INVALID_HANDLE_VALUE)
1717 SetErrnoFromWinError (GetLastError ());
1719 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_INFO, "open", expfn);
1720 GNUNET_free (expfn);
1725 if (flags & GNUNET_DISK_OPEN_APPEND)
1726 if (SetFilePointer (h, 0, 0, FILE_END) == INVALID_SET_FILE_POINTER)
1728 SetErrnoFromWinError (GetLastError ());
1729 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "SetFilePointer", expfn);
1731 GNUNET_free (expfn);
1736 ret = GNUNET_malloc (sizeof (struct GNUNET_DISK_FileHandle));
1739 ret->type = GNUNET_DISK_HANLDE_TYPE_FILE;
1743 GNUNET_free (expfn);
1749 * Close an open file
1750 * @param h file handle
1751 * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
1754 GNUNET_DISK_file_close (struct GNUNET_DISK_FileHandle *h)
1759 return GNUNET_SYSERR;
1763 if (!CloseHandle (h->h))
1765 SetErrnoFromWinError (GetLastError ());
1766 LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING, "close");
1767 GNUNET_free (h->oOverlapRead);
1768 GNUNET_free (h->oOverlapWrite);
1770 return GNUNET_SYSERR;
1773 if (close (h->fd) != 0)
1775 LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING, "close");
1777 return GNUNET_SYSERR;
1786 * Get a handle from a native FD.
1788 * @param fd native file descriptor
1789 * @return file handle corresponding to the descriptor
1791 struct GNUNET_DISK_FileHandle *
1792 GNUNET_DISK_get_handle_from_native (FILE *fd)
1794 struct GNUNET_DISK_FileHandle *fh;
1805 osfh = _get_osfhandle (fno);
1806 if (INVALID_HANDLE_VALUE == (HANDLE) osfh)
1810 fh = GNUNET_malloc (sizeof (struct GNUNET_DISK_FileHandle));
1813 fh->h = (HANDLE) osfh;
1814 /* Assume it to be a pipe. TODO: use some kind of detection
1815 * function to figure out handle type.
1816 * Note that we can't make it overlapped if it isn't already.
1817 * (ReOpenFile() is only available in 2003/Vista).
1818 * The process that opened this file in the first place (usually a parent
1819 * process, if this is stdin/stdout/stderr) must make it overlapped,
1820 * otherwise we're screwed, as selecting on non-overlapped handle
1823 fh->type = GNUNET_DISK_HANLDE_TYPE_PIPE;
1824 fh->oOverlapRead = GNUNET_malloc (sizeof (OVERLAPPED));
1825 fh->oOverlapWrite = GNUNET_malloc (sizeof (OVERLAPPED));
1826 fh->oOverlapRead->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
1827 fh->oOverlapWrite->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
1837 * Construct full path to a file inside of the private
1838 * directory used by GNUnet. Also creates the corresponding
1839 * directory. If the resulting name is supposed to be
1840 * a directory, end the last argument in '/' (or pass
1841 * DIR_SEPARATOR_STR as the last argument before NULL).
1843 * @param cfg configuration to use (determines HOME)
1844 * @param serviceName name of the service
1845 * @param ... is NULL-terminated list of
1846 * path components to append to the
1847 * private directory name.
1848 * @return the constructed filename
1851 GNUNET_DISK_get_home_filename (const struct GNUNET_CONFIGURATION_Handle *cfg,
1852 const char *serviceName, ...)
1858 unsigned int needed;
1861 GNUNET_CONFIGURATION_get_value_filename (cfg, serviceName, "HOME", &pfx))
1865 LOG (GNUNET_ERROR_TYPE_WARNING,
1866 _("No `%s' specified for service `%s' in configuration.\n"), "HOME",
1870 needed = strlen (pfx) + 2;
1871 if ((pfx[strlen (pfx) - 1] != '/') && (pfx[strlen (pfx) - 1] != '\\'))
1873 va_start (ap, serviceName);
1876 c = va_arg (ap, const char *);
1880 needed += strlen (c);
1881 if ((c[strlen (c) - 1] != '/') && (c[strlen (c) - 1] != '\\'))
1885 ret = GNUNET_malloc (needed);
1888 va_start (ap, serviceName);
1891 c = va_arg (ap, const char *);
1895 if ((c[strlen (c) - 1] != '/') && (c[strlen (c) - 1] != '\\'))
1896 strcat (ret, DIR_SEPARATOR_STR);
1900 if ((ret[strlen (ret) - 1] != '/') && (ret[strlen (ret) - 1] != '\\'))
1901 (void) GNUNET_DISK_directory_create_for_file (ret);
1903 (void) GNUNET_DISK_directory_create (ret);
1909 * Handle for a memory-mapping operation.
1911 struct GNUNET_DISK_MapHandle
1914 * Address where the map is in memory.
1920 * Underlying OS handle.
1925 * Number of bytes mapped.
1933 #define MAP_FAILED ((void *) -1)
1937 * Map a file into memory
1939 * @param h open file handle
1940 * @param m handle to the new mapping
1941 * @param access access specification, GNUNET_DISK_MAP_TYPE_xxx
1942 * @param len size of the mapping
1943 * @return pointer to the mapped memory region, NULL on failure
1946 GNUNET_DISK_file_map (const struct GNUNET_DISK_FileHandle *h,
1947 struct GNUNET_DISK_MapHandle **m,
1948 enum GNUNET_DISK_MapType access, size_t len)
1957 DWORD mapAccess, protect;
1959 if ((access & GNUNET_DISK_MAP_TYPE_READ) &&
1960 (access & GNUNET_DISK_MAP_TYPE_WRITE))
1962 protect = PAGE_READWRITE;
1963 mapAccess = FILE_MAP_ALL_ACCESS;
1965 else if (access & GNUNET_DISK_MAP_TYPE_READ)
1967 protect = PAGE_READONLY;
1968 mapAccess = FILE_MAP_READ;
1970 else if (access & GNUNET_DISK_MAP_TYPE_WRITE)
1972 protect = PAGE_READWRITE;
1973 mapAccess = FILE_MAP_WRITE;
1981 *m = GNUNET_malloc (sizeof (struct GNUNET_DISK_MapHandle));
1982 (*m)->h = CreateFileMapping (h->h, NULL, protect, 0, 0, NULL);
1983 if ((*m)->h == INVALID_HANDLE_VALUE)
1985 SetErrnoFromWinError (GetLastError ());
1990 (*m)->addr = MapViewOfFile ((*m)->h, mapAccess, 0, 0, len);
1993 SetErrnoFromWinError (GetLastError ());
1994 CloseHandle ((*m)->h);
2003 if (access & GNUNET_DISK_MAP_TYPE_READ)
2005 if (access & GNUNET_DISK_MAP_TYPE_WRITE)
2007 *m = GNUNET_malloc (sizeof (struct GNUNET_DISK_MapHandle));
2008 (*m)->addr = mmap (NULL, len, prot, MAP_SHARED, h->fd, 0);
2009 GNUNET_assert (NULL != (*m)->addr);
2010 if (MAP_FAILED == (*m)->addr)
2022 * @param h mapping handle
2023 * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
2026 GNUNET_DISK_file_unmap (struct GNUNET_DISK_MapHandle *h)
2033 return GNUNET_SYSERR;
2037 ret = UnmapViewOfFile (h->addr) ? GNUNET_OK : GNUNET_SYSERR;
2038 if (ret != GNUNET_OK)
2039 SetErrnoFromWinError (GetLastError ());
2040 if (!CloseHandle (h->h) && (ret == GNUNET_OK))
2042 ret = GNUNET_SYSERR;
2043 SetErrnoFromWinError (GetLastError ());
2046 ret = munmap (h->addr, h->len) != -1 ? GNUNET_OK : GNUNET_SYSERR;
2054 * Write file changes to disk
2055 * @param h handle to an open file
2056 * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
2059 GNUNET_DISK_file_sync (const struct GNUNET_DISK_FileHandle *h)
2064 return GNUNET_SYSERR;
2070 ret = FlushFileBuffers (h->h) ? GNUNET_OK : GNUNET_SYSERR;
2071 if (ret != GNUNET_OK)
2072 SetErrnoFromWinError (GetLastError ());
2074 #elif defined(FREEBSD) || defined(OPENBSD) || defined(DARWIN)
2075 return fsync (h->fd) == -1 ? GNUNET_SYSERR : GNUNET_OK;
2077 return fdatasync (h->fd) == -1 ? GNUNET_SYSERR : GNUNET_OK;
2083 /* Copyright Bob Byrnes <byrnes <at> curl.com>
2084 http://permalink.gmane.org/gmane.os.cygwin.patches/2121
2086 /* Create a pipe, and return handles to the read and write ends,
2087 just like CreatePipe, but ensure that the write end permits
2088 FILE_READ_ATTRIBUTES access, on later versions of win32 where
2089 this is supported. This access is needed by NtQueryInformationFile,
2090 which is used to implement select and nonblocking writes.
2091 Note that the return value is either NO_ERROR or GetLastError,
2092 unlike CreatePipe, which returns a bool for success or failure. */
2094 create_selectable_pipe (PHANDLE read_pipe_ptr, PHANDLE write_pipe_ptr,
2095 LPSECURITY_ATTRIBUTES sa_ptr, DWORD psize,
2096 DWORD dwReadMode, DWORD dwWriteMode)
2098 /* Default to error. */
2099 *read_pipe_ptr = *write_pipe_ptr = INVALID_HANDLE_VALUE;
2101 HANDLE read_pipe = INVALID_HANDLE_VALUE, write_pipe = INVALID_HANDLE_VALUE;
2103 /* Ensure that there is enough pipe buffer space for atomic writes. */
2104 if (psize < PIPE_BUF)
2107 char pipename[MAX_PATH];
2109 /* Retry CreateNamedPipe as long as the pipe name is in use.
2110 * Retrying will probably never be necessary, but we want
2111 * to be as robust as possible. */
2114 static volatile LONG pipe_unique_id;
2116 snprintf (pipename, sizeof pipename, "\\\\.\\pipe\\gnunet-%d-%ld",
2117 getpid (), InterlockedIncrement ((LONG *) & pipe_unique_id));
2118 LOG (GNUNET_ERROR_TYPE_DEBUG, "CreateNamedPipe: name = %s, size = %lu\n",
2120 /* Use CreateNamedPipe instead of CreatePipe, because the latter
2121 * returns a write handle that does not permit FILE_READ_ATTRIBUTES
2122 * access, on versions of win32 earlier than WinXP SP2.
2123 * CreatePipe also stupidly creates a full duplex pipe, which is
2124 * a waste, since only a single direction is actually used.
2125 * It's important to only allow a single instance, to ensure that
2126 * the pipe was not created earlier by some other process, even if
2127 * the pid has been reused. We avoid FILE_FLAG_FIRST_PIPE_INSTANCE
2128 * because that is only available for Win2k SP2 and WinXP. */
2129 read_pipe = CreateNamedPipeA (pipename, PIPE_ACCESS_INBOUND | dwReadMode, PIPE_TYPE_BYTE | PIPE_READMODE_BYTE, 1, /* max instances */
2130 psize, /* output buffer size */
2131 psize, /* input buffer size */
2132 NMPWAIT_USE_DEFAULT_WAIT, sa_ptr);
2134 if (read_pipe != INVALID_HANDLE_VALUE)
2136 LOG (GNUNET_ERROR_TYPE_DEBUG, "pipe read handle = %p\n", read_pipe);
2140 DWORD err = GetLastError ();
2144 case ERROR_PIPE_BUSY:
2145 /* The pipe is already open with compatible parameters.
2146 * Pick a new name and retry. */
2147 LOG (GNUNET_ERROR_TYPE_DEBUG, "pipe busy, retrying\n");
2149 case ERROR_ACCESS_DENIED:
2150 /* The pipe is already open with incompatible parameters.
2151 * Pick a new name and retry. */
2152 LOG (GNUNET_ERROR_TYPE_DEBUG, "pipe access denied, retrying\n");
2154 case ERROR_CALL_NOT_IMPLEMENTED:
2155 /* We are on an older Win9x platform without named pipes.
2156 * Return an anonymous pipe as the best approximation. */
2157 LOG (GNUNET_ERROR_TYPE_DEBUG,
2158 "CreateNamedPipe not implemented, resorting to "
2159 "CreatePipe: size = %lu\n", psize);
2160 if (CreatePipe (read_pipe_ptr, write_pipe_ptr, sa_ptr, psize))
2162 LOG (GNUNET_ERROR_TYPE_DEBUG, "pipe read handle = %p, write handle = %p\n",
2167 err = GetLastError ();
2168 LOG (GNUNET_ERROR_TYPE_ERROR, "CreatePipe failed: %d\n", err);
2171 LOG (GNUNET_ERROR_TYPE_ERROR, "CreateNamedPipe failed: %d\n", err);
2176 LOG (GNUNET_ERROR_TYPE_DEBUG, "CreateFile: name = %s\n", pipename);
2178 /* Open the named pipe for writing.
2179 * Be sure to permit FILE_READ_ATTRIBUTES access. */
2180 write_pipe = CreateFileA (pipename, GENERIC_WRITE | FILE_READ_ATTRIBUTES, 0, /* share mode */
2181 sa_ptr, OPEN_EXISTING, dwWriteMode, /* flags and attributes */
2182 0); /* handle to template file */
2184 if (write_pipe == INVALID_HANDLE_VALUE)
2187 DWORD err = GetLastError ();
2189 LOG (GNUNET_ERROR_TYPE_DEBUG, "CreateFile failed: %d\n", err);
2190 CloseHandle (read_pipe);
2193 LOG (GNUNET_ERROR_TYPE_DEBUG, "pipe write handle = %p\n", write_pipe);
2195 *read_pipe_ptr = read_pipe;
2196 *write_pipe_ptr = write_pipe;
2203 * Creates an interprocess channel
2205 * @param blocking_read creates an asynchronous pipe for reading if set to GNUNET_NO
2206 * @param blocking_write creates an asynchronous pipe for writing if set to GNUNET_NO
2207 * @param inherit_read inherit the parent processes stdin (only for windows)
2208 * @param inherit_write inherit the parent processes stdout (only for windows)
2209 * @return handle to the new pipe, NULL on error
2211 struct GNUNET_DISK_PipeHandle *
2212 GNUNET_DISK_pipe (int blocking_read, int blocking_write, int inherit_read, int inherit_write)
2223 LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR, "pipe");
2227 return GNUNET_DISK_pipe_from_fd (blocking_read,
2231 struct GNUNET_DISK_PipeHandle *p;
2232 struct GNUNET_DISK_FileHandle *fds;
2237 p = GNUNET_malloc (sizeof (struct GNUNET_DISK_PipeHandle) +
2238 2 * sizeof (struct GNUNET_DISK_FileHandle));
2239 fds = (struct GNUNET_DISK_FileHandle *) &p[1];
2243 /* All pipes are overlapped. If you want them to block - just
2244 * call WriteFile() and ReadFile() with NULL overlapped pointer.
2247 create_selectable_pipe (&p->fd[0]->h, &p->fd[1]->h, NULL, 0,
2248 FILE_FLAG_OVERLAPPED,
2249 FILE_FLAG_OVERLAPPED);
2253 SetErrnoFromWinError (GetLastError ());
2256 if (!DuplicateHandle
2257 (GetCurrentProcess (), p->fd[0]->h, GetCurrentProcess (), &tmp_handle, 0,
2258 inherit_read == GNUNET_YES ? TRUE : FALSE, DUPLICATE_SAME_ACCESS))
2260 SetErrnoFromWinError (GetLastError ());
2261 CloseHandle (p->fd[0]->h);
2262 CloseHandle (p->fd[1]->h);
2266 CloseHandle (p->fd[0]->h);
2267 p->fd[0]->h = tmp_handle;
2269 if (!DuplicateHandle
2270 (GetCurrentProcess (), p->fd[1]->h, GetCurrentProcess (), &tmp_handle, 0,
2271 inherit_write == GNUNET_YES ? TRUE : FALSE, DUPLICATE_SAME_ACCESS))
2273 SetErrnoFromWinError (GetLastError ());
2274 CloseHandle (p->fd[0]->h);
2275 CloseHandle (p->fd[1]->h);
2279 CloseHandle (p->fd[1]->h);
2280 p->fd[1]->h = tmp_handle;
2282 p->fd[0]->type = GNUNET_DISK_HANLDE_TYPE_PIPE;
2283 p->fd[1]->type = GNUNET_DISK_HANLDE_TYPE_PIPE;
2285 p->fd[0]->oOverlapRead = GNUNET_malloc (sizeof (OVERLAPPED));
2286 p->fd[0]->oOverlapWrite = GNUNET_malloc (sizeof (OVERLAPPED));
2287 p->fd[1]->oOverlapRead = GNUNET_malloc (sizeof (OVERLAPPED));
2288 p->fd[1]->oOverlapWrite = GNUNET_malloc (sizeof (OVERLAPPED));
2290 p->fd[0]->oOverlapRead->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2291 p->fd[0]->oOverlapWrite->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2293 p->fd[1]->oOverlapRead->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2294 p->fd[1]->oOverlapWrite->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2302 * Creates a pipe object from a couple of file descriptors.
2303 * Useful for wrapping existing pipe FDs.
2305 * @param blocking_read creates an asynchronous pipe for reading if set to GNUNET_NO
2306 * @param blocking_write creates an asynchronous pipe for writing if set to GNUNET_NO
2307 * @param fd an array of two fd values. One of them may be -1 for read-only or write-only pipes
2309 * @return handle to the new pipe, NULL on error
2311 struct GNUNET_DISK_PipeHandle *
2312 GNUNET_DISK_pipe_from_fd (int blocking_read, int blocking_write, int fd[2])
2314 struct GNUNET_DISK_PipeHandle *p;
2315 struct GNUNET_DISK_FileHandle *fds;
2317 p = GNUNET_malloc (sizeof (struct GNUNET_DISK_PipeHandle) +
2318 2 * sizeof (struct GNUNET_DISK_FileHandle));
2319 fds = (struct GNUNET_DISK_FileHandle *) &p[1];
2325 int eno = 0; /* make gcc happy */
2327 p->fd[0]->fd = fd[0];
2328 p->fd[1]->fd = fd[1];
2334 flags = fcntl (fd[0], F_GETFL);
2335 flags |= O_NONBLOCK;
2336 if (0 > fcntl (fd[0], F_SETFL, flags))
2342 flags = fcntl (fd[0], F_GETFD);
2343 flags |= FD_CLOEXEC;
2344 if (0 > fcntl (fd[0], F_SETFD, flags))
2353 if (!blocking_write)
2355 flags = fcntl (fd[1], F_GETFL);
2356 flags |= O_NONBLOCK;
2357 if (0 > fcntl (fd[1], F_SETFL, flags))
2363 flags = fcntl (fd[1], F_GETFD);
2364 flags |= FD_CLOEXEC;
2365 if (0 > fcntl (fd[1], F_SETFD, flags))
2374 LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR, "fcntl");
2375 if (p->fd[0]->fd >= 0)
2376 GNUNET_break (0 == close (p->fd[0]->fd));
2377 if (p->fd[1]->fd >= 0)
2378 GNUNET_break (0 == close (p->fd[1]->fd));
2385 p->fd[0]->h = (HANDLE) _get_osfhandle (fd[0]);
2387 p->fd[0]->h = INVALID_HANDLE_VALUE;
2389 p->fd[1]->h = (HANDLE) _get_osfhandle (fd[1]);
2391 p->fd[1]->h = INVALID_HANDLE_VALUE;
2393 if (p->fd[0]->h != INVALID_HANDLE_VALUE)
2395 p->fd[0]->type = GNUNET_DISK_HANLDE_TYPE_PIPE;
2396 p->fd[0]->oOverlapRead = GNUNET_malloc (sizeof (OVERLAPPED));
2397 p->fd[0]->oOverlapWrite = GNUNET_malloc (sizeof (OVERLAPPED));
2398 p->fd[0]->oOverlapRead->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2399 p->fd[0]->oOverlapWrite->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2402 if (p->fd[1]->h != INVALID_HANDLE_VALUE)
2404 p->fd[1]->type = GNUNET_DISK_HANLDE_TYPE_PIPE;
2405 p->fd[1]->oOverlapRead = GNUNET_malloc (sizeof (OVERLAPPED));
2406 p->fd[1]->oOverlapWrite = GNUNET_malloc (sizeof (OVERLAPPED));
2407 p->fd[1]->oOverlapRead->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2408 p->fd[1]->oOverlapWrite->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2416 * Closes an interprocess channel
2418 * @param p pipe to close
2419 * @param end which end of the pipe to close
2420 * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
2423 GNUNET_DISK_pipe_close_end (struct GNUNET_DISK_PipeHandle *p,
2424 enum GNUNET_DISK_PipeEnd end)
2426 int ret = GNUNET_OK;
2430 if (end == GNUNET_DISK_PIPE_END_READ)
2432 if (p->fd[0]->h != INVALID_HANDLE_VALUE)
2434 if (!CloseHandle (p->fd[0]->h))
2436 SetErrnoFromWinError (GetLastError ());
2437 ret = GNUNET_SYSERR;
2439 GNUNET_free (p->fd[0]->oOverlapRead);
2440 GNUNET_free (p->fd[0]->oOverlapWrite);
2441 p->fd[0]->h = INVALID_HANDLE_VALUE;
2444 else if (end == GNUNET_DISK_PIPE_END_WRITE)
2446 if (p->fd[0]->h != INVALID_HANDLE_VALUE)
2448 if (!CloseHandle (p->fd[1]->h))
2450 SetErrnoFromWinError (GetLastError ());
2451 ret = GNUNET_SYSERR;
2453 GNUNET_free (p->fd[1]->oOverlapRead);
2454 GNUNET_free (p->fd[1]->oOverlapWrite);
2455 p->fd[1]->h = INVALID_HANDLE_VALUE;
2461 if (end == GNUNET_DISK_PIPE_END_READ)
2463 if (0 != close (p->fd[0]->fd))
2465 ret = GNUNET_SYSERR;
2470 else if (end == GNUNET_DISK_PIPE_END_WRITE)
2472 if (0 != close (p->fd[1]->fd))
2474 ret = GNUNET_SYSERR;
2486 * Closes an interprocess channel
2488 * @param p pipe to close
2489 * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
2492 GNUNET_DISK_pipe_close (struct GNUNET_DISK_PipeHandle *p)
2494 int ret = GNUNET_OK;
2498 if (p->fd[0]->h != INVALID_HANDLE_VALUE)
2500 if (!CloseHandle (p->fd[0]->h))
2502 SetErrnoFromWinError (GetLastError ());
2503 ret = GNUNET_SYSERR;
2505 GNUNET_free (p->fd[0]->oOverlapRead);
2506 GNUNET_free (p->fd[0]->oOverlapWrite);
2508 if (p->fd[1]->h != INVALID_HANDLE_VALUE)
2510 if (!CloseHandle (p->fd[1]->h))
2512 SetErrnoFromWinError (GetLastError ());
2513 ret = GNUNET_SYSERR;
2515 GNUNET_free (p->fd[1]->oOverlapRead);
2516 GNUNET_free (p->fd[1]->oOverlapWrite);
2521 if (p->fd[0]->fd != -1)
2523 if (0 != close (p->fd[0]->fd))
2525 ret = GNUNET_SYSERR;
2530 if (p->fd[1]->fd != -1)
2532 if (0 != close (p->fd[1]->fd))
2534 ret = GNUNET_SYSERR;
2546 * Get the handle to a particular pipe end
2549 * @param n end to access
2550 * @return handle for the respective end
2552 const struct GNUNET_DISK_FileHandle *
2553 GNUNET_DISK_pipe_handle (const struct GNUNET_DISK_PipeHandle *p,
2554 enum GNUNET_DISK_PipeEnd n)
2558 case GNUNET_DISK_PIPE_END_READ:
2559 case GNUNET_DISK_PIPE_END_WRITE:
2569 * Retrieve OS file handle
2571 * @param fh GNUnet file descriptor
2572 * @param dst destination buffer
2573 * @param dst_len length of dst
2574 * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
2577 GNUNET_DISK_internal_file_handle_ (const struct GNUNET_DISK_FileHandle *fh,
2578 void *dst, size_t dst_len)
2581 if (dst_len < sizeof (HANDLE))
2582 return GNUNET_SYSERR;
2583 *((HANDLE *) dst) = fh->h;
2585 if (dst_len < sizeof (int))
2586 return GNUNET_SYSERR;
2587 *((int *) dst) = fh->fd;