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)) &&
176 (0 == ACCESS (fn, X_OK)) &&
177 ((!S_ISLNK (buf.st_mode)) || (gfsd->include_sym_links == GNUNET_YES)))
179 if (GNUNET_SYSERR == GNUNET_DISK_directory_scan (fn, &getSizeRec, gfsd))
180 return GNUNET_SYSERR;
187 * Checks whether a handle is invalid
189 * @param h handle to check
190 * @return GNUNET_YES if invalid, GNUNET_NO if valid
193 GNUNET_DISK_handle_invalid (const struct GNUNET_DISK_FileHandle *h)
196 return ((!h) || (h->h == INVALID_HANDLE_VALUE)) ? GNUNET_YES : GNUNET_NO;
198 return ((!h) || (h->fd == -1)) ? GNUNET_YES : GNUNET_NO;
204 * Move the read/write pointer in a file
206 * @param h handle of an open file
207 * @param offset position to move to
208 * @param whence specification to which position the offset parameter relates to
209 * @return the new position on success, GNUNET_SYSERR otherwise
212 GNUNET_DISK_file_seek (const struct GNUNET_DISK_FileHandle * h, off_t offset,
213 enum GNUNET_DISK_Seek whence)
218 return GNUNET_SYSERR;
224 static DWORD t[] = {[GNUNET_DISK_SEEK_SET] = FILE_BEGIN,
225 [GNUNET_DISK_SEEK_CUR] = FILE_CURRENT,[GNUNET_DISK_SEEK_END] = FILE_END
228 ret = SetFilePointer (h->h, offset, NULL, t[whence]);
229 if (ret == INVALID_SET_FILE_POINTER)
231 SetErrnoFromWinError (GetLastError ());
232 return GNUNET_SYSERR;
236 static int t[] = {[GNUNET_DISK_SEEK_SET] = SEEK_SET,
237 [GNUNET_DISK_SEEK_CUR] = SEEK_CUR,[GNUNET_DISK_SEEK_END] = SEEK_END
240 return lseek (h->fd, offset, t[whence]);
246 * Get the size of the file (or directory) of the given file (in
249 * @param filename name of the file or directory
250 * @param size set to the size of the file (or,
251 * in the case of directories, the sum
252 * of all sizes of files in the directory)
253 * @param includeSymLinks should symbolic links be
255 * @return GNUNET_SYSERR on error, GNUNET_OK on success
258 GNUNET_DISK_file_size (const char *filename,
259 uint64_t * size, int includeSymLinks)
261 struct GetFileSizeData gfsd;
264 GNUNET_assert (size != NULL);
266 gfsd.include_sym_links = includeSymLinks;
267 ret = getSizeRec (&gfsd, filename);
274 * Obtain some unique identifiers for the given file
275 * that can be used to identify it in the local system.
276 * This function is used between GNUnet processes to
277 * quickly check if two files with the same absolute path
278 * are actually identical. The two processes represent
279 * the same peer but may communicate over the network
280 * (and the file may be on an NFS volume). This function
281 * may not be supported on all operating systems.
283 * @param filename name of the file
284 * @param dev set to the device ID
285 * @param ino set to the inode ID
286 * @return GNUNET_OK on success
289 GNUNET_DISK_file_get_identifiers (const char *filename,
290 uint64_t * dev, uint64_t * ino)
296 if ((0 == stat (filename, &sbuf)) && (0 == statvfs (filename, &fbuf)))
298 *dev = (uint64_t) fbuf.f_fsid;
299 *ino = (uint64_t) sbuf.st_ino;
306 if ((0 == stat (filename, &sbuf)) && (0 == statfs (filename, &fbuf)))
308 *dev = ((uint64_t) fbuf.f_fsid.val[0]) << 32 ||
309 ((uint64_t) fbuf.f_fsid.val[1]);
310 *ino = (uint64_t) sbuf.st_ino;
314 // FIXME NILS: test this
315 struct GNUNET_DISK_FileHandle *fh;
316 BY_HANDLE_FILE_INFORMATION info;
319 fh = GNUNET_DISK_file_open (filename, GNUNET_DISK_OPEN_READ, 0);
321 return GNUNET_SYSERR;
322 succ = GetFileInformationByHandle (fh->h, &info);
323 GNUNET_DISK_file_close (fh);
326 *dev = info.dwVolumeSerialNumber;
327 *ino = ((info.nFileIndexHigh << sizeof (DWORD)) | info.nFileIndexLow);
331 return GNUNET_SYSERR;
334 return GNUNET_SYSERR;
339 * Create an (empty) temporary file on disk. If the given name is not
340 * an absolute path, the current 'TMPDIR' will be prepended. In any case,
341 * 6 random characters will be appended to the name to create a unique
344 * @param t component to use for the name;
345 * does NOT contain "XXXXXX" or "/tmp/".
346 * @return NULL on error, otherwise name of fresh
347 * file on disk in directory for temporary files
350 GNUNET_DISK_mktemp (const char *t)
357 if ((t[0] != '/') && (t[0] != '\\')
359 && !(isalpha ((int) t[0]) && (t[0] != '\0') && (t[1] == ':'))
363 tmpdir = getenv ("TMPDIR");
364 tmpdir = tmpdir ? tmpdir : "/tmp";
365 GNUNET_asprintf (&tmpl, "%s/%s%s", tmpdir, t, "XXXXXX");
369 GNUNET_asprintf (&tmpl, "%s%s", t, "XXXXXX");
372 fn = (char *) GNUNET_malloc (MAX_PATH + 1);
373 if (ERROR_SUCCESS != plibc_conv_to_win_path (tmpl, fn))
386 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR, "mkstemp", fn);
391 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "close", fn);
397 * Get the number of blocks that are left on the partition that
398 * contains the given file (for normal users).
400 * @param part a file on the partition to check
401 * @return -1 on errors, otherwise the number of free blocks
404 GNUNET_DISK_get_blocks_available (const char *part)
409 if (0 != statvfs (part, &buf))
411 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "statfs", part);
421 path = GNUNET_STRINGS_filename_expand (part);
424 memcpy (szDrive, path, 3);
427 if (!GetDiskFreeSpace (szDrive, &dwDummy, &dwDummy, &dwBlocks, &dwDummy))
429 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
430 _("`%s' failed for drive `%s': %u\n"),
431 "GetDiskFreeSpace", szDrive, GetLastError ());
439 if (0 != statfs (part, &s))
441 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "statfs", part);
450 * Test if "fil" is a directory.
451 * Will not print an error message if the directory
452 * does not exist. Will log errors if GNUNET_SYSERR is
453 * returned (i.e., a file exists with the same name).
455 * @param fil filename to test
456 * @return GNUNET_YES if yes, GNUNET_NO if not, GNUNET_SYSERR if it
460 GNUNET_DISK_directory_test (const char *fil)
462 struct stat filestat;
465 ret = STAT (fil, &filestat);
470 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "stat", fil);
471 return GNUNET_SYSERR;
475 if (!S_ISDIR (filestat.st_mode))
477 if (ACCESS (fil, R_OK | X_OK) < 0)
479 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "access", fil);
480 return GNUNET_SYSERR;
486 * Check that fil corresponds to a filename
487 * (of a file that exists and that is not a directory).
489 * @param fil filename to check
490 * @return GNUNET_YES if yes, GNUNET_NO if not a file, GNUNET_SYSERR if something
491 * else (will print an error message in that case, too).
494 GNUNET_DISK_file_test (const char *fil)
496 struct stat filestat;
500 rdir = GNUNET_STRINGS_filename_expand (fil);
502 return GNUNET_SYSERR;
504 ret = STAT (rdir, &filestat);
509 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "stat", rdir);
511 return GNUNET_SYSERR;
516 if (!S_ISREG (filestat.st_mode))
521 if (ACCESS (rdir, R_OK) < 0)
523 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "access", rdir);
525 return GNUNET_SYSERR;
533 * Implementation of "mkdir -p"
534 * @param dir the directory to create
535 * @returns GNUNET_OK on success, GNUNET_SYSERR on failure
538 GNUNET_DISK_directory_create (const char *dir)
545 rdir = GNUNET_STRINGS_filename_expand (dir);
547 return GNUNET_SYSERR;
551 pos = 1; /* skip heading '/' */
553 /* Local or Network path? */
554 if (strncmp (rdir, "\\\\", 2) == 0)
559 if (rdir[pos] == '\\')
569 pos = 3; /* strlen("C:\\") */
574 if ((rdir[pos] == DIR_SEPARATOR) || (pos == len))
577 ret = GNUNET_DISK_directory_test (rdir);
578 if (ret == GNUNET_SYSERR)
581 return GNUNET_SYSERR;
583 if (ret == GNUNET_NO)
586 ret = mkdir (rdir, S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH); /* 755 */
590 if ((ret != 0) && (errno != EEXIST))
592 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR, "mkdir", rdir);
594 return GNUNET_SYSERR;
597 rdir[pos] = DIR_SEPARATOR;
607 * Create the directory structure for storing
610 * @param filename name of a file in the directory
611 * @returns GNUNET_OK on success,
612 * GNUNET_SYSERR on failure,
613 * GNUNET_NO if the directory
614 * exists but is not writeable for us
617 GNUNET_DISK_directory_create_for_file (const char *filename)
623 rdir = GNUNET_STRINGS_filename_expand (filename);
625 return GNUNET_SYSERR;
627 while ((len > 0) && (rdir[len] != DIR_SEPARATOR))
630 ret = GNUNET_DISK_directory_create (rdir);
631 if ((ret == GNUNET_OK) && (0 != ACCESS (rdir, W_OK)))
639 * Read the contents of a binary file into a buffer.
640 * @param h handle to an open file
641 * @param result the buffer to write the result to
642 * @param len the maximum number of bytes to read
643 * @return the number of bytes read on success, GNUNET_SYSERR on failure
646 GNUNET_DISK_file_read (const struct GNUNET_DISK_FileHandle * h, void *result,
652 return GNUNET_SYSERR;
658 if (h->type != GNUNET_PIPE)
660 if (!ReadFile (h->h, result, len, &bytesRead, NULL))
662 SetErrnoFromWinError (GetLastError ());
663 return GNUNET_SYSERR;
668 if (!ReadFile (h->h, result, len, NULL, h->oOverlapRead))
670 if (GetLastError () != ERROR_IO_PENDING)
672 SetErrnoFromWinError (GetLastError ());
673 return GNUNET_SYSERR;
676 GetOverlappedResult (h->h, h->oOverlapRead, &bytesRead, TRUE);
680 return read (h->fd, result, len);
686 * Read the contents of a binary file into a buffer.
688 * @param fn file name
689 * @param result the buffer to write the result to
690 * @param len the maximum number of bytes to read
691 * @return number of bytes read, GNUNET_SYSERR on failure
694 GNUNET_DISK_fn_read (const char *fn, void *result, size_t len)
696 struct GNUNET_DISK_FileHandle *fh;
699 fh = GNUNET_DISK_file_open (fn, GNUNET_DISK_OPEN_READ, GNUNET_DISK_PERM_NONE);
701 return GNUNET_SYSERR;
702 ret = GNUNET_DISK_file_read (fh, result, len);
703 GNUNET_assert (GNUNET_OK == GNUNET_DISK_file_close (fh));
710 * Write a buffer to a file.
711 * @param h handle to open file
712 * @param buffer the data to write
713 * @param n number of bytes to write
714 * @return number of bytes written on success, GNUNET_SYSERR on error
717 GNUNET_DISK_file_write (const struct GNUNET_DISK_FileHandle * h,
718 const void *buffer, size_t n)
723 return GNUNET_SYSERR;
729 if (h->type != GNUNET_PIPE)
731 if (!WriteFile (h->h, buffer, n, &bytesWritten, NULL))
733 SetErrnoFromWinError (GetLastError ());
734 return GNUNET_SYSERR;
740 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "It is a pipe trying to write\n");
742 if (!WriteFile (h->h, buffer, n, NULL, h->oOverlapWrite))
744 if (GetLastError () != ERROR_IO_PENDING)
746 SetErrnoFromWinError (GetLastError ());
748 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Error writing to pipe\n");
750 return GNUNET_SYSERR;
754 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Will get overlapped result\n");
756 GetOverlappedResult (h->h, h->oOverlapWrite, &bytesWritten, TRUE);
760 return write (h->fd, buffer, n);
765 * Write a buffer to a file. If the file is longer than the
766 * number of bytes that will be written, it will be truncated.
768 * @param fn file name
769 * @param buffer the data to write
770 * @param n number of bytes to write
771 * @param mode file permissions
772 * @return number of bytes written on success, GNUNET_SYSERR on error
775 GNUNET_DISK_fn_write (const char *fn, const void *buffer,
776 size_t n, enum GNUNET_DISK_AccessPermissions mode)
778 struct GNUNET_DISK_FileHandle *fh;
781 fh = GNUNET_DISK_file_open (fn,
782 GNUNET_DISK_OPEN_WRITE
783 | GNUNET_DISK_OPEN_TRUNCATE
784 | GNUNET_DISK_OPEN_CREATE, mode);
786 return GNUNET_SYSERR;
787 ret = GNUNET_DISK_file_write (fh, buffer, n);
788 GNUNET_assert (GNUNET_OK == GNUNET_DISK_file_close (fh));
793 * Scan a directory for files.
795 * @param dirName the name of the directory
796 * @param callback the method to call for each file,
797 * can be NULL, in that case, we only count
798 * @param callback_cls closure for callback
799 * @return the number of files found, GNUNET_SYSERR on error or
800 * ieration aborted by callback returning GNUNET_SYSERR
803 GNUNET_DISK_directory_scan (const char *dirName,
804 GNUNET_FileNameCallback callback,
808 struct dirent *finfo;
813 unsigned int name_len;
816 GNUNET_assert (dirName != NULL);
817 dname = GNUNET_STRINGS_filename_expand (dirName);
819 return GNUNET_SYSERR;
820 while ((strlen (dname) > 0) && (dname[strlen (dname) - 1] == DIR_SEPARATOR))
821 dname[strlen (dname) - 1] = '\0';
822 if (0 != STAT (dname, &istat))
824 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "stat", dname);
826 return GNUNET_SYSERR;
828 if (!S_ISDIR (istat.st_mode))
830 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
831 _("Expected `%s' to be a directory!\n"), dirName);
833 return GNUNET_SYSERR;
836 dinfo = OPENDIR (dname);
837 if ((errno == EACCES) || (dinfo == NULL))
839 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "opendir", dname);
843 return GNUNET_SYSERR;
846 n_size = strlen (dname) + name_len + 2;
847 name = GNUNET_malloc (n_size);
848 while ((finfo = readdir (dinfo)) != NULL)
850 if ((0 == strcmp (finfo->d_name, ".")) ||
851 (0 == strcmp (finfo->d_name, "..")))
853 if (callback != NULL)
855 if (name_len < strlen (finfo->d_name))
858 name_len = strlen (finfo->d_name);
859 n_size = strlen (dname) + name_len + 2;
860 name = GNUNET_malloc (n_size);
862 /* dname can end in "/" only if dname == "/";
863 * if dname does not end in "/", we need to add
864 * a "/" (otherwise, we must not!) */
865 GNUNET_snprintf (name,
869 (strcmp (dname, DIR_SEPARATOR_STR) ==
870 0) ? "" : DIR_SEPARATOR_STR, finfo->d_name);
871 if (GNUNET_OK != callback (callback_cls, name))
876 return GNUNET_SYSERR;
889 * Opaque handle used for iterating over a directory.
891 struct GNUNET_DISK_DirectoryIterator
895 * Function to call on directory entries.
897 GNUNET_DISK_DirectoryIteratorCallback callback;
900 * Closure for callback.
905 * Reference to directory.
915 * Next filename to process.
922 enum GNUNET_SCHEDULER_Priority priority;
928 * Task used by the directory iterator.
931 directory_iterator_task (void *cls,
932 const struct GNUNET_SCHEDULER_TaskContext *tc)
934 struct GNUNET_DISK_DirectoryIterator *iter = cls;
937 name = iter->next_name;
938 GNUNET_assert (name != NULL);
939 iter->next_name = NULL;
940 iter->callback (iter->callback_cls, iter, name, iter->dirname);
946 * This function must be called during the DiskIteratorCallback
947 * (exactly once) to schedule the task to process the next
948 * filename in the directory (if there is one).
950 * @param iter opaque handle for the iterator
951 * @param can set to GNUNET_YES to terminate the iteration early
952 * @return GNUNET_YES if iteration will continue,
953 * GNUNET_NO if this was the last entry (and iteration is complete),
954 * GNUNET_SYSERR if abort was YES
957 GNUNET_DISK_directory_iterator_next (struct GNUNET_DISK_DirectoryIterator
960 struct dirent *finfo;
962 GNUNET_assert (iter->next_name == NULL);
963 if (can == GNUNET_YES)
965 closedir (iter->directory);
966 GNUNET_free (iter->dirname);
968 return GNUNET_SYSERR;
970 while (NULL != (finfo = readdir (iter->directory)))
972 if ((0 == strcmp (finfo->d_name, ".")) ||
973 (0 == strcmp (finfo->d_name, "..")))
975 GNUNET_asprintf (&iter->next_name,
976 "%s%s%s", iter->dirname, DIR_SEPARATOR_STR, finfo->d_name);
981 GNUNET_DISK_directory_iterator_next (iter, GNUNET_YES);
984 GNUNET_SCHEDULER_add_with_priority (iter->priority,
985 &directory_iterator_task, iter);
991 * Scan a directory for files using the scheduler to run a task for
992 * each entry. The name of the directory must be expanded first (!).
993 * If a scheduler does not need to be used, GNUNET_DISK_directory_scan
994 * may provide a simpler API.
996 * @param prio priority to use
997 * @param dirName the name of the directory
998 * @param callback the method to call for each file
999 * @param callback_cls closure for callback
1002 GNUNET_DISK_directory_iterator_start (enum GNUNET_SCHEDULER_Priority prio,
1003 const char *dirName,
1004 GNUNET_DISK_DirectoryIteratorCallback
1005 callback, void *callback_cls)
1007 struct GNUNET_DISK_DirectoryIterator *di;
1009 di = GNUNET_malloc (sizeof (struct GNUNET_DISK_DirectoryIterator));
1010 di->callback = callback;
1011 di->callback_cls = callback_cls;
1012 di->directory = OPENDIR (dirName);
1013 if (di->directory == NULL)
1016 callback (callback_cls, NULL, NULL, NULL);
1019 di->dirname = GNUNET_strdup (dirName);
1020 di->priority = prio;
1021 GNUNET_DISK_directory_iterator_next (di, GNUNET_NO);
1026 * Function that removes the given directory by calling
1027 * "GNUNET_DISK_directory_remove".
1029 * @param unused not used
1030 * @param fn directory to remove
1034 remove_helper (void *unused, const char *fn)
1036 (void) GNUNET_DISK_directory_remove (fn);
1042 * Remove all files in a directory (rm -rf). Call with
1046 * @param fileName the file to remove
1047 * @return GNUNET_OK on success, GNUNET_SYSERR on error
1050 GNUNET_DISK_directory_remove (const char *fileName)
1054 if (0 != LSTAT (fileName, &istat))
1055 return GNUNET_NO; /* file may not exist... */
1056 CHMOD (fileName, S_IWUSR | S_IRUSR | S_IXUSR);
1057 if (UNLINK (fileName) == 0)
1059 if ((errno != EISDIR) &&
1060 /* EISDIR is not sufficient in all cases, e.g.
1061 * sticky /tmp directory may result in EPERM on BSD.
1062 * So we also explicitly check "isDirectory" */
1063 (GNUNET_YES != GNUNET_DISK_directory_test (fileName)))
1065 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "rmdir", fileName);
1066 return GNUNET_SYSERR;
1068 if (GNUNET_SYSERR ==
1069 GNUNET_DISK_directory_scan (fileName, &remove_helper, NULL))
1070 return GNUNET_SYSERR;
1071 if (0 != RMDIR (fileName))
1073 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "rmdir", fileName);
1074 return GNUNET_SYSERR;
1083 * @param src file to copy
1084 * @param dst destination file name
1085 * @return GNUNET_OK on success, GNUNET_SYSERR on error
1088 GNUNET_DISK_file_copy (const char *src, const char *dst)
1094 struct GNUNET_DISK_FileHandle *in;
1095 struct GNUNET_DISK_FileHandle *out;
1097 if (GNUNET_OK != GNUNET_DISK_file_size (src, &size, GNUNET_YES))
1098 return GNUNET_SYSERR;
1100 in = GNUNET_DISK_file_open (src, GNUNET_DISK_OPEN_READ,
1101 GNUNET_DISK_PERM_NONE);
1103 return GNUNET_SYSERR;
1104 out = GNUNET_DISK_file_open (dst, GNUNET_DISK_OPEN_WRITE
1105 | GNUNET_DISK_OPEN_CREATE |
1106 GNUNET_DISK_OPEN_FAILIFEXISTS,
1107 GNUNET_DISK_PERM_USER_READ |
1108 GNUNET_DISK_PERM_USER_WRITE |
1109 GNUNET_DISK_PERM_GROUP_READ |
1110 GNUNET_DISK_PERM_GROUP_WRITE);
1113 GNUNET_DISK_file_close (in);
1114 return GNUNET_SYSERR;
1116 buf = GNUNET_malloc (COPY_BLK_SIZE);
1119 len = COPY_BLK_SIZE;
1120 if (len > size - pos)
1122 if (len != GNUNET_DISK_file_read (in, buf, len))
1124 if (len != GNUNET_DISK_file_write (out, buf, len))
1129 GNUNET_DISK_file_close (in);
1130 GNUNET_DISK_file_close (out);
1134 GNUNET_DISK_file_close (in);
1135 GNUNET_DISK_file_close (out);
1136 return GNUNET_SYSERR;
1141 * @brief Removes special characters as ':' from a filename.
1142 * @param fn the filename to canonicalize
1145 GNUNET_DISK_filename_canonicalize (char *fn)
1155 if (c == '/' || c == '\\' || c == ':' || c == '*' || c == '?' ||
1156 c == '"' || c == '<' || c == '>' || c == '|')
1168 * @brief Change owner of a file
1170 * @param filename name of file to change the owner of
1171 * @param user name of the new owner
1172 * @return GNUNET_OK on success, GNUNET_SYSERR on failure
1175 GNUNET_DISK_file_change_owner (const char *filename, const char *user)
1180 pws = getpwnam (user);
1183 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1184 _("Cannot obtain information about user `%s': %s\n"),
1185 user, STRERROR (errno));
1186 return GNUNET_SYSERR;
1188 if (0 != chown (filename, pws->pw_uid, pws->pw_gid))
1189 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "chown", filename);
1196 * Lock a part of a file
1197 * @param fh file handle
1198 * @param lockStart absolute position from where to lock
1199 * @param lockEnd absolute position until where to lock
1200 * @param excl GNUNET_YES for an exclusive lock
1201 * @return GNUNET_OK on success, GNUNET_SYSERR on error
1204 GNUNET_DISK_file_lock (struct GNUNET_DISK_FileHandle *fh, off_t lockStart,
1205 off_t lockEnd, int excl)
1210 return GNUNET_SYSERR;
1216 memset (&fl, 0, sizeof (struct flock));
1217 fl.l_type = excl ? F_WRLCK : F_RDLCK;
1218 fl.l_whence = SEEK_SET;
1219 fl.l_start = lockStart;
1222 return fcntl (fh->fd, F_SETLK, &fl) != 0 ? GNUNET_SYSERR : GNUNET_OK;
1226 memset (&o, 0, sizeof (OVERLAPPED));
1227 o.Offset = lockStart;
1229 if (!LockFileEx (fh->h, (excl ? LOCKFILE_EXCLUSIVE_LOCK : 0)
1230 | LOCKFILE_FAIL_IMMEDIATELY, 0, lockEnd - lockStart, 0, &o))
1232 SetErrnoFromWinError (GetLastError ());
1233 return GNUNET_SYSERR;
1242 * Unlock a part of a file
1243 * @param fh file handle
1244 * @param unlockStart absolute position from where to unlock
1245 * @param unlockEnd absolute position until where to unlock
1246 * @return GNUNET_OK on success, GNUNET_SYSERR on error
1249 GNUNET_DISK_file_unlock (struct GNUNET_DISK_FileHandle *fh, off_t unlockStart,
1255 return GNUNET_SYSERR;
1261 memset (&fl, 0, sizeof (struct flock));
1262 fl.l_type = F_UNLCK;
1263 fl.l_whence = SEEK_SET;
1264 fl.l_start = unlockStart;
1265 fl.l_len = unlockEnd;
1267 return fcntl (fh->fd, F_SETLK, &fl) != 0 ? GNUNET_SYSERR : GNUNET_OK;
1271 memset (&o, 0, sizeof (OVERLAPPED));
1272 o.Offset = unlockStart;
1274 if (!UnlockFileEx (fh->h, 0, unlockEnd - unlockStart, 0, &o))
1276 SetErrnoFromWinError (GetLastError ());
1277 return GNUNET_SYSERR;
1286 * Open a file. Note that the access permissions will only be
1287 * used if a new file is created and if the underlying operating
1288 * system supports the given permissions.
1290 * @param fn file name to be opened
1291 * @param flags opening flags, a combination of GNUNET_DISK_OPEN_xxx bit flags
1292 * @param perm permissions for the newly created file, use
1293 * GNUNET_DISK_PERM_USER_NONE if a file could not be created by this
1294 * call (because of flags)
1295 * @return IO handle on success, NULL on error
1297 struct GNUNET_DISK_FileHandle *
1298 GNUNET_DISK_file_open (const char *fn,
1299 enum GNUNET_DISK_OpenFlags flags,
1300 enum GNUNET_DISK_AccessPermissions perm)
1303 struct GNUNET_DISK_FileHandle *ret;
1315 expfn = GNUNET_STRINGS_filename_expand (fn);
1320 if (GNUNET_DISK_OPEN_READWRITE == (flags & GNUNET_DISK_OPEN_READWRITE))
1321 oflags = O_RDWR; /* note: O_RDWR is NOT always O_RDONLY | O_WRONLY */
1322 else if (flags & GNUNET_DISK_OPEN_READ)
1324 else if (flags & GNUNET_DISK_OPEN_WRITE)
1329 GNUNET_free (expfn);
1332 if (flags & GNUNET_DISK_OPEN_FAILIFEXISTS)
1333 oflags |= (O_CREAT | O_EXCL);
1334 if (flags & GNUNET_DISK_OPEN_TRUNCATE)
1336 if (flags & GNUNET_DISK_OPEN_APPEND)
1338 if (flags & GNUNET_DISK_OPEN_CREATE)
1340 (void) GNUNET_DISK_directory_create_for_file (expfn);
1342 mode = translate_unix_perms (perm);
1345 fd = open (expfn, oflags | O_LARGEFILE, mode);
1348 if (0 == (flags & GNUNET_DISK_OPEN_FAILIFEXISTS))
1349 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "open", expfn);
1351 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_DEBUG, "open", expfn);
1352 GNUNET_free (expfn);
1359 if (GNUNET_DISK_OPEN_READWRITE == (flags & GNUNET_DISK_OPEN_READWRITE))
1360 access = FILE_READ_DATA | FILE_WRITE_DATA;
1361 else if (flags & GNUNET_DISK_OPEN_READ)
1362 access = FILE_READ_DATA;
1363 else if (flags & GNUNET_DISK_OPEN_WRITE)
1364 access = FILE_WRITE_DATA;
1366 if (flags & GNUNET_DISK_OPEN_FAILIFEXISTS)
1370 else if (flags & GNUNET_DISK_OPEN_CREATE)
1372 (void) GNUNET_DISK_directory_create_for_file (expfn);
1373 if (flags & GNUNET_DISK_OPEN_TRUNCATE)
1374 disp = CREATE_ALWAYS;
1378 else if (flags & GNUNET_DISK_OPEN_TRUNCATE)
1380 disp = TRUNCATE_EXISTING;
1384 disp = OPEN_EXISTING;
1387 /* TODO: access priviledges? */
1388 h = CreateFile (expfn, access, FILE_SHARE_DELETE | FILE_SHARE_READ
1389 | FILE_SHARE_WRITE, NULL, disp, FILE_ATTRIBUTE_NORMAL, NULL);
1390 if (h == INVALID_HANDLE_VALUE)
1392 SetErrnoFromWinError (GetLastError ());
1393 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "open", expfn);
1394 GNUNET_free (expfn);
1398 if (flags & GNUNET_DISK_OPEN_APPEND)
1399 if (SetFilePointer (h, 0, 0, FILE_END) == INVALID_SET_FILE_POINTER)
1401 SetErrnoFromWinError (GetLastError ());
1402 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "SetFilePointer",
1405 GNUNET_free (expfn);
1410 ret = GNUNET_malloc (sizeof (struct GNUNET_DISK_FileHandle));
1413 ret->type = GNUNET_DISK_FILE;
1417 GNUNET_free (expfn);
1423 * Close an open file
1424 * @param h file handle
1425 * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
1428 GNUNET_DISK_file_close (struct GNUNET_DISK_FileHandle *h)
1433 return GNUNET_SYSERR;
1437 if (!CloseHandle (h->h))
1439 SetErrnoFromWinError (GetLastError ());
1440 GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "close");
1441 GNUNET_free (h->oOverlapRead);
1442 GNUNET_free (h->oOverlapWrite);
1444 return GNUNET_SYSERR;
1447 if (close (h->fd) != 0)
1449 GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "close");
1451 return GNUNET_SYSERR;
1460 * Construct full path to a file inside of the private
1461 * directory used by GNUnet. Also creates the corresponding
1462 * directory. If the resulting name is supposed to be
1463 * a directory, end the last argument in '/' (or pass
1464 * DIR_SEPARATOR_STR as the last argument before NULL).
1466 * @param cfg configuration to use (determines HOME)
1467 * @param serviceName name of the service
1468 * @param ... is NULL-terminated list of
1469 * path components to append to the
1470 * private directory name.
1471 * @return the constructed filename
1474 GNUNET_DISK_get_home_filename (const struct GNUNET_CONFIGURATION_Handle *cfg,
1475 const char *serviceName, ...)
1481 unsigned int needed;
1484 GNUNET_CONFIGURATION_get_value_filename (cfg, serviceName, "HOME", &pfx))
1488 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1489 _("No `%s' specified for service `%s' in configuration.\n"),
1490 "HOME", serviceName);
1493 needed = strlen (pfx) + 2;
1494 if ((pfx[strlen (pfx) - 1] != '/') && (pfx[strlen (pfx) - 1] != '\\'))
1496 va_start (ap, serviceName);
1499 c = va_arg (ap, const char *);
1503 needed += strlen (c);
1504 if ((c[strlen (c) - 1] != '/') && (c[strlen (c) - 1] != '\\'))
1508 ret = GNUNET_malloc (needed);
1511 va_start (ap, serviceName);
1514 c = va_arg (ap, const char *);
1518 if ((c[strlen (c) - 1] != '/') && (c[strlen (c) - 1] != '\\'))
1519 strcat (ret, DIR_SEPARATOR_STR);
1523 if ((ret[strlen (ret) - 1] != '/') && (ret[strlen (ret) - 1] != '\\'))
1524 (void) GNUNET_DISK_directory_create_for_file (ret);
1526 (void) GNUNET_DISK_directory_create (ret);
1532 * Handle for a memory-mapping operation.
1534 struct GNUNET_DISK_MapHandle
1537 * Address where the map is in memory.
1543 * Underlying OS handle.
1548 * Number of bytes mapped.
1556 #define MAP_FAILED ((void *) -1)
1560 * Map a file into memory
1562 * @param h open file handle
1563 * @param m handle to the new mapping
1564 * @param access access specification, GNUNET_DISK_MAP_TYPE_xxx
1565 * @param len size of the mapping
1566 * @return pointer to the mapped memory region, NULL on failure
1569 GNUNET_DISK_file_map (const struct GNUNET_DISK_FileHandle *h,
1570 struct GNUNET_DISK_MapHandle **m,
1571 enum GNUNET_DISK_MapType access, size_t len)
1580 DWORD mapAccess, protect;
1582 if ((access & GNUNET_DISK_MAP_TYPE_READ) &&
1583 (access & GNUNET_DISK_MAP_TYPE_WRITE))
1585 protect = PAGE_READWRITE;
1586 mapAccess = FILE_MAP_ALL_ACCESS;
1588 else if (access & GNUNET_DISK_MAP_TYPE_READ)
1590 protect = PAGE_READONLY;
1591 mapAccess = FILE_MAP_READ;
1593 else if (access & GNUNET_DISK_MAP_TYPE_WRITE)
1595 protect = PAGE_READWRITE;
1596 mapAccess = FILE_MAP_WRITE;
1604 *m = GNUNET_malloc (sizeof (struct GNUNET_DISK_MapHandle));
1605 (*m)->h = CreateFileMapping (h->h, NULL, protect, 0, 0, NULL);
1606 if ((*m)->h == INVALID_HANDLE_VALUE)
1608 SetErrnoFromWinError (GetLastError ());
1613 (*m)->addr = MapViewOfFile ((*m)->h, mapAccess, 0, 0, len);
1616 SetErrnoFromWinError (GetLastError ());
1617 CloseHandle ((*m)->h);
1626 if (access & GNUNET_DISK_MAP_TYPE_READ)
1628 if (access & GNUNET_DISK_MAP_TYPE_WRITE)
1630 *m = GNUNET_malloc (sizeof (struct GNUNET_DISK_MapHandle));
1631 (*m)->addr = mmap (NULL, len, prot, MAP_SHARED, h->fd, 0);
1632 GNUNET_assert (NULL != (*m)->addr);
1633 if (MAP_FAILED == (*m)->addr)
1645 * @param h mapping handle
1646 * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
1649 GNUNET_DISK_file_unmap (struct GNUNET_DISK_MapHandle *h)
1656 return GNUNET_SYSERR;
1660 ret = UnmapViewOfFile (h->addr) ? GNUNET_OK : GNUNET_SYSERR;
1661 if (ret != GNUNET_OK)
1662 SetErrnoFromWinError (GetLastError ());
1663 if (!CloseHandle (h->h) && (ret == GNUNET_OK))
1665 ret = GNUNET_SYSERR;
1666 SetErrnoFromWinError (GetLastError ());
1669 ret = munmap (h->addr, h->len) != -1 ? GNUNET_OK : GNUNET_SYSERR;
1677 * Write file changes to disk
1678 * @param h handle to an open file
1679 * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
1682 GNUNET_DISK_file_sync (const struct GNUNET_DISK_FileHandle *h)
1687 return GNUNET_SYSERR;
1693 ret = FlushFileBuffers (h->h) ? GNUNET_OK : GNUNET_SYSERR;
1694 if (ret != GNUNET_OK)
1695 SetErrnoFromWinError (GetLastError ());
1697 #elif defined(FREEBSD) || defined(OPENBSD) || defined(DARWIN)
1698 return fsync (h->fd) == -1 ? GNUNET_SYSERR : GNUNET_OK;
1700 return fdatasync (h->fd) == -1 ? GNUNET_SYSERR : GNUNET_OK;
1705 /* Copyright Bob Byrnes <byrnes <at> curl.com>
1706 http://permalink.gmane.org/gmane.os.cygwin.patches/2121
1708 /* Create a pipe, and return handles to the read and write ends,
1709 just like CreatePipe, but ensure that the write end permits
1710 FILE_READ_ATTRIBUTES access, on later versions of win32 where
1711 this is supported. This access is needed by NtQueryInformationFile,
1712 which is used to implement select and nonblocking writes.
1713 Note that the return value is either NO_ERROR or GetLastError,
1714 unlike CreatePipe, which returns a bool for success or failure. */
1716 create_selectable_pipe (PHANDLE read_pipe_ptr,
1717 PHANDLE write_pipe_ptr,
1718 LPSECURITY_ATTRIBUTES sa_ptr,
1719 DWORD psize, DWORD dwReadMode, DWORD dwWriteMode)
1721 /* Default to error. */
1722 *read_pipe_ptr = *write_pipe_ptr = INVALID_HANDLE_VALUE;
1724 HANDLE read_pipe = INVALID_HANDLE_VALUE, write_pipe = INVALID_HANDLE_VALUE;
1726 /* Ensure that there is enough pipe buffer space for atomic writes. */
1727 if (psize < PIPE_BUF)
1730 char pipename[MAX_PATH];
1732 /* Retry CreateNamedPipe as long as the pipe name is in use.
1733 * Retrying will probably never be necessary, but we want
1734 * to be as robust as possible. */
1737 static volatile LONG pipe_unique_id;
1739 snprintf (pipename, sizeof pipename, "\\\\.\\pipe\\gnunet-%d-%ld",
1740 getpid (), InterlockedIncrement ((LONG *) & pipe_unique_id));
1742 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1743 "CreateNamedPipe: name = %s, size = %lu\n", pipename, psize);
1745 /* Use CreateNamedPipe instead of CreatePipe, because the latter
1746 * returns a write handle that does not permit FILE_READ_ATTRIBUTES
1747 * access, on versions of win32 earlier than WinXP SP2.
1748 * CreatePipe also stupidly creates a full duplex pipe, which is
1749 * a waste, since only a single direction is actually used.
1750 * It's important to only allow a single instance, to ensure that
1751 * the pipe was not created earlier by some other process, even if
1752 * the pid has been reused. We avoid FILE_FLAG_FIRST_PIPE_INSTANCE
1753 * because that is only available for Win2k SP2 and WinXP. */
1754 read_pipe = CreateNamedPipeA (pipename, PIPE_ACCESS_INBOUND | dwReadMode, PIPE_TYPE_BYTE | PIPE_READMODE_BYTE, 1, /* max instances */
1755 psize, /* output buffer size */
1756 psize, /* input buffer size */
1757 NMPWAIT_USE_DEFAULT_WAIT, sa_ptr);
1759 if (read_pipe != INVALID_HANDLE_VALUE)
1762 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "pipe read handle = %p\n",
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 GNUNET_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 GNUNET_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 GNUNET_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 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "pipe read handle = %p\n",
1799 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "pipe write handle = %p\n",
1804 err = GetLastError ();
1805 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "CreatePipe failed: %d\n", err);
1808 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "CreateNamedPipe failed: %d\n", err);
1814 GNUNET_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 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "CreateFile failed: %d\n", err);
1831 CloseHandle (read_pipe);
1835 GNUNET_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 GNUNET_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 GNUNET_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 (GetCurrentProcess (), p->fd[0]->h,
1925 GetCurrentProcess (), &tmp_handle, 0,
1926 inherit_read == GNUNET_YES ? TRUE : FALSE,
1927 DUPLICATE_SAME_ACCESS))
1929 SetErrnoFromWinError (GetLastError ());
1930 CloseHandle (p->fd[0]->h);
1931 CloseHandle (p->fd[1]->h);
1935 CloseHandle (p->fd[0]->h);
1936 p->fd[0]->h = tmp_handle;
1938 if (!DuplicateHandle (GetCurrentProcess (), p->fd[1]->h,
1939 GetCurrentProcess (), &tmp_handle, 0,
1940 inherit_write == GNUNET_YES ? TRUE : FALSE,
1941 DUPLICATE_SAME_ACCESS))
1943 SetErrnoFromWinError (GetLastError ());
1944 CloseHandle (p->fd[0]->h);
1945 CloseHandle (p->fd[1]->h);
1949 CloseHandle (p->fd[1]->h);
1950 p->fd[1]->h = tmp_handle;
1956 SetNamedPipeHandleState (p->fd[0]->h, &mode, NULL, NULL);
1957 SetNamedPipeHandleState (p->fd[1]->h, &mode, NULL, NULL);
1958 /* this always fails on Windows 95, so we don't care about error handling */
1960 p->fd[0]->type = GNUNET_PIPE;
1961 p->fd[1]->type = GNUNET_PIPE;
1963 p->fd[0]->oOverlapRead = GNUNET_malloc (sizeof (OVERLAPPED));
1964 p->fd[0]->oOverlapWrite = GNUNET_malloc (sizeof (OVERLAPPED));
1965 p->fd[1]->oOverlapRead = GNUNET_malloc (sizeof (OVERLAPPED));
1966 p->fd[1]->oOverlapWrite = GNUNET_malloc (sizeof (OVERLAPPED));
1968 p->fd[0]->oOverlapRead->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
1969 p->fd[0]->oOverlapWrite->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
1971 p->fd[1]->oOverlapRead->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
1972 p->fd[1]->oOverlapWrite->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
1980 * Closes an interprocess channel
1982 * @param p pipe to close
1983 * @param end which end of the pipe to close
1984 * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
1987 GNUNET_DISK_pipe_close_end (struct GNUNET_DISK_PipeHandle *p,
1988 enum GNUNET_DISK_PipeEnd end)
1990 int ret = GNUNET_OK;
1994 if (end == GNUNET_DISK_PIPE_END_READ)
1996 if (!CloseHandle (p->fd[0]->h))
1998 SetErrnoFromWinError (GetLastError ());
1999 ret = GNUNET_SYSERR;
2001 p->fd[0]->h = INVALID_HANDLE_VALUE;
2003 else if (end == GNUNET_DISK_PIPE_END_WRITE)
2005 if (!CloseHandle (p->fd[1]->h))
2007 SetErrnoFromWinError (GetLastError ());
2008 ret = GNUNET_SYSERR;
2010 p->fd[1]->h = INVALID_HANDLE_VALUE;
2015 if (end == GNUNET_DISK_PIPE_END_READ)
2017 if (0 != close (p->fd[0]->fd))
2019 ret = GNUNET_SYSERR;
2024 else if (end == GNUNET_DISK_PIPE_END_WRITE)
2026 if (0 != close (p->fd[1]->fd))
2028 ret = GNUNET_SYSERR;
2039 * Closes an interprocess channel
2041 * @param p pipe to close
2042 * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
2045 GNUNET_DISK_pipe_close (struct GNUNET_DISK_PipeHandle *p)
2047 int ret = GNUNET_OK;
2051 if (!CloseHandle (p->fd[0]->h))
2053 SetErrnoFromWinError (GetLastError ());
2054 ret = GNUNET_SYSERR;
2056 if (!CloseHandle (p->fd[1]->h))
2058 SetErrnoFromWinError (GetLastError ());
2059 ret = GNUNET_SYSERR;
2064 if (p->fd[0]->fd != -1)
2066 if (0 != close (p->fd[0]->fd))
2068 ret = GNUNET_SYSERR;
2073 if (p->fd[1]->fd != -1)
2075 if (0 != close (p->fd[1]->fd))
2077 ret = GNUNET_SYSERR;
2089 * Creates a named pipe/FIFO and opens it
2090 * @param fn pointer to the name of the named pipe or to NULL
2091 * @param flags open flags
2092 * @param perm access permissions
2093 * @return pipe handle on success, NULL on error
2095 struct GNUNET_DISK_FileHandle *
2096 GNUNET_DISK_npipe_create (char **fn,
2097 enum GNUNET_DISK_OpenFlags flags,
2098 enum GNUNET_DISK_AccessPermissions perm)
2101 struct GNUNET_DISK_FileHandle *ret;
2107 if (flags & GNUNET_DISK_OPEN_READWRITE)
2108 openMode = PIPE_ACCESS_DUPLEX;
2109 else if (flags & GNUNET_DISK_OPEN_READ)
2110 openMode = PIPE_ACCESS_INBOUND;
2111 else if (flags & GNUNET_DISK_OPEN_WRITE)
2112 openMode = PIPE_ACCESS_OUTBOUND;
2114 if (flags & GNUNET_DISK_OPEN_FAILIFEXISTS)
2115 openMode |= FILE_FLAG_FIRST_PIPE_INSTANCE;
2124 GNUNET_asprintf (&name, "\\\\.\\pipe\\%.246s", fn);
2126 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2127 "Trying to create an instance of named pipe `%s'\n", name);
2129 h = CreateNamedPipe (name, openMode | FILE_FLAG_OVERLAPPED,
2130 PIPE_TYPE_BYTE | PIPE_READMODE_BYTE, 2, 1, 1, 0,
2135 GNUNET_asprintf (fn, "\\\\.\\pipe\\gnunet-%llu",
2136 GNUNET_CRYPTO_random_u64 (GNUNET_CRYPTO_QUALITY_WEAK,
2139 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2140 "Trying to create unique named pipe `%s'\n", *fn);
2142 h = CreateNamedPipe (*fn,
2143 openMode | FILE_FLAG_OVERLAPPED |
2144 FILE_FLAG_FIRST_PIPE_INSTANCE,
2145 PIPE_TYPE_BYTE | PIPE_READMODE_BYTE, 2, 1, 1, 0,
2148 error_code = GetLastError ();
2151 /* don't re-set name to NULL yet */
2152 if (h == INVALID_HANDLE_VALUE)
2154 SetErrnoFromWinError (error_code);
2156 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2157 "Pipe creation have failed because of %d, errno is %d\n",
2163 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2164 "Pipe was to be unique, considering re-creation\n");
2168 if (error_code != ERROR_ACCESS_DENIED && error_code != ERROR_PIPE_BUSY)
2173 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2174 "Pipe name was not unique, trying again\n");
2184 ret = GNUNET_malloc (sizeof (*ret));
2186 ret->type = GNUNET_PIPE;
2188 ret->oOverlapRead = GNUNET_malloc (sizeof (OVERLAPPED));
2189 ret->oOverlapWrite = GNUNET_malloc (sizeof (OVERLAPPED));
2191 ret->oOverlapRead->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2192 ret->oOverlapWrite->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2198 char dir[] = "/tmp/gnunet-pipe-XXXXXX";
2200 if (mkdtemp (dir) == NULL)
2202 GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "mkdtemp");
2205 GNUNET_asprintf (fn, "%s/child-control", dir);
2208 if (mkfifo (*fn, translate_unix_perms (perm)) == -1)
2210 if ((errno != EEXIST) || (0 != (flags & GNUNET_DISK_OPEN_FAILIFEXISTS)))
2214 flags = flags & (~GNUNET_DISK_OPEN_FAILIFEXISTS);
2215 return GNUNET_DISK_file_open (*fn, flags, perm);
2221 * Opens already existing named pipe/FIFO
2223 * @param fn name of an existing named pipe
2224 * @param flags open flags
2225 * @param perm access permissions
2226 * @return pipe handle on success, NULL on error
2228 struct GNUNET_DISK_FileHandle *
2229 GNUNET_DISK_npipe_open (const char *fn,
2230 enum GNUNET_DISK_OpenFlags flags,
2231 enum GNUNET_DISK_AccessPermissions perm)
2234 struct GNUNET_DISK_FileHandle *ret;
2239 if (flags & GNUNET_DISK_OPEN_READWRITE)
2240 openMode = GENERIC_WRITE | GENERIC_READ;
2241 else if (flags & GNUNET_DISK_OPEN_READ)
2242 openMode = GENERIC_READ;
2243 else if (flags & GNUNET_DISK_OPEN_WRITE)
2244 openMode = GENERIC_WRITE;
2246 h = CreateFile (fn, openMode, 0, NULL, OPEN_EXISTING,
2247 FILE_FLAG_OVERLAPPED | FILE_READ_ATTRIBUTES, NULL);
2248 if (h == INVALID_HANDLE_VALUE)
2250 SetErrnoFromWinError (GetLastError ());
2254 ret = GNUNET_malloc (sizeof (*ret));
2256 ret->type = GNUNET_PIPE;
2257 ret->oOverlapRead = GNUNET_malloc (sizeof (OVERLAPPED));
2258 ret->oOverlapWrite = GNUNET_malloc (sizeof (OVERLAPPED));
2259 ret->oOverlapRead->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2260 ret->oOverlapWrite->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2264 flags = flags & (~GNUNET_DISK_OPEN_FAILIFEXISTS);
2265 return GNUNET_DISK_file_open (fn, flags, perm);
2270 * Closes a named pipe/FIFO
2271 * @param pipe named pipe
2272 * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
2275 GNUNET_DISK_npipe_close (struct GNUNET_DISK_FileHandle *pipe)
2278 return close (pipe->fd) == 0 ? GNUNET_OK : GNUNET_SYSERR;
2282 ret = CloseHandle (pipe->h);
2285 SetErrnoFromWinError (GetLastError ());
2286 return GNUNET_SYSERR;
2295 * Get the handle to a particular pipe end
2298 * @param n end to access
2299 * @return handle for the respective end
2301 const struct GNUNET_DISK_FileHandle *
2302 GNUNET_DISK_pipe_handle (const struct GNUNET_DISK_PipeHandle *p,
2303 enum GNUNET_DISK_PipeEnd n)
2307 case GNUNET_DISK_PIPE_END_READ:
2308 case GNUNET_DISK_PIPE_END_WRITE:
2318 * Retrieve OS file handle
2320 * @param fh GNUnet file descriptor
2321 * @param dst destination buffer
2322 * @param dst_len length of dst
2323 * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
2326 GNUNET_DISK_internal_file_handle_ (const struct GNUNET_DISK_FileHandle *fh,
2327 void *dst, size_t dst_len)
2330 if (dst_len < sizeof (HANDLE))
2331 return GNUNET_SYSERR;
2332 *((HANDLE *) dst) = fh->h;
2334 if (dst_len < sizeof (int))
2335 return GNUNET_SYSERR;
2336 *((int *) dst) = fh->fd;