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;
722 LOG (GNUNET_ERROR_TYPE_DEBUG, "It is a pipe trying to read\n");
724 if (!ReadFile (h->h, result, len, &bytesRead, h->oOverlapRead))
726 if (GetLastError () != ERROR_IO_PENDING)
729 LOG (GNUNET_ERROR_TYPE_DEBUG, "Error reading from pipe: %u\n", GetLastError ());
731 SetErrnoFromWinError (GetLastError ());
732 return GNUNET_SYSERR;
735 LOG (GNUNET_ERROR_TYPE_DEBUG, "Will get overlapped result\n");
737 GetOverlappedResult (h->h, h->oOverlapRead, &bytesRead, TRUE);
740 LOG (GNUNET_ERROR_TYPE_DEBUG, "Read %u bytes\n", bytesRead);
745 return read (h->fd, result, len);
751 * Read the contents of a binary file into a buffer.
753 * @param fn file name
754 * @param result the buffer to write the result to
755 * @param len the maximum number of bytes to read
756 * @return number of bytes read, GNUNET_SYSERR on failure
759 GNUNET_DISK_fn_read (const char *fn, void *result, size_t len)
761 struct GNUNET_DISK_FileHandle *fh;
764 fh = GNUNET_DISK_file_open (fn, GNUNET_DISK_OPEN_READ, GNUNET_DISK_PERM_NONE);
766 return GNUNET_SYSERR;
767 ret = GNUNET_DISK_file_read (fh, result, len);
768 GNUNET_assert (GNUNET_OK == GNUNET_DISK_file_close (fh));
775 * Write a buffer to a file.
776 * @param h handle to open file
777 * @param buffer the data to write
778 * @param n number of bytes to write
779 * @return number of bytes written on success, GNUNET_SYSERR on error
782 GNUNET_DISK_file_write (const struct GNUNET_DISK_FileHandle * h,
783 const void *buffer, size_t n)
788 return GNUNET_SYSERR;
794 if (h->type != GNUNET_PIPE)
796 if (!WriteFile (h->h, buffer, n, &bytesWritten, NULL))
798 SetErrnoFromWinError (GetLastError ());
799 return GNUNET_SYSERR;
805 LOG (GNUNET_ERROR_TYPE_DEBUG, "It is a pipe trying to write %u bytes\n", n);
807 if (!WriteFile (h->h, buffer, n, &bytesWritten, h->oOverlapWrite))
809 if (GetLastError () != ERROR_IO_PENDING)
811 SetErrnoFromWinError (GetLastError ());
813 LOG (GNUNET_ERROR_TYPE_DEBUG, "Error writing to pipe: %u\n",
816 return GNUNET_SYSERR;
819 LOG (GNUNET_ERROR_TYPE_DEBUG, "Will get overlapped result\n");
821 if (!GetOverlappedResult (h->h, h->oOverlapWrite, &bytesWritten, TRUE))
823 SetErrnoFromWinError (GetLastError ());
825 LOG (GNUNET_ERROR_TYPE_DEBUG,
826 "Error getting overlapped result while writing to pipe: %u\n",
829 return GNUNET_SYSERR;
835 if (!GetOverlappedResult (h->h, h->oOverlapWrite, &ovr, TRUE))
838 LOG (GNUNET_ERROR_TYPE_DEBUG,
839 "Error getting control overlapped result while writing to pipe: %u\n",
846 LOG (GNUNET_ERROR_TYPE_DEBUG,
847 "Wrote %u bytes (ovr says %u), picking the greatest\n",
852 if (bytesWritten == 0)
857 LOG (GNUNET_ERROR_TYPE_DEBUG, "Wrote %u bytes, returning -1 with EAGAIN\n", bytesWritten);
860 return GNUNET_SYSERR;
864 LOG (GNUNET_ERROR_TYPE_DEBUG, "Wrote %u bytes\n", bytesWritten);
869 return write (h->fd, buffer, n);
874 * Write a buffer to a file. If the file is longer than the
875 * number of bytes that will be written, it will be truncated.
877 * @param fn file name
878 * @param buffer the data to write
879 * @param n number of bytes to write
880 * @param mode file permissions
881 * @return number of bytes written on success, GNUNET_SYSERR on error
884 GNUNET_DISK_fn_write (const char *fn, const void *buffer, size_t n,
885 enum GNUNET_DISK_AccessPermissions mode)
887 struct GNUNET_DISK_FileHandle *fh;
890 fh = GNUNET_DISK_file_open (fn,
891 GNUNET_DISK_OPEN_WRITE | GNUNET_DISK_OPEN_TRUNCATE
892 | GNUNET_DISK_OPEN_CREATE, mode);
894 return GNUNET_SYSERR;
895 ret = GNUNET_DISK_file_write (fh, buffer, n);
896 GNUNET_assert (GNUNET_OK == GNUNET_DISK_file_close (fh));
902 * Scan a directory for files.
904 * @param dirName the name of the directory
905 * @param callback the method to call for each file,
906 * can be NULL, in that case, we only count
907 * @param callback_cls closure for callback
908 * @return the number of files found, GNUNET_SYSERR on error or
909 * ieration aborted by callback returning GNUNET_SYSERR
912 GNUNET_DISK_directory_scan (const char *dirName,
913 GNUNET_FileNameCallback callback,
917 struct dirent *finfo;
922 unsigned int name_len;
925 GNUNET_assert (dirName != NULL);
926 dname = GNUNET_STRINGS_filename_expand (dirName);
928 return GNUNET_SYSERR;
929 while ((strlen (dname) > 0) && (dname[strlen (dname) - 1] == DIR_SEPARATOR))
930 dname[strlen (dname) - 1] = '\0';
931 if (0 != STAT (dname, &istat))
933 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "stat", dname);
935 return GNUNET_SYSERR;
937 if (!S_ISDIR (istat.st_mode))
939 LOG (GNUNET_ERROR_TYPE_WARNING, _("Expected `%s' to be a directory!\n"),
942 return GNUNET_SYSERR;
945 dinfo = OPENDIR (dname);
946 if ((errno == EACCES) || (dinfo == NULL))
948 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "opendir", dname);
952 return GNUNET_SYSERR;
955 n_size = strlen (dname) + name_len + 2;
956 name = GNUNET_malloc (n_size);
957 while ((finfo = READDIR (dinfo)) != NULL)
959 if ((0 == strcmp (finfo->d_name, ".")) ||
960 (0 == strcmp (finfo->d_name, "..")))
962 if (callback != NULL)
964 if (name_len < strlen (finfo->d_name))
967 name_len = strlen (finfo->d_name);
968 n_size = strlen (dname) + name_len + 2;
969 name = GNUNET_malloc (n_size);
971 /* dname can end in "/" only if dname == "/";
972 * if dname does not end in "/", we need to add
973 * a "/" (otherwise, we must not!) */
974 GNUNET_snprintf (name, n_size, "%s%s%s", dname,
975 (strcmp (dname, DIR_SEPARATOR_STR) ==
976 0) ? "" : DIR_SEPARATOR_STR, finfo->d_name);
977 if (GNUNET_OK != callback (callback_cls, name))
982 return GNUNET_SYSERR;
995 * Opaque handle used for iterating over a directory.
997 struct GNUNET_DISK_DirectoryIterator
1001 * Function to call on directory entries.
1003 GNUNET_DISK_DirectoryIteratorCallback callback;
1006 * Closure for callback.
1011 * Reference to directory.
1021 * Next filename to process.
1028 enum GNUNET_SCHEDULER_Priority priority;
1034 * Task used by the directory iterator.
1037 directory_iterator_task (void *cls,
1038 const struct GNUNET_SCHEDULER_TaskContext *tc)
1040 struct GNUNET_DISK_DirectoryIterator *iter = cls;
1043 name = iter->next_name;
1044 GNUNET_assert (name != NULL);
1045 iter->next_name = NULL;
1046 iter->callback (iter->callback_cls, iter, name, iter->dirname);
1052 * This function must be called during the DiskIteratorCallback
1053 * (exactly once) to schedule the task to process the next
1054 * filename in the directory (if there is one).
1056 * @param iter opaque handle for the iterator
1057 * @param can set to GNUNET_YES to terminate the iteration early
1058 * @return GNUNET_YES if iteration will continue,
1059 * GNUNET_NO if this was the last entry (and iteration is complete),
1060 * GNUNET_SYSERR if abort was YES
1063 GNUNET_DISK_directory_iterator_next (struct GNUNET_DISK_DirectoryIterator *iter,
1066 struct dirent *finfo;
1068 GNUNET_assert (iter->next_name == NULL);
1069 if (can == GNUNET_YES)
1071 CLOSEDIR (iter->directory);
1072 GNUNET_free (iter->dirname);
1074 return GNUNET_SYSERR;
1076 while (NULL != (finfo = READDIR (iter->directory)))
1078 if ((0 == strcmp (finfo->d_name, ".")) ||
1079 (0 == strcmp (finfo->d_name, "..")))
1081 GNUNET_asprintf (&iter->next_name, "%s%s%s", iter->dirname,
1082 DIR_SEPARATOR_STR, finfo->d_name);
1087 GNUNET_DISK_directory_iterator_next (iter, GNUNET_YES);
1090 GNUNET_SCHEDULER_add_with_priority (iter->priority, &directory_iterator_task,
1097 * Scan a directory for files using the scheduler to run a task for
1098 * each entry. The name of the directory must be expanded first (!).
1099 * If a scheduler does not need to be used, GNUNET_DISK_directory_scan
1100 * may provide a simpler API.
1102 * @param prio priority to use
1103 * @param dirName the name of the directory
1104 * @param callback the method to call for each file
1105 * @param callback_cls closure for callback
1106 * @return GNUNET_YES if directory is not empty and @callback
1107 * will be called later, GNUNET_NO otherwise, GNUNET_SYSERR on error.
1110 GNUNET_DISK_directory_iterator_start (enum GNUNET_SCHEDULER_Priority prio,
1111 const char *dirName,
1112 GNUNET_DISK_DirectoryIteratorCallback
1113 callback, void *callback_cls)
1115 struct GNUNET_DISK_DirectoryIterator *di;
1117 di = GNUNET_malloc (sizeof (struct GNUNET_DISK_DirectoryIterator));
1118 di->callback = callback;
1119 di->callback_cls = callback_cls;
1120 di->directory = OPENDIR (dirName);
1121 if (di->directory == NULL)
1124 callback (callback_cls, NULL, NULL, NULL);
1125 return GNUNET_SYSERR;
1127 di->dirname = GNUNET_strdup (dirName);
1128 di->priority = prio;
1129 return GNUNET_DISK_directory_iterator_next (di, GNUNET_NO);
1134 * Function that removes the given directory by calling
1135 * "GNUNET_DISK_directory_remove".
1137 * @param unused not used
1138 * @param fn directory to remove
1142 remove_helper (void *unused, const char *fn)
1144 (void) GNUNET_DISK_directory_remove (fn);
1150 * Remove all files in a directory (rm -rf). Call with
1154 * @param fileName the file to remove
1155 * @return GNUNET_OK on success, GNUNET_SYSERR on error
1158 GNUNET_DISK_directory_remove (const char *fileName)
1162 if (0 != LSTAT (fileName, &istat))
1163 return GNUNET_NO; /* file may not exist... */
1164 CHMOD (fileName, S_IWUSR | S_IRUSR | S_IXUSR);
1165 if (UNLINK (fileName) == 0)
1167 if ((errno != EISDIR) &&
1168 /* EISDIR is not sufficient in all cases, e.g.
1169 * sticky /tmp directory may result in EPERM on BSD.
1170 * So we also explicitly check "isDirectory" */
1171 (GNUNET_YES != GNUNET_DISK_directory_test (fileName)))
1173 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "rmdir", fileName);
1174 return GNUNET_SYSERR;
1176 if (GNUNET_SYSERR ==
1177 GNUNET_DISK_directory_scan (fileName, &remove_helper, NULL))
1178 return GNUNET_SYSERR;
1179 if (0 != RMDIR (fileName))
1181 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "rmdir", fileName);
1182 return GNUNET_SYSERR;
1191 * @param src file to copy
1192 * @param dst destination file name
1193 * @return GNUNET_OK on success, GNUNET_SYSERR on error
1196 GNUNET_DISK_file_copy (const char *src, const char *dst)
1202 struct GNUNET_DISK_FileHandle *in;
1203 struct GNUNET_DISK_FileHandle *out;
1205 if (GNUNET_OK != GNUNET_DISK_file_size (src, &size, GNUNET_YES))
1206 return GNUNET_SYSERR;
1208 in = GNUNET_DISK_file_open (src, GNUNET_DISK_OPEN_READ,
1209 GNUNET_DISK_PERM_NONE);
1211 return GNUNET_SYSERR;
1213 GNUNET_DISK_file_open (dst,
1214 GNUNET_DISK_OPEN_WRITE | GNUNET_DISK_OPEN_CREATE |
1215 GNUNET_DISK_OPEN_FAILIFEXISTS,
1216 GNUNET_DISK_PERM_USER_READ |
1217 GNUNET_DISK_PERM_USER_WRITE |
1218 GNUNET_DISK_PERM_GROUP_READ |
1219 GNUNET_DISK_PERM_GROUP_WRITE);
1222 GNUNET_DISK_file_close (in);
1223 return GNUNET_SYSERR;
1225 buf = GNUNET_malloc (COPY_BLK_SIZE);
1228 len = COPY_BLK_SIZE;
1229 if (len > size - pos)
1231 if (len != GNUNET_DISK_file_read (in, buf, len))
1233 if (len != GNUNET_DISK_file_write (out, buf, len))
1238 GNUNET_DISK_file_close (in);
1239 GNUNET_DISK_file_close (out);
1243 GNUNET_DISK_file_close (in);
1244 GNUNET_DISK_file_close (out);
1245 return GNUNET_SYSERR;
1250 * @brief Removes special characters as ':' from a filename.
1251 * @param fn the filename to canonicalize
1254 GNUNET_DISK_filename_canonicalize (char *fn)
1264 if (c == '/' || c == '\\' || c == ':' || c == '*' || c == '?' || c == '"' ||
1265 c == '<' || c == '>' || c == '|')
1277 * @brief Change owner of a file
1279 * @param filename name of file to change the owner of
1280 * @param user name of the new owner
1281 * @return GNUNET_OK on success, GNUNET_SYSERR on failure
1284 GNUNET_DISK_file_change_owner (const char *filename, const char *user)
1289 pws = getpwnam (user);
1292 LOG (GNUNET_ERROR_TYPE_ERROR,
1293 _("Cannot obtain information about user `%s': %s\n"), user,
1295 return GNUNET_SYSERR;
1297 if (0 != chown (filename, pws->pw_uid, pws->pw_gid))
1298 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "chown", filename);
1305 * Lock a part of a file
1306 * @param fh file handle
1307 * @param lockStart absolute position from where to lock
1308 * @param lockEnd absolute position until where to lock
1309 * @param excl GNUNET_YES for an exclusive lock
1310 * @return GNUNET_OK on success, GNUNET_SYSERR on error
1313 GNUNET_DISK_file_lock (struct GNUNET_DISK_FileHandle *fh, OFF_T lockStart,
1314 OFF_T lockEnd, int excl)
1319 return GNUNET_SYSERR;
1325 memset (&fl, 0, sizeof (struct flock));
1326 fl.l_type = excl ? F_WRLCK : F_RDLCK;
1327 fl.l_whence = SEEK_SET;
1328 fl.l_start = lockStart;
1331 return fcntl (fh->fd, F_SETLK, &fl) != 0 ? GNUNET_SYSERR : GNUNET_OK;
1334 OFF_T diff = lockEnd - lockStart;
1335 DWORD diff_low, diff_high;
1336 diff_low = (DWORD) (diff & 0xFFFFFFFF);
1337 diff_high = (DWORD) ((diff >> (sizeof (DWORD) * 8)) & 0xFFFFFFFF);
1339 memset (&o, 0, sizeof (OVERLAPPED));
1340 o.Offset = (DWORD) (lockStart & 0xFFFFFFFF);;
1341 o.OffsetHigh = (DWORD) (((lockStart & ~0xFFFFFFFF) >> (sizeof (DWORD) * 8)) & 0xFFFFFFFF);
1344 (fh->h, (excl ? LOCKFILE_EXCLUSIVE_LOCK : 0) | LOCKFILE_FAIL_IMMEDIATELY,
1345 0, diff_low, diff_high, &o))
1347 SetErrnoFromWinError (GetLastError ());
1348 return GNUNET_SYSERR;
1357 * Unlock a part of a file
1358 * @param fh file handle
1359 * @param unlockStart absolute position from where to unlock
1360 * @param unlockEnd absolute position until where to unlock
1361 * @return GNUNET_OK on success, GNUNET_SYSERR on error
1364 GNUNET_DISK_file_unlock (struct GNUNET_DISK_FileHandle *fh, OFF_T unlockStart,
1370 return GNUNET_SYSERR;
1376 memset (&fl, 0, sizeof (struct flock));
1377 fl.l_type = F_UNLCK;
1378 fl.l_whence = SEEK_SET;
1379 fl.l_start = unlockStart;
1380 fl.l_len = unlockEnd;
1382 return fcntl (fh->fd, F_SETLK, &fl) != 0 ? GNUNET_SYSERR : GNUNET_OK;
1385 OFF_T diff = unlockEnd - unlockStart;
1386 DWORD diff_low, diff_high;
1387 diff_low = (DWORD) (diff & 0xFFFFFFFF);
1388 diff_high = (DWORD) ((diff >> (sizeof (DWORD) * 8)) & 0xFFFFFFFF);
1390 memset (&o, 0, sizeof (OVERLAPPED));
1391 o.Offset = (DWORD) (unlockStart & 0xFFFFFFFF);;
1392 o.OffsetHigh = (DWORD) (((unlockStart & ~0xFFFFFFFF) >> (sizeof (DWORD) * 8)) & 0xFFFFFFFF);
1394 if (!UnlockFileEx (fh->h, 0, diff_low, diff_high, &o))
1396 SetErrnoFromWinError (GetLastError ());
1397 return GNUNET_SYSERR;
1406 * Open a file. Note that the access permissions will only be
1407 * used if a new file is created and if the underlying operating
1408 * system supports the given permissions.
1410 * @param fn file name to be opened
1411 * @param flags opening flags, a combination of GNUNET_DISK_OPEN_xxx bit flags
1412 * @param perm permissions for the newly created file, use
1413 * GNUNET_DISK_PERM_USER_NONE if a file could not be created by this
1414 * call (because of flags)
1415 * @return IO handle on success, NULL on error
1417 struct GNUNET_DISK_FileHandle *
1418 GNUNET_DISK_file_open (const char *fn, enum GNUNET_DISK_OpenFlags flags,
1419 enum GNUNET_DISK_AccessPermissions perm)
1422 struct GNUNET_DISK_FileHandle *ret;
1428 wchar_t wexpfn[MAX_PATH + 1];
1435 expfn = GNUNET_STRINGS_filename_expand (fn);
1440 if (GNUNET_DISK_OPEN_READWRITE == (flags & GNUNET_DISK_OPEN_READWRITE))
1441 oflags = O_RDWR; /* note: O_RDWR is NOT always O_RDONLY | O_WRONLY */
1442 else if (flags & GNUNET_DISK_OPEN_READ)
1444 else if (flags & GNUNET_DISK_OPEN_WRITE)
1449 GNUNET_free (expfn);
1452 if (flags & GNUNET_DISK_OPEN_FAILIFEXISTS)
1453 oflags |= (O_CREAT | O_EXCL);
1454 if (flags & GNUNET_DISK_OPEN_TRUNCATE)
1456 if (flags & GNUNET_DISK_OPEN_APPEND)
1458 if (flags & GNUNET_DISK_OPEN_CREATE)
1460 (void) GNUNET_DISK_directory_create_for_file (expfn);
1462 mode = translate_unix_perms (perm);
1465 fd = open (expfn, oflags | O_LARGEFILE, mode);
1468 if (0 == (flags & GNUNET_DISK_OPEN_FAILIFEXISTS))
1469 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "open", expfn);
1471 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_DEBUG, "open", expfn);
1472 GNUNET_free (expfn);
1479 if (GNUNET_DISK_OPEN_READWRITE == (flags & GNUNET_DISK_OPEN_READWRITE))
1480 access = FILE_READ_DATA | FILE_WRITE_DATA;
1481 else if (flags & GNUNET_DISK_OPEN_READ)
1482 access = FILE_READ_DATA;
1483 else if (flags & GNUNET_DISK_OPEN_WRITE)
1484 access = FILE_WRITE_DATA;
1486 if (flags & GNUNET_DISK_OPEN_FAILIFEXISTS)
1490 else if (flags & GNUNET_DISK_OPEN_CREATE)
1492 (void) GNUNET_DISK_directory_create_for_file (expfn);
1493 if (flags & GNUNET_DISK_OPEN_TRUNCATE)
1494 disp = CREATE_ALWAYS;
1498 else if (flags & GNUNET_DISK_OPEN_TRUNCATE)
1500 disp = TRUNCATE_EXISTING;
1504 disp = OPEN_EXISTING;
1507 if (ERROR_SUCCESS == plibc_conv_to_win_pathwconv(expfn, wexpfn))
1508 h = CreateFileW (wexpfn, access,
1509 FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE, NULL,
1510 disp, FILE_ATTRIBUTE_NORMAL, NULL);
1512 h = INVALID_HANDLE_VALUE;
1513 if (h == INVALID_HANDLE_VALUE)
1515 SetErrnoFromWinError (GetLastError ());
1516 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "open", expfn);
1517 GNUNET_free (expfn);
1521 if (flags & GNUNET_DISK_OPEN_APPEND)
1522 if (SetFilePointer (h, 0, 0, FILE_END) == INVALID_SET_FILE_POINTER)
1524 SetErrnoFromWinError (GetLastError ());
1525 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "SetFilePointer", expfn);
1527 GNUNET_free (expfn);
1532 ret = GNUNET_malloc (sizeof (struct GNUNET_DISK_FileHandle));
1535 ret->type = GNUNET_DISK_FILE;
1539 GNUNET_free (expfn);
1545 * Close an open file
1546 * @param h file handle
1547 * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
1550 GNUNET_DISK_file_close (struct GNUNET_DISK_FileHandle *h)
1555 return GNUNET_SYSERR;
1559 if (!CloseHandle (h->h))
1561 SetErrnoFromWinError (GetLastError ());
1562 LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING, "close");
1563 GNUNET_free (h->oOverlapRead);
1564 GNUNET_free (h->oOverlapWrite);
1566 return GNUNET_SYSERR;
1569 if (close (h->fd) != 0)
1571 LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING, "close");
1573 return GNUNET_SYSERR;
1582 * Construct full path to a file inside of the private
1583 * directory used by GNUnet. Also creates the corresponding
1584 * directory. If the resulting name is supposed to be
1585 * a directory, end the last argument in '/' (or pass
1586 * DIR_SEPARATOR_STR as the last argument before NULL).
1588 * @param cfg configuration to use (determines HOME)
1589 * @param serviceName name of the service
1590 * @param ... is NULL-terminated list of
1591 * path components to append to the
1592 * private directory name.
1593 * @return the constructed filename
1596 GNUNET_DISK_get_home_filename (const struct GNUNET_CONFIGURATION_Handle *cfg,
1597 const char *serviceName, ...)
1603 unsigned int needed;
1606 GNUNET_CONFIGURATION_get_value_filename (cfg, serviceName, "HOME", &pfx))
1610 LOG (GNUNET_ERROR_TYPE_WARNING,
1611 _("No `%s' specified for service `%s' in configuration.\n"), "HOME",
1615 needed = strlen (pfx) + 2;
1616 if ((pfx[strlen (pfx) - 1] != '/') && (pfx[strlen (pfx) - 1] != '\\'))
1618 va_start (ap, serviceName);
1621 c = va_arg (ap, const char *);
1625 needed += strlen (c);
1626 if ((c[strlen (c) - 1] != '/') && (c[strlen (c) - 1] != '\\'))
1630 ret = GNUNET_malloc (needed);
1633 va_start (ap, serviceName);
1636 c = va_arg (ap, const char *);
1640 if ((c[strlen (c) - 1] != '/') && (c[strlen (c) - 1] != '\\'))
1641 strcat (ret, DIR_SEPARATOR_STR);
1645 if ((ret[strlen (ret) - 1] != '/') && (ret[strlen (ret) - 1] != '\\'))
1646 (void) GNUNET_DISK_directory_create_for_file (ret);
1648 (void) GNUNET_DISK_directory_create (ret);
1654 * Handle for a memory-mapping operation.
1656 struct GNUNET_DISK_MapHandle
1659 * Address where the map is in memory.
1665 * Underlying OS handle.
1670 * Number of bytes mapped.
1678 #define MAP_FAILED ((void *) -1)
1682 * Map a file into memory
1684 * @param h open file handle
1685 * @param m handle to the new mapping
1686 * @param access access specification, GNUNET_DISK_MAP_TYPE_xxx
1687 * @param len size of the mapping
1688 * @return pointer to the mapped memory region, NULL on failure
1691 GNUNET_DISK_file_map (const struct GNUNET_DISK_FileHandle *h,
1692 struct GNUNET_DISK_MapHandle **m,
1693 enum GNUNET_DISK_MapType access, size_t len)
1702 DWORD mapAccess, protect;
1704 if ((access & GNUNET_DISK_MAP_TYPE_READ) &&
1705 (access & GNUNET_DISK_MAP_TYPE_WRITE))
1707 protect = PAGE_READWRITE;
1708 mapAccess = FILE_MAP_ALL_ACCESS;
1710 else if (access & GNUNET_DISK_MAP_TYPE_READ)
1712 protect = PAGE_READONLY;
1713 mapAccess = FILE_MAP_READ;
1715 else if (access & GNUNET_DISK_MAP_TYPE_WRITE)
1717 protect = PAGE_READWRITE;
1718 mapAccess = FILE_MAP_WRITE;
1726 *m = GNUNET_malloc (sizeof (struct GNUNET_DISK_MapHandle));
1727 (*m)->h = CreateFileMapping (h->h, NULL, protect, 0, 0, NULL);
1728 if ((*m)->h == INVALID_HANDLE_VALUE)
1730 SetErrnoFromWinError (GetLastError ());
1735 (*m)->addr = MapViewOfFile ((*m)->h, mapAccess, 0, 0, len);
1738 SetErrnoFromWinError (GetLastError ());
1739 CloseHandle ((*m)->h);
1748 if (access & GNUNET_DISK_MAP_TYPE_READ)
1750 if (access & GNUNET_DISK_MAP_TYPE_WRITE)
1752 *m = GNUNET_malloc (sizeof (struct GNUNET_DISK_MapHandle));
1753 (*m)->addr = mmap (NULL, len, prot, MAP_SHARED, h->fd, 0);
1754 GNUNET_assert (NULL != (*m)->addr);
1755 if (MAP_FAILED == (*m)->addr)
1767 * @param h mapping handle
1768 * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
1771 GNUNET_DISK_file_unmap (struct GNUNET_DISK_MapHandle *h)
1778 return GNUNET_SYSERR;
1782 ret = UnmapViewOfFile (h->addr) ? GNUNET_OK : GNUNET_SYSERR;
1783 if (ret != GNUNET_OK)
1784 SetErrnoFromWinError (GetLastError ());
1785 if (!CloseHandle (h->h) && (ret == GNUNET_OK))
1787 ret = GNUNET_SYSERR;
1788 SetErrnoFromWinError (GetLastError ());
1791 ret = munmap (h->addr, h->len) != -1 ? GNUNET_OK : GNUNET_SYSERR;
1799 * Write file changes to disk
1800 * @param h handle to an open file
1801 * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
1804 GNUNET_DISK_file_sync (const struct GNUNET_DISK_FileHandle *h)
1809 return GNUNET_SYSERR;
1815 ret = FlushFileBuffers (h->h) ? GNUNET_OK : GNUNET_SYSERR;
1816 if (ret != GNUNET_OK)
1817 SetErrnoFromWinError (GetLastError ());
1819 #elif defined(FREEBSD) || defined(OPENBSD) || defined(DARWIN)
1820 return fsync (h->fd) == -1 ? GNUNET_SYSERR : GNUNET_OK;
1822 return fdatasync (h->fd) == -1 ? GNUNET_SYSERR : GNUNET_OK;
1827 /* Copyright Bob Byrnes <byrnes <at> curl.com>
1828 http://permalink.gmane.org/gmane.os.cygwin.patches/2121
1830 /* Create a pipe, and return handles to the read and write ends,
1831 just like CreatePipe, but ensure that the write end permits
1832 FILE_READ_ATTRIBUTES access, on later versions of win32 where
1833 this is supported. This access is needed by NtQueryInformationFile,
1834 which is used to implement select and nonblocking writes.
1835 Note that the return value is either NO_ERROR or GetLastError,
1836 unlike CreatePipe, which returns a bool for success or failure. */
1838 create_selectable_pipe (PHANDLE read_pipe_ptr, PHANDLE write_pipe_ptr,
1839 LPSECURITY_ATTRIBUTES sa_ptr, DWORD psize,
1840 DWORD dwReadMode, DWORD dwWriteMode)
1842 /* Default to error. */
1843 *read_pipe_ptr = *write_pipe_ptr = INVALID_HANDLE_VALUE;
1845 HANDLE read_pipe = INVALID_HANDLE_VALUE, write_pipe = INVALID_HANDLE_VALUE;
1847 /* Ensure that there is enough pipe buffer space for atomic writes. */
1848 if (psize < PIPE_BUF)
1851 char pipename[MAX_PATH];
1853 /* Retry CreateNamedPipe as long as the pipe name is in use.
1854 * Retrying will probably never be necessary, but we want
1855 * to be as robust as possible. */
1858 static volatile LONG pipe_unique_id;
1860 snprintf (pipename, sizeof pipename, "\\\\.\\pipe\\gnunet-%d-%ld",
1861 getpid (), InterlockedIncrement ((LONG *) & pipe_unique_id));
1863 LOG (GNUNET_ERROR_TYPE_DEBUG, "CreateNamedPipe: name = %s, size = %lu\n",
1866 /* Use CreateNamedPipe instead of CreatePipe, because the latter
1867 * returns a write handle that does not permit FILE_READ_ATTRIBUTES
1868 * access, on versions of win32 earlier than WinXP SP2.
1869 * CreatePipe also stupidly creates a full duplex pipe, which is
1870 * a waste, since only a single direction is actually used.
1871 * It's important to only allow a single instance, to ensure that
1872 * the pipe was not created earlier by some other process, even if
1873 * the pid has been reused. We avoid FILE_FLAG_FIRST_PIPE_INSTANCE
1874 * because that is only available for Win2k SP2 and WinXP. */
1875 read_pipe = CreateNamedPipeA (pipename, PIPE_ACCESS_INBOUND | dwReadMode, PIPE_TYPE_BYTE | PIPE_READMODE_BYTE, 1, /* max instances */
1876 psize, /* output buffer size */
1877 psize, /* input buffer size */
1878 NMPWAIT_USE_DEFAULT_WAIT, sa_ptr);
1880 if (read_pipe != INVALID_HANDLE_VALUE)
1883 LOG (GNUNET_ERROR_TYPE_DEBUG, "pipe read handle = %p\n", read_pipe);
1888 DWORD err = GetLastError ();
1892 case ERROR_PIPE_BUSY:
1893 /* The pipe is already open with compatible parameters.
1894 * Pick a new name and retry. */
1896 LOG (GNUNET_ERROR_TYPE_DEBUG, "pipe busy, retrying\n");
1899 case ERROR_ACCESS_DENIED:
1900 /* The pipe is already open with incompatible parameters.
1901 * Pick a new name and retry. */
1903 LOG (GNUNET_ERROR_TYPE_DEBUG, "pipe access denied, retrying\n");
1906 case ERROR_CALL_NOT_IMPLEMENTED:
1907 /* We are on an older Win9x platform without named pipes.
1908 * Return an anonymous pipe as the best approximation. */
1910 LOG (GNUNET_ERROR_TYPE_DEBUG,
1911 "CreateNamedPipe not implemented, resorting to "
1912 "CreatePipe: size = %lu\n", psize);
1914 if (CreatePipe (read_pipe_ptr, write_pipe_ptr, sa_ptr, psize))
1917 LOG (GNUNET_ERROR_TYPE_DEBUG, "pipe read handle = %p\n",
1919 LOG (GNUNET_ERROR_TYPE_DEBUG, "pipe write handle = %p\n",
1924 err = GetLastError ();
1925 LOG (GNUNET_ERROR_TYPE_ERROR, "CreatePipe failed: %d\n", err);
1928 LOG (GNUNET_ERROR_TYPE_ERROR, "CreateNamedPipe failed: %d\n", err);
1934 LOG (GNUNET_ERROR_TYPE_DEBUG, "CreateFile: name = %s\n", pipename);
1937 /* Open the named pipe for writing.
1938 * Be sure to permit FILE_READ_ATTRIBUTES access. */
1939 write_pipe = CreateFileA (pipename, GENERIC_WRITE | FILE_READ_ATTRIBUTES, 0, /* share mode */
1940 sa_ptr, OPEN_EXISTING, dwWriteMode, /* flags and attributes */
1941 0); /* handle to template file */
1943 if (write_pipe == INVALID_HANDLE_VALUE)
1946 DWORD err = GetLastError ();
1949 LOG (GNUNET_ERROR_TYPE_DEBUG, "CreateFile failed: %d\n", err);
1951 CloseHandle (read_pipe);
1955 LOG (GNUNET_ERROR_TYPE_DEBUG, "pipe write handle = %p\n", write_pipe);
1958 *read_pipe_ptr = read_pipe;
1959 *write_pipe_ptr = write_pipe;
1965 * Creates an interprocess channel
1967 * @param blocking creates an asynchronous pipe if set to GNUNET_NO
1968 * @param inherit_read inherit the parent processes stdin (only for windows)
1969 * @param inherit_write inherit the parent processes stdout (only for windows)
1971 * @return handle to the new pipe, NULL on error
1973 struct GNUNET_DISK_PipeHandle *
1974 GNUNET_DISK_pipe (int blocking, int inherit_read, int inherit_write)
1976 struct GNUNET_DISK_PipeHandle *p;
1977 struct GNUNET_DISK_FileHandle *fds;
1979 p = GNUNET_malloc (sizeof (struct GNUNET_DISK_PipeHandle) +
1980 2 * sizeof (struct GNUNET_DISK_FileHandle));
1981 fds = (struct GNUNET_DISK_FileHandle *) &p[1];
1994 LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR, "pipe");
1999 p->fd[0]->fd = fd[0];
2000 p->fd[1]->fd = fd[1];
2002 flags = fcntl (fd[0], F_GETFL);
2004 flags |= O_NONBLOCK;
2005 if (0 > fcntl (fd[0], F_SETFL, flags))
2007 flags = fcntl (fd[0], F_GETFD);
2008 flags |= FD_CLOEXEC;
2009 if (0 > fcntl (fd[0], F_SETFD, flags))
2012 flags = fcntl (fd[1], F_GETFL);
2014 flags |= O_NONBLOCK;
2015 if (0 > fcntl (fd[1], F_SETFL, flags))
2017 flags = fcntl (fd[1], F_GETFD);
2018 flags |= FD_CLOEXEC;
2019 if (0 > fcntl (fd[1], F_SETFD, flags))
2024 LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR, "fcntl");
2025 GNUNET_break (0 == close (p->fd[0]->fd));
2026 GNUNET_break (0 == close (p->fd[1]->fd));
2036 create_selectable_pipe (&p->fd[0]->h, &p->fd[1]->h, NULL, 0,
2037 FILE_FLAG_OVERLAPPED, FILE_FLAG_OVERLAPPED);
2041 SetErrnoFromWinError (GetLastError ());
2044 if (!DuplicateHandle
2045 (GetCurrentProcess (), p->fd[0]->h, GetCurrentProcess (), &tmp_handle, 0,
2046 inherit_read == GNUNET_YES ? TRUE : FALSE, DUPLICATE_SAME_ACCESS))
2048 SetErrnoFromWinError (GetLastError ());
2049 CloseHandle (p->fd[0]->h);
2050 CloseHandle (p->fd[1]->h);
2054 CloseHandle (p->fd[0]->h);
2055 p->fd[0]->h = tmp_handle;
2057 if (!DuplicateHandle
2058 (GetCurrentProcess (), p->fd[1]->h, GetCurrentProcess (), &tmp_handle, 0,
2059 inherit_write == GNUNET_YES ? TRUE : FALSE, DUPLICATE_SAME_ACCESS))
2061 SetErrnoFromWinError (GetLastError ());
2062 CloseHandle (p->fd[0]->h);
2063 CloseHandle (p->fd[1]->h);
2067 CloseHandle (p->fd[1]->h);
2068 p->fd[1]->h = tmp_handle;
2074 SetNamedPipeHandleState (p->fd[0]->h, &mode, NULL, NULL);
2075 SetNamedPipeHandleState (p->fd[1]->h, &mode, NULL, NULL);
2076 /* this always fails on Windows 95, so we don't care about error handling */
2078 p->fd[0]->type = GNUNET_PIPE;
2079 p->fd[1]->type = GNUNET_PIPE;
2081 p->fd[0]->oOverlapRead = GNUNET_malloc (sizeof (OVERLAPPED));
2082 p->fd[0]->oOverlapWrite = GNUNET_malloc (sizeof (OVERLAPPED));
2083 p->fd[1]->oOverlapRead = GNUNET_malloc (sizeof (OVERLAPPED));
2084 p->fd[1]->oOverlapWrite = GNUNET_malloc (sizeof (OVERLAPPED));
2086 p->fd[0]->oOverlapRead->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2087 p->fd[0]->oOverlapWrite->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2089 p->fd[1]->oOverlapRead->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2090 p->fd[1]->oOverlapWrite->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2097 * Creates a pipe object from a couple of file descriptors.
2098 * Useful for wrapping existing pipe FDs.
2100 * @param blocking creates an asynchronous pipe if set to GNUNET_NO
2101 * @param fd an array of two fd values. One of them may be -1 for read-only or write-only pipes
2103 * @return handle to the new pipe, NULL on error
2105 struct GNUNET_DISK_PipeHandle *
2106 GNUNET_DISK_pipe_from_fd (int blocking, int fd[2])
2108 struct GNUNET_DISK_PipeHandle *p;
2109 struct GNUNET_DISK_FileHandle *fds;
2111 p = GNUNET_malloc (sizeof (struct GNUNET_DISK_PipeHandle) +
2112 2 * sizeof (struct GNUNET_DISK_FileHandle));
2113 fds = (struct GNUNET_DISK_FileHandle *) &p[1];
2121 p->fd[0]->fd = fd[0];
2122 p->fd[1]->fd = fd[1];
2126 flags = fcntl (fd[0], F_GETFL);
2128 flags |= O_NONBLOCK;
2129 if (0 > fcntl (fd[0], F_SETFL, flags))
2131 flags = fcntl (fd[0], F_GETFD);
2132 flags |= FD_CLOEXEC;
2133 if (0 > fcntl (fd[0], F_SETFD, flags))
2139 flags = fcntl (fd[1], F_GETFL);
2141 flags |= O_NONBLOCK;
2142 if (0 > fcntl (fd[1], F_SETFL, flags))
2144 flags = fcntl (fd[1], F_GETFD);
2145 flags |= FD_CLOEXEC;
2146 if (0 > fcntl (fd[1], F_SETFD, flags))
2152 LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR, "fcntl");
2153 if (p->fd[0]->fd >= 0)
2154 GNUNET_break (0 == close (p->fd[0]->fd));
2155 if (p->fd[1]->fd >= 0)
2156 GNUNET_break (0 == close (p->fd[1]->fd));
2165 p->fd[0]->h = _get_osfhandle (fd[0]);
2167 p->fd[0]->h = INVALID_HANDLE_VALUE;
2169 p->fd[1]->h = _get_osfhandle (fd[1]);
2171 p->fd[1]->h = INVALID_HANDLE_VALUE;
2178 if (p->fd[0]->h != INVALID_HANDLE_VALUE)
2179 SetNamedPipeHandleState (p->fd[0]->h, &mode, NULL, NULL);
2180 if (p->fd[1]->h != INVALID_HANDLE_VALUE)
2181 SetNamedPipeHandleState (p->fd[1]->h, &mode, NULL, NULL);
2182 /* this always fails on Windows 95, so we don't care about error handling */
2185 if (p->fd[0]->h != INVALID_HANDLE_VALUE)
2187 p->fd[0]->type = GNUNET_PIPE;
2188 p->fd[0]->oOverlapRead = GNUNET_malloc (sizeof (OVERLAPPED));
2189 p->fd[0]->oOverlapWrite = GNUNET_malloc (sizeof (OVERLAPPED));
2190 p->fd[0]->oOverlapRead->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2191 p->fd[0]->oOverlapWrite->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2194 if (p->fd[1]->h != INVALID_HANDLE_VALUE)
2196 p->fd[1]->type = GNUNET_PIPE;
2197 p->fd[1]->oOverlapRead = GNUNET_malloc (sizeof (OVERLAPPED));
2198 p->fd[1]->oOverlapWrite = GNUNET_malloc (sizeof (OVERLAPPED));
2199 p->fd[1]->oOverlapRead->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2200 p->fd[1]->oOverlapWrite->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2208 * Closes an interprocess channel
2210 * @param p pipe to close
2211 * @param end which end of the pipe to close
2212 * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
2215 GNUNET_DISK_pipe_close_end (struct GNUNET_DISK_PipeHandle *p,
2216 enum GNUNET_DISK_PipeEnd end)
2218 int ret = GNUNET_OK;
2222 if (end == GNUNET_DISK_PIPE_END_READ)
2224 if (p->fd[0]->h != INVALID_HANDLE_VALUE)
2226 if (!CloseHandle (p->fd[0]->h))
2228 SetErrnoFromWinError (GetLastError ());
2229 ret = GNUNET_SYSERR;
2231 GNUNET_free (p->fd[0]->oOverlapRead);
2232 GNUNET_free (p->fd[0]->oOverlapWrite);
2233 p->fd[0]->h = INVALID_HANDLE_VALUE;
2236 else if (end == GNUNET_DISK_PIPE_END_WRITE)
2238 if (p->fd[0]->h != INVALID_HANDLE_VALUE)
2240 if (!CloseHandle (p->fd[1]->h))
2242 SetErrnoFromWinError (GetLastError ());
2243 ret = GNUNET_SYSERR;
2245 GNUNET_free (p->fd[1]->oOverlapRead);
2246 GNUNET_free (p->fd[1]->oOverlapWrite);
2247 p->fd[1]->h = INVALID_HANDLE_VALUE;
2253 if (end == GNUNET_DISK_PIPE_END_READ)
2255 if (0 != close (p->fd[0]->fd))
2257 ret = GNUNET_SYSERR;
2262 else if (end == GNUNET_DISK_PIPE_END_WRITE)
2264 if (0 != close (p->fd[1]->fd))
2266 ret = GNUNET_SYSERR;
2277 * Closes an interprocess channel
2279 * @param p pipe to close
2280 * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
2283 GNUNET_DISK_pipe_close (struct GNUNET_DISK_PipeHandle *p)
2285 int ret = GNUNET_OK;
2289 if (p->fd[0]->h != INVALID_HANDLE_VALUE)
2291 if (!CloseHandle (p->fd[0]->h))
2293 SetErrnoFromWinError (GetLastError ());
2294 ret = GNUNET_SYSERR;
2296 GNUNET_free (p->fd[0]->oOverlapRead);
2297 GNUNET_free (p->fd[0]->oOverlapWrite);
2299 if (p->fd[1]->h != INVALID_HANDLE_VALUE)
2301 if (!CloseHandle (p->fd[1]->h))
2303 SetErrnoFromWinError (GetLastError ());
2304 ret = GNUNET_SYSERR;
2306 GNUNET_free (p->fd[1]->oOverlapRead);
2307 GNUNET_free (p->fd[1]->oOverlapWrite);
2312 if (p->fd[0]->fd != -1)
2314 if (0 != close (p->fd[0]->fd))
2316 ret = GNUNET_SYSERR;
2321 if (p->fd[1]->fd != -1)
2323 if (0 != close (p->fd[1]->fd))
2325 ret = GNUNET_SYSERR;
2337 * Creates a named pipe/FIFO and opens it
2338 * @param fn pointer to the name of the named pipe or to NULL
2339 * @param flags open flags
2340 * @param perm access permissions
2341 * @return pipe handle on success, NULL on error
2343 struct GNUNET_DISK_FileHandle *
2344 GNUNET_DISK_npipe_create (char **fn, enum GNUNET_DISK_OpenFlags flags,
2345 enum GNUNET_DISK_AccessPermissions perm)
2348 struct GNUNET_DISK_FileHandle *ret;
2354 if (flags & GNUNET_DISK_OPEN_READWRITE)
2355 openMode = PIPE_ACCESS_DUPLEX;
2356 else if (flags & GNUNET_DISK_OPEN_READ)
2357 openMode = PIPE_ACCESS_INBOUND;
2358 else if (flags & GNUNET_DISK_OPEN_WRITE)
2359 openMode = PIPE_ACCESS_OUTBOUND;
2361 if (flags & GNUNET_DISK_OPEN_FAILIFEXISTS)
2362 openMode |= FILE_FLAG_FIRST_PIPE_INSTANCE;
2371 GNUNET_asprintf (&name, "\\\\.\\pipe\\%.246s", fn);
2373 LOG (GNUNET_ERROR_TYPE_DEBUG,
2374 "Trying to create an instance of named pipe `%s'\n", name);
2376 /* 1) This might work just fine with UTF-8 strings as it is.
2377 * 2) This is only used by GNUnet itself, and only with latin names.
2379 h = CreateNamedPipe (name, openMode | FILE_FLAG_OVERLAPPED,
2380 PIPE_TYPE_BYTE | PIPE_READMODE_BYTE, 2, 1, 1, 0,
2385 GNUNET_asprintf (fn, "\\\\.\\pipe\\gnunet-%llu",
2386 GNUNET_CRYPTO_random_u64 (GNUNET_CRYPTO_QUALITY_WEAK,
2389 LOG (GNUNET_ERROR_TYPE_DEBUG, "Trying to create unique named pipe `%s'\n",
2392 h = CreateNamedPipe (*fn,
2393 openMode | FILE_FLAG_OVERLAPPED |
2394 FILE_FLAG_FIRST_PIPE_INSTANCE,
2395 PIPE_TYPE_BYTE | PIPE_READMODE_BYTE, 2, 1, 1, 0,
2398 error_code = GetLastError ();
2401 /* don't re-set name to NULL yet */
2402 if (h == INVALID_HANDLE_VALUE)
2404 SetErrnoFromWinError (error_code);
2406 LOG (GNUNET_ERROR_TYPE_DEBUG,
2407 "Pipe creation have failed because of %d, errno is %d\n", error_code,
2413 LOG (GNUNET_ERROR_TYPE_DEBUG,
2414 "Pipe was to be unique, considering re-creation\n");
2418 if (error_code != ERROR_ACCESS_DENIED && error_code != ERROR_PIPE_BUSY)
2423 LOG (GNUNET_ERROR_TYPE_DEBUG,
2424 "Pipe name was not unique, trying again\n");
2434 ret = GNUNET_malloc (sizeof (*ret));
2436 ret->type = GNUNET_PIPE;
2438 ret->oOverlapRead = GNUNET_malloc (sizeof (OVERLAPPED));
2439 ret->oOverlapWrite = GNUNET_malloc (sizeof (OVERLAPPED));
2441 ret->oOverlapRead->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2442 ret->oOverlapWrite->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2448 char dir[] = "/tmp/gnunet-pipe-XXXXXX";
2450 if (mkdtemp (dir) == NULL)
2452 LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR, "mkdtemp");
2455 GNUNET_asprintf (fn, "%s/child-control", dir);
2458 if (mkfifo (*fn, translate_unix_perms (perm)) == -1)
2460 if ((errno != EEXIST) || (0 != (flags & GNUNET_DISK_OPEN_FAILIFEXISTS)))
2464 flags = flags & (~GNUNET_DISK_OPEN_FAILIFEXISTS);
2465 return GNUNET_DISK_file_open (*fn, flags, perm);
2471 * Opens already existing named pipe/FIFO
2473 * @param fn name of an existing named pipe
2474 * @param flags open flags
2475 * @param perm access permissions
2476 * @return pipe handle on success, NULL on error
2478 struct GNUNET_DISK_FileHandle *
2479 GNUNET_DISK_npipe_open (const char *fn, enum GNUNET_DISK_OpenFlags flags,
2480 enum GNUNET_DISK_AccessPermissions perm)
2483 struct GNUNET_DISK_FileHandle *ret;
2488 if (flags & GNUNET_DISK_OPEN_READWRITE)
2489 openMode = GENERIC_WRITE | GENERIC_READ;
2490 else if (flags & GNUNET_DISK_OPEN_READ)
2491 openMode = GENERIC_READ;
2492 else if (flags & GNUNET_DISK_OPEN_WRITE)
2493 openMode = GENERIC_WRITE;
2495 h = CreateFile (fn, openMode, 0, NULL, OPEN_EXISTING,
2496 FILE_FLAG_OVERLAPPED | FILE_READ_ATTRIBUTES, NULL);
2497 if (h == INVALID_HANDLE_VALUE)
2499 SetErrnoFromWinError (GetLastError ());
2503 ret = GNUNET_malloc (sizeof (*ret));
2505 ret->type = GNUNET_PIPE;
2506 ret->oOverlapRead = GNUNET_malloc (sizeof (OVERLAPPED));
2507 ret->oOverlapWrite = GNUNET_malloc (sizeof (OVERLAPPED));
2508 ret->oOverlapRead->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2509 ret->oOverlapWrite->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2513 flags = flags & (~GNUNET_DISK_OPEN_FAILIFEXISTS);
2514 return GNUNET_DISK_file_open (fn, flags, perm);
2519 * Closes a named pipe/FIFO
2520 * @param pipe named pipe
2521 * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
2524 GNUNET_DISK_npipe_close (struct GNUNET_DISK_FileHandle *pipe)
2527 return close (pipe->fd) == 0 ? GNUNET_OK : GNUNET_SYSERR;
2531 ret = CloseHandle (pipe->h);
2534 SetErrnoFromWinError (GetLastError ());
2535 return GNUNET_SYSERR;
2544 * Get the handle to a particular pipe end
2547 * @param n end to access
2548 * @return handle for the respective end
2550 const struct GNUNET_DISK_FileHandle *
2551 GNUNET_DISK_pipe_handle (const struct GNUNET_DISK_PipeHandle *p,
2552 enum GNUNET_DISK_PipeEnd n)
2556 case GNUNET_DISK_PIPE_END_READ:
2557 case GNUNET_DISK_PIPE_END_WRITE:
2567 * Retrieve OS file handle
2569 * @param fh GNUnet file descriptor
2570 * @param dst destination buffer
2571 * @param dst_len length of dst
2572 * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
2575 GNUNET_DISK_internal_file_handle_ (const struct GNUNET_DISK_FileHandle *fh,
2576 void *dst, size_t dst_len)
2579 if (dst_len < sizeof (HANDLE))
2580 return GNUNET_SYSERR;
2581 *((HANDLE *) dst) = fh->h;
2583 if (dst_len < sizeof (int))
2584 return GNUNET_SYSERR;
2585 *((int *) dst) = fh->fd;