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 * Move the read/write pointer in a file
211 * @param h handle of an open file
212 * @param offset position to move to
213 * @param whence specification to which position the offset parameter relates to
214 * @return the new position on success, GNUNET_SYSERR otherwise
217 GNUNET_DISK_file_seek (const struct GNUNET_DISK_FileHandle * h, off_t offset,
218 enum GNUNET_DISK_Seek whence)
223 return GNUNET_SYSERR;
229 static DWORD t[] = {[GNUNET_DISK_SEEK_SET] = FILE_BEGIN,
230 [GNUNET_DISK_SEEK_CUR] = FILE_CURRENT,[GNUNET_DISK_SEEK_END] = FILE_END
233 ret = SetFilePointer (h->h, offset, NULL, t[whence]);
234 if (ret == INVALID_SET_FILE_POINTER)
236 SetErrnoFromWinError (GetLastError ());
237 return GNUNET_SYSERR;
241 static int t[] = {[GNUNET_DISK_SEEK_SET] = SEEK_SET,
242 [GNUNET_DISK_SEEK_CUR] = SEEK_CUR,[GNUNET_DISK_SEEK_END] = SEEK_END
245 return lseek (h->fd, offset, t[whence]);
251 * Get the size of the file (or directory) of the given file (in
254 * @param filename name of the file or directory
255 * @param size set to the size of the file (or,
256 * in the case of directories, the sum
257 * of all sizes of files in the directory)
258 * @param includeSymLinks should symbolic links be
260 * @return GNUNET_SYSERR on error, GNUNET_OK on success
263 GNUNET_DISK_file_size (const char *filename, uint64_t * size,
266 struct GetFileSizeData gfsd;
269 GNUNET_assert (size != NULL);
271 gfsd.include_sym_links = includeSymLinks;
272 ret = getSizeRec (&gfsd, filename);
279 * Obtain some unique identifiers for the given file
280 * that can be used to identify it in the local system.
281 * This function is used between GNUnet processes to
282 * quickly check if two files with the same absolute path
283 * are actually identical. The two processes represent
284 * the same peer but may communicate over the network
285 * (and the file may be on an NFS volume). This function
286 * may not be supported on all operating systems.
288 * @param filename name of the file
289 * @param dev set to the device ID
290 * @param ino set to the inode ID
291 * @return GNUNET_OK on success
294 GNUNET_DISK_file_get_identifiers (const char *filename, uint64_t * dev,
301 if ((0 == stat (filename, &sbuf)) && (0 == statvfs (filename, &fbuf)))
303 *dev = (uint64_t) fbuf.f_fsid;
304 *ino = (uint64_t) sbuf.st_ino;
311 if ((0 == stat (filename, &sbuf)) && (0 == statfs (filename, &fbuf)))
313 *dev = ((uint64_t) fbuf.f_fsid.val[0]) << 32 ||
314 ((uint64_t) fbuf.f_fsid.val[1]);
315 *ino = (uint64_t) sbuf.st_ino;
319 // FIXME NILS: test this
320 struct GNUNET_DISK_FileHandle *fh;
321 BY_HANDLE_FILE_INFORMATION info;
324 fh = GNUNET_DISK_file_open (filename, GNUNET_DISK_OPEN_READ, 0);
326 return GNUNET_SYSERR;
327 succ = GetFileInformationByHandle (fh->h, &info);
328 GNUNET_DISK_file_close (fh);
331 *dev = info.dwVolumeSerialNumber;
332 *ino = ((info.nFileIndexHigh << sizeof (DWORD)) | info.nFileIndexLow);
336 return GNUNET_SYSERR;
339 return GNUNET_SYSERR;
344 * Create an (empty) temporary file on disk. If the given name is not
345 * an absolute path, the current 'TMPDIR' will be prepended. In any case,
346 * 6 random characters will be appended to the name to create a unique
349 * @param t component to use for the name;
350 * does NOT contain "XXXXXX" or "/tmp/".
351 * @return NULL on error, otherwise name of fresh
352 * file on disk in directory for temporary files
355 GNUNET_DISK_mktemp (const char *t)
362 if ((t[0] != '/') && (t[0] != '\\')
364 && !(isalpha ((int) t[0]) && (t[0] != '\0') && (t[1] == ':'))
368 tmpdir = getenv ("TMPDIR");
369 tmpdir = tmpdir ? tmpdir : "/tmp";
370 GNUNET_asprintf (&tmpl, "%s/%s%s", tmpdir, t, "XXXXXX");
374 GNUNET_asprintf (&tmpl, "%s%s", t, "XXXXXX");
377 fn = (char *) GNUNET_malloc (MAX_PATH + 1);
378 if (ERROR_SUCCESS != plibc_conv_to_win_path (tmpl, fn))
391 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_ERROR, "mkstemp", fn);
396 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "close", fn);
402 * Get the number of blocks that are left on the partition that
403 * contains the given file (for normal users).
405 * @param part a file on the partition to check
406 * @return -1 on errors, otherwise the number of free blocks
409 GNUNET_DISK_get_blocks_available (const char *part)
414 if (0 != statvfs (part, &buf))
416 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "statfs", part);
426 path = GNUNET_STRINGS_filename_expand (part);
429 memcpy (szDrive, path, 3);
432 if (!GetDiskFreeSpace (szDrive, &dwDummy, &dwDummy, &dwBlocks, &dwDummy))
434 LOG (GNUNET_ERROR_TYPE_WARNING,
435 _("`%s' failed for drive `%s': %u\n"), "GetDiskFreeSpace",
436 szDrive, GetLastError ());
444 if (0 != statfs (part, &s))
446 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "statfs", part);
455 * Test if "fil" is a directory.
456 * Will not print an error message if the directory
457 * does not exist. Will log errors if GNUNET_SYSERR is
458 * returned (i.e., a file exists with the same name).
460 * @param fil filename to test
461 * @return GNUNET_YES if yes, GNUNET_NO if not, GNUNET_SYSERR if it
465 GNUNET_DISK_directory_test (const char *fil)
467 struct stat filestat;
470 ret = STAT (fil, &filestat);
475 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "stat", fil);
476 return GNUNET_SYSERR;
480 if (!S_ISDIR (filestat.st_mode))
482 if (ACCESS (fil, R_OK | X_OK) < 0)
484 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "access", fil);
485 return GNUNET_SYSERR;
491 * Check that fil corresponds to a filename
492 * (of a file that exists and that is not a directory).
494 * @param fil filename to check
495 * @return GNUNET_YES if yes, GNUNET_NO if not a file, GNUNET_SYSERR if something
496 * else (will print an error message in that case, too).
499 GNUNET_DISK_file_test (const char *fil)
501 struct stat filestat;
505 rdir = GNUNET_STRINGS_filename_expand (fil);
507 return GNUNET_SYSERR;
509 ret = STAT (rdir, &filestat);
514 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "stat", rdir);
516 return GNUNET_SYSERR;
521 if (!S_ISREG (filestat.st_mode))
526 if (ACCESS (rdir, R_OK) < 0)
528 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "access", rdir);
530 return GNUNET_SYSERR;
538 * Implementation of "mkdir -p"
539 * @param dir the directory to create
540 * @returns GNUNET_OK on success, GNUNET_SYSERR on failure
543 GNUNET_DISK_directory_create (const char *dir)
550 rdir = GNUNET_STRINGS_filename_expand (dir);
552 return GNUNET_SYSERR;
556 pos = 1; /* skip heading '/' */
558 /* Local or Network path? */
559 if (strncmp (rdir, "\\\\", 2) == 0)
564 if (rdir[pos] == '\\')
574 pos = 3; /* strlen("C:\\") */
579 if ((rdir[pos] == DIR_SEPARATOR) || (pos == len))
582 ret = GNUNET_DISK_directory_test (rdir);
583 if (ret == GNUNET_SYSERR)
586 return GNUNET_SYSERR;
588 if (ret == GNUNET_NO)
591 ret = mkdir (rdir, S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH); /* 755 */
595 if ((ret != 0) && (errno != EEXIST))
597 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_ERROR, "mkdir", rdir);
599 return GNUNET_SYSERR;
602 rdir[pos] = DIR_SEPARATOR;
612 * Create the directory structure for storing
615 * @param filename name of a file in the directory
616 * @returns GNUNET_OK on success,
617 * GNUNET_SYSERR on failure,
618 * GNUNET_NO if the directory
619 * exists but is not writeable for us
622 GNUNET_DISK_directory_create_for_file (const char *filename)
628 rdir = GNUNET_STRINGS_filename_expand (filename);
630 return GNUNET_SYSERR;
632 while ((len > 0) && (rdir[len] != DIR_SEPARATOR))
635 ret = GNUNET_DISK_directory_create (rdir);
636 if ((ret == GNUNET_OK) && (0 != ACCESS (rdir, W_OK)))
644 * Read the contents of a binary file into a buffer.
645 * @param h handle to an open file
646 * @param result the buffer to write the result to
647 * @param len the maximum number of bytes to read
648 * @return the number of bytes read on success, GNUNET_SYSERR on failure
651 GNUNET_DISK_file_read (const struct GNUNET_DISK_FileHandle * h, void *result,
657 return GNUNET_SYSERR;
663 if (h->type != GNUNET_PIPE)
665 if (!ReadFile (h->h, result, len, &bytesRead, NULL))
667 SetErrnoFromWinError (GetLastError ());
668 return GNUNET_SYSERR;
673 if (!ReadFile (h->h, result, len, NULL, h->oOverlapRead))
675 if (GetLastError () != ERROR_IO_PENDING)
677 SetErrnoFromWinError (GetLastError ());
678 return GNUNET_SYSERR;
681 GetOverlappedResult (h->h, h->oOverlapRead, &bytesRead, TRUE);
685 return read (h->fd, result, len);
691 * Read the contents of a binary file into a buffer.
693 * @param fn file name
694 * @param result the buffer to write the result to
695 * @param len the maximum number of bytes to read
696 * @return number of bytes read, GNUNET_SYSERR on failure
699 GNUNET_DISK_fn_read (const char *fn, void *result, size_t len)
701 struct GNUNET_DISK_FileHandle *fh;
705 GNUNET_DISK_file_open (fn, GNUNET_DISK_OPEN_READ, GNUNET_DISK_PERM_NONE);
707 return GNUNET_SYSERR;
708 ret = GNUNET_DISK_file_read (fh, result, len);
709 GNUNET_assert (GNUNET_OK == GNUNET_DISK_file_close (fh));
716 * Write a buffer to a file.
717 * @param h handle to open file
718 * @param buffer the data to write
719 * @param n number of bytes to write
720 * @return number of bytes written on success, GNUNET_SYSERR on error
723 GNUNET_DISK_file_write (const struct GNUNET_DISK_FileHandle * h,
724 const void *buffer, size_t n)
729 return GNUNET_SYSERR;
735 if (h->type != GNUNET_PIPE)
737 if (!WriteFile (h->h, buffer, n, &bytesWritten, NULL))
739 SetErrnoFromWinError (GetLastError ());
740 return GNUNET_SYSERR;
746 LOG (GNUNET_ERROR_TYPE_DEBUG, "It is a pipe trying to write\n");
748 if (!WriteFile (h->h, buffer, n, NULL, h->oOverlapWrite))
750 if (GetLastError () != ERROR_IO_PENDING)
752 SetErrnoFromWinError (GetLastError ());
754 LOG (GNUNET_ERROR_TYPE_DEBUG, "Error writing to pipe\n");
756 return GNUNET_SYSERR;
760 LOG (GNUNET_ERROR_TYPE_DEBUG, "Will get overlapped result\n");
762 GetOverlappedResult (h->h, h->oOverlapWrite, &bytesWritten, TRUE);
766 return write (h->fd, buffer, n);
771 * Write a buffer to a file. If the file is longer than the
772 * number of bytes that will be written, it will be truncated.
774 * @param fn file name
775 * @param buffer the data to write
776 * @param n number of bytes to write
777 * @param mode file permissions
778 * @return number of bytes written on success, GNUNET_SYSERR on error
781 GNUNET_DISK_fn_write (const char *fn, const void *buffer, size_t n,
782 enum GNUNET_DISK_AccessPermissions mode)
784 struct GNUNET_DISK_FileHandle *fh;
787 fh = GNUNET_DISK_file_open (fn,
788 GNUNET_DISK_OPEN_WRITE |
789 GNUNET_DISK_OPEN_TRUNCATE |
790 GNUNET_DISK_OPEN_CREATE, mode);
792 return GNUNET_SYSERR;
793 ret = GNUNET_DISK_file_write (fh, buffer, n);
794 GNUNET_assert (GNUNET_OK == GNUNET_DISK_file_close (fh));
800 * Scan a directory for files.
802 * @param dirName the name of the directory
803 * @param callback the method to call for each file,
804 * can be NULL, in that case, we only count
805 * @param callback_cls closure for callback
806 * @return the number of files found, GNUNET_SYSERR on error or
807 * ieration aborted by callback returning GNUNET_SYSERR
810 GNUNET_DISK_directory_scan (const char *dirName,
811 GNUNET_FileNameCallback callback,
815 struct dirent *finfo;
820 unsigned int name_len;
823 GNUNET_assert (dirName != NULL);
824 dname = GNUNET_STRINGS_filename_expand (dirName);
826 return GNUNET_SYSERR;
827 while ((strlen (dname) > 0) && (dname[strlen (dname) - 1] == DIR_SEPARATOR))
828 dname[strlen (dname) - 1] = '\0';
829 if (0 != STAT (dname, &istat))
831 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "stat", dname);
833 return GNUNET_SYSERR;
835 if (!S_ISDIR (istat.st_mode))
837 LOG (GNUNET_ERROR_TYPE_WARNING,
838 _("Expected `%s' to be a directory!\n"), dirName);
840 return GNUNET_SYSERR;
843 dinfo = OPENDIR (dname);
844 if ((errno == EACCES) || (dinfo == NULL))
846 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "opendir", dname);
850 return GNUNET_SYSERR;
853 n_size = strlen (dname) + name_len + 2;
854 name = GNUNET_malloc (n_size);
855 while ((finfo = readdir (dinfo)) != NULL)
857 if ((0 == strcmp (finfo->d_name, ".")) ||
858 (0 == strcmp (finfo->d_name, "..")))
860 if (callback != NULL)
862 if (name_len < strlen (finfo->d_name))
865 name_len = strlen (finfo->d_name);
866 n_size = strlen (dname) + name_len + 2;
867 name = GNUNET_malloc (n_size);
869 /* dname can end in "/" only if dname == "/";
870 * if dname does not end in "/", we need to add
871 * a "/" (otherwise, we must not!) */
872 GNUNET_snprintf (name, n_size, "%s%s%s", dname,
873 (strcmp (dname, DIR_SEPARATOR_STR) ==
874 0) ? "" : DIR_SEPARATOR_STR, finfo->d_name);
875 if (GNUNET_OK != callback (callback_cls, name))
880 return GNUNET_SYSERR;
893 * Opaque handle used for iterating over a directory.
895 struct GNUNET_DISK_DirectoryIterator
899 * Function to call on directory entries.
901 GNUNET_DISK_DirectoryIteratorCallback callback;
904 * Closure for callback.
909 * Reference to directory.
919 * Next filename to process.
926 enum GNUNET_SCHEDULER_Priority priority;
932 * Task used by the directory iterator.
935 directory_iterator_task (void *cls,
936 const struct GNUNET_SCHEDULER_TaskContext *tc)
938 struct GNUNET_DISK_DirectoryIterator *iter = cls;
941 name = iter->next_name;
942 GNUNET_assert (name != NULL);
943 iter->next_name = NULL;
944 iter->callback (iter->callback_cls, iter, name, iter->dirname);
950 * This function must be called during the DiskIteratorCallback
951 * (exactly once) to schedule the task to process the next
952 * filename in the directory (if there is one).
954 * @param iter opaque handle for the iterator
955 * @param can set to GNUNET_YES to terminate the iteration early
956 * @return GNUNET_YES if iteration will continue,
957 * GNUNET_NO if this was the last entry (and iteration is complete),
958 * GNUNET_SYSERR if abort was YES
961 GNUNET_DISK_directory_iterator_next (struct GNUNET_DISK_DirectoryIterator
964 struct dirent *finfo;
966 GNUNET_assert (iter->next_name == NULL);
967 if (can == GNUNET_YES)
969 closedir (iter->directory);
970 GNUNET_free (iter->dirname);
972 return GNUNET_SYSERR;
974 while (NULL != (finfo = readdir (iter->directory)))
976 if ((0 == strcmp (finfo->d_name, ".")) ||
977 (0 == strcmp (finfo->d_name, "..")))
979 GNUNET_asprintf (&iter->next_name, "%s%s%s", iter->dirname,
980 DIR_SEPARATOR_STR, finfo->d_name);
985 GNUNET_DISK_directory_iterator_next (iter, GNUNET_YES);
988 GNUNET_SCHEDULER_add_with_priority (iter->priority,
989 &directory_iterator_task, iter);
995 * Scan a directory for files using the scheduler to run a task for
996 * each entry. The name of the directory must be expanded first (!).
997 * If a scheduler does not need to be used, GNUNET_DISK_directory_scan
998 * may provide a simpler API.
1000 * @param prio priority to use
1001 * @param dirName the name of the directory
1002 * @param callback the method to call for each file
1003 * @param callback_cls closure for callback
1006 GNUNET_DISK_directory_iterator_start (enum GNUNET_SCHEDULER_Priority prio,
1007 const char *dirName,
1008 GNUNET_DISK_DirectoryIteratorCallback
1009 callback, void *callback_cls)
1011 struct GNUNET_DISK_DirectoryIterator *di;
1013 di = GNUNET_malloc (sizeof (struct GNUNET_DISK_DirectoryIterator));
1014 di->callback = callback;
1015 di->callback_cls = callback_cls;
1016 di->directory = OPENDIR (dirName);
1017 if (di->directory == NULL)
1020 callback (callback_cls, NULL, NULL, NULL);
1023 di->dirname = GNUNET_strdup (dirName);
1024 di->priority = prio;
1025 GNUNET_DISK_directory_iterator_next (di, GNUNET_NO);
1030 * Function that removes the given directory by calling
1031 * "GNUNET_DISK_directory_remove".
1033 * @param unused not used
1034 * @param fn directory to remove
1038 remove_helper (void *unused, const char *fn)
1040 (void) GNUNET_DISK_directory_remove (fn);
1046 * Remove all files in a directory (rm -rf). Call with
1050 * @param fileName the file to remove
1051 * @return GNUNET_OK on success, GNUNET_SYSERR on error
1054 GNUNET_DISK_directory_remove (const char *fileName)
1058 if (0 != LSTAT (fileName, &istat))
1059 return GNUNET_NO; /* file may not exist... */
1060 CHMOD (fileName, S_IWUSR | S_IRUSR | S_IXUSR);
1061 if (UNLINK (fileName) == 0)
1063 if ((errno != EISDIR) &&
1064 /* EISDIR is not sufficient in all cases, e.g.
1065 * sticky /tmp directory may result in EPERM on BSD.
1066 * So we also explicitly check "isDirectory" */
1067 (GNUNET_YES != GNUNET_DISK_directory_test (fileName)))
1069 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "rmdir", fileName);
1070 return GNUNET_SYSERR;
1072 if (GNUNET_SYSERR ==
1073 GNUNET_DISK_directory_scan (fileName, &remove_helper, NULL))
1074 return GNUNET_SYSERR;
1075 if (0 != RMDIR (fileName))
1077 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "rmdir", fileName);
1078 return GNUNET_SYSERR;
1087 * @param src file to copy
1088 * @param dst destination file name
1089 * @return GNUNET_OK on success, GNUNET_SYSERR on error
1092 GNUNET_DISK_file_copy (const char *src, const char *dst)
1098 struct GNUNET_DISK_FileHandle *in;
1099 struct GNUNET_DISK_FileHandle *out;
1101 if (GNUNET_OK != GNUNET_DISK_file_size (src, &size, GNUNET_YES))
1102 return GNUNET_SYSERR;
1104 in = GNUNET_DISK_file_open (src, GNUNET_DISK_OPEN_READ,
1105 GNUNET_DISK_PERM_NONE);
1107 return GNUNET_SYSERR;
1109 GNUNET_DISK_file_open (dst,
1110 GNUNET_DISK_OPEN_WRITE | GNUNET_DISK_OPEN_CREATE |
1111 GNUNET_DISK_OPEN_FAILIFEXISTS,
1112 GNUNET_DISK_PERM_USER_READ |
1113 GNUNET_DISK_PERM_USER_WRITE |
1114 GNUNET_DISK_PERM_GROUP_READ |
1115 GNUNET_DISK_PERM_GROUP_WRITE);
1118 GNUNET_DISK_file_close (in);
1119 return GNUNET_SYSERR;
1121 buf = GNUNET_malloc (COPY_BLK_SIZE);
1124 len = COPY_BLK_SIZE;
1125 if (len > size - pos)
1127 if (len != GNUNET_DISK_file_read (in, buf, len))
1129 if (len != GNUNET_DISK_file_write (out, buf, len))
1134 GNUNET_DISK_file_close (in);
1135 GNUNET_DISK_file_close (out);
1139 GNUNET_DISK_file_close (in);
1140 GNUNET_DISK_file_close (out);
1141 return GNUNET_SYSERR;
1146 * @brief Removes special characters as ':' from a filename.
1147 * @param fn the filename to canonicalize
1150 GNUNET_DISK_filename_canonicalize (char *fn)
1160 if (c == '/' || c == '\\' || c == ':' || c == '*' || c == '?'
1161 || c == '"' || c == '<' || c == '>' || c == '|')
1173 * @brief Change owner of a file
1175 * @param filename name of file to change the owner of
1176 * @param user name of the new owner
1177 * @return GNUNET_OK on success, GNUNET_SYSERR on failure
1180 GNUNET_DISK_file_change_owner (const char *filename, const char *user)
1185 pws = getpwnam (user);
1188 LOG (GNUNET_ERROR_TYPE_ERROR,
1189 _("Cannot obtain information about user `%s': %s\n"), user,
1191 return GNUNET_SYSERR;
1193 if (0 != chown (filename, pws->pw_uid, pws->pw_gid))
1194 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "chown", filename);
1201 * Lock a part of a file
1202 * @param fh file handle
1203 * @param lockStart absolute position from where to lock
1204 * @param lockEnd absolute position until where to lock
1205 * @param excl GNUNET_YES for an exclusive lock
1206 * @return GNUNET_OK on success, GNUNET_SYSERR on error
1209 GNUNET_DISK_file_lock (struct GNUNET_DISK_FileHandle *fh, off_t lockStart,
1210 off_t lockEnd, int excl)
1215 return GNUNET_SYSERR;
1221 memset (&fl, 0, sizeof (struct flock));
1222 fl.l_type = excl ? F_WRLCK : F_RDLCK;
1223 fl.l_whence = SEEK_SET;
1224 fl.l_start = lockStart;
1227 return fcntl (fh->fd, F_SETLK, &fl) != 0 ? GNUNET_SYSERR : GNUNET_OK;
1231 memset (&o, 0, sizeof (OVERLAPPED));
1232 o.Offset = lockStart;
1236 (excl ? LOCKFILE_EXCLUSIVE_LOCK : 0) | LOCKFILE_FAIL_IMMEDIATELY, 0,
1237 lockEnd - lockStart, 0, &o))
1239 SetErrnoFromWinError (GetLastError ());
1240 return GNUNET_SYSERR;
1249 * Unlock a part of a file
1250 * @param fh file handle
1251 * @param unlockStart absolute position from where to unlock
1252 * @param unlockEnd absolute position until where to unlock
1253 * @return GNUNET_OK on success, GNUNET_SYSERR on error
1256 GNUNET_DISK_file_unlock (struct GNUNET_DISK_FileHandle *fh, off_t unlockStart,
1262 return GNUNET_SYSERR;
1268 memset (&fl, 0, sizeof (struct flock));
1269 fl.l_type = F_UNLCK;
1270 fl.l_whence = SEEK_SET;
1271 fl.l_start = unlockStart;
1272 fl.l_len = unlockEnd;
1274 return fcntl (fh->fd, F_SETLK, &fl) != 0 ? GNUNET_SYSERR : GNUNET_OK;
1278 memset (&o, 0, sizeof (OVERLAPPED));
1279 o.Offset = unlockStart;
1281 if (!UnlockFileEx (fh->h, 0, unlockEnd - unlockStart, 0, &o))
1283 SetErrnoFromWinError (GetLastError ());
1284 return GNUNET_SYSERR;
1293 * Open a file. Note that the access permissions will only be
1294 * used if a new file is created and if the underlying operating
1295 * system supports the given permissions.
1297 * @param fn file name to be opened
1298 * @param flags opening flags, a combination of GNUNET_DISK_OPEN_xxx bit flags
1299 * @param perm permissions for the newly created file, use
1300 * GNUNET_DISK_PERM_USER_NONE if a file could not be created by this
1301 * call (because of flags)
1302 * @return IO handle on success, NULL on error
1304 struct GNUNET_DISK_FileHandle *
1305 GNUNET_DISK_file_open (const char *fn, enum GNUNET_DISK_OpenFlags flags,
1306 enum GNUNET_DISK_AccessPermissions perm)
1309 struct GNUNET_DISK_FileHandle *ret;
1321 expfn = GNUNET_STRINGS_filename_expand (fn);
1326 if (GNUNET_DISK_OPEN_READWRITE == (flags & GNUNET_DISK_OPEN_READWRITE))
1327 oflags = O_RDWR; /* note: O_RDWR is NOT always O_RDONLY | O_WRONLY */
1328 else if (flags & GNUNET_DISK_OPEN_READ)
1330 else if (flags & GNUNET_DISK_OPEN_WRITE)
1335 GNUNET_free (expfn);
1338 if (flags & GNUNET_DISK_OPEN_FAILIFEXISTS)
1339 oflags |= (O_CREAT | O_EXCL);
1340 if (flags & GNUNET_DISK_OPEN_TRUNCATE)
1342 if (flags & GNUNET_DISK_OPEN_APPEND)
1344 if (flags & GNUNET_DISK_OPEN_CREATE)
1346 (void) GNUNET_DISK_directory_create_for_file (expfn);
1348 mode = translate_unix_perms (perm);
1351 fd = open (expfn, oflags | O_LARGEFILE, mode);
1354 if (0 == (flags & GNUNET_DISK_OPEN_FAILIFEXISTS))
1355 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "open", expfn);
1357 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_DEBUG, "open", expfn);
1358 GNUNET_free (expfn);
1365 if (GNUNET_DISK_OPEN_READWRITE == (flags & GNUNET_DISK_OPEN_READWRITE))
1366 access = FILE_READ_DATA | FILE_WRITE_DATA;
1367 else if (flags & GNUNET_DISK_OPEN_READ)
1368 access = FILE_READ_DATA;
1369 else if (flags & GNUNET_DISK_OPEN_WRITE)
1370 access = FILE_WRITE_DATA;
1372 if (flags & GNUNET_DISK_OPEN_FAILIFEXISTS)
1376 else if (flags & GNUNET_DISK_OPEN_CREATE)
1378 (void) GNUNET_DISK_directory_create_for_file (expfn);
1379 if (flags & GNUNET_DISK_OPEN_TRUNCATE)
1380 disp = CREATE_ALWAYS;
1384 else if (flags & GNUNET_DISK_OPEN_TRUNCATE)
1386 disp = TRUNCATE_EXISTING;
1390 disp = OPEN_EXISTING;
1393 /* TODO: access priviledges? */
1394 h = CreateFile (expfn, access,
1395 FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE,
1396 NULL, disp, FILE_ATTRIBUTE_NORMAL, NULL);
1397 if (h == INVALID_HANDLE_VALUE)
1399 SetErrnoFromWinError (GetLastError ());
1400 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "open", expfn);
1401 GNUNET_free (expfn);
1405 if (flags & GNUNET_DISK_OPEN_APPEND)
1406 if (SetFilePointer (h, 0, 0, FILE_END) == INVALID_SET_FILE_POINTER)
1408 SetErrnoFromWinError (GetLastError ());
1409 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "SetFilePointer",
1412 GNUNET_free (expfn);
1417 ret = GNUNET_malloc (sizeof (struct GNUNET_DISK_FileHandle));
1420 ret->type = GNUNET_DISK_FILE;
1424 GNUNET_free (expfn);
1430 * Close an open file
1431 * @param h file handle
1432 * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
1435 GNUNET_DISK_file_close (struct GNUNET_DISK_FileHandle *h)
1440 return GNUNET_SYSERR;
1444 if (!CloseHandle (h->h))
1446 SetErrnoFromWinError (GetLastError ());
1447 LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING, "close");
1448 GNUNET_free (h->oOverlapRead);
1449 GNUNET_free (h->oOverlapWrite);
1451 return GNUNET_SYSERR;
1454 if (close (h->fd) != 0)
1456 LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING, "close");
1458 return GNUNET_SYSERR;
1467 * Construct full path to a file inside of the private
1468 * directory used by GNUnet. Also creates the corresponding
1469 * directory. If the resulting name is supposed to be
1470 * a directory, end the last argument in '/' (or pass
1471 * DIR_SEPARATOR_STR as the last argument before NULL).
1473 * @param cfg configuration to use (determines HOME)
1474 * @param serviceName name of the service
1475 * @param ... is NULL-terminated list of
1476 * path components to append to the
1477 * private directory name.
1478 * @return the constructed filename
1481 GNUNET_DISK_get_home_filename (const struct GNUNET_CONFIGURATION_Handle *cfg,
1482 const char *serviceName, ...)
1488 unsigned int needed;
1491 GNUNET_CONFIGURATION_get_value_filename (cfg, serviceName, "HOME",
1496 LOG (GNUNET_ERROR_TYPE_WARNING,
1497 _("No `%s' specified for service `%s' in configuration.\n"),
1498 "HOME", serviceName);
1501 needed = strlen (pfx) + 2;
1502 if ((pfx[strlen (pfx) - 1] != '/') && (pfx[strlen (pfx) - 1] != '\\'))
1504 va_start (ap, serviceName);
1507 c = va_arg (ap, const char *);
1511 needed += strlen (c);
1512 if ((c[strlen (c) - 1] != '/') && (c[strlen (c) - 1] != '\\'))
1516 ret = GNUNET_malloc (needed);
1519 va_start (ap, serviceName);
1522 c = va_arg (ap, const char *);
1526 if ((c[strlen (c) - 1] != '/') && (c[strlen (c) - 1] != '\\'))
1527 strcat (ret, DIR_SEPARATOR_STR);
1531 if ((ret[strlen (ret) - 1] != '/') && (ret[strlen (ret) - 1] != '\\'))
1532 (void) GNUNET_DISK_directory_create_for_file (ret);
1534 (void) GNUNET_DISK_directory_create (ret);
1540 * Handle for a memory-mapping operation.
1542 struct GNUNET_DISK_MapHandle
1545 * Address where the map is in memory.
1551 * Underlying OS handle.
1556 * Number of bytes mapped.
1564 #define MAP_FAILED ((void *) -1)
1568 * Map a file into memory
1570 * @param h open file handle
1571 * @param m handle to the new mapping
1572 * @param access access specification, GNUNET_DISK_MAP_TYPE_xxx
1573 * @param len size of the mapping
1574 * @return pointer to the mapped memory region, NULL on failure
1577 GNUNET_DISK_file_map (const struct GNUNET_DISK_FileHandle *h,
1578 struct GNUNET_DISK_MapHandle **m,
1579 enum GNUNET_DISK_MapType access, size_t len)
1588 DWORD mapAccess, protect;
1590 if ((access & GNUNET_DISK_MAP_TYPE_READ) &&
1591 (access & GNUNET_DISK_MAP_TYPE_WRITE))
1593 protect = PAGE_READWRITE;
1594 mapAccess = FILE_MAP_ALL_ACCESS;
1596 else if (access & GNUNET_DISK_MAP_TYPE_READ)
1598 protect = PAGE_READONLY;
1599 mapAccess = FILE_MAP_READ;
1601 else if (access & GNUNET_DISK_MAP_TYPE_WRITE)
1603 protect = PAGE_READWRITE;
1604 mapAccess = FILE_MAP_WRITE;
1612 *m = GNUNET_malloc (sizeof (struct GNUNET_DISK_MapHandle));
1613 (*m)->h = CreateFileMapping (h->h, NULL, protect, 0, 0, NULL);
1614 if ((*m)->h == INVALID_HANDLE_VALUE)
1616 SetErrnoFromWinError (GetLastError ());
1621 (*m)->addr = MapViewOfFile ((*m)->h, mapAccess, 0, 0, len);
1624 SetErrnoFromWinError (GetLastError ());
1625 CloseHandle ((*m)->h);
1634 if (access & GNUNET_DISK_MAP_TYPE_READ)
1636 if (access & GNUNET_DISK_MAP_TYPE_WRITE)
1638 *m = GNUNET_malloc (sizeof (struct GNUNET_DISK_MapHandle));
1639 (*m)->addr = mmap (NULL, len, prot, MAP_SHARED, h->fd, 0);
1640 GNUNET_assert (NULL != (*m)->addr);
1641 if (MAP_FAILED == (*m)->addr)
1653 * @param h mapping handle
1654 * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
1657 GNUNET_DISK_file_unmap (struct GNUNET_DISK_MapHandle *h)
1664 return GNUNET_SYSERR;
1668 ret = UnmapViewOfFile (h->addr) ? GNUNET_OK : GNUNET_SYSERR;
1669 if (ret != GNUNET_OK)
1670 SetErrnoFromWinError (GetLastError ());
1671 if (!CloseHandle (h->h) && (ret == GNUNET_OK))
1673 ret = GNUNET_SYSERR;
1674 SetErrnoFromWinError (GetLastError ());
1677 ret = munmap (h->addr, h->len) != -1 ? GNUNET_OK : GNUNET_SYSERR;
1685 * Write file changes to disk
1686 * @param h handle to an open file
1687 * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
1690 GNUNET_DISK_file_sync (const struct GNUNET_DISK_FileHandle *h)
1695 return GNUNET_SYSERR;
1701 ret = FlushFileBuffers (h->h) ? GNUNET_OK : GNUNET_SYSERR;
1702 if (ret != GNUNET_OK)
1703 SetErrnoFromWinError (GetLastError ());
1705 #elif defined(FREEBSD) || defined(OPENBSD) || defined(DARWIN)
1706 return fsync (h->fd) == -1 ? GNUNET_SYSERR : GNUNET_OK;
1708 return fdatasync (h->fd) == -1 ? GNUNET_SYSERR : GNUNET_OK;
1713 /* Copyright Bob Byrnes <byrnes <at> curl.com>
1714 http://permalink.gmane.org/gmane.os.cygwin.patches/2121
1716 /* Create a pipe, and return handles to the read and write ends,
1717 just like CreatePipe, but ensure that the write end permits
1718 FILE_READ_ATTRIBUTES access, on later versions of win32 where
1719 this is supported. This access is needed by NtQueryInformationFile,
1720 which is used to implement select and nonblocking writes.
1721 Note that the return value is either NO_ERROR or GetLastError,
1722 unlike CreatePipe, which returns a bool for success or failure. */
1724 create_selectable_pipe (PHANDLE read_pipe_ptr, PHANDLE write_pipe_ptr,
1725 LPSECURITY_ATTRIBUTES sa_ptr, DWORD psize,
1726 DWORD dwReadMode, DWORD dwWriteMode)
1728 /* Default to error. */
1729 *read_pipe_ptr = *write_pipe_ptr = INVALID_HANDLE_VALUE;
1731 HANDLE read_pipe = INVALID_HANDLE_VALUE, write_pipe = INVALID_HANDLE_VALUE;
1733 /* Ensure that there is enough pipe buffer space for atomic writes. */
1734 if (psize < PIPE_BUF)
1737 char pipename[MAX_PATH];
1739 /* Retry CreateNamedPipe as long as the pipe name is in use.
1740 * Retrying will probably never be necessary, but we want
1741 * to be as robust as possible. */
1744 static volatile LONG pipe_unique_id;
1746 snprintf (pipename, sizeof pipename, "\\\\.\\pipe\\gnunet-%d-%ld",
1747 getpid (), InterlockedIncrement ((LONG *) & pipe_unique_id));
1749 LOG (GNUNET_ERROR_TYPE_DEBUG,
1750 "CreateNamedPipe: name = %s, size = %lu\n", pipename, psize);
1752 /* Use CreateNamedPipe instead of CreatePipe, because the latter
1753 * returns a write handle that does not permit FILE_READ_ATTRIBUTES
1754 * access, on versions of win32 earlier than WinXP SP2.
1755 * CreatePipe also stupidly creates a full duplex pipe, which is
1756 * a waste, since only a single direction is actually used.
1757 * It's important to only allow a single instance, to ensure that
1758 * the pipe was not created earlier by some other process, even if
1759 * the pid has been reused. We avoid FILE_FLAG_FIRST_PIPE_INSTANCE
1760 * because that is only available for Win2k SP2 and WinXP. */
1761 read_pipe = CreateNamedPipeA (pipename, PIPE_ACCESS_INBOUND | dwReadMode, PIPE_TYPE_BYTE | PIPE_READMODE_BYTE, 1, /* max instances */
1762 psize, /* output buffer size */
1763 psize, /* input buffer size */
1764 NMPWAIT_USE_DEFAULT_WAIT, sa_ptr);
1766 if (read_pipe != INVALID_HANDLE_VALUE)
1769 LOG (GNUNET_ERROR_TYPE_DEBUG, "pipe read handle = %p\n", read_pipe);
1774 DWORD err = GetLastError ();
1778 case ERROR_PIPE_BUSY:
1779 /* The pipe is already open with compatible parameters.
1780 * Pick a new name and retry. */
1782 LOG (GNUNET_ERROR_TYPE_DEBUG, "pipe busy, retrying\n");
1785 case ERROR_ACCESS_DENIED:
1786 /* The pipe is already open with incompatible parameters.
1787 * Pick a new name and retry. */
1789 LOG (GNUNET_ERROR_TYPE_DEBUG, "pipe access denied, retrying\n");
1792 case ERROR_CALL_NOT_IMPLEMENTED:
1793 /* We are on an older Win9x platform without named pipes.
1794 * Return an anonymous pipe as the best approximation. */
1796 LOG (GNUNET_ERROR_TYPE_DEBUG,
1797 "CreateNamedPipe not implemented, resorting to "
1798 "CreatePipe: size = %lu\n", psize);
1800 if (CreatePipe (read_pipe_ptr, write_pipe_ptr, sa_ptr, psize))
1803 LOG (GNUNET_ERROR_TYPE_DEBUG, "pipe read handle = %p\n",
1805 LOG (GNUNET_ERROR_TYPE_DEBUG, "pipe write handle = %p\n",
1810 err = GetLastError ();
1811 LOG (GNUNET_ERROR_TYPE_ERROR, "CreatePipe failed: %d\n", err);
1814 LOG (GNUNET_ERROR_TYPE_ERROR, "CreateNamedPipe failed: %d\n", err);
1820 LOG (GNUNET_ERROR_TYPE_DEBUG, "CreateFile: name = %s\n", pipename);
1823 /* Open the named pipe for writing.
1824 * Be sure to permit FILE_READ_ATTRIBUTES access. */
1825 write_pipe = CreateFileA (pipename, GENERIC_WRITE | FILE_READ_ATTRIBUTES, 0, /* share mode */
1826 sa_ptr, OPEN_EXISTING, dwWriteMode, /* flags and attributes */
1827 0); /* handle to template file */
1829 if (write_pipe == INVALID_HANDLE_VALUE)
1832 DWORD err = GetLastError ();
1835 LOG (GNUNET_ERROR_TYPE_DEBUG, "CreateFile failed: %d\n", err);
1837 CloseHandle (read_pipe);
1841 LOG (GNUNET_ERROR_TYPE_DEBUG, "pipe write handle = %p\n", write_pipe);
1844 *read_pipe_ptr = read_pipe;
1845 *write_pipe_ptr = write_pipe;
1851 * Creates an interprocess channel
1853 * @param blocking creates an asynchronous pipe if set to GNUNET_NO
1854 * @param inherit_read inherit the parent processes stdin (only for windows)
1855 * @param inherit_write inherit the parent processes stdout (only for windows)
1857 * @return handle to the new pipe, NULL on error
1859 struct GNUNET_DISK_PipeHandle *
1860 GNUNET_DISK_pipe (int blocking, int inherit_read, int inherit_write)
1862 struct GNUNET_DISK_PipeHandle *p;
1863 struct GNUNET_DISK_FileHandle *fds;
1865 p = GNUNET_malloc (sizeof (struct GNUNET_DISK_PipeHandle) +
1866 2 * sizeof (struct GNUNET_DISK_FileHandle));
1867 fds = (struct GNUNET_DISK_FileHandle *) &p[1];
1880 LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR, "pipe");
1885 p->fd[0]->fd = fd[0];
1886 p->fd[1]->fd = fd[1];
1888 flags = fcntl (fd[0], F_GETFL);
1890 flags |= O_NONBLOCK;
1891 if (0 > fcntl (fd[0], F_SETFL, flags))
1893 flags = fcntl (fd[0], F_GETFD);
1894 flags |= FD_CLOEXEC;
1895 if (0 > fcntl (fd[0], F_SETFD, flags))
1898 flags = fcntl (fd[1], F_GETFL);
1900 flags |= O_NONBLOCK;
1901 if (0 > fcntl (fd[1], F_SETFL, flags))
1903 flags = fcntl (fd[1], F_GETFD);
1904 flags |= FD_CLOEXEC;
1905 if (0 > fcntl (fd[1], F_SETFD, flags))
1910 LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR, "fcntl");
1911 GNUNET_break (0 == close (p->fd[0]->fd));
1912 GNUNET_break (0 == close (p->fd[1]->fd));
1922 create_selectable_pipe (&p->fd[0]->h, &p->fd[1]->h, NULL, 0,
1923 FILE_FLAG_OVERLAPPED, FILE_FLAG_OVERLAPPED);
1927 SetErrnoFromWinError (GetLastError ());
1930 if (!DuplicateHandle
1931 (GetCurrentProcess (), p->fd[0]->h, GetCurrentProcess (), &tmp_handle,
1932 0, inherit_read == GNUNET_YES ? TRUE : FALSE, DUPLICATE_SAME_ACCESS))
1934 SetErrnoFromWinError (GetLastError ());
1935 CloseHandle (p->fd[0]->h);
1936 CloseHandle (p->fd[1]->h);
1940 CloseHandle (p->fd[0]->h);
1941 p->fd[0]->h = tmp_handle;
1943 if (!DuplicateHandle
1944 (GetCurrentProcess (), p->fd[1]->h, GetCurrentProcess (), &tmp_handle,
1945 0, inherit_write == GNUNET_YES ? TRUE : FALSE, DUPLICATE_SAME_ACCESS))
1947 SetErrnoFromWinError (GetLastError ());
1948 CloseHandle (p->fd[0]->h);
1949 CloseHandle (p->fd[1]->h);
1953 CloseHandle (p->fd[1]->h);
1954 p->fd[1]->h = tmp_handle;
1960 SetNamedPipeHandleState (p->fd[0]->h, &mode, NULL, NULL);
1961 SetNamedPipeHandleState (p->fd[1]->h, &mode, NULL, NULL);
1962 /* this always fails on Windows 95, so we don't care about error handling */
1964 p->fd[0]->type = GNUNET_PIPE;
1965 p->fd[1]->type = GNUNET_PIPE;
1967 p->fd[0]->oOverlapRead = GNUNET_malloc (sizeof (OVERLAPPED));
1968 p->fd[0]->oOverlapWrite = GNUNET_malloc (sizeof (OVERLAPPED));
1969 p->fd[1]->oOverlapRead = GNUNET_malloc (sizeof (OVERLAPPED));
1970 p->fd[1]->oOverlapWrite = GNUNET_malloc (sizeof (OVERLAPPED));
1972 p->fd[0]->oOverlapRead->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
1973 p->fd[0]->oOverlapWrite->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
1975 p->fd[1]->oOverlapRead->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
1976 p->fd[1]->oOverlapWrite->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
1984 * Closes an interprocess channel
1986 * @param p pipe to close
1987 * @param end which end of the pipe to close
1988 * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
1991 GNUNET_DISK_pipe_close_end (struct GNUNET_DISK_PipeHandle *p,
1992 enum GNUNET_DISK_PipeEnd end)
1994 int ret = GNUNET_OK;
1998 if (end == GNUNET_DISK_PIPE_END_READ)
2000 if (!CloseHandle (p->fd[0]->h))
2002 SetErrnoFromWinError (GetLastError ());
2003 ret = GNUNET_SYSERR;
2005 p->fd[0]->h = INVALID_HANDLE_VALUE;
2007 else if (end == GNUNET_DISK_PIPE_END_WRITE)
2009 if (!CloseHandle (p->fd[1]->h))
2011 SetErrnoFromWinError (GetLastError ());
2012 ret = GNUNET_SYSERR;
2014 p->fd[1]->h = INVALID_HANDLE_VALUE;
2019 if (end == GNUNET_DISK_PIPE_END_READ)
2021 if (0 != close (p->fd[0]->fd))
2023 ret = GNUNET_SYSERR;
2028 else if (end == GNUNET_DISK_PIPE_END_WRITE)
2030 if (0 != close (p->fd[1]->fd))
2032 ret = GNUNET_SYSERR;
2043 * Closes an interprocess channel
2045 * @param p pipe to close
2046 * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
2049 GNUNET_DISK_pipe_close (struct GNUNET_DISK_PipeHandle *p)
2051 int ret = GNUNET_OK;
2055 if (!CloseHandle (p->fd[0]->h))
2057 SetErrnoFromWinError (GetLastError ());
2058 ret = GNUNET_SYSERR;
2060 if (!CloseHandle (p->fd[1]->h))
2062 SetErrnoFromWinError (GetLastError ());
2063 ret = GNUNET_SYSERR;
2068 if (p->fd[0]->fd != -1)
2070 if (0 != close (p->fd[0]->fd))
2072 ret = GNUNET_SYSERR;
2077 if (p->fd[1]->fd != -1)
2079 if (0 != close (p->fd[1]->fd))
2081 ret = GNUNET_SYSERR;
2093 * Creates a named pipe/FIFO and opens it
2094 * @param fn pointer to the name of the named pipe or to NULL
2095 * @param flags open flags
2096 * @param perm access permissions
2097 * @return pipe handle on success, NULL on error
2099 struct GNUNET_DISK_FileHandle *
2100 GNUNET_DISK_npipe_create (char **fn, enum GNUNET_DISK_OpenFlags flags,
2101 enum GNUNET_DISK_AccessPermissions perm)
2104 struct GNUNET_DISK_FileHandle *ret;
2110 if (flags & GNUNET_DISK_OPEN_READWRITE)
2111 openMode = PIPE_ACCESS_DUPLEX;
2112 else if (flags & GNUNET_DISK_OPEN_READ)
2113 openMode = PIPE_ACCESS_INBOUND;
2114 else if (flags & GNUNET_DISK_OPEN_WRITE)
2115 openMode = PIPE_ACCESS_OUTBOUND;
2117 if (flags & GNUNET_DISK_OPEN_FAILIFEXISTS)
2118 openMode |= FILE_FLAG_FIRST_PIPE_INSTANCE;
2127 GNUNET_asprintf (&name, "\\\\.\\pipe\\%.246s", fn);
2129 LOG (GNUNET_ERROR_TYPE_DEBUG,
2130 "Trying to create an instance of named pipe `%s'\n", name);
2132 h = CreateNamedPipe (name, openMode | FILE_FLAG_OVERLAPPED,
2133 PIPE_TYPE_BYTE | PIPE_READMODE_BYTE, 2, 1, 1,
2138 GNUNET_asprintf (fn, "\\\\.\\pipe\\gnunet-%llu",
2139 GNUNET_CRYPTO_random_u64
2140 (GNUNET_CRYPTO_QUALITY_WEAK, UINT64_MAX));
2142 LOG (GNUNET_ERROR_TYPE_DEBUG,
2143 "Trying to create unique named pipe `%s'\n", *fn);
2145 h = CreateNamedPipe (*fn,
2146 openMode | FILE_FLAG_OVERLAPPED |
2147 FILE_FLAG_FIRST_PIPE_INSTANCE,
2148 PIPE_TYPE_BYTE | PIPE_READMODE_BYTE, 2, 1, 1,
2151 error_code = GetLastError ();
2154 /* don't re-set name to NULL yet */
2155 if (h == INVALID_HANDLE_VALUE)
2157 SetErrnoFromWinError (error_code);
2159 LOG (GNUNET_ERROR_TYPE_DEBUG,
2160 "Pipe creation have failed because of %d, errno is %d\n",
2166 LOG (GNUNET_ERROR_TYPE_DEBUG,
2167 "Pipe was to be unique, considering re-creation\n");
2171 if (error_code != ERROR_ACCESS_DENIED
2172 && error_code != ERROR_PIPE_BUSY)
2177 LOG (GNUNET_ERROR_TYPE_DEBUG,
2178 "Pipe name was not unique, trying again\n");
2188 ret = GNUNET_malloc (sizeof (*ret));
2190 ret->type = GNUNET_PIPE;
2192 ret->oOverlapRead = GNUNET_malloc (sizeof (OVERLAPPED));
2193 ret->oOverlapWrite = GNUNET_malloc (sizeof (OVERLAPPED));
2195 ret->oOverlapRead->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2196 ret->oOverlapWrite->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2202 char dir[] = "/tmp/gnunet-pipe-XXXXXX";
2204 if (mkdtemp (dir) == NULL)
2206 LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR, "mkdtemp");
2209 GNUNET_asprintf (fn, "%s/child-control", dir);
2212 if (mkfifo (*fn, translate_unix_perms (perm)) == -1)
2214 if ((errno != EEXIST) || (0 != (flags & GNUNET_DISK_OPEN_FAILIFEXISTS)))
2218 flags = flags & (~GNUNET_DISK_OPEN_FAILIFEXISTS);
2219 return GNUNET_DISK_file_open (*fn, flags, perm);
2225 * Opens already existing named pipe/FIFO
2227 * @param fn name of an existing named pipe
2228 * @param flags open flags
2229 * @param perm access permissions
2230 * @return pipe handle on success, NULL on error
2232 struct GNUNET_DISK_FileHandle *
2233 GNUNET_DISK_npipe_open (const char *fn, enum GNUNET_DISK_OpenFlags flags,
2234 enum GNUNET_DISK_AccessPermissions perm)
2237 struct GNUNET_DISK_FileHandle *ret;
2242 if (flags & GNUNET_DISK_OPEN_READWRITE)
2243 openMode = GENERIC_WRITE | GENERIC_READ;
2244 else if (flags & GNUNET_DISK_OPEN_READ)
2245 openMode = GENERIC_READ;
2246 else if (flags & GNUNET_DISK_OPEN_WRITE)
2247 openMode = GENERIC_WRITE;
2249 h = CreateFile (fn, openMode, 0, NULL, OPEN_EXISTING,
2250 FILE_FLAG_OVERLAPPED | FILE_READ_ATTRIBUTES, NULL);
2251 if (h == INVALID_HANDLE_VALUE)
2253 SetErrnoFromWinError (GetLastError ());
2257 ret = GNUNET_malloc (sizeof (*ret));
2259 ret->type = GNUNET_PIPE;
2260 ret->oOverlapRead = GNUNET_malloc (sizeof (OVERLAPPED));
2261 ret->oOverlapWrite = GNUNET_malloc (sizeof (OVERLAPPED));
2262 ret->oOverlapRead->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2263 ret->oOverlapWrite->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2267 flags = flags & (~GNUNET_DISK_OPEN_FAILIFEXISTS);
2268 return GNUNET_DISK_file_open (fn, flags, perm);
2273 * Closes a named pipe/FIFO
2274 * @param pipe named pipe
2275 * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
2278 GNUNET_DISK_npipe_close (struct GNUNET_DISK_FileHandle *pipe)
2281 return close (pipe->fd) == 0 ? GNUNET_OK : GNUNET_SYSERR;
2285 ret = CloseHandle (pipe->h);
2288 SetErrnoFromWinError (GetLastError ());
2289 return GNUNET_SYSERR;
2298 * Get the handle to a particular pipe end
2301 * @param n end to access
2302 * @return handle for the respective end
2304 const struct GNUNET_DISK_FileHandle *
2305 GNUNET_DISK_pipe_handle (const struct GNUNET_DISK_PipeHandle *p,
2306 enum GNUNET_DISK_PipeEnd n)
2310 case GNUNET_DISK_PIPE_END_READ:
2311 case GNUNET_DISK_PIPE_END_WRITE:
2321 * Retrieve OS file handle
2323 * @param fh GNUnet file descriptor
2324 * @param dst destination buffer
2325 * @param dst_len length of dst
2326 * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
2329 GNUNET_DISK_internal_file_handle_ (const struct GNUNET_DISK_FileHandle *fh,
2330 void *dst, size_t dst_len)
2333 if (dst_len < sizeof (HANDLE))
2334 return GNUNET_SYSERR;
2335 *((HANDLE *) dst) = fh->h;
2337 if (dst_len < sizeof (int))
2338 return GNUNET_SYSERR;
2339 *((int *) dst) = fh->fd;