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 DEBUG_NPIPE GNUNET_NO
39 #define DEBUG_PIPE GNUNET_NO
42 * Block size for IO for copying files.
44 #define COPY_BLK_SIZE 65536
48 #if defined(LINUX) || defined(CYGWIN)
51 #if defined(SOMEBSD) || defined(DARWIN)
52 #include <sys/param.h>
53 #include <sys/mount.h>
56 #include <sys/types.h>
57 #include <sys/statvfs.h>
62 ULONG PipeSerialNumber;
64 #define _IFMT 0170000 /* type of file */
65 #define _IFLNK 0120000 /* symbolic link */
66 #define S_ISLNK(m) (((m)&_IFMT) == _IFLNK)
68 #error PORT-ME: need to port statfs (how much space is left on the drive?)
74 #if !defined(SOMEBSD) && !defined(DARWIN) && !defined(WINDOWS)
78 #include <sys/statvfs.h>
83 * Handle used to manage a pipe.
85 struct GNUNET_DISK_PipeHandle
88 * File descriptors for the pipe.
90 struct GNUNET_DISK_FileHandle *fd[2];
95 * Closure for the recursion to determine the file size
98 struct GetFileSizeData
101 * Set to the total file size.
106 * GNUNET_YES if symbolic links should be included.
108 int include_sym_links;
112 int translate_unix_perms(enum GNUNET_DISK_AccessPermissions perm)
117 if (perm & GNUNET_DISK_PERM_USER_READ)
119 if (perm & GNUNET_DISK_PERM_USER_WRITE)
121 if (perm & GNUNET_DISK_PERM_USER_EXEC)
123 if (perm & GNUNET_DISK_PERM_GROUP_READ)
125 if (perm & GNUNET_DISK_PERM_GROUP_WRITE)
127 if (perm & GNUNET_DISK_PERM_GROUP_EXEC)
129 if (perm & GNUNET_DISK_PERM_OTHER_READ)
131 if (perm & GNUNET_DISK_PERM_OTHER_WRITE)
133 if (perm & GNUNET_DISK_PERM_OTHER_EXEC)
141 * Iterate over all files in the given directory and
142 * accumulate their size.
144 * @param cls closure of type "struct GetFileSizeData"
145 * @param fn current filename we are looking at
146 * @return GNUNET_SYSERR on serious errors, otherwise GNUNET_OK
149 getSizeRec (void *cls, const char *fn)
151 struct GetFileSizeData *gfsd = cls;
159 if (0 != STAT64 (fn, &buf))
161 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "stat64", fn);
162 return GNUNET_SYSERR;
165 if (0 != STAT (fn, &buf))
167 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "stat", fn);
168 return GNUNET_SYSERR;
171 if ((!S_ISLNK (buf.st_mode)) || (gfsd->include_sym_links == GNUNET_YES))
172 gfsd->total += buf.st_size;
173 if ((S_ISDIR (buf.st_mode)) &&
174 (0 == ACCESS (fn, X_OK)) &&
175 ((!S_ISLNK (buf.st_mode)) || (gfsd->include_sym_links == GNUNET_YES)))
177 if (GNUNET_SYSERR == GNUNET_DISK_directory_scan (fn, &getSizeRec, gfsd))
178 return GNUNET_SYSERR;
185 * Checks whether a handle is invalid
187 * @param h handle to check
188 * @return GNUNET_YES if invalid, GNUNET_NO if valid
191 GNUNET_DISK_handle_invalid (const struct GNUNET_DISK_FileHandle *h)
194 return ((!h) || (h->h == INVALID_HANDLE_VALUE)) ? GNUNET_YES : GNUNET_NO;
196 return ((!h) || (h->fd == -1)) ? GNUNET_YES : GNUNET_NO;
202 * Move the read/write pointer in a file
204 * @param h handle of an open file
205 * @param offset position to move to
206 * @param whence specification to which position the offset parameter relates to
207 * @return the new position on success, GNUNET_SYSERR otherwise
210 GNUNET_DISK_file_seek (const struct GNUNET_DISK_FileHandle * h, off_t offset,
211 enum GNUNET_DISK_Seek whence)
216 return GNUNET_SYSERR;
221 static DWORD t[] = {[GNUNET_DISK_SEEK_SET] = FILE_BEGIN,
222 [GNUNET_DISK_SEEK_CUR] = FILE_CURRENT,[GNUNET_DISK_SEEK_END] = FILE_END
225 ret = SetFilePointer (h->h, offset, NULL, t[whence]);
226 if (ret == INVALID_SET_FILE_POINTER)
228 SetErrnoFromWinError (GetLastError ());
229 return GNUNET_SYSERR;
233 static int t[] = {[GNUNET_DISK_SEEK_SET] = SEEK_SET,
234 [GNUNET_DISK_SEEK_CUR] = SEEK_CUR,[GNUNET_DISK_SEEK_END] = SEEK_END
237 return lseek (h->fd, offset, t[whence]);
243 * Get the size of the file (or directory) of the given file (in
246 * @param filename name of the file or directory
247 * @param size set to the size of the file (or,
248 * in the case of directories, the sum
249 * of all sizes of files in the directory)
250 * @param includeSymLinks should symbolic links be
252 * @return GNUNET_SYSERR on error, GNUNET_OK on success
255 GNUNET_DISK_file_size (const char *filename,
256 uint64_t * size, int includeSymLinks)
258 struct GetFileSizeData gfsd;
261 GNUNET_assert (size != NULL);
263 gfsd.include_sym_links = includeSymLinks;
264 ret = getSizeRec (&gfsd, filename);
271 * Obtain some unique identifiers for the given file
272 * that can be used to identify it in the local system.
273 * This function is used between GNUnet processes to
274 * quickly check if two files with the same absolute path
275 * are actually identical. The two processes represent
276 * the same peer but may communicate over the network
277 * (and the file may be on an NFS volume). This function
278 * may not be supported on all operating systems.
280 * @param filename name of the file
281 * @param dev set to the device ID
282 * @param ino set to the inode ID
283 * @return GNUNET_OK on success
286 GNUNET_DISK_file_get_identifiers (const char *filename,
287 uint64_t * dev, uint64_t * ino)
293 if ((0 == stat (filename, &sbuf)) && (0 == statvfs (filename, &fbuf)))
295 *dev = (uint64_t) fbuf.f_fsid;
296 *ino = (uint64_t) sbuf.st_ino;
303 if ( (0 == stat (filename, &sbuf)) &&
304 (0 == statfs (filename, &fbuf) ) )
306 *dev = ((uint64_t) fbuf.f_fsid.val[0]) << 32 || ((uint64_t) fbuf.f_fsid.val[1]);
307 *ino = (uint64_t) sbuf.st_ino;
311 // FIXME NILS: test this
312 struct GNUNET_DISK_FileHandle *fh;
313 BY_HANDLE_FILE_INFORMATION info;
316 fh = GNUNET_DISK_file_open(filename, GNUNET_DISK_OPEN_READ, 0);
318 return GNUNET_SYSERR;
319 succ = GetFileInformationByHandle(fh->h, &info);
320 GNUNET_DISK_file_close(fh);
323 *dev = info.dwVolumeSerialNumber;
324 *ino = ((info.nFileIndexHigh << sizeof(DWORD)) | info.nFileIndexLow);
328 return GNUNET_SYSERR;
331 return GNUNET_SYSERR;
336 * Create an (empty) temporary file on disk. If the given name is not
337 * an absolute path, the current 'TMPDIR' will be prepended. In any case,
338 * 6 random characters will be appended to the name to create a unique
341 * @param t component to use for the name;
342 * does NOT contain "XXXXXX" or "/tmp/".
343 * @return NULL on error, otherwise name of fresh
344 * file on disk in directory for temporary files
347 GNUNET_DISK_mktemp (const char *t)
354 if ( (t[0] != '/') &&
357 && ! (isalpha ((int) t[0]) && (t[0] != '\0') && (t[1] == ':'))
361 tmpdir = getenv ("TMPDIR");
362 tmpdir = tmpdir ? tmpdir : "/tmp";
363 GNUNET_asprintf (&tmpl, "%s/%s%s", tmpdir, t, "XXXXXX");
367 GNUNET_asprintf (&tmpl, "%s%s", t, "XXXXXX");
370 fn = (char *) GNUNET_malloc (MAX_PATH + 1);
371 if (ERROR_SUCCESS != plibc_conv_to_win_path (tmpl, fn))
384 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR, "mkstemp", fn);
389 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "close", fn);
395 * Get the number of blocks that are left on the partition that
396 * contains the given file (for normal users).
398 * @param part a file on the partition to check
399 * @return -1 on errors, otherwise the number of free blocks
402 GNUNET_DISK_get_blocks_available (const char *part)
407 if (0 != statvfs (part, &buf))
409 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "statfs", part);
419 path = GNUNET_STRINGS_filename_expand (part);
422 memcpy (szDrive, path, 3);
425 if (!GetDiskFreeSpace (szDrive, &dwDummy, &dwDummy, &dwBlocks, &dwDummy))
427 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
428 _("`%s' failed for drive `%s': %u\n"),
429 "GetDiskFreeSpace", szDrive, GetLastError ());
436 if (0 != statfs (part, &s))
438 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "statfs", part);
447 * Test if "fil" is a directory.
448 * Will not print an error message if the directory
449 * does not exist. Will log errors if GNUNET_SYSERR is
450 * returned (i.e., a file exists with the same name).
452 * @param fil filename to test
453 * @return GNUNET_YES if yes, GNUNET_NO if not, GNUNET_SYSERR if it
457 GNUNET_DISK_directory_test (const char *fil)
459 struct stat filestat;
462 ret = STAT (fil, &filestat);
467 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "stat", fil);
468 return GNUNET_SYSERR;
472 if (!S_ISDIR (filestat.st_mode))
474 if (ACCESS (fil, R_OK | X_OK) < 0)
476 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "access", fil);
477 return GNUNET_SYSERR;
483 * Check that fil corresponds to a filename
484 * (of a file that exists and that is not a directory).
486 * @param fil filename to check
487 * @return GNUNET_YES if yes, GNUNET_NO if not a file, GNUNET_SYSERR if something
488 * else (will print an error message in that case, too).
491 GNUNET_DISK_file_test (const char *fil)
493 struct stat filestat;
497 rdir = GNUNET_STRINGS_filename_expand (fil);
499 return GNUNET_SYSERR;
501 ret = STAT (rdir, &filestat);
506 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "stat", rdir);
508 return GNUNET_SYSERR;
513 if (!S_ISREG (filestat.st_mode))
518 if (ACCESS (rdir, R_OK) < 0)
520 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "access", rdir);
522 return GNUNET_SYSERR;
530 * Implementation of "mkdir -p"
531 * @param dir the directory to create
532 * @returns GNUNET_OK on success, GNUNET_SYSERR on failure
535 GNUNET_DISK_directory_create (const char *dir)
542 rdir = GNUNET_STRINGS_filename_expand (dir);
544 return GNUNET_SYSERR;
548 pos = 1; /* skip heading '/' */
550 /* Local or Network path? */
551 if (strncmp (rdir, "\\\\", 2) == 0)
556 if (rdir[pos] == '\\')
566 pos = 3; /* strlen("C:\\") */
571 if ((rdir[pos] == DIR_SEPARATOR) || (pos == len))
574 ret = GNUNET_DISK_directory_test (rdir);
575 if (ret == GNUNET_SYSERR)
578 return GNUNET_SYSERR;
580 if (ret == GNUNET_NO)
583 ret = mkdir (rdir, S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH); /* 755 */
587 if ((ret != 0) && (errno != EEXIST))
589 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR, "mkdir",
592 return GNUNET_SYSERR;
595 rdir[pos] = DIR_SEPARATOR;
605 * Create the directory structure for storing
608 * @param filename name of a file in the directory
609 * @returns GNUNET_OK on success,
610 * GNUNET_SYSERR on failure,
611 * GNUNET_NO if the directory
612 * exists but is not writeable for us
615 GNUNET_DISK_directory_create_for_file (const char *filename)
621 rdir = GNUNET_STRINGS_filename_expand (filename);
623 return GNUNET_SYSERR;
625 while ((len > 0) && (rdir[len] != DIR_SEPARATOR))
628 ret = GNUNET_DISK_directory_create (rdir);
629 if ((ret == GNUNET_OK) && (0 != ACCESS (rdir, W_OK)))
637 * Read the contents of a binary file into a buffer.
638 * @param h handle to an open file
639 * @param result the buffer to write the result to
640 * @param len the maximum number of bytes to read
641 * @return the number of bytes read on success, GNUNET_SYSERR on failure
644 GNUNET_DISK_file_read (const struct GNUNET_DISK_FileHandle * h, void *result,
650 return GNUNET_SYSERR;
656 if(h->type != GNUNET_PIPE)
658 if (!ReadFile (h->h, result, len, &bytesRead, NULL))
660 SetErrnoFromWinError (GetLastError ());
661 return GNUNET_SYSERR;
666 if (!ReadFile (h->h, result, len, NULL, h->oOverlapRead))
668 if(GetLastError () != ERROR_IO_PENDING )
670 SetErrnoFromWinError (GetLastError ());
671 return GNUNET_SYSERR;
674 GetOverlappedResult(h->h, h->oOverlapRead, &bytesRead, TRUE);
678 return read (h->fd, result, len);
684 * Read the contents of a binary file into a buffer.
686 * @param fn file name
687 * @param result the buffer to write the result to
688 * @param len the maximum number of bytes to read
689 * @return number of bytes read, GNUNET_SYSERR on failure
692 GNUNET_DISK_fn_read (const char *fn, void *result, size_t len)
694 struct GNUNET_DISK_FileHandle *fh;
697 fh = GNUNET_DISK_file_open (fn, GNUNET_DISK_OPEN_READ,
698 GNUNET_DISK_PERM_NONE);
700 return GNUNET_SYSERR;
701 ret = GNUNET_DISK_file_read (fh, result, len);
702 GNUNET_assert (GNUNET_OK == GNUNET_DISK_file_close (fh));
709 * Write a buffer to a file.
710 * @param h handle to open file
711 * @param buffer the data to write
712 * @param n number of bytes to write
713 * @return number of bytes written on success, GNUNET_SYSERR on error
716 GNUNET_DISK_file_write (const struct GNUNET_DISK_FileHandle * h,
717 const void *buffer, size_t n)
722 return GNUNET_SYSERR;
728 if(h->type != GNUNET_PIPE)
730 if (!WriteFile (h->h, buffer, n, &bytesWritten, NULL))
732 SetErrnoFromWinError (GetLastError ());
733 return GNUNET_SYSERR;
739 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "It is a pipe trying to write\n");
741 if (!WriteFile (h->h, buffer, n, NULL, h->oOverlapWrite))
743 if(GetLastError () != ERROR_IO_PENDING )
745 SetErrnoFromWinError (GetLastError ());
747 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Error writing to pipe\n");
749 return GNUNET_SYSERR;
753 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Will get overlapped result\n");
755 GetOverlappedResult(h->h, h->oOverlapWrite, &bytesWritten, TRUE);
759 return write (h->fd, buffer, n);
764 * Write a buffer to a file. If the file is longer than the
765 * number of bytes that will be written, it will be truncated.
767 * @param fn file name
768 * @param buffer the data to write
769 * @param n number of bytes to write
770 * @param mode file permissions
771 * @return number of bytes written on success, GNUNET_SYSERR on error
774 GNUNET_DISK_fn_write (const char *fn, const void *buffer,
775 size_t n, enum GNUNET_DISK_AccessPermissions mode)
777 struct GNUNET_DISK_FileHandle *fh;
780 fh = GNUNET_DISK_file_open (fn,
781 GNUNET_DISK_OPEN_WRITE
782 | GNUNET_DISK_OPEN_TRUNCATE
783 | GNUNET_DISK_OPEN_CREATE, mode);
785 return GNUNET_SYSERR;
786 ret = GNUNET_DISK_file_write (fh, buffer, n);
787 GNUNET_assert (GNUNET_OK == GNUNET_DISK_file_close (fh));
792 * Scan a directory for files.
794 * @param dirName the name of the directory
795 * @param callback the method to call for each file,
796 * can be NULL, in that case, we only count
797 * @param callback_cls closure for callback
798 * @return the number of files found, GNUNET_SYSERR on error or
799 * ieration aborted by callback returning GNUNET_SYSERR
802 GNUNET_DISK_directory_scan (const char *dirName,
803 GNUNET_FileNameCallback callback,
807 struct dirent *finfo;
812 unsigned int name_len;
815 GNUNET_assert (dirName != NULL);
816 dname = GNUNET_STRINGS_filename_expand (dirName);
818 return GNUNET_SYSERR;
819 while ((strlen (dname) > 0) && (dname[strlen (dname) - 1] == DIR_SEPARATOR))
820 dname[strlen (dname) - 1] = '\0';
821 if (0 != STAT (dname, &istat))
823 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "stat", dname);
825 return GNUNET_SYSERR;
827 if (!S_ISDIR (istat.st_mode))
829 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
830 _("Expected `%s' to be a directory!\n"), dirName);
832 return GNUNET_SYSERR;
835 dinfo = OPENDIR (dname);
836 if ((errno == EACCES) || (dinfo == NULL))
838 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "opendir", dname);
842 return GNUNET_SYSERR;
845 n_size = strlen (dname) + name_len + 2;
846 name = GNUNET_malloc (n_size);
847 while ((finfo = readdir (dinfo)) != NULL)
849 if ((0 == strcmp (finfo->d_name, ".")) ||
850 (0 == strcmp (finfo->d_name, "..")))
852 if (callback != NULL)
854 if (name_len < strlen (finfo->d_name))
857 name_len = strlen (finfo->d_name);
858 n_size = strlen (dname) + name_len + 2;
859 name = GNUNET_malloc (n_size);
861 /* dname can end in "/" only if dname == "/";
862 if dname does not end in "/", we need to add
863 a "/" (otherwise, we must not!) */
864 GNUNET_snprintf (name,
868 (strcmp (dname, DIR_SEPARATOR_STR) ==
869 0) ? "" : DIR_SEPARATOR_STR, finfo->d_name);
870 if (GNUNET_OK != callback (callback_cls, name))
875 return GNUNET_SYSERR;
888 * Opaque handle used for iterating over a directory.
890 struct GNUNET_DISK_DirectoryIterator
894 * Function to call on directory entries.
896 GNUNET_DISK_DirectoryIteratorCallback callback;
899 * Closure for callback.
904 * Reference to directory.
914 * Next filename to process.
921 enum GNUNET_SCHEDULER_Priority priority;
927 * Task used by the directory iterator.
930 directory_iterator_task (void *cls,
931 const struct GNUNET_SCHEDULER_TaskContext *tc)
933 struct GNUNET_DISK_DirectoryIterator *iter = cls;
936 name = iter->next_name;
937 GNUNET_assert (name != NULL);
938 iter->next_name = NULL;
939 iter->callback (iter->callback_cls, iter, name, iter->dirname);
945 * This function must be called during the DiskIteratorCallback
946 * (exactly once) to schedule the task to process the next
947 * filename in the directory (if there is one).
949 * @param iter opaque handle for the iterator
950 * @param can set to GNUNET_YES to terminate the iteration early
951 * @return GNUNET_YES if iteration will continue,
952 * GNUNET_NO if this was the last entry (and iteration is complete),
953 * GNUNET_SYSERR if abort was YES
956 GNUNET_DISK_directory_iterator_next (struct GNUNET_DISK_DirectoryIterator
959 struct dirent *finfo;
961 GNUNET_assert (iter->next_name == NULL);
962 if (can == GNUNET_YES)
964 closedir (iter->directory);
965 GNUNET_free (iter->dirname);
967 return GNUNET_SYSERR;
969 while (NULL != (finfo = readdir (iter->directory)))
971 if ((0 == strcmp (finfo->d_name, ".")) ||
972 (0 == strcmp (finfo->d_name, "..")))
974 GNUNET_asprintf (&iter->next_name,
976 iter->dirname, DIR_SEPARATOR_STR, finfo->d_name);
981 GNUNET_DISK_directory_iterator_next (iter, GNUNET_YES);
984 GNUNET_SCHEDULER_add_with_priority (iter->priority,
985 &directory_iterator_task, iter);
991 * Scan a directory for files using the scheduler to run a task for
992 * each entry. The name of the directory must be expanded first (!).
993 * If a scheduler does not need to be used, GNUNET_DISK_directory_scan
994 * may provide a simpler API.
996 * @param prio priority to use
997 * @param dirName the name of the directory
998 * @param callback the method to call for each file
999 * @param callback_cls closure for callback
1002 GNUNET_DISK_directory_iterator_start (enum GNUNET_SCHEDULER_Priority prio,
1003 const char *dirName,
1004 GNUNET_DISK_DirectoryIteratorCallback
1005 callback, void *callback_cls)
1007 struct GNUNET_DISK_DirectoryIterator *di;
1009 di = GNUNET_malloc (sizeof (struct GNUNET_DISK_DirectoryIterator));
1010 di->callback = callback;
1011 di->callback_cls = callback_cls;
1012 di->directory = OPENDIR (dirName);
1013 if (di->directory == NULL)
1016 callback (callback_cls, NULL, NULL, NULL);
1019 di->dirname = GNUNET_strdup (dirName);
1020 di->priority = prio;
1021 GNUNET_DISK_directory_iterator_next (di, GNUNET_NO);
1026 * Function that removes the given directory by calling
1027 * "GNUNET_DISK_directory_remove".
1029 * @param unused not used
1030 * @param fn directory to remove
1034 remove_helper (void *unused, const char *fn)
1036 (void) GNUNET_DISK_directory_remove (fn);
1042 * Remove all files in a directory (rm -rf). Call with
1046 * @param fileName the file to remove
1047 * @return GNUNET_OK on success, GNUNET_SYSERR on error
1050 GNUNET_DISK_directory_remove (const char *fileName)
1054 if (0 != LSTAT (fileName, &istat))
1055 return GNUNET_NO; /* file may not exist... */
1056 CHMOD (fileName, S_IWUSR | S_IRUSR | S_IXUSR);
1057 if (UNLINK (fileName) == 0)
1059 if ((errno != EISDIR) &&
1060 /* EISDIR is not sufficient in all cases, e.g.
1061 sticky /tmp directory may result in EPERM on BSD.
1062 So we also explicitly check "isDirectory" */
1063 (GNUNET_YES != GNUNET_DISK_directory_test (fileName)))
1065 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "rmdir", fileName);
1066 return GNUNET_SYSERR;
1068 if (GNUNET_SYSERR ==
1069 GNUNET_DISK_directory_scan (fileName, &remove_helper, NULL))
1070 return GNUNET_SYSERR;
1071 if (0 != RMDIR (fileName))
1073 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "rmdir", fileName);
1074 return GNUNET_SYSERR;
1083 * @param src file to copy
1084 * @param dst destination file name
1085 * @return GNUNET_OK on success, GNUNET_SYSERR on error
1088 GNUNET_DISK_file_copy (const char *src, const char *dst)
1094 struct GNUNET_DISK_FileHandle *in;
1095 struct GNUNET_DISK_FileHandle *out;
1097 if (GNUNET_OK != GNUNET_DISK_file_size (src, &size, GNUNET_YES))
1098 return GNUNET_SYSERR;
1100 in = GNUNET_DISK_file_open (src, GNUNET_DISK_OPEN_READ,
1101 GNUNET_DISK_PERM_NONE);
1103 return GNUNET_SYSERR;
1104 out = GNUNET_DISK_file_open (dst, GNUNET_DISK_OPEN_WRITE
1105 | GNUNET_DISK_OPEN_CREATE |
1106 GNUNET_DISK_OPEN_FAILIFEXISTS,
1107 GNUNET_DISK_PERM_USER_READ |
1108 GNUNET_DISK_PERM_USER_WRITE |
1109 GNUNET_DISK_PERM_GROUP_READ |
1110 GNUNET_DISK_PERM_GROUP_WRITE);
1113 GNUNET_DISK_file_close (in);
1114 return GNUNET_SYSERR;
1116 buf = GNUNET_malloc (COPY_BLK_SIZE);
1119 len = COPY_BLK_SIZE;
1120 if (len > size - pos)
1122 if (len != GNUNET_DISK_file_read (in, buf, len))
1124 if (len != GNUNET_DISK_file_write (out, buf, len))
1129 GNUNET_DISK_file_close (in);
1130 GNUNET_DISK_file_close (out);
1134 GNUNET_DISK_file_close (in);
1135 GNUNET_DISK_file_close (out);
1136 return GNUNET_SYSERR;
1141 * @brief Removes special characters as ':' from a filename.
1142 * @param fn the filename to canonicalize
1145 GNUNET_DISK_filename_canonicalize (char *fn)
1155 if (c == '/' || c == '\\' || c == ':' || c == '*' || c == '?' ||
1156 c == '"' || c == '<' || c == '>' || c == '|')
1168 * @brief Change owner of a file
1170 * @param filename name of file to change the owner of
1171 * @param user name of the new owner
1172 * @return GNUNET_OK on success, GNUNET_SYSERR on failure
1175 GNUNET_DISK_file_change_owner (const char *filename, const char *user)
1180 pws = getpwnam (user);
1183 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1184 _("Cannot obtain information about user `%s': %s\n"),
1185 user, STRERROR (errno));
1186 return GNUNET_SYSERR;
1188 if (0 != chown (filename, pws->pw_uid, pws->pw_gid))
1189 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "chown", filename);
1196 * Lock a part of a file
1197 * @param fh file handle
1198 * @param lockStart absolute position from where to lock
1199 * @param lockEnd absolute position until where to lock
1200 * @param excl GNUNET_YES for an exclusive lock
1201 * @return GNUNET_OK on success, GNUNET_SYSERR on error
1204 GNUNET_DISK_file_lock (struct GNUNET_DISK_FileHandle *fh, off_t lockStart,
1205 off_t lockEnd, int excl)
1210 return GNUNET_SYSERR;
1216 memset (&fl, 0, sizeof (struct flock));
1217 fl.l_type = excl ? F_WRLCK : F_RDLCK;
1218 fl.l_whence = SEEK_SET;
1219 fl.l_start = lockStart;
1222 return fcntl (fh->fd, F_SETLK, &fl) != 0 ? GNUNET_SYSERR : GNUNET_OK;
1226 memset (&o, 0, sizeof (OVERLAPPED));
1227 o.Offset = lockStart;
1229 if (!LockFileEx (fh->h, (excl ? LOCKFILE_EXCLUSIVE_LOCK : 0)
1230 | LOCKFILE_FAIL_IMMEDIATELY, 0, lockEnd - lockStart, 0,
1233 SetErrnoFromWinError (GetLastError ());
1234 return GNUNET_SYSERR;
1243 * Unlock a part of a file
1244 * @param fh file handle
1245 * @param unlockStart absolute position from where to unlock
1246 * @param unlockEnd absolute position until where to unlock
1247 * @return GNUNET_OK on success, GNUNET_SYSERR on error
1250 GNUNET_DISK_file_unlock (struct GNUNET_DISK_FileHandle *fh, off_t unlockStart,
1256 return GNUNET_SYSERR;
1262 memset (&fl, 0, sizeof (struct flock));
1263 fl.l_type = F_UNLCK;
1264 fl.l_whence = SEEK_SET;
1265 fl.l_start = unlockStart;
1266 fl.l_len = unlockEnd;
1268 return fcntl (fh->fd, F_SETLK, &fl) != 0 ? GNUNET_SYSERR : GNUNET_OK;
1272 memset (&o, 0, sizeof (OVERLAPPED));
1273 o.Offset = unlockStart;
1275 if (!UnlockFileEx (fh->h, 0, unlockEnd - unlockStart, 0, &o))
1277 SetErrnoFromWinError (GetLastError ());
1278 return GNUNET_SYSERR;
1287 * Open a file. Note that the access permissions will only be
1288 * used if a new file is created and if the underlying operating
1289 * system supports the given permissions.
1291 * @param fn file name to be opened
1292 * @param flags opening flags, a combination of GNUNET_DISK_OPEN_xxx bit flags
1293 * @param perm permissions for the newly created file, use
1294 * GNUNET_DISK_PERM_USER_NONE if a file could not be created by this
1295 * call (because of flags)
1296 * @return IO handle on success, NULL on error
1298 struct GNUNET_DISK_FileHandle *
1299 GNUNET_DISK_file_open (const char *fn,
1300 enum GNUNET_DISK_OpenFlags flags,
1301 enum GNUNET_DISK_AccessPermissions perm)
1304 struct GNUNET_DISK_FileHandle *ret;
1315 expfn = GNUNET_STRINGS_filename_expand (fn);
1320 if (GNUNET_DISK_OPEN_READWRITE == (flags & GNUNET_DISK_OPEN_READWRITE))
1321 oflags = O_RDWR; /* note: O_RDWR is NOT always O_RDONLY | O_WRONLY */
1322 else if (flags & GNUNET_DISK_OPEN_READ)
1324 else if (flags & GNUNET_DISK_OPEN_WRITE)
1329 GNUNET_free (expfn);
1332 if (flags & GNUNET_DISK_OPEN_FAILIFEXISTS)
1333 oflags |= (O_CREAT | O_EXCL);
1334 if (flags & GNUNET_DISK_OPEN_TRUNCATE)
1336 if (flags & GNUNET_DISK_OPEN_APPEND)
1338 if (flags & GNUNET_DISK_OPEN_CREATE)
1340 (void) GNUNET_DISK_directory_create_for_file (expfn);
1342 mode = translate_unix_perms(perm);
1345 fd = open (expfn, oflags | O_LARGEFILE, mode);
1348 if (0 == (flags & GNUNET_DISK_OPEN_FAILIFEXISTS))
1349 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "open", expfn);
1351 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_DEBUG, "open", expfn);
1352 GNUNET_free (expfn);
1359 if (GNUNET_DISK_OPEN_READWRITE == (flags & GNUNET_DISK_OPEN_READWRITE))
1360 access = FILE_READ_DATA | FILE_WRITE_DATA;
1361 else if (flags & GNUNET_DISK_OPEN_READ)
1362 access = FILE_READ_DATA;
1363 else if (flags & GNUNET_DISK_OPEN_WRITE)
1364 access = FILE_WRITE_DATA;
1366 if (flags & GNUNET_DISK_OPEN_FAILIFEXISTS)
1370 else if (flags & GNUNET_DISK_OPEN_CREATE)
1372 (void) GNUNET_DISK_directory_create_for_file (expfn);
1373 if (flags & GNUNET_DISK_OPEN_TRUNCATE)
1374 disp = CREATE_ALWAYS;
1378 else if (flags & GNUNET_DISK_OPEN_TRUNCATE)
1380 disp = TRUNCATE_EXISTING;
1384 disp = OPEN_EXISTING;
1387 /* TODO: access priviledges? */
1388 h = CreateFile (expfn, access, FILE_SHARE_DELETE | FILE_SHARE_READ
1389 | FILE_SHARE_WRITE, NULL, disp, FILE_ATTRIBUTE_NORMAL,
1391 if (h == INVALID_HANDLE_VALUE)
1393 SetErrnoFromWinError (GetLastError ());
1394 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "open", expfn);
1395 GNUNET_free (expfn);
1399 if (flags & GNUNET_DISK_OPEN_APPEND)
1400 if (SetFilePointer (h, 0, 0, FILE_END) == INVALID_SET_FILE_POINTER)
1402 SetErrnoFromWinError (GetLastError ());
1403 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "SetFilePointer",
1406 GNUNET_free (expfn);
1411 ret = GNUNET_malloc (sizeof (struct GNUNET_DISK_FileHandle));
1414 ret->type = GNUNET_DISK_FILE;
1418 GNUNET_free (expfn);
1424 * Close an open file
1425 * @param h file handle
1426 * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
1429 GNUNET_DISK_file_close (struct GNUNET_DISK_FileHandle *h)
1434 return GNUNET_SYSERR;
1438 if (!CloseHandle (h->h))
1440 SetErrnoFromWinError (GetLastError ());
1441 GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "close");
1442 GNUNET_free (h->oOverlapRead);
1443 GNUNET_free (h->oOverlapWrite);
1445 return GNUNET_SYSERR;
1448 if (close (h->fd) != 0)
1450 GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "close");
1452 return GNUNET_SYSERR;
1461 * Construct full path to a file inside of the private
1462 * directory used by GNUnet. Also creates the corresponding
1463 * directory. If the resulting name is supposed to be
1464 * a directory, end the last argument in '/' (or pass
1465 * DIR_SEPARATOR_STR as the last argument before NULL).
1467 * @param cfg configuration to use (determines HOME)
1468 * @param serviceName name of the service
1469 * @param ... is NULL-terminated list of
1470 * path components to append to the
1471 * private directory name.
1472 * @return the constructed filename
1475 GNUNET_DISK_get_home_filename (const struct GNUNET_CONFIGURATION_Handle *cfg,
1476 const char *serviceName, ...)
1482 unsigned int needed;
1485 GNUNET_CONFIGURATION_get_value_filename (cfg,
1486 serviceName, "HOME", &pfx))
1490 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1491 _("No `%s' specified for service `%s' in configuration.\n"),
1492 "HOME", serviceName);
1495 needed = strlen (pfx) + 2;
1496 if ((pfx[strlen (pfx) - 1] != '/') && (pfx[strlen (pfx) - 1] != '\\'))
1498 va_start (ap, serviceName);
1501 c = va_arg (ap, const char *);
1504 needed += strlen (c);
1505 if ((c[strlen (c) - 1] != '/') && (c[strlen (c) - 1] != '\\'))
1509 ret = GNUNET_malloc (needed);
1512 va_start (ap, serviceName);
1515 c = va_arg (ap, const char *);
1518 if ((c[strlen (c) - 1] != '/') && (c[strlen (c) - 1] != '\\'))
1519 strcat (ret, DIR_SEPARATOR_STR);
1523 if ((ret[strlen (ret) - 1] != '/') && (ret[strlen (ret) - 1] != '\\'))
1524 (void) GNUNET_DISK_directory_create_for_file (ret);
1526 (void) GNUNET_DISK_directory_create (ret);
1532 * Handle for a memory-mapping operation.
1534 struct GNUNET_DISK_MapHandle
1537 * Address where the map is in memory.
1543 * Underlying OS handle.
1548 * Number of bytes mapped.
1556 #define MAP_FAILED ((void *) -1)
1560 * Map a file into memory
1562 * @param h open file handle
1563 * @param m handle to the new mapping
1564 * @param access access specification, GNUNET_DISK_MAP_TYPE_xxx
1565 * @param len size of the mapping
1566 * @return pointer to the mapped memory region, NULL on failure
1569 GNUNET_DISK_file_map (const struct GNUNET_DISK_FileHandle *h,
1570 struct GNUNET_DISK_MapHandle **m,
1571 enum GNUNET_DISK_MapType access, size_t len)
1580 DWORD mapAccess, protect;
1582 if ((access & GNUNET_DISK_MAP_TYPE_READ) &&
1583 (access & GNUNET_DISK_MAP_TYPE_WRITE))
1585 protect = PAGE_READWRITE;
1586 mapAccess = FILE_MAP_ALL_ACCESS;
1588 else if (access & GNUNET_DISK_MAP_TYPE_READ)
1590 protect = PAGE_READONLY;
1591 mapAccess = FILE_MAP_READ;
1593 else if (access & GNUNET_DISK_MAP_TYPE_WRITE)
1595 protect = PAGE_READWRITE;
1596 mapAccess = FILE_MAP_WRITE;
1604 *m = GNUNET_malloc (sizeof (struct GNUNET_DISK_MapHandle));
1605 (*m)->h = CreateFileMapping (h->h, NULL, protect, 0, 0, NULL);
1606 if ((*m)->h == INVALID_HANDLE_VALUE)
1608 SetErrnoFromWinError (GetLastError ());
1613 (*m)->addr = MapViewOfFile ((*m)->h, mapAccess, 0, 0, len);
1616 SetErrnoFromWinError (GetLastError ());
1617 CloseHandle ((*m)->h);
1626 if (access & GNUNET_DISK_MAP_TYPE_READ)
1628 if (access & GNUNET_DISK_MAP_TYPE_WRITE)
1630 *m = GNUNET_malloc (sizeof (struct GNUNET_DISK_MapHandle));
1631 (*m)->addr = mmap (NULL, len, prot, MAP_SHARED, h->fd, 0);
1632 GNUNET_assert (NULL != (*m)->addr);
1633 if (MAP_FAILED == (*m)->addr)
1645 * @param h mapping handle
1646 * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
1649 GNUNET_DISK_file_unmap (struct GNUNET_DISK_MapHandle *h)
1655 return GNUNET_SYSERR;
1659 ret = UnmapViewOfFile (h->addr) ? GNUNET_OK : GNUNET_SYSERR;
1660 if (ret != GNUNET_OK)
1661 SetErrnoFromWinError (GetLastError ());
1662 if (!CloseHandle (h->h) && (ret == GNUNET_OK))
1664 ret = GNUNET_SYSERR;
1665 SetErrnoFromWinError (GetLastError ());
1668 ret = munmap (h->addr, h->len) != -1 ? GNUNET_OK : GNUNET_SYSERR;
1676 * Write file changes to disk
1677 * @param h handle to an open file
1678 * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
1681 GNUNET_DISK_file_sync (const struct GNUNET_DISK_FileHandle *h)
1686 return GNUNET_SYSERR;
1692 ret = FlushFileBuffers (h->h) ? GNUNET_OK : GNUNET_SYSERR;
1693 if (ret != GNUNET_OK)
1694 SetErrnoFromWinError (GetLastError ());
1696 #elif defined(FREEBSD) || defined(OPENBSD) || defined(DARWIN)
1697 return fsync (h->fd) == -1 ? GNUNET_SYSERR : GNUNET_OK;
1699 return fdatasync (h->fd) == -1 ? GNUNET_SYSERR : GNUNET_OK;
1704 /* Copyright Bob Byrnes <byrnes <at> curl.com>
1705 http://permalink.gmane.org/gmane.os.cygwin.patches/2121
1707 /* Create a pipe, and return handles to the read and write ends,
1708 just like CreatePipe, but ensure that the write end permits
1709 FILE_READ_ATTRIBUTES access, on later versions of win32 where
1710 this is supported. This access is needed by NtQueryInformationFile,
1711 which is used to implement select and nonblocking writes.
1712 Note that the return value is either NO_ERROR or GetLastError,
1713 unlike CreatePipe, which returns a bool for success or failure. */
1715 create_selectable_pipe (PHANDLE read_pipe_ptr,
1716 PHANDLE write_pipe_ptr,
1717 LPSECURITY_ATTRIBUTES sa_ptr,
1722 /* Default to error. */
1723 *read_pipe_ptr = *write_pipe_ptr = INVALID_HANDLE_VALUE;
1725 HANDLE read_pipe = INVALID_HANDLE_VALUE, write_pipe = INVALID_HANDLE_VALUE;
1727 /* Ensure that there is enough pipe buffer space for atomic writes. */
1728 if (psize < PIPE_BUF)
1731 char pipename[MAX_PATH];
1733 /* Retry CreateNamedPipe as long as the pipe name is in use.
1734 Retrying will probably never be necessary, but we want
1735 to be as robust as possible. */
1738 static volatile LONG pipe_unique_id;
1740 snprintf (pipename, sizeof pipename, "\\\\.\\pipe\\gnunet-%d-%ld",
1741 getpid (), InterlockedIncrement ((LONG *)&pipe_unique_id));
1743 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "CreateNamedPipe: name = %s, size = %lu\n", pipename, psize);
1745 /* Use CreateNamedPipe instead of CreatePipe, because the latter
1746 returns a write handle that does not permit FILE_READ_ATTRIBUTES
1747 access, on versions of win32 earlier than WinXP SP2.
1748 CreatePipe also stupidly creates a full duplex pipe, which is
1749 a waste, since only a single direction is actually used.
1750 It's important to only allow a single instance, to ensure that
1751 the pipe was not created earlier by some other process, even if
1752 the pid has been reused. We avoid FILE_FLAG_FIRST_PIPE_INSTANCE
1753 because that is only available for Win2k SP2 and WinXP. */
1754 read_pipe = CreateNamedPipeA (pipename,
1755 PIPE_ACCESS_INBOUND | dwReadMode,
1756 PIPE_TYPE_BYTE | PIPE_READMODE_BYTE,
1757 1, /* max instances */
1758 psize, /* output buffer size */
1759 psize, /* input buffer size */
1760 NMPWAIT_USE_DEFAULT_WAIT,
1763 if (read_pipe != INVALID_HANDLE_VALUE)
1766 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "pipe read handle = %p\n", read_pipe);
1771 DWORD err = GetLastError ();
1774 case ERROR_PIPE_BUSY:
1775 /* The pipe is already open with compatible parameters.
1776 Pick a new name and retry. */
1778 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "pipe busy, retrying\n");
1781 case ERROR_ACCESS_DENIED:
1782 /* The pipe is already open with incompatible parameters.
1783 Pick a new name and retry. */
1785 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "pipe access denied, retrying\n");
1788 case ERROR_CALL_NOT_IMPLEMENTED:
1789 /* We are on an older Win9x platform without named pipes.
1790 Return an anonymous pipe as the best approximation. */
1792 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "CreateNamedPipe not implemented, resorting to "
1793 "CreatePipe: size = %lu\n", psize);
1795 if (CreatePipe (read_pipe_ptr, write_pipe_ptr, sa_ptr, psize))
1798 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "pipe read handle = %p\n", *read_pipe_ptr);
1799 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "pipe write handle = %p\n", *write_pipe_ptr);
1803 err = GetLastError ();
1804 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "CreatePipe failed: %d\n", err);
1807 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "CreateNamedPipe failed: %d\n", err);
1813 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "CreateFile: name = %s\n", pipename);
1816 /* Open the named pipe for writing.
1817 Be sure to permit FILE_READ_ATTRIBUTES access. */
1818 write_pipe = CreateFileA (pipename,
1819 GENERIC_WRITE | FILE_READ_ATTRIBUTES,
1823 dwWriteMode, /* flags and attributes */
1824 0); /* handle to template file */
1826 if (write_pipe == INVALID_HANDLE_VALUE)
1829 DWORD err = GetLastError ();
1831 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "CreateFile failed: %d\n", err);
1833 CloseHandle (read_pipe);
1837 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "pipe write handle = %p\n", write_pipe);
1840 *read_pipe_ptr = read_pipe;
1841 *write_pipe_ptr = write_pipe;
1847 * Creates an interprocess channel
1849 * @param blocking creates an asynchronous pipe if set to GNUNET_NO
1850 * @param inherit_read inherit the parent processes stdin (only for windows)
1851 * @param inherit_write inherit the parent processes stdout (only for windows)
1853 * @return handle to the new pipe, NULL on error
1855 struct GNUNET_DISK_PipeHandle *
1856 GNUNET_DISK_pipe (int blocking, int inherit_read, int inherit_write)
1858 struct GNUNET_DISK_PipeHandle *p;
1859 struct GNUNET_DISK_FileHandle *fds;
1862 GNUNET_malloc (sizeof (struct GNUNET_DISK_PipeHandle) +
1863 2 * sizeof (struct GNUNET_DISK_FileHandle));
1864 fds = (struct GNUNET_DISK_FileHandle *) &p[1];
1877 GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "pipe");
1882 p->fd[0]->fd = fd[0];
1883 p->fd[1]->fd = fd[1];
1885 flags = fcntl (fd[0], F_GETFL);
1887 flags |= O_NONBLOCK;
1888 if (0 > fcntl (fd[0], F_SETFL, flags))
1890 flags = fcntl (fd[0], F_GETFD);
1891 flags |= FD_CLOEXEC;
1892 if (0 > fcntl (fd[0], F_SETFD, flags))
1895 flags = fcntl (fd[1], F_GETFL);
1897 flags |= O_NONBLOCK;
1898 if (0 > fcntl (fd[1], F_SETFL, flags))
1900 flags = fcntl (fd[1], F_GETFD);
1901 flags |= FD_CLOEXEC;
1902 if (0 > fcntl (fd[1], F_SETFD, flags))
1907 GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "fcntl");
1908 GNUNET_break (0 == close (p->fd[0]->fd));
1909 GNUNET_break (0 == close (p->fd[1]->fd));
1918 ret = create_selectable_pipe (&p->fd[0]->h, &p->fd[1]->h, NULL, 0, FILE_FLAG_OVERLAPPED, FILE_FLAG_OVERLAPPED);
1922 SetErrnoFromWinError (GetLastError ());
1925 if (!DuplicateHandle (GetCurrentProcess (), p->fd[0]->h,
1926 GetCurrentProcess (), &tmp_handle, 0, inherit_read == GNUNET_YES ? TRUE : FALSE,
1927 DUPLICATE_SAME_ACCESS))
1929 SetErrnoFromWinError (GetLastError ());
1930 CloseHandle (p->fd[0]->h);
1931 CloseHandle (p->fd[1]->h);
1935 CloseHandle (p->fd[0]->h);
1936 p->fd[0]->h = tmp_handle;
1938 if (!DuplicateHandle (GetCurrentProcess (), p->fd[1]->h,
1939 GetCurrentProcess (), &tmp_handle, 0, inherit_write == GNUNET_YES ? TRUE : FALSE,
1940 DUPLICATE_SAME_ACCESS))
1942 SetErrnoFromWinError (GetLastError ());
1943 CloseHandle (p->fd[0]->h);
1944 CloseHandle (p->fd[1]->h);
1948 CloseHandle (p->fd[1]->h);
1949 p->fd[1]->h = tmp_handle;
1955 SetNamedPipeHandleState (p->fd[0]->h, &mode, NULL, NULL);
1956 SetNamedPipeHandleState (p->fd[1]->h, &mode, NULL, NULL);
1957 /* this always fails on Windows 95, so we don't care about error handling */
1959 p->fd[0]->type = GNUNET_PIPE;
1960 p->fd[1]->type = GNUNET_PIPE;
1962 p->fd[0]->oOverlapRead = GNUNET_malloc (sizeof (OVERLAPPED));
1963 p->fd[0]->oOverlapWrite = GNUNET_malloc (sizeof (OVERLAPPED));
1964 p->fd[1]->oOverlapRead = GNUNET_malloc (sizeof (OVERLAPPED));
1965 p->fd[1]->oOverlapWrite = GNUNET_malloc (sizeof (OVERLAPPED));
1967 p->fd[0]->oOverlapRead->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
1968 p->fd[0]->oOverlapWrite->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
1970 p->fd[1]->oOverlapRead->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
1971 p->fd[1]->oOverlapWrite->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
1979 * Closes an interprocess channel
1981 * @param p pipe to close
1982 * @param end which end of the pipe to close
1983 * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
1986 GNUNET_DISK_pipe_close_end (struct GNUNET_DISK_PipeHandle *p,
1987 enum GNUNET_DISK_PipeEnd end)
1989 int ret = GNUNET_OK;
1993 if (end == GNUNET_DISK_PIPE_END_READ)
1995 if (!CloseHandle (p->fd[0]->h))
1997 SetErrnoFromWinError (GetLastError ());
1998 ret = GNUNET_SYSERR;
2000 p->fd[0]->h = INVALID_HANDLE_VALUE;
2002 else if (end == GNUNET_DISK_PIPE_END_WRITE)
2004 if (!CloseHandle (p->fd[1]->h))
2006 SetErrnoFromWinError (GetLastError ());
2007 ret = GNUNET_SYSERR;
2009 p->fd[1]->h = INVALID_HANDLE_VALUE;
2014 if (end == GNUNET_DISK_PIPE_END_READ)
2016 if (0 != close (p->fd[0]->fd))
2018 ret = GNUNET_SYSERR;
2023 else if (end == GNUNET_DISK_PIPE_END_WRITE)
2025 if (0 != close (p->fd[1]->fd))
2027 ret = GNUNET_SYSERR;
2038 * Closes an interprocess channel
2040 * @param p pipe to close
2041 * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
2044 GNUNET_DISK_pipe_close (struct GNUNET_DISK_PipeHandle *p)
2046 int ret = GNUNET_OK;
2050 if (!CloseHandle (p->fd[0]->h))
2052 SetErrnoFromWinError (GetLastError ());
2053 ret = GNUNET_SYSERR;
2055 if (!CloseHandle (p->fd[1]->h))
2057 SetErrnoFromWinError (GetLastError ());
2058 ret = GNUNET_SYSERR;
2063 if (p->fd[0]->fd != -1)
2065 if (0 != close (p->fd[0]->fd))
2067 ret = GNUNET_SYSERR;
2072 if (p->fd[1]->fd != -1)
2074 if (0 != close (p->fd[1]->fd))
2076 ret = GNUNET_SYSERR;
2088 * Creates a named pipe/FIFO and opens it
2089 * @param fn pointer to the name of the named pipe or to NULL
2090 * @param flags open flags
2091 * @param perm access permissions
2092 * @return pipe handle on success, NULL on error
2094 struct GNUNET_DISK_FileHandle *
2095 GNUNET_DISK_npipe_create (char **fn,
2096 enum GNUNET_DISK_OpenFlags flags,
2097 enum GNUNET_DISK_AccessPermissions perm)
2100 struct GNUNET_DISK_FileHandle *ret;
2106 if (flags & GNUNET_DISK_OPEN_READWRITE)
2107 openMode = PIPE_ACCESS_DUPLEX;
2108 else if (flags & GNUNET_DISK_OPEN_READ)
2109 openMode = PIPE_ACCESS_INBOUND;
2110 else if (flags & GNUNET_DISK_OPEN_WRITE)
2111 openMode = PIPE_ACCESS_OUTBOUND;
2113 if (flags & GNUNET_DISK_OPEN_FAILIFEXISTS)
2114 openMode |= FILE_FLAG_FIRST_PIPE_INSTANCE;
2122 GNUNET_asprintf(&name, "\\\\.\\pipe\\%.246s", fn);
2124 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Trying to create an instance of named pipe `%s'\n", name);
2126 h = CreateNamedPipe (name, openMode | FILE_FLAG_OVERLAPPED,
2127 PIPE_TYPE_BYTE | PIPE_READMODE_BYTE, 2, 1, 1, 0, NULL);
2131 GNUNET_asprintf(fn, "\\\\.\\pipe\\gnunet-%llu",
2132 GNUNET_CRYPTO_random_u64 (GNUNET_CRYPTO_QUALITY_WEAK, UINT64_MAX));
2134 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Trying to create unique named pipe `%s'\n", *fn);
2136 h = CreateNamedPipe (*fn, openMode | FILE_FLAG_OVERLAPPED | FILE_FLAG_FIRST_PIPE_INSTANCE,
2137 PIPE_TYPE_BYTE | PIPE_READMODE_BYTE, 2, 1, 1, 0, NULL);
2139 error_code = GetLastError ();
2142 /* don't re-set name to NULL yet */
2143 if (h == INVALID_HANDLE_VALUE)
2145 SetErrnoFromWinError(error_code);
2147 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Pipe creation have failed because of %d, errno is %d\n", error_code, errno);
2152 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Pipe was to be unique, considering re-creation\n");
2156 if (error_code != ERROR_ACCESS_DENIED && error_code != ERROR_PIPE_BUSY)
2161 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Pipe name was not unique, trying again\n");
2171 ret = GNUNET_malloc(sizeof(*ret));
2173 ret->type = GNUNET_PIPE;
2175 ret->oOverlapRead = GNUNET_malloc (sizeof (OVERLAPPED));
2176 ret->oOverlapWrite = GNUNET_malloc (sizeof (OVERLAPPED));
2178 ret->oOverlapRead->hEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
2179 ret->oOverlapWrite->hEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
2185 char dir[] = "/tmp/gnunet-pipe-XXXXXX";
2187 if (mkdtemp(dir) == NULL)
2189 GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "mkdtemp");
2192 GNUNET_asprintf(fn, "%s/child-control", dir);
2195 if (mkfifo(*fn, translate_unix_perms(perm)) == -1)
2197 if ( (errno != EEXIST) ||
2198 (0 != (flags & GNUNET_DISK_OPEN_FAILIFEXISTS)) )
2202 flags = flags & (~GNUNET_DISK_OPEN_FAILIFEXISTS);
2203 return GNUNET_DISK_file_open(*fn, flags, perm);
2209 * Opens already existing named pipe/FIFO
2211 * @param fn name of an existing named pipe
2212 * @param flags open flags
2213 * @param perm access permissions
2214 * @return pipe handle on success, NULL on error
2216 struct GNUNET_DISK_FileHandle *
2217 GNUNET_DISK_npipe_open (const char *fn,
2218 enum GNUNET_DISK_OpenFlags flags,
2219 enum GNUNET_DISK_AccessPermissions perm)
2222 struct GNUNET_DISK_FileHandle *ret;
2227 if (flags & GNUNET_DISK_OPEN_READWRITE)
2228 openMode = GENERIC_WRITE | GENERIC_READ;
2229 else if (flags & GNUNET_DISK_OPEN_READ)
2230 openMode = GENERIC_READ;
2231 else if (flags & GNUNET_DISK_OPEN_WRITE)
2232 openMode = GENERIC_WRITE;
2234 h = CreateFile (fn, openMode, 0, NULL, OPEN_EXISTING,
2235 FILE_FLAG_OVERLAPPED | FILE_READ_ATTRIBUTES, NULL);
2236 if (h == INVALID_HANDLE_VALUE)
2238 SetErrnoFromWinError(GetLastError());
2242 ret = GNUNET_malloc(sizeof(*ret));
2244 ret->type = GNUNET_PIPE;
2245 ret->oOverlapRead = GNUNET_malloc (sizeof (OVERLAPPED));
2246 ret->oOverlapWrite = GNUNET_malloc (sizeof (OVERLAPPED));
2247 ret->oOverlapRead->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2248 ret->oOverlapWrite->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2252 flags = flags & (~GNUNET_DISK_OPEN_FAILIFEXISTS);
2253 return GNUNET_DISK_file_open(fn, flags, perm);
2258 * Closes a named pipe/FIFO
2259 * @param pipe named pipe
2260 * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
2263 GNUNET_DISK_npipe_close (struct GNUNET_DISK_FileHandle *pipe)
2266 return close(pipe->fd) == 0 ? GNUNET_OK : GNUNET_SYSERR;
2270 ret = CloseHandle(pipe->h);
2273 SetErrnoFromWinError(GetLastError());
2274 return GNUNET_SYSERR;
2283 * Get the handle to a particular pipe end
2286 * @param n end to access
2287 * @return handle for the respective end
2289 const struct GNUNET_DISK_FileHandle *
2290 GNUNET_DISK_pipe_handle (const struct GNUNET_DISK_PipeHandle *p,
2291 enum GNUNET_DISK_PipeEnd n)
2295 case GNUNET_DISK_PIPE_END_READ:
2296 case GNUNET_DISK_PIPE_END_WRITE:
2306 * Retrieve OS file handle
2308 * @param fh GNUnet file descriptor
2309 * @param dst destination buffer
2310 * @param dst_len length of dst
2311 * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
2314 GNUNET_DISK_internal_file_handle_ (const struct GNUNET_DISK_FileHandle *fh,
2315 void *dst, size_t dst_len)
2318 if (dst_len < sizeof (HANDLE))
2319 return GNUNET_SYSERR;
2320 *((HANDLE *) dst) = fh->h;
2322 if (dst_len < sizeof (int))
2323 return GNUNET_SYSERR;
2324 *((int *) dst) = fh->fd;