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, _("`%s' failed for drive `%s': %u\n"),
435 "GetDiskFreeSpace", szDrive, GetLastError ());
443 if (0 != statfs (part, &s))
445 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "statfs", part);
454 * Test if "fil" is a directory.
455 * Will not print an error message if the directory
456 * does not exist. Will log errors if GNUNET_SYSERR is
457 * returned (i.e., a file exists with the same name).
459 * @param fil filename to test
460 * @return GNUNET_YES if yes, GNUNET_NO if not, GNUNET_SYSERR if it
464 GNUNET_DISK_directory_test (const char *fil)
466 struct stat filestat;
469 ret = STAT (fil, &filestat);
474 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "stat", fil);
475 return GNUNET_SYSERR;
479 if (!S_ISDIR (filestat.st_mode))
481 if (ACCESS (fil, R_OK | X_OK) < 0)
483 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "access", fil);
484 return GNUNET_SYSERR;
490 * Check that fil corresponds to a filename
491 * (of a file that exists and that is not a directory).
493 * @param fil filename to check
494 * @return GNUNET_YES if yes, GNUNET_NO if not a file, GNUNET_SYSERR if something
495 * else (will print an error message in that case, too).
498 GNUNET_DISK_file_test (const char *fil)
500 struct stat filestat;
504 rdir = GNUNET_STRINGS_filename_expand (fil);
506 return GNUNET_SYSERR;
508 ret = STAT (rdir, &filestat);
513 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "stat", rdir);
515 return GNUNET_SYSERR;
520 if (!S_ISREG (filestat.st_mode))
525 if (ACCESS (rdir, R_OK) < 0)
527 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "access", rdir);
529 return GNUNET_SYSERR;
537 * Implementation of "mkdir -p"
538 * @param dir the directory to create
539 * @returns GNUNET_OK on success, GNUNET_SYSERR on failure
542 GNUNET_DISK_directory_create (const char *dir)
549 rdir = GNUNET_STRINGS_filename_expand (dir);
551 return GNUNET_SYSERR;
555 pos = 1; /* skip heading '/' */
557 /* Local or Network path? */
558 if (strncmp (rdir, "\\\\", 2) == 0)
563 if (rdir[pos] == '\\')
573 pos = 3; /* strlen("C:\\") */
578 if ((rdir[pos] == DIR_SEPARATOR) || (pos == len))
581 ret = GNUNET_DISK_directory_test (rdir);
582 if (ret == GNUNET_SYSERR)
585 return GNUNET_SYSERR;
587 if (ret == GNUNET_NO)
590 ret = mkdir (rdir, S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH); /* 755 */
594 if ((ret != 0) && (errno != EEXIST))
596 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_ERROR, "mkdir", rdir);
598 return GNUNET_SYSERR;
601 rdir[pos] = DIR_SEPARATOR;
611 * Create the directory structure for storing
614 * @param filename name of a file in the directory
615 * @returns GNUNET_OK on success,
616 * GNUNET_SYSERR on failure,
617 * GNUNET_NO if the directory
618 * exists but is not writeable for us
621 GNUNET_DISK_directory_create_for_file (const char *filename)
627 rdir = GNUNET_STRINGS_filename_expand (filename);
629 return GNUNET_SYSERR;
631 while ((len > 0) && (rdir[len] != DIR_SEPARATOR))
634 ret = GNUNET_DISK_directory_create (rdir);
635 if ((ret == GNUNET_OK) && (0 != ACCESS (rdir, W_OK)))
643 * Read the contents of a binary file into a buffer.
644 * @param h handle to an open file
645 * @param result the buffer to write the result to
646 * @param len the maximum number of bytes to read
647 * @return the number of bytes read on success, GNUNET_SYSERR on failure
650 GNUNET_DISK_file_read (const struct GNUNET_DISK_FileHandle * h, void *result,
656 return GNUNET_SYSERR;
662 if (h->type != GNUNET_PIPE)
664 if (!ReadFile (h->h, result, len, &bytesRead, NULL))
666 SetErrnoFromWinError (GetLastError ());
667 return GNUNET_SYSERR;
672 if (!ReadFile (h->h, result, len, NULL, h->oOverlapRead))
674 if (GetLastError () != ERROR_IO_PENDING)
676 SetErrnoFromWinError (GetLastError ());
677 return GNUNET_SYSERR;
680 GetOverlappedResult (h->h, h->oOverlapRead, &bytesRead, TRUE);
684 return read (h->fd, result, len);
690 * Read the contents of a binary file into a buffer.
692 * @param fn file name
693 * @param result the buffer to write the result to
694 * @param len the maximum number of bytes to read
695 * @return number of bytes read, GNUNET_SYSERR on failure
698 GNUNET_DISK_fn_read (const char *fn, void *result, size_t len)
700 struct GNUNET_DISK_FileHandle *fh;
703 fh = GNUNET_DISK_file_open (fn, GNUNET_DISK_OPEN_READ, GNUNET_DISK_PERM_NONE);
705 return GNUNET_SYSERR;
706 ret = GNUNET_DISK_file_read (fh, result, len);
707 GNUNET_assert (GNUNET_OK == GNUNET_DISK_file_close (fh));
714 * Write a buffer to a file.
715 * @param h handle to open file
716 * @param buffer the data to write
717 * @param n number of bytes to write
718 * @return number of bytes written on success, GNUNET_SYSERR on error
721 GNUNET_DISK_file_write (const struct GNUNET_DISK_FileHandle * h,
722 const void *buffer, size_t n)
727 return GNUNET_SYSERR;
733 if (h->type != GNUNET_PIPE)
735 if (!WriteFile (h->h, buffer, n, &bytesWritten, NULL))
737 SetErrnoFromWinError (GetLastError ());
738 return GNUNET_SYSERR;
744 LOG (GNUNET_ERROR_TYPE_DEBUG, "It is a pipe trying to write\n");
746 if (!WriteFile (h->h, buffer, n, NULL, h->oOverlapWrite))
748 if (GetLastError () != ERROR_IO_PENDING)
750 SetErrnoFromWinError (GetLastError ());
752 LOG (GNUNET_ERROR_TYPE_DEBUG, "Error writing to pipe\n");
754 return GNUNET_SYSERR;
758 LOG (GNUNET_ERROR_TYPE_DEBUG, "Will get overlapped result\n");
760 GetOverlappedResult (h->h, h->oOverlapWrite, &bytesWritten, TRUE);
764 return write (h->fd, buffer, n);
769 * Write a buffer to a file. If the file is longer than the
770 * number of bytes that will be written, it will be truncated.
772 * @param fn file name
773 * @param buffer the data to write
774 * @param n number of bytes to write
775 * @param mode file permissions
776 * @return number of bytes written on success, GNUNET_SYSERR on error
779 GNUNET_DISK_fn_write (const char *fn, const void *buffer, size_t n,
780 enum GNUNET_DISK_AccessPermissions mode)
782 struct GNUNET_DISK_FileHandle *fh;
785 fh = GNUNET_DISK_file_open (fn,
786 GNUNET_DISK_OPEN_WRITE | GNUNET_DISK_OPEN_TRUNCATE
787 | GNUNET_DISK_OPEN_CREATE, mode);
789 return GNUNET_SYSERR;
790 ret = GNUNET_DISK_file_write (fh, buffer, n);
791 GNUNET_assert (GNUNET_OK == GNUNET_DISK_file_close (fh));
797 * Scan a directory for files.
799 * @param dirName the name of the directory
800 * @param callback the method to call for each file,
801 * can be NULL, in that case, we only count
802 * @param callback_cls closure for callback
803 * @return the number of files found, GNUNET_SYSERR on error or
804 * ieration aborted by callback returning GNUNET_SYSERR
807 GNUNET_DISK_directory_scan (const char *dirName,
808 GNUNET_FileNameCallback callback,
812 struct dirent *finfo;
817 unsigned int name_len;
820 GNUNET_assert (dirName != NULL);
821 dname = GNUNET_STRINGS_filename_expand (dirName);
823 return GNUNET_SYSERR;
824 while ((strlen (dname) > 0) && (dname[strlen (dname) - 1] == DIR_SEPARATOR))
825 dname[strlen (dname) - 1] = '\0';
826 if (0 != STAT (dname, &istat))
828 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "stat", dname);
830 return GNUNET_SYSERR;
832 if (!S_ISDIR (istat.st_mode))
834 LOG (GNUNET_ERROR_TYPE_WARNING, _("Expected `%s' to be a directory!\n"),
837 return GNUNET_SYSERR;
840 dinfo = OPENDIR (dname);
841 if ((errno == EACCES) || (dinfo == NULL))
843 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "opendir", dname);
847 return GNUNET_SYSERR;
850 n_size = strlen (dname) + name_len + 2;
851 name = GNUNET_malloc (n_size);
852 while ((finfo = readdir (dinfo)) != NULL)
854 if ((0 == strcmp (finfo->d_name, ".")) ||
855 (0 == strcmp (finfo->d_name, "..")))
857 if (callback != NULL)
859 if (name_len < strlen (finfo->d_name))
862 name_len = strlen (finfo->d_name);
863 n_size = strlen (dname) + name_len + 2;
864 name = GNUNET_malloc (n_size);
866 /* dname can end in "/" only if dname == "/";
867 * if dname does not end in "/", we need to add
868 * a "/" (otherwise, we must not!) */
869 GNUNET_snprintf (name, n_size, "%s%s%s", dname,
870 (strcmp (dname, DIR_SEPARATOR_STR) ==
871 0) ? "" : DIR_SEPARATOR_STR, finfo->d_name);
872 if (GNUNET_OK != callback (callback_cls, name))
877 return GNUNET_SYSERR;
890 * Opaque handle used for iterating over a directory.
892 struct GNUNET_DISK_DirectoryIterator
896 * Function to call on directory entries.
898 GNUNET_DISK_DirectoryIteratorCallback callback;
901 * Closure for callback.
906 * Reference to directory.
916 * Next filename to process.
923 enum GNUNET_SCHEDULER_Priority priority;
929 * Task used by the directory iterator.
932 directory_iterator_task (void *cls,
933 const struct GNUNET_SCHEDULER_TaskContext *tc)
935 struct GNUNET_DISK_DirectoryIterator *iter = cls;
938 name = iter->next_name;
939 GNUNET_assert (name != NULL);
940 iter->next_name = NULL;
941 iter->callback (iter->callback_cls, iter, name, iter->dirname);
947 * This function must be called during the DiskIteratorCallback
948 * (exactly once) to schedule the task to process the next
949 * filename in the directory (if there is one).
951 * @param iter opaque handle for the iterator
952 * @param can set to GNUNET_YES to terminate the iteration early
953 * @return GNUNET_YES if iteration will continue,
954 * GNUNET_NO if this was the last entry (and iteration is complete),
955 * GNUNET_SYSERR if abort was YES
958 GNUNET_DISK_directory_iterator_next (struct GNUNET_DISK_DirectoryIterator *iter,
961 struct dirent *finfo;
963 GNUNET_assert (iter->next_name == NULL);
964 if (can == GNUNET_YES)
966 closedir (iter->directory);
967 GNUNET_free (iter->dirname);
969 return GNUNET_SYSERR;
971 while (NULL != (finfo = readdir (iter->directory)))
973 if ((0 == strcmp (finfo->d_name, ".")) ||
974 (0 == strcmp (finfo->d_name, "..")))
976 GNUNET_asprintf (&iter->next_name, "%s%s%s", iter->dirname,
977 DIR_SEPARATOR_STR, finfo->d_name);
982 GNUNET_DISK_directory_iterator_next (iter, GNUNET_YES);
985 GNUNET_SCHEDULER_add_with_priority (iter->priority, &directory_iterator_task,
992 * Scan a directory for files using the scheduler to run a task for
993 * each entry. The name of the directory must be expanded first (!).
994 * If a scheduler does not need to be used, GNUNET_DISK_directory_scan
995 * may provide a simpler API.
997 * @param prio priority to use
998 * @param dirName the name of the directory
999 * @param callback the method to call for each file
1000 * @param callback_cls closure for callback
1003 GNUNET_DISK_directory_iterator_start (enum GNUNET_SCHEDULER_Priority prio,
1004 const char *dirName,
1005 GNUNET_DISK_DirectoryIteratorCallback
1006 callback, void *callback_cls)
1008 struct GNUNET_DISK_DirectoryIterator *di;
1010 di = GNUNET_malloc (sizeof (struct GNUNET_DISK_DirectoryIterator));
1011 di->callback = callback;
1012 di->callback_cls = callback_cls;
1013 di->directory = OPENDIR (dirName);
1014 if (di->directory == NULL)
1017 callback (callback_cls, NULL, NULL, NULL);
1020 di->dirname = GNUNET_strdup (dirName);
1021 di->priority = prio;
1022 GNUNET_DISK_directory_iterator_next (di, GNUNET_NO);
1027 * Function that removes the given directory by calling
1028 * "GNUNET_DISK_directory_remove".
1030 * @param unused not used
1031 * @param fn directory to remove
1035 remove_helper (void *unused, const char *fn)
1037 (void) GNUNET_DISK_directory_remove (fn);
1043 * Remove all files in a directory (rm -rf). Call with
1047 * @param fileName the file to remove
1048 * @return GNUNET_OK on success, GNUNET_SYSERR on error
1051 GNUNET_DISK_directory_remove (const char *fileName)
1055 if (0 != LSTAT (fileName, &istat))
1056 return GNUNET_NO; /* file may not exist... */
1057 CHMOD (fileName, S_IWUSR | S_IRUSR | S_IXUSR);
1058 if (UNLINK (fileName) == 0)
1060 if ((errno != EISDIR) &&
1061 /* EISDIR is not sufficient in all cases, e.g.
1062 * sticky /tmp directory may result in EPERM on BSD.
1063 * So we also explicitly check "isDirectory" */
1064 (GNUNET_YES != GNUNET_DISK_directory_test (fileName)))
1066 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "rmdir", fileName);
1067 return GNUNET_SYSERR;
1069 if (GNUNET_SYSERR ==
1070 GNUNET_DISK_directory_scan (fileName, &remove_helper, NULL))
1071 return GNUNET_SYSERR;
1072 if (0 != RMDIR (fileName))
1074 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "rmdir", fileName);
1075 return GNUNET_SYSERR;
1084 * @param src file to copy
1085 * @param dst destination file name
1086 * @return GNUNET_OK on success, GNUNET_SYSERR on error
1089 GNUNET_DISK_file_copy (const char *src, const char *dst)
1095 struct GNUNET_DISK_FileHandle *in;
1096 struct GNUNET_DISK_FileHandle *out;
1098 if (GNUNET_OK != GNUNET_DISK_file_size (src, &size, GNUNET_YES))
1099 return GNUNET_SYSERR;
1101 in = GNUNET_DISK_file_open (src, GNUNET_DISK_OPEN_READ,
1102 GNUNET_DISK_PERM_NONE);
1104 return GNUNET_SYSERR;
1106 GNUNET_DISK_file_open (dst,
1107 GNUNET_DISK_OPEN_WRITE | GNUNET_DISK_OPEN_CREATE |
1108 GNUNET_DISK_OPEN_FAILIFEXISTS,
1109 GNUNET_DISK_PERM_USER_READ |
1110 GNUNET_DISK_PERM_USER_WRITE |
1111 GNUNET_DISK_PERM_GROUP_READ |
1112 GNUNET_DISK_PERM_GROUP_WRITE);
1115 GNUNET_DISK_file_close (in);
1116 return GNUNET_SYSERR;
1118 buf = GNUNET_malloc (COPY_BLK_SIZE);
1121 len = COPY_BLK_SIZE;
1122 if (len > size - pos)
1124 if (len != GNUNET_DISK_file_read (in, buf, len))
1126 if (len != GNUNET_DISK_file_write (out, buf, len))
1131 GNUNET_DISK_file_close (in);
1132 GNUNET_DISK_file_close (out);
1136 GNUNET_DISK_file_close (in);
1137 GNUNET_DISK_file_close (out);
1138 return GNUNET_SYSERR;
1143 * @brief Removes special characters as ':' from a filename.
1144 * @param fn the filename to canonicalize
1147 GNUNET_DISK_filename_canonicalize (char *fn)
1157 if (c == '/' || c == '\\' || c == ':' || c == '*' || c == '?' || c == '"' ||
1158 c == '<' || c == '>' || c == '|')
1170 * @brief Change owner of a file
1172 * @param filename name of file to change the owner of
1173 * @param user name of the new owner
1174 * @return GNUNET_OK on success, GNUNET_SYSERR on failure
1177 GNUNET_DISK_file_change_owner (const char *filename, const char *user)
1182 pws = getpwnam (user);
1185 LOG (GNUNET_ERROR_TYPE_ERROR,
1186 _("Cannot obtain information about user `%s': %s\n"), user,
1188 return GNUNET_SYSERR;
1190 if (0 != chown (filename, pws->pw_uid, pws->pw_gid))
1191 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "chown", filename);
1198 * Lock a part of a file
1199 * @param fh file handle
1200 * @param lockStart absolute position from where to lock
1201 * @param lockEnd absolute position until where to lock
1202 * @param excl GNUNET_YES for an exclusive lock
1203 * @return GNUNET_OK on success, GNUNET_SYSERR on error
1206 GNUNET_DISK_file_lock (struct GNUNET_DISK_FileHandle *fh, off_t lockStart,
1207 off_t lockEnd, int excl)
1212 return GNUNET_SYSERR;
1218 memset (&fl, 0, sizeof (struct flock));
1219 fl.l_type = excl ? F_WRLCK : F_RDLCK;
1220 fl.l_whence = SEEK_SET;
1221 fl.l_start = lockStart;
1224 return fcntl (fh->fd, F_SETLK, &fl) != 0 ? GNUNET_SYSERR : GNUNET_OK;
1228 memset (&o, 0, sizeof (OVERLAPPED));
1229 o.Offset = lockStart;
1232 (fh->h, (excl ? LOCKFILE_EXCLUSIVE_LOCK : 0) | LOCKFILE_FAIL_IMMEDIATELY,
1233 0, lockEnd - lockStart, 0, &o))
1235 SetErrnoFromWinError (GetLastError ());
1236 return GNUNET_SYSERR;
1245 * Unlock a part of a file
1246 * @param fh file handle
1247 * @param unlockStart absolute position from where to unlock
1248 * @param unlockEnd absolute position until where to unlock
1249 * @return GNUNET_OK on success, GNUNET_SYSERR on error
1252 GNUNET_DISK_file_unlock (struct GNUNET_DISK_FileHandle *fh, off_t unlockStart,
1258 return GNUNET_SYSERR;
1264 memset (&fl, 0, sizeof (struct flock));
1265 fl.l_type = F_UNLCK;
1266 fl.l_whence = SEEK_SET;
1267 fl.l_start = unlockStart;
1268 fl.l_len = unlockEnd;
1270 return fcntl (fh->fd, F_SETLK, &fl) != 0 ? GNUNET_SYSERR : GNUNET_OK;
1274 memset (&o, 0, sizeof (OVERLAPPED));
1275 o.Offset = unlockStart;
1277 if (!UnlockFileEx (fh->h, 0, unlockEnd - unlockStart, 0, &o))
1279 SetErrnoFromWinError (GetLastError ());
1280 return GNUNET_SYSERR;
1289 * Open a file. Note that the access permissions will only be
1290 * used if a new file is created and if the underlying operating
1291 * system supports the given permissions.
1293 * @param fn file name to be opened
1294 * @param flags opening flags, a combination of GNUNET_DISK_OPEN_xxx bit flags
1295 * @param perm permissions for the newly created file, use
1296 * GNUNET_DISK_PERM_USER_NONE if a file could not be created by this
1297 * call (because of flags)
1298 * @return IO handle on success, NULL on error
1300 struct GNUNET_DISK_FileHandle *
1301 GNUNET_DISK_file_open (const char *fn, enum GNUNET_DISK_OpenFlags flags,
1302 enum GNUNET_DISK_AccessPermissions perm)
1305 struct GNUNET_DISK_FileHandle *ret;
1317 expfn = GNUNET_STRINGS_filename_expand (fn);
1322 if (GNUNET_DISK_OPEN_READWRITE == (flags & GNUNET_DISK_OPEN_READWRITE))
1323 oflags = O_RDWR; /* note: O_RDWR is NOT always O_RDONLY | O_WRONLY */
1324 else if (flags & GNUNET_DISK_OPEN_READ)
1326 else if (flags & GNUNET_DISK_OPEN_WRITE)
1331 GNUNET_free (expfn);
1334 if (flags & GNUNET_DISK_OPEN_FAILIFEXISTS)
1335 oflags |= (O_CREAT | O_EXCL);
1336 if (flags & GNUNET_DISK_OPEN_TRUNCATE)
1338 if (flags & GNUNET_DISK_OPEN_APPEND)
1340 if (flags & GNUNET_DISK_OPEN_CREATE)
1342 (void) GNUNET_DISK_directory_create_for_file (expfn);
1344 mode = translate_unix_perms (perm);
1347 fd = open (expfn, oflags | O_LARGEFILE, mode);
1350 if (0 == (flags & GNUNET_DISK_OPEN_FAILIFEXISTS))
1351 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "open", expfn);
1353 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_DEBUG, "open", expfn);
1354 GNUNET_free (expfn);
1361 if (GNUNET_DISK_OPEN_READWRITE == (flags & GNUNET_DISK_OPEN_READWRITE))
1362 access = FILE_READ_DATA | FILE_WRITE_DATA;
1363 else if (flags & GNUNET_DISK_OPEN_READ)
1364 access = FILE_READ_DATA;
1365 else if (flags & GNUNET_DISK_OPEN_WRITE)
1366 access = FILE_WRITE_DATA;
1368 if (flags & GNUNET_DISK_OPEN_FAILIFEXISTS)
1372 else if (flags & GNUNET_DISK_OPEN_CREATE)
1374 (void) GNUNET_DISK_directory_create_for_file (expfn);
1375 if (flags & GNUNET_DISK_OPEN_TRUNCATE)
1376 disp = CREATE_ALWAYS;
1380 else if (flags & GNUNET_DISK_OPEN_TRUNCATE)
1382 disp = TRUNCATE_EXISTING;
1386 disp = OPEN_EXISTING;
1389 /* TODO: access priviledges? */
1390 h = CreateFile (expfn, access,
1391 FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE, NULL,
1392 disp, FILE_ATTRIBUTE_NORMAL, NULL);
1393 if (h == INVALID_HANDLE_VALUE)
1395 SetErrnoFromWinError (GetLastError ());
1396 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "open", expfn);
1397 GNUNET_free (expfn);
1401 if (flags & GNUNET_DISK_OPEN_APPEND)
1402 if (SetFilePointer (h, 0, 0, FILE_END) == INVALID_SET_FILE_POINTER)
1404 SetErrnoFromWinError (GetLastError ());
1405 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "SetFilePointer", expfn);
1407 GNUNET_free (expfn);
1412 ret = GNUNET_malloc (sizeof (struct GNUNET_DISK_FileHandle));
1415 ret->type = GNUNET_DISK_FILE;
1419 GNUNET_free (expfn);
1425 * Close an open file
1426 * @param h file handle
1427 * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
1430 GNUNET_DISK_file_close (struct GNUNET_DISK_FileHandle *h)
1435 return GNUNET_SYSERR;
1439 if (!CloseHandle (h->h))
1441 SetErrnoFromWinError (GetLastError ());
1442 LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING, "close");
1443 GNUNET_free (h->oOverlapRead);
1444 GNUNET_free (h->oOverlapWrite);
1446 return GNUNET_SYSERR;
1449 if (close (h->fd) != 0)
1451 LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING, "close");
1453 return GNUNET_SYSERR;
1462 * Construct full path to a file inside of the private
1463 * directory used by GNUnet. Also creates the corresponding
1464 * directory. If the resulting name is supposed to be
1465 * a directory, end the last argument in '/' (or pass
1466 * DIR_SEPARATOR_STR as the last argument before NULL).
1468 * @param cfg configuration to use (determines HOME)
1469 * @param serviceName name of the service
1470 * @param ... is NULL-terminated list of
1471 * path components to append to the
1472 * private directory name.
1473 * @return the constructed filename
1476 GNUNET_DISK_get_home_filename (const struct GNUNET_CONFIGURATION_Handle *cfg,
1477 const char *serviceName, ...)
1483 unsigned int needed;
1486 GNUNET_CONFIGURATION_get_value_filename (cfg, serviceName, "HOME", &pfx))
1490 LOG (GNUNET_ERROR_TYPE_WARNING,
1491 _("No `%s' specified for service `%s' in configuration.\n"), "HOME",
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 *);
1505 needed += strlen (c);
1506 if ((c[strlen (c) - 1] != '/') && (c[strlen (c) - 1] != '\\'))
1510 ret = GNUNET_malloc (needed);
1513 va_start (ap, serviceName);
1516 c = va_arg (ap, const char *);
1520 if ((c[strlen (c) - 1] != '/') && (c[strlen (c) - 1] != '\\'))
1521 strcat (ret, DIR_SEPARATOR_STR);
1525 if ((ret[strlen (ret) - 1] != '/') && (ret[strlen (ret) - 1] != '\\'))
1526 (void) GNUNET_DISK_directory_create_for_file (ret);
1528 (void) GNUNET_DISK_directory_create (ret);
1534 * Handle for a memory-mapping operation.
1536 struct GNUNET_DISK_MapHandle
1539 * Address where the map is in memory.
1545 * Underlying OS handle.
1550 * Number of bytes mapped.
1558 #define MAP_FAILED ((void *) -1)
1562 * Map a file into memory
1564 * @param h open file handle
1565 * @param m handle to the new mapping
1566 * @param access access specification, GNUNET_DISK_MAP_TYPE_xxx
1567 * @param len size of the mapping
1568 * @return pointer to the mapped memory region, NULL on failure
1571 GNUNET_DISK_file_map (const struct GNUNET_DISK_FileHandle *h,
1572 struct GNUNET_DISK_MapHandle **m,
1573 enum GNUNET_DISK_MapType access, size_t len)
1582 DWORD mapAccess, protect;
1584 if ((access & GNUNET_DISK_MAP_TYPE_READ) &&
1585 (access & GNUNET_DISK_MAP_TYPE_WRITE))
1587 protect = PAGE_READWRITE;
1588 mapAccess = FILE_MAP_ALL_ACCESS;
1590 else if (access & GNUNET_DISK_MAP_TYPE_READ)
1592 protect = PAGE_READONLY;
1593 mapAccess = FILE_MAP_READ;
1595 else if (access & GNUNET_DISK_MAP_TYPE_WRITE)
1597 protect = PAGE_READWRITE;
1598 mapAccess = FILE_MAP_WRITE;
1606 *m = GNUNET_malloc (sizeof (struct GNUNET_DISK_MapHandle));
1607 (*m)->h = CreateFileMapping (h->h, NULL, protect, 0, 0, NULL);
1608 if ((*m)->h == INVALID_HANDLE_VALUE)
1610 SetErrnoFromWinError (GetLastError ());
1615 (*m)->addr = MapViewOfFile ((*m)->h, mapAccess, 0, 0, len);
1618 SetErrnoFromWinError (GetLastError ());
1619 CloseHandle ((*m)->h);
1628 if (access & GNUNET_DISK_MAP_TYPE_READ)
1630 if (access & GNUNET_DISK_MAP_TYPE_WRITE)
1632 *m = GNUNET_malloc (sizeof (struct GNUNET_DISK_MapHandle));
1633 (*m)->addr = mmap (NULL, len, prot, MAP_SHARED, h->fd, 0);
1634 GNUNET_assert (NULL != (*m)->addr);
1635 if (MAP_FAILED == (*m)->addr)
1647 * @param h mapping handle
1648 * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
1651 GNUNET_DISK_file_unmap (struct GNUNET_DISK_MapHandle *h)
1658 return GNUNET_SYSERR;
1662 ret = UnmapViewOfFile (h->addr) ? GNUNET_OK : GNUNET_SYSERR;
1663 if (ret != GNUNET_OK)
1664 SetErrnoFromWinError (GetLastError ());
1665 if (!CloseHandle (h->h) && (ret == GNUNET_OK))
1667 ret = GNUNET_SYSERR;
1668 SetErrnoFromWinError (GetLastError ());
1671 ret = munmap (h->addr, h->len) != -1 ? GNUNET_OK : GNUNET_SYSERR;
1679 * Write file changes to disk
1680 * @param h handle to an open file
1681 * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
1684 GNUNET_DISK_file_sync (const struct GNUNET_DISK_FileHandle *h)
1689 return GNUNET_SYSERR;
1695 ret = FlushFileBuffers (h->h) ? GNUNET_OK : GNUNET_SYSERR;
1696 if (ret != GNUNET_OK)
1697 SetErrnoFromWinError (GetLastError ());
1699 #elif defined(FREEBSD) || defined(OPENBSD) || defined(DARWIN)
1700 return fsync (h->fd) == -1 ? GNUNET_SYSERR : GNUNET_OK;
1702 return fdatasync (h->fd) == -1 ? GNUNET_SYSERR : GNUNET_OK;
1707 /* Copyright Bob Byrnes <byrnes <at> curl.com>
1708 http://permalink.gmane.org/gmane.os.cygwin.patches/2121
1710 /* Create a pipe, and return handles to the read and write ends,
1711 just like CreatePipe, but ensure that the write end permits
1712 FILE_READ_ATTRIBUTES access, on later versions of win32 where
1713 this is supported. This access is needed by NtQueryInformationFile,
1714 which is used to implement select and nonblocking writes.
1715 Note that the return value is either NO_ERROR or GetLastError,
1716 unlike CreatePipe, which returns a bool for success or failure. */
1718 create_selectable_pipe (PHANDLE read_pipe_ptr, PHANDLE write_pipe_ptr,
1719 LPSECURITY_ATTRIBUTES sa_ptr, DWORD psize,
1720 DWORD dwReadMode, DWORD dwWriteMode)
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 LOG (GNUNET_ERROR_TYPE_DEBUG, "CreateNamedPipe: name = %s, size = %lu\n",
1746 /* Use CreateNamedPipe instead of CreatePipe, because the latter
1747 * returns a write handle that does not permit FILE_READ_ATTRIBUTES
1748 * access, on versions of win32 earlier than WinXP SP2.
1749 * CreatePipe also stupidly creates a full duplex pipe, which is
1750 * a waste, since only a single direction is actually used.
1751 * It's important to only allow a single instance, to ensure that
1752 * the pipe was not created earlier by some other process, even if
1753 * the pid has been reused. We avoid FILE_FLAG_FIRST_PIPE_INSTANCE
1754 * because that is only available for Win2k SP2 and WinXP. */
1755 read_pipe = CreateNamedPipeA (pipename, PIPE_ACCESS_INBOUND | dwReadMode, PIPE_TYPE_BYTE | PIPE_READMODE_BYTE, 1, /* max instances */
1756 psize, /* output buffer size */
1757 psize, /* input buffer size */
1758 NMPWAIT_USE_DEFAULT_WAIT, sa_ptr);
1760 if (read_pipe != INVALID_HANDLE_VALUE)
1763 LOG (GNUNET_ERROR_TYPE_DEBUG, "pipe read handle = %p\n", read_pipe);
1768 DWORD err = GetLastError ();
1772 case ERROR_PIPE_BUSY:
1773 /* The pipe is already open with compatible parameters.
1774 * Pick a new name and retry. */
1776 LOG (GNUNET_ERROR_TYPE_DEBUG, "pipe busy, retrying\n");
1779 case ERROR_ACCESS_DENIED:
1780 /* The pipe is already open with incompatible parameters.
1781 * Pick a new name and retry. */
1783 LOG (GNUNET_ERROR_TYPE_DEBUG, "pipe access denied, retrying\n");
1786 case ERROR_CALL_NOT_IMPLEMENTED:
1787 /* We are on an older Win9x platform without named pipes.
1788 * Return an anonymous pipe as the best approximation. */
1790 LOG (GNUNET_ERROR_TYPE_DEBUG,
1791 "CreateNamedPipe not implemented, resorting to "
1792 "CreatePipe: size = %lu\n", psize);
1794 if (CreatePipe (read_pipe_ptr, write_pipe_ptr, sa_ptr, psize))
1797 LOG (GNUNET_ERROR_TYPE_DEBUG, "pipe read handle = %p\n",
1799 LOG (GNUNET_ERROR_TYPE_DEBUG, "pipe write handle = %p\n",
1804 err = GetLastError ();
1805 LOG (GNUNET_ERROR_TYPE_ERROR, "CreatePipe failed: %d\n", err);
1808 LOG (GNUNET_ERROR_TYPE_ERROR, "CreateNamedPipe failed: %d\n", err);
1814 LOG (GNUNET_ERROR_TYPE_DEBUG, "CreateFile: name = %s\n", pipename);
1817 /* Open the named pipe for writing.
1818 * Be sure to permit FILE_READ_ATTRIBUTES access. */
1819 write_pipe = CreateFileA (pipename, GENERIC_WRITE | FILE_READ_ATTRIBUTES, 0, /* share mode */
1820 sa_ptr, OPEN_EXISTING, dwWriteMode, /* flags and attributes */
1821 0); /* handle to template file */
1823 if (write_pipe == INVALID_HANDLE_VALUE)
1826 DWORD err = GetLastError ();
1829 LOG (GNUNET_ERROR_TYPE_DEBUG, "CreateFile failed: %d\n", err);
1831 CloseHandle (read_pipe);
1835 LOG (GNUNET_ERROR_TYPE_DEBUG, "pipe write handle = %p\n", write_pipe);
1838 *read_pipe_ptr = read_pipe;
1839 *write_pipe_ptr = write_pipe;
1845 * Creates an interprocess channel
1847 * @param blocking creates an asynchronous pipe if set to GNUNET_NO
1848 * @param inherit_read inherit the parent processes stdin (only for windows)
1849 * @param inherit_write inherit the parent processes stdout (only for windows)
1851 * @return handle to the new pipe, NULL on error
1853 struct GNUNET_DISK_PipeHandle *
1854 GNUNET_DISK_pipe (int blocking, int inherit_read, int inherit_write)
1856 struct GNUNET_DISK_PipeHandle *p;
1857 struct GNUNET_DISK_FileHandle *fds;
1859 p = GNUNET_malloc (sizeof (struct GNUNET_DISK_PipeHandle) +
1860 2 * sizeof (struct GNUNET_DISK_FileHandle));
1861 fds = (struct GNUNET_DISK_FileHandle *) &p[1];
1874 LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR, "pipe");
1879 p->fd[0]->fd = fd[0];
1880 p->fd[1]->fd = fd[1];
1882 flags = fcntl (fd[0], F_GETFL);
1884 flags |= O_NONBLOCK;
1885 if (0 > fcntl (fd[0], F_SETFL, flags))
1887 flags = fcntl (fd[0], F_GETFD);
1888 flags |= FD_CLOEXEC;
1889 if (0 > fcntl (fd[0], F_SETFD, flags))
1892 flags = fcntl (fd[1], F_GETFL);
1894 flags |= O_NONBLOCK;
1895 if (0 > fcntl (fd[1], F_SETFL, flags))
1897 flags = fcntl (fd[1], F_GETFD);
1898 flags |= FD_CLOEXEC;
1899 if (0 > fcntl (fd[1], F_SETFD, flags))
1904 LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR, "fcntl");
1905 GNUNET_break (0 == close (p->fd[0]->fd));
1906 GNUNET_break (0 == close (p->fd[1]->fd));
1916 create_selectable_pipe (&p->fd[0]->h, &p->fd[1]->h, NULL, 0,
1917 FILE_FLAG_OVERLAPPED, FILE_FLAG_OVERLAPPED);
1921 SetErrnoFromWinError (GetLastError ());
1924 if (!DuplicateHandle
1925 (GetCurrentProcess (), p->fd[0]->h, GetCurrentProcess (), &tmp_handle, 0,
1926 inherit_read == GNUNET_YES ? TRUE : FALSE, DUPLICATE_SAME_ACCESS))
1928 SetErrnoFromWinError (GetLastError ());
1929 CloseHandle (p->fd[0]->h);
1930 CloseHandle (p->fd[1]->h);
1934 CloseHandle (p->fd[0]->h);
1935 p->fd[0]->h = tmp_handle;
1937 if (!DuplicateHandle
1938 (GetCurrentProcess (), p->fd[1]->h, GetCurrentProcess (), &tmp_handle, 0,
1939 inherit_write == GNUNET_YES ? TRUE : FALSE, DUPLICATE_SAME_ACCESS))
1941 SetErrnoFromWinError (GetLastError ());
1942 CloseHandle (p->fd[0]->h);
1943 CloseHandle (p->fd[1]->h);
1947 CloseHandle (p->fd[1]->h);
1948 p->fd[1]->h = tmp_handle;
1954 SetNamedPipeHandleState (p->fd[0]->h, &mode, NULL, NULL);
1955 SetNamedPipeHandleState (p->fd[1]->h, &mode, NULL, NULL);
1956 /* this always fails on Windows 95, so we don't care about error handling */
1958 p->fd[0]->type = GNUNET_PIPE;
1959 p->fd[1]->type = GNUNET_PIPE;
1961 p->fd[0]->oOverlapRead = GNUNET_malloc (sizeof (OVERLAPPED));
1962 p->fd[0]->oOverlapWrite = GNUNET_malloc (sizeof (OVERLAPPED));
1963 p->fd[1]->oOverlapRead = GNUNET_malloc (sizeof (OVERLAPPED));
1964 p->fd[1]->oOverlapWrite = GNUNET_malloc (sizeof (OVERLAPPED));
1966 p->fd[0]->oOverlapRead->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
1967 p->fd[0]->oOverlapWrite->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
1969 p->fd[1]->oOverlapRead->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
1970 p->fd[1]->oOverlapWrite->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
1978 * Closes an interprocess channel
1980 * @param p pipe to close
1981 * @param end which end of the pipe to close
1982 * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
1985 GNUNET_DISK_pipe_close_end (struct GNUNET_DISK_PipeHandle *p,
1986 enum GNUNET_DISK_PipeEnd end)
1988 int ret = GNUNET_OK;
1992 if (end == GNUNET_DISK_PIPE_END_READ)
1994 if (!CloseHandle (p->fd[0]->h))
1996 SetErrnoFromWinError (GetLastError ());
1997 ret = GNUNET_SYSERR;
1999 p->fd[0]->h = INVALID_HANDLE_VALUE;
2001 else if (end == GNUNET_DISK_PIPE_END_WRITE)
2003 if (!CloseHandle (p->fd[1]->h))
2005 SetErrnoFromWinError (GetLastError ());
2006 ret = GNUNET_SYSERR;
2008 p->fd[1]->h = INVALID_HANDLE_VALUE;
2013 if (end == GNUNET_DISK_PIPE_END_READ)
2015 if (0 != close (p->fd[0]->fd))
2017 ret = GNUNET_SYSERR;
2022 else if (end == GNUNET_DISK_PIPE_END_WRITE)
2024 if (0 != close (p->fd[1]->fd))
2026 ret = GNUNET_SYSERR;
2037 * Closes an interprocess channel
2039 * @param p pipe to close
2040 * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
2043 GNUNET_DISK_pipe_close (struct GNUNET_DISK_PipeHandle *p)
2045 int ret = GNUNET_OK;
2049 if (!CloseHandle (p->fd[0]->h))
2051 SetErrnoFromWinError (GetLastError ());
2052 ret = GNUNET_SYSERR;
2054 if (!CloseHandle (p->fd[1]->h))
2056 SetErrnoFromWinError (GetLastError ());
2057 ret = GNUNET_SYSERR;
2062 if (p->fd[0]->fd != -1)
2064 if (0 != close (p->fd[0]->fd))
2066 ret = GNUNET_SYSERR;
2071 if (p->fd[1]->fd != -1)
2073 if (0 != close (p->fd[1]->fd))
2075 ret = GNUNET_SYSERR;
2087 * Creates a named pipe/FIFO and opens it
2088 * @param fn pointer to the name of the named pipe or to NULL
2089 * @param flags open flags
2090 * @param perm access permissions
2091 * @return pipe handle on success, NULL on error
2093 struct GNUNET_DISK_FileHandle *
2094 GNUNET_DISK_npipe_create (char **fn, enum GNUNET_DISK_OpenFlags flags,
2095 enum GNUNET_DISK_AccessPermissions perm)
2098 struct GNUNET_DISK_FileHandle *ret;
2104 if (flags & GNUNET_DISK_OPEN_READWRITE)
2105 openMode = PIPE_ACCESS_DUPLEX;
2106 else if (flags & GNUNET_DISK_OPEN_READ)
2107 openMode = PIPE_ACCESS_INBOUND;
2108 else if (flags & GNUNET_DISK_OPEN_WRITE)
2109 openMode = PIPE_ACCESS_OUTBOUND;
2111 if (flags & GNUNET_DISK_OPEN_FAILIFEXISTS)
2112 openMode |= FILE_FLAG_FIRST_PIPE_INSTANCE;
2121 GNUNET_asprintf (&name, "\\\\.\\pipe\\%.246s", fn);
2123 LOG (GNUNET_ERROR_TYPE_DEBUG,
2124 "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,
2132 GNUNET_asprintf (fn, "\\\\.\\pipe\\gnunet-%llu",
2133 GNUNET_CRYPTO_random_u64 (GNUNET_CRYPTO_QUALITY_WEAK,
2136 LOG (GNUNET_ERROR_TYPE_DEBUG, "Trying to create unique named pipe `%s'\n",
2139 h = CreateNamedPipe (*fn,
2140 openMode | FILE_FLAG_OVERLAPPED |
2141 FILE_FLAG_FIRST_PIPE_INSTANCE,
2142 PIPE_TYPE_BYTE | PIPE_READMODE_BYTE, 2, 1, 1, 0,
2145 error_code = GetLastError ();
2148 /* don't re-set name to NULL yet */
2149 if (h == INVALID_HANDLE_VALUE)
2151 SetErrnoFromWinError (error_code);
2153 LOG (GNUNET_ERROR_TYPE_DEBUG,
2154 "Pipe creation have failed because of %d, errno is %d\n", error_code,
2160 LOG (GNUNET_ERROR_TYPE_DEBUG,
2161 "Pipe was to be unique, considering re-creation\n");
2165 if (error_code != ERROR_ACCESS_DENIED && error_code != ERROR_PIPE_BUSY)
2170 LOG (GNUNET_ERROR_TYPE_DEBUG,
2171 "Pipe name was not unique, trying again\n");
2181 ret = GNUNET_malloc (sizeof (*ret));
2183 ret->type = GNUNET_PIPE;
2185 ret->oOverlapRead = GNUNET_malloc (sizeof (OVERLAPPED));
2186 ret->oOverlapWrite = GNUNET_malloc (sizeof (OVERLAPPED));
2188 ret->oOverlapRead->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2189 ret->oOverlapWrite->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2195 char dir[] = "/tmp/gnunet-pipe-XXXXXX";
2197 if (mkdtemp (dir) == NULL)
2199 LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR, "mkdtemp");
2202 GNUNET_asprintf (fn, "%s/child-control", dir);
2205 if (mkfifo (*fn, translate_unix_perms (perm)) == -1)
2207 if ((errno != EEXIST) || (0 != (flags & GNUNET_DISK_OPEN_FAILIFEXISTS)))
2211 flags = flags & (~GNUNET_DISK_OPEN_FAILIFEXISTS);
2212 return GNUNET_DISK_file_open (*fn, flags, perm);
2218 * Opens already existing named pipe/FIFO
2220 * @param fn name of an existing named pipe
2221 * @param flags open flags
2222 * @param perm access permissions
2223 * @return pipe handle on success, NULL on error
2225 struct GNUNET_DISK_FileHandle *
2226 GNUNET_DISK_npipe_open (const char *fn, enum GNUNET_DISK_OpenFlags flags,
2227 enum GNUNET_DISK_AccessPermissions perm)
2230 struct GNUNET_DISK_FileHandle *ret;
2235 if (flags & GNUNET_DISK_OPEN_READWRITE)
2236 openMode = GENERIC_WRITE | GENERIC_READ;
2237 else if (flags & GNUNET_DISK_OPEN_READ)
2238 openMode = GENERIC_READ;
2239 else if (flags & GNUNET_DISK_OPEN_WRITE)
2240 openMode = GENERIC_WRITE;
2242 h = CreateFile (fn, openMode, 0, NULL, OPEN_EXISTING,
2243 FILE_FLAG_OVERLAPPED | FILE_READ_ATTRIBUTES, NULL);
2244 if (h == INVALID_HANDLE_VALUE)
2246 SetErrnoFromWinError (GetLastError ());
2250 ret = GNUNET_malloc (sizeof (*ret));
2252 ret->type = GNUNET_PIPE;
2253 ret->oOverlapRead = GNUNET_malloc (sizeof (OVERLAPPED));
2254 ret->oOverlapWrite = GNUNET_malloc (sizeof (OVERLAPPED));
2255 ret->oOverlapRead->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2256 ret->oOverlapWrite->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2260 flags = flags & (~GNUNET_DISK_OPEN_FAILIFEXISTS);
2261 return GNUNET_DISK_file_open (fn, flags, perm);
2266 * Closes a named pipe/FIFO
2267 * @param pipe named pipe
2268 * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
2271 GNUNET_DISK_npipe_close (struct GNUNET_DISK_FileHandle *pipe)
2274 return close (pipe->fd) == 0 ? GNUNET_OK : GNUNET_SYSERR;
2278 ret = CloseHandle (pipe->h);
2281 SetErrnoFromWinError (GetLastError ());
2282 return GNUNET_SYSERR;
2291 * Get the handle to a particular pipe end
2294 * @param n end to access
2295 * @return handle for the respective end
2297 const struct GNUNET_DISK_FileHandle *
2298 GNUNET_DISK_pipe_handle (const struct GNUNET_DISK_PipeHandle *p,
2299 enum GNUNET_DISK_PipeEnd n)
2303 case GNUNET_DISK_PIPE_END_READ:
2304 case GNUNET_DISK_PIPE_END_WRITE:
2314 * Retrieve OS file handle
2316 * @param fh GNUnet file descriptor
2317 * @param dst destination buffer
2318 * @param dst_len length of dst
2319 * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
2322 GNUNET_DISK_internal_file_handle_ (const struct GNUNET_DISK_FileHandle *fh,
2323 void *dst, size_t dst_len)
2326 if (dst_len < sizeof (HANDLE))
2327 return GNUNET_SYSERR;
2328 *((HANDLE *) dst) = fh->h;
2330 if (dst_len < sizeof (int))
2331 return GNUNET_SYSERR;
2332 *((int *) dst) = fh->fd;