2 This file is part of GNUnet.
3 (C) 2001, 2002, 2005, 2006, 2009 Christian Grothoff (and other contributing authors)
5 GNUnet is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 2, or (at your
8 option) any later version.
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with GNUnet; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
23 * @brief disk IO convenience methods
24 * @author Christian Grothoff
29 #include "gnunet_common.h"
30 #include "gnunet_directories.h"
31 #include "gnunet_disk_lib.h"
32 #include "gnunet_scheduler_lib.h"
33 #include "gnunet_strings_lib.h"
34 #include "gnunet_crypto_lib.h"
37 #define DEBUG_NPIPE GNUNET_NO
39 #define DEBUG_PIPE GNUNET_NO
42 * Block size for IO for copying files.
44 #define COPY_BLK_SIZE 65536
48 #if defined(LINUX) || defined(CYGWIN)
51 #if defined(SOMEBSD) || defined(DARWIN)
52 #include <sys/param.h>
53 #include <sys/mount.h>
56 #include <sys/types.h>
57 #include <sys/statvfs.h>
62 ULONG PipeSerialNumber;
64 #define _IFMT 0170000 /* type of file */
65 #define _IFLNK 0120000 /* symbolic link */
66 #define S_ISLNK(m) (((m)&_IFMT) == _IFLNK)
68 #error PORT-ME: need to port statfs (how much space is left on the drive?)
74 #if !defined(SOMEBSD) && !defined(DARWIN) && !defined(WINDOWS)
78 #include <sys/statvfs.h>
83 * Handle used to manage a pipe.
85 struct GNUNET_DISK_PipeHandle
88 * File descriptors for the pipe.
90 struct GNUNET_DISK_FileHandle *fd[2];
95 * Closure for the recursion to determine the file size
98 struct GetFileSizeData
101 * Set to the total file size.
106 * GNUNET_YES if symbolic links should be included.
108 int include_sym_links;
113 translate_unix_perms (enum GNUNET_DISK_AccessPermissions perm)
118 if (perm & GNUNET_DISK_PERM_USER_READ)
120 if (perm & GNUNET_DISK_PERM_USER_WRITE)
122 if (perm & GNUNET_DISK_PERM_USER_EXEC)
124 if (perm & GNUNET_DISK_PERM_GROUP_READ)
126 if (perm & GNUNET_DISK_PERM_GROUP_WRITE)
128 if (perm & GNUNET_DISK_PERM_GROUP_EXEC)
130 if (perm & GNUNET_DISK_PERM_OTHER_READ)
132 if (perm & GNUNET_DISK_PERM_OTHER_WRITE)
134 if (perm & GNUNET_DISK_PERM_OTHER_EXEC)
142 * Iterate over all files in the given directory and
143 * accumulate their size.
145 * @param cls closure of type "struct GetFileSizeData"
146 * @param fn current filename we are looking at
147 * @return GNUNET_SYSERR on serious errors, otherwise GNUNET_OK
150 getSizeRec (void *cls, const char *fn)
152 struct GetFileSizeData *gfsd = cls;
161 if (0 != STAT64 (fn, &buf))
163 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "stat64", fn);
164 return GNUNET_SYSERR;
167 if (0 != STAT (fn, &buf))
169 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "stat", fn);
170 return GNUNET_SYSERR;
173 if ((!S_ISLNK (buf.st_mode)) || (gfsd->include_sym_links == GNUNET_YES))
174 gfsd->total += buf.st_size;
175 if ((S_ISDIR (buf.st_mode)) && (0 == ACCESS (fn, X_OK)) &&
176 ((!S_ISLNK (buf.st_mode)) || (gfsd->include_sym_links == GNUNET_YES)))
178 if (GNUNET_SYSERR == GNUNET_DISK_directory_scan (fn, &getSizeRec, gfsd))
179 return GNUNET_SYSERR;
186 * Checks whether a handle is invalid
188 * @param h handle to check
189 * @return GNUNET_YES if invalid, GNUNET_NO if valid
192 GNUNET_DISK_handle_invalid (const struct GNUNET_DISK_FileHandle *h)
195 return ((!h) || (h->h == INVALID_HANDLE_VALUE)) ? GNUNET_YES : GNUNET_NO;
197 return ((!h) || (h->fd == -1)) ? GNUNET_YES : GNUNET_NO;
203 * Move the read/write pointer in a file
205 * @param h handle of an open file
206 * @param offset position to move to
207 * @param whence specification to which position the offset parameter relates to
208 * @return the new position on success, GNUNET_SYSERR otherwise
211 GNUNET_DISK_file_seek (const struct GNUNET_DISK_FileHandle * h, off_t offset,
212 enum GNUNET_DISK_Seek whence)
217 return GNUNET_SYSERR;
223 static DWORD t[] = {[GNUNET_DISK_SEEK_SET] = FILE_BEGIN,
224 [GNUNET_DISK_SEEK_CUR] = FILE_CURRENT,[GNUNET_DISK_SEEK_END] = FILE_END
227 ret = SetFilePointer (h->h, offset, NULL, t[whence]);
228 if (ret == INVALID_SET_FILE_POINTER)
230 SetErrnoFromWinError (GetLastError ());
231 return GNUNET_SYSERR;
235 static int t[] = {[GNUNET_DISK_SEEK_SET] = SEEK_SET,
236 [GNUNET_DISK_SEEK_CUR] = SEEK_CUR,[GNUNET_DISK_SEEK_END] = SEEK_END
239 return lseek (h->fd, offset, t[whence]);
245 * Get the size of the file (or directory) of the given file (in
248 * @param filename name of the file or directory
249 * @param size set to the size of the file (or,
250 * in the case of directories, the sum
251 * of all sizes of files in the directory)
252 * @param includeSymLinks should symbolic links be
254 * @return GNUNET_SYSERR on error, GNUNET_OK on success
257 GNUNET_DISK_file_size (const char *filename, uint64_t * size,
260 struct GetFileSizeData gfsd;
263 GNUNET_assert (size != NULL);
265 gfsd.include_sym_links = includeSymLinks;
266 ret = getSizeRec (&gfsd, filename);
273 * Obtain some unique identifiers for the given file
274 * that can be used to identify it in the local system.
275 * This function is used between GNUnet processes to
276 * quickly check if two files with the same absolute path
277 * are actually identical. The two processes represent
278 * the same peer but may communicate over the network
279 * (and the file may be on an NFS volume). This function
280 * may not be supported on all operating systems.
282 * @param filename name of the file
283 * @param dev set to the device ID
284 * @param ino set to the inode ID
285 * @return GNUNET_OK on success
288 GNUNET_DISK_file_get_identifiers (const char *filename, uint64_t * dev,
295 if ((0 == stat (filename, &sbuf)) && (0 == statvfs (filename, &fbuf)))
297 *dev = (uint64_t) fbuf.f_fsid;
298 *ino = (uint64_t) sbuf.st_ino;
305 if ((0 == stat (filename, &sbuf)) && (0 == statfs (filename, &fbuf)))
307 *dev = ((uint64_t) fbuf.f_fsid.val[0]) << 32 ||
308 ((uint64_t) fbuf.f_fsid.val[1]);
309 *ino = (uint64_t) sbuf.st_ino;
313 // FIXME NILS: test this
314 struct GNUNET_DISK_FileHandle *fh;
315 BY_HANDLE_FILE_INFORMATION info;
318 fh = GNUNET_DISK_file_open (filename, GNUNET_DISK_OPEN_READ, 0);
320 return GNUNET_SYSERR;
321 succ = GetFileInformationByHandle (fh->h, &info);
322 GNUNET_DISK_file_close (fh);
325 *dev = info.dwVolumeSerialNumber;
326 *ino = ((info.nFileIndexHigh << sizeof (DWORD)) | info.nFileIndexLow);
330 return GNUNET_SYSERR;
333 return GNUNET_SYSERR;
338 * Create an (empty) temporary file on disk. If the given name is not
339 * an absolute path, the current 'TMPDIR' will be prepended. In any case,
340 * 6 random characters will be appended to the name to create a unique
343 * @param t component to use for the name;
344 * does NOT contain "XXXXXX" or "/tmp/".
345 * @return NULL on error, otherwise name of fresh
346 * file on disk in directory for temporary files
349 GNUNET_DISK_mktemp (const char *t)
356 if ((t[0] != '/') && (t[0] != '\\')
358 && !(isalpha ((int) t[0]) && (t[0] != '\0') && (t[1] == ':'))
362 tmpdir = getenv ("TMPDIR");
363 tmpdir = tmpdir ? tmpdir : "/tmp";
364 GNUNET_asprintf (&tmpl, "%s/%s%s", tmpdir, t, "XXXXXX");
368 GNUNET_asprintf (&tmpl, "%s%s", t, "XXXXXX");
371 fn = (char *) GNUNET_malloc (MAX_PATH + 1);
372 if (ERROR_SUCCESS != plibc_conv_to_win_path (tmpl, fn))
385 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR, "mkstemp", fn);
390 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "close", fn);
396 * Get the number of blocks that are left on the partition that
397 * contains the given file (for normal users).
399 * @param part a file on the partition to check
400 * @return -1 on errors, otherwise the number of free blocks
403 GNUNET_DISK_get_blocks_available (const char *part)
408 if (0 != statvfs (part, &buf))
410 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "statfs", part);
420 path = GNUNET_STRINGS_filename_expand (part);
423 memcpy (szDrive, path, 3);
426 if (!GetDiskFreeSpace (szDrive, &dwDummy, &dwDummy, &dwBlocks, &dwDummy))
428 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
429 _("`%s' failed for drive `%s': %u\n"), "GetDiskFreeSpace",
430 szDrive, GetLastError ());
438 if (0 != statfs (part, &s))
440 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "statfs", part);
449 * Test if "fil" is a directory.
450 * Will not print an error message if the directory
451 * does not exist. Will log errors if GNUNET_SYSERR is
452 * returned (i.e., a file exists with the same name).
454 * @param fil filename to test
455 * @return GNUNET_YES if yes, GNUNET_NO if not, GNUNET_SYSERR if it
459 GNUNET_DISK_directory_test (const char *fil)
461 struct stat filestat;
464 ret = STAT (fil, &filestat);
469 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "stat", fil);
470 return GNUNET_SYSERR;
474 if (!S_ISDIR (filestat.st_mode))
476 if (ACCESS (fil, R_OK | X_OK) < 0)
478 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "access", fil);
479 return GNUNET_SYSERR;
485 * Check that fil corresponds to a filename
486 * (of a file that exists and that is not a directory).
488 * @param fil filename to check
489 * @return GNUNET_YES if yes, GNUNET_NO if not a file, GNUNET_SYSERR if something
490 * else (will print an error message in that case, too).
493 GNUNET_DISK_file_test (const char *fil)
495 struct stat filestat;
499 rdir = GNUNET_STRINGS_filename_expand (fil);
501 return GNUNET_SYSERR;
503 ret = STAT (rdir, &filestat);
508 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "stat", rdir);
510 return GNUNET_SYSERR;
515 if (!S_ISREG (filestat.st_mode))
520 if (ACCESS (rdir, R_OK) < 0)
522 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "access", rdir);
524 return GNUNET_SYSERR;
532 * Implementation of "mkdir -p"
533 * @param dir the directory to create
534 * @returns GNUNET_OK on success, GNUNET_SYSERR on failure
537 GNUNET_DISK_directory_create (const char *dir)
544 rdir = GNUNET_STRINGS_filename_expand (dir);
546 return GNUNET_SYSERR;
550 pos = 1; /* skip heading '/' */
552 /* Local or Network path? */
553 if (strncmp (rdir, "\\\\", 2) == 0)
558 if (rdir[pos] == '\\')
568 pos = 3; /* strlen("C:\\") */
573 if ((rdir[pos] == DIR_SEPARATOR) || (pos == len))
576 ret = GNUNET_DISK_directory_test (rdir);
577 if (ret == GNUNET_SYSERR)
580 return GNUNET_SYSERR;
582 if (ret == GNUNET_NO)
585 ret = mkdir (rdir, S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH); /* 755 */
589 if ((ret != 0) && (errno != EEXIST))
591 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR, "mkdir", rdir);
593 return GNUNET_SYSERR;
596 rdir[pos] = DIR_SEPARATOR;
606 * Create the directory structure for storing
609 * @param filename name of a file in the directory
610 * @returns GNUNET_OK on success,
611 * GNUNET_SYSERR on failure,
612 * GNUNET_NO if the directory
613 * exists but is not writeable for us
616 GNUNET_DISK_directory_create_for_file (const char *filename)
622 rdir = GNUNET_STRINGS_filename_expand (filename);
624 return GNUNET_SYSERR;
626 while ((len > 0) && (rdir[len] != DIR_SEPARATOR))
629 ret = GNUNET_DISK_directory_create (rdir);
630 if ((ret == GNUNET_OK) && (0 != ACCESS (rdir, W_OK)))
638 * Read the contents of a binary file into a buffer.
639 * @param h handle to an open file
640 * @param result the buffer to write the result to
641 * @param len the maximum number of bytes to read
642 * @return the number of bytes read on success, GNUNET_SYSERR on failure
645 GNUNET_DISK_file_read (const struct GNUNET_DISK_FileHandle * h, void *result,
651 return GNUNET_SYSERR;
657 if (h->type != GNUNET_PIPE)
659 if (!ReadFile (h->h, result, len, &bytesRead, NULL))
661 SetErrnoFromWinError (GetLastError ());
662 return GNUNET_SYSERR;
667 if (!ReadFile (h->h, result, len, NULL, h->oOverlapRead))
669 if (GetLastError () != ERROR_IO_PENDING)
671 SetErrnoFromWinError (GetLastError ());
672 return GNUNET_SYSERR;
675 GetOverlappedResult (h->h, h->oOverlapRead, &bytesRead, TRUE);
679 return read (h->fd, result, len);
685 * Read the contents of a binary file into a buffer.
687 * @param fn file name
688 * @param result the buffer to write the result to
689 * @param len the maximum number of bytes to read
690 * @return number of bytes read, GNUNET_SYSERR on failure
693 GNUNET_DISK_fn_read (const char *fn, void *result, size_t len)
695 struct GNUNET_DISK_FileHandle *fh;
698 fh = GNUNET_DISK_file_open (fn, GNUNET_DISK_OPEN_READ, GNUNET_DISK_PERM_NONE);
700 return GNUNET_SYSERR;
701 ret = GNUNET_DISK_file_read (fh, result, len);
702 GNUNET_assert (GNUNET_OK == GNUNET_DISK_file_close (fh));
709 * Write a buffer to a file.
710 * @param h handle to open file
711 * @param buffer the data to write
712 * @param n number of bytes to write
713 * @return number of bytes written on success, GNUNET_SYSERR on error
716 GNUNET_DISK_file_write (const struct GNUNET_DISK_FileHandle * h,
717 const void *buffer, size_t n)
722 return GNUNET_SYSERR;
728 if (h->type != GNUNET_PIPE)
730 if (!WriteFile (h->h, buffer, n, &bytesWritten, NULL))
732 SetErrnoFromWinError (GetLastError ());
733 return GNUNET_SYSERR;
739 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "It is a pipe trying to write\n");
741 if (!WriteFile (h->h, buffer, n, NULL, h->oOverlapWrite))
743 if (GetLastError () != ERROR_IO_PENDING)
745 SetErrnoFromWinError (GetLastError ());
747 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Error writing to pipe\n");
749 return GNUNET_SYSERR;
753 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Will get overlapped result\n");
755 GetOverlappedResult (h->h, h->oOverlapWrite, &bytesWritten, TRUE);
759 return write (h->fd, buffer, n);
764 * Write a buffer to a file. If the file is longer than the
765 * number of bytes that will be written, it will be truncated.
767 * @param fn file name
768 * @param buffer the data to write
769 * @param n number of bytes to write
770 * @param mode file permissions
771 * @return number of bytes written on success, GNUNET_SYSERR on error
774 GNUNET_DISK_fn_write (const char *fn, const void *buffer, size_t n,
775 enum GNUNET_DISK_AccessPermissions mode)
777 struct GNUNET_DISK_FileHandle *fh;
780 fh = GNUNET_DISK_file_open (fn,
781 GNUNET_DISK_OPEN_WRITE | GNUNET_DISK_OPEN_TRUNCATE
782 | GNUNET_DISK_OPEN_CREATE, mode);
784 return GNUNET_SYSERR;
785 ret = GNUNET_DISK_file_write (fh, buffer, n);
786 GNUNET_assert (GNUNET_OK == GNUNET_DISK_file_close (fh));
791 * Scan a directory for files.
793 * @param dirName the name of the directory
794 * @param callback the method to call for each file,
795 * can be NULL, in that case, we only count
796 * @param callback_cls closure for callback
797 * @return the number of files found, GNUNET_SYSERR on error or
798 * ieration aborted by callback returning GNUNET_SYSERR
801 GNUNET_DISK_directory_scan (const char *dirName,
802 GNUNET_FileNameCallback callback,
806 struct dirent *finfo;
811 unsigned int name_len;
814 GNUNET_assert (dirName != NULL);
815 dname = GNUNET_STRINGS_filename_expand (dirName);
817 return GNUNET_SYSERR;
818 while ((strlen (dname) > 0) && (dname[strlen (dname) - 1] == DIR_SEPARATOR))
819 dname[strlen (dname) - 1] = '\0';
820 if (0 != STAT (dname, &istat))
822 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "stat", dname);
824 return GNUNET_SYSERR;
826 if (!S_ISDIR (istat.st_mode))
828 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
829 _("Expected `%s' to be a directory!\n"), dirName);
831 return GNUNET_SYSERR;
834 dinfo = OPENDIR (dname);
835 if ((errno == EACCES) || (dinfo == NULL))
837 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "opendir", dname);
841 return GNUNET_SYSERR;
844 n_size = strlen (dname) + name_len + 2;
845 name = GNUNET_malloc (n_size);
846 while ((finfo = readdir (dinfo)) != NULL)
848 if ((0 == strcmp (finfo->d_name, ".")) ||
849 (0 == strcmp (finfo->d_name, "..")))
851 if (callback != NULL)
853 if (name_len < strlen (finfo->d_name))
856 name_len = strlen (finfo->d_name);
857 n_size = strlen (dname) + name_len + 2;
858 name = GNUNET_malloc (n_size);
860 /* dname can end in "/" only if dname == "/";
861 * if dname does not end in "/", we need to add
862 * a "/" (otherwise, we must not!) */
863 GNUNET_snprintf (name, n_size, "%s%s%s", dname,
864 (strcmp (dname, DIR_SEPARATOR_STR) ==
865 0) ? "" : DIR_SEPARATOR_STR, finfo->d_name);
866 if (GNUNET_OK != callback (callback_cls, name))
871 return GNUNET_SYSERR;
884 * Opaque handle used for iterating over a directory.
886 struct GNUNET_DISK_DirectoryIterator
890 * Function to call on directory entries.
892 GNUNET_DISK_DirectoryIteratorCallback callback;
895 * Closure for callback.
900 * Reference to directory.
910 * Next filename to process.
917 enum GNUNET_SCHEDULER_Priority priority;
923 * Task used by the directory iterator.
926 directory_iterator_task (void *cls,
927 const struct GNUNET_SCHEDULER_TaskContext *tc)
929 struct GNUNET_DISK_DirectoryIterator *iter = cls;
932 name = iter->next_name;
933 GNUNET_assert (name != NULL);
934 iter->next_name = NULL;
935 iter->callback (iter->callback_cls, iter, name, iter->dirname);
941 * This function must be called during the DiskIteratorCallback
942 * (exactly once) to schedule the task to process the next
943 * filename in the directory (if there is one).
945 * @param iter opaque handle for the iterator
946 * @param can set to GNUNET_YES to terminate the iteration early
947 * @return GNUNET_YES if iteration will continue,
948 * GNUNET_NO if this was the last entry (and iteration is complete),
949 * GNUNET_SYSERR if abort was YES
952 GNUNET_DISK_directory_iterator_next (struct GNUNET_DISK_DirectoryIterator *iter,
955 struct dirent *finfo;
957 GNUNET_assert (iter->next_name == NULL);
958 if (can == GNUNET_YES)
960 closedir (iter->directory);
961 GNUNET_free (iter->dirname);
963 return GNUNET_SYSERR;
965 while (NULL != (finfo = readdir (iter->directory)))
967 if ((0 == strcmp (finfo->d_name, ".")) ||
968 (0 == strcmp (finfo->d_name, "..")))
970 GNUNET_asprintf (&iter->next_name, "%s%s%s", iter->dirname,
971 DIR_SEPARATOR_STR, finfo->d_name);
976 GNUNET_DISK_directory_iterator_next (iter, GNUNET_YES);
979 GNUNET_SCHEDULER_add_with_priority (iter->priority, &directory_iterator_task,
986 * Scan a directory for files using the scheduler to run a task for
987 * each entry. The name of the directory must be expanded first (!).
988 * If a scheduler does not need to be used, GNUNET_DISK_directory_scan
989 * may provide a simpler API.
991 * @param prio priority to use
992 * @param dirName the name of the directory
993 * @param callback the method to call for each file
994 * @param callback_cls closure for callback
997 GNUNET_DISK_directory_iterator_start (enum GNUNET_SCHEDULER_Priority prio,
999 GNUNET_DISK_DirectoryIteratorCallback
1000 callback, void *callback_cls)
1002 struct GNUNET_DISK_DirectoryIterator *di;
1004 di = GNUNET_malloc (sizeof (struct GNUNET_DISK_DirectoryIterator));
1005 di->callback = callback;
1006 di->callback_cls = callback_cls;
1007 di->directory = OPENDIR (dirName);
1008 if (di->directory == NULL)
1011 callback (callback_cls, NULL, NULL, NULL);
1014 di->dirname = GNUNET_strdup (dirName);
1015 di->priority = prio;
1016 GNUNET_DISK_directory_iterator_next (di, GNUNET_NO);
1021 * Function that removes the given directory by calling
1022 * "GNUNET_DISK_directory_remove".
1024 * @param unused not used
1025 * @param fn directory to remove
1029 remove_helper (void *unused, const char *fn)
1031 (void) GNUNET_DISK_directory_remove (fn);
1037 * Remove all files in a directory (rm -rf). Call with
1041 * @param fileName the file to remove
1042 * @return GNUNET_OK on success, GNUNET_SYSERR on error
1045 GNUNET_DISK_directory_remove (const char *fileName)
1049 if (0 != LSTAT (fileName, &istat))
1050 return GNUNET_NO; /* file may not exist... */
1051 CHMOD (fileName, S_IWUSR | S_IRUSR | S_IXUSR);
1052 if (UNLINK (fileName) == 0)
1054 if ((errno != EISDIR) &&
1055 /* EISDIR is not sufficient in all cases, e.g.
1056 * sticky /tmp directory may result in EPERM on BSD.
1057 * So we also explicitly check "isDirectory" */
1058 (GNUNET_YES != GNUNET_DISK_directory_test (fileName)))
1060 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "rmdir", fileName);
1061 return GNUNET_SYSERR;
1063 if (GNUNET_SYSERR ==
1064 GNUNET_DISK_directory_scan (fileName, &remove_helper, NULL))
1065 return GNUNET_SYSERR;
1066 if (0 != RMDIR (fileName))
1068 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "rmdir", fileName);
1069 return GNUNET_SYSERR;
1078 * @param src file to copy
1079 * @param dst destination file name
1080 * @return GNUNET_OK on success, GNUNET_SYSERR on error
1083 GNUNET_DISK_file_copy (const char *src, const char *dst)
1089 struct GNUNET_DISK_FileHandle *in;
1090 struct GNUNET_DISK_FileHandle *out;
1092 if (GNUNET_OK != GNUNET_DISK_file_size (src, &size, GNUNET_YES))
1093 return GNUNET_SYSERR;
1095 in = GNUNET_DISK_file_open (src, GNUNET_DISK_OPEN_READ,
1096 GNUNET_DISK_PERM_NONE);
1098 return GNUNET_SYSERR;
1100 GNUNET_DISK_file_open (dst,
1101 GNUNET_DISK_OPEN_WRITE | GNUNET_DISK_OPEN_CREATE |
1102 GNUNET_DISK_OPEN_FAILIFEXISTS,
1103 GNUNET_DISK_PERM_USER_READ |
1104 GNUNET_DISK_PERM_USER_WRITE |
1105 GNUNET_DISK_PERM_GROUP_READ |
1106 GNUNET_DISK_PERM_GROUP_WRITE);
1109 GNUNET_DISK_file_close (in);
1110 return GNUNET_SYSERR;
1112 buf = GNUNET_malloc (COPY_BLK_SIZE);
1115 len = COPY_BLK_SIZE;
1116 if (len > size - pos)
1118 if (len != GNUNET_DISK_file_read (in, buf, len))
1120 if (len != GNUNET_DISK_file_write (out, buf, len))
1125 GNUNET_DISK_file_close (in);
1126 GNUNET_DISK_file_close (out);
1130 GNUNET_DISK_file_close (in);
1131 GNUNET_DISK_file_close (out);
1132 return GNUNET_SYSERR;
1137 * @brief Removes special characters as ':' from a filename.
1138 * @param fn the filename to canonicalize
1141 GNUNET_DISK_filename_canonicalize (char *fn)
1151 if (c == '/' || c == '\\' || c == ':' || c == '*' || c == '?' || c == '"' ||
1152 c == '<' || c == '>' || c == '|')
1164 * @brief Change owner of a file
1166 * @param filename name of file to change the owner of
1167 * @param user name of the new owner
1168 * @return GNUNET_OK on success, GNUNET_SYSERR on failure
1171 GNUNET_DISK_file_change_owner (const char *filename, const char *user)
1176 pws = getpwnam (user);
1179 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1180 _("Cannot obtain information about user `%s': %s\n"), user,
1182 return GNUNET_SYSERR;
1184 if (0 != chown (filename, pws->pw_uid, pws->pw_gid))
1185 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "chown", filename);
1192 * Lock a part of a file
1193 * @param fh file handle
1194 * @param lockStart absolute position from where to lock
1195 * @param lockEnd absolute position until where to lock
1196 * @param excl GNUNET_YES for an exclusive lock
1197 * @return GNUNET_OK on success, GNUNET_SYSERR on error
1200 GNUNET_DISK_file_lock (struct GNUNET_DISK_FileHandle *fh, off_t lockStart,
1201 off_t lockEnd, int excl)
1206 return GNUNET_SYSERR;
1212 memset (&fl, 0, sizeof (struct flock));
1213 fl.l_type = excl ? F_WRLCK : F_RDLCK;
1214 fl.l_whence = SEEK_SET;
1215 fl.l_start = lockStart;
1218 return fcntl (fh->fd, F_SETLK, &fl) != 0 ? GNUNET_SYSERR : GNUNET_OK;
1222 memset (&o, 0, sizeof (OVERLAPPED));
1223 o.Offset = lockStart;
1226 (fh->h, (excl ? LOCKFILE_EXCLUSIVE_LOCK : 0) | LOCKFILE_FAIL_IMMEDIATELY,
1227 0, lockEnd - lockStart, 0, &o))
1229 SetErrnoFromWinError (GetLastError ());
1230 return GNUNET_SYSERR;
1239 * Unlock a part of a file
1240 * @param fh file handle
1241 * @param unlockStart absolute position from where to unlock
1242 * @param unlockEnd absolute position until where to unlock
1243 * @return GNUNET_OK on success, GNUNET_SYSERR on error
1246 GNUNET_DISK_file_unlock (struct GNUNET_DISK_FileHandle *fh, off_t unlockStart,
1252 return GNUNET_SYSERR;
1258 memset (&fl, 0, sizeof (struct flock));
1259 fl.l_type = F_UNLCK;
1260 fl.l_whence = SEEK_SET;
1261 fl.l_start = unlockStart;
1262 fl.l_len = unlockEnd;
1264 return fcntl (fh->fd, F_SETLK, &fl) != 0 ? GNUNET_SYSERR : GNUNET_OK;
1268 memset (&o, 0, sizeof (OVERLAPPED));
1269 o.Offset = unlockStart;
1271 if (!UnlockFileEx (fh->h, 0, unlockEnd - unlockStart, 0, &o))
1273 SetErrnoFromWinError (GetLastError ());
1274 return GNUNET_SYSERR;
1283 * Open a file. Note that the access permissions will only be
1284 * used if a new file is created and if the underlying operating
1285 * system supports the given permissions.
1287 * @param fn file name to be opened
1288 * @param flags opening flags, a combination of GNUNET_DISK_OPEN_xxx bit flags
1289 * @param perm permissions for the newly created file, use
1290 * GNUNET_DISK_PERM_USER_NONE if a file could not be created by this
1291 * call (because of flags)
1292 * @return IO handle on success, NULL on error
1294 struct GNUNET_DISK_FileHandle *
1295 GNUNET_DISK_file_open (const char *fn, enum GNUNET_DISK_OpenFlags flags,
1296 enum GNUNET_DISK_AccessPermissions perm)
1299 struct GNUNET_DISK_FileHandle *ret;
1311 expfn = GNUNET_STRINGS_filename_expand (fn);
1316 if (GNUNET_DISK_OPEN_READWRITE == (flags & GNUNET_DISK_OPEN_READWRITE))
1317 oflags = O_RDWR; /* note: O_RDWR is NOT always O_RDONLY | O_WRONLY */
1318 else if (flags & GNUNET_DISK_OPEN_READ)
1320 else if (flags & GNUNET_DISK_OPEN_WRITE)
1325 GNUNET_free (expfn);
1328 if (flags & GNUNET_DISK_OPEN_FAILIFEXISTS)
1329 oflags |= (O_CREAT | O_EXCL);
1330 if (flags & GNUNET_DISK_OPEN_TRUNCATE)
1332 if (flags & GNUNET_DISK_OPEN_APPEND)
1334 if (flags & GNUNET_DISK_OPEN_CREATE)
1336 (void) GNUNET_DISK_directory_create_for_file (expfn);
1338 mode = translate_unix_perms (perm);
1341 fd = open (expfn, oflags | O_LARGEFILE, mode);
1344 if (0 == (flags & GNUNET_DISK_OPEN_FAILIFEXISTS))
1345 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "open", expfn);
1347 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_DEBUG, "open", expfn);
1348 GNUNET_free (expfn);
1355 if (GNUNET_DISK_OPEN_READWRITE == (flags & GNUNET_DISK_OPEN_READWRITE))
1356 access = FILE_READ_DATA | FILE_WRITE_DATA;
1357 else if (flags & GNUNET_DISK_OPEN_READ)
1358 access = FILE_READ_DATA;
1359 else if (flags & GNUNET_DISK_OPEN_WRITE)
1360 access = FILE_WRITE_DATA;
1362 if (flags & GNUNET_DISK_OPEN_FAILIFEXISTS)
1366 else if (flags & GNUNET_DISK_OPEN_CREATE)
1368 (void) GNUNET_DISK_directory_create_for_file (expfn);
1369 if (flags & GNUNET_DISK_OPEN_TRUNCATE)
1370 disp = CREATE_ALWAYS;
1374 else if (flags & GNUNET_DISK_OPEN_TRUNCATE)
1376 disp = TRUNCATE_EXISTING;
1380 disp = OPEN_EXISTING;
1383 /* TODO: access priviledges? */
1384 h = CreateFile (expfn, access,
1385 FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE, NULL,
1386 disp, FILE_ATTRIBUTE_NORMAL, NULL);
1387 if (h == INVALID_HANDLE_VALUE)
1389 SetErrnoFromWinError (GetLastError ());
1390 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "open", expfn);
1391 GNUNET_free (expfn);
1395 if (flags & GNUNET_DISK_OPEN_APPEND)
1396 if (SetFilePointer (h, 0, 0, FILE_END) == INVALID_SET_FILE_POINTER)
1398 SetErrnoFromWinError (GetLastError ());
1399 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "SetFilePointer",
1402 GNUNET_free (expfn);
1407 ret = GNUNET_malloc (sizeof (struct GNUNET_DISK_FileHandle));
1410 ret->type = GNUNET_DISK_FILE;
1414 GNUNET_free (expfn);
1420 * Close an open file
1421 * @param h file handle
1422 * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
1425 GNUNET_DISK_file_close (struct GNUNET_DISK_FileHandle *h)
1430 return GNUNET_SYSERR;
1434 if (!CloseHandle (h->h))
1436 SetErrnoFromWinError (GetLastError ());
1437 GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "close");
1438 GNUNET_free (h->oOverlapRead);
1439 GNUNET_free (h->oOverlapWrite);
1441 return GNUNET_SYSERR;
1444 if (close (h->fd) != 0)
1446 GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "close");
1448 return GNUNET_SYSERR;
1457 * Construct full path to a file inside of the private
1458 * directory used by GNUnet. Also creates the corresponding
1459 * directory. If the resulting name is supposed to be
1460 * a directory, end the last argument in '/' (or pass
1461 * DIR_SEPARATOR_STR as the last argument before NULL).
1463 * @param cfg configuration to use (determines HOME)
1464 * @param serviceName name of the service
1465 * @param ... is NULL-terminated list of
1466 * path components to append to the
1467 * private directory name.
1468 * @return the constructed filename
1471 GNUNET_DISK_get_home_filename (const struct GNUNET_CONFIGURATION_Handle *cfg,
1472 const char *serviceName, ...)
1478 unsigned int needed;
1481 GNUNET_CONFIGURATION_get_value_filename (cfg, serviceName, "HOME", &pfx))
1485 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1486 _("No `%s' specified for service `%s' in configuration.\n"),
1487 "HOME", serviceName);
1490 needed = strlen (pfx) + 2;
1491 if ((pfx[strlen (pfx) - 1] != '/') && (pfx[strlen (pfx) - 1] != '\\'))
1493 va_start (ap, serviceName);
1496 c = va_arg (ap, const char *);
1500 needed += strlen (c);
1501 if ((c[strlen (c) - 1] != '/') && (c[strlen (c) - 1] != '\\'))
1505 ret = GNUNET_malloc (needed);
1508 va_start (ap, serviceName);
1511 c = va_arg (ap, const char *);
1515 if ((c[strlen (c) - 1] != '/') && (c[strlen (c) - 1] != '\\'))
1516 strcat (ret, DIR_SEPARATOR_STR);
1520 if ((ret[strlen (ret) - 1] != '/') && (ret[strlen (ret) - 1] != '\\'))
1521 (void) GNUNET_DISK_directory_create_for_file (ret);
1523 (void) GNUNET_DISK_directory_create (ret);
1529 * Handle for a memory-mapping operation.
1531 struct GNUNET_DISK_MapHandle
1534 * Address where the map is in memory.
1540 * Underlying OS handle.
1545 * Number of bytes mapped.
1553 #define MAP_FAILED ((void *) -1)
1557 * Map a file into memory
1559 * @param h open file handle
1560 * @param m handle to the new mapping
1561 * @param access access specification, GNUNET_DISK_MAP_TYPE_xxx
1562 * @param len size of the mapping
1563 * @return pointer to the mapped memory region, NULL on failure
1566 GNUNET_DISK_file_map (const struct GNUNET_DISK_FileHandle *h,
1567 struct GNUNET_DISK_MapHandle **m,
1568 enum GNUNET_DISK_MapType access, size_t len)
1577 DWORD mapAccess, protect;
1579 if ((access & GNUNET_DISK_MAP_TYPE_READ) &&
1580 (access & GNUNET_DISK_MAP_TYPE_WRITE))
1582 protect = PAGE_READWRITE;
1583 mapAccess = FILE_MAP_ALL_ACCESS;
1585 else if (access & GNUNET_DISK_MAP_TYPE_READ)
1587 protect = PAGE_READONLY;
1588 mapAccess = FILE_MAP_READ;
1590 else if (access & GNUNET_DISK_MAP_TYPE_WRITE)
1592 protect = PAGE_READWRITE;
1593 mapAccess = FILE_MAP_WRITE;
1601 *m = GNUNET_malloc (sizeof (struct GNUNET_DISK_MapHandle));
1602 (*m)->h = CreateFileMapping (h->h, NULL, protect, 0, 0, NULL);
1603 if ((*m)->h == INVALID_HANDLE_VALUE)
1605 SetErrnoFromWinError (GetLastError ());
1610 (*m)->addr = MapViewOfFile ((*m)->h, mapAccess, 0, 0, len);
1613 SetErrnoFromWinError (GetLastError ());
1614 CloseHandle ((*m)->h);
1623 if (access & GNUNET_DISK_MAP_TYPE_READ)
1625 if (access & GNUNET_DISK_MAP_TYPE_WRITE)
1627 *m = GNUNET_malloc (sizeof (struct GNUNET_DISK_MapHandle));
1628 (*m)->addr = mmap (NULL, len, prot, MAP_SHARED, h->fd, 0);
1629 GNUNET_assert (NULL != (*m)->addr);
1630 if (MAP_FAILED == (*m)->addr)
1642 * @param h mapping handle
1643 * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
1646 GNUNET_DISK_file_unmap (struct GNUNET_DISK_MapHandle *h)
1653 return GNUNET_SYSERR;
1657 ret = UnmapViewOfFile (h->addr) ? GNUNET_OK : GNUNET_SYSERR;
1658 if (ret != GNUNET_OK)
1659 SetErrnoFromWinError (GetLastError ());
1660 if (!CloseHandle (h->h) && (ret == GNUNET_OK))
1662 ret = GNUNET_SYSERR;
1663 SetErrnoFromWinError (GetLastError ());
1666 ret = munmap (h->addr, h->len) != -1 ? GNUNET_OK : GNUNET_SYSERR;
1674 * Write file changes to disk
1675 * @param h handle to an open file
1676 * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
1679 GNUNET_DISK_file_sync (const struct GNUNET_DISK_FileHandle *h)
1684 return GNUNET_SYSERR;
1690 ret = FlushFileBuffers (h->h) ? GNUNET_OK : GNUNET_SYSERR;
1691 if (ret != GNUNET_OK)
1692 SetErrnoFromWinError (GetLastError ());
1694 #elif defined(FREEBSD) || defined(OPENBSD) || defined(DARWIN)
1695 return fsync (h->fd) == -1 ? GNUNET_SYSERR : GNUNET_OK;
1697 return fdatasync (h->fd) == -1 ? GNUNET_SYSERR : GNUNET_OK;
1702 /* Copyright Bob Byrnes <byrnes <at> curl.com>
1703 http://permalink.gmane.org/gmane.os.cygwin.patches/2121
1705 /* Create a pipe, and return handles to the read and write ends,
1706 just like CreatePipe, but ensure that the write end permits
1707 FILE_READ_ATTRIBUTES access, on later versions of win32 where
1708 this is supported. This access is needed by NtQueryInformationFile,
1709 which is used to implement select and nonblocking writes.
1710 Note that the return value is either NO_ERROR or GetLastError,
1711 unlike CreatePipe, which returns a bool for success or failure. */
1713 create_selectable_pipe (PHANDLE read_pipe_ptr, PHANDLE write_pipe_ptr,
1714 LPSECURITY_ATTRIBUTES sa_ptr, DWORD psize,
1715 DWORD dwReadMode, DWORD dwWriteMode)
1717 /* Default to error. */
1718 *read_pipe_ptr = *write_pipe_ptr = INVALID_HANDLE_VALUE;
1720 HANDLE read_pipe = INVALID_HANDLE_VALUE, write_pipe = INVALID_HANDLE_VALUE;
1722 /* Ensure that there is enough pipe buffer space for atomic writes. */
1723 if (psize < PIPE_BUF)
1726 char pipename[MAX_PATH];
1728 /* Retry CreateNamedPipe as long as the pipe name is in use.
1729 * Retrying will probably never be necessary, but we want
1730 * to be as robust as possible. */
1733 static volatile LONG pipe_unique_id;
1735 snprintf (pipename, sizeof pipename, "\\\\.\\pipe\\gnunet-%d-%ld",
1736 getpid (), InterlockedIncrement ((LONG *) & pipe_unique_id));
1738 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1739 "CreateNamedPipe: name = %s, size = %lu\n", pipename, psize);
1741 /* Use CreateNamedPipe instead of CreatePipe, because the latter
1742 * returns a write handle that does not permit FILE_READ_ATTRIBUTES
1743 * access, on versions of win32 earlier than WinXP SP2.
1744 * CreatePipe also stupidly creates a full duplex pipe, which is
1745 * a waste, since only a single direction is actually used.
1746 * It's important to only allow a single instance, to ensure that
1747 * the pipe was not created earlier by some other process, even if
1748 * the pid has been reused. We avoid FILE_FLAG_FIRST_PIPE_INSTANCE
1749 * because that is only available for Win2k SP2 and WinXP. */
1750 read_pipe = CreateNamedPipeA (pipename, PIPE_ACCESS_INBOUND | dwReadMode, PIPE_TYPE_BYTE | PIPE_READMODE_BYTE, 1, /* max instances */
1751 psize, /* output buffer size */
1752 psize, /* input buffer size */
1753 NMPWAIT_USE_DEFAULT_WAIT, sa_ptr);
1755 if (read_pipe != INVALID_HANDLE_VALUE)
1758 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "pipe read handle = %p\n",
1764 DWORD err = GetLastError ();
1768 case ERROR_PIPE_BUSY:
1769 /* The pipe is already open with compatible parameters.
1770 * Pick a new name and retry. */
1772 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "pipe busy, retrying\n");
1775 case ERROR_ACCESS_DENIED:
1776 /* The pipe is already open with incompatible parameters.
1777 * Pick a new name and retry. */
1779 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "pipe access denied, retrying\n");
1782 case ERROR_CALL_NOT_IMPLEMENTED:
1783 /* We are on an older Win9x platform without named pipes.
1784 * Return an anonymous pipe as the best approximation. */
1786 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1787 "CreateNamedPipe not implemented, resorting to "
1788 "CreatePipe: size = %lu\n", psize);
1790 if (CreatePipe (read_pipe_ptr, write_pipe_ptr, sa_ptr, psize))
1793 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "pipe read handle = %p\n",
1795 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "pipe write handle = %p\n",
1800 err = GetLastError ();
1801 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "CreatePipe failed: %d\n", err);
1804 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "CreateNamedPipe failed: %d\n", err);
1810 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "CreateFile: name = %s\n", pipename);
1813 /* Open the named pipe for writing.
1814 * Be sure to permit FILE_READ_ATTRIBUTES access. */
1815 write_pipe = CreateFileA (pipename, GENERIC_WRITE | FILE_READ_ATTRIBUTES, 0, /* share mode */
1816 sa_ptr, OPEN_EXISTING, dwWriteMode, /* flags and attributes */
1817 0); /* handle to template file */
1819 if (write_pipe == INVALID_HANDLE_VALUE)
1822 DWORD err = GetLastError ();
1825 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "CreateFile failed: %d\n", err);
1827 CloseHandle (read_pipe);
1831 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "pipe write handle = %p\n", write_pipe);
1834 *read_pipe_ptr = read_pipe;
1835 *write_pipe_ptr = write_pipe;
1841 * Creates an interprocess channel
1843 * @param blocking creates an asynchronous pipe if set to GNUNET_NO
1844 * @param inherit_read inherit the parent processes stdin (only for windows)
1845 * @param inherit_write inherit the parent processes stdout (only for windows)
1847 * @return handle to the new pipe, NULL on error
1849 struct GNUNET_DISK_PipeHandle *
1850 GNUNET_DISK_pipe (int blocking, int inherit_read, int inherit_write)
1852 struct GNUNET_DISK_PipeHandle *p;
1853 struct GNUNET_DISK_FileHandle *fds;
1855 p = GNUNET_malloc (sizeof (struct GNUNET_DISK_PipeHandle) +
1856 2 * sizeof (struct GNUNET_DISK_FileHandle));
1857 fds = (struct GNUNET_DISK_FileHandle *) &p[1];
1870 GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "pipe");
1875 p->fd[0]->fd = fd[0];
1876 p->fd[1]->fd = fd[1];
1878 flags = fcntl (fd[0], F_GETFL);
1880 flags |= O_NONBLOCK;
1881 if (0 > fcntl (fd[0], F_SETFL, flags))
1883 flags = fcntl (fd[0], F_GETFD);
1884 flags |= FD_CLOEXEC;
1885 if (0 > fcntl (fd[0], F_SETFD, flags))
1888 flags = fcntl (fd[1], F_GETFL);
1890 flags |= O_NONBLOCK;
1891 if (0 > fcntl (fd[1], F_SETFL, flags))
1893 flags = fcntl (fd[1], F_GETFD);
1894 flags |= FD_CLOEXEC;
1895 if (0 > fcntl (fd[1], F_SETFD, flags))
1900 GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "fcntl");
1901 GNUNET_break (0 == close (p->fd[0]->fd));
1902 GNUNET_break (0 == close (p->fd[1]->fd));
1912 create_selectable_pipe (&p->fd[0]->h, &p->fd[1]->h, NULL, 0,
1913 FILE_FLAG_OVERLAPPED, FILE_FLAG_OVERLAPPED);
1917 SetErrnoFromWinError (GetLastError ());
1920 if (!DuplicateHandle
1921 (GetCurrentProcess (), p->fd[0]->h, GetCurrentProcess (), &tmp_handle, 0,
1922 inherit_read == GNUNET_YES ? TRUE : FALSE, DUPLICATE_SAME_ACCESS))
1924 SetErrnoFromWinError (GetLastError ());
1925 CloseHandle (p->fd[0]->h);
1926 CloseHandle (p->fd[1]->h);
1930 CloseHandle (p->fd[0]->h);
1931 p->fd[0]->h = tmp_handle;
1933 if (!DuplicateHandle
1934 (GetCurrentProcess (), p->fd[1]->h, GetCurrentProcess (), &tmp_handle, 0,
1935 inherit_write == GNUNET_YES ? TRUE : FALSE, DUPLICATE_SAME_ACCESS))
1937 SetErrnoFromWinError (GetLastError ());
1938 CloseHandle (p->fd[0]->h);
1939 CloseHandle (p->fd[1]->h);
1943 CloseHandle (p->fd[1]->h);
1944 p->fd[1]->h = tmp_handle;
1950 SetNamedPipeHandleState (p->fd[0]->h, &mode, NULL, NULL);
1951 SetNamedPipeHandleState (p->fd[1]->h, &mode, NULL, NULL);
1952 /* this always fails on Windows 95, so we don't care about error handling */
1954 p->fd[0]->type = GNUNET_PIPE;
1955 p->fd[1]->type = GNUNET_PIPE;
1957 p->fd[0]->oOverlapRead = GNUNET_malloc (sizeof (OVERLAPPED));
1958 p->fd[0]->oOverlapWrite = GNUNET_malloc (sizeof (OVERLAPPED));
1959 p->fd[1]->oOverlapRead = GNUNET_malloc (sizeof (OVERLAPPED));
1960 p->fd[1]->oOverlapWrite = GNUNET_malloc (sizeof (OVERLAPPED));
1962 p->fd[0]->oOverlapRead->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
1963 p->fd[0]->oOverlapWrite->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
1965 p->fd[1]->oOverlapRead->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
1966 p->fd[1]->oOverlapWrite->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
1974 * Closes an interprocess channel
1976 * @param p pipe to close
1977 * @param end which end of the pipe to close
1978 * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
1981 GNUNET_DISK_pipe_close_end (struct GNUNET_DISK_PipeHandle *p,
1982 enum GNUNET_DISK_PipeEnd end)
1984 int ret = GNUNET_OK;
1988 if (end == GNUNET_DISK_PIPE_END_READ)
1990 if (!CloseHandle (p->fd[0]->h))
1992 SetErrnoFromWinError (GetLastError ());
1993 ret = GNUNET_SYSERR;
1995 p->fd[0]->h = INVALID_HANDLE_VALUE;
1997 else if (end == GNUNET_DISK_PIPE_END_WRITE)
1999 if (!CloseHandle (p->fd[1]->h))
2001 SetErrnoFromWinError (GetLastError ());
2002 ret = GNUNET_SYSERR;
2004 p->fd[1]->h = INVALID_HANDLE_VALUE;
2009 if (end == GNUNET_DISK_PIPE_END_READ)
2011 if (0 != close (p->fd[0]->fd))
2013 ret = GNUNET_SYSERR;
2018 else if (end == GNUNET_DISK_PIPE_END_WRITE)
2020 if (0 != close (p->fd[1]->fd))
2022 ret = GNUNET_SYSERR;
2033 * Closes an interprocess channel
2035 * @param p pipe to close
2036 * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
2039 GNUNET_DISK_pipe_close (struct GNUNET_DISK_PipeHandle *p)
2041 int ret = GNUNET_OK;
2045 if (!CloseHandle (p->fd[0]->h))
2047 SetErrnoFromWinError (GetLastError ());
2048 ret = GNUNET_SYSERR;
2050 if (!CloseHandle (p->fd[1]->h))
2052 SetErrnoFromWinError (GetLastError ());
2053 ret = GNUNET_SYSERR;
2058 if (p->fd[0]->fd != -1)
2060 if (0 != close (p->fd[0]->fd))
2062 ret = GNUNET_SYSERR;
2067 if (p->fd[1]->fd != -1)
2069 if (0 != close (p->fd[1]->fd))
2071 ret = GNUNET_SYSERR;
2083 * Creates a named pipe/FIFO and opens it
2084 * @param fn pointer to the name of the named pipe or to NULL
2085 * @param flags open flags
2086 * @param perm access permissions
2087 * @return pipe handle on success, NULL on error
2089 struct GNUNET_DISK_FileHandle *
2090 GNUNET_DISK_npipe_create (char **fn, enum GNUNET_DISK_OpenFlags flags,
2091 enum GNUNET_DISK_AccessPermissions perm)
2094 struct GNUNET_DISK_FileHandle *ret;
2100 if (flags & GNUNET_DISK_OPEN_READWRITE)
2101 openMode = PIPE_ACCESS_DUPLEX;
2102 else if (flags & GNUNET_DISK_OPEN_READ)
2103 openMode = PIPE_ACCESS_INBOUND;
2104 else if (flags & GNUNET_DISK_OPEN_WRITE)
2105 openMode = PIPE_ACCESS_OUTBOUND;
2107 if (flags & GNUNET_DISK_OPEN_FAILIFEXISTS)
2108 openMode |= FILE_FLAG_FIRST_PIPE_INSTANCE;
2117 GNUNET_asprintf (&name, "\\\\.\\pipe\\%.246s", fn);
2119 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2120 "Trying to create an instance of named pipe `%s'\n", name);
2122 h = CreateNamedPipe (name, openMode | FILE_FLAG_OVERLAPPED,
2123 PIPE_TYPE_BYTE | PIPE_READMODE_BYTE, 2, 1, 1, 0,
2128 GNUNET_asprintf (fn, "\\\\.\\pipe\\gnunet-%llu",
2129 GNUNET_CRYPTO_random_u64 (GNUNET_CRYPTO_QUALITY_WEAK,
2132 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2133 "Trying to create unique named pipe `%s'\n", *fn);
2135 h = CreateNamedPipe (*fn,
2136 openMode | FILE_FLAG_OVERLAPPED |
2137 FILE_FLAG_FIRST_PIPE_INSTANCE,
2138 PIPE_TYPE_BYTE | PIPE_READMODE_BYTE, 2, 1, 1, 0,
2141 error_code = GetLastError ();
2144 /* don't re-set name to NULL yet */
2145 if (h == INVALID_HANDLE_VALUE)
2147 SetErrnoFromWinError (error_code);
2149 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2150 "Pipe creation have failed because of %d, errno is %d\n",
2156 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2157 "Pipe was to be unique, considering re-creation\n");
2161 if (error_code != ERROR_ACCESS_DENIED && error_code != ERROR_PIPE_BUSY)
2166 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2167 "Pipe name was not unique, trying again\n");
2177 ret = GNUNET_malloc (sizeof (*ret));
2179 ret->type = GNUNET_PIPE;
2181 ret->oOverlapRead = GNUNET_malloc (sizeof (OVERLAPPED));
2182 ret->oOverlapWrite = GNUNET_malloc (sizeof (OVERLAPPED));
2184 ret->oOverlapRead->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2185 ret->oOverlapWrite->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2191 char dir[] = "/tmp/gnunet-pipe-XXXXXX";
2193 if (mkdtemp (dir) == NULL)
2195 GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "mkdtemp");
2198 GNUNET_asprintf (fn, "%s/child-control", dir);
2201 if (mkfifo (*fn, translate_unix_perms (perm)) == -1)
2203 if ((errno != EEXIST) || (0 != (flags & GNUNET_DISK_OPEN_FAILIFEXISTS)))
2207 flags = flags & (~GNUNET_DISK_OPEN_FAILIFEXISTS);
2208 return GNUNET_DISK_file_open (*fn, flags, perm);
2214 * Opens already existing named pipe/FIFO
2216 * @param fn name of an existing named pipe
2217 * @param flags open flags
2218 * @param perm access permissions
2219 * @return pipe handle on success, NULL on error
2221 struct GNUNET_DISK_FileHandle *
2222 GNUNET_DISK_npipe_open (const char *fn, enum GNUNET_DISK_OpenFlags flags,
2223 enum GNUNET_DISK_AccessPermissions perm)
2226 struct GNUNET_DISK_FileHandle *ret;
2231 if (flags & GNUNET_DISK_OPEN_READWRITE)
2232 openMode = GENERIC_WRITE | GENERIC_READ;
2233 else if (flags & GNUNET_DISK_OPEN_READ)
2234 openMode = GENERIC_READ;
2235 else if (flags & GNUNET_DISK_OPEN_WRITE)
2236 openMode = GENERIC_WRITE;
2238 h = CreateFile (fn, openMode, 0, NULL, OPEN_EXISTING,
2239 FILE_FLAG_OVERLAPPED | FILE_READ_ATTRIBUTES, NULL);
2240 if (h == INVALID_HANDLE_VALUE)
2242 SetErrnoFromWinError (GetLastError ());
2246 ret = GNUNET_malloc (sizeof (*ret));
2248 ret->type = GNUNET_PIPE;
2249 ret->oOverlapRead = GNUNET_malloc (sizeof (OVERLAPPED));
2250 ret->oOverlapWrite = GNUNET_malloc (sizeof (OVERLAPPED));
2251 ret->oOverlapRead->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2252 ret->oOverlapWrite->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2256 flags = flags & (~GNUNET_DISK_OPEN_FAILIFEXISTS);
2257 return GNUNET_DISK_file_open (fn, flags, perm);
2262 * Closes a named pipe/FIFO
2263 * @param pipe named pipe
2264 * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
2267 GNUNET_DISK_npipe_close (struct GNUNET_DISK_FileHandle *pipe)
2270 return close (pipe->fd) == 0 ? GNUNET_OK : GNUNET_SYSERR;
2274 ret = CloseHandle (pipe->h);
2277 SetErrnoFromWinError (GetLastError ());
2278 return GNUNET_SYSERR;
2287 * Get the handle to a particular pipe end
2290 * @param n end to access
2291 * @return handle for the respective end
2293 const struct GNUNET_DISK_FileHandle *
2294 GNUNET_DISK_pipe_handle (const struct GNUNET_DISK_PipeHandle *p,
2295 enum GNUNET_DISK_PipeEnd n)
2299 case GNUNET_DISK_PIPE_END_READ:
2300 case GNUNET_DISK_PIPE_END_WRITE:
2310 * Retrieve OS file handle
2312 * @param fh GNUnet file descriptor
2313 * @param dst destination buffer
2314 * @param dst_len length of dst
2315 * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
2318 GNUNET_DISK_internal_file_handle_ (const struct GNUNET_DISK_FileHandle *fh,
2319 void *dst, size_t dst_len)
2322 if (dst_len < sizeof (HANDLE))
2323 return GNUNET_SYSERR;
2324 *((HANDLE *) dst) = fh->h;
2326 if (dst_len < sizeof (int))
2327 return GNUNET_SYSERR;
2328 *((int *) dst) = fh->fd;