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 * Move a file out of the way (create a backup) by
499 * renaming it to "orig.NUM~" where NUM is the smallest
500 * number that is not used yet.
502 * @param fil name of the file to back up
505 GNUNET_DISK_file_backup (const char *fil)
511 slen = strlen (fil) + 20;
512 target = GNUNET_malloc (slen);
516 GNUNET_snprintf (target, slen,
520 } while (0 == access (target, F_OK));
521 if (0 != rename (fil, target))
522 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR,
525 GNUNET_free (target);
530 * Create an (empty) temporary file on disk. If the given name is not
531 * an absolute path, the current 'TMPDIR' will be prepended. In any case,
532 * 6 random characters will be appended to the name to create a unique
535 * @param t component to use for the name;
536 * does NOT contain "XXXXXX" or "/tmp/".
537 * @return NULL on error, otherwise name of fresh
538 * file on disk in directory for temporary files
541 GNUNET_DISK_mktemp (const char *t)
546 fn = mktemp_name (t);
547 if (-1 == (fd = mkstemp (fn)))
549 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_ERROR, "mkstemp", fn);
554 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "close", fn);
560 * Get the number of blocks that are left on the partition that
561 * contains the given file (for normal users).
563 * @param part a file on the partition to check
564 * @return -1 on errors, otherwise the number of free blocks
567 GNUNET_DISK_get_blocks_available (const char *part)
572 if (0 != statvfs (part, &buf))
574 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "statfs", part);
582 wchar_t wpath[MAX_PATH + 1];
585 path = GNUNET_STRINGS_filename_expand (part);
588 /* "part" was in UTF-8, and so is "path" */
589 if (ERROR_SUCCESS != plibc_conv_to_win_pathwconv(path, wpath))
595 wcsncpy (szDrive, wpath, 3);
597 if (!GetDiskFreeSpaceW (szDrive, &dwDummy, &dwDummy, &dwBlocks, &dwDummy))
599 LOG (GNUNET_ERROR_TYPE_WARNING, _("`%s' failed for drive `%S': %u\n"),
600 "GetDiskFreeSpace", szDrive, GetLastError ());
608 if (0 != statfs (part, &s))
610 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "statfs", part);
619 * Test if "fil" is a directory and listable. Optionally, also check if the
620 * directory is readable. Will not print an error message if the directory does
621 * not exist. Will log errors if GNUNET_SYSERR is returned (i.e., a file exists
622 * with the same name).
624 * @param fil filename to test
625 * @param is_readable GNUNET_YES to additionally check if "fil" is readable;
626 * GNUNET_NO to disable this check
627 * @return GNUNET_YES if yes, GNUNET_NO if not; GNUNET_SYSERR if it
628 * does not exist or stat'ed
631 GNUNET_DISK_directory_test (const char *fil, int is_readable)
633 struct stat filestat;
636 ret = STAT (fil, &filestat);
640 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "stat", fil);
641 return GNUNET_SYSERR;
643 if (!S_ISDIR (filestat.st_mode))
645 LOG (GNUNET_ERROR_TYPE_WARNING,
646 "A file already exits with the same name %s\n", fil);
649 if (GNUNET_YES == is_readable)
650 ret = ACCESS (fil, R_OK | X_OK);
652 ret = ACCESS (fil, X_OK);
655 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "access", fil);
663 * Check that fil corresponds to a filename
664 * (of a file that exists and that is not a directory).
666 * @param fil filename to check
667 * @return GNUNET_YES if yes, GNUNET_NO if not a file, GNUNET_SYSERR if something
668 * else (will print an error message in that case, too).
671 GNUNET_DISK_file_test (const char *fil)
673 struct stat filestat;
677 rdir = GNUNET_STRINGS_filename_expand (fil);
679 return GNUNET_SYSERR;
681 ret = STAT (rdir, &filestat);
686 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "stat", rdir);
688 return GNUNET_SYSERR;
693 if (!S_ISREG (filestat.st_mode))
698 if (ACCESS (rdir, F_OK) < 0)
700 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "access", rdir);
702 return GNUNET_SYSERR;
710 * Implementation of "mkdir -p"
711 * @param dir the directory to create
712 * @returns GNUNET_OK on success, GNUNET_SYSERR on failure
715 GNUNET_DISK_directory_create (const char *dir)
723 rdir = GNUNET_STRINGS_filename_expand (dir);
725 return GNUNET_SYSERR;
729 pos = 1; /* skip heading '/' */
731 /* Local or Network path? */
732 if (strncmp (rdir, "\\\\", 2) == 0)
737 if (rdir[pos] == '\\')
747 pos = 3; /* strlen("C:\\") */
750 /* Check which low level directories already exist */
752 rdir[len] = DIR_SEPARATOR;
755 if (DIR_SEPARATOR == rdir[pos2])
758 ret = GNUNET_DISK_directory_test (rdir, GNUNET_NO);
759 if (GNUNET_NO == ret)
762 return GNUNET_SYSERR;
764 rdir[pos2] = DIR_SEPARATOR;
765 if (GNUNET_YES == ret)
776 /* Start creating directories */
779 if ((rdir[pos] == DIR_SEPARATOR) || (pos == len))
782 ret = GNUNET_DISK_directory_test (rdir, GNUNET_NO);
783 if (GNUNET_NO == ret)
786 return GNUNET_SYSERR;
788 if (GNUNET_SYSERR == ret)
791 ret = mkdir (rdir, S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH); /* 755 */
793 wchar_t wrdir[MAX_PATH + 1];
794 if (ERROR_SUCCESS == plibc_conv_to_win_pathwconv(rdir, wrdir))
795 ret = !CreateDirectoryW (wrdir, NULL);
799 if ((ret != 0) && (errno != EEXIST))
801 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_ERROR, "mkdir", rdir);
803 return GNUNET_SYSERR;
806 rdir[pos] = DIR_SEPARATOR;
816 * Create the directory structure for storing
819 * @param filename name of a file in the directory
820 * @returns GNUNET_OK on success,
821 * GNUNET_SYSERR on failure,
822 * GNUNET_NO if the directory
823 * exists but is not writeable for us
826 GNUNET_DISK_directory_create_for_file (const char *filename)
832 rdir = GNUNET_STRINGS_filename_expand (filename);
834 return GNUNET_SYSERR;
836 while ((len > 0) && (rdir[len] != DIR_SEPARATOR))
839 ret = GNUNET_DISK_directory_create (rdir);
840 if ((ret == GNUNET_OK) && (0 != ACCESS (rdir, W_OK)))
848 * Read the contents of a binary file into a buffer.
849 * @param h handle to an open file
850 * @param result the buffer to write the result to
851 * @param len the maximum number of bytes to read
852 * @return the number of bytes read on success, GNUNET_SYSERR on failure
855 GNUNET_DISK_file_read (const struct GNUNET_DISK_FileHandle * h, void *result,
861 return GNUNET_SYSERR;
867 if (h->type != GNUNET_DISK_HANLDE_TYPE_PIPE)
869 if (!ReadFile (h->h, result, len, &bytesRead, NULL))
871 SetErrnoFromWinError (GetLastError ());
872 return GNUNET_SYSERR;
877 if (!ReadFile (h->h, result, len, &bytesRead, h->oOverlapRead))
879 if (GetLastError () != ERROR_IO_PENDING)
881 LOG (GNUNET_ERROR_TYPE_DEBUG, "Error reading from pipe: %u\n", GetLastError ());
882 SetErrnoFromWinError (GetLastError ());
883 return GNUNET_SYSERR;
885 LOG (GNUNET_ERROR_TYPE_DEBUG, "Will get overlapped result\n");
886 GetOverlappedResult (h->h, h->oOverlapRead, &bytesRead, TRUE);
888 LOG (GNUNET_ERROR_TYPE_DEBUG, "Read %u bytes from pipe\n", bytesRead);
892 return read (h->fd, result, len);
898 * Read the contents of a binary file into a buffer.
899 * Guarantees not to block (returns GNUNET_SYSERR and sets errno to EAGAIN
900 * when no data can be read).
902 * @param h handle to an open file
903 * @param result the buffer to write the result to
904 * @param len the maximum number of bytes to read
905 * @return the number of bytes read on success, GNUNET_SYSERR on failure
908 GNUNET_DISK_file_read_non_blocking (const struct GNUNET_DISK_FileHandle * h,
915 return GNUNET_SYSERR;
921 if (h->type != GNUNET_DISK_HANLDE_TYPE_PIPE)
923 if (!ReadFile (h->h, result, len, &bytesRead, NULL))
925 SetErrnoFromWinError (GetLastError ());
926 return GNUNET_SYSERR;
931 if (!ReadFile (h->h, result, len, &bytesRead, h->oOverlapRead))
933 if (GetLastError () != ERROR_IO_PENDING)
935 LOG (GNUNET_ERROR_TYPE_DEBUG, "Error reading from pipe: %u\n", GetLastError ());
936 SetErrnoFromWinError (GetLastError ());
937 return GNUNET_SYSERR;
941 LOG (GNUNET_ERROR_TYPE_DEBUG,
942 "ReadFile() queued a read, cancelling\n");
945 return GNUNET_SYSERR;
948 LOG (GNUNET_ERROR_TYPE_DEBUG, "Read %u bytes\n", bytesRead);
955 /* set to non-blocking, read, then set back */
956 flags = fcntl (h->fd, F_GETFL);
957 if (0 == (flags & O_NONBLOCK))
958 (void) fcntl (h->fd, F_SETFL, flags | O_NONBLOCK);
959 ret = read (h->fd, result, len);
960 if (0 == (flags & O_NONBLOCK))
963 (void) fcntl (h->fd, F_SETFL, flags);
972 * Read the contents of a binary file into a buffer.
974 * @param fn file name
975 * @param result the buffer to write the result to
976 * @param len the maximum number of bytes to read
977 * @return number of bytes read, GNUNET_SYSERR on failure
980 GNUNET_DISK_fn_read (const char *fn, void *result, size_t len)
982 struct GNUNET_DISK_FileHandle *fh;
985 fh = GNUNET_DISK_file_open (fn, GNUNET_DISK_OPEN_READ, GNUNET_DISK_PERM_NONE);
987 return GNUNET_SYSERR;
988 ret = GNUNET_DISK_file_read (fh, result, len);
989 GNUNET_assert (GNUNET_OK == GNUNET_DISK_file_close (fh));
996 * Write a buffer to a file.
997 * @param h handle to open file
998 * @param buffer the data to write
999 * @param n number of bytes to write
1000 * @return number of bytes written on success, GNUNET_SYSERR on error
1003 GNUNET_DISK_file_write (const struct GNUNET_DISK_FileHandle * h,
1004 const void *buffer, size_t n)
1009 return GNUNET_SYSERR;
1015 if (h->type != GNUNET_DISK_HANLDE_TYPE_PIPE)
1017 if (!WriteFile (h->h, buffer, n, &bytesWritten, NULL))
1019 SetErrnoFromWinError (GetLastError ());
1020 return GNUNET_SYSERR;
1025 LOG (GNUNET_ERROR_TYPE_DEBUG, "It is a pipe trying to write %u bytes\n", n);
1026 if (!WriteFile (h->h, buffer, n, &bytesWritten, h->oOverlapWrite))
1028 if (GetLastError () != ERROR_IO_PENDING)
1030 SetErrnoFromWinError (GetLastError ());
1031 LOG (GNUNET_ERROR_TYPE_DEBUG, "Error writing to pipe: %u\n",
1033 return GNUNET_SYSERR;
1035 LOG (GNUNET_ERROR_TYPE_DEBUG, "Will get overlapped result\n");
1036 if (!GetOverlappedResult (h->h, h->oOverlapWrite, &bytesWritten, TRUE))
1038 SetErrnoFromWinError (GetLastError ());
1039 LOG (GNUNET_ERROR_TYPE_DEBUG,
1040 "Error getting overlapped result while writing to pipe: %u\n",
1042 return GNUNET_SYSERR;
1048 if (!GetOverlappedResult (h->h, h->oOverlapWrite, &ovr, TRUE))
1050 LOG (GNUNET_ERROR_TYPE_DEBUG,
1051 "Error getting control overlapped result while writing to pipe: %u\n",
1056 LOG (GNUNET_ERROR_TYPE_DEBUG,
1057 "Wrote %u bytes (ovr says %u), picking the greatest\n",
1061 if (bytesWritten == 0)
1065 LOG (GNUNET_ERROR_TYPE_DEBUG, "Wrote %u bytes, returning -1 with EAGAIN\n", bytesWritten);
1067 return GNUNET_SYSERR;
1070 LOG (GNUNET_ERROR_TYPE_DEBUG, "Wrote %u bytes\n", bytesWritten);
1072 return bytesWritten;
1074 return write (h->fd, buffer, n);
1080 * Write a buffer to a file, blocking, if necessary.
1081 * @param h handle to open file
1082 * @param buffer the data to write
1083 * @param n number of bytes to write
1084 * @return number of bytes written on success, GNUNET_SYSERR on error
1087 GNUNET_DISK_file_write_blocking (const struct GNUNET_DISK_FileHandle * h,
1088 const void *buffer, size_t n)
1093 return GNUNET_SYSERR;
1098 /* We do a non-overlapped write, which is as blocking as it gets */
1099 LOG (GNUNET_ERROR_TYPE_DEBUG, "Writing %u bytes\n", n);
1100 if (!WriteFile (h->h, buffer, n, &bytesWritten, NULL))
1102 SetErrnoFromWinError (GetLastError ());
1103 LOG (GNUNET_ERROR_TYPE_DEBUG, "Error writing to pipe: %u\n",
1105 return GNUNET_SYSERR;
1107 if (bytesWritten == 0 && n > 0)
1109 LOG (GNUNET_ERROR_TYPE_DEBUG, "Waiting for pipe to clean\n");
1110 WaitForSingleObject (h->h, INFINITE);
1111 if (!WriteFile (h->h, buffer, n, &bytesWritten, NULL))
1113 SetErrnoFromWinError (GetLastError ());
1114 LOG (GNUNET_ERROR_TYPE_DEBUG, "Error writing to pipe: %u\n",
1116 return GNUNET_SYSERR;
1119 LOG (GNUNET_ERROR_TYPE_DEBUG, "Wrote %u bytes\n", bytesWritten);
1120 return bytesWritten;
1125 /* set to blocking, write, then set back */
1126 flags = fcntl (h->fd, F_GETFL);
1127 if (0 != (flags & O_NONBLOCK))
1128 (void) fcntl (h->fd, F_SETFL, flags - O_NONBLOCK);
1129 ret = write (h->fd, buffer, n);
1130 if (0 == (flags & O_NONBLOCK))
1131 (void) fcntl (h->fd, F_SETFL, flags);
1138 * Write a buffer to a file. If the file is longer than the
1139 * number of bytes that will be written, it will be truncated.
1141 * @param fn file name
1142 * @param buffer the data to write
1143 * @param n number of bytes to write
1144 * @param mode file permissions
1145 * @return number of bytes written on success, GNUNET_SYSERR on error
1148 GNUNET_DISK_fn_write (const char *fn, const void *buffer, size_t n,
1149 enum GNUNET_DISK_AccessPermissions mode)
1151 struct GNUNET_DISK_FileHandle *fh;
1154 fh = GNUNET_DISK_file_open (fn,
1155 GNUNET_DISK_OPEN_WRITE | GNUNET_DISK_OPEN_TRUNCATE
1156 | GNUNET_DISK_OPEN_CREATE, mode);
1158 return GNUNET_SYSERR;
1159 ret = GNUNET_DISK_file_write (fh, buffer, n);
1160 GNUNET_assert (GNUNET_OK == GNUNET_DISK_file_close (fh));
1166 * Scan a directory for files.
1168 * @param dirName the name of the directory
1169 * @param callback the method to call for each file,
1170 * can be NULL, in that case, we only count
1171 * @param callback_cls closure for callback
1172 * @return the number of files found, GNUNET_SYSERR on error or
1173 * ieration aborted by callback returning GNUNET_SYSERR
1176 GNUNET_DISK_directory_scan (const char *dirName,
1177 GNUNET_FileNameCallback callback,
1181 struct dirent *finfo;
1186 unsigned int name_len;
1187 unsigned int n_size;
1189 GNUNET_assert (dirName != NULL);
1190 dname = GNUNET_STRINGS_filename_expand (dirName);
1192 return GNUNET_SYSERR;
1193 while ((strlen (dname) > 0) && (dname[strlen (dname) - 1] == DIR_SEPARATOR))
1194 dname[strlen (dname) - 1] = '\0';
1195 if (0 != STAT (dname, &istat))
1197 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "stat", dname);
1198 GNUNET_free (dname);
1199 return GNUNET_SYSERR;
1201 if (!S_ISDIR (istat.st_mode))
1203 LOG (GNUNET_ERROR_TYPE_WARNING, _("Expected `%s' to be a directory!\n"),
1205 GNUNET_free (dname);
1206 return GNUNET_SYSERR;
1209 dinfo = OPENDIR (dname);
1210 if ((errno == EACCES) || (dinfo == NULL))
1212 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "opendir", dname);
1215 GNUNET_free (dname);
1216 return GNUNET_SYSERR;
1219 n_size = strlen (dname) + name_len + 2;
1220 name = GNUNET_malloc (n_size);
1221 while ((finfo = READDIR (dinfo)) != NULL)
1223 if ((0 == strcmp (finfo->d_name, ".")) ||
1224 (0 == strcmp (finfo->d_name, "..")))
1226 if (callback != NULL)
1228 if (name_len < strlen (finfo->d_name))
1231 name_len = strlen (finfo->d_name);
1232 n_size = strlen (dname) + name_len + 2;
1233 name = GNUNET_malloc (n_size);
1235 /* dname can end in "/" only if dname == "/";
1236 * if dname does not end in "/", we need to add
1237 * a "/" (otherwise, we must not!) */
1238 GNUNET_snprintf (name, n_size, "%s%s%s", dname,
1239 (strcmp (dname, DIR_SEPARATOR_STR) ==
1240 0) ? "" : DIR_SEPARATOR_STR, finfo->d_name);
1241 if (GNUNET_OK != callback (callback_cls, name))
1245 GNUNET_free (dname);
1246 return GNUNET_SYSERR;
1253 GNUNET_free (dname);
1259 * Opaque handle used for iterating over a directory.
1261 struct GNUNET_DISK_DirectoryIterator
1265 * Function to call on directory entries.
1267 GNUNET_DISK_DirectoryIteratorCallback callback;
1270 * Closure for callback.
1275 * Reference to directory.
1285 * Next filename to process.
1292 enum GNUNET_SCHEDULER_Priority priority;
1298 * Task used by the directory iterator.
1301 directory_iterator_task (void *cls,
1302 const struct GNUNET_SCHEDULER_TaskContext *tc)
1304 struct GNUNET_DISK_DirectoryIterator *iter = cls;
1307 name = iter->next_name;
1308 GNUNET_assert (name != NULL);
1309 iter->next_name = NULL;
1310 iter->callback (iter->callback_cls, iter, name, iter->dirname);
1316 * This function must be called during the DiskIteratorCallback
1317 * (exactly once) to schedule the task to process the next
1318 * filename in the directory (if there is one).
1320 * @param iter opaque handle for the iterator
1321 * @param can set to GNUNET_YES to terminate the iteration early
1322 * @return GNUNET_YES if iteration will continue,
1323 * GNUNET_NO if this was the last entry (and iteration is complete),
1324 * GNUNET_SYSERR if abort was YES
1327 GNUNET_DISK_directory_iterator_next (struct GNUNET_DISK_DirectoryIterator *iter,
1330 struct dirent *finfo;
1332 GNUNET_assert (iter->next_name == NULL);
1333 if (can == GNUNET_YES)
1335 CLOSEDIR (iter->directory);
1336 GNUNET_free (iter->dirname);
1338 return GNUNET_SYSERR;
1340 while (NULL != (finfo = READDIR (iter->directory)))
1342 if ((0 == strcmp (finfo->d_name, ".")) ||
1343 (0 == strcmp (finfo->d_name, "..")))
1345 GNUNET_asprintf (&iter->next_name, "%s%s%s", iter->dirname,
1346 DIR_SEPARATOR_STR, finfo->d_name);
1351 GNUNET_DISK_directory_iterator_next (iter, GNUNET_YES);
1354 GNUNET_SCHEDULER_add_with_priority (iter->priority, &directory_iterator_task,
1361 * Scan a directory for files using the scheduler to run a task for
1362 * each entry. The name of the directory must be expanded first (!).
1363 * If a scheduler does not need to be used, GNUNET_DISK_directory_scan
1364 * may provide a simpler API.
1366 * @param prio priority to use
1367 * @param dirName the name of the directory
1368 * @param callback the method to call for each file
1369 * @param callback_cls closure for callback
1370 * @return GNUNET_YES if directory is not empty and 'callback'
1371 * will be called later, GNUNET_NO otherwise, GNUNET_SYSERR on error.
1374 GNUNET_DISK_directory_iterator_start (enum GNUNET_SCHEDULER_Priority prio,
1375 const char *dirName,
1376 GNUNET_DISK_DirectoryIteratorCallback
1377 callback, void *callback_cls)
1379 struct GNUNET_DISK_DirectoryIterator *di;
1381 di = GNUNET_malloc (sizeof (struct GNUNET_DISK_DirectoryIterator));
1382 di->callback = callback;
1383 di->callback_cls = callback_cls;
1384 di->directory = OPENDIR (dirName);
1385 if (di->directory == NULL)
1388 callback (callback_cls, NULL, NULL, NULL);
1389 return GNUNET_SYSERR;
1391 di->dirname = GNUNET_strdup (dirName);
1392 di->priority = prio;
1393 return GNUNET_DISK_directory_iterator_next (di, GNUNET_NO);
1398 * Function that removes the given directory by calling
1399 * "GNUNET_DISK_directory_remove".
1401 * @param unused not used
1402 * @param fn directory to remove
1406 remove_helper (void *unused, const char *fn)
1408 (void) GNUNET_DISK_directory_remove (fn);
1414 * Remove all files in a directory (rm -rf). Call with
1418 * @param filename the file to remove
1419 * @return GNUNET_OK on success, GNUNET_SYSERR on error
1422 GNUNET_DISK_directory_remove (const char *filename)
1426 if (0 != LSTAT (filename, &istat))
1427 return GNUNET_NO; /* file may not exist... */
1428 (void) CHMOD (filename, S_IWUSR | S_IRUSR | S_IXUSR);
1429 if (UNLINK (filename) == 0)
1431 if ((errno != EISDIR) &&
1432 /* EISDIR is not sufficient in all cases, e.g.
1433 * sticky /tmp directory may result in EPERM on BSD.
1434 * So we also explicitly check "isDirectory" */
1435 (GNUNET_YES != GNUNET_DISK_directory_test (filename, GNUNET_YES)))
1437 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "rmdir", filename);
1438 return GNUNET_SYSERR;
1440 if (GNUNET_SYSERR ==
1441 GNUNET_DISK_directory_scan (filename, &remove_helper, NULL))
1442 return GNUNET_SYSERR;
1443 if (0 != RMDIR (filename))
1445 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "rmdir", filename);
1446 return GNUNET_SYSERR;
1455 * @param src file to copy
1456 * @param dst destination file name
1457 * @return GNUNET_OK on success, GNUNET_SYSERR on error
1460 GNUNET_DISK_file_copy (const char *src, const char *dst)
1466 struct GNUNET_DISK_FileHandle *in;
1467 struct GNUNET_DISK_FileHandle *out;
1469 if (GNUNET_OK != GNUNET_DISK_file_size (src, &size, GNUNET_YES, GNUNET_YES))
1470 return GNUNET_SYSERR;
1472 in = GNUNET_DISK_file_open (src, GNUNET_DISK_OPEN_READ,
1473 GNUNET_DISK_PERM_NONE);
1475 return GNUNET_SYSERR;
1477 GNUNET_DISK_file_open (dst,
1478 GNUNET_DISK_OPEN_WRITE | GNUNET_DISK_OPEN_CREATE |
1479 GNUNET_DISK_OPEN_FAILIFEXISTS,
1480 GNUNET_DISK_PERM_USER_READ |
1481 GNUNET_DISK_PERM_USER_WRITE |
1482 GNUNET_DISK_PERM_GROUP_READ |
1483 GNUNET_DISK_PERM_GROUP_WRITE);
1486 GNUNET_DISK_file_close (in);
1487 return GNUNET_SYSERR;
1489 buf = GNUNET_malloc (COPY_BLK_SIZE);
1492 len = COPY_BLK_SIZE;
1493 if (len > size - pos)
1495 if (len != GNUNET_DISK_file_read (in, buf, len))
1497 if (len != GNUNET_DISK_file_write (out, buf, len))
1502 GNUNET_DISK_file_close (in);
1503 GNUNET_DISK_file_close (out);
1507 GNUNET_DISK_file_close (in);
1508 GNUNET_DISK_file_close (out);
1509 return GNUNET_SYSERR;
1514 * @brief Removes special characters as ':' from a filename.
1515 * @param fn the filename to canonicalize
1518 GNUNET_DISK_filename_canonicalize (char *fn)
1528 if (c == '/' || c == '\\' || c == ':' || c == '*' || c == '?' || c == '"' ||
1529 c == '<' || c == '>' || c == '|')
1541 * @brief Change owner of a file
1543 * @param filename name of file to change the owner of
1544 * @param user name of the new owner
1545 * @return GNUNET_OK on success, GNUNET_SYSERR on failure
1548 GNUNET_DISK_file_change_owner (const char *filename, const char *user)
1553 pws = getpwnam (user);
1556 LOG (GNUNET_ERROR_TYPE_ERROR,
1557 _("Cannot obtain information about user `%s': %s\n"), user,
1559 return GNUNET_SYSERR;
1561 if (0 != chown (filename, pws->pw_uid, pws->pw_gid))
1562 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "chown", filename);
1569 * Lock a part of a file
1570 * @param fh file handle
1571 * @param lockStart absolute position from where to lock
1572 * @param lockEnd absolute position until where to lock
1573 * @param excl GNUNET_YES for an exclusive lock
1574 * @return GNUNET_OK on success, GNUNET_SYSERR on error
1577 GNUNET_DISK_file_lock (struct GNUNET_DISK_FileHandle *fh, OFF_T lockStart,
1578 OFF_T lockEnd, int excl)
1583 return GNUNET_SYSERR;
1589 memset (&fl, 0, sizeof (struct flock));
1590 fl.l_type = excl ? F_WRLCK : F_RDLCK;
1591 fl.l_whence = SEEK_SET;
1592 fl.l_start = lockStart;
1595 return fcntl (fh->fd, F_SETLK, &fl) != 0 ? GNUNET_SYSERR : GNUNET_OK;
1598 OFF_T diff = lockEnd - lockStart;
1599 DWORD diff_low, diff_high;
1600 diff_low = (DWORD) (diff & 0xFFFFFFFF);
1601 diff_high = (DWORD) ((diff >> (sizeof (DWORD) * 8)) & 0xFFFFFFFF);
1603 memset (&o, 0, sizeof (OVERLAPPED));
1604 o.Offset = (DWORD) (lockStart & 0xFFFFFFFF);;
1605 o.OffsetHigh = (DWORD) (((lockStart & ~0xFFFFFFFF) >> (sizeof (DWORD) * 8)) & 0xFFFFFFFF);
1608 (fh->h, (excl ? LOCKFILE_EXCLUSIVE_LOCK : 0) | LOCKFILE_FAIL_IMMEDIATELY,
1609 0, diff_low, diff_high, &o))
1611 SetErrnoFromWinError (GetLastError ());
1612 return GNUNET_SYSERR;
1621 * Unlock a part of a file
1622 * @param fh file handle
1623 * @param unlockStart absolute position from where to unlock
1624 * @param unlockEnd absolute position until where to unlock
1625 * @return GNUNET_OK on success, GNUNET_SYSERR on error
1628 GNUNET_DISK_file_unlock (struct GNUNET_DISK_FileHandle *fh, OFF_T unlockStart,
1634 return GNUNET_SYSERR;
1640 memset (&fl, 0, sizeof (struct flock));
1641 fl.l_type = F_UNLCK;
1642 fl.l_whence = SEEK_SET;
1643 fl.l_start = unlockStart;
1644 fl.l_len = unlockEnd;
1646 return fcntl (fh->fd, F_SETLK, &fl) != 0 ? GNUNET_SYSERR : GNUNET_OK;
1649 OFF_T diff = unlockEnd - unlockStart;
1650 DWORD diff_low, diff_high;
1651 diff_low = (DWORD) (diff & 0xFFFFFFFF);
1652 diff_high = (DWORD) ((diff >> (sizeof (DWORD) * 8)) & 0xFFFFFFFF);
1654 memset (&o, 0, sizeof (OVERLAPPED));
1655 o.Offset = (DWORD) (unlockStart & 0xFFFFFFFF);;
1656 o.OffsetHigh = (DWORD) (((unlockStart & ~0xFFFFFFFF) >> (sizeof (DWORD) * 8)) & 0xFFFFFFFF);
1658 if (!UnlockFileEx (fh->h, 0, diff_low, diff_high, &o))
1660 SetErrnoFromWinError (GetLastError ());
1661 return GNUNET_SYSERR;
1670 * Open a file. Note that the access permissions will only be
1671 * used if a new file is created and if the underlying operating
1672 * system supports the given permissions.
1674 * @param fn file name to be opened
1675 * @param flags opening flags, a combination of GNUNET_DISK_OPEN_xxx bit flags
1676 * @param perm permissions for the newly created file, use
1677 * GNUNET_DISK_PERM_USER_NONE if a file could not be created by this
1678 * call (because of flags)
1679 * @return IO handle on success, NULL on error
1681 struct GNUNET_DISK_FileHandle *
1682 GNUNET_DISK_file_open (const char *fn, enum GNUNET_DISK_OpenFlags flags,
1683 enum GNUNET_DISK_AccessPermissions perm)
1686 struct GNUNET_DISK_FileHandle *ret;
1692 wchar_t wexpfn[MAX_PATH + 1];
1699 expfn = GNUNET_STRINGS_filename_expand (fn);
1704 if (GNUNET_DISK_OPEN_READWRITE == (flags & GNUNET_DISK_OPEN_READWRITE))
1705 oflags = O_RDWR; /* note: O_RDWR is NOT always O_RDONLY | O_WRONLY */
1706 else if (flags & GNUNET_DISK_OPEN_READ)
1708 else if (flags & GNUNET_DISK_OPEN_WRITE)
1713 GNUNET_free (expfn);
1716 if (flags & GNUNET_DISK_OPEN_FAILIFEXISTS)
1717 oflags |= (O_CREAT | O_EXCL);
1718 if (flags & GNUNET_DISK_OPEN_TRUNCATE)
1720 if (flags & GNUNET_DISK_OPEN_APPEND)
1722 if (flags & GNUNET_DISK_OPEN_CREATE)
1724 (void) GNUNET_DISK_directory_create_for_file (expfn);
1726 mode = translate_unix_perms (perm);
1729 fd = open (expfn, oflags | O_LARGEFILE, mode);
1732 if (0 == (flags & GNUNET_DISK_OPEN_FAILIFEXISTS))
1733 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "open", expfn);
1735 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_DEBUG, "open", expfn);
1736 GNUNET_free (expfn);
1743 if (GNUNET_DISK_OPEN_READWRITE == (flags & GNUNET_DISK_OPEN_READWRITE))
1744 access = FILE_READ_DATA | FILE_WRITE_DATA;
1745 else if (flags & GNUNET_DISK_OPEN_READ)
1746 access = FILE_READ_DATA;
1747 else if (flags & GNUNET_DISK_OPEN_WRITE)
1748 access = FILE_WRITE_DATA;
1750 if (flags & GNUNET_DISK_OPEN_FAILIFEXISTS)
1754 else if (flags & GNUNET_DISK_OPEN_CREATE)
1756 (void) GNUNET_DISK_directory_create_for_file (expfn);
1757 if (flags & GNUNET_DISK_OPEN_TRUNCATE)
1758 disp = CREATE_ALWAYS;
1762 else if (flags & GNUNET_DISK_OPEN_TRUNCATE)
1764 disp = TRUNCATE_EXISTING;
1768 disp = OPEN_EXISTING;
1771 if (ERROR_SUCCESS == plibc_conv_to_win_pathwconv(expfn, wexpfn))
1772 h = CreateFileW (wexpfn, access,
1773 FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE, NULL,
1774 disp, FILE_ATTRIBUTE_NORMAL, NULL);
1776 h = INVALID_HANDLE_VALUE;
1777 if (h == INVALID_HANDLE_VALUE)
1780 SetErrnoFromWinError (GetLastError ());
1782 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_INFO, "open", expfn);
1783 GNUNET_free (expfn);
1788 if (flags & GNUNET_DISK_OPEN_APPEND)
1789 if (SetFilePointer (h, 0, 0, FILE_END) == INVALID_SET_FILE_POINTER)
1791 SetErrnoFromWinError (GetLastError ());
1792 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "SetFilePointer", expfn);
1794 GNUNET_free (expfn);
1799 ret = GNUNET_malloc (sizeof (struct GNUNET_DISK_FileHandle));
1802 ret->type = GNUNET_DISK_HANLDE_TYPE_FILE;
1806 GNUNET_free (expfn);
1812 * Close an open file
1813 * @param h file handle
1814 * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
1817 GNUNET_DISK_file_close (struct GNUNET_DISK_FileHandle *h)
1822 return GNUNET_SYSERR;
1826 if (!CloseHandle (h->h))
1828 SetErrnoFromWinError (GetLastError ());
1829 LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING, "close");
1830 GNUNET_free (h->oOverlapRead);
1831 GNUNET_free (h->oOverlapWrite);
1833 return GNUNET_SYSERR;
1836 if (close (h->fd) != 0)
1838 LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING, "close");
1840 return GNUNET_SYSERR;
1849 * Get a handle from a native FD.
1851 * @param fd native file descriptor
1852 * @return file handle corresponding to the descriptor
1854 struct GNUNET_DISK_FileHandle *
1855 GNUNET_DISK_get_handle_from_native (FILE *fd)
1857 struct GNUNET_DISK_FileHandle *fh;
1868 osfh = _get_osfhandle (fno);
1869 if (INVALID_HANDLE_VALUE == (HANDLE) osfh)
1873 fh = GNUNET_malloc (sizeof (struct GNUNET_DISK_FileHandle));
1876 fh->h = (HANDLE) osfh;
1877 /* Assume it to be a pipe. TODO: use some kind of detection
1878 * function to figure out handle type.
1879 * Note that we can't make it overlapped if it isn't already.
1880 * (ReOpenFile() is only available in 2003/Vista).
1881 * The process that opened this file in the first place (usually a parent
1882 * process, if this is stdin/stdout/stderr) must make it overlapped,
1883 * otherwise we're screwed, as selecting on non-overlapped handle
1886 fh->type = GNUNET_DISK_HANLDE_TYPE_PIPE;
1887 fh->oOverlapRead = GNUNET_malloc (sizeof (OVERLAPPED));
1888 fh->oOverlapWrite = GNUNET_malloc (sizeof (OVERLAPPED));
1889 fh->oOverlapRead->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
1890 fh->oOverlapWrite->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
1900 * Construct full path to a file inside of the private
1901 * directory used by GNUnet. Also creates the corresponding
1902 * directory. If the resulting name is supposed to be
1903 * a directory, end the last argument in '/' (or pass
1904 * DIR_SEPARATOR_STR as the last argument before NULL).
1906 * @param cfg configuration to use (determines HOME)
1907 * @param serviceName name of the service
1908 * @param ... is NULL-terminated list of
1909 * path components to append to the
1910 * private directory name.
1911 * @return the constructed filename
1914 GNUNET_DISK_get_home_filename (const struct GNUNET_CONFIGURATION_Handle *cfg,
1915 const char *serviceName, ...)
1921 unsigned int needed;
1924 GNUNET_CONFIGURATION_get_value_filename (cfg, serviceName, "HOME", &pfx))
1928 LOG (GNUNET_ERROR_TYPE_WARNING,
1929 _("No `%s' specified for service `%s' in configuration.\n"), "HOME",
1933 needed = strlen (pfx) + 2;
1934 if ((pfx[strlen (pfx) - 1] != '/') && (pfx[strlen (pfx) - 1] != '\\'))
1936 va_start (ap, serviceName);
1939 c = va_arg (ap, const char *);
1943 needed += strlen (c);
1944 if ((c[strlen (c) - 1] != '/') && (c[strlen (c) - 1] != '\\'))
1948 ret = GNUNET_malloc (needed);
1951 va_start (ap, serviceName);
1954 c = va_arg (ap, const char *);
1958 if ((c[strlen (c) - 1] != '/') && (c[strlen (c) - 1] != '\\'))
1959 strcat (ret, DIR_SEPARATOR_STR);
1963 if ((ret[strlen (ret) - 1] != '/') && (ret[strlen (ret) - 1] != '\\'))
1964 (void) GNUNET_DISK_directory_create_for_file (ret);
1966 (void) GNUNET_DISK_directory_create (ret);
1972 * Handle for a memory-mapping operation.
1974 struct GNUNET_DISK_MapHandle
1977 * Address where the map is in memory.
1983 * Underlying OS handle.
1988 * Number of bytes mapped.
1996 #define MAP_FAILED ((void *) -1)
2000 * Map a file into memory
2002 * @param h open file handle
2003 * @param m handle to the new mapping
2004 * @param access access specification, GNUNET_DISK_MAP_TYPE_xxx
2005 * @param len size of the mapping
2006 * @return pointer to the mapped memory region, NULL on failure
2009 GNUNET_DISK_file_map (const struct GNUNET_DISK_FileHandle *h,
2010 struct GNUNET_DISK_MapHandle **m,
2011 enum GNUNET_DISK_MapType access, size_t len)
2020 DWORD mapAccess, protect;
2022 if ((access & GNUNET_DISK_MAP_TYPE_READ) &&
2023 (access & GNUNET_DISK_MAP_TYPE_WRITE))
2025 protect = PAGE_READWRITE;
2026 mapAccess = FILE_MAP_ALL_ACCESS;
2028 else if (access & GNUNET_DISK_MAP_TYPE_READ)
2030 protect = PAGE_READONLY;
2031 mapAccess = FILE_MAP_READ;
2033 else if (access & GNUNET_DISK_MAP_TYPE_WRITE)
2035 protect = PAGE_READWRITE;
2036 mapAccess = FILE_MAP_WRITE;
2044 *m = GNUNET_malloc (sizeof (struct GNUNET_DISK_MapHandle));
2045 (*m)->h = CreateFileMapping (h->h, NULL, protect, 0, 0, NULL);
2046 if ((*m)->h == INVALID_HANDLE_VALUE)
2048 SetErrnoFromWinError (GetLastError ());
2053 (*m)->addr = MapViewOfFile ((*m)->h, mapAccess, 0, 0, len);
2056 SetErrnoFromWinError (GetLastError ());
2057 CloseHandle ((*m)->h);
2066 if (access & GNUNET_DISK_MAP_TYPE_READ)
2068 if (access & GNUNET_DISK_MAP_TYPE_WRITE)
2070 *m = GNUNET_malloc (sizeof (struct GNUNET_DISK_MapHandle));
2071 (*m)->addr = mmap (NULL, len, prot, MAP_SHARED, h->fd, 0);
2072 GNUNET_assert (NULL != (*m)->addr);
2073 if (MAP_FAILED == (*m)->addr)
2085 * @param h mapping handle
2086 * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
2089 GNUNET_DISK_file_unmap (struct GNUNET_DISK_MapHandle *h)
2096 return GNUNET_SYSERR;
2100 ret = UnmapViewOfFile (h->addr) ? GNUNET_OK : GNUNET_SYSERR;
2101 if (ret != GNUNET_OK)
2102 SetErrnoFromWinError (GetLastError ());
2103 if (!CloseHandle (h->h) && (ret == GNUNET_OK))
2105 ret = GNUNET_SYSERR;
2106 SetErrnoFromWinError (GetLastError ());
2109 ret = munmap (h->addr, h->len) != -1 ? GNUNET_OK : GNUNET_SYSERR;
2117 * Write file changes to disk
2118 * @param h handle to an open file
2119 * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
2122 GNUNET_DISK_file_sync (const struct GNUNET_DISK_FileHandle *h)
2127 return GNUNET_SYSERR;
2133 ret = FlushFileBuffers (h->h) ? GNUNET_OK : GNUNET_SYSERR;
2134 if (ret != GNUNET_OK)
2135 SetErrnoFromWinError (GetLastError ());
2137 #elif defined(FREEBSD) || defined(OPENBSD) || defined(DARWIN)
2138 return fsync (h->fd) == -1 ? GNUNET_SYSERR : GNUNET_OK;
2140 return fdatasync (h->fd) == -1 ? GNUNET_SYSERR : GNUNET_OK;
2146 /* Copyright Bob Byrnes <byrnes <at> curl.com>
2147 http://permalink.gmane.org/gmane.os.cygwin.patches/2121
2149 /* Create a pipe, and return handles to the read and write ends,
2150 just like CreatePipe, but ensure that the write end permits
2151 FILE_READ_ATTRIBUTES access, on later versions of win32 where
2152 this is supported. This access is needed by NtQueryInformationFile,
2153 which is used to implement select and nonblocking writes.
2154 Note that the return value is either NO_ERROR or GetLastError,
2155 unlike CreatePipe, which returns a bool for success or failure. */
2157 create_selectable_pipe (PHANDLE read_pipe_ptr, PHANDLE write_pipe_ptr,
2158 LPSECURITY_ATTRIBUTES sa_ptr, DWORD psize,
2159 DWORD dwReadMode, DWORD dwWriteMode)
2161 /* Default to error. */
2162 *read_pipe_ptr = *write_pipe_ptr = INVALID_HANDLE_VALUE;
2164 HANDLE read_pipe = INVALID_HANDLE_VALUE, write_pipe = INVALID_HANDLE_VALUE;
2166 /* Ensure that there is enough pipe buffer space for atomic writes. */
2167 if (psize < PIPE_BUF)
2170 char pipename[MAX_PATH];
2172 /* Retry CreateNamedPipe as long as the pipe name is in use.
2173 * Retrying will probably never be necessary, but we want
2174 * to be as robust as possible. */
2177 static volatile LONG pipe_unique_id;
2179 snprintf (pipename, sizeof pipename, "\\\\.\\pipe\\gnunet-%d-%ld",
2180 getpid (), InterlockedIncrement ((LONG *) & pipe_unique_id));
2181 LOG (GNUNET_ERROR_TYPE_DEBUG, "CreateNamedPipe: name = %s, size = %lu\n",
2183 /* Use CreateNamedPipe instead of CreatePipe, because the latter
2184 * returns a write handle that does not permit FILE_READ_ATTRIBUTES
2185 * access, on versions of win32 earlier than WinXP SP2.
2186 * CreatePipe also stupidly creates a full duplex pipe, which is
2187 * a waste, since only a single direction is actually used.
2188 * It's important to only allow a single instance, to ensure that
2189 * the pipe was not created earlier by some other process, even if
2190 * the pid has been reused. We avoid FILE_FLAG_FIRST_PIPE_INSTANCE
2191 * because that is only available for Win2k SP2 and WinXP. */
2192 read_pipe = CreateNamedPipeA (pipename, PIPE_ACCESS_INBOUND | dwReadMode, PIPE_TYPE_BYTE | PIPE_READMODE_BYTE, 1, /* max instances */
2193 psize, /* output buffer size */
2194 psize, /* input buffer size */
2195 NMPWAIT_USE_DEFAULT_WAIT, sa_ptr);
2197 if (read_pipe != INVALID_HANDLE_VALUE)
2199 LOG (GNUNET_ERROR_TYPE_DEBUG, "pipe read handle = %p\n", read_pipe);
2203 DWORD err = GetLastError ();
2207 case ERROR_PIPE_BUSY:
2208 /* The pipe is already open with compatible parameters.
2209 * Pick a new name and retry. */
2210 LOG (GNUNET_ERROR_TYPE_DEBUG, "pipe busy, retrying\n");
2212 case ERROR_ACCESS_DENIED:
2213 /* The pipe is already open with incompatible parameters.
2214 * Pick a new name and retry. */
2215 LOG (GNUNET_ERROR_TYPE_DEBUG, "pipe access denied, retrying\n");
2217 case ERROR_CALL_NOT_IMPLEMENTED:
2218 /* We are on an older Win9x platform without named pipes.
2219 * Return an anonymous pipe as the best approximation. */
2220 LOG (GNUNET_ERROR_TYPE_DEBUG,
2221 "CreateNamedPipe not implemented, resorting to "
2222 "CreatePipe: size = %lu\n", psize);
2223 if (CreatePipe (read_pipe_ptr, write_pipe_ptr, sa_ptr, psize))
2225 LOG (GNUNET_ERROR_TYPE_DEBUG, "pipe read handle = %p, write handle = %p\n",
2230 err = GetLastError ();
2231 LOG (GNUNET_ERROR_TYPE_ERROR, "CreatePipe failed: %d\n", err);
2234 LOG (GNUNET_ERROR_TYPE_ERROR, "CreateNamedPipe failed: %d\n", err);
2239 LOG (GNUNET_ERROR_TYPE_DEBUG, "CreateFile: name = %s\n", pipename);
2241 /* Open the named pipe for writing.
2242 * Be sure to permit FILE_READ_ATTRIBUTES access. */
2243 write_pipe = CreateFileA (pipename, GENERIC_WRITE | FILE_READ_ATTRIBUTES, 0, /* share mode */
2244 sa_ptr, OPEN_EXISTING, dwWriteMode, /* flags and attributes */
2245 0); /* handle to template file */
2247 if (write_pipe == INVALID_HANDLE_VALUE)
2250 DWORD err = GetLastError ();
2252 LOG (GNUNET_ERROR_TYPE_DEBUG, "CreateFile failed: %d\n", err);
2253 CloseHandle (read_pipe);
2256 LOG (GNUNET_ERROR_TYPE_DEBUG, "pipe write handle = %p\n", write_pipe);
2258 *read_pipe_ptr = read_pipe;
2259 *write_pipe_ptr = write_pipe;
2266 * Creates an interprocess channel
2268 * @param blocking_read creates an asynchronous pipe for reading if set to GNUNET_NO
2269 * @param blocking_write creates an asynchronous pipe for writing if set to GNUNET_NO
2270 * @param inherit_read inherit the parent processes stdin (only for windows)
2271 * @param inherit_write inherit the parent processes stdout (only for windows)
2272 * @return handle to the new pipe, NULL on error
2274 struct GNUNET_DISK_PipeHandle *
2275 GNUNET_DISK_pipe (int blocking_read, int blocking_write, int inherit_read, int inherit_write)
2286 LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR, "pipe");
2290 return GNUNET_DISK_pipe_from_fd (blocking_read,
2294 struct GNUNET_DISK_PipeHandle *p;
2295 struct GNUNET_DISK_FileHandle *fds;
2300 p = GNUNET_malloc (sizeof (struct GNUNET_DISK_PipeHandle) +
2301 2 * sizeof (struct GNUNET_DISK_FileHandle));
2302 fds = (struct GNUNET_DISK_FileHandle *) &p[1];
2306 /* All pipes are overlapped. If you want them to block - just
2307 * call WriteFile() and ReadFile() with NULL overlapped pointer.
2310 create_selectable_pipe (&p->fd[0]->h, &p->fd[1]->h, NULL, 0,
2311 FILE_FLAG_OVERLAPPED,
2312 FILE_FLAG_OVERLAPPED);
2316 SetErrnoFromWinError (GetLastError ());
2319 if (!DuplicateHandle
2320 (GetCurrentProcess (), p->fd[0]->h, GetCurrentProcess (), &tmp_handle, 0,
2321 inherit_read == GNUNET_YES ? TRUE : FALSE, DUPLICATE_SAME_ACCESS))
2323 SetErrnoFromWinError (GetLastError ());
2324 CloseHandle (p->fd[0]->h);
2325 CloseHandle (p->fd[1]->h);
2329 CloseHandle (p->fd[0]->h);
2330 p->fd[0]->h = tmp_handle;
2332 if (!DuplicateHandle
2333 (GetCurrentProcess (), p->fd[1]->h, GetCurrentProcess (), &tmp_handle, 0,
2334 inherit_write == GNUNET_YES ? TRUE : FALSE, DUPLICATE_SAME_ACCESS))
2336 SetErrnoFromWinError (GetLastError ());
2337 CloseHandle (p->fd[0]->h);
2338 CloseHandle (p->fd[1]->h);
2342 CloseHandle (p->fd[1]->h);
2343 p->fd[1]->h = tmp_handle;
2345 p->fd[0]->type = GNUNET_DISK_HANLDE_TYPE_PIPE;
2346 p->fd[1]->type = GNUNET_DISK_HANLDE_TYPE_PIPE;
2348 p->fd[0]->oOverlapRead = GNUNET_malloc (sizeof (OVERLAPPED));
2349 p->fd[0]->oOverlapWrite = GNUNET_malloc (sizeof (OVERLAPPED));
2350 p->fd[1]->oOverlapRead = GNUNET_malloc (sizeof (OVERLAPPED));
2351 p->fd[1]->oOverlapWrite = GNUNET_malloc (sizeof (OVERLAPPED));
2353 p->fd[0]->oOverlapRead->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2354 p->fd[0]->oOverlapWrite->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2356 p->fd[1]->oOverlapRead->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2357 p->fd[1]->oOverlapWrite->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2365 * Creates a pipe object from a couple of file descriptors.
2366 * Useful for wrapping existing pipe FDs.
2368 * @param blocking_read creates an asynchronous pipe for reading if set to GNUNET_NO
2369 * @param blocking_write creates an asynchronous pipe for writing if set to GNUNET_NO
2370 * @param fd an array of two fd values. One of them may be -1 for read-only or write-only pipes
2372 * @return handle to the new pipe, NULL on error
2374 struct GNUNET_DISK_PipeHandle *
2375 GNUNET_DISK_pipe_from_fd (int blocking_read, int blocking_write, int fd[2])
2377 struct GNUNET_DISK_PipeHandle *p;
2378 struct GNUNET_DISK_FileHandle *fds;
2380 p = GNUNET_malloc (sizeof (struct GNUNET_DISK_PipeHandle) +
2381 2 * sizeof (struct GNUNET_DISK_FileHandle));
2382 fds = (struct GNUNET_DISK_FileHandle *) &p[1];
2388 int eno = 0; /* make gcc happy */
2390 p->fd[0]->fd = fd[0];
2391 p->fd[1]->fd = fd[1];
2397 flags = fcntl (fd[0], F_GETFL);
2398 flags |= O_NONBLOCK;
2399 if (0 > fcntl (fd[0], F_SETFL, flags))
2405 flags = fcntl (fd[0], F_GETFD);
2406 flags |= FD_CLOEXEC;
2407 if (0 > fcntl (fd[0], F_SETFD, flags))
2416 if (!blocking_write)
2418 flags = fcntl (fd[1], F_GETFL);
2419 flags |= O_NONBLOCK;
2420 if (0 > fcntl (fd[1], F_SETFL, flags))
2426 flags = fcntl (fd[1], F_GETFD);
2427 flags |= FD_CLOEXEC;
2428 if (0 > fcntl (fd[1], F_SETFD, flags))
2437 LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR, "fcntl");
2438 if (p->fd[0]->fd >= 0)
2439 GNUNET_break (0 == close (p->fd[0]->fd));
2440 if (p->fd[1]->fd >= 0)
2441 GNUNET_break (0 == close (p->fd[1]->fd));
2448 p->fd[0]->h = (HANDLE) _get_osfhandle (fd[0]);
2450 p->fd[0]->h = INVALID_HANDLE_VALUE;
2452 p->fd[1]->h = (HANDLE) _get_osfhandle (fd[1]);
2454 p->fd[1]->h = INVALID_HANDLE_VALUE;
2456 if (p->fd[0]->h != INVALID_HANDLE_VALUE)
2458 p->fd[0]->type = GNUNET_DISK_HANLDE_TYPE_PIPE;
2459 p->fd[0]->oOverlapRead = GNUNET_malloc (sizeof (OVERLAPPED));
2460 p->fd[0]->oOverlapWrite = GNUNET_malloc (sizeof (OVERLAPPED));
2461 p->fd[0]->oOverlapRead->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2462 p->fd[0]->oOverlapWrite->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2465 if (p->fd[1]->h != INVALID_HANDLE_VALUE)
2467 p->fd[1]->type = GNUNET_DISK_HANLDE_TYPE_PIPE;
2468 p->fd[1]->oOverlapRead = GNUNET_malloc (sizeof (OVERLAPPED));
2469 p->fd[1]->oOverlapWrite = GNUNET_malloc (sizeof (OVERLAPPED));
2470 p->fd[1]->oOverlapRead->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2471 p->fd[1]->oOverlapWrite->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2479 * Closes an interprocess channel
2481 * @param p pipe to close
2482 * @param end which end of the pipe to close
2483 * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
2486 GNUNET_DISK_pipe_close_end (struct GNUNET_DISK_PipeHandle *p,
2487 enum GNUNET_DISK_PipeEnd end)
2489 int ret = GNUNET_OK;
2493 if (end == GNUNET_DISK_PIPE_END_READ)
2495 if (p->fd[0]->h != INVALID_HANDLE_VALUE)
2497 if (!CloseHandle (p->fd[0]->h))
2499 SetErrnoFromWinError (GetLastError ());
2500 ret = GNUNET_SYSERR;
2502 GNUNET_free (p->fd[0]->oOverlapRead);
2503 GNUNET_free (p->fd[0]->oOverlapWrite);
2504 p->fd[0]->h = INVALID_HANDLE_VALUE;
2507 else if (end == GNUNET_DISK_PIPE_END_WRITE)
2509 if (p->fd[0]->h != INVALID_HANDLE_VALUE)
2511 if (!CloseHandle (p->fd[1]->h))
2513 SetErrnoFromWinError (GetLastError ());
2514 ret = GNUNET_SYSERR;
2516 GNUNET_free (p->fd[1]->oOverlapRead);
2517 GNUNET_free (p->fd[1]->oOverlapWrite);
2518 p->fd[1]->h = INVALID_HANDLE_VALUE;
2524 if (end == GNUNET_DISK_PIPE_END_READ)
2526 if (0 != close (p->fd[0]->fd))
2528 ret = GNUNET_SYSERR;
2533 else if (end == GNUNET_DISK_PIPE_END_WRITE)
2535 if (0 != close (p->fd[1]->fd))
2537 ret = GNUNET_SYSERR;
2549 * Closes an interprocess channel
2551 * @param p pipe to close
2552 * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
2555 GNUNET_DISK_pipe_close (struct GNUNET_DISK_PipeHandle *p)
2557 int ret = GNUNET_OK;
2561 if (p->fd[0]->h != INVALID_HANDLE_VALUE)
2563 if (!CloseHandle (p->fd[0]->h))
2565 SetErrnoFromWinError (GetLastError ());
2566 ret = GNUNET_SYSERR;
2568 GNUNET_free (p->fd[0]->oOverlapRead);
2569 GNUNET_free (p->fd[0]->oOverlapWrite);
2571 if (p->fd[1]->h != INVALID_HANDLE_VALUE)
2573 if (!CloseHandle (p->fd[1]->h))
2575 SetErrnoFromWinError (GetLastError ());
2576 ret = GNUNET_SYSERR;
2578 GNUNET_free (p->fd[1]->oOverlapRead);
2579 GNUNET_free (p->fd[1]->oOverlapWrite);
2584 if (p->fd[0]->fd != -1)
2586 if (0 != close (p->fd[0]->fd))
2588 ret = GNUNET_SYSERR;
2593 if (p->fd[1]->fd != -1)
2595 if (0 != close (p->fd[1]->fd))
2597 ret = GNUNET_SYSERR;
2609 * Get the handle to a particular pipe end
2612 * @param n end to access
2613 * @return handle for the respective end
2615 const struct GNUNET_DISK_FileHandle *
2616 GNUNET_DISK_pipe_handle (const struct GNUNET_DISK_PipeHandle *p,
2617 enum GNUNET_DISK_PipeEnd n)
2621 case GNUNET_DISK_PIPE_END_READ:
2622 case GNUNET_DISK_PIPE_END_WRITE:
2632 * Retrieve OS file handle
2634 * @param fh GNUnet file descriptor
2635 * @param dst destination buffer
2636 * @param dst_len length of dst
2637 * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
2640 GNUNET_DISK_internal_file_handle_ (const struct GNUNET_DISK_FileHandle *fh,
2641 void *dst, size_t dst_len)
2644 if (dst_len < sizeof (HANDLE))
2645 return GNUNET_SYSERR;
2646 *((HANDLE *) dst) = fh->h;
2648 if (dst_len < sizeof (int))
2649 return GNUNET_SYSERR;
2650 *((int *) dst) = fh->fd;