2 This file is part of GNUnet.
3 (C) 2001--2012 Christian Grothoff (and other contributing authors)
5 GNUnet is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 2, or (at your
8 option) any later version.
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with GNUnet; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
23 * @brief disk IO convenience methods
24 * @author Christian Grothoff
29 #include "gnunet_common.h"
30 #include "gnunet_directories.h"
31 #include "gnunet_disk_lib.h"
32 #include "gnunet_scheduler_lib.h"
33 #include "gnunet_strings_lib.h"
34 #include "gnunet_crypto_lib.h"
37 #define LOG(kind,...) GNUNET_log_from (kind, "util", __VA_ARGS__)
39 #define LOG_STRERROR(kind,syscall) GNUNET_log_from_strerror (kind, "util", syscall)
41 #define LOG_STRERROR_FILE(kind,syscall,filename) GNUNET_log_from_strerror_file (kind, "util", syscall, filename)
44 * Block size for IO for copying files.
46 #define COPY_BLK_SIZE 65536
50 #if defined(LINUX) || defined(CYGWIN) || defined(GNU)
53 #if defined(SOMEBSD) || defined(DARWIN)
54 #include <sys/param.h>
55 #include <sys/mount.h>
58 #include <sys/types.h>
59 #include <sys/statvfs.h>
64 ULONG PipeSerialNumber;
66 #define _IFMT 0170000 /* type of file */
67 #define _IFLNK 0120000 /* symbolic link */
68 #define S_ISLNK(m) (((m)&_IFMT) == _IFLNK)
70 #error PORT-ME: need to port statfs (how much space is left on the drive?)
76 #if !defined(SOMEBSD) && !defined(DARWIN) && !defined(WINDOWS)
80 #include <sys/statvfs.h>
85 * Handle used to manage a pipe.
87 struct GNUNET_DISK_PipeHandle
90 * File descriptors for the pipe.
92 struct GNUNET_DISK_FileHandle *fd[2];
97 * Closure for the recursion to determine the file size
100 struct GetFileSizeData
103 * Set to the total file size.
108 * GNUNET_YES if symbolic links should be included.
110 int include_sym_links;
113 * GNUNET_YES if mode is file-only (return total == -1 for directories).
115 int single_file_mode;
121 * Translate GNUnet-internal permission bitmap to UNIX file
122 * access permission bitmap.
124 * @param perm file permissions, GNUnet style
125 * @return file permissions, UNIX style
128 translate_unix_perms (enum GNUNET_DISK_AccessPermissions perm)
133 if (perm & GNUNET_DISK_PERM_USER_READ)
135 if (perm & GNUNET_DISK_PERM_USER_WRITE)
137 if (perm & GNUNET_DISK_PERM_USER_EXEC)
139 if (perm & GNUNET_DISK_PERM_GROUP_READ)
141 if (perm & GNUNET_DISK_PERM_GROUP_WRITE)
143 if (perm & GNUNET_DISK_PERM_GROUP_EXEC)
145 if (perm & GNUNET_DISK_PERM_OTHER_READ)
147 if (perm & GNUNET_DISK_PERM_OTHER_WRITE)
149 if (perm & GNUNET_DISK_PERM_OTHER_EXEC)
158 * Iterate over all files in the given directory and
159 * accumulate their size.
161 * @param cls closure of type "struct GetFileSizeData"
162 * @param fn current filename we are looking at
163 * @return GNUNET_SYSERR on serious errors, otherwise GNUNET_OK
166 getSizeRec (void *cls, const char *fn)
168 struct GetFileSizeData *gfsd = cls;
177 if (0 != STAT64 (fn, &buf))
179 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_DEBUG, "stat64", fn);
180 return GNUNET_SYSERR;
183 if (0 != STAT (fn, &buf))
185 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_DEBUG, "stat", fn);
186 return GNUNET_SYSERR;
189 if ((S_ISDIR (buf.st_mode)) && (gfsd->single_file_mode == GNUNET_YES))
192 return GNUNET_SYSERR;
194 if ((!S_ISLNK (buf.st_mode)) || (gfsd->include_sym_links == GNUNET_YES))
195 gfsd->total += buf.st_size;
196 if ((S_ISDIR (buf.st_mode)) && (0 == ACCESS (fn, X_OK)) &&
197 ((!S_ISLNK (buf.st_mode)) || (gfsd->include_sym_links == GNUNET_YES)))
199 if (GNUNET_SYSERR == GNUNET_DISK_directory_scan (fn, &getSizeRec, gfsd))
200 return GNUNET_SYSERR;
207 * Checks whether a handle is invalid
209 * @param h handle to check
210 * @return GNUNET_YES if invalid, GNUNET_NO if valid
213 GNUNET_DISK_handle_invalid (const struct GNUNET_DISK_FileHandle *h)
216 return ((!h) || (h->h == INVALID_HANDLE_VALUE)) ? GNUNET_YES : GNUNET_NO;
218 return ((!h) || (h->fd == -1)) ? GNUNET_YES : GNUNET_NO;
223 * Get the size of an open file.
225 * @param fh open file handle
226 * @param size where to write size of the file
227 * @return GNUNET_OK on success, GNUNET_SYSERR on error
230 GNUNET_DISK_file_handle_size (struct GNUNET_DISK_FileHandle *fh,
236 b = GetFileSizeEx (fh->h, &li);
239 SetErrnoFromWinError (GetLastError ());
240 return GNUNET_SYSERR;
242 *size = (OFF_T) li.QuadPart;
246 if (0 != FSTAT (fh->fd, &sbuf))
247 return GNUNET_SYSERR;
248 *size = sbuf.st_size;
255 * Move the read/write pointer in a file
257 * @param h handle of an open file
258 * @param offset position to move to
259 * @param whence specification to which position the offset parameter relates to
260 * @return the new position on success, GNUNET_SYSERR otherwise
263 GNUNET_DISK_file_seek (const struct GNUNET_DISK_FileHandle * h, OFF_T offset,
264 enum GNUNET_DISK_Seek whence)
269 return GNUNET_SYSERR;
274 LARGE_INTEGER new_pos;
277 static DWORD t[] = { FILE_BEGIN, FILE_CURRENT, FILE_END };
278 li.QuadPart = offset;
280 b = SetFilePointerEx (h->h, li, &new_pos, t[whence]);
283 SetErrnoFromWinError (GetLastError ());
284 return GNUNET_SYSERR;
286 return (OFF_T) new_pos.QuadPart;
288 static int t[] = { SEEK_SET, SEEK_CUR, SEEK_END };
290 return lseek (h->fd, offset, t[whence]);
296 * Get the size of the file (or directory) of the given file (in
299 * @param filename name of the file or directory
300 * @param size set to the size of the file (or,
301 * in the case of directories, the sum
302 * of all sizes of files in the directory)
303 * @param includeSymLinks should symbolic links be
305 * @param singleFileMode GNUNET_YES to only get size of one file
306 * and return GNUNET_SYSERR for directories.
307 * @return GNUNET_SYSERR on error, GNUNET_OK on success
310 GNUNET_DISK_file_size (const char *filename, uint64_t * size,
311 int includeSymLinks, int singleFileMode)
313 struct GetFileSizeData gfsd;
316 GNUNET_assert (size != NULL);
318 gfsd.include_sym_links = includeSymLinks;
319 gfsd.single_file_mode = singleFileMode;
320 ret = getSizeRec (&gfsd, filename);
327 * Obtain some unique identifiers for the given file
328 * that can be used to identify it in the local system.
329 * This function is used between GNUnet processes to
330 * quickly check if two files with the same absolute path
331 * are actually identical. The two processes represent
332 * the same peer but may communicate over the network
333 * (and the file may be on an NFS volume). This function
334 * may not be supported on all operating systems.
336 * @param filename name of the file
337 * @param dev set to the device ID
338 * @param ino set to the inode ID
339 * @return GNUNET_OK on success
342 GNUNET_DISK_file_get_identifiers (const char *filename, uint64_t * dev,
349 if ((0 == stat (filename, &sbuf)) && (0 == statvfs (filename, &fbuf)))
351 *dev = (uint64_t) fbuf.f_fsid;
352 *ino = (uint64_t) sbuf.st_ino;
359 if ((0 == stat (filename, &sbuf)) && (0 == statfs (filename, &fbuf)))
361 *dev = ((uint64_t) fbuf.f_fsid.val[0]) << 32 ||
362 ((uint64_t) fbuf.f_fsid.val[1]);
363 *ino = (uint64_t) sbuf.st_ino;
367 // FIXME NILS: test this
368 struct GNUNET_DISK_FileHandle *fh;
369 BY_HANDLE_FILE_INFORMATION info;
372 fh = GNUNET_DISK_file_open (filename, GNUNET_DISK_OPEN_READ, 0);
374 return GNUNET_SYSERR;
375 succ = GetFileInformationByHandle (fh->h, &info);
376 GNUNET_DISK_file_close (fh);
379 *dev = info.dwVolumeSerialNumber;
380 *ino = ((((uint64_t) info.nFileIndexHigh) << (sizeof (DWORD) * 8)) | info.nFileIndexLow);
384 return GNUNET_SYSERR;
387 return GNUNET_SYSERR;
392 * Create the name for a temporary file or directory from a template.
394 * @param t template (without XXXXX or "/tmp/")
395 * @return name ready for passing to 'mktemp' or 'mkdtemp', NULL on error
398 mktemp_name (const char *t)
404 if ((t[0] != '/') && (t[0] != '\\')
406 && !(isalpha ((int) t[0]) && (t[0] != '\0') && (t[1] == ':'))
410 /* FIXME: This uses system codepage on W32, not UTF-8 */
411 tmpdir = getenv ("TMPDIR");
413 tmpdir = getenv ("TMP");
415 tmpdir = getenv ("TEMP");
418 GNUNET_asprintf (&tmpl, "%s/%s%s", tmpdir, t, "XXXXXX");
422 GNUNET_asprintf (&tmpl, "%s%s", t, "XXXXXX");
425 fn = (char *) GNUNET_malloc (MAX_PATH + 1);
426 if (ERROR_SUCCESS != plibc_conv_to_win_path (tmpl, fn))
447 tfn = GNUNET_strdup (fn);
448 random_fn = _mktemp (tfn);
449 if (NULL == random_fn)
454 /* FIXME: assume fn to be UTF-8-encoded and do the right thing */
455 if (0 == CreateDirectoryA (tfn, NULL))
457 DWORD error = GetLastError ();
459 if (ERROR_ALREADY_EXISTS == error)
471 * Create an (empty) temporary directory on disk. If the given name is not
472 * an absolute path, the current 'TMPDIR' will be prepended. In any case,
473 * 6 random characters will be appended to the name to create a unique
476 * @param t component to use for the name;
477 * does NOT contain "XXXXXX" or "/tmp/".
478 * @return NULL on error, otherwise name of fresh
479 * file on disk in directory for temporary files
482 GNUNET_DISK_mkdtemp (const char *t)
486 fn = mktemp_name (t);
487 if (fn != mkdtemp (fn))
489 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_ERROR, "mkstemp", fn);
498 * Create an (empty) temporary file on disk. If the given name is not
499 * an absolute path, the current 'TMPDIR' will be prepended. In any case,
500 * 6 random characters will be appended to the name to create a unique
503 * @param t component to use for the name;
504 * does NOT contain "XXXXXX" or "/tmp/".
505 * @return NULL on error, otherwise name of fresh
506 * file on disk in directory for temporary files
509 GNUNET_DISK_mktemp (const char *t)
514 fn = mktemp_name (t);
515 if (-1 == (fd = mkstemp (fn)))
517 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_ERROR, "mkstemp", fn);
522 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "close", fn);
528 * Get the number of blocks that are left on the partition that
529 * contains the given file (for normal users).
531 * @param part a file on the partition to check
532 * @return -1 on errors, otherwise the number of free blocks
535 GNUNET_DISK_get_blocks_available (const char *part)
540 if (0 != statvfs (part, &buf))
542 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "statfs", part);
550 wchar_t wpath[MAX_PATH + 1];
553 path = GNUNET_STRINGS_filename_expand (part);
556 /* "part" was in UTF-8, and so is "path" */
557 if (ERROR_SUCCESS != plibc_conv_to_win_pathwconv(path, wpath))
563 wcsncpy (szDrive, wpath, 3);
565 if (!GetDiskFreeSpaceW (szDrive, &dwDummy, &dwDummy, &dwBlocks, &dwDummy))
567 LOG (GNUNET_ERROR_TYPE_WARNING, _("`%s' failed for drive `%S': %u\n"),
568 "GetDiskFreeSpace", szDrive, GetLastError ());
576 if (0 != statfs (part, &s))
578 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "statfs", part);
587 * Test if "fil" is a directory and readable. Also check if the directory is
588 * listable. Will not print an error message if the directory does not exist.
589 * Will log errors if GNUNET_SYSERR is returned (i.e., a file exists with the
592 * @param fil filename to test
593 * @param is_listable GNUNET_YES to additionally check if "fil" is listable;
594 * GNUNET_NO to disable this check
595 * @return GNUNET_YES if yes, GNUNET_NO if not; GNUNET_SYSERR if it
599 GNUNET_DISK_directory_test (const char *fil, int is_listable)
601 struct stat filestat;
604 ret = STAT (fil, &filestat);
609 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "stat", fil);
610 return GNUNET_SYSERR;
614 if (!S_ISDIR (filestat.st_mode))
616 if (GNUNET_YES == is_listable)
617 ret = ACCESS (fil, R_OK | X_OK);
619 ret = ACCESS (fil, R_OK);
622 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "access", fil);
623 return GNUNET_SYSERR;
630 * Check that fil corresponds to a filename
631 * (of a file that exists and that is not a directory).
633 * @param fil filename to check
634 * @return GNUNET_YES if yes, GNUNET_NO if not a file, GNUNET_SYSERR if something
635 * else (will print an error message in that case, too).
638 GNUNET_DISK_file_test (const char *fil)
640 struct stat filestat;
644 rdir = GNUNET_STRINGS_filename_expand (fil);
646 return GNUNET_SYSERR;
648 ret = STAT (rdir, &filestat);
653 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "stat", rdir);
655 return GNUNET_SYSERR;
660 if (!S_ISREG (filestat.st_mode))
665 if (ACCESS (rdir, F_OK) < 0)
667 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "access", rdir);
669 return GNUNET_SYSERR;
677 * Implementation of "mkdir -p"
678 * @param dir the directory to create
679 * @returns GNUNET_OK on success, GNUNET_SYSERR on failure
682 GNUNET_DISK_directory_create (const char *dir)
690 rdir = GNUNET_STRINGS_filename_expand (dir);
692 return GNUNET_SYSERR;
696 pos = 1; /* skip heading '/' */
698 /* Local or Network path? */
699 if (strncmp (rdir, "\\\\", 2) == 0)
704 if (rdir[pos] == '\\')
714 pos = 3; /* strlen("C:\\") */
717 /* Check which low level directories already exist */
719 rdir[len] = DIR_SEPARATOR;
722 if (DIR_SEPARATOR == rdir[pos2])
725 ret = GNUNET_DISK_directory_test (rdir, GNUNET_YES);
726 if (GNUNET_SYSERR == ret)
729 return GNUNET_SYSERR;
731 rdir[pos2] = DIR_SEPARATOR;
732 if (GNUNET_YES == ret)
743 /* Start creating directories */
746 if ((rdir[pos] == DIR_SEPARATOR) || (pos == len))
749 ret = GNUNET_DISK_directory_test (rdir, GNUNET_YES);
750 if (ret == GNUNET_SYSERR)
753 return GNUNET_SYSERR;
755 if (ret == GNUNET_NO)
758 ret = mkdir (rdir, S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH); /* 755 */
760 wchar_t wrdir[MAX_PATH + 1];
761 if (ERROR_SUCCESS == plibc_conv_to_win_pathwconv(rdir, wrdir))
762 ret = !CreateDirectoryW (wrdir, NULL);
766 if ((ret != 0) && (errno != EEXIST))
768 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_ERROR, "mkdir", rdir);
770 return GNUNET_SYSERR;
773 rdir[pos] = DIR_SEPARATOR;
783 * Create the directory structure for storing
786 * @param filename name of a file in the directory
787 * @returns GNUNET_OK on success,
788 * GNUNET_SYSERR on failure,
789 * GNUNET_NO if the directory
790 * exists but is not writeable for us
793 GNUNET_DISK_directory_create_for_file (const char *filename)
799 rdir = GNUNET_STRINGS_filename_expand (filename);
801 return GNUNET_SYSERR;
803 while ((len > 0) && (rdir[len] != DIR_SEPARATOR))
806 ret = GNUNET_DISK_directory_create (rdir);
807 if ((ret == GNUNET_OK) && (0 != ACCESS (rdir, W_OK)))
815 * Read the contents of a binary file into a buffer.
816 * @param h handle to an open file
817 * @param result the buffer to write the result to
818 * @param len the maximum number of bytes to read
819 * @return the number of bytes read on success, GNUNET_SYSERR on failure
822 GNUNET_DISK_file_read (const struct GNUNET_DISK_FileHandle * h, void *result,
828 return GNUNET_SYSERR;
834 if (h->type != GNUNET_DISK_HANLDE_TYPE_PIPE)
836 if (!ReadFile (h->h, result, len, &bytesRead, NULL))
838 SetErrnoFromWinError (GetLastError ());
839 return GNUNET_SYSERR;
844 if (!ReadFile (h->h, result, len, &bytesRead, h->oOverlapRead))
846 if (GetLastError () != ERROR_IO_PENDING)
848 LOG (GNUNET_ERROR_TYPE_DEBUG, "Error reading from pipe: %u\n", GetLastError ());
849 SetErrnoFromWinError (GetLastError ());
850 return GNUNET_SYSERR;
852 LOG (GNUNET_ERROR_TYPE_DEBUG, "Will get overlapped result\n");
853 GetOverlappedResult (h->h, h->oOverlapRead, &bytesRead, TRUE);
855 LOG (GNUNET_ERROR_TYPE_DEBUG, "Read %u bytes from pipe\n", bytesRead);
859 return read (h->fd, result, len);
865 * Read the contents of a binary file into a buffer.
866 * Guarantees not to block (returns GNUNET_SYSERR and sets errno to EAGAIN
867 * when no data can be read).
869 * @param h handle to an open file
870 * @param result the buffer to write the result to
871 * @param len the maximum number of bytes to read
872 * @return the number of bytes read on success, GNUNET_SYSERR on failure
875 GNUNET_DISK_file_read_non_blocking (const struct GNUNET_DISK_FileHandle * h,
882 return GNUNET_SYSERR;
888 if (h->type != GNUNET_DISK_HANLDE_TYPE_PIPE)
890 if (!ReadFile (h->h, result, len, &bytesRead, NULL))
892 SetErrnoFromWinError (GetLastError ());
893 return GNUNET_SYSERR;
898 if (!ReadFile (h->h, result, len, &bytesRead, h->oOverlapRead))
900 if (GetLastError () != ERROR_IO_PENDING)
902 LOG (GNUNET_ERROR_TYPE_DEBUG, "Error reading from pipe: %u\n", GetLastError ());
903 SetErrnoFromWinError (GetLastError ());
904 return GNUNET_SYSERR;
908 LOG (GNUNET_ERROR_TYPE_DEBUG,
909 "ReadFile() queued a read, cancelling\n");
912 return GNUNET_SYSERR;
915 LOG (GNUNET_ERROR_TYPE_DEBUG, "Read %u bytes\n", bytesRead);
922 /* set to non-blocking, read, then set back */
923 flags = fcntl (h->fd, F_GETFL);
924 if (0 == (flags & O_NONBLOCK))
925 (void) fcntl (h->fd, F_SETFL, flags | O_NONBLOCK);
926 ret = read (h->fd, result, len);
927 if (0 == (flags & O_NONBLOCK))
930 (void) fcntl (h->fd, F_SETFL, flags);
939 * Read the contents of a binary file into a buffer.
941 * @param fn file name
942 * @param result the buffer to write the result to
943 * @param len the maximum number of bytes to read
944 * @return number of bytes read, GNUNET_SYSERR on failure
947 GNUNET_DISK_fn_read (const char *fn, void *result, size_t len)
949 struct GNUNET_DISK_FileHandle *fh;
952 fh = GNUNET_DISK_file_open (fn, GNUNET_DISK_OPEN_READ, GNUNET_DISK_PERM_NONE);
954 return GNUNET_SYSERR;
955 ret = GNUNET_DISK_file_read (fh, result, len);
956 GNUNET_assert (GNUNET_OK == GNUNET_DISK_file_close (fh));
963 * Write a buffer to a file.
964 * @param h handle to open file
965 * @param buffer the data to write
966 * @param n number of bytes to write
967 * @return number of bytes written on success, GNUNET_SYSERR on error
970 GNUNET_DISK_file_write (const struct GNUNET_DISK_FileHandle * h,
971 const void *buffer, size_t n)
976 return GNUNET_SYSERR;
982 if (h->type != GNUNET_DISK_HANLDE_TYPE_PIPE)
984 if (!WriteFile (h->h, buffer, n, &bytesWritten, NULL))
986 SetErrnoFromWinError (GetLastError ());
987 return GNUNET_SYSERR;
992 LOG (GNUNET_ERROR_TYPE_DEBUG, "It is a pipe trying to write %u bytes\n", n);
993 if (!WriteFile (h->h, buffer, n, &bytesWritten, h->oOverlapWrite))
995 if (GetLastError () != ERROR_IO_PENDING)
997 SetErrnoFromWinError (GetLastError ());
998 LOG (GNUNET_ERROR_TYPE_DEBUG, "Error writing to pipe: %u\n",
1000 return GNUNET_SYSERR;
1002 LOG (GNUNET_ERROR_TYPE_DEBUG, "Will get overlapped result\n");
1003 if (!GetOverlappedResult (h->h, h->oOverlapWrite, &bytesWritten, TRUE))
1005 SetErrnoFromWinError (GetLastError ());
1006 LOG (GNUNET_ERROR_TYPE_DEBUG,
1007 "Error getting overlapped result while writing to pipe: %u\n",
1009 return GNUNET_SYSERR;
1015 if (!GetOverlappedResult (h->h, h->oOverlapWrite, &ovr, TRUE))
1017 LOG (GNUNET_ERROR_TYPE_DEBUG,
1018 "Error getting control overlapped result while writing to pipe: %u\n",
1023 LOG (GNUNET_ERROR_TYPE_DEBUG,
1024 "Wrote %u bytes (ovr says %u), picking the greatest\n",
1028 if (bytesWritten == 0)
1032 LOG (GNUNET_ERROR_TYPE_DEBUG, "Wrote %u bytes, returning -1 with EAGAIN\n", bytesWritten);
1034 return GNUNET_SYSERR;
1037 LOG (GNUNET_ERROR_TYPE_DEBUG, "Wrote %u bytes\n", bytesWritten);
1039 return bytesWritten;
1041 return write (h->fd, buffer, n);
1047 * Write a buffer to a file, blocking, if necessary.
1048 * @param h handle to open file
1049 * @param buffer the data to write
1050 * @param n number of bytes to write
1051 * @return number of bytes written on success, GNUNET_SYSERR on error
1054 GNUNET_DISK_file_write_blocking (const struct GNUNET_DISK_FileHandle * h,
1055 const void *buffer, size_t n)
1060 return GNUNET_SYSERR;
1065 /* We do a non-overlapped write, which is as blocking as it gets */
1066 LOG (GNUNET_ERROR_TYPE_DEBUG, "Writing %u bytes\n", n);
1067 if (!WriteFile (h->h, buffer, n, &bytesWritten, NULL))
1069 SetErrnoFromWinError (GetLastError ());
1070 LOG (GNUNET_ERROR_TYPE_DEBUG, "Error writing to pipe: %u\n",
1072 return GNUNET_SYSERR;
1074 if (bytesWritten == 0 && n > 0)
1076 LOG (GNUNET_ERROR_TYPE_DEBUG, "Waiting for pipe to clean\n");
1077 WaitForSingleObject (h->h, INFINITE);
1078 if (!WriteFile (h->h, buffer, n, &bytesWritten, NULL))
1080 SetErrnoFromWinError (GetLastError ());
1081 LOG (GNUNET_ERROR_TYPE_DEBUG, "Error writing to pipe: %u\n",
1083 return GNUNET_SYSERR;
1086 LOG (GNUNET_ERROR_TYPE_DEBUG, "Wrote %u bytes\n", bytesWritten);
1087 return bytesWritten;
1092 /* set to blocking, write, then set back */
1093 flags = fcntl (h->fd, F_GETFL);
1094 if (0 != (flags & O_NONBLOCK))
1095 (void) fcntl (h->fd, F_SETFL, flags - O_NONBLOCK);
1096 ret = write (h->fd, buffer, n);
1097 if (0 == (flags & O_NONBLOCK))
1098 (void) fcntl (h->fd, F_SETFL, flags);
1105 * Write a buffer to a file. If the file is longer than the
1106 * number of bytes that will be written, it will be truncated.
1108 * @param fn file name
1109 * @param buffer the data to write
1110 * @param n number of bytes to write
1111 * @param mode file permissions
1112 * @return number of bytes written on success, GNUNET_SYSERR on error
1115 GNUNET_DISK_fn_write (const char *fn, const void *buffer, size_t n,
1116 enum GNUNET_DISK_AccessPermissions mode)
1118 struct GNUNET_DISK_FileHandle *fh;
1121 fh = GNUNET_DISK_file_open (fn,
1122 GNUNET_DISK_OPEN_WRITE | GNUNET_DISK_OPEN_TRUNCATE
1123 | GNUNET_DISK_OPEN_CREATE, mode);
1125 return GNUNET_SYSERR;
1126 ret = GNUNET_DISK_file_write (fh, buffer, n);
1127 GNUNET_assert (GNUNET_OK == GNUNET_DISK_file_close (fh));
1133 * Scan a directory for files.
1135 * @param dirName the name of the directory
1136 * @param callback the method to call for each file,
1137 * can be NULL, in that case, we only count
1138 * @param callback_cls closure for callback
1139 * @return the number of files found, GNUNET_SYSERR on error or
1140 * ieration aborted by callback returning GNUNET_SYSERR
1143 GNUNET_DISK_directory_scan (const char *dirName,
1144 GNUNET_FileNameCallback callback,
1148 struct dirent *finfo;
1153 unsigned int name_len;
1154 unsigned int n_size;
1156 GNUNET_assert (dirName != NULL);
1157 dname = GNUNET_STRINGS_filename_expand (dirName);
1159 return GNUNET_SYSERR;
1160 while ((strlen (dname) > 0) && (dname[strlen (dname) - 1] == DIR_SEPARATOR))
1161 dname[strlen (dname) - 1] = '\0';
1162 if (0 != STAT (dname, &istat))
1164 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "stat", dname);
1165 GNUNET_free (dname);
1166 return GNUNET_SYSERR;
1168 if (!S_ISDIR (istat.st_mode))
1170 LOG (GNUNET_ERROR_TYPE_WARNING, _("Expected `%s' to be a directory!\n"),
1172 GNUNET_free (dname);
1173 return GNUNET_SYSERR;
1176 dinfo = OPENDIR (dname);
1177 if ((errno == EACCES) || (dinfo == NULL))
1179 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "opendir", dname);
1182 GNUNET_free (dname);
1183 return GNUNET_SYSERR;
1186 n_size = strlen (dname) + name_len + 2;
1187 name = GNUNET_malloc (n_size);
1188 while ((finfo = READDIR (dinfo)) != NULL)
1190 if ((0 == strcmp (finfo->d_name, ".")) ||
1191 (0 == strcmp (finfo->d_name, "..")))
1193 if (callback != NULL)
1195 if (name_len < strlen (finfo->d_name))
1198 name_len = strlen (finfo->d_name);
1199 n_size = strlen (dname) + name_len + 2;
1200 name = GNUNET_malloc (n_size);
1202 /* dname can end in "/" only if dname == "/";
1203 * if dname does not end in "/", we need to add
1204 * a "/" (otherwise, we must not!) */
1205 GNUNET_snprintf (name, n_size, "%s%s%s", dname,
1206 (strcmp (dname, DIR_SEPARATOR_STR) ==
1207 0) ? "" : DIR_SEPARATOR_STR, finfo->d_name);
1208 if (GNUNET_OK != callback (callback_cls, name))
1212 GNUNET_free (dname);
1213 return GNUNET_SYSERR;
1220 GNUNET_free (dname);
1226 * Opaque handle used for iterating over a directory.
1228 struct GNUNET_DISK_DirectoryIterator
1232 * Function to call on directory entries.
1234 GNUNET_DISK_DirectoryIteratorCallback callback;
1237 * Closure for callback.
1242 * Reference to directory.
1252 * Next filename to process.
1259 enum GNUNET_SCHEDULER_Priority priority;
1265 * Task used by the directory iterator.
1268 directory_iterator_task (void *cls,
1269 const struct GNUNET_SCHEDULER_TaskContext *tc)
1271 struct GNUNET_DISK_DirectoryIterator *iter = cls;
1274 name = iter->next_name;
1275 GNUNET_assert (name != NULL);
1276 iter->next_name = NULL;
1277 iter->callback (iter->callback_cls, iter, name, iter->dirname);
1283 * This function must be called during the DiskIteratorCallback
1284 * (exactly once) to schedule the task to process the next
1285 * filename in the directory (if there is one).
1287 * @param iter opaque handle for the iterator
1288 * @param can set to GNUNET_YES to terminate the iteration early
1289 * @return GNUNET_YES if iteration will continue,
1290 * GNUNET_NO if this was the last entry (and iteration is complete),
1291 * GNUNET_SYSERR if abort was YES
1294 GNUNET_DISK_directory_iterator_next (struct GNUNET_DISK_DirectoryIterator *iter,
1297 struct dirent *finfo;
1299 GNUNET_assert (iter->next_name == NULL);
1300 if (can == GNUNET_YES)
1302 CLOSEDIR (iter->directory);
1303 GNUNET_free (iter->dirname);
1305 return GNUNET_SYSERR;
1307 while (NULL != (finfo = READDIR (iter->directory)))
1309 if ((0 == strcmp (finfo->d_name, ".")) ||
1310 (0 == strcmp (finfo->d_name, "..")))
1312 GNUNET_asprintf (&iter->next_name, "%s%s%s", iter->dirname,
1313 DIR_SEPARATOR_STR, finfo->d_name);
1318 GNUNET_DISK_directory_iterator_next (iter, GNUNET_YES);
1321 GNUNET_SCHEDULER_add_with_priority (iter->priority, &directory_iterator_task,
1328 * Scan a directory for files using the scheduler to run a task for
1329 * each entry. The name of the directory must be expanded first (!).
1330 * If a scheduler does not need to be used, GNUNET_DISK_directory_scan
1331 * may provide a simpler API.
1333 * @param prio priority to use
1334 * @param dirName the name of the directory
1335 * @param callback the method to call for each file
1336 * @param callback_cls closure for callback
1337 * @return GNUNET_YES if directory is not empty and 'callback'
1338 * will be called later, GNUNET_NO otherwise, GNUNET_SYSERR on error.
1341 GNUNET_DISK_directory_iterator_start (enum GNUNET_SCHEDULER_Priority prio,
1342 const char *dirName,
1343 GNUNET_DISK_DirectoryIteratorCallback
1344 callback, void *callback_cls)
1346 struct GNUNET_DISK_DirectoryIterator *di;
1348 di = GNUNET_malloc (sizeof (struct GNUNET_DISK_DirectoryIterator));
1349 di->callback = callback;
1350 di->callback_cls = callback_cls;
1351 di->directory = OPENDIR (dirName);
1352 if (di->directory == NULL)
1355 callback (callback_cls, NULL, NULL, NULL);
1356 return GNUNET_SYSERR;
1358 di->dirname = GNUNET_strdup (dirName);
1359 di->priority = prio;
1360 return GNUNET_DISK_directory_iterator_next (di, GNUNET_NO);
1365 * Function that removes the given directory by calling
1366 * "GNUNET_DISK_directory_remove".
1368 * @param unused not used
1369 * @param fn directory to remove
1373 remove_helper (void *unused, const char *fn)
1375 (void) GNUNET_DISK_directory_remove (fn);
1381 * Remove all files in a directory (rm -rf). Call with
1385 * @param filename the file to remove
1386 * @return GNUNET_OK on success, GNUNET_SYSERR on error
1389 GNUNET_DISK_directory_remove (const char *filename)
1393 if (0 != LSTAT (filename, &istat))
1394 return GNUNET_NO; /* file may not exist... */
1395 (void) CHMOD (filename, S_IWUSR | S_IRUSR | S_IXUSR);
1396 if (UNLINK (filename) == 0)
1398 if ((errno != EISDIR) &&
1399 /* EISDIR is not sufficient in all cases, e.g.
1400 * sticky /tmp directory may result in EPERM on BSD.
1401 * So we also explicitly check "isDirectory" */
1402 (GNUNET_YES != GNUNET_DISK_directory_test (filename, GNUNET_YES)))
1404 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "rmdir", filename);
1405 return GNUNET_SYSERR;
1407 if (GNUNET_SYSERR ==
1408 GNUNET_DISK_directory_scan (filename, &remove_helper, NULL))
1409 return GNUNET_SYSERR;
1410 if (0 != RMDIR (filename))
1412 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "rmdir", filename);
1413 return GNUNET_SYSERR;
1422 * @param src file to copy
1423 * @param dst destination file name
1424 * @return GNUNET_OK on success, GNUNET_SYSERR on error
1427 GNUNET_DISK_file_copy (const char *src, const char *dst)
1433 struct GNUNET_DISK_FileHandle *in;
1434 struct GNUNET_DISK_FileHandle *out;
1436 if (GNUNET_OK != GNUNET_DISK_file_size (src, &size, GNUNET_YES, GNUNET_YES))
1437 return GNUNET_SYSERR;
1439 in = GNUNET_DISK_file_open (src, GNUNET_DISK_OPEN_READ,
1440 GNUNET_DISK_PERM_NONE);
1442 return GNUNET_SYSERR;
1444 GNUNET_DISK_file_open (dst,
1445 GNUNET_DISK_OPEN_WRITE | GNUNET_DISK_OPEN_CREATE |
1446 GNUNET_DISK_OPEN_FAILIFEXISTS,
1447 GNUNET_DISK_PERM_USER_READ |
1448 GNUNET_DISK_PERM_USER_WRITE |
1449 GNUNET_DISK_PERM_GROUP_READ |
1450 GNUNET_DISK_PERM_GROUP_WRITE);
1453 GNUNET_DISK_file_close (in);
1454 return GNUNET_SYSERR;
1456 buf = GNUNET_malloc (COPY_BLK_SIZE);
1459 len = COPY_BLK_SIZE;
1460 if (len > size - pos)
1462 if (len != GNUNET_DISK_file_read (in, buf, len))
1464 if (len != GNUNET_DISK_file_write (out, buf, len))
1469 GNUNET_DISK_file_close (in);
1470 GNUNET_DISK_file_close (out);
1474 GNUNET_DISK_file_close (in);
1475 GNUNET_DISK_file_close (out);
1476 return GNUNET_SYSERR;
1481 * @brief Removes special characters as ':' from a filename.
1482 * @param fn the filename to canonicalize
1485 GNUNET_DISK_filename_canonicalize (char *fn)
1495 if (c == '/' || c == '\\' || c == ':' || c == '*' || c == '?' || c == '"' ||
1496 c == '<' || c == '>' || c == '|')
1508 * @brief Change owner of a file
1510 * @param filename name of file to change the owner of
1511 * @param user name of the new owner
1512 * @return GNUNET_OK on success, GNUNET_SYSERR on failure
1515 GNUNET_DISK_file_change_owner (const char *filename, const char *user)
1520 pws = getpwnam (user);
1523 LOG (GNUNET_ERROR_TYPE_ERROR,
1524 _("Cannot obtain information about user `%s': %s\n"), user,
1526 return GNUNET_SYSERR;
1528 if (0 != chown (filename, pws->pw_uid, pws->pw_gid))
1529 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "chown", filename);
1536 * Lock a part of a file
1537 * @param fh file handle
1538 * @param lockStart absolute position from where to lock
1539 * @param lockEnd absolute position until where to lock
1540 * @param excl GNUNET_YES for an exclusive lock
1541 * @return GNUNET_OK on success, GNUNET_SYSERR on error
1544 GNUNET_DISK_file_lock (struct GNUNET_DISK_FileHandle *fh, OFF_T lockStart,
1545 OFF_T lockEnd, int excl)
1550 return GNUNET_SYSERR;
1556 memset (&fl, 0, sizeof (struct flock));
1557 fl.l_type = excl ? F_WRLCK : F_RDLCK;
1558 fl.l_whence = SEEK_SET;
1559 fl.l_start = lockStart;
1562 return fcntl (fh->fd, F_SETLK, &fl) != 0 ? GNUNET_SYSERR : GNUNET_OK;
1565 OFF_T diff = lockEnd - lockStart;
1566 DWORD diff_low, diff_high;
1567 diff_low = (DWORD) (diff & 0xFFFFFFFF);
1568 diff_high = (DWORD) ((diff >> (sizeof (DWORD) * 8)) & 0xFFFFFFFF);
1570 memset (&o, 0, sizeof (OVERLAPPED));
1571 o.Offset = (DWORD) (lockStart & 0xFFFFFFFF);;
1572 o.OffsetHigh = (DWORD) (((lockStart & ~0xFFFFFFFF) >> (sizeof (DWORD) * 8)) & 0xFFFFFFFF);
1575 (fh->h, (excl ? LOCKFILE_EXCLUSIVE_LOCK : 0) | LOCKFILE_FAIL_IMMEDIATELY,
1576 0, diff_low, diff_high, &o))
1578 SetErrnoFromWinError (GetLastError ());
1579 return GNUNET_SYSERR;
1588 * Unlock a part of a file
1589 * @param fh file handle
1590 * @param unlockStart absolute position from where to unlock
1591 * @param unlockEnd absolute position until where to unlock
1592 * @return GNUNET_OK on success, GNUNET_SYSERR on error
1595 GNUNET_DISK_file_unlock (struct GNUNET_DISK_FileHandle *fh, OFF_T unlockStart,
1601 return GNUNET_SYSERR;
1607 memset (&fl, 0, sizeof (struct flock));
1608 fl.l_type = F_UNLCK;
1609 fl.l_whence = SEEK_SET;
1610 fl.l_start = unlockStart;
1611 fl.l_len = unlockEnd;
1613 return fcntl (fh->fd, F_SETLK, &fl) != 0 ? GNUNET_SYSERR : GNUNET_OK;
1616 OFF_T diff = unlockEnd - unlockStart;
1617 DWORD diff_low, diff_high;
1618 diff_low = (DWORD) (diff & 0xFFFFFFFF);
1619 diff_high = (DWORD) ((diff >> (sizeof (DWORD) * 8)) & 0xFFFFFFFF);
1621 memset (&o, 0, sizeof (OVERLAPPED));
1622 o.Offset = (DWORD) (unlockStart & 0xFFFFFFFF);;
1623 o.OffsetHigh = (DWORD) (((unlockStart & ~0xFFFFFFFF) >> (sizeof (DWORD) * 8)) & 0xFFFFFFFF);
1625 if (!UnlockFileEx (fh->h, 0, diff_low, diff_high, &o))
1627 SetErrnoFromWinError (GetLastError ());
1628 return GNUNET_SYSERR;
1637 * Open a file. Note that the access permissions will only be
1638 * used if a new file is created and if the underlying operating
1639 * system supports the given permissions.
1641 * @param fn file name to be opened
1642 * @param flags opening flags, a combination of GNUNET_DISK_OPEN_xxx bit flags
1643 * @param perm permissions for the newly created file, use
1644 * GNUNET_DISK_PERM_USER_NONE if a file could not be created by this
1645 * call (because of flags)
1646 * @return IO handle on success, NULL on error
1648 struct GNUNET_DISK_FileHandle *
1649 GNUNET_DISK_file_open (const char *fn, enum GNUNET_DISK_OpenFlags flags,
1650 enum GNUNET_DISK_AccessPermissions perm)
1653 struct GNUNET_DISK_FileHandle *ret;
1659 wchar_t wexpfn[MAX_PATH + 1];
1666 expfn = GNUNET_STRINGS_filename_expand (fn);
1671 if (GNUNET_DISK_OPEN_READWRITE == (flags & GNUNET_DISK_OPEN_READWRITE))
1672 oflags = O_RDWR; /* note: O_RDWR is NOT always O_RDONLY | O_WRONLY */
1673 else if (flags & GNUNET_DISK_OPEN_READ)
1675 else if (flags & GNUNET_DISK_OPEN_WRITE)
1680 GNUNET_free (expfn);
1683 if (flags & GNUNET_DISK_OPEN_FAILIFEXISTS)
1684 oflags |= (O_CREAT | O_EXCL);
1685 if (flags & GNUNET_DISK_OPEN_TRUNCATE)
1687 if (flags & GNUNET_DISK_OPEN_APPEND)
1689 if (flags & GNUNET_DISK_OPEN_CREATE)
1691 (void) GNUNET_DISK_directory_create_for_file (expfn);
1693 mode = translate_unix_perms (perm);
1696 fd = open (expfn, oflags | O_LARGEFILE, mode);
1699 if (0 == (flags & GNUNET_DISK_OPEN_FAILIFEXISTS))
1700 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "open", expfn);
1702 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_DEBUG, "open", expfn);
1703 GNUNET_free (expfn);
1710 if (GNUNET_DISK_OPEN_READWRITE == (flags & GNUNET_DISK_OPEN_READWRITE))
1711 access = FILE_READ_DATA | FILE_WRITE_DATA;
1712 else if (flags & GNUNET_DISK_OPEN_READ)
1713 access = FILE_READ_DATA;
1714 else if (flags & GNUNET_DISK_OPEN_WRITE)
1715 access = FILE_WRITE_DATA;
1717 if (flags & GNUNET_DISK_OPEN_FAILIFEXISTS)
1721 else if (flags & GNUNET_DISK_OPEN_CREATE)
1723 (void) GNUNET_DISK_directory_create_for_file (expfn);
1724 if (flags & GNUNET_DISK_OPEN_TRUNCATE)
1725 disp = CREATE_ALWAYS;
1729 else if (flags & GNUNET_DISK_OPEN_TRUNCATE)
1731 disp = TRUNCATE_EXISTING;
1735 disp = OPEN_EXISTING;
1738 if (ERROR_SUCCESS == plibc_conv_to_win_pathwconv(expfn, wexpfn))
1739 h = CreateFileW (wexpfn, access,
1740 FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE, NULL,
1741 disp, FILE_ATTRIBUTE_NORMAL, NULL);
1743 h = INVALID_HANDLE_VALUE;
1744 if (h == INVALID_HANDLE_VALUE)
1747 SetErrnoFromWinError (GetLastError ());
1749 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_INFO, "open", expfn);
1750 GNUNET_free (expfn);
1755 if (flags & GNUNET_DISK_OPEN_APPEND)
1756 if (SetFilePointer (h, 0, 0, FILE_END) == INVALID_SET_FILE_POINTER)
1758 SetErrnoFromWinError (GetLastError ());
1759 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "SetFilePointer", expfn);
1761 GNUNET_free (expfn);
1766 ret = GNUNET_malloc (sizeof (struct GNUNET_DISK_FileHandle));
1769 ret->type = GNUNET_DISK_HANLDE_TYPE_FILE;
1773 GNUNET_free (expfn);
1779 * Close an open file
1780 * @param h file handle
1781 * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
1784 GNUNET_DISK_file_close (struct GNUNET_DISK_FileHandle *h)
1789 return GNUNET_SYSERR;
1793 if (!CloseHandle (h->h))
1795 SetErrnoFromWinError (GetLastError ());
1796 LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING, "close");
1797 GNUNET_free (h->oOverlapRead);
1798 GNUNET_free (h->oOverlapWrite);
1800 return GNUNET_SYSERR;
1803 if (close (h->fd) != 0)
1805 LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING, "close");
1807 return GNUNET_SYSERR;
1816 * Get a handle from a native FD.
1818 * @param fd native file descriptor
1819 * @return file handle corresponding to the descriptor
1821 struct GNUNET_DISK_FileHandle *
1822 GNUNET_DISK_get_handle_from_native (FILE *fd)
1824 struct GNUNET_DISK_FileHandle *fh;
1835 osfh = _get_osfhandle (fno);
1836 if (INVALID_HANDLE_VALUE == (HANDLE) osfh)
1840 fh = GNUNET_malloc (sizeof (struct GNUNET_DISK_FileHandle));
1843 fh->h = (HANDLE) osfh;
1844 /* Assume it to be a pipe. TODO: use some kind of detection
1845 * function to figure out handle type.
1846 * Note that we can't make it overlapped if it isn't already.
1847 * (ReOpenFile() is only available in 2003/Vista).
1848 * The process that opened this file in the first place (usually a parent
1849 * process, if this is stdin/stdout/stderr) must make it overlapped,
1850 * otherwise we're screwed, as selecting on non-overlapped handle
1853 fh->type = GNUNET_DISK_HANLDE_TYPE_PIPE;
1854 fh->oOverlapRead = GNUNET_malloc (sizeof (OVERLAPPED));
1855 fh->oOverlapWrite = GNUNET_malloc (sizeof (OVERLAPPED));
1856 fh->oOverlapRead->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
1857 fh->oOverlapWrite->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
1867 * Construct full path to a file inside of the private
1868 * directory used by GNUnet. Also creates the corresponding
1869 * directory. If the resulting name is supposed to be
1870 * a directory, end the last argument in '/' (or pass
1871 * DIR_SEPARATOR_STR as the last argument before NULL).
1873 * @param cfg configuration to use (determines HOME)
1874 * @param serviceName name of the service
1875 * @param ... is NULL-terminated list of
1876 * path components to append to the
1877 * private directory name.
1878 * @return the constructed filename
1881 GNUNET_DISK_get_home_filename (const struct GNUNET_CONFIGURATION_Handle *cfg,
1882 const char *serviceName, ...)
1888 unsigned int needed;
1891 GNUNET_CONFIGURATION_get_value_filename (cfg, serviceName, "HOME", &pfx))
1895 LOG (GNUNET_ERROR_TYPE_WARNING,
1896 _("No `%s' specified for service `%s' in configuration.\n"), "HOME",
1900 needed = strlen (pfx) + 2;
1901 if ((pfx[strlen (pfx) - 1] != '/') && (pfx[strlen (pfx) - 1] != '\\'))
1903 va_start (ap, serviceName);
1906 c = va_arg (ap, const char *);
1910 needed += strlen (c);
1911 if ((c[strlen (c) - 1] != '/') && (c[strlen (c) - 1] != '\\'))
1915 ret = GNUNET_malloc (needed);
1918 va_start (ap, serviceName);
1921 c = va_arg (ap, const char *);
1925 if ((c[strlen (c) - 1] != '/') && (c[strlen (c) - 1] != '\\'))
1926 strcat (ret, DIR_SEPARATOR_STR);
1930 if ((ret[strlen (ret) - 1] != '/') && (ret[strlen (ret) - 1] != '\\'))
1931 (void) GNUNET_DISK_directory_create_for_file (ret);
1933 (void) GNUNET_DISK_directory_create (ret);
1939 * Handle for a memory-mapping operation.
1941 struct GNUNET_DISK_MapHandle
1944 * Address where the map is in memory.
1950 * Underlying OS handle.
1955 * Number of bytes mapped.
1963 #define MAP_FAILED ((void *) -1)
1967 * Map a file into memory
1969 * @param h open file handle
1970 * @param m handle to the new mapping
1971 * @param access access specification, GNUNET_DISK_MAP_TYPE_xxx
1972 * @param len size of the mapping
1973 * @return pointer to the mapped memory region, NULL on failure
1976 GNUNET_DISK_file_map (const struct GNUNET_DISK_FileHandle *h,
1977 struct GNUNET_DISK_MapHandle **m,
1978 enum GNUNET_DISK_MapType access, size_t len)
1987 DWORD mapAccess, protect;
1989 if ((access & GNUNET_DISK_MAP_TYPE_READ) &&
1990 (access & GNUNET_DISK_MAP_TYPE_WRITE))
1992 protect = PAGE_READWRITE;
1993 mapAccess = FILE_MAP_ALL_ACCESS;
1995 else if (access & GNUNET_DISK_MAP_TYPE_READ)
1997 protect = PAGE_READONLY;
1998 mapAccess = FILE_MAP_READ;
2000 else if (access & GNUNET_DISK_MAP_TYPE_WRITE)
2002 protect = PAGE_READWRITE;
2003 mapAccess = FILE_MAP_WRITE;
2011 *m = GNUNET_malloc (sizeof (struct GNUNET_DISK_MapHandle));
2012 (*m)->h = CreateFileMapping (h->h, NULL, protect, 0, 0, NULL);
2013 if ((*m)->h == INVALID_HANDLE_VALUE)
2015 SetErrnoFromWinError (GetLastError ());
2020 (*m)->addr = MapViewOfFile ((*m)->h, mapAccess, 0, 0, len);
2023 SetErrnoFromWinError (GetLastError ());
2024 CloseHandle ((*m)->h);
2033 if (access & GNUNET_DISK_MAP_TYPE_READ)
2035 if (access & GNUNET_DISK_MAP_TYPE_WRITE)
2037 *m = GNUNET_malloc (sizeof (struct GNUNET_DISK_MapHandle));
2038 (*m)->addr = mmap (NULL, len, prot, MAP_SHARED, h->fd, 0);
2039 GNUNET_assert (NULL != (*m)->addr);
2040 if (MAP_FAILED == (*m)->addr)
2052 * @param h mapping handle
2053 * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
2056 GNUNET_DISK_file_unmap (struct GNUNET_DISK_MapHandle *h)
2063 return GNUNET_SYSERR;
2067 ret = UnmapViewOfFile (h->addr) ? GNUNET_OK : GNUNET_SYSERR;
2068 if (ret != GNUNET_OK)
2069 SetErrnoFromWinError (GetLastError ());
2070 if (!CloseHandle (h->h) && (ret == GNUNET_OK))
2072 ret = GNUNET_SYSERR;
2073 SetErrnoFromWinError (GetLastError ());
2076 ret = munmap (h->addr, h->len) != -1 ? GNUNET_OK : GNUNET_SYSERR;
2084 * Write file changes to disk
2085 * @param h handle to an open file
2086 * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
2089 GNUNET_DISK_file_sync (const struct GNUNET_DISK_FileHandle *h)
2094 return GNUNET_SYSERR;
2100 ret = FlushFileBuffers (h->h) ? GNUNET_OK : GNUNET_SYSERR;
2101 if (ret != GNUNET_OK)
2102 SetErrnoFromWinError (GetLastError ());
2104 #elif defined(FREEBSD) || defined(OPENBSD) || defined(DARWIN)
2105 return fsync (h->fd) == -1 ? GNUNET_SYSERR : GNUNET_OK;
2107 return fdatasync (h->fd) == -1 ? GNUNET_SYSERR : GNUNET_OK;
2113 /* Copyright Bob Byrnes <byrnes <at> curl.com>
2114 http://permalink.gmane.org/gmane.os.cygwin.patches/2121
2116 /* Create a pipe, and return handles to the read and write ends,
2117 just like CreatePipe, but ensure that the write end permits
2118 FILE_READ_ATTRIBUTES access, on later versions of win32 where
2119 this is supported. This access is needed by NtQueryInformationFile,
2120 which is used to implement select and nonblocking writes.
2121 Note that the return value is either NO_ERROR or GetLastError,
2122 unlike CreatePipe, which returns a bool for success or failure. */
2124 create_selectable_pipe (PHANDLE read_pipe_ptr, PHANDLE write_pipe_ptr,
2125 LPSECURITY_ATTRIBUTES sa_ptr, DWORD psize,
2126 DWORD dwReadMode, DWORD dwWriteMode)
2128 /* Default to error. */
2129 *read_pipe_ptr = *write_pipe_ptr = INVALID_HANDLE_VALUE;
2131 HANDLE read_pipe = INVALID_HANDLE_VALUE, write_pipe = INVALID_HANDLE_VALUE;
2133 /* Ensure that there is enough pipe buffer space for atomic writes. */
2134 if (psize < PIPE_BUF)
2137 char pipename[MAX_PATH];
2139 /* Retry CreateNamedPipe as long as the pipe name is in use.
2140 * Retrying will probably never be necessary, but we want
2141 * to be as robust as possible. */
2144 static volatile LONG pipe_unique_id;
2146 snprintf (pipename, sizeof pipename, "\\\\.\\pipe\\gnunet-%d-%ld",
2147 getpid (), InterlockedIncrement ((LONG *) & pipe_unique_id));
2148 LOG (GNUNET_ERROR_TYPE_DEBUG, "CreateNamedPipe: name = %s, size = %lu\n",
2150 /* Use CreateNamedPipe instead of CreatePipe, because the latter
2151 * returns a write handle that does not permit FILE_READ_ATTRIBUTES
2152 * access, on versions of win32 earlier than WinXP SP2.
2153 * CreatePipe also stupidly creates a full duplex pipe, which is
2154 * a waste, since only a single direction is actually used.
2155 * It's important to only allow a single instance, to ensure that
2156 * the pipe was not created earlier by some other process, even if
2157 * the pid has been reused. We avoid FILE_FLAG_FIRST_PIPE_INSTANCE
2158 * because that is only available for Win2k SP2 and WinXP. */
2159 read_pipe = CreateNamedPipeA (pipename, PIPE_ACCESS_INBOUND | dwReadMode, PIPE_TYPE_BYTE | PIPE_READMODE_BYTE, 1, /* max instances */
2160 psize, /* output buffer size */
2161 psize, /* input buffer size */
2162 NMPWAIT_USE_DEFAULT_WAIT, sa_ptr);
2164 if (read_pipe != INVALID_HANDLE_VALUE)
2166 LOG (GNUNET_ERROR_TYPE_DEBUG, "pipe read handle = %p\n", read_pipe);
2170 DWORD err = GetLastError ();
2174 case ERROR_PIPE_BUSY:
2175 /* The pipe is already open with compatible parameters.
2176 * Pick a new name and retry. */
2177 LOG (GNUNET_ERROR_TYPE_DEBUG, "pipe busy, retrying\n");
2179 case ERROR_ACCESS_DENIED:
2180 /* The pipe is already open with incompatible parameters.
2181 * Pick a new name and retry. */
2182 LOG (GNUNET_ERROR_TYPE_DEBUG, "pipe access denied, retrying\n");
2184 case ERROR_CALL_NOT_IMPLEMENTED:
2185 /* We are on an older Win9x platform without named pipes.
2186 * Return an anonymous pipe as the best approximation. */
2187 LOG (GNUNET_ERROR_TYPE_DEBUG,
2188 "CreateNamedPipe not implemented, resorting to "
2189 "CreatePipe: size = %lu\n", psize);
2190 if (CreatePipe (read_pipe_ptr, write_pipe_ptr, sa_ptr, psize))
2192 LOG (GNUNET_ERROR_TYPE_DEBUG, "pipe read handle = %p, write handle = %p\n",
2197 err = GetLastError ();
2198 LOG (GNUNET_ERROR_TYPE_ERROR, "CreatePipe failed: %d\n", err);
2201 LOG (GNUNET_ERROR_TYPE_ERROR, "CreateNamedPipe failed: %d\n", err);
2206 LOG (GNUNET_ERROR_TYPE_DEBUG, "CreateFile: name = %s\n", pipename);
2208 /* Open the named pipe for writing.
2209 * Be sure to permit FILE_READ_ATTRIBUTES access. */
2210 write_pipe = CreateFileA (pipename, GENERIC_WRITE | FILE_READ_ATTRIBUTES, 0, /* share mode */
2211 sa_ptr, OPEN_EXISTING, dwWriteMode, /* flags and attributes */
2212 0); /* handle to template file */
2214 if (write_pipe == INVALID_HANDLE_VALUE)
2217 DWORD err = GetLastError ();
2219 LOG (GNUNET_ERROR_TYPE_DEBUG, "CreateFile failed: %d\n", err);
2220 CloseHandle (read_pipe);
2223 LOG (GNUNET_ERROR_TYPE_DEBUG, "pipe write handle = %p\n", write_pipe);
2225 *read_pipe_ptr = read_pipe;
2226 *write_pipe_ptr = write_pipe;
2233 * Creates an interprocess channel
2235 * @param blocking_read creates an asynchronous pipe for reading if set to GNUNET_NO
2236 * @param blocking_write creates an asynchronous pipe for writing if set to GNUNET_NO
2237 * @param inherit_read inherit the parent processes stdin (only for windows)
2238 * @param inherit_write inherit the parent processes stdout (only for windows)
2239 * @return handle to the new pipe, NULL on error
2241 struct GNUNET_DISK_PipeHandle *
2242 GNUNET_DISK_pipe (int blocking_read, int blocking_write, int inherit_read, int inherit_write)
2253 LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR, "pipe");
2257 return GNUNET_DISK_pipe_from_fd (blocking_read,
2261 struct GNUNET_DISK_PipeHandle *p;
2262 struct GNUNET_DISK_FileHandle *fds;
2267 p = GNUNET_malloc (sizeof (struct GNUNET_DISK_PipeHandle) +
2268 2 * sizeof (struct GNUNET_DISK_FileHandle));
2269 fds = (struct GNUNET_DISK_FileHandle *) &p[1];
2273 /* All pipes are overlapped. If you want them to block - just
2274 * call WriteFile() and ReadFile() with NULL overlapped pointer.
2277 create_selectable_pipe (&p->fd[0]->h, &p->fd[1]->h, NULL, 0,
2278 FILE_FLAG_OVERLAPPED,
2279 FILE_FLAG_OVERLAPPED);
2283 SetErrnoFromWinError (GetLastError ());
2286 if (!DuplicateHandle
2287 (GetCurrentProcess (), p->fd[0]->h, GetCurrentProcess (), &tmp_handle, 0,
2288 inherit_read == GNUNET_YES ? TRUE : FALSE, DUPLICATE_SAME_ACCESS))
2290 SetErrnoFromWinError (GetLastError ());
2291 CloseHandle (p->fd[0]->h);
2292 CloseHandle (p->fd[1]->h);
2296 CloseHandle (p->fd[0]->h);
2297 p->fd[0]->h = tmp_handle;
2299 if (!DuplicateHandle
2300 (GetCurrentProcess (), p->fd[1]->h, GetCurrentProcess (), &tmp_handle, 0,
2301 inherit_write == GNUNET_YES ? TRUE : FALSE, DUPLICATE_SAME_ACCESS))
2303 SetErrnoFromWinError (GetLastError ());
2304 CloseHandle (p->fd[0]->h);
2305 CloseHandle (p->fd[1]->h);
2309 CloseHandle (p->fd[1]->h);
2310 p->fd[1]->h = tmp_handle;
2312 p->fd[0]->type = GNUNET_DISK_HANLDE_TYPE_PIPE;
2313 p->fd[1]->type = GNUNET_DISK_HANLDE_TYPE_PIPE;
2315 p->fd[0]->oOverlapRead = GNUNET_malloc (sizeof (OVERLAPPED));
2316 p->fd[0]->oOverlapWrite = GNUNET_malloc (sizeof (OVERLAPPED));
2317 p->fd[1]->oOverlapRead = GNUNET_malloc (sizeof (OVERLAPPED));
2318 p->fd[1]->oOverlapWrite = GNUNET_malloc (sizeof (OVERLAPPED));
2320 p->fd[0]->oOverlapRead->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2321 p->fd[0]->oOverlapWrite->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2323 p->fd[1]->oOverlapRead->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2324 p->fd[1]->oOverlapWrite->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2332 * Creates a pipe object from a couple of file descriptors.
2333 * Useful for wrapping existing pipe FDs.
2335 * @param blocking_read creates an asynchronous pipe for reading if set to GNUNET_NO
2336 * @param blocking_write creates an asynchronous pipe for writing if set to GNUNET_NO
2337 * @param fd an array of two fd values. One of them may be -1 for read-only or write-only pipes
2339 * @return handle to the new pipe, NULL on error
2341 struct GNUNET_DISK_PipeHandle *
2342 GNUNET_DISK_pipe_from_fd (int blocking_read, int blocking_write, int fd[2])
2344 struct GNUNET_DISK_PipeHandle *p;
2345 struct GNUNET_DISK_FileHandle *fds;
2347 p = GNUNET_malloc (sizeof (struct GNUNET_DISK_PipeHandle) +
2348 2 * sizeof (struct GNUNET_DISK_FileHandle));
2349 fds = (struct GNUNET_DISK_FileHandle *) &p[1];
2355 int eno = 0; /* make gcc happy */
2357 p->fd[0]->fd = fd[0];
2358 p->fd[1]->fd = fd[1];
2364 flags = fcntl (fd[0], F_GETFL);
2365 flags |= O_NONBLOCK;
2366 if (0 > fcntl (fd[0], F_SETFL, flags))
2372 flags = fcntl (fd[0], F_GETFD);
2373 flags |= FD_CLOEXEC;
2374 if (0 > fcntl (fd[0], F_SETFD, flags))
2383 if (!blocking_write)
2385 flags = fcntl (fd[1], F_GETFL);
2386 flags |= O_NONBLOCK;
2387 if (0 > fcntl (fd[1], F_SETFL, flags))
2393 flags = fcntl (fd[1], F_GETFD);
2394 flags |= FD_CLOEXEC;
2395 if (0 > fcntl (fd[1], F_SETFD, flags))
2404 LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR, "fcntl");
2405 if (p->fd[0]->fd >= 0)
2406 GNUNET_break (0 == close (p->fd[0]->fd));
2407 if (p->fd[1]->fd >= 0)
2408 GNUNET_break (0 == close (p->fd[1]->fd));
2415 p->fd[0]->h = (HANDLE) _get_osfhandle (fd[0]);
2417 p->fd[0]->h = INVALID_HANDLE_VALUE;
2419 p->fd[1]->h = (HANDLE) _get_osfhandle (fd[1]);
2421 p->fd[1]->h = INVALID_HANDLE_VALUE;
2423 if (p->fd[0]->h != INVALID_HANDLE_VALUE)
2425 p->fd[0]->type = GNUNET_DISK_HANLDE_TYPE_PIPE;
2426 p->fd[0]->oOverlapRead = GNUNET_malloc (sizeof (OVERLAPPED));
2427 p->fd[0]->oOverlapWrite = GNUNET_malloc (sizeof (OVERLAPPED));
2428 p->fd[0]->oOverlapRead->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2429 p->fd[0]->oOverlapWrite->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2432 if (p->fd[1]->h != INVALID_HANDLE_VALUE)
2434 p->fd[1]->type = GNUNET_DISK_HANLDE_TYPE_PIPE;
2435 p->fd[1]->oOverlapRead = GNUNET_malloc (sizeof (OVERLAPPED));
2436 p->fd[1]->oOverlapWrite = GNUNET_malloc (sizeof (OVERLAPPED));
2437 p->fd[1]->oOverlapRead->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2438 p->fd[1]->oOverlapWrite->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2446 * Closes an interprocess channel
2448 * @param p pipe to close
2449 * @param end which end of the pipe to close
2450 * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
2453 GNUNET_DISK_pipe_close_end (struct GNUNET_DISK_PipeHandle *p,
2454 enum GNUNET_DISK_PipeEnd end)
2456 int ret = GNUNET_OK;
2460 if (end == GNUNET_DISK_PIPE_END_READ)
2462 if (p->fd[0]->h != INVALID_HANDLE_VALUE)
2464 if (!CloseHandle (p->fd[0]->h))
2466 SetErrnoFromWinError (GetLastError ());
2467 ret = GNUNET_SYSERR;
2469 GNUNET_free (p->fd[0]->oOverlapRead);
2470 GNUNET_free (p->fd[0]->oOverlapWrite);
2471 p->fd[0]->h = INVALID_HANDLE_VALUE;
2474 else if (end == GNUNET_DISK_PIPE_END_WRITE)
2476 if (p->fd[0]->h != INVALID_HANDLE_VALUE)
2478 if (!CloseHandle (p->fd[1]->h))
2480 SetErrnoFromWinError (GetLastError ());
2481 ret = GNUNET_SYSERR;
2483 GNUNET_free (p->fd[1]->oOverlapRead);
2484 GNUNET_free (p->fd[1]->oOverlapWrite);
2485 p->fd[1]->h = INVALID_HANDLE_VALUE;
2491 if (end == GNUNET_DISK_PIPE_END_READ)
2493 if (0 != close (p->fd[0]->fd))
2495 ret = GNUNET_SYSERR;
2500 else if (end == GNUNET_DISK_PIPE_END_WRITE)
2502 if (0 != close (p->fd[1]->fd))
2504 ret = GNUNET_SYSERR;
2516 * Closes an interprocess channel
2518 * @param p pipe to close
2519 * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
2522 GNUNET_DISK_pipe_close (struct GNUNET_DISK_PipeHandle *p)
2524 int ret = GNUNET_OK;
2528 if (p->fd[0]->h != INVALID_HANDLE_VALUE)
2530 if (!CloseHandle (p->fd[0]->h))
2532 SetErrnoFromWinError (GetLastError ());
2533 ret = GNUNET_SYSERR;
2535 GNUNET_free (p->fd[0]->oOverlapRead);
2536 GNUNET_free (p->fd[0]->oOverlapWrite);
2538 if (p->fd[1]->h != INVALID_HANDLE_VALUE)
2540 if (!CloseHandle (p->fd[1]->h))
2542 SetErrnoFromWinError (GetLastError ());
2543 ret = GNUNET_SYSERR;
2545 GNUNET_free (p->fd[1]->oOverlapRead);
2546 GNUNET_free (p->fd[1]->oOverlapWrite);
2551 if (p->fd[0]->fd != -1)
2553 if (0 != close (p->fd[0]->fd))
2555 ret = GNUNET_SYSERR;
2560 if (p->fd[1]->fd != -1)
2562 if (0 != close (p->fd[1]->fd))
2564 ret = GNUNET_SYSERR;
2576 * Get the handle to a particular pipe end
2579 * @param n end to access
2580 * @return handle for the respective end
2582 const struct GNUNET_DISK_FileHandle *
2583 GNUNET_DISK_pipe_handle (const struct GNUNET_DISK_PipeHandle *p,
2584 enum GNUNET_DISK_PipeEnd n)
2588 case GNUNET_DISK_PIPE_END_READ:
2589 case GNUNET_DISK_PIPE_END_WRITE:
2599 * Retrieve OS file handle
2601 * @param fh GNUnet file descriptor
2602 * @param dst destination buffer
2603 * @param dst_len length of dst
2604 * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
2607 GNUNET_DISK_internal_file_handle_ (const struct GNUNET_DISK_FileHandle *fh,
2608 void *dst, size_t dst_len)
2611 if (dst_len < sizeof (HANDLE))
2612 return GNUNET_SYSERR;
2613 *((HANDLE *) dst) = fh->h;
2615 if (dst_len < sizeof (int))
2616 return GNUNET_SYSERR;
2617 *((int *) dst) = fh->fd;