2 This file is part of GNUnet.
3 (C) 2001, 2002, 2005, 2006, 2009 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)
43 #define DEBUG_NPIPE GNUNET_EXTRA_LOGGING
45 #define DEBUG_PIPE GNUNET_EXTRA_LOGGING
48 * Block size for IO for copying files.
50 #define COPY_BLK_SIZE 65536
54 #if defined(LINUX) || defined(CYGWIN)
57 #if defined(SOMEBSD) || defined(DARWIN)
58 #include <sys/param.h>
59 #include <sys/mount.h>
62 #include <sys/types.h>
63 #include <sys/statvfs.h>
68 ULONG PipeSerialNumber;
70 #define _IFMT 0170000 /* type of file */
71 #define _IFLNK 0120000 /* symbolic link */
72 #define S_ISLNK(m) (((m)&_IFMT) == _IFLNK)
74 #error PORT-ME: need to port statfs (how much space is left on the drive?)
80 #if !defined(SOMEBSD) && !defined(DARWIN) && !defined(WINDOWS)
84 #include <sys/statvfs.h>
89 * Handle used to manage a pipe.
91 struct GNUNET_DISK_PipeHandle
94 * File descriptors for the pipe.
96 struct GNUNET_DISK_FileHandle *fd[2];
101 * Closure for the recursion to determine the file size
104 struct GetFileSizeData
107 * Set to the total file size.
112 * GNUNET_YES if symbolic links should be included.
114 int include_sym_links;
119 translate_unix_perms (enum GNUNET_DISK_AccessPermissions perm)
124 if (perm & GNUNET_DISK_PERM_USER_READ)
126 if (perm & GNUNET_DISK_PERM_USER_WRITE)
128 if (perm & GNUNET_DISK_PERM_USER_EXEC)
130 if (perm & GNUNET_DISK_PERM_GROUP_READ)
132 if (perm & GNUNET_DISK_PERM_GROUP_WRITE)
134 if (perm & GNUNET_DISK_PERM_GROUP_EXEC)
136 if (perm & GNUNET_DISK_PERM_OTHER_READ)
138 if (perm & GNUNET_DISK_PERM_OTHER_WRITE)
140 if (perm & GNUNET_DISK_PERM_OTHER_EXEC)
148 * Iterate over all files in the given directory and
149 * accumulate their size.
151 * @param cls closure of type "struct GetFileSizeData"
152 * @param fn current filename we are looking at
153 * @return GNUNET_SYSERR on serious errors, otherwise GNUNET_OK
156 getSizeRec (void *cls, const char *fn)
158 struct GetFileSizeData *gfsd = cls;
167 if (0 != STAT64 (fn, &buf))
169 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "stat64", fn);
170 return GNUNET_SYSERR;
173 if (0 != STAT (fn, &buf))
175 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "stat", fn);
176 return GNUNET_SYSERR;
179 if ((!S_ISLNK (buf.st_mode)) || (gfsd->include_sym_links == GNUNET_YES))
180 gfsd->total += buf.st_size;
181 if ((S_ISDIR (buf.st_mode)) && (0 == ACCESS (fn, X_OK)) &&
182 ((!S_ISLNK (buf.st_mode)) || (gfsd->include_sym_links == GNUNET_YES)))
184 if (GNUNET_SYSERR == GNUNET_DISK_directory_scan (fn, &getSizeRec, gfsd))
185 return GNUNET_SYSERR;
192 * Checks whether a handle is invalid
194 * @param h handle to check
195 * @return GNUNET_YES if invalid, GNUNET_NO if valid
198 GNUNET_DISK_handle_invalid (const struct GNUNET_DISK_FileHandle *h)
201 return ((!h) || (h->h == INVALID_HANDLE_VALUE)) ? GNUNET_YES : GNUNET_NO;
203 return ((!h) || (h->fd == -1)) ? GNUNET_YES : GNUNET_NO;
209 * Get the size of an open file.
211 * @param fh open file handle
212 * @param size where to write size of the file
213 * @return GNUNET_OK on success, GNUNET_SYSERR on error
216 GNUNET_DISK_file_handle_size (struct GNUNET_DISK_FileHandle *fh,
222 b = GetFileSizeEx (fh->h, &li);
225 SetErrnoFromWinError (GetLastError ());
226 return GNUNET_SYSERR;
228 *size = (off_t) li.QuadPart;
232 if (0 != FSTAT (fh->fd, &sbuf))
233 return GNUNET_SYSERR;
234 *size = sbuf.st_size;
241 * Move the read/write pointer in a file
243 * @param h handle of an open file
244 * @param offset position to move to
245 * @param whence specification to which position the offset parameter relates to
246 * @return the new position on success, GNUNET_SYSERR otherwise
249 GNUNET_DISK_file_seek (const struct GNUNET_DISK_FileHandle * h, off_t offset,
250 enum GNUNET_DISK_Seek whence)
255 return GNUNET_SYSERR;
261 static DWORD t[] = {[GNUNET_DISK_SEEK_SET] = FILE_BEGIN,
262 [GNUNET_DISK_SEEK_CUR] = FILE_CURRENT,[GNUNET_DISK_SEEK_END] = FILE_END
265 ret = SetFilePointer (h->h, offset, NULL, t[whence]);
266 if (ret == INVALID_SET_FILE_POINTER)
268 SetErrnoFromWinError (GetLastError ());
269 return GNUNET_SYSERR;
273 static int t[] = {[GNUNET_DISK_SEEK_SET] = SEEK_SET,
274 [GNUNET_DISK_SEEK_CUR] = SEEK_CUR,[GNUNET_DISK_SEEK_END] = SEEK_END
277 return lseek (h->fd, offset, t[whence]);
283 * Get the size of the file (or directory) of the given file (in
286 * @param filename name of the file or directory
287 * @param size set to the size of the file (or,
288 * in the case of directories, the sum
289 * of all sizes of files in the directory)
290 * @param includeSymLinks should symbolic links be
292 * @return GNUNET_SYSERR on error, GNUNET_OK on success
295 GNUNET_DISK_file_size (const char *filename, uint64_t * size,
298 struct GetFileSizeData gfsd;
301 GNUNET_assert (size != NULL);
303 gfsd.include_sym_links = includeSymLinks;
304 ret = getSizeRec (&gfsd, filename);
311 * Obtain some unique identifiers for the given file
312 * that can be used to identify it in the local system.
313 * This function is used between GNUnet processes to
314 * quickly check if two files with the same absolute path
315 * are actually identical. The two processes represent
316 * the same peer but may communicate over the network
317 * (and the file may be on an NFS volume). This function
318 * may not be supported on all operating systems.
320 * @param filename name of the file
321 * @param dev set to the device ID
322 * @param ino set to the inode ID
323 * @return GNUNET_OK on success
326 GNUNET_DISK_file_get_identifiers (const char *filename, uint64_t * dev,
333 if ((0 == stat (filename, &sbuf)) && (0 == statvfs (filename, &fbuf)))
335 *dev = (uint64_t) fbuf.f_fsid;
336 *ino = (uint64_t) sbuf.st_ino;
343 if ((0 == stat (filename, &sbuf)) && (0 == statfs (filename, &fbuf)))
345 *dev = ((uint64_t) fbuf.f_fsid.val[0]) << 32 ||
346 ((uint64_t) fbuf.f_fsid.val[1]);
347 *ino = (uint64_t) sbuf.st_ino;
351 // FIXME NILS: test this
352 struct GNUNET_DISK_FileHandle *fh;
353 BY_HANDLE_FILE_INFORMATION info;
356 fh = GNUNET_DISK_file_open (filename, GNUNET_DISK_OPEN_READ, 0);
358 return GNUNET_SYSERR;
359 succ = GetFileInformationByHandle (fh->h, &info);
360 GNUNET_DISK_file_close (fh);
363 *dev = info.dwVolumeSerialNumber;
364 *ino = ((info.nFileIndexHigh << sizeof (DWORD)) | info.nFileIndexLow);
368 return GNUNET_SYSERR;
371 return GNUNET_SYSERR;
376 * Create an (empty) temporary file on disk. If the given name is not
377 * an absolute path, the current 'TMPDIR' will be prepended. In any case,
378 * 6 random characters will be appended to the name to create a unique
381 * @param t component to use for the name;
382 * does NOT contain "XXXXXX" or "/tmp/".
383 * @return NULL on error, otherwise name of fresh
384 * file on disk in directory for temporary files
387 GNUNET_DISK_mktemp (const char *t)
394 if ((t[0] != '/') && (t[0] != '\\')
396 && !(isalpha ((int) t[0]) && (t[0] != '\0') && (t[1] == ':'))
400 tmpdir = getenv ("TMPDIR");
401 tmpdir = tmpdir ? tmpdir : "/tmp";
402 GNUNET_asprintf (&tmpl, "%s/%s%s", tmpdir, t, "XXXXXX");
406 GNUNET_asprintf (&tmpl, "%s%s", t, "XXXXXX");
409 fn = (char *) GNUNET_malloc (MAX_PATH + 1);
410 if (ERROR_SUCCESS != plibc_conv_to_win_path (tmpl, fn))
423 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_ERROR, "mkstemp", fn);
428 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "close", fn);
434 * Get the number of blocks that are left on the partition that
435 * contains the given file (for normal users).
437 * @param part a file on the partition to check
438 * @return -1 on errors, otherwise the number of free blocks
441 GNUNET_DISK_get_blocks_available (const char *part)
446 if (0 != statvfs (part, &buf))
448 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "statfs", part);
458 path = GNUNET_STRINGS_filename_expand (part);
461 memcpy (szDrive, path, 3);
464 if (!GetDiskFreeSpace (szDrive, &dwDummy, &dwDummy, &dwBlocks, &dwDummy))
466 LOG (GNUNET_ERROR_TYPE_WARNING, _("`%s' failed for drive `%s': %u\n"),
467 "GetDiskFreeSpace", szDrive, GetLastError ());
475 if (0 != statfs (part, &s))
477 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "statfs", part);
486 * Test if "fil" is a directory.
487 * Will not print an error message if the directory
488 * does not exist. Will log errors if GNUNET_SYSERR is
489 * returned (i.e., a file exists with the same name).
491 * @param fil filename to test
492 * @return GNUNET_YES if yes, GNUNET_NO if not, GNUNET_SYSERR if it
496 GNUNET_DISK_directory_test (const char *fil)
498 struct stat filestat;
501 ret = STAT (fil, &filestat);
506 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "stat", fil);
507 return GNUNET_SYSERR;
511 if (!S_ISDIR (filestat.st_mode))
513 if (ACCESS (fil, R_OK | X_OK) < 0)
515 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "access", fil);
516 return GNUNET_SYSERR;
522 * Check that fil corresponds to a filename
523 * (of a file that exists and that is not a directory).
525 * @param fil filename to check
526 * @return GNUNET_YES if yes, GNUNET_NO if not a file, GNUNET_SYSERR if something
527 * else (will print an error message in that case, too).
530 GNUNET_DISK_file_test (const char *fil)
532 struct stat filestat;
536 rdir = GNUNET_STRINGS_filename_expand (fil);
538 return GNUNET_SYSERR;
540 ret = STAT (rdir, &filestat);
545 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "stat", rdir);
547 return GNUNET_SYSERR;
552 if (!S_ISREG (filestat.st_mode))
557 if (ACCESS (rdir, R_OK) < 0)
559 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "access", rdir);
561 return GNUNET_SYSERR;
569 * Implementation of "mkdir -p"
570 * @param dir the directory to create
571 * @returns GNUNET_OK on success, GNUNET_SYSERR on failure
574 GNUNET_DISK_directory_create (const char *dir)
581 rdir = GNUNET_STRINGS_filename_expand (dir);
583 return GNUNET_SYSERR;
587 pos = 1; /* skip heading '/' */
589 /* Local or Network path? */
590 if (strncmp (rdir, "\\\\", 2) == 0)
595 if (rdir[pos] == '\\')
605 pos = 3; /* strlen("C:\\") */
610 if ((rdir[pos] == DIR_SEPARATOR) || (pos == len))
613 ret = GNUNET_DISK_directory_test (rdir);
614 if (ret == GNUNET_SYSERR)
617 return GNUNET_SYSERR;
619 if (ret == GNUNET_NO)
622 ret = mkdir (rdir, S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH); /* 755 */
626 if ((ret != 0) && (errno != EEXIST))
628 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_ERROR, "mkdir", rdir);
630 return GNUNET_SYSERR;
633 rdir[pos] = DIR_SEPARATOR;
643 * Create the directory structure for storing
646 * @param filename name of a file in the directory
647 * @returns GNUNET_OK on success,
648 * GNUNET_SYSERR on failure,
649 * GNUNET_NO if the directory
650 * exists but is not writeable for us
653 GNUNET_DISK_directory_create_for_file (const char *filename)
659 rdir = GNUNET_STRINGS_filename_expand (filename);
661 return GNUNET_SYSERR;
663 while ((len > 0) && (rdir[len] != DIR_SEPARATOR))
666 ret = GNUNET_DISK_directory_create (rdir);
667 if ((ret == GNUNET_OK) && (0 != ACCESS (rdir, W_OK)))
675 * Read the contents of a binary file into a buffer.
676 * @param h handle to an open file
677 * @param result the buffer to write the result to
678 * @param len the maximum number of bytes to read
679 * @return the number of bytes read on success, GNUNET_SYSERR on failure
682 GNUNET_DISK_file_read (const struct GNUNET_DISK_FileHandle * h, void *result,
688 return GNUNET_SYSERR;
694 if (h->type != GNUNET_PIPE)
696 if (!ReadFile (h->h, result, len, &bytesRead, NULL))
698 SetErrnoFromWinError (GetLastError ());
699 return GNUNET_SYSERR;
704 if (!ReadFile (h->h, result, len, NULL, h->oOverlapRead))
706 if (GetLastError () != ERROR_IO_PENDING)
708 SetErrnoFromWinError (GetLastError ());
709 return GNUNET_SYSERR;
712 GetOverlappedResult (h->h, h->oOverlapRead, &bytesRead, TRUE);
716 return read (h->fd, result, len);
722 * Read the contents of a binary file into a buffer.
724 * @param fn file name
725 * @param result the buffer to write the result to
726 * @param len the maximum number of bytes to read
727 * @return number of bytes read, GNUNET_SYSERR on failure
730 GNUNET_DISK_fn_read (const char *fn, void *result, size_t len)
732 struct GNUNET_DISK_FileHandle *fh;
735 fh = GNUNET_DISK_file_open (fn, GNUNET_DISK_OPEN_READ, GNUNET_DISK_PERM_NONE);
737 return GNUNET_SYSERR;
738 ret = GNUNET_DISK_file_read (fh, result, len);
739 GNUNET_assert (GNUNET_OK == GNUNET_DISK_file_close (fh));
746 * Write a buffer to a file.
747 * @param h handle to open file
748 * @param buffer the data to write
749 * @param n number of bytes to write
750 * @return number of bytes written on success, GNUNET_SYSERR on error
753 GNUNET_DISK_file_write (const struct GNUNET_DISK_FileHandle * h,
754 const void *buffer, size_t n)
759 return GNUNET_SYSERR;
765 if (h->type != GNUNET_PIPE)
767 if (!WriteFile (h->h, buffer, n, &bytesWritten, NULL))
769 SetErrnoFromWinError (GetLastError ());
770 return GNUNET_SYSERR;
776 LOG (GNUNET_ERROR_TYPE_DEBUG, "It is a pipe trying to write\n");
778 if (!WriteFile (h->h, buffer, n, NULL, h->oOverlapWrite))
780 if (GetLastError () != ERROR_IO_PENDING)
782 SetErrnoFromWinError (GetLastError ());
784 LOG (GNUNET_ERROR_TYPE_DEBUG, "Error writing to pipe\n");
786 return GNUNET_SYSERR;
790 LOG (GNUNET_ERROR_TYPE_DEBUG, "Will get overlapped result\n");
792 GetOverlappedResult (h->h, h->oOverlapWrite, &bytesWritten, TRUE);
796 return write (h->fd, buffer, n);
801 * Write a buffer to a file. If the file is longer than the
802 * number of bytes that will be written, it will be truncated.
804 * @param fn file name
805 * @param buffer the data to write
806 * @param n number of bytes to write
807 * @param mode file permissions
808 * @return number of bytes written on success, GNUNET_SYSERR on error
811 GNUNET_DISK_fn_write (const char *fn, const void *buffer, size_t n,
812 enum GNUNET_DISK_AccessPermissions mode)
814 struct GNUNET_DISK_FileHandle *fh;
817 fh = GNUNET_DISK_file_open (fn,
818 GNUNET_DISK_OPEN_WRITE | GNUNET_DISK_OPEN_TRUNCATE
819 | GNUNET_DISK_OPEN_CREATE, mode);
821 return GNUNET_SYSERR;
822 ret = GNUNET_DISK_file_write (fh, buffer, n);
823 GNUNET_assert (GNUNET_OK == GNUNET_DISK_file_close (fh));
829 * Scan a directory for files.
831 * @param dirName the name of the directory
832 * @param callback the method to call for each file,
833 * can be NULL, in that case, we only count
834 * @param callback_cls closure for callback
835 * @return the number of files found, GNUNET_SYSERR on error or
836 * ieration aborted by callback returning GNUNET_SYSERR
839 GNUNET_DISK_directory_scan (const char *dirName,
840 GNUNET_FileNameCallback callback,
844 struct dirent *finfo;
849 unsigned int name_len;
852 GNUNET_assert (dirName != NULL);
853 dname = GNUNET_STRINGS_filename_expand (dirName);
855 return GNUNET_SYSERR;
856 while ((strlen (dname) > 0) && (dname[strlen (dname) - 1] == DIR_SEPARATOR))
857 dname[strlen (dname) - 1] = '\0';
858 if (0 != STAT (dname, &istat))
860 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "stat", dname);
862 return GNUNET_SYSERR;
864 if (!S_ISDIR (istat.st_mode))
866 LOG (GNUNET_ERROR_TYPE_WARNING, _("Expected `%s' to be a directory!\n"),
869 return GNUNET_SYSERR;
872 dinfo = OPENDIR (dname);
873 if ((errno == EACCES) || (dinfo == NULL))
875 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "opendir", dname);
879 return GNUNET_SYSERR;
882 n_size = strlen (dname) + name_len + 2;
883 name = GNUNET_malloc (n_size);
884 while ((finfo = readdir (dinfo)) != NULL)
886 if ((0 == strcmp (finfo->d_name, ".")) ||
887 (0 == strcmp (finfo->d_name, "..")))
889 if (callback != NULL)
891 if (name_len < strlen (finfo->d_name))
894 name_len = strlen (finfo->d_name);
895 n_size = strlen (dname) + name_len + 2;
896 name = GNUNET_malloc (n_size);
898 /* dname can end in "/" only if dname == "/";
899 * if dname does not end in "/", we need to add
900 * a "/" (otherwise, we must not!) */
901 GNUNET_snprintf (name, n_size, "%s%s%s", dname,
902 (strcmp (dname, DIR_SEPARATOR_STR) ==
903 0) ? "" : DIR_SEPARATOR_STR, finfo->d_name);
904 if (GNUNET_OK != callback (callback_cls, name))
909 return GNUNET_SYSERR;
922 * Opaque handle used for iterating over a directory.
924 struct GNUNET_DISK_DirectoryIterator
928 * Function to call on directory entries.
930 GNUNET_DISK_DirectoryIteratorCallback callback;
933 * Closure for callback.
938 * Reference to directory.
948 * Next filename to process.
955 enum GNUNET_SCHEDULER_Priority priority;
961 * Task used by the directory iterator.
964 directory_iterator_task (void *cls,
965 const struct GNUNET_SCHEDULER_TaskContext *tc)
967 struct GNUNET_DISK_DirectoryIterator *iter = cls;
970 name = iter->next_name;
971 GNUNET_assert (name != NULL);
972 iter->next_name = NULL;
973 iter->callback (iter->callback_cls, iter, name, iter->dirname);
979 * This function must be called during the DiskIteratorCallback
980 * (exactly once) to schedule the task to process the next
981 * filename in the directory (if there is one).
983 * @param iter opaque handle for the iterator
984 * @param can set to GNUNET_YES to terminate the iteration early
985 * @return GNUNET_YES if iteration will continue,
986 * GNUNET_NO if this was the last entry (and iteration is complete),
987 * GNUNET_SYSERR if abort was YES
990 GNUNET_DISK_directory_iterator_next (struct GNUNET_DISK_DirectoryIterator *iter,
993 struct dirent *finfo;
995 GNUNET_assert (iter->next_name == NULL);
996 if (can == GNUNET_YES)
998 closedir (iter->directory);
999 GNUNET_free (iter->dirname);
1001 return GNUNET_SYSERR;
1003 while (NULL != (finfo = readdir (iter->directory)))
1005 if ((0 == strcmp (finfo->d_name, ".")) ||
1006 (0 == strcmp (finfo->d_name, "..")))
1008 GNUNET_asprintf (&iter->next_name, "%s%s%s", iter->dirname,
1009 DIR_SEPARATOR_STR, finfo->d_name);
1014 GNUNET_DISK_directory_iterator_next (iter, GNUNET_YES);
1017 GNUNET_SCHEDULER_add_with_priority (iter->priority, &directory_iterator_task,
1024 * Scan a directory for files using the scheduler to run a task for
1025 * each entry. The name of the directory must be expanded first (!).
1026 * If a scheduler does not need to be used, GNUNET_DISK_directory_scan
1027 * may provide a simpler API.
1029 * @param prio priority to use
1030 * @param dirName the name of the directory
1031 * @param callback the method to call for each file
1032 * @param callback_cls closure for callback
1035 GNUNET_DISK_directory_iterator_start (enum GNUNET_SCHEDULER_Priority prio,
1036 const char *dirName,
1037 GNUNET_DISK_DirectoryIteratorCallback
1038 callback, void *callback_cls)
1040 struct GNUNET_DISK_DirectoryIterator *di;
1042 di = GNUNET_malloc (sizeof (struct GNUNET_DISK_DirectoryIterator));
1043 di->callback = callback;
1044 di->callback_cls = callback_cls;
1045 di->directory = OPENDIR (dirName);
1046 if (di->directory == NULL)
1049 callback (callback_cls, NULL, NULL, NULL);
1052 di->dirname = GNUNET_strdup (dirName);
1053 di->priority = prio;
1054 GNUNET_DISK_directory_iterator_next (di, GNUNET_NO);
1059 * Function that removes the given directory by calling
1060 * "GNUNET_DISK_directory_remove".
1062 * @param unused not used
1063 * @param fn directory to remove
1067 remove_helper (void *unused, const char *fn)
1069 (void) GNUNET_DISK_directory_remove (fn);
1075 * Remove all files in a directory (rm -rf). Call with
1079 * @param fileName the file to remove
1080 * @return GNUNET_OK on success, GNUNET_SYSERR on error
1083 GNUNET_DISK_directory_remove (const char *fileName)
1087 if (0 != LSTAT (fileName, &istat))
1088 return GNUNET_NO; /* file may not exist... */
1089 CHMOD (fileName, S_IWUSR | S_IRUSR | S_IXUSR);
1090 if (UNLINK (fileName) == 0)
1092 if ((errno != EISDIR) &&
1093 /* EISDIR is not sufficient in all cases, e.g.
1094 * sticky /tmp directory may result in EPERM on BSD.
1095 * So we also explicitly check "isDirectory" */
1096 (GNUNET_YES != GNUNET_DISK_directory_test (fileName)))
1098 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "rmdir", fileName);
1099 return GNUNET_SYSERR;
1101 if (GNUNET_SYSERR ==
1102 GNUNET_DISK_directory_scan (fileName, &remove_helper, NULL))
1103 return GNUNET_SYSERR;
1104 if (0 != RMDIR (fileName))
1106 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "rmdir", fileName);
1107 return GNUNET_SYSERR;
1116 * @param src file to copy
1117 * @param dst destination file name
1118 * @return GNUNET_OK on success, GNUNET_SYSERR on error
1121 GNUNET_DISK_file_copy (const char *src, const char *dst)
1127 struct GNUNET_DISK_FileHandle *in;
1128 struct GNUNET_DISK_FileHandle *out;
1130 if (GNUNET_OK != GNUNET_DISK_file_size (src, &size, GNUNET_YES))
1131 return GNUNET_SYSERR;
1133 in = GNUNET_DISK_file_open (src, GNUNET_DISK_OPEN_READ,
1134 GNUNET_DISK_PERM_NONE);
1136 return GNUNET_SYSERR;
1138 GNUNET_DISK_file_open (dst,
1139 GNUNET_DISK_OPEN_WRITE | GNUNET_DISK_OPEN_CREATE |
1140 GNUNET_DISK_OPEN_FAILIFEXISTS,
1141 GNUNET_DISK_PERM_USER_READ |
1142 GNUNET_DISK_PERM_USER_WRITE |
1143 GNUNET_DISK_PERM_GROUP_READ |
1144 GNUNET_DISK_PERM_GROUP_WRITE);
1147 GNUNET_DISK_file_close (in);
1148 return GNUNET_SYSERR;
1150 buf = GNUNET_malloc (COPY_BLK_SIZE);
1153 len = COPY_BLK_SIZE;
1154 if (len > size - pos)
1156 if (len != GNUNET_DISK_file_read (in, buf, len))
1158 if (len != GNUNET_DISK_file_write (out, buf, len))
1163 GNUNET_DISK_file_close (in);
1164 GNUNET_DISK_file_close (out);
1168 GNUNET_DISK_file_close (in);
1169 GNUNET_DISK_file_close (out);
1170 return GNUNET_SYSERR;
1175 * @brief Removes special characters as ':' from a filename.
1176 * @param fn the filename to canonicalize
1179 GNUNET_DISK_filename_canonicalize (char *fn)
1189 if (c == '/' || c == '\\' || c == ':' || c == '*' || c == '?' || c == '"' ||
1190 c == '<' || c == '>' || c == '|')
1202 * @brief Change owner of a file
1204 * @param filename name of file to change the owner of
1205 * @param user name of the new owner
1206 * @return GNUNET_OK on success, GNUNET_SYSERR on failure
1209 GNUNET_DISK_file_change_owner (const char *filename, const char *user)
1214 pws = getpwnam (user);
1217 LOG (GNUNET_ERROR_TYPE_ERROR,
1218 _("Cannot obtain information about user `%s': %s\n"), user,
1220 return GNUNET_SYSERR;
1222 if (0 != chown (filename, pws->pw_uid, pws->pw_gid))
1223 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "chown", filename);
1230 * Lock a part of a file
1231 * @param fh file handle
1232 * @param lockStart absolute position from where to lock
1233 * @param lockEnd absolute position until where to lock
1234 * @param excl GNUNET_YES for an exclusive lock
1235 * @return GNUNET_OK on success, GNUNET_SYSERR on error
1238 GNUNET_DISK_file_lock (struct GNUNET_DISK_FileHandle *fh, off_t lockStart,
1239 off_t lockEnd, int excl)
1244 return GNUNET_SYSERR;
1250 memset (&fl, 0, sizeof (struct flock));
1251 fl.l_type = excl ? F_WRLCK : F_RDLCK;
1252 fl.l_whence = SEEK_SET;
1253 fl.l_start = lockStart;
1256 return fcntl (fh->fd, F_SETLK, &fl) != 0 ? GNUNET_SYSERR : GNUNET_OK;
1260 memset (&o, 0, sizeof (OVERLAPPED));
1261 o.Offset = lockStart;
1264 (fh->h, (excl ? LOCKFILE_EXCLUSIVE_LOCK : 0) | LOCKFILE_FAIL_IMMEDIATELY,
1265 0, lockEnd - lockStart, 0, &o))
1267 SetErrnoFromWinError (GetLastError ());
1268 return GNUNET_SYSERR;
1277 * Unlock a part of a file
1278 * @param fh file handle
1279 * @param unlockStart absolute position from where to unlock
1280 * @param unlockEnd absolute position until where to unlock
1281 * @return GNUNET_OK on success, GNUNET_SYSERR on error
1284 GNUNET_DISK_file_unlock (struct GNUNET_DISK_FileHandle *fh, off_t unlockStart,
1290 return GNUNET_SYSERR;
1296 memset (&fl, 0, sizeof (struct flock));
1297 fl.l_type = F_UNLCK;
1298 fl.l_whence = SEEK_SET;
1299 fl.l_start = unlockStart;
1300 fl.l_len = unlockEnd;
1302 return fcntl (fh->fd, F_SETLK, &fl) != 0 ? GNUNET_SYSERR : GNUNET_OK;
1306 memset (&o, 0, sizeof (OVERLAPPED));
1307 o.Offset = unlockStart;
1309 if (!UnlockFileEx (fh->h, 0, unlockEnd - unlockStart, 0, &o))
1311 SetErrnoFromWinError (GetLastError ());
1312 return GNUNET_SYSERR;
1321 * Open a file. Note that the access permissions will only be
1322 * used if a new file is created and if the underlying operating
1323 * system supports the given permissions.
1325 * @param fn file name to be opened
1326 * @param flags opening flags, a combination of GNUNET_DISK_OPEN_xxx bit flags
1327 * @param perm permissions for the newly created file, use
1328 * GNUNET_DISK_PERM_USER_NONE if a file could not be created by this
1329 * call (because of flags)
1330 * @return IO handle on success, NULL on error
1332 struct GNUNET_DISK_FileHandle *
1333 GNUNET_DISK_file_open (const char *fn, enum GNUNET_DISK_OpenFlags flags,
1334 enum GNUNET_DISK_AccessPermissions perm)
1337 struct GNUNET_DISK_FileHandle *ret;
1349 expfn = GNUNET_STRINGS_filename_expand (fn);
1354 if (GNUNET_DISK_OPEN_READWRITE == (flags & GNUNET_DISK_OPEN_READWRITE))
1355 oflags = O_RDWR; /* note: O_RDWR is NOT always O_RDONLY | O_WRONLY */
1356 else if (flags & GNUNET_DISK_OPEN_READ)
1358 else if (flags & GNUNET_DISK_OPEN_WRITE)
1363 GNUNET_free (expfn);
1366 if (flags & GNUNET_DISK_OPEN_FAILIFEXISTS)
1367 oflags |= (O_CREAT | O_EXCL);
1368 if (flags & GNUNET_DISK_OPEN_TRUNCATE)
1370 if (flags & GNUNET_DISK_OPEN_APPEND)
1372 if (flags & GNUNET_DISK_OPEN_CREATE)
1374 (void) GNUNET_DISK_directory_create_for_file (expfn);
1376 mode = translate_unix_perms (perm);
1379 fd = open (expfn, oflags | O_LARGEFILE, mode);
1382 if (0 == (flags & GNUNET_DISK_OPEN_FAILIFEXISTS))
1383 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "open", expfn);
1385 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_DEBUG, "open", expfn);
1386 GNUNET_free (expfn);
1393 if (GNUNET_DISK_OPEN_READWRITE == (flags & GNUNET_DISK_OPEN_READWRITE))
1394 access = FILE_READ_DATA | FILE_WRITE_DATA;
1395 else if (flags & GNUNET_DISK_OPEN_READ)
1396 access = FILE_READ_DATA;
1397 else if (flags & GNUNET_DISK_OPEN_WRITE)
1398 access = FILE_WRITE_DATA;
1400 if (flags & GNUNET_DISK_OPEN_FAILIFEXISTS)
1404 else if (flags & GNUNET_DISK_OPEN_CREATE)
1406 (void) GNUNET_DISK_directory_create_for_file (expfn);
1407 if (flags & GNUNET_DISK_OPEN_TRUNCATE)
1408 disp = CREATE_ALWAYS;
1412 else if (flags & GNUNET_DISK_OPEN_TRUNCATE)
1414 disp = TRUNCATE_EXISTING;
1418 disp = OPEN_EXISTING;
1421 /* TODO: access priviledges? */
1422 h = CreateFile (expfn, access,
1423 FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE, NULL,
1424 disp, FILE_ATTRIBUTE_NORMAL, NULL);
1425 if (h == INVALID_HANDLE_VALUE)
1427 SetErrnoFromWinError (GetLastError ());
1428 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "open", expfn);
1429 GNUNET_free (expfn);
1433 if (flags & GNUNET_DISK_OPEN_APPEND)
1434 if (SetFilePointer (h, 0, 0, FILE_END) == INVALID_SET_FILE_POINTER)
1436 SetErrnoFromWinError (GetLastError ());
1437 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "SetFilePointer", expfn);
1439 GNUNET_free (expfn);
1444 ret = GNUNET_malloc (sizeof (struct GNUNET_DISK_FileHandle));
1447 ret->type = GNUNET_DISK_FILE;
1451 GNUNET_free (expfn);
1457 * Close an open file
1458 * @param h file handle
1459 * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
1462 GNUNET_DISK_file_close (struct GNUNET_DISK_FileHandle *h)
1467 return GNUNET_SYSERR;
1471 if (!CloseHandle (h->h))
1473 SetErrnoFromWinError (GetLastError ());
1474 LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING, "close");
1475 GNUNET_free (h->oOverlapRead);
1476 GNUNET_free (h->oOverlapWrite);
1478 return GNUNET_SYSERR;
1481 if (close (h->fd) != 0)
1483 LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING, "close");
1485 return GNUNET_SYSERR;
1494 * Construct full path to a file inside of the private
1495 * directory used by GNUnet. Also creates the corresponding
1496 * directory. If the resulting name is supposed to be
1497 * a directory, end the last argument in '/' (or pass
1498 * DIR_SEPARATOR_STR as the last argument before NULL).
1500 * @param cfg configuration to use (determines HOME)
1501 * @param serviceName name of the service
1502 * @param ... is NULL-terminated list of
1503 * path components to append to the
1504 * private directory name.
1505 * @return the constructed filename
1508 GNUNET_DISK_get_home_filename (const struct GNUNET_CONFIGURATION_Handle *cfg,
1509 const char *serviceName, ...)
1515 unsigned int needed;
1518 GNUNET_CONFIGURATION_get_value_filename (cfg, serviceName, "HOME", &pfx))
1522 LOG (GNUNET_ERROR_TYPE_WARNING,
1523 _("No `%s' specified for service `%s' in configuration.\n"), "HOME",
1527 needed = strlen (pfx) + 2;
1528 if ((pfx[strlen (pfx) - 1] != '/') && (pfx[strlen (pfx) - 1] != '\\'))
1530 va_start (ap, serviceName);
1533 c = va_arg (ap, const char *);
1537 needed += strlen (c);
1538 if ((c[strlen (c) - 1] != '/') && (c[strlen (c) - 1] != '\\'))
1542 ret = GNUNET_malloc (needed);
1545 va_start (ap, serviceName);
1548 c = va_arg (ap, const char *);
1552 if ((c[strlen (c) - 1] != '/') && (c[strlen (c) - 1] != '\\'))
1553 strcat (ret, DIR_SEPARATOR_STR);
1557 if ((ret[strlen (ret) - 1] != '/') && (ret[strlen (ret) - 1] != '\\'))
1558 (void) GNUNET_DISK_directory_create_for_file (ret);
1560 (void) GNUNET_DISK_directory_create (ret);
1566 * Handle for a memory-mapping operation.
1568 struct GNUNET_DISK_MapHandle
1571 * Address where the map is in memory.
1577 * Underlying OS handle.
1582 * Number of bytes mapped.
1590 #define MAP_FAILED ((void *) -1)
1594 * Map a file into memory
1596 * @param h open file handle
1597 * @param m handle to the new mapping
1598 * @param access access specification, GNUNET_DISK_MAP_TYPE_xxx
1599 * @param len size of the mapping
1600 * @return pointer to the mapped memory region, NULL on failure
1603 GNUNET_DISK_file_map (const struct GNUNET_DISK_FileHandle *h,
1604 struct GNUNET_DISK_MapHandle **m,
1605 enum GNUNET_DISK_MapType access, size_t len)
1614 DWORD mapAccess, protect;
1616 if ((access & GNUNET_DISK_MAP_TYPE_READ) &&
1617 (access & GNUNET_DISK_MAP_TYPE_WRITE))
1619 protect = PAGE_READWRITE;
1620 mapAccess = FILE_MAP_ALL_ACCESS;
1622 else if (access & GNUNET_DISK_MAP_TYPE_READ)
1624 protect = PAGE_READONLY;
1625 mapAccess = FILE_MAP_READ;
1627 else if (access & GNUNET_DISK_MAP_TYPE_WRITE)
1629 protect = PAGE_READWRITE;
1630 mapAccess = FILE_MAP_WRITE;
1638 *m = GNUNET_malloc (sizeof (struct GNUNET_DISK_MapHandle));
1639 (*m)->h = CreateFileMapping (h->h, NULL, protect, 0, 0, NULL);
1640 if ((*m)->h == INVALID_HANDLE_VALUE)
1642 SetErrnoFromWinError (GetLastError ());
1647 (*m)->addr = MapViewOfFile ((*m)->h, mapAccess, 0, 0, len);
1650 SetErrnoFromWinError (GetLastError ());
1651 CloseHandle ((*m)->h);
1660 if (access & GNUNET_DISK_MAP_TYPE_READ)
1662 if (access & GNUNET_DISK_MAP_TYPE_WRITE)
1664 *m = GNUNET_malloc (sizeof (struct GNUNET_DISK_MapHandle));
1665 (*m)->addr = mmap (NULL, len, prot, MAP_SHARED, h->fd, 0);
1666 GNUNET_assert (NULL != (*m)->addr);
1667 if (MAP_FAILED == (*m)->addr)
1679 * @param h mapping handle
1680 * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
1683 GNUNET_DISK_file_unmap (struct GNUNET_DISK_MapHandle *h)
1690 return GNUNET_SYSERR;
1694 ret = UnmapViewOfFile (h->addr) ? GNUNET_OK : GNUNET_SYSERR;
1695 if (ret != GNUNET_OK)
1696 SetErrnoFromWinError (GetLastError ());
1697 if (!CloseHandle (h->h) && (ret == GNUNET_OK))
1699 ret = GNUNET_SYSERR;
1700 SetErrnoFromWinError (GetLastError ());
1703 ret = munmap (h->addr, h->len) != -1 ? GNUNET_OK : GNUNET_SYSERR;
1711 * Write file changes to disk
1712 * @param h handle to an open file
1713 * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
1716 GNUNET_DISK_file_sync (const struct GNUNET_DISK_FileHandle *h)
1721 return GNUNET_SYSERR;
1727 ret = FlushFileBuffers (h->h) ? GNUNET_OK : GNUNET_SYSERR;
1728 if (ret != GNUNET_OK)
1729 SetErrnoFromWinError (GetLastError ());
1731 #elif defined(FREEBSD) || defined(OPENBSD) || defined(DARWIN)
1732 return fsync (h->fd) == -1 ? GNUNET_SYSERR : GNUNET_OK;
1734 return fdatasync (h->fd) == -1 ? GNUNET_SYSERR : GNUNET_OK;
1739 /* Copyright Bob Byrnes <byrnes <at> curl.com>
1740 http://permalink.gmane.org/gmane.os.cygwin.patches/2121
1742 /* Create a pipe, and return handles to the read and write ends,
1743 just like CreatePipe, but ensure that the write end permits
1744 FILE_READ_ATTRIBUTES access, on later versions of win32 where
1745 this is supported. This access is needed by NtQueryInformationFile,
1746 which is used to implement select and nonblocking writes.
1747 Note that the return value is either NO_ERROR or GetLastError,
1748 unlike CreatePipe, which returns a bool for success or failure. */
1750 create_selectable_pipe (PHANDLE read_pipe_ptr, PHANDLE write_pipe_ptr,
1751 LPSECURITY_ATTRIBUTES sa_ptr, DWORD psize,
1752 DWORD dwReadMode, DWORD dwWriteMode)
1754 /* Default to error. */
1755 *read_pipe_ptr = *write_pipe_ptr = INVALID_HANDLE_VALUE;
1757 HANDLE read_pipe = INVALID_HANDLE_VALUE, write_pipe = INVALID_HANDLE_VALUE;
1759 /* Ensure that there is enough pipe buffer space for atomic writes. */
1760 if (psize < PIPE_BUF)
1763 char pipename[MAX_PATH];
1765 /* Retry CreateNamedPipe as long as the pipe name is in use.
1766 * Retrying will probably never be necessary, but we want
1767 * to be as robust as possible. */
1770 static volatile LONG pipe_unique_id;
1772 snprintf (pipename, sizeof pipename, "\\\\.\\pipe\\gnunet-%d-%ld",
1773 getpid (), InterlockedIncrement ((LONG *) & pipe_unique_id));
1775 LOG (GNUNET_ERROR_TYPE_DEBUG, "CreateNamedPipe: name = %s, size = %lu\n",
1778 /* Use CreateNamedPipe instead of CreatePipe, because the latter
1779 * returns a write handle that does not permit FILE_READ_ATTRIBUTES
1780 * access, on versions of win32 earlier than WinXP SP2.
1781 * CreatePipe also stupidly creates a full duplex pipe, which is
1782 * a waste, since only a single direction is actually used.
1783 * It's important to only allow a single instance, to ensure that
1784 * the pipe was not created earlier by some other process, even if
1785 * the pid has been reused. We avoid FILE_FLAG_FIRST_PIPE_INSTANCE
1786 * because that is only available for Win2k SP2 and WinXP. */
1787 read_pipe = CreateNamedPipeA (pipename, PIPE_ACCESS_INBOUND | dwReadMode, PIPE_TYPE_BYTE | PIPE_READMODE_BYTE, 1, /* max instances */
1788 psize, /* output buffer size */
1789 psize, /* input buffer size */
1790 NMPWAIT_USE_DEFAULT_WAIT, sa_ptr);
1792 if (read_pipe != INVALID_HANDLE_VALUE)
1795 LOG (GNUNET_ERROR_TYPE_DEBUG, "pipe read handle = %p\n", read_pipe);
1800 DWORD err = GetLastError ();
1804 case ERROR_PIPE_BUSY:
1805 /* The pipe is already open with compatible parameters.
1806 * Pick a new name and retry. */
1808 LOG (GNUNET_ERROR_TYPE_DEBUG, "pipe busy, retrying\n");
1811 case ERROR_ACCESS_DENIED:
1812 /* The pipe is already open with incompatible parameters.
1813 * Pick a new name and retry. */
1815 LOG (GNUNET_ERROR_TYPE_DEBUG, "pipe access denied, retrying\n");
1818 case ERROR_CALL_NOT_IMPLEMENTED:
1819 /* We are on an older Win9x platform without named pipes.
1820 * Return an anonymous pipe as the best approximation. */
1822 LOG (GNUNET_ERROR_TYPE_DEBUG,
1823 "CreateNamedPipe not implemented, resorting to "
1824 "CreatePipe: size = %lu\n", psize);
1826 if (CreatePipe (read_pipe_ptr, write_pipe_ptr, sa_ptr, psize))
1829 LOG (GNUNET_ERROR_TYPE_DEBUG, "pipe read handle = %p\n",
1831 LOG (GNUNET_ERROR_TYPE_DEBUG, "pipe write handle = %p\n",
1836 err = GetLastError ();
1837 LOG (GNUNET_ERROR_TYPE_ERROR, "CreatePipe failed: %d\n", err);
1840 LOG (GNUNET_ERROR_TYPE_ERROR, "CreateNamedPipe failed: %d\n", err);
1846 LOG (GNUNET_ERROR_TYPE_DEBUG, "CreateFile: name = %s\n", pipename);
1849 /* Open the named pipe for writing.
1850 * Be sure to permit FILE_READ_ATTRIBUTES access. */
1851 write_pipe = CreateFileA (pipename, GENERIC_WRITE | FILE_READ_ATTRIBUTES, 0, /* share mode */
1852 sa_ptr, OPEN_EXISTING, dwWriteMode, /* flags and attributes */
1853 0); /* handle to template file */
1855 if (write_pipe == INVALID_HANDLE_VALUE)
1858 DWORD err = GetLastError ();
1861 LOG (GNUNET_ERROR_TYPE_DEBUG, "CreateFile failed: %d\n", err);
1863 CloseHandle (read_pipe);
1867 LOG (GNUNET_ERROR_TYPE_DEBUG, "pipe write handle = %p\n", write_pipe);
1870 *read_pipe_ptr = read_pipe;
1871 *write_pipe_ptr = write_pipe;
1877 * Creates an interprocess channel
1879 * @param blocking creates an asynchronous pipe if set to GNUNET_NO
1880 * @param inherit_read inherit the parent processes stdin (only for windows)
1881 * @param inherit_write inherit the parent processes stdout (only for windows)
1883 * @return handle to the new pipe, NULL on error
1885 struct GNUNET_DISK_PipeHandle *
1886 GNUNET_DISK_pipe (int blocking, int inherit_read, int inherit_write)
1888 struct GNUNET_DISK_PipeHandle *p;
1889 struct GNUNET_DISK_FileHandle *fds;
1891 p = GNUNET_malloc (sizeof (struct GNUNET_DISK_PipeHandle) +
1892 2 * sizeof (struct GNUNET_DISK_FileHandle));
1893 fds = (struct GNUNET_DISK_FileHandle *) &p[1];
1906 LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR, "pipe");
1911 p->fd[0]->fd = fd[0];
1912 p->fd[1]->fd = fd[1];
1914 flags = fcntl (fd[0], F_GETFL);
1916 flags |= O_NONBLOCK;
1917 if (0 > fcntl (fd[0], F_SETFL, flags))
1919 flags = fcntl (fd[0], F_GETFD);
1920 flags |= FD_CLOEXEC;
1921 if (0 > fcntl (fd[0], F_SETFD, flags))
1924 flags = fcntl (fd[1], F_GETFL);
1926 flags |= O_NONBLOCK;
1927 if (0 > fcntl (fd[1], F_SETFL, flags))
1929 flags = fcntl (fd[1], F_GETFD);
1930 flags |= FD_CLOEXEC;
1931 if (0 > fcntl (fd[1], F_SETFD, flags))
1936 LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR, "fcntl");
1937 GNUNET_break (0 == close (p->fd[0]->fd));
1938 GNUNET_break (0 == close (p->fd[1]->fd));
1948 create_selectable_pipe (&p->fd[0]->h, &p->fd[1]->h, NULL, 0,
1949 FILE_FLAG_OVERLAPPED, FILE_FLAG_OVERLAPPED);
1953 SetErrnoFromWinError (GetLastError ());
1956 if (!DuplicateHandle
1957 (GetCurrentProcess (), p->fd[0]->h, GetCurrentProcess (), &tmp_handle, 0,
1958 inherit_read == GNUNET_YES ? TRUE : FALSE, DUPLICATE_SAME_ACCESS))
1960 SetErrnoFromWinError (GetLastError ());
1961 CloseHandle (p->fd[0]->h);
1962 CloseHandle (p->fd[1]->h);
1966 CloseHandle (p->fd[0]->h);
1967 p->fd[0]->h = tmp_handle;
1969 if (!DuplicateHandle
1970 (GetCurrentProcess (), p->fd[1]->h, GetCurrentProcess (), &tmp_handle, 0,
1971 inherit_write == GNUNET_YES ? TRUE : FALSE, DUPLICATE_SAME_ACCESS))
1973 SetErrnoFromWinError (GetLastError ());
1974 CloseHandle (p->fd[0]->h);
1975 CloseHandle (p->fd[1]->h);
1979 CloseHandle (p->fd[1]->h);
1980 p->fd[1]->h = tmp_handle;
1986 SetNamedPipeHandleState (p->fd[0]->h, &mode, NULL, NULL);
1987 SetNamedPipeHandleState (p->fd[1]->h, &mode, NULL, NULL);
1988 /* this always fails on Windows 95, so we don't care about error handling */
1990 p->fd[0]->type = GNUNET_PIPE;
1991 p->fd[1]->type = GNUNET_PIPE;
1993 p->fd[0]->oOverlapRead = GNUNET_malloc (sizeof (OVERLAPPED));
1994 p->fd[0]->oOverlapWrite = GNUNET_malloc (sizeof (OVERLAPPED));
1995 p->fd[1]->oOverlapRead = GNUNET_malloc (sizeof (OVERLAPPED));
1996 p->fd[1]->oOverlapWrite = GNUNET_malloc (sizeof (OVERLAPPED));
1998 p->fd[0]->oOverlapRead->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
1999 p->fd[0]->oOverlapWrite->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2001 p->fd[1]->oOverlapRead->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2002 p->fd[1]->oOverlapWrite->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2010 * Closes an interprocess channel
2012 * @param p pipe to close
2013 * @param end which end of the pipe to close
2014 * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
2017 GNUNET_DISK_pipe_close_end (struct GNUNET_DISK_PipeHandle *p,
2018 enum GNUNET_DISK_PipeEnd end)
2020 int ret = GNUNET_OK;
2024 if (end == GNUNET_DISK_PIPE_END_READ)
2026 if (!CloseHandle (p->fd[0]->h))
2028 SetErrnoFromWinError (GetLastError ());
2029 ret = GNUNET_SYSERR;
2031 p->fd[0]->h = INVALID_HANDLE_VALUE;
2033 else if (end == GNUNET_DISK_PIPE_END_WRITE)
2035 if (!CloseHandle (p->fd[1]->h))
2037 SetErrnoFromWinError (GetLastError ());
2038 ret = GNUNET_SYSERR;
2040 p->fd[1]->h = INVALID_HANDLE_VALUE;
2045 if (end == GNUNET_DISK_PIPE_END_READ)
2047 if (0 != close (p->fd[0]->fd))
2049 ret = GNUNET_SYSERR;
2054 else if (end == GNUNET_DISK_PIPE_END_WRITE)
2056 if (0 != close (p->fd[1]->fd))
2058 ret = GNUNET_SYSERR;
2069 * Closes an interprocess channel
2071 * @param p pipe to close
2072 * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
2075 GNUNET_DISK_pipe_close (struct GNUNET_DISK_PipeHandle *p)
2077 int ret = GNUNET_OK;
2081 if (!CloseHandle (p->fd[0]->h))
2083 SetErrnoFromWinError (GetLastError ());
2084 ret = GNUNET_SYSERR;
2086 if (!CloseHandle (p->fd[1]->h))
2088 SetErrnoFromWinError (GetLastError ());
2089 ret = GNUNET_SYSERR;
2094 if (p->fd[0]->fd != -1)
2096 if (0 != close (p->fd[0]->fd))
2098 ret = GNUNET_SYSERR;
2103 if (p->fd[1]->fd != -1)
2105 if (0 != close (p->fd[1]->fd))
2107 ret = GNUNET_SYSERR;
2119 * Creates a named pipe/FIFO and opens it
2120 * @param fn pointer to the name of the named pipe or to NULL
2121 * @param flags open flags
2122 * @param perm access permissions
2123 * @return pipe handle on success, NULL on error
2125 struct GNUNET_DISK_FileHandle *
2126 GNUNET_DISK_npipe_create (char **fn, enum GNUNET_DISK_OpenFlags flags,
2127 enum GNUNET_DISK_AccessPermissions perm)
2130 struct GNUNET_DISK_FileHandle *ret;
2136 if (flags & GNUNET_DISK_OPEN_READWRITE)
2137 openMode = PIPE_ACCESS_DUPLEX;
2138 else if (flags & GNUNET_DISK_OPEN_READ)
2139 openMode = PIPE_ACCESS_INBOUND;
2140 else if (flags & GNUNET_DISK_OPEN_WRITE)
2141 openMode = PIPE_ACCESS_OUTBOUND;
2143 if (flags & GNUNET_DISK_OPEN_FAILIFEXISTS)
2144 openMode |= FILE_FLAG_FIRST_PIPE_INSTANCE;
2153 GNUNET_asprintf (&name, "\\\\.\\pipe\\%.246s", fn);
2155 LOG (GNUNET_ERROR_TYPE_DEBUG,
2156 "Trying to create an instance of named pipe `%s'\n", name);
2158 h = CreateNamedPipe (name, openMode | FILE_FLAG_OVERLAPPED,
2159 PIPE_TYPE_BYTE | PIPE_READMODE_BYTE, 2, 1, 1, 0,
2164 GNUNET_asprintf (fn, "\\\\.\\pipe\\gnunet-%llu",
2165 GNUNET_CRYPTO_random_u64 (GNUNET_CRYPTO_QUALITY_WEAK,
2168 LOG (GNUNET_ERROR_TYPE_DEBUG, "Trying to create unique named pipe `%s'\n",
2171 h = CreateNamedPipe (*fn,
2172 openMode | FILE_FLAG_OVERLAPPED |
2173 FILE_FLAG_FIRST_PIPE_INSTANCE,
2174 PIPE_TYPE_BYTE | PIPE_READMODE_BYTE, 2, 1, 1, 0,
2177 error_code = GetLastError ();
2180 /* don't re-set name to NULL yet */
2181 if (h == INVALID_HANDLE_VALUE)
2183 SetErrnoFromWinError (error_code);
2185 LOG (GNUNET_ERROR_TYPE_DEBUG,
2186 "Pipe creation have failed because of %d, errno is %d\n", error_code,
2192 LOG (GNUNET_ERROR_TYPE_DEBUG,
2193 "Pipe was to be unique, considering re-creation\n");
2197 if (error_code != ERROR_ACCESS_DENIED && error_code != ERROR_PIPE_BUSY)
2202 LOG (GNUNET_ERROR_TYPE_DEBUG,
2203 "Pipe name was not unique, trying again\n");
2213 ret = GNUNET_malloc (sizeof (*ret));
2215 ret->type = GNUNET_PIPE;
2217 ret->oOverlapRead = GNUNET_malloc (sizeof (OVERLAPPED));
2218 ret->oOverlapWrite = GNUNET_malloc (sizeof (OVERLAPPED));
2220 ret->oOverlapRead->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2221 ret->oOverlapWrite->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2227 char dir[] = "/tmp/gnunet-pipe-XXXXXX";
2229 if (mkdtemp (dir) == NULL)
2231 LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR, "mkdtemp");
2234 GNUNET_asprintf (fn, "%s/child-control", dir);
2237 if (mkfifo (*fn, translate_unix_perms (perm)) == -1)
2239 if ((errno != EEXIST) || (0 != (flags & GNUNET_DISK_OPEN_FAILIFEXISTS)))
2243 flags = flags & (~GNUNET_DISK_OPEN_FAILIFEXISTS);
2244 return GNUNET_DISK_file_open (*fn, flags, perm);
2250 * Opens already existing named pipe/FIFO
2252 * @param fn name of an existing named pipe
2253 * @param flags open flags
2254 * @param perm access permissions
2255 * @return pipe handle on success, NULL on error
2257 struct GNUNET_DISK_FileHandle *
2258 GNUNET_DISK_npipe_open (const char *fn, enum GNUNET_DISK_OpenFlags flags,
2259 enum GNUNET_DISK_AccessPermissions perm)
2262 struct GNUNET_DISK_FileHandle *ret;
2267 if (flags & GNUNET_DISK_OPEN_READWRITE)
2268 openMode = GENERIC_WRITE | GENERIC_READ;
2269 else if (flags & GNUNET_DISK_OPEN_READ)
2270 openMode = GENERIC_READ;
2271 else if (flags & GNUNET_DISK_OPEN_WRITE)
2272 openMode = GENERIC_WRITE;
2274 h = CreateFile (fn, openMode, 0, NULL, OPEN_EXISTING,
2275 FILE_FLAG_OVERLAPPED | FILE_READ_ATTRIBUTES, NULL);
2276 if (h == INVALID_HANDLE_VALUE)
2278 SetErrnoFromWinError (GetLastError ());
2282 ret = GNUNET_malloc (sizeof (*ret));
2284 ret->type = GNUNET_PIPE;
2285 ret->oOverlapRead = GNUNET_malloc (sizeof (OVERLAPPED));
2286 ret->oOverlapWrite = GNUNET_malloc (sizeof (OVERLAPPED));
2287 ret->oOverlapRead->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2288 ret->oOverlapWrite->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2292 flags = flags & (~GNUNET_DISK_OPEN_FAILIFEXISTS);
2293 return GNUNET_DISK_file_open (fn, flags, perm);
2298 * Closes a named pipe/FIFO
2299 * @param pipe named pipe
2300 * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
2303 GNUNET_DISK_npipe_close (struct GNUNET_DISK_FileHandle *pipe)
2306 return close (pipe->fd) == 0 ? GNUNET_OK : GNUNET_SYSERR;
2310 ret = CloseHandle (pipe->h);
2313 SetErrnoFromWinError (GetLastError ());
2314 return GNUNET_SYSERR;
2323 * Get the handle to a particular pipe end
2326 * @param n end to access
2327 * @return handle for the respective end
2329 const struct GNUNET_DISK_FileHandle *
2330 GNUNET_DISK_pipe_handle (const struct GNUNET_DISK_PipeHandle *p,
2331 enum GNUNET_DISK_PipeEnd n)
2335 case GNUNET_DISK_PIPE_END_READ:
2336 case GNUNET_DISK_PIPE_END_WRITE:
2346 * Retrieve OS file handle
2348 * @param fh GNUnet file descriptor
2349 * @param dst destination buffer
2350 * @param dst_len length of dst
2351 * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
2354 GNUNET_DISK_internal_file_handle_ (const struct GNUNET_DISK_FileHandle *fh,
2355 void *dst, size_t dst_len)
2358 if (dst_len < sizeof (HANDLE))
2359 return GNUNET_SYSERR;
2360 *((HANDLE *) dst) = fh->h;
2362 if (dst_len < sizeof (int))
2363 return GNUNET_SYSERR;
2364 *((int *) dst) = fh->fd;