2 This file is part of GNUnet.
3 (C) 2001, 2002, 2005, 2006, 2009 Christian Grothoff (and other contributing authors)
5 GNUnet is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 2, or (at your
8 option) any later version.
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with GNUnet; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
23 * @brief disk IO convenience methods
24 * @author Christian Grothoff
29 #include "gnunet_common.h"
30 #include "gnunet_directories.h"
31 #include "gnunet_disk_lib.h"
32 #include "gnunet_scheduler_lib.h"
33 #include "gnunet_strings_lib.h"
34 #include "gnunet_crypto_lib.h"
37 #define LOG(kind,...) GNUNET_log_from (kind, "util", __VA_ARGS__)
39 #define LOG_STRERROR(kind,syscall) GNUNET_log_from_strerror (kind, "util", syscall)
41 #define LOG_STRERROR_FILE(kind,syscall,filename) GNUNET_log_from_strerror_file (kind, "util", syscall, filename)
43 #define DEBUG_NPIPE GNUNET_EXTRA_LOGGING
45 #define DEBUG_PIPE GNUNET_EXTRA_LOGGING
48 * Block size for IO for copying files.
50 #define COPY_BLK_SIZE 65536
54 #if defined(LINUX) || defined(CYGWIN)
57 #if defined(SOMEBSD) || defined(DARWIN)
58 #include <sys/param.h>
59 #include <sys/mount.h>
62 #include <sys/types.h>
63 #include <sys/statvfs.h>
68 ULONG PipeSerialNumber;
70 #define _IFMT 0170000 /* type of file */
71 #define _IFLNK 0120000 /* symbolic link */
72 #define S_ISLNK(m) (((m)&_IFMT) == _IFLNK)
74 #error PORT-ME: need to port statfs (how much space is left on the drive?)
80 #if !defined(SOMEBSD) && !defined(DARWIN) && !defined(WINDOWS)
84 #include <sys/statvfs.h>
89 * Handle used to manage a pipe.
91 struct GNUNET_DISK_PipeHandle
94 * File descriptors for the pipe.
96 struct GNUNET_DISK_FileHandle *fd[2];
101 * Closure for the recursion to determine the file size
104 struct GetFileSizeData
107 * Set to the total file size.
112 * GNUNET_YES if symbolic links should be included.
114 int include_sym_links;
119 translate_unix_perms (enum GNUNET_DISK_AccessPermissions perm)
124 if (perm & GNUNET_DISK_PERM_USER_READ)
126 if (perm & GNUNET_DISK_PERM_USER_WRITE)
128 if (perm & GNUNET_DISK_PERM_USER_EXEC)
130 if (perm & GNUNET_DISK_PERM_GROUP_READ)
132 if (perm & GNUNET_DISK_PERM_GROUP_WRITE)
134 if (perm & GNUNET_DISK_PERM_GROUP_EXEC)
136 if (perm & GNUNET_DISK_PERM_OTHER_READ)
138 if (perm & GNUNET_DISK_PERM_OTHER_WRITE)
140 if (perm & GNUNET_DISK_PERM_OTHER_EXEC)
148 * Iterate over all files in the given directory and
149 * accumulate their size.
151 * @param cls closure of type "struct GetFileSizeData"
152 * @param fn current filename we are looking at
153 * @return GNUNET_SYSERR on serious errors, otherwise GNUNET_OK
156 getSizeRec (void *cls, const char *fn)
158 struct GetFileSizeData *gfsd = cls;
167 if (0 != STAT64 (fn, &buf))
169 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "stat64", fn);
170 return GNUNET_SYSERR;
173 if (0 != STAT (fn, &buf))
175 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "stat", fn);
176 return GNUNET_SYSERR;
179 if ((!S_ISLNK (buf.st_mode)) || (gfsd->include_sym_links == GNUNET_YES))
180 gfsd->total += buf.st_size;
181 if ((S_ISDIR (buf.st_mode)) && (0 == ACCESS (fn, X_OK)) &&
182 ((!S_ISLNK (buf.st_mode)) || (gfsd->include_sym_links == GNUNET_YES)))
184 if (GNUNET_SYSERR == GNUNET_DISK_directory_scan (fn, &getSizeRec, gfsd))
185 return GNUNET_SYSERR;
192 * Checks whether a handle is invalid
194 * @param h handle to check
195 * @return GNUNET_YES if invalid, GNUNET_NO if valid
198 GNUNET_DISK_handle_invalid (const struct GNUNET_DISK_FileHandle *h)
201 return ((!h) || (h->h == INVALID_HANDLE_VALUE)) ? GNUNET_YES : GNUNET_NO;
203 return ((!h) || (h->fd == -1)) ? GNUNET_YES : GNUNET_NO;
208 * Get the size of an open file.
210 * @param fh open file handle
211 * @param size where to write size of the file
212 * @return GNUNET_OK on success, GNUNET_SYSERR on error
215 GNUNET_DISK_file_handle_size (struct GNUNET_DISK_FileHandle *fh,
221 b = GetFileSizeEx (fh->h, &li);
224 SetErrnoFromWinError (GetLastError ());
225 return GNUNET_SYSERR;
227 *size = (OFF_T) li.QuadPart;
231 if (0 != FSTAT (fh->fd, &sbuf))
232 return GNUNET_SYSERR;
233 *size = sbuf.st_size;
240 * Move the read/write pointer in a file
242 * @param h handle of an open file
243 * @param offset position to move to
244 * @param whence specification to which position the offset parameter relates to
245 * @return the new position on success, GNUNET_SYSERR otherwise
248 GNUNET_DISK_file_seek (const struct GNUNET_DISK_FileHandle * h, OFF_T offset,
249 enum GNUNET_DISK_Seek whence)
254 return GNUNET_SYSERR;
258 LARGE_INTEGER li, new_pos;
261 static DWORD t[] = {[GNUNET_DISK_SEEK_SET] = FILE_BEGIN,
262 [GNUNET_DISK_SEEK_CUR] = FILE_CURRENT,[GNUNET_DISK_SEEK_END] = FILE_END
264 li.QuadPart = offset;
266 b = SetFilePointerEx (h->h, li, &new_pos, t[whence]);
269 SetErrnoFromWinError (GetLastError ());
270 return GNUNET_SYSERR;
272 return (OFF_T) new_pos.QuadPart;
274 static int t[] = {[GNUNET_DISK_SEEK_SET] = SEEK_SET,
275 [GNUNET_DISK_SEEK_CUR] = SEEK_CUR,[GNUNET_DISK_SEEK_END] = SEEK_END
278 return lseek (h->fd, offset, t[whence]);
284 * Get the size of the file (or directory) of the given file (in
287 * @param filename name of the file or directory
288 * @param size set to the size of the file (or,
289 * in the case of directories, the sum
290 * of all sizes of files in the directory)
291 * @param includeSymLinks should symbolic links be
293 * @return GNUNET_SYSERR on error, GNUNET_OK on success
296 GNUNET_DISK_file_size (const char *filename, uint64_t * size,
299 struct GetFileSizeData gfsd;
302 GNUNET_assert (size != NULL);
304 gfsd.include_sym_links = includeSymLinks;
305 ret = getSizeRec (&gfsd, filename);
312 * Obtain some unique identifiers for the given file
313 * that can be used to identify it in the local system.
314 * This function is used between GNUnet processes to
315 * quickly check if two files with the same absolute path
316 * are actually identical. The two processes represent
317 * the same peer but may communicate over the network
318 * (and the file may be on an NFS volume). This function
319 * may not be supported on all operating systems.
321 * @param filename name of the file
322 * @param dev set to the device ID
323 * @param ino set to the inode ID
324 * @return GNUNET_OK on success
327 GNUNET_DISK_file_get_identifiers (const char *filename, uint64_t * dev,
334 if ((0 == stat (filename, &sbuf)) && (0 == statvfs (filename, &fbuf)))
336 *dev = (uint64_t) fbuf.f_fsid;
337 *ino = (uint64_t) sbuf.st_ino;
344 if ((0 == stat (filename, &sbuf)) && (0 == statfs (filename, &fbuf)))
346 *dev = ((uint64_t) fbuf.f_fsid.val[0]) << 32 ||
347 ((uint64_t) fbuf.f_fsid.val[1]);
348 *ino = (uint64_t) sbuf.st_ino;
352 // FIXME NILS: test this
353 struct GNUNET_DISK_FileHandle *fh;
354 BY_HANDLE_FILE_INFORMATION info;
357 fh = GNUNET_DISK_file_open (filename, GNUNET_DISK_OPEN_READ, 0);
359 return GNUNET_SYSERR;
360 succ = GetFileInformationByHandle (fh->h, &info);
361 GNUNET_DISK_file_close (fh);
364 *dev = info.dwVolumeSerialNumber;
365 *ino = ((((uint64_t) info.nFileIndexHigh) << (sizeof (DWORD) * 8)) | info.nFileIndexLow);
369 return GNUNET_SYSERR;
372 return GNUNET_SYSERR;
377 * Create an (empty) temporary file on disk. If the given name is not
378 * an absolute path, the current 'TMPDIR' will be prepended. In any case,
379 * 6 random characters will be appended to the name to create a unique
382 * @param t component to use for the name;
383 * does NOT contain "XXXXXX" or "/tmp/".
384 * @return NULL on error, otherwise name of fresh
385 * file on disk in directory for temporary files
388 GNUNET_DISK_mktemp (const char *t)
395 if ((t[0] != '/') && (t[0] != '\\')
397 && !(isalpha ((int) t[0]) && (t[0] != '\0') && (t[1] == ':'))
401 /* FIXME: This uses system codepage on W32, not UTF-8 */
402 tmpdir = getenv ("TMPDIR");
403 tmpdir = tmpdir ? tmpdir : "/tmp";
404 GNUNET_asprintf (&tmpl, "%s/%s%s", tmpdir, t, "XXXXXX");
408 GNUNET_asprintf (&tmpl, "%s%s", t, "XXXXXX");
411 fn = (char *) GNUNET_malloc (MAX_PATH + 1);
412 if (ERROR_SUCCESS != plibc_conv_to_win_path (tmpl, fn))
422 /* FIXME: why is this not MKSTEMP()? This function is implemented in plibc.
423 * It will assume that fn is UTF-8-encoded, if compiled with UTF-8 support.
428 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_ERROR, "mkstemp", fn);
433 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "close", fn);
439 * Get the number of blocks that are left on the partition that
440 * contains the given file (for normal users).
442 * @param part a file on the partition to check
443 * @return -1 on errors, otherwise the number of free blocks
446 GNUNET_DISK_get_blocks_available (const char *part)
451 if (0 != statvfs (part, &buf))
453 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "statfs", part);
461 wchar_t wpath[MAX_PATH + 1];
464 path = GNUNET_STRINGS_filename_expand (part);
467 /* "part" was in UTF-8, and so is "path" */
468 if (ERROR_SUCCESS != plibc_conv_to_win_pathwconv(path, wpath))
474 wcsncpy (szDrive, wpath, 3);
477 if (!GetDiskFreeSpaceW (szDrive, &dwDummy, &dwDummy, &dwBlocks, &dwDummy))
479 LOG (GNUNET_ERROR_TYPE_WARNING, _("`%s' failed for drive `%S': %u\n"),
480 "GetDiskFreeSpace", szDrive, GetLastError ());
488 if (0 != statfs (part, &s))
490 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "statfs", part);
499 * Test if "fil" is a directory.
500 * Will not print an error message if the directory
501 * does not exist. Will log errors if GNUNET_SYSERR is
502 * returned (i.e., a file exists with the same name).
504 * @param fil filename to test
505 * @return GNUNET_YES if yes, GNUNET_NO if not, GNUNET_SYSERR if it
509 GNUNET_DISK_directory_test (const char *fil)
511 struct stat filestat;
514 ret = STAT (fil, &filestat);
519 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "stat", fil);
520 return GNUNET_SYSERR;
524 if (!S_ISDIR (filestat.st_mode))
526 if (ACCESS (fil, R_OK | X_OK) < 0)
528 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "access", fil);
529 return GNUNET_SYSERR;
535 * Check that fil corresponds to a filename
536 * (of a file that exists and that is not a directory).
538 * @param fil filename to check
539 * @return GNUNET_YES if yes, GNUNET_NO if not a file, GNUNET_SYSERR if something
540 * else (will print an error message in that case, too).
543 GNUNET_DISK_file_test (const char *fil)
545 struct stat filestat;
549 rdir = GNUNET_STRINGS_filename_expand (fil);
551 return GNUNET_SYSERR;
553 ret = STAT (rdir, &filestat);
558 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "stat", rdir);
560 return GNUNET_SYSERR;
565 if (!S_ISREG (filestat.st_mode))
570 if (ACCESS (rdir, R_OK) < 0)
572 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "access", rdir);
574 return GNUNET_SYSERR;
582 * Implementation of "mkdir -p"
583 * @param dir the directory to create
584 * @returns GNUNET_OK on success, GNUNET_SYSERR on failure
587 GNUNET_DISK_directory_create (const char *dir)
594 rdir = GNUNET_STRINGS_filename_expand (dir);
596 return GNUNET_SYSERR;
600 pos = 1; /* skip heading '/' */
602 /* Local or Network path? */
603 if (strncmp (rdir, "\\\\", 2) == 0)
608 if (rdir[pos] == '\\')
618 pos = 3; /* strlen("C:\\") */
623 if ((rdir[pos] == DIR_SEPARATOR) || (pos == len))
626 ret = GNUNET_DISK_directory_test (rdir);
627 if (ret == GNUNET_SYSERR)
630 return GNUNET_SYSERR;
632 if (ret == GNUNET_NO)
635 ret = mkdir (rdir, S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH); /* 755 */
637 wchar_t wrdir[MAX_PATH + 1];
638 if (ERROR_SUCCESS == plibc_conv_to_win_pathwconv(rdir, wrdir))
639 ret = !CreateDirectoryW (wrdir, NULL);
643 if ((ret != 0) && (errno != EEXIST))
645 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_ERROR, "mkdir", rdir);
647 return GNUNET_SYSERR;
650 rdir[pos] = DIR_SEPARATOR;
660 * Create the directory structure for storing
663 * @param filename name of a file in the directory
664 * @returns GNUNET_OK on success,
665 * GNUNET_SYSERR on failure,
666 * GNUNET_NO if the directory
667 * exists but is not writeable for us
670 GNUNET_DISK_directory_create_for_file (const char *filename)
676 rdir = GNUNET_STRINGS_filename_expand (filename);
678 return GNUNET_SYSERR;
680 while ((len > 0) && (rdir[len] != DIR_SEPARATOR))
683 ret = GNUNET_DISK_directory_create (rdir);
684 if ((ret == GNUNET_OK) && (0 != ACCESS (rdir, W_OK)))
692 * Read the contents of a binary file into a buffer.
693 * @param h handle to an open file
694 * @param result the buffer to write the result to
695 * @param len the maximum number of bytes to read
696 * @return the number of bytes read on success, GNUNET_SYSERR on failure
699 GNUNET_DISK_file_read (const struct GNUNET_DISK_FileHandle * h, void *result,
705 return GNUNET_SYSERR;
711 if (h->type != GNUNET_PIPE)
713 if (!ReadFile (h->h, result, len, &bytesRead, NULL))
715 SetErrnoFromWinError (GetLastError ());
716 return GNUNET_SYSERR;
721 if (!ReadFile (h->h, result, len, NULL, h->oOverlapRead))
723 if (GetLastError () != ERROR_IO_PENDING)
725 SetErrnoFromWinError (GetLastError ());
726 return GNUNET_SYSERR;
729 GetOverlappedResult (h->h, h->oOverlapRead, &bytesRead, TRUE);
733 return read (h->fd, result, len);
739 * Read the contents of a binary file into a buffer.
741 * @param fn file name
742 * @param result the buffer to write the result to
743 * @param len the maximum number of bytes to read
744 * @return number of bytes read, GNUNET_SYSERR on failure
747 GNUNET_DISK_fn_read (const char *fn, void *result, size_t len)
749 struct GNUNET_DISK_FileHandle *fh;
752 fh = GNUNET_DISK_file_open (fn, GNUNET_DISK_OPEN_READ, GNUNET_DISK_PERM_NONE);
754 return GNUNET_SYSERR;
755 ret = GNUNET_DISK_file_read (fh, result, len);
756 GNUNET_assert (GNUNET_OK == GNUNET_DISK_file_close (fh));
763 * Write a buffer to a file.
764 * @param h handle to open file
765 * @param buffer the data to write
766 * @param n number of bytes to write
767 * @return number of bytes written on success, GNUNET_SYSERR on error
770 GNUNET_DISK_file_write (const struct GNUNET_DISK_FileHandle * h,
771 const void *buffer, size_t n)
776 return GNUNET_SYSERR;
782 if (h->type != GNUNET_PIPE)
784 if (!WriteFile (h->h, buffer, n, &bytesWritten, NULL))
786 SetErrnoFromWinError (GetLastError ());
787 return GNUNET_SYSERR;
793 LOG (GNUNET_ERROR_TYPE_DEBUG, "It is a pipe trying to write\n");
795 if (!WriteFile (h->h, buffer, n, NULL, h->oOverlapWrite))
797 if (GetLastError () != ERROR_IO_PENDING)
799 SetErrnoFromWinError (GetLastError ());
801 LOG (GNUNET_ERROR_TYPE_DEBUG, "Error writing to pipe\n");
803 return GNUNET_SYSERR;
807 LOG (GNUNET_ERROR_TYPE_DEBUG, "Will get overlapped result\n");
809 GetOverlappedResult (h->h, h->oOverlapWrite, &bytesWritten, TRUE);
813 return write (h->fd, buffer, n);
818 * Write a buffer to a file. If the file is longer than the
819 * number of bytes that will be written, it will be truncated.
821 * @param fn file name
822 * @param buffer the data to write
823 * @param n number of bytes to write
824 * @param mode file permissions
825 * @return number of bytes written on success, GNUNET_SYSERR on error
828 GNUNET_DISK_fn_write (const char *fn, const void *buffer, size_t n,
829 enum GNUNET_DISK_AccessPermissions mode)
831 struct GNUNET_DISK_FileHandle *fh;
834 fh = GNUNET_DISK_file_open (fn,
835 GNUNET_DISK_OPEN_WRITE | GNUNET_DISK_OPEN_TRUNCATE
836 | GNUNET_DISK_OPEN_CREATE, mode);
838 return GNUNET_SYSERR;
839 ret = GNUNET_DISK_file_write (fh, buffer, n);
840 GNUNET_assert (GNUNET_OK == GNUNET_DISK_file_close (fh));
846 * Scan a directory for files.
848 * @param dirName the name of the directory
849 * @param callback the method to call for each file,
850 * can be NULL, in that case, we only count
851 * @param callback_cls closure for callback
852 * @return the number of files found, GNUNET_SYSERR on error or
853 * ieration aborted by callback returning GNUNET_SYSERR
856 GNUNET_DISK_directory_scan (const char *dirName,
857 GNUNET_FileNameCallback callback,
861 struct dirent *finfo;
866 unsigned int name_len;
869 GNUNET_assert (dirName != NULL);
870 dname = GNUNET_STRINGS_filename_expand (dirName);
872 return GNUNET_SYSERR;
873 while ((strlen (dname) > 0) && (dname[strlen (dname) - 1] == DIR_SEPARATOR))
874 dname[strlen (dname) - 1] = '\0';
875 if (0 != STAT (dname, &istat))
877 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "stat", dname);
879 return GNUNET_SYSERR;
881 if (!S_ISDIR (istat.st_mode))
883 LOG (GNUNET_ERROR_TYPE_WARNING, _("Expected `%s' to be a directory!\n"),
886 return GNUNET_SYSERR;
889 dinfo = OPENDIR (dname);
890 if ((errno == EACCES) || (dinfo == NULL))
892 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "opendir", dname);
896 return GNUNET_SYSERR;
899 n_size = strlen (dname) + name_len + 2;
900 name = GNUNET_malloc (n_size);
901 while ((finfo = READDIR (dinfo)) != NULL)
903 if ((0 == strcmp (finfo->d_name, ".")) ||
904 (0 == strcmp (finfo->d_name, "..")))
906 if (callback != NULL)
908 if (name_len < strlen (finfo->d_name))
911 name_len = strlen (finfo->d_name);
912 n_size = strlen (dname) + name_len + 2;
913 name = GNUNET_malloc (n_size);
915 /* dname can end in "/" only if dname == "/";
916 * if dname does not end in "/", we need to add
917 * a "/" (otherwise, we must not!) */
918 GNUNET_snprintf (name, n_size, "%s%s%s", dname,
919 (strcmp (dname, DIR_SEPARATOR_STR) ==
920 0) ? "" : DIR_SEPARATOR_STR, finfo->d_name);
921 if (GNUNET_OK != callback (callback_cls, name))
926 return GNUNET_SYSERR;
939 * Opaque handle used for iterating over a directory.
941 struct GNUNET_DISK_DirectoryIterator
945 * Function to call on directory entries.
947 GNUNET_DISK_DirectoryIteratorCallback callback;
950 * Closure for callback.
955 * Reference to directory.
965 * Next filename to process.
972 enum GNUNET_SCHEDULER_Priority priority;
978 * Task used by the directory iterator.
981 directory_iterator_task (void *cls,
982 const struct GNUNET_SCHEDULER_TaskContext *tc)
984 struct GNUNET_DISK_DirectoryIterator *iter = cls;
987 name = iter->next_name;
988 GNUNET_assert (name != NULL);
989 iter->next_name = NULL;
990 iter->callback (iter->callback_cls, iter, name, iter->dirname);
996 * This function must be called during the DiskIteratorCallback
997 * (exactly once) to schedule the task to process the next
998 * filename in the directory (if there is one).
1000 * @param iter opaque handle for the iterator
1001 * @param can set to GNUNET_YES to terminate the iteration early
1002 * @return GNUNET_YES if iteration will continue,
1003 * GNUNET_NO if this was the last entry (and iteration is complete),
1004 * GNUNET_SYSERR if abort was YES
1007 GNUNET_DISK_directory_iterator_next (struct GNUNET_DISK_DirectoryIterator *iter,
1010 struct dirent *finfo;
1012 GNUNET_assert (iter->next_name == NULL);
1013 if (can == GNUNET_YES)
1015 CLOSEDIR (iter->directory);
1016 GNUNET_free (iter->dirname);
1018 return GNUNET_SYSERR;
1020 while (NULL != (finfo = READDIR (iter->directory)))
1022 if ((0 == strcmp (finfo->d_name, ".")) ||
1023 (0 == strcmp (finfo->d_name, "..")))
1025 GNUNET_asprintf (&iter->next_name, "%s%s%s", iter->dirname,
1026 DIR_SEPARATOR_STR, finfo->d_name);
1031 GNUNET_DISK_directory_iterator_next (iter, GNUNET_YES);
1034 GNUNET_SCHEDULER_add_with_priority (iter->priority, &directory_iterator_task,
1041 * Scan a directory for files using the scheduler to run a task for
1042 * each entry. The name of the directory must be expanded first (!).
1043 * If a scheduler does not need to be used, GNUNET_DISK_directory_scan
1044 * may provide a simpler API.
1046 * @param prio priority to use
1047 * @param dirName the name of the directory
1048 * @param callback the method to call for each file
1049 * @param callback_cls closure for callback
1052 GNUNET_DISK_directory_iterator_start (enum GNUNET_SCHEDULER_Priority prio,
1053 const char *dirName,
1054 GNUNET_DISK_DirectoryIteratorCallback
1055 callback, void *callback_cls)
1057 struct GNUNET_DISK_DirectoryIterator *di;
1059 di = GNUNET_malloc (sizeof (struct GNUNET_DISK_DirectoryIterator));
1060 di->callback = callback;
1061 di->callback_cls = callback_cls;
1062 di->directory = OPENDIR (dirName);
1063 if (di->directory == NULL)
1066 callback (callback_cls, NULL, NULL, NULL);
1069 di->dirname = GNUNET_strdup (dirName);
1070 di->priority = prio;
1071 GNUNET_DISK_directory_iterator_next (di, GNUNET_NO);
1076 * Function that removes the given directory by calling
1077 * "GNUNET_DISK_directory_remove".
1079 * @param unused not used
1080 * @param fn directory to remove
1084 remove_helper (void *unused, const char *fn)
1086 (void) GNUNET_DISK_directory_remove (fn);
1092 * Remove all files in a directory (rm -rf). Call with
1096 * @param fileName the file to remove
1097 * @return GNUNET_OK on success, GNUNET_SYSERR on error
1100 GNUNET_DISK_directory_remove (const char *fileName)
1104 if (0 != LSTAT (fileName, &istat))
1105 return GNUNET_NO; /* file may not exist... */
1106 CHMOD (fileName, S_IWUSR | S_IRUSR | S_IXUSR);
1107 if (UNLINK (fileName) == 0)
1109 if ((errno != EISDIR) &&
1110 /* EISDIR is not sufficient in all cases, e.g.
1111 * sticky /tmp directory may result in EPERM on BSD.
1112 * So we also explicitly check "isDirectory" */
1113 (GNUNET_YES != GNUNET_DISK_directory_test (fileName)))
1115 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "rmdir", fileName);
1116 return GNUNET_SYSERR;
1118 if (GNUNET_SYSERR ==
1119 GNUNET_DISK_directory_scan (fileName, &remove_helper, NULL))
1120 return GNUNET_SYSERR;
1121 if (0 != RMDIR (fileName))
1123 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "rmdir", fileName);
1124 return GNUNET_SYSERR;
1133 * @param src file to copy
1134 * @param dst destination file name
1135 * @return GNUNET_OK on success, GNUNET_SYSERR on error
1138 GNUNET_DISK_file_copy (const char *src, const char *dst)
1144 struct GNUNET_DISK_FileHandle *in;
1145 struct GNUNET_DISK_FileHandle *out;
1147 if (GNUNET_OK != GNUNET_DISK_file_size (src, &size, GNUNET_YES))
1148 return GNUNET_SYSERR;
1150 in = GNUNET_DISK_file_open (src, GNUNET_DISK_OPEN_READ,
1151 GNUNET_DISK_PERM_NONE);
1153 return GNUNET_SYSERR;
1155 GNUNET_DISK_file_open (dst,
1156 GNUNET_DISK_OPEN_WRITE | GNUNET_DISK_OPEN_CREATE |
1157 GNUNET_DISK_OPEN_FAILIFEXISTS,
1158 GNUNET_DISK_PERM_USER_READ |
1159 GNUNET_DISK_PERM_USER_WRITE |
1160 GNUNET_DISK_PERM_GROUP_READ |
1161 GNUNET_DISK_PERM_GROUP_WRITE);
1164 GNUNET_DISK_file_close (in);
1165 return GNUNET_SYSERR;
1167 buf = GNUNET_malloc (COPY_BLK_SIZE);
1170 len = COPY_BLK_SIZE;
1171 if (len > size - pos)
1173 if (len != GNUNET_DISK_file_read (in, buf, len))
1175 if (len != GNUNET_DISK_file_write (out, buf, len))
1180 GNUNET_DISK_file_close (in);
1181 GNUNET_DISK_file_close (out);
1185 GNUNET_DISK_file_close (in);
1186 GNUNET_DISK_file_close (out);
1187 return GNUNET_SYSERR;
1192 * @brief Removes special characters as ':' from a filename.
1193 * @param fn the filename to canonicalize
1196 GNUNET_DISK_filename_canonicalize (char *fn)
1206 if (c == '/' || c == '\\' || c == ':' || c == '*' || c == '?' || c == '"' ||
1207 c == '<' || c == '>' || c == '|')
1219 * @brief Change owner of a file
1221 * @param filename name of file to change the owner of
1222 * @param user name of the new owner
1223 * @return GNUNET_OK on success, GNUNET_SYSERR on failure
1226 GNUNET_DISK_file_change_owner (const char *filename, const char *user)
1231 pws = getpwnam (user);
1234 LOG (GNUNET_ERROR_TYPE_ERROR,
1235 _("Cannot obtain information about user `%s': %s\n"), user,
1237 return GNUNET_SYSERR;
1239 if (0 != chown (filename, pws->pw_uid, pws->pw_gid))
1240 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "chown", filename);
1247 * Lock a part of a file
1248 * @param fh file handle
1249 * @param lockStart absolute position from where to lock
1250 * @param lockEnd absolute position until where to lock
1251 * @param excl GNUNET_YES for an exclusive lock
1252 * @return GNUNET_OK on success, GNUNET_SYSERR on error
1255 GNUNET_DISK_file_lock (struct GNUNET_DISK_FileHandle *fh, OFF_T lockStart,
1256 OFF_T lockEnd, int excl)
1261 return GNUNET_SYSERR;
1267 memset (&fl, 0, sizeof (struct flock));
1268 fl.l_type = excl ? F_WRLCK : F_RDLCK;
1269 fl.l_whence = SEEK_SET;
1270 fl.l_start = lockStart;
1273 return fcntl (fh->fd, F_SETLK, &fl) != 0 ? GNUNET_SYSERR : GNUNET_OK;
1276 OFF_T diff = lockEnd - lockStart;
1277 DWORD diff_low, diff_high;
1278 diff_low = (DWORD) (diff & 0xFFFFFFFF);
1279 diff_high = (DWORD) ((diff >> (sizeof (DWORD) * 8)) & 0xFFFFFFFF);
1281 memset (&o, 0, sizeof (OVERLAPPED));
1282 o.Offset = (DWORD) (lockStart & 0xFFFFFFFF);;
1283 o.OffsetHigh = (DWORD) (((lockStart & ~0xFFFFFFFF) >> (sizeof (DWORD) * 8)) & 0xFFFFFFFF);
1286 (fh->h, (excl ? LOCKFILE_EXCLUSIVE_LOCK : 0) | LOCKFILE_FAIL_IMMEDIATELY,
1287 0, diff_low, diff_high, &o))
1289 SetErrnoFromWinError (GetLastError ());
1290 return GNUNET_SYSERR;
1299 * Unlock a part of a file
1300 * @param fh file handle
1301 * @param unlockStart absolute position from where to unlock
1302 * @param unlockEnd absolute position until where to unlock
1303 * @return GNUNET_OK on success, GNUNET_SYSERR on error
1306 GNUNET_DISK_file_unlock (struct GNUNET_DISK_FileHandle *fh, OFF_T unlockStart,
1312 return GNUNET_SYSERR;
1318 memset (&fl, 0, sizeof (struct flock));
1319 fl.l_type = F_UNLCK;
1320 fl.l_whence = SEEK_SET;
1321 fl.l_start = unlockStart;
1322 fl.l_len = unlockEnd;
1324 return fcntl (fh->fd, F_SETLK, &fl) != 0 ? GNUNET_SYSERR : GNUNET_OK;
1327 OFF_T diff = unlockEnd - unlockStart;
1328 DWORD diff_low, diff_high;
1329 diff_low = (DWORD) (diff & 0xFFFFFFFF);
1330 diff_high = (DWORD) ((diff >> (sizeof (DWORD) * 8)) & 0xFFFFFFFF);
1332 memset (&o, 0, sizeof (OVERLAPPED));
1333 o.Offset = (DWORD) (unlockStart & 0xFFFFFFFF);;
1334 o.OffsetHigh = (DWORD) (((unlockStart & ~0xFFFFFFFF) >> (sizeof (DWORD) * 8)) & 0xFFFFFFFF);
1336 if (!UnlockFileEx (fh->h, 0, diff_low, diff_high, &o))
1338 SetErrnoFromWinError (GetLastError ());
1339 return GNUNET_SYSERR;
1348 * Open a file. Note that the access permissions will only be
1349 * used if a new file is created and if the underlying operating
1350 * system supports the given permissions.
1352 * @param fn file name to be opened
1353 * @param flags opening flags, a combination of GNUNET_DISK_OPEN_xxx bit flags
1354 * @param perm permissions for the newly created file, use
1355 * GNUNET_DISK_PERM_USER_NONE if a file could not be created by this
1356 * call (because of flags)
1357 * @return IO handle on success, NULL on error
1359 struct GNUNET_DISK_FileHandle *
1360 GNUNET_DISK_file_open (const char *fn, enum GNUNET_DISK_OpenFlags flags,
1361 enum GNUNET_DISK_AccessPermissions perm)
1364 struct GNUNET_DISK_FileHandle *ret;
1370 wchar_t wexpfn[MAX_PATH + 1];
1377 expfn = GNUNET_STRINGS_filename_expand (fn);
1382 if (GNUNET_DISK_OPEN_READWRITE == (flags & GNUNET_DISK_OPEN_READWRITE))
1383 oflags = O_RDWR; /* note: O_RDWR is NOT always O_RDONLY | O_WRONLY */
1384 else if (flags & GNUNET_DISK_OPEN_READ)
1386 else if (flags & GNUNET_DISK_OPEN_WRITE)
1391 GNUNET_free (expfn);
1394 if (flags & GNUNET_DISK_OPEN_FAILIFEXISTS)
1395 oflags |= (O_CREAT | O_EXCL);
1396 if (flags & GNUNET_DISK_OPEN_TRUNCATE)
1398 if (flags & GNUNET_DISK_OPEN_APPEND)
1400 if (flags & GNUNET_DISK_OPEN_CREATE)
1402 (void) GNUNET_DISK_directory_create_for_file (expfn);
1404 mode = translate_unix_perms (perm);
1407 fd = open (expfn, oflags | O_LARGEFILE, mode);
1410 if (0 == (flags & GNUNET_DISK_OPEN_FAILIFEXISTS))
1411 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "open", expfn);
1413 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_DEBUG, "open", expfn);
1414 GNUNET_free (expfn);
1421 if (GNUNET_DISK_OPEN_READWRITE == (flags & GNUNET_DISK_OPEN_READWRITE))
1422 access = FILE_READ_DATA | FILE_WRITE_DATA;
1423 else if (flags & GNUNET_DISK_OPEN_READ)
1424 access = FILE_READ_DATA;
1425 else if (flags & GNUNET_DISK_OPEN_WRITE)
1426 access = FILE_WRITE_DATA;
1428 if (flags & GNUNET_DISK_OPEN_FAILIFEXISTS)
1432 else if (flags & GNUNET_DISK_OPEN_CREATE)
1434 (void) GNUNET_DISK_directory_create_for_file (expfn);
1435 if (flags & GNUNET_DISK_OPEN_TRUNCATE)
1436 disp = CREATE_ALWAYS;
1440 else if (flags & GNUNET_DISK_OPEN_TRUNCATE)
1442 disp = TRUNCATE_EXISTING;
1446 disp = OPEN_EXISTING;
1449 if (ERROR_SUCCESS == plibc_conv_to_win_pathwconv(expfn, wexpfn))
1450 h = CreateFileW (wexpfn, access,
1451 FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE, NULL,
1452 disp, FILE_ATTRIBUTE_NORMAL, NULL);
1454 h = INVALID_HANDLE_VALUE;
1455 if (h == INVALID_HANDLE_VALUE)
1457 SetErrnoFromWinError (GetLastError ());
1458 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "open", expfn);
1459 GNUNET_free (expfn);
1463 if (flags & GNUNET_DISK_OPEN_APPEND)
1464 if (SetFilePointer (h, 0, 0, FILE_END) == INVALID_SET_FILE_POINTER)
1466 SetErrnoFromWinError (GetLastError ());
1467 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "SetFilePointer", expfn);
1469 GNUNET_free (expfn);
1474 ret = GNUNET_malloc (sizeof (struct GNUNET_DISK_FileHandle));
1477 ret->type = GNUNET_DISK_FILE;
1481 GNUNET_free (expfn);
1487 * Close an open file
1488 * @param h file handle
1489 * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
1492 GNUNET_DISK_file_close (struct GNUNET_DISK_FileHandle *h)
1497 return GNUNET_SYSERR;
1501 if (!CloseHandle (h->h))
1503 SetErrnoFromWinError (GetLastError ());
1504 LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING, "close");
1505 GNUNET_free (h->oOverlapRead);
1506 GNUNET_free (h->oOverlapWrite);
1508 return GNUNET_SYSERR;
1511 if (close (h->fd) != 0)
1513 LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING, "close");
1515 return GNUNET_SYSERR;
1524 * Construct full path to a file inside of the private
1525 * directory used by GNUnet. Also creates the corresponding
1526 * directory. If the resulting name is supposed to be
1527 * a directory, end the last argument in '/' (or pass
1528 * DIR_SEPARATOR_STR as the last argument before NULL).
1530 * @param cfg configuration to use (determines HOME)
1531 * @param serviceName name of the service
1532 * @param ... is NULL-terminated list of
1533 * path components to append to the
1534 * private directory name.
1535 * @return the constructed filename
1538 GNUNET_DISK_get_home_filename (const struct GNUNET_CONFIGURATION_Handle *cfg,
1539 const char *serviceName, ...)
1545 unsigned int needed;
1548 GNUNET_CONFIGURATION_get_value_filename (cfg, serviceName, "HOME", &pfx))
1552 LOG (GNUNET_ERROR_TYPE_WARNING,
1553 _("No `%s' specified for service `%s' in configuration.\n"), "HOME",
1557 needed = strlen (pfx) + 2;
1558 if ((pfx[strlen (pfx) - 1] != '/') && (pfx[strlen (pfx) - 1] != '\\'))
1560 va_start (ap, serviceName);
1563 c = va_arg (ap, const char *);
1567 needed += strlen (c);
1568 if ((c[strlen (c) - 1] != '/') && (c[strlen (c) - 1] != '\\'))
1572 ret = GNUNET_malloc (needed);
1575 va_start (ap, serviceName);
1578 c = va_arg (ap, const char *);
1582 if ((c[strlen (c) - 1] != '/') && (c[strlen (c) - 1] != '\\'))
1583 strcat (ret, DIR_SEPARATOR_STR);
1587 if ((ret[strlen (ret) - 1] != '/') && (ret[strlen (ret) - 1] != '\\'))
1588 (void) GNUNET_DISK_directory_create_for_file (ret);
1590 (void) GNUNET_DISK_directory_create (ret);
1596 * Handle for a memory-mapping operation.
1598 struct GNUNET_DISK_MapHandle
1601 * Address where the map is in memory.
1607 * Underlying OS handle.
1612 * Number of bytes mapped.
1620 #define MAP_FAILED ((void *) -1)
1624 * Map a file into memory
1626 * @param h open file handle
1627 * @param m handle to the new mapping
1628 * @param access access specification, GNUNET_DISK_MAP_TYPE_xxx
1629 * @param len size of the mapping
1630 * @return pointer to the mapped memory region, NULL on failure
1633 GNUNET_DISK_file_map (const struct GNUNET_DISK_FileHandle *h,
1634 struct GNUNET_DISK_MapHandle **m,
1635 enum GNUNET_DISK_MapType access, size_t len)
1644 DWORD mapAccess, protect;
1646 if ((access & GNUNET_DISK_MAP_TYPE_READ) &&
1647 (access & GNUNET_DISK_MAP_TYPE_WRITE))
1649 protect = PAGE_READWRITE;
1650 mapAccess = FILE_MAP_ALL_ACCESS;
1652 else if (access & GNUNET_DISK_MAP_TYPE_READ)
1654 protect = PAGE_READONLY;
1655 mapAccess = FILE_MAP_READ;
1657 else if (access & GNUNET_DISK_MAP_TYPE_WRITE)
1659 protect = PAGE_READWRITE;
1660 mapAccess = FILE_MAP_WRITE;
1668 *m = GNUNET_malloc (sizeof (struct GNUNET_DISK_MapHandle));
1669 (*m)->h = CreateFileMapping (h->h, NULL, protect, 0, 0, NULL);
1670 if ((*m)->h == INVALID_HANDLE_VALUE)
1672 SetErrnoFromWinError (GetLastError ());
1677 (*m)->addr = MapViewOfFile ((*m)->h, mapAccess, 0, 0, len);
1680 SetErrnoFromWinError (GetLastError ());
1681 CloseHandle ((*m)->h);
1690 if (access & GNUNET_DISK_MAP_TYPE_READ)
1692 if (access & GNUNET_DISK_MAP_TYPE_WRITE)
1694 *m = GNUNET_malloc (sizeof (struct GNUNET_DISK_MapHandle));
1695 (*m)->addr = mmap (NULL, len, prot, MAP_SHARED, h->fd, 0);
1696 GNUNET_assert (NULL != (*m)->addr);
1697 if (MAP_FAILED == (*m)->addr)
1709 * @param h mapping handle
1710 * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
1713 GNUNET_DISK_file_unmap (struct GNUNET_DISK_MapHandle *h)
1720 return GNUNET_SYSERR;
1724 ret = UnmapViewOfFile (h->addr) ? GNUNET_OK : GNUNET_SYSERR;
1725 if (ret != GNUNET_OK)
1726 SetErrnoFromWinError (GetLastError ());
1727 if (!CloseHandle (h->h) && (ret == GNUNET_OK))
1729 ret = GNUNET_SYSERR;
1730 SetErrnoFromWinError (GetLastError ());
1733 ret = munmap (h->addr, h->len) != -1 ? GNUNET_OK : GNUNET_SYSERR;
1741 * Write file changes to disk
1742 * @param h handle to an open file
1743 * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
1746 GNUNET_DISK_file_sync (const struct GNUNET_DISK_FileHandle *h)
1751 return GNUNET_SYSERR;
1757 ret = FlushFileBuffers (h->h) ? GNUNET_OK : GNUNET_SYSERR;
1758 if (ret != GNUNET_OK)
1759 SetErrnoFromWinError (GetLastError ());
1761 #elif defined(FREEBSD) || defined(OPENBSD) || defined(DARWIN)
1762 return fsync (h->fd) == -1 ? GNUNET_SYSERR : GNUNET_OK;
1764 return fdatasync (h->fd) == -1 ? GNUNET_SYSERR : GNUNET_OK;
1769 /* Copyright Bob Byrnes <byrnes <at> curl.com>
1770 http://permalink.gmane.org/gmane.os.cygwin.patches/2121
1772 /* Create a pipe, and return handles to the read and write ends,
1773 just like CreatePipe, but ensure that the write end permits
1774 FILE_READ_ATTRIBUTES access, on later versions of win32 where
1775 this is supported. This access is needed by NtQueryInformationFile,
1776 which is used to implement select and nonblocking writes.
1777 Note that the return value is either NO_ERROR or GetLastError,
1778 unlike CreatePipe, which returns a bool for success or failure. */
1780 create_selectable_pipe (PHANDLE read_pipe_ptr, PHANDLE write_pipe_ptr,
1781 LPSECURITY_ATTRIBUTES sa_ptr, DWORD psize,
1782 DWORD dwReadMode, DWORD dwWriteMode)
1784 /* Default to error. */
1785 *read_pipe_ptr = *write_pipe_ptr = INVALID_HANDLE_VALUE;
1787 HANDLE read_pipe = INVALID_HANDLE_VALUE, write_pipe = INVALID_HANDLE_VALUE;
1789 /* Ensure that there is enough pipe buffer space for atomic writes. */
1790 if (psize < PIPE_BUF)
1793 char pipename[MAX_PATH];
1795 /* Retry CreateNamedPipe as long as the pipe name is in use.
1796 * Retrying will probably never be necessary, but we want
1797 * to be as robust as possible. */
1800 static volatile LONG pipe_unique_id;
1802 snprintf (pipename, sizeof pipename, "\\\\.\\pipe\\gnunet-%d-%ld",
1803 getpid (), InterlockedIncrement ((LONG *) & pipe_unique_id));
1805 LOG (GNUNET_ERROR_TYPE_DEBUG, "CreateNamedPipe: name = %s, size = %lu\n",
1808 /* Use CreateNamedPipe instead of CreatePipe, because the latter
1809 * returns a write handle that does not permit FILE_READ_ATTRIBUTES
1810 * access, on versions of win32 earlier than WinXP SP2.
1811 * CreatePipe also stupidly creates a full duplex pipe, which is
1812 * a waste, since only a single direction is actually used.
1813 * It's important to only allow a single instance, to ensure that
1814 * the pipe was not created earlier by some other process, even if
1815 * the pid has been reused. We avoid FILE_FLAG_FIRST_PIPE_INSTANCE
1816 * because that is only available for Win2k SP2 and WinXP. */
1817 read_pipe = CreateNamedPipeA (pipename, PIPE_ACCESS_INBOUND | dwReadMode, PIPE_TYPE_BYTE | PIPE_READMODE_BYTE, 1, /* max instances */
1818 psize, /* output buffer size */
1819 psize, /* input buffer size */
1820 NMPWAIT_USE_DEFAULT_WAIT, sa_ptr);
1822 if (read_pipe != INVALID_HANDLE_VALUE)
1825 LOG (GNUNET_ERROR_TYPE_DEBUG, "pipe read handle = %p\n", read_pipe);
1830 DWORD err = GetLastError ();
1834 case ERROR_PIPE_BUSY:
1835 /* The pipe is already open with compatible parameters.
1836 * Pick a new name and retry. */
1838 LOG (GNUNET_ERROR_TYPE_DEBUG, "pipe busy, retrying\n");
1841 case ERROR_ACCESS_DENIED:
1842 /* The pipe is already open with incompatible parameters.
1843 * Pick a new name and retry. */
1845 LOG (GNUNET_ERROR_TYPE_DEBUG, "pipe access denied, retrying\n");
1848 case ERROR_CALL_NOT_IMPLEMENTED:
1849 /* We are on an older Win9x platform without named pipes.
1850 * Return an anonymous pipe as the best approximation. */
1852 LOG (GNUNET_ERROR_TYPE_DEBUG,
1853 "CreateNamedPipe not implemented, resorting to "
1854 "CreatePipe: size = %lu\n", psize);
1856 if (CreatePipe (read_pipe_ptr, write_pipe_ptr, sa_ptr, psize))
1859 LOG (GNUNET_ERROR_TYPE_DEBUG, "pipe read handle = %p\n",
1861 LOG (GNUNET_ERROR_TYPE_DEBUG, "pipe write handle = %p\n",
1866 err = GetLastError ();
1867 LOG (GNUNET_ERROR_TYPE_ERROR, "CreatePipe failed: %d\n", err);
1870 LOG (GNUNET_ERROR_TYPE_ERROR, "CreateNamedPipe failed: %d\n", err);
1876 LOG (GNUNET_ERROR_TYPE_DEBUG, "CreateFile: name = %s\n", pipename);
1879 /* Open the named pipe for writing.
1880 * Be sure to permit FILE_READ_ATTRIBUTES access. */
1881 write_pipe = CreateFileA (pipename, GENERIC_WRITE | FILE_READ_ATTRIBUTES, 0, /* share mode */
1882 sa_ptr, OPEN_EXISTING, dwWriteMode, /* flags and attributes */
1883 0); /* handle to template file */
1885 if (write_pipe == INVALID_HANDLE_VALUE)
1888 DWORD err = GetLastError ();
1891 LOG (GNUNET_ERROR_TYPE_DEBUG, "CreateFile failed: %d\n", err);
1893 CloseHandle (read_pipe);
1897 LOG (GNUNET_ERROR_TYPE_DEBUG, "pipe write handle = %p\n", write_pipe);
1900 *read_pipe_ptr = read_pipe;
1901 *write_pipe_ptr = write_pipe;
1907 * Creates an interprocess channel
1909 * @param blocking creates an asynchronous pipe if set to GNUNET_NO
1910 * @param inherit_read inherit the parent processes stdin (only for windows)
1911 * @param inherit_write inherit the parent processes stdout (only for windows)
1913 * @return handle to the new pipe, NULL on error
1915 struct GNUNET_DISK_PipeHandle *
1916 GNUNET_DISK_pipe (int blocking, int inherit_read, int inherit_write)
1918 struct GNUNET_DISK_PipeHandle *p;
1919 struct GNUNET_DISK_FileHandle *fds;
1921 p = GNUNET_malloc (sizeof (struct GNUNET_DISK_PipeHandle) +
1922 2 * sizeof (struct GNUNET_DISK_FileHandle));
1923 fds = (struct GNUNET_DISK_FileHandle *) &p[1];
1936 LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR, "pipe");
1941 p->fd[0]->fd = fd[0];
1942 p->fd[1]->fd = fd[1];
1944 flags = fcntl (fd[0], F_GETFL);
1946 flags |= O_NONBLOCK;
1947 if (0 > fcntl (fd[0], F_SETFL, flags))
1949 flags = fcntl (fd[0], F_GETFD);
1950 flags |= FD_CLOEXEC;
1951 if (0 > fcntl (fd[0], F_SETFD, flags))
1954 flags = fcntl (fd[1], F_GETFL);
1956 flags |= O_NONBLOCK;
1957 if (0 > fcntl (fd[1], F_SETFL, flags))
1959 flags = fcntl (fd[1], F_GETFD);
1960 flags |= FD_CLOEXEC;
1961 if (0 > fcntl (fd[1], F_SETFD, flags))
1966 LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR, "fcntl");
1967 GNUNET_break (0 == close (p->fd[0]->fd));
1968 GNUNET_break (0 == close (p->fd[1]->fd));
1978 create_selectable_pipe (&p->fd[0]->h, &p->fd[1]->h, NULL, 0,
1979 FILE_FLAG_OVERLAPPED, FILE_FLAG_OVERLAPPED);
1983 SetErrnoFromWinError (GetLastError ());
1986 if (!DuplicateHandle
1987 (GetCurrentProcess (), p->fd[0]->h, GetCurrentProcess (), &tmp_handle, 0,
1988 inherit_read == GNUNET_YES ? TRUE : FALSE, DUPLICATE_SAME_ACCESS))
1990 SetErrnoFromWinError (GetLastError ());
1991 CloseHandle (p->fd[0]->h);
1992 CloseHandle (p->fd[1]->h);
1996 CloseHandle (p->fd[0]->h);
1997 p->fd[0]->h = tmp_handle;
1999 if (!DuplicateHandle
2000 (GetCurrentProcess (), p->fd[1]->h, GetCurrentProcess (), &tmp_handle, 0,
2001 inherit_write == GNUNET_YES ? TRUE : FALSE, DUPLICATE_SAME_ACCESS))
2003 SetErrnoFromWinError (GetLastError ());
2004 CloseHandle (p->fd[0]->h);
2005 CloseHandle (p->fd[1]->h);
2009 CloseHandle (p->fd[1]->h);
2010 p->fd[1]->h = tmp_handle;
2016 SetNamedPipeHandleState (p->fd[0]->h, &mode, NULL, NULL);
2017 SetNamedPipeHandleState (p->fd[1]->h, &mode, NULL, NULL);
2018 /* this always fails on Windows 95, so we don't care about error handling */
2020 p->fd[0]->type = GNUNET_PIPE;
2021 p->fd[1]->type = GNUNET_PIPE;
2023 p->fd[0]->oOverlapRead = GNUNET_malloc (sizeof (OVERLAPPED));
2024 p->fd[0]->oOverlapWrite = GNUNET_malloc (sizeof (OVERLAPPED));
2025 p->fd[1]->oOverlapRead = GNUNET_malloc (sizeof (OVERLAPPED));
2026 p->fd[1]->oOverlapWrite = GNUNET_malloc (sizeof (OVERLAPPED));
2028 p->fd[0]->oOverlapRead->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2029 p->fd[0]->oOverlapWrite->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2031 p->fd[1]->oOverlapRead->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2032 p->fd[1]->oOverlapWrite->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2040 * Closes an interprocess channel
2042 * @param p pipe to close
2043 * @param end which end of the pipe to close
2044 * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
2047 GNUNET_DISK_pipe_close_end (struct GNUNET_DISK_PipeHandle *p,
2048 enum GNUNET_DISK_PipeEnd end)
2050 int ret = GNUNET_OK;
2054 if (end == GNUNET_DISK_PIPE_END_READ)
2056 if (!CloseHandle (p->fd[0]->h))
2058 SetErrnoFromWinError (GetLastError ());
2059 ret = GNUNET_SYSERR;
2061 p->fd[0]->h = INVALID_HANDLE_VALUE;
2063 else if (end == GNUNET_DISK_PIPE_END_WRITE)
2065 if (!CloseHandle (p->fd[1]->h))
2067 SetErrnoFromWinError (GetLastError ());
2068 ret = GNUNET_SYSERR;
2070 p->fd[1]->h = INVALID_HANDLE_VALUE;
2075 if (end == GNUNET_DISK_PIPE_END_READ)
2077 if (0 != close (p->fd[0]->fd))
2079 ret = GNUNET_SYSERR;
2084 else if (end == GNUNET_DISK_PIPE_END_WRITE)
2086 if (0 != close (p->fd[1]->fd))
2088 ret = GNUNET_SYSERR;
2099 * Closes an interprocess channel
2101 * @param p pipe to close
2102 * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
2105 GNUNET_DISK_pipe_close (struct GNUNET_DISK_PipeHandle *p)
2107 int ret = GNUNET_OK;
2111 if (!CloseHandle (p->fd[0]->h))
2113 SetErrnoFromWinError (GetLastError ());
2114 ret = GNUNET_SYSERR;
2116 if (!CloseHandle (p->fd[1]->h))
2118 SetErrnoFromWinError (GetLastError ());
2119 ret = GNUNET_SYSERR;
2124 if (p->fd[0]->fd != -1)
2126 if (0 != close (p->fd[0]->fd))
2128 ret = GNUNET_SYSERR;
2133 if (p->fd[1]->fd != -1)
2135 if (0 != close (p->fd[1]->fd))
2137 ret = GNUNET_SYSERR;
2149 * Creates a named pipe/FIFO and opens it
2150 * @param fn pointer to the name of the named pipe or to NULL
2151 * @param flags open flags
2152 * @param perm access permissions
2153 * @return pipe handle on success, NULL on error
2155 struct GNUNET_DISK_FileHandle *
2156 GNUNET_DISK_npipe_create (char **fn, enum GNUNET_DISK_OpenFlags flags,
2157 enum GNUNET_DISK_AccessPermissions perm)
2160 struct GNUNET_DISK_FileHandle *ret;
2166 if (flags & GNUNET_DISK_OPEN_READWRITE)
2167 openMode = PIPE_ACCESS_DUPLEX;
2168 else if (flags & GNUNET_DISK_OPEN_READ)
2169 openMode = PIPE_ACCESS_INBOUND;
2170 else if (flags & GNUNET_DISK_OPEN_WRITE)
2171 openMode = PIPE_ACCESS_OUTBOUND;
2173 if (flags & GNUNET_DISK_OPEN_FAILIFEXISTS)
2174 openMode |= FILE_FLAG_FIRST_PIPE_INSTANCE;
2183 GNUNET_asprintf (&name, "\\\\.\\pipe\\%.246s", fn);
2185 LOG (GNUNET_ERROR_TYPE_DEBUG,
2186 "Trying to create an instance of named pipe `%s'\n", name);
2188 /* 1) This might work just fine with UTF-8 strings as it is.
2189 * 2) This is only used by GNUnet itself, and only with latin names.
2191 h = CreateNamedPipe (name, openMode | FILE_FLAG_OVERLAPPED,
2192 PIPE_TYPE_BYTE | PIPE_READMODE_BYTE, 2, 1, 1, 0,
2197 GNUNET_asprintf (fn, "\\\\.\\pipe\\gnunet-%llu",
2198 GNUNET_CRYPTO_random_u64 (GNUNET_CRYPTO_QUALITY_WEAK,
2201 LOG (GNUNET_ERROR_TYPE_DEBUG, "Trying to create unique named pipe `%s'\n",
2204 h = CreateNamedPipe (*fn,
2205 openMode | FILE_FLAG_OVERLAPPED |
2206 FILE_FLAG_FIRST_PIPE_INSTANCE,
2207 PIPE_TYPE_BYTE | PIPE_READMODE_BYTE, 2, 1, 1, 0,
2210 error_code = GetLastError ();
2213 /* don't re-set name to NULL yet */
2214 if (h == INVALID_HANDLE_VALUE)
2216 SetErrnoFromWinError (error_code);
2218 LOG (GNUNET_ERROR_TYPE_DEBUG,
2219 "Pipe creation have failed because of %d, errno is %d\n", error_code,
2225 LOG (GNUNET_ERROR_TYPE_DEBUG,
2226 "Pipe was to be unique, considering re-creation\n");
2230 if (error_code != ERROR_ACCESS_DENIED && error_code != ERROR_PIPE_BUSY)
2235 LOG (GNUNET_ERROR_TYPE_DEBUG,
2236 "Pipe name was not unique, trying again\n");
2246 ret = GNUNET_malloc (sizeof (*ret));
2248 ret->type = GNUNET_PIPE;
2250 ret->oOverlapRead = GNUNET_malloc (sizeof (OVERLAPPED));
2251 ret->oOverlapWrite = GNUNET_malloc (sizeof (OVERLAPPED));
2253 ret->oOverlapRead->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2254 ret->oOverlapWrite->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2260 char dir[] = "/tmp/gnunet-pipe-XXXXXX";
2262 if (mkdtemp (dir) == NULL)
2264 LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR, "mkdtemp");
2267 GNUNET_asprintf (fn, "%s/child-control", dir);
2270 if (mkfifo (*fn, translate_unix_perms (perm)) == -1)
2272 if ((errno != EEXIST) || (0 != (flags & GNUNET_DISK_OPEN_FAILIFEXISTS)))
2276 flags = flags & (~GNUNET_DISK_OPEN_FAILIFEXISTS);
2277 return GNUNET_DISK_file_open (*fn, flags, perm);
2283 * Opens already existing named pipe/FIFO
2285 * @param fn name of an existing named pipe
2286 * @param flags open flags
2287 * @param perm access permissions
2288 * @return pipe handle on success, NULL on error
2290 struct GNUNET_DISK_FileHandle *
2291 GNUNET_DISK_npipe_open (const char *fn, enum GNUNET_DISK_OpenFlags flags,
2292 enum GNUNET_DISK_AccessPermissions perm)
2295 struct GNUNET_DISK_FileHandle *ret;
2300 if (flags & GNUNET_DISK_OPEN_READWRITE)
2301 openMode = GENERIC_WRITE | GENERIC_READ;
2302 else if (flags & GNUNET_DISK_OPEN_READ)
2303 openMode = GENERIC_READ;
2304 else if (flags & GNUNET_DISK_OPEN_WRITE)
2305 openMode = GENERIC_WRITE;
2307 h = CreateFile (fn, openMode, 0, NULL, OPEN_EXISTING,
2308 FILE_FLAG_OVERLAPPED | FILE_READ_ATTRIBUTES, NULL);
2309 if (h == INVALID_HANDLE_VALUE)
2311 SetErrnoFromWinError (GetLastError ());
2315 ret = GNUNET_malloc (sizeof (*ret));
2317 ret->type = GNUNET_PIPE;
2318 ret->oOverlapRead = GNUNET_malloc (sizeof (OVERLAPPED));
2319 ret->oOverlapWrite = GNUNET_malloc (sizeof (OVERLAPPED));
2320 ret->oOverlapRead->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2321 ret->oOverlapWrite->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2325 flags = flags & (~GNUNET_DISK_OPEN_FAILIFEXISTS);
2326 return GNUNET_DISK_file_open (fn, flags, perm);
2331 * Closes a named pipe/FIFO
2332 * @param pipe named pipe
2333 * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
2336 GNUNET_DISK_npipe_close (struct GNUNET_DISK_FileHandle *pipe)
2339 return close (pipe->fd) == 0 ? GNUNET_OK : GNUNET_SYSERR;
2343 ret = CloseHandle (pipe->h);
2346 SetErrnoFromWinError (GetLastError ());
2347 return GNUNET_SYSERR;
2356 * Get the handle to a particular pipe end
2359 * @param n end to access
2360 * @return handle for the respective end
2362 const struct GNUNET_DISK_FileHandle *
2363 GNUNET_DISK_pipe_handle (const struct GNUNET_DISK_PipeHandle *p,
2364 enum GNUNET_DISK_PipeEnd n)
2368 case GNUNET_DISK_PIPE_END_READ:
2369 case GNUNET_DISK_PIPE_END_WRITE:
2379 * Retrieve OS file handle
2381 * @param fh GNUnet file descriptor
2382 * @param dst destination buffer
2383 * @param dst_len length of dst
2384 * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
2387 GNUNET_DISK_internal_file_handle_ (const struct GNUNET_DISK_FileHandle *fh,
2388 void *dst, size_t dst_len)
2391 if (dst_len < sizeof (HANDLE))
2392 return GNUNET_SYSERR;
2393 *((HANDLE *) dst) = fh->h;
2395 if (dst_len < sizeof (int))
2396 return GNUNET_SYSERR;
2397 *((int *) dst) = fh->fd;