LRN: enable more fine-grained control over blocking/non-blocking pipe operation
[oweals/gnunet.git] / src / util / disk.c
1 /*
2      This file is part of GNUnet.
3      (C) 2001, 2002, 2005, 2006, 2009 Christian Grothoff (and other contributing authors)
4
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.
9
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.
14
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.
19 */
20
21 /**
22  * @file util/disk.c
23  * @brief disk IO convenience methods
24  * @author Christian Grothoff
25  * @author Nils Durner
26  */
27
28 #include "platform.h"
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"
35 #include "disk.h"
36
37 #define LOG(kind,...) GNUNET_log_from (kind, "util", __VA_ARGS__)
38
39 #define LOG_STRERROR(kind,syscall) GNUNET_log_from_strerror (kind, "util", syscall)
40
41 #define LOG_STRERROR_FILE(kind,syscall,filename) GNUNET_log_from_strerror_file (kind, "util", syscall, filename)
42
43 #define DEBUG_NPIPE GNUNET_EXTRA_LOGGING
44
45 #define DEBUG_PIPE GNUNET_EXTRA_LOGGING
46
47 /**
48  * Block size for IO for copying files.
49  */
50 #define COPY_BLK_SIZE 65536
51
52
53
54 #if defined(LINUX) || defined(CYGWIN)
55 #include <sys/vfs.h>
56 #else
57 #if defined(SOMEBSD) || defined(DARWIN)
58 #include <sys/param.h>
59 #include <sys/mount.h>
60 #else
61 #ifdef SOLARIS
62 #include <sys/types.h>
63 #include <sys/statvfs.h>
64 #else
65 #ifdef MINGW
66 #ifndef PIPE_BUF
67 #define PIPE_BUF        512
68 ULONG PipeSerialNumber;
69 #endif
70 #define         _IFMT           0170000 /* type of file */
71 #define         _IFLNK          0120000 /* symbolic link */
72 #define  S_ISLNK(m)     (((m)&_IFMT) == _IFLNK)
73 #else
74 #error PORT-ME: need to port statfs (how much space is left on the drive?)
75 #endif
76 #endif
77 #endif
78 #endif
79
80 #if !defined(SOMEBSD) && !defined(DARWIN) && !defined(WINDOWS)
81 #include <wordexp.h>
82 #endif
83 #if LINUX
84 #include <sys/statvfs.h>
85 #endif
86
87
88 /**
89  * Handle used to manage a pipe.
90  */
91 struct GNUNET_DISK_PipeHandle
92 {
93   /**
94    * File descriptors for the pipe.
95    */
96   struct GNUNET_DISK_FileHandle *fd[2];
97 };
98
99
100 /**
101  * Closure for the recursion to determine the file size
102  * of a directory.
103  */
104 struct GetFileSizeData
105 {
106   /**
107    * Set to the total file size.
108    */
109   uint64_t total;
110
111   /**
112    * GNUNET_YES if symbolic links should be included.
113    */
114   int include_sym_links;
115 };
116
117
118 int
119 translate_unix_perms (enum GNUNET_DISK_AccessPermissions perm)
120 {
121   int mode;
122
123   mode = 0;
124   if (perm & GNUNET_DISK_PERM_USER_READ)
125     mode |= S_IRUSR;
126   if (perm & GNUNET_DISK_PERM_USER_WRITE)
127     mode |= S_IWUSR;
128   if (perm & GNUNET_DISK_PERM_USER_EXEC)
129     mode |= S_IXUSR;
130   if (perm & GNUNET_DISK_PERM_GROUP_READ)
131     mode |= S_IRGRP;
132   if (perm & GNUNET_DISK_PERM_GROUP_WRITE)
133     mode |= S_IWGRP;
134   if (perm & GNUNET_DISK_PERM_GROUP_EXEC)
135     mode |= S_IXGRP;
136   if (perm & GNUNET_DISK_PERM_OTHER_READ)
137     mode |= S_IROTH;
138   if (perm & GNUNET_DISK_PERM_OTHER_WRITE)
139     mode |= S_IWOTH;
140   if (perm & GNUNET_DISK_PERM_OTHER_EXEC)
141     mode |= S_IXOTH;
142
143   return mode;
144 }
145
146
147 /**
148  * Iterate over all files in the given directory and
149  * accumulate their size.
150  *
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
154  */
155 static int
156 getSizeRec (void *cls, const char *fn)
157 {
158   struct GetFileSizeData *gfsd = cls;
159
160 #ifdef HAVE_STAT64
161   struct stat64 buf;
162 #else
163   struct stat buf;
164 #endif
165
166 #ifdef HAVE_STAT64
167   if (0 != STAT64 (fn, &buf))
168   {
169     LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "stat64", fn);
170     return GNUNET_SYSERR;
171   }
172 #else
173   if (0 != STAT (fn, &buf))
174   {
175     LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "stat", fn);
176     return GNUNET_SYSERR;
177   }
178 #endif
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)))
183   {
184     if (GNUNET_SYSERR == GNUNET_DISK_directory_scan (fn, &getSizeRec, gfsd))
185       return GNUNET_SYSERR;
186   }
187   return GNUNET_OK;
188 }
189
190
191 /**
192  * Checks whether a handle is invalid
193  *
194  * @param h handle to check
195  * @return GNUNET_YES if invalid, GNUNET_NO if valid
196  */
197 int
198 GNUNET_DISK_handle_invalid (const struct GNUNET_DISK_FileHandle *h)
199 {
200 #ifdef MINGW
201   return ((!h) || (h->h == INVALID_HANDLE_VALUE)) ? GNUNET_YES : GNUNET_NO;
202 #else
203   return ((!h) || (h->fd == -1)) ? GNUNET_YES : GNUNET_NO;
204 #endif
205 }
206
207 /**
208  * Get the size of an open file.
209  *
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
213  */
214 int
215 GNUNET_DISK_file_handle_size (struct GNUNET_DISK_FileHandle *fh,
216                               OFF_T *size)
217 {
218 #if WINDOWS
219   BOOL b;
220   LARGE_INTEGER li;
221   b = GetFileSizeEx (fh->h, &li);
222   if (!b)
223   {
224     SetErrnoFromWinError (GetLastError ());
225     return GNUNET_SYSERR;
226   }
227   *size = (OFF_T) li.QuadPart;
228 #else
229   struct stat sbuf;
230
231   if (0 != FSTAT (fh->fd, &sbuf))
232     return GNUNET_SYSERR;
233   *size = sbuf.st_size;
234 #endif
235   return GNUNET_OK;
236 }
237
238
239 /**
240  * Move the read/write pointer in a file
241  *
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
246  */
247 OFF_T
248 GNUNET_DISK_file_seek (const struct GNUNET_DISK_FileHandle * h, OFF_T offset,
249                        enum GNUNET_DISK_Seek whence)
250 {
251   if (h == NULL)
252   {
253     errno = EINVAL;
254     return GNUNET_SYSERR;
255   }
256
257 #ifdef MINGW
258   LARGE_INTEGER li, new_pos;
259   BOOL b;
260
261   static DWORD t[] = {[GNUNET_DISK_SEEK_SET] = FILE_BEGIN,
262     [GNUNET_DISK_SEEK_CUR] = FILE_CURRENT,[GNUNET_DISK_SEEK_END] = FILE_END
263   };
264   li.QuadPart = offset;
265
266   b = SetFilePointerEx (h->h, li, &new_pos, t[whence]);
267   if (b == 0)
268   {
269     SetErrnoFromWinError (GetLastError ());
270     return GNUNET_SYSERR;
271   }
272   return (OFF_T) new_pos.QuadPart;
273 #else
274   static int t[] = {[GNUNET_DISK_SEEK_SET] = SEEK_SET,
275     [GNUNET_DISK_SEEK_CUR] = SEEK_CUR,[GNUNET_DISK_SEEK_END] = SEEK_END
276   };
277
278   return lseek (h->fd, offset, t[whence]);
279 #endif
280 }
281
282
283 /**
284  * Get the size of the file (or directory) of the given file (in
285  * bytes).
286  *
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
292  *        included?
293  * @return GNUNET_SYSERR on error, GNUNET_OK on success
294  */
295 int
296 GNUNET_DISK_file_size (const char *filename, uint64_t * size,
297                        int includeSymLinks)
298 {
299   struct GetFileSizeData gfsd;
300   int ret;
301
302   GNUNET_assert (size != NULL);
303   gfsd.total = 0;
304   gfsd.include_sym_links = includeSymLinks;
305   ret = getSizeRec (&gfsd, filename);
306   *size = gfsd.total;
307   return ret;
308 }
309
310
311 /**
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.
320  *
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
325  */
326 int
327 GNUNET_DISK_file_get_identifiers (const char *filename, uint64_t * dev,
328                                   uint64_t * ino)
329 {
330 #if LINUX
331   struct stat sbuf;
332   struct statvfs fbuf;
333
334   if ((0 == stat (filename, &sbuf)) && (0 == statvfs (filename, &fbuf)))
335   {
336     *dev = (uint64_t) fbuf.f_fsid;
337     *ino = (uint64_t) sbuf.st_ino;
338     return GNUNET_OK;
339   }
340 #elif SOMEBSD
341   struct stat sbuf;
342   struct statfs fbuf;
343
344   if ((0 == stat (filename, &sbuf)) && (0 == statfs (filename, &fbuf)))
345   {
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;
349     return GNUNET_OK;
350   }
351 #elif WINDOWS
352   // FIXME NILS: test this
353   struct GNUNET_DISK_FileHandle *fh;
354   BY_HANDLE_FILE_INFORMATION info;
355   int succ;
356
357   fh = GNUNET_DISK_file_open (filename, GNUNET_DISK_OPEN_READ, 0);
358   if (fh == NULL)
359     return GNUNET_SYSERR;
360   succ = GetFileInformationByHandle (fh->h, &info);
361   GNUNET_DISK_file_close (fh);
362   if (succ)
363   {
364     *dev = info.dwVolumeSerialNumber;
365     *ino = ((((uint64_t) info.nFileIndexHigh) << (sizeof (DWORD) * 8)) | info.nFileIndexLow);
366     return GNUNET_OK;
367   }
368   else
369     return GNUNET_SYSERR;
370
371 #endif
372   return GNUNET_SYSERR;
373 }
374
375
376 /**
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
380  * filename.
381  *
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
386  */
387 char *
388 GNUNET_DISK_mktemp (const char *t)
389 {
390   const char *tmpdir;
391   int fd;
392   char *tmpl;
393   char *fn;
394
395   if ((t[0] != '/') && (t[0] != '\\')
396 #if WINDOWS
397       && !(isalpha ((int) t[0]) && (t[0] != '\0') && (t[1] == ':'))
398 #endif
399       )
400   {
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");
405   }
406   else
407   {
408     GNUNET_asprintf (&tmpl, "%s%s", t, "XXXXXX");
409   }
410 #ifdef MINGW
411   fn = (char *) GNUNET_malloc (MAX_PATH + 1);
412   if (ERROR_SUCCESS != plibc_conv_to_win_path (tmpl, fn))
413   {
414     GNUNET_free (fn);
415     GNUNET_free (tmpl);
416     return NULL;
417   }
418   GNUNET_free (tmpl);
419 #else
420   fn = tmpl;
421 #endif
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.
424    */
425   fd = mkstemp (fn);
426   if (fd == -1)
427   {
428     LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_ERROR, "mkstemp", fn);
429     GNUNET_free (fn);
430     return NULL;
431   }
432   if (0 != CLOSE (fd))
433     LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "close", fn);
434   return fn;
435 }
436
437
438 /**
439  * Get the number of blocks that are left on the partition that
440  * contains the given file (for normal users).
441  *
442  * @param part a file on the partition to check
443  * @return -1 on errors, otherwise the number of free blocks
444  */
445 long
446 GNUNET_DISK_get_blocks_available (const char *part)
447 {
448 #ifdef SOLARIS
449   struct statvfs buf;
450
451   if (0 != statvfs (part, &buf))
452   {
453     LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "statfs", part);
454     return -1;
455   }
456   return buf.f_bavail;
457 #elif MINGW
458   DWORD dwDummy;
459   DWORD dwBlocks;
460   wchar_t szDrive[4];
461   wchar_t wpath[MAX_PATH + 1];
462   char *path;
463
464   path = GNUNET_STRINGS_filename_expand (part);
465   if (path == NULL)
466     return -1;
467   /* "part" was in UTF-8, and so is "path" */
468   if (ERROR_SUCCESS != plibc_conv_to_win_pathwconv(path, wpath))
469   {
470     GNUNET_free (path);
471     return -1;
472   }
473   GNUNET_free (path);
474   wcsncpy (szDrive, wpath, 3);
475   GNUNET_free (wpath);
476   szDrive[3] = 0;
477   if (!GetDiskFreeSpaceW (szDrive, &dwDummy, &dwDummy, &dwBlocks, &dwDummy))
478   {
479     LOG (GNUNET_ERROR_TYPE_WARNING, _("`%s' failed for drive `%S': %u\n"),
480          "GetDiskFreeSpace", szDrive, GetLastError ());
481
482     return -1;
483   }
484   return dwBlocks;
485 #else
486   struct statfs s;
487
488   if (0 != statfs (part, &s))
489   {
490     LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "statfs", part);
491     return -1;
492   }
493   return s.f_bavail;
494 #endif
495 }
496
497
498 /**
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).
503  *
504  * @param fil filename to test
505  * @return GNUNET_YES if yes, GNUNET_NO if not, GNUNET_SYSERR if it
506  *   does not exist
507  */
508 int
509 GNUNET_DISK_directory_test (const char *fil)
510 {
511   struct stat filestat;
512   int ret;
513
514   ret = STAT (fil, &filestat);
515   if (ret != 0)
516   {
517     if (errno != ENOENT)
518     {
519       LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "stat", fil);
520       return GNUNET_SYSERR;
521     }
522     return GNUNET_NO;
523   }
524   if (!S_ISDIR (filestat.st_mode))
525     return GNUNET_NO;
526   if (ACCESS (fil, R_OK | X_OK) < 0)
527   {
528     LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "access", fil);
529     return GNUNET_SYSERR;
530   }
531   return GNUNET_YES;
532 }
533
534 /**
535  * Check that fil corresponds to a filename
536  * (of a file that exists and that is not a directory).
537  *
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).
541  */
542 int
543 GNUNET_DISK_file_test (const char *fil)
544 {
545   struct stat filestat;
546   int ret;
547   char *rdir;
548
549   rdir = GNUNET_STRINGS_filename_expand (fil);
550   if (rdir == NULL)
551     return GNUNET_SYSERR;
552
553   ret = STAT (rdir, &filestat);
554   if (ret != 0)
555   {
556     if (errno != ENOENT)
557     {
558       LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "stat", rdir);
559       GNUNET_free (rdir);
560       return GNUNET_SYSERR;
561     }
562     GNUNET_free (rdir);
563     return GNUNET_NO;
564   }
565   if (!S_ISREG (filestat.st_mode))
566   {
567     GNUNET_free (rdir);
568     return GNUNET_NO;
569   }
570   if (ACCESS (rdir, R_OK) < 0)
571   {
572     LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "access", rdir);
573     GNUNET_free (rdir);
574     return GNUNET_SYSERR;
575   }
576   GNUNET_free (rdir);
577   return GNUNET_YES;
578 }
579
580
581 /**
582  * Implementation of "mkdir -p"
583  * @param dir the directory to create
584  * @returns GNUNET_OK on success, GNUNET_SYSERR on failure
585  */
586 int
587 GNUNET_DISK_directory_create (const char *dir)
588 {
589   char *rdir;
590   int len;
591   int pos;
592   int ret = GNUNET_OK;
593
594   rdir = GNUNET_STRINGS_filename_expand (dir);
595   if (rdir == NULL)
596     return GNUNET_SYSERR;
597
598   len = strlen (rdir);
599 #ifndef MINGW
600   pos = 1;                      /* skip heading '/' */
601 #else
602   /* Local or Network path? */
603   if (strncmp (rdir, "\\\\", 2) == 0)
604   {
605     pos = 2;
606     while (rdir[pos])
607     {
608       if (rdir[pos] == '\\')
609       {
610         pos++;
611         break;
612       }
613       pos++;
614     }
615   }
616   else
617   {
618     pos = 3;                    /* strlen("C:\\") */
619   }
620 #endif
621   while (pos <= len)
622   {
623     if ((rdir[pos] == DIR_SEPARATOR) || (pos == len))
624     {
625       rdir[pos] = '\0';
626       ret = GNUNET_DISK_directory_test (rdir);
627       if (ret == GNUNET_SYSERR)
628       {
629         GNUNET_free (rdir);
630         return GNUNET_SYSERR;
631       }
632       if (ret == GNUNET_NO)
633       {
634 #ifndef MINGW
635         ret = mkdir (rdir, S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH);        /* 755 */
636 #else
637         wchar_t wrdir[MAX_PATH + 1];
638         if (ERROR_SUCCESS == plibc_conv_to_win_pathwconv(rdir, wrdir))
639           ret = !CreateDirectoryW (wrdir, NULL);
640         else
641           ret = 1;
642 #endif
643         if ((ret != 0) && (errno != EEXIST))
644         {
645           LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_ERROR, "mkdir", rdir);
646           GNUNET_free (rdir);
647           return GNUNET_SYSERR;
648         }
649       }
650       rdir[pos] = DIR_SEPARATOR;
651     }
652     pos++;
653   }
654   GNUNET_free (rdir);
655   return GNUNET_OK;
656 }
657
658
659 /**
660  * Create the directory structure for storing
661  * a file.
662  *
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
668  */
669 int
670 GNUNET_DISK_directory_create_for_file (const char *filename)
671 {
672   char *rdir;
673   int len;
674   int ret;
675
676   rdir = GNUNET_STRINGS_filename_expand (filename);
677   if (rdir == NULL)
678     return GNUNET_SYSERR;
679   len = strlen (rdir);
680   while ((len > 0) && (rdir[len] != DIR_SEPARATOR))
681     len--;
682   rdir[len] = '\0';
683   ret = GNUNET_DISK_directory_create (rdir);
684   if ((ret == GNUNET_OK) && (0 != ACCESS (rdir, W_OK)))
685     ret = GNUNET_NO;
686   GNUNET_free (rdir);
687   return ret;
688 }
689
690
691 /**
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
697  */
698 ssize_t
699 GNUNET_DISK_file_read (const struct GNUNET_DISK_FileHandle * h, void *result,
700                        size_t len)
701 {
702   if (h == NULL)
703   {
704     errno = EINVAL;
705     return GNUNET_SYSERR;
706   }
707
708 #ifdef MINGW
709   DWORD bytesRead;
710
711   if (h->type != GNUNET_PIPE)
712   {
713     if (!ReadFile (h->h, result, len, &bytesRead, NULL))
714     {
715       SetErrnoFromWinError (GetLastError ());
716       return GNUNET_SYSERR;
717     }
718   }
719   else
720   {
721 #if DEBUG_PIPE
722     LOG (GNUNET_ERROR_TYPE_DEBUG, "It is a pipe trying to read\n");
723 #endif
724     if (!ReadFile (h->h, result, len, &bytesRead, h->oOverlapRead))
725     {
726       if (GetLastError () != ERROR_IO_PENDING)
727       {
728 #if DEBUG_PIPE
729         LOG (GNUNET_ERROR_TYPE_DEBUG, "Error reading from pipe: %u\n", GetLastError ());
730 #endif
731         SetErrnoFromWinError (GetLastError ());
732         return GNUNET_SYSERR;
733       }
734 #if DEBUG_PIPE
735       LOG (GNUNET_ERROR_TYPE_DEBUG, "Will get overlapped result\n");
736 #endif
737       GetOverlappedResult (h->h, h->oOverlapRead, &bytesRead, TRUE);
738     }
739 #if DEBUG_PIPE
740     LOG (GNUNET_ERROR_TYPE_DEBUG, "Read %u bytes\n", bytesRead);
741 #endif
742   }
743   return bytesRead;
744 #else
745   return read (h->fd, result, len);
746 #endif
747 }
748
749 /**
750  * Read the contents of a binary file into a buffer.
751  * Guarantees not to block (returns GNUNET_SYSERR and sets errno to EAGAIN
752  * when no data can be read).
753  *
754  * @param h handle to an open file
755  * @param result the buffer to write the result to
756  * @param len the maximum number of bytes to read
757  * @return the number of bytes read on success, GNUNET_SYSERR on failure
758  */
759 ssize_t
760 GNUNET_DISK_file_read_non_blocking (const struct GNUNET_DISK_FileHandle * h,
761     void *result, size_t len)
762 {
763   if (h == NULL)
764   {
765     errno = EINVAL;
766     return GNUNET_SYSERR;
767   }
768
769 #ifdef MINGW
770   DWORD bytesRead;
771
772   if (h->type != GNUNET_PIPE)
773   {
774     if (!ReadFile (h->h, result, len, &bytesRead, NULL))
775     {
776       SetErrnoFromWinError (GetLastError ());
777       return GNUNET_SYSERR;
778     }
779   }
780   else
781   {
782 #if DEBUG_PIPE
783     LOG (GNUNET_ERROR_TYPE_DEBUG, "It is a pipe, trying to read\n");
784 #endif
785     if (!ReadFile (h->h, result, len, &bytesRead, h->oOverlapRead))
786     {
787       if (GetLastError () != ERROR_IO_PENDING)
788       {
789 #if DEBUG_PIPE
790         LOG (GNUNET_ERROR_TYPE_DEBUG, "Error reading from pipe: %u\n", GetLastError ());
791 #endif
792         SetErrnoFromWinError (GetLastError ());
793         return GNUNET_SYSERR;
794       }
795       else
796       {
797 #if DEBUG_PIPE
798         LOG (GNUNET_ERROR_TYPE_DEBUG,
799             "ReadFile() queued a read, cancelling\n");
800 #endif
801         CancelIo (h->h);
802         errno = EAGAIN;
803         return GNUNET_SYSERR;
804       }
805     }
806 #if DEBUG_PIPE
807     LOG (GNUNET_ERROR_TYPE_DEBUG, "Read %u bytes\n", bytesRead);
808 #endif
809   }
810   return bytesRead;
811 #else
812   /* FIXME: set to non-blocking (fcntl?), read, then set back? */
813   return read (h->fd, result, len);
814 #endif
815 }
816
817
818 /**
819  * Read the contents of a binary file into a buffer.
820  *
821  * @param fn file name
822  * @param result the buffer to write the result to
823  * @param len the maximum number of bytes to read
824  * @return number of bytes read, GNUNET_SYSERR on failure
825  */
826 ssize_t
827 GNUNET_DISK_fn_read (const char *fn, void *result, size_t len)
828 {
829   struct GNUNET_DISK_FileHandle *fh;
830   ssize_t ret;
831
832   fh = GNUNET_DISK_file_open (fn, GNUNET_DISK_OPEN_READ, GNUNET_DISK_PERM_NONE);
833   if (!fh)
834     return GNUNET_SYSERR;
835   ret = GNUNET_DISK_file_read (fh, result, len);
836   GNUNET_assert (GNUNET_OK == GNUNET_DISK_file_close (fh));
837
838   return ret;
839 }
840
841
842 /**
843  * Write a buffer to a file.
844  * @param h handle to open file
845  * @param buffer the data to write
846  * @param n number of bytes to write
847  * @return number of bytes written on success, GNUNET_SYSERR on error
848  */
849 ssize_t
850 GNUNET_DISK_file_write (const struct GNUNET_DISK_FileHandle * h,
851                         const void *buffer, size_t n)
852 {
853   if (h == NULL)
854   {
855     errno = EINVAL;
856     return GNUNET_SYSERR;
857   }
858
859 #ifdef MINGW
860   DWORD bytesWritten;
861
862   if (h->type != GNUNET_PIPE)
863   {
864     if (!WriteFile (h->h, buffer, n, &bytesWritten, NULL))
865     {
866       SetErrnoFromWinError (GetLastError ());
867       return GNUNET_SYSERR;
868     }
869   }
870   else
871   {
872 #if DEBUG_PIPE
873     LOG (GNUNET_ERROR_TYPE_DEBUG, "It is a pipe trying to write %u bytes\n", n);
874 #endif
875     if (!WriteFile (h->h, buffer, n, &bytesWritten, h->oOverlapWrite))
876     {
877       if (GetLastError () != ERROR_IO_PENDING)
878       {
879         SetErrnoFromWinError (GetLastError ());
880 #if DEBUG_PIPE
881         LOG (GNUNET_ERROR_TYPE_DEBUG, "Error writing to pipe: %u\n",
882             GetLastError ());
883 #endif
884         return GNUNET_SYSERR;
885       }
886 #if DEBUG_PIPE
887       LOG (GNUNET_ERROR_TYPE_DEBUG, "Will get overlapped result\n");
888 #endif
889       if (!GetOverlappedResult (h->h, h->oOverlapWrite, &bytesWritten, TRUE))
890       {
891         SetErrnoFromWinError (GetLastError ());
892 #if DEBUG_PIPE
893         LOG (GNUNET_ERROR_TYPE_DEBUG,
894             "Error getting overlapped result while writing to pipe: %u\n",
895             GetLastError ());
896 #endif
897         return GNUNET_SYSERR;
898       }
899     }
900     else
901     {
902       DWORD ovr;
903       if (!GetOverlappedResult (h->h, h->oOverlapWrite, &ovr, TRUE))
904       {
905 #if DEBUG_PIPE
906         LOG (GNUNET_ERROR_TYPE_DEBUG,
907             "Error getting control overlapped result while writing to pipe: %u\n",
908             GetLastError ());
909 #endif
910       }
911       else
912       {
913 #if DEBUG_PIPE
914         LOG (GNUNET_ERROR_TYPE_DEBUG,
915             "Wrote %u bytes (ovr says %u), picking the greatest\n",
916             bytesWritten, ovr);
917 #endif
918       }
919     }
920     if (bytesWritten == 0)
921     {
922       if (n > 0)
923       {
924 #if DEBUG_PIPE
925         LOG (GNUNET_ERROR_TYPE_DEBUG, "Wrote %u bytes, returning -1 with EAGAIN\n", bytesWritten);
926 #endif
927         errno = EAGAIN;
928         return GNUNET_SYSERR;
929       }
930     }
931 #if DEBUG_PIPE
932     LOG (GNUNET_ERROR_TYPE_DEBUG, "Wrote %u bytes\n", bytesWritten);
933 #endif
934   }
935   return bytesWritten;
936 #else
937   return write (h->fd, buffer, n);
938 #endif
939 }
940
941 /**
942  * Write a buffer to a file, blocking, if necessary.
943  * @param h handle to open file
944  * @param buffer the data to write
945  * @param n number of bytes to write
946  * @return number of bytes written on success, GNUNET_SYSERR on error
947  */
948 ssize_t
949 GNUNET_DISK_file_write_blocking (const struct GNUNET_DISK_FileHandle * h,
950     const void *buffer, size_t n)
951 {
952   if (h == NULL)
953   {
954     errno = EINVAL;
955     return GNUNET_SYSERR;
956   }
957
958 #ifdef MINGW
959   DWORD bytesWritten;
960   /* We do a non-overlapped write, which is as blocking as it gets */
961 #if DEBUG_PIPE
962   LOG (GNUNET_ERROR_TYPE_DEBUG, "Writing %u bytes\n", n);
963 #endif
964   if (!WriteFile (h->h, buffer, n, &bytesWritten, NULL))
965   {
966     SetErrnoFromWinError (GetLastError ());
967 #if DEBUG_PIPE
968     LOG (GNUNET_ERROR_TYPE_DEBUG, "Error writing to pipe: %u\n",
969         GetLastError ());
970 #endif
971     return GNUNET_SYSERR;
972   }
973   if (bytesWritten == 0 && n > 0)
974   {
975 #if DEBUG_PIPE
976     LOG (GNUNET_ERROR_TYPE_DEBUG, "Waiting for pipe to clean\n");
977 #endif
978     WaitForSingleObject (h->h, INFINITE);
979     if (!WriteFile (h->h, buffer, n, &bytesWritten, NULL))
980     {
981       SetErrnoFromWinError (GetLastError ());
982 #if DEBUG_PIPE
983       LOG (GNUNET_ERROR_TYPE_DEBUG, "Error writing to pipe: %u\n",
984           GetLastError ());
985 #endif
986       return GNUNET_SYSERR;
987     }
988   }
989 #if DEBUG_PIPE
990   LOG (GNUNET_ERROR_TYPE_DEBUG, "Wrote %u bytes\n", bytesWritten);
991 #endif
992   return bytesWritten;
993 #else
994   /* FIXME: switch to blocking mode (fcntl?), write, then switch back? */
995   return write (h->fd, buffer, n);
996 #endif
997 }
998
999 /**
1000  * Write a buffer to a file.  If the file is longer than the
1001  * number of bytes that will be written, it will be truncated.
1002  *
1003  * @param fn file name
1004  * @param buffer the data to write
1005  * @param n number of bytes to write
1006  * @param mode file permissions
1007  * @return number of bytes written on success, GNUNET_SYSERR on error
1008  */
1009 ssize_t
1010 GNUNET_DISK_fn_write (const char *fn, const void *buffer, size_t n,
1011                       enum GNUNET_DISK_AccessPermissions mode)
1012 {
1013   struct GNUNET_DISK_FileHandle *fh;
1014   ssize_t ret;
1015
1016   fh = GNUNET_DISK_file_open (fn,
1017                               GNUNET_DISK_OPEN_WRITE | GNUNET_DISK_OPEN_TRUNCATE
1018                               | GNUNET_DISK_OPEN_CREATE, mode);
1019   if (!fh)
1020     return GNUNET_SYSERR;
1021   ret = GNUNET_DISK_file_write (fh, buffer, n);
1022   GNUNET_assert (GNUNET_OK == GNUNET_DISK_file_close (fh));
1023   return ret;
1024 }
1025
1026
1027 /**
1028  * Scan a directory for files.
1029  *
1030  * @param dirName the name of the directory
1031  * @param callback the method to call for each file,
1032  *        can be NULL, in that case, we only count
1033  * @param callback_cls closure for callback
1034  * @return the number of files found, GNUNET_SYSERR on error or
1035  *         ieration aborted by callback returning GNUNET_SYSERR
1036  */
1037 int
1038 GNUNET_DISK_directory_scan (const char *dirName,
1039                             GNUNET_FileNameCallback callback,
1040                             void *callback_cls)
1041 {
1042   DIR *dinfo;
1043   struct dirent *finfo;
1044   struct stat istat;
1045   int count = 0;
1046   char *name;
1047   char *dname;
1048   unsigned int name_len;
1049   unsigned int n_size;
1050
1051   GNUNET_assert (dirName != NULL);
1052   dname = GNUNET_STRINGS_filename_expand (dirName);
1053   if (dname == NULL)
1054     return GNUNET_SYSERR;
1055   while ((strlen (dname) > 0) && (dname[strlen (dname) - 1] == DIR_SEPARATOR))
1056     dname[strlen (dname) - 1] = '\0';
1057   if (0 != STAT (dname, &istat))
1058   {
1059     LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "stat", dname);
1060     GNUNET_free (dname);
1061     return GNUNET_SYSERR;
1062   }
1063   if (!S_ISDIR (istat.st_mode))
1064   {
1065     LOG (GNUNET_ERROR_TYPE_WARNING, _("Expected `%s' to be a directory!\n"),
1066          dirName);
1067     GNUNET_free (dname);
1068     return GNUNET_SYSERR;
1069   }
1070   errno = 0;
1071   dinfo = OPENDIR (dname);
1072   if ((errno == EACCES) || (dinfo == NULL))
1073   {
1074     LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "opendir", dname);
1075     if (dinfo != NULL)
1076       CLOSEDIR (dinfo);
1077     GNUNET_free (dname);
1078     return GNUNET_SYSERR;
1079   }
1080   name_len = 256;
1081   n_size = strlen (dname) + name_len + 2;
1082   name = GNUNET_malloc (n_size);
1083   while ((finfo = READDIR (dinfo)) != NULL)
1084   {
1085     if ((0 == strcmp (finfo->d_name, ".")) ||
1086         (0 == strcmp (finfo->d_name, "..")))
1087       continue;
1088     if (callback != NULL)
1089     {
1090       if (name_len < strlen (finfo->d_name))
1091       {
1092         GNUNET_free (name);
1093         name_len = strlen (finfo->d_name);
1094         n_size = strlen (dname) + name_len + 2;
1095         name = GNUNET_malloc (n_size);
1096       }
1097       /* dname can end in "/" only if dname == "/";
1098        * if dname does not end in "/", we need to add
1099        * a "/" (otherwise, we must not!) */
1100       GNUNET_snprintf (name, n_size, "%s%s%s", dname,
1101                        (strcmp (dname, DIR_SEPARATOR_STR) ==
1102                         0) ? "" : DIR_SEPARATOR_STR, finfo->d_name);
1103       if (GNUNET_OK != callback (callback_cls, name))
1104       {
1105         CLOSEDIR (dinfo);
1106         GNUNET_free (name);
1107         GNUNET_free (dname);
1108         return GNUNET_SYSERR;
1109       }
1110     }
1111     count++;
1112   }
1113   CLOSEDIR (dinfo);
1114   GNUNET_free (name);
1115   GNUNET_free (dname);
1116   return count;
1117 }
1118
1119
1120 /**
1121  * Opaque handle used for iterating over a directory.
1122  */
1123 struct GNUNET_DISK_DirectoryIterator
1124 {
1125
1126   /**
1127    * Function to call on directory entries.
1128    */
1129   GNUNET_DISK_DirectoryIteratorCallback callback;
1130
1131   /**
1132    * Closure for callback.
1133    */
1134   void *callback_cls;
1135
1136   /**
1137    * Reference to directory.
1138    */
1139   DIR *directory;
1140
1141   /**
1142    * Directory name.
1143    */
1144   char *dirname;
1145
1146   /**
1147    * Next filename to process.
1148    */
1149   char *next_name;
1150
1151   /**
1152    * Our priority.
1153    */
1154   enum GNUNET_SCHEDULER_Priority priority;
1155
1156 };
1157
1158
1159 /**
1160  * Task used by the directory iterator.
1161  */
1162 static void
1163 directory_iterator_task (void *cls,
1164                          const struct GNUNET_SCHEDULER_TaskContext *tc)
1165 {
1166   struct GNUNET_DISK_DirectoryIterator *iter = cls;
1167   char *name;
1168
1169   name = iter->next_name;
1170   GNUNET_assert (name != NULL);
1171   iter->next_name = NULL;
1172   iter->callback (iter->callback_cls, iter, name, iter->dirname);
1173   GNUNET_free (name);
1174 }
1175
1176
1177 /**
1178  * This function must be called during the DiskIteratorCallback
1179  * (exactly once) to schedule the task to process the next
1180  * filename in the directory (if there is one).
1181  *
1182  * @param iter opaque handle for the iterator
1183  * @param can set to GNUNET_YES to terminate the iteration early
1184  * @return GNUNET_YES if iteration will continue,
1185  *         GNUNET_NO if this was the last entry (and iteration is complete),
1186  *         GNUNET_SYSERR if abort was YES
1187  */
1188 int
1189 GNUNET_DISK_directory_iterator_next (struct GNUNET_DISK_DirectoryIterator *iter,
1190                                      int can)
1191 {
1192   struct dirent *finfo;
1193
1194   GNUNET_assert (iter->next_name == NULL);
1195   if (can == GNUNET_YES)
1196   {
1197     CLOSEDIR (iter->directory);
1198     GNUNET_free (iter->dirname);
1199     GNUNET_free (iter);
1200     return GNUNET_SYSERR;
1201   }
1202   while (NULL != (finfo = READDIR (iter->directory)))
1203   {
1204     if ((0 == strcmp (finfo->d_name, ".")) ||
1205         (0 == strcmp (finfo->d_name, "..")))
1206       continue;
1207     GNUNET_asprintf (&iter->next_name, "%s%s%s", iter->dirname,
1208                      DIR_SEPARATOR_STR, finfo->d_name);
1209     break;
1210   }
1211   if (finfo == NULL)
1212   {
1213     GNUNET_DISK_directory_iterator_next (iter, GNUNET_YES);
1214     return GNUNET_NO;
1215   }
1216   GNUNET_SCHEDULER_add_with_priority (iter->priority, &directory_iterator_task,
1217                                       iter);
1218   return GNUNET_YES;
1219 }
1220
1221
1222 /**
1223  * Scan a directory for files using the scheduler to run a task for
1224  * each entry.  The name of the directory must be expanded first (!).
1225  * If a scheduler does not need to be used, GNUNET_DISK_directory_scan
1226  * may provide a simpler API.
1227  *
1228  * @param prio priority to use
1229  * @param dirName the name of the directory
1230  * @param callback the method to call for each file
1231  * @param callback_cls closure for callback
1232  * @return GNUNET_YES if directory is not empty and @callback
1233  *         will be called later, GNUNET_NO otherwise, GNUNET_SYSERR on error.
1234  */
1235 int
1236 GNUNET_DISK_directory_iterator_start (enum GNUNET_SCHEDULER_Priority prio,
1237                                       const char *dirName,
1238                                       GNUNET_DISK_DirectoryIteratorCallback
1239                                       callback, void *callback_cls)
1240 {
1241   struct GNUNET_DISK_DirectoryIterator *di;
1242
1243   di = GNUNET_malloc (sizeof (struct GNUNET_DISK_DirectoryIterator));
1244   di->callback = callback;
1245   di->callback_cls = callback_cls;
1246   di->directory = OPENDIR (dirName);
1247   if (di->directory == NULL)
1248   {
1249     GNUNET_free (di);
1250     callback (callback_cls, NULL, NULL, NULL);
1251     return GNUNET_SYSERR;
1252   }
1253   di->dirname = GNUNET_strdup (dirName);
1254   di->priority = prio;
1255   return GNUNET_DISK_directory_iterator_next (di, GNUNET_NO);
1256 }
1257
1258
1259 /**
1260  * Function that removes the given directory by calling
1261  * "GNUNET_DISK_directory_remove".
1262  *
1263  * @param unused not used
1264  * @param fn directory to remove
1265  * @return GNUNET_OK
1266  */
1267 static int
1268 remove_helper (void *unused, const char *fn)
1269 {
1270   (void) GNUNET_DISK_directory_remove (fn);
1271   return GNUNET_OK;
1272 }
1273
1274
1275 /**
1276  * Remove all files in a directory (rm -rf). Call with
1277  * caution.
1278  *
1279  *
1280  * @param fileName the file to remove
1281  * @return GNUNET_OK on success, GNUNET_SYSERR on error
1282  */
1283 int
1284 GNUNET_DISK_directory_remove (const char *fileName)
1285 {
1286   struct stat istat;
1287
1288   if (0 != LSTAT (fileName, &istat))
1289     return GNUNET_NO;           /* file may not exist... */
1290   CHMOD (fileName, S_IWUSR | S_IRUSR | S_IXUSR);
1291   if (UNLINK (fileName) == 0)
1292     return GNUNET_OK;
1293   if ((errno != EISDIR) &&
1294       /* EISDIR is not sufficient in all cases, e.g.
1295        * sticky /tmp directory may result in EPERM on BSD.
1296        * So we also explicitly check "isDirectory" */
1297       (GNUNET_YES != GNUNET_DISK_directory_test (fileName)))
1298   {
1299     LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "rmdir", fileName);
1300     return GNUNET_SYSERR;
1301   }
1302   if (GNUNET_SYSERR ==
1303       GNUNET_DISK_directory_scan (fileName, &remove_helper, NULL))
1304     return GNUNET_SYSERR;
1305   if (0 != RMDIR (fileName))
1306   {
1307     LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "rmdir", fileName);
1308     return GNUNET_SYSERR;
1309   }
1310   return GNUNET_OK;
1311 }
1312
1313
1314 /**
1315  * Copy a file.
1316  *
1317  * @param src file to copy
1318  * @param dst destination file name
1319  * @return GNUNET_OK on success, GNUNET_SYSERR on error
1320  */
1321 int
1322 GNUNET_DISK_file_copy (const char *src, const char *dst)
1323 {
1324   char *buf;
1325   uint64_t pos;
1326   uint64_t size;
1327   size_t len;
1328   struct GNUNET_DISK_FileHandle *in;
1329   struct GNUNET_DISK_FileHandle *out;
1330
1331   if (GNUNET_OK != GNUNET_DISK_file_size (src, &size, GNUNET_YES))
1332     return GNUNET_SYSERR;
1333   pos = 0;
1334   in = GNUNET_DISK_file_open (src, GNUNET_DISK_OPEN_READ,
1335                               GNUNET_DISK_PERM_NONE);
1336   if (!in)
1337     return GNUNET_SYSERR;
1338   out =
1339       GNUNET_DISK_file_open (dst,
1340                              GNUNET_DISK_OPEN_WRITE | GNUNET_DISK_OPEN_CREATE |
1341                              GNUNET_DISK_OPEN_FAILIFEXISTS,
1342                              GNUNET_DISK_PERM_USER_READ |
1343                              GNUNET_DISK_PERM_USER_WRITE |
1344                              GNUNET_DISK_PERM_GROUP_READ |
1345                              GNUNET_DISK_PERM_GROUP_WRITE);
1346   if (!out)
1347   {
1348     GNUNET_DISK_file_close (in);
1349     return GNUNET_SYSERR;
1350   }
1351   buf = GNUNET_malloc (COPY_BLK_SIZE);
1352   while (pos < size)
1353   {
1354     len = COPY_BLK_SIZE;
1355     if (len > size - pos)
1356       len = size - pos;
1357     if (len != GNUNET_DISK_file_read (in, buf, len))
1358       goto FAIL;
1359     if (len != GNUNET_DISK_file_write (out, buf, len))
1360       goto FAIL;
1361     pos += len;
1362   }
1363   GNUNET_free (buf);
1364   GNUNET_DISK_file_close (in);
1365   GNUNET_DISK_file_close (out);
1366   return GNUNET_OK;
1367 FAIL:
1368   GNUNET_free (buf);
1369   GNUNET_DISK_file_close (in);
1370   GNUNET_DISK_file_close (out);
1371   return GNUNET_SYSERR;
1372 }
1373
1374
1375 /**
1376  * @brief Removes special characters as ':' from a filename.
1377  * @param fn the filename to canonicalize
1378  */
1379 void
1380 GNUNET_DISK_filename_canonicalize (char *fn)
1381 {
1382   char *idx;
1383   char c;
1384
1385   idx = fn;
1386   while (*idx)
1387   {
1388     c = *idx;
1389
1390     if (c == '/' || c == '\\' || c == ':' || c == '*' || c == '?' || c == '"' ||
1391         c == '<' || c == '>' || c == '|')
1392     {
1393       *idx = '_';
1394     }
1395
1396     idx++;
1397   }
1398 }
1399
1400
1401
1402 /**
1403  * @brief Change owner of a file
1404  *
1405  * @param filename name of file to change the owner of
1406  * @param user name of the new owner
1407  * @return GNUNET_OK on success, GNUNET_SYSERR on failure
1408  */
1409 int
1410 GNUNET_DISK_file_change_owner (const char *filename, const char *user)
1411 {
1412 #ifndef MINGW
1413   struct passwd *pws;
1414
1415   pws = getpwnam (user);
1416   if (pws == NULL)
1417   {
1418     LOG (GNUNET_ERROR_TYPE_ERROR,
1419          _("Cannot obtain information about user `%s': %s\n"), user,
1420          STRERROR (errno));
1421     return GNUNET_SYSERR;
1422   }
1423   if (0 != chown (filename, pws->pw_uid, pws->pw_gid))
1424     LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "chown", filename);
1425 #endif
1426   return GNUNET_OK;
1427 }
1428
1429
1430 /**
1431  * Lock a part of a file
1432  * @param fh file handle
1433  * @param lockStart absolute position from where to lock
1434  * @param lockEnd absolute position until where to lock
1435  * @param excl GNUNET_YES for an exclusive lock
1436  * @return GNUNET_OK on success, GNUNET_SYSERR on error
1437  */
1438 int
1439 GNUNET_DISK_file_lock (struct GNUNET_DISK_FileHandle *fh, OFF_T lockStart,
1440                        OFF_T lockEnd, int excl)
1441 {
1442   if (fh == NULL)
1443   {
1444     errno = EINVAL;
1445     return GNUNET_SYSERR;
1446   }
1447
1448 #ifndef MINGW
1449   struct flock fl;
1450
1451   memset (&fl, 0, sizeof (struct flock));
1452   fl.l_type = excl ? F_WRLCK : F_RDLCK;
1453   fl.l_whence = SEEK_SET;
1454   fl.l_start = lockStart;
1455   fl.l_len = lockEnd;
1456
1457   return fcntl (fh->fd, F_SETLK, &fl) != 0 ? GNUNET_SYSERR : GNUNET_OK;
1458 #else
1459   OVERLAPPED o;
1460   OFF_T diff = lockEnd - lockStart;
1461   DWORD diff_low, diff_high;
1462   diff_low = (DWORD) (diff & 0xFFFFFFFF);
1463   diff_high = (DWORD) ((diff >> (sizeof (DWORD) * 8)) & 0xFFFFFFFF);
1464
1465   memset (&o, 0, sizeof (OVERLAPPED));
1466   o.Offset = (DWORD) (lockStart & 0xFFFFFFFF);;
1467   o.OffsetHigh = (DWORD) (((lockStart & ~0xFFFFFFFF) >> (sizeof (DWORD) * 8)) & 0xFFFFFFFF);
1468
1469   if (!LockFileEx
1470       (fh->h, (excl ? LOCKFILE_EXCLUSIVE_LOCK : 0) | LOCKFILE_FAIL_IMMEDIATELY,
1471        0, diff_low, diff_high, &o))
1472   {
1473     SetErrnoFromWinError (GetLastError ());
1474     return GNUNET_SYSERR;
1475   }
1476
1477   return GNUNET_OK;
1478 #endif
1479 }
1480
1481
1482 /**
1483  * Unlock a part of a file
1484  * @param fh file handle
1485  * @param unlockStart absolute position from where to unlock
1486  * @param unlockEnd absolute position until where to unlock
1487  * @return GNUNET_OK on success, GNUNET_SYSERR on error
1488  */
1489 int
1490 GNUNET_DISK_file_unlock (struct GNUNET_DISK_FileHandle *fh, OFF_T unlockStart,
1491                          OFF_T unlockEnd)
1492 {
1493   if (fh == NULL)
1494   {
1495     errno = EINVAL;
1496     return GNUNET_SYSERR;
1497   }
1498
1499 #ifndef MINGW
1500   struct flock fl;
1501
1502   memset (&fl, 0, sizeof (struct flock));
1503   fl.l_type = F_UNLCK;
1504   fl.l_whence = SEEK_SET;
1505   fl.l_start = unlockStart;
1506   fl.l_len = unlockEnd;
1507
1508   return fcntl (fh->fd, F_SETLK, &fl) != 0 ? GNUNET_SYSERR : GNUNET_OK;
1509 #else
1510   OVERLAPPED o;
1511   OFF_T diff = unlockEnd - unlockStart;
1512   DWORD diff_low, diff_high;
1513   diff_low = (DWORD) (diff & 0xFFFFFFFF);
1514   diff_high = (DWORD) ((diff >> (sizeof (DWORD) * 8)) & 0xFFFFFFFF);
1515
1516   memset (&o, 0, sizeof (OVERLAPPED));
1517   o.Offset = (DWORD) (unlockStart & 0xFFFFFFFF);;
1518   o.OffsetHigh = (DWORD) (((unlockStart & ~0xFFFFFFFF) >> (sizeof (DWORD) * 8)) & 0xFFFFFFFF);
1519
1520   if (!UnlockFileEx (fh->h, 0, diff_low, diff_high, &o))
1521   {
1522     SetErrnoFromWinError (GetLastError ());
1523     return GNUNET_SYSERR;
1524   }
1525
1526   return GNUNET_OK;
1527 #endif
1528 }
1529
1530
1531 /**
1532  * Open a file.  Note that the access permissions will only be
1533  * used if a new file is created and if the underlying operating
1534  * system supports the given permissions.
1535  *
1536  * @param fn file name to be opened
1537  * @param flags opening flags, a combination of GNUNET_DISK_OPEN_xxx bit flags
1538  * @param perm permissions for the newly created file, use
1539  *             GNUNET_DISK_PERM_USER_NONE if a file could not be created by this
1540  *             call (because of flags)
1541  * @return IO handle on success, NULL on error
1542  */
1543 struct GNUNET_DISK_FileHandle *
1544 GNUNET_DISK_file_open (const char *fn, enum GNUNET_DISK_OpenFlags flags,
1545                        enum GNUNET_DISK_AccessPermissions perm)
1546 {
1547   char *expfn;
1548   struct GNUNET_DISK_FileHandle *ret;
1549
1550 #ifdef MINGW
1551   DWORD access;
1552   DWORD disp;
1553   HANDLE h;
1554   wchar_t wexpfn[MAX_PATH + 1];
1555 #else
1556   int oflags;
1557   int mode;
1558   int fd;
1559 #endif
1560
1561   expfn = GNUNET_STRINGS_filename_expand (fn);
1562   if (NULL == expfn)
1563     return NULL;
1564 #ifndef MINGW
1565   mode = 0;
1566   if (GNUNET_DISK_OPEN_READWRITE == (flags & GNUNET_DISK_OPEN_READWRITE))
1567     oflags = O_RDWR;            /* note: O_RDWR is NOT always O_RDONLY | O_WRONLY */
1568   else if (flags & GNUNET_DISK_OPEN_READ)
1569     oflags = O_RDONLY;
1570   else if (flags & GNUNET_DISK_OPEN_WRITE)
1571     oflags = O_WRONLY;
1572   else
1573   {
1574     GNUNET_break (0);
1575     GNUNET_free (expfn);
1576     return NULL;
1577   }
1578   if (flags & GNUNET_DISK_OPEN_FAILIFEXISTS)
1579     oflags |= (O_CREAT | O_EXCL);
1580   if (flags & GNUNET_DISK_OPEN_TRUNCATE)
1581     oflags |= O_TRUNC;
1582   if (flags & GNUNET_DISK_OPEN_APPEND)
1583     oflags |= O_APPEND;
1584   if (flags & GNUNET_DISK_OPEN_CREATE)
1585   {
1586     (void) GNUNET_DISK_directory_create_for_file (expfn);
1587     oflags |= O_CREAT;
1588     mode = translate_unix_perms (perm);
1589   }
1590
1591   fd = open (expfn, oflags | O_LARGEFILE, mode);
1592   if (fd == -1)
1593   {
1594     if (0 == (flags & GNUNET_DISK_OPEN_FAILIFEXISTS))
1595       LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "open", expfn);
1596     else
1597       LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_DEBUG, "open", expfn);
1598     GNUNET_free (expfn);
1599     return NULL;
1600   }
1601 #else
1602   access = 0;
1603   disp = OPEN_ALWAYS;
1604
1605   if (GNUNET_DISK_OPEN_READWRITE == (flags & GNUNET_DISK_OPEN_READWRITE))
1606     access = FILE_READ_DATA | FILE_WRITE_DATA;
1607   else if (flags & GNUNET_DISK_OPEN_READ)
1608     access = FILE_READ_DATA;
1609   else if (flags & GNUNET_DISK_OPEN_WRITE)
1610     access = FILE_WRITE_DATA;
1611
1612   if (flags & GNUNET_DISK_OPEN_FAILIFEXISTS)
1613   {
1614     disp = CREATE_NEW;
1615   }
1616   else if (flags & GNUNET_DISK_OPEN_CREATE)
1617   {
1618     (void) GNUNET_DISK_directory_create_for_file (expfn);
1619     if (flags & GNUNET_DISK_OPEN_TRUNCATE)
1620       disp = CREATE_ALWAYS;
1621     else
1622       disp = OPEN_ALWAYS;
1623   }
1624   else if (flags & GNUNET_DISK_OPEN_TRUNCATE)
1625   {
1626     disp = TRUNCATE_EXISTING;
1627   }
1628   else
1629   {
1630     disp = OPEN_EXISTING;
1631   }
1632
1633   if (ERROR_SUCCESS == plibc_conv_to_win_pathwconv(expfn, wexpfn))
1634     h = CreateFileW (wexpfn, access,
1635                     FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE, NULL,
1636                     disp, FILE_ATTRIBUTE_NORMAL, NULL);
1637   else
1638     h = INVALID_HANDLE_VALUE;
1639   if (h == INVALID_HANDLE_VALUE)
1640   {
1641     SetErrnoFromWinError (GetLastError ());
1642     LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "open", expfn);
1643     GNUNET_free (expfn);
1644     return NULL;
1645   }
1646
1647   if (flags & GNUNET_DISK_OPEN_APPEND)
1648     if (SetFilePointer (h, 0, 0, FILE_END) == INVALID_SET_FILE_POINTER)
1649     {
1650       SetErrnoFromWinError (GetLastError ());
1651       LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "SetFilePointer", expfn);
1652       CloseHandle (h);
1653       GNUNET_free (expfn);
1654       return NULL;
1655     }
1656 #endif
1657
1658   ret = GNUNET_malloc (sizeof (struct GNUNET_DISK_FileHandle));
1659 #ifdef MINGW
1660   ret->h = h;
1661   ret->type = GNUNET_DISK_FILE;
1662 #else
1663   ret->fd = fd;
1664 #endif
1665   GNUNET_free (expfn);
1666   return ret;
1667 }
1668
1669
1670 /**
1671  * Close an open file
1672  * @param h file handle
1673  * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
1674  */
1675 int
1676 GNUNET_DISK_file_close (struct GNUNET_DISK_FileHandle *h)
1677 {
1678   if (h == NULL)
1679   {
1680     errno = EINVAL;
1681     return GNUNET_SYSERR;
1682   }
1683
1684 #if MINGW
1685   if (!CloseHandle (h->h))
1686   {
1687     SetErrnoFromWinError (GetLastError ());
1688     LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING, "close");
1689     GNUNET_free (h->oOverlapRead);
1690     GNUNET_free (h->oOverlapWrite);
1691     GNUNET_free (h);
1692     return GNUNET_SYSERR;
1693   }
1694 #else
1695   if (close (h->fd) != 0)
1696   {
1697     LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING, "close");
1698     GNUNET_free (h);
1699     return GNUNET_SYSERR;
1700   }
1701 #endif
1702   GNUNET_free (h);
1703   return GNUNET_OK;
1704 }
1705
1706
1707 /**
1708  * Construct full path to a file inside of the private
1709  * directory used by GNUnet.  Also creates the corresponding
1710  * directory.  If the resulting name is supposed to be
1711  * a directory, end the last argument in '/' (or pass
1712  * DIR_SEPARATOR_STR as the last argument before NULL).
1713  *
1714  * @param cfg configuration to use (determines HOME)
1715  * @param serviceName name of the service
1716  * @param ... is NULL-terminated list of
1717  *                path components to append to the
1718  *                private directory name.
1719  * @return the constructed filename
1720  */
1721 char *
1722 GNUNET_DISK_get_home_filename (const struct GNUNET_CONFIGURATION_Handle *cfg,
1723                                const char *serviceName, ...)
1724 {
1725   const char *c;
1726   char *pfx;
1727   char *ret;
1728   va_list ap;
1729   unsigned int needed;
1730
1731   if (GNUNET_OK !=
1732       GNUNET_CONFIGURATION_get_value_filename (cfg, serviceName, "HOME", &pfx))
1733     return NULL;
1734   if (pfx == NULL)
1735   {
1736     LOG (GNUNET_ERROR_TYPE_WARNING,
1737          _("No `%s' specified for service `%s' in configuration.\n"), "HOME",
1738          serviceName);
1739     return NULL;
1740   }
1741   needed = strlen (pfx) + 2;
1742   if ((pfx[strlen (pfx) - 1] != '/') && (pfx[strlen (pfx) - 1] != '\\'))
1743     needed++;
1744   va_start (ap, serviceName);
1745   while (1)
1746   {
1747     c = va_arg (ap, const char *);
1748
1749     if (c == NULL)
1750       break;
1751     needed += strlen (c);
1752     if ((c[strlen (c) - 1] != '/') && (c[strlen (c) - 1] != '\\'))
1753       needed++;
1754   }
1755   va_end (ap);
1756   ret = GNUNET_malloc (needed);
1757   strcpy (ret, pfx);
1758   GNUNET_free (pfx);
1759   va_start (ap, serviceName);
1760   while (1)
1761   {
1762     c = va_arg (ap, const char *);
1763
1764     if (c == NULL)
1765       break;
1766     if ((c[strlen (c) - 1] != '/') && (c[strlen (c) - 1] != '\\'))
1767       strcat (ret, DIR_SEPARATOR_STR);
1768     strcat (ret, c);
1769   }
1770   va_end (ap);
1771   if ((ret[strlen (ret) - 1] != '/') && (ret[strlen (ret) - 1] != '\\'))
1772     (void) GNUNET_DISK_directory_create_for_file (ret);
1773   else
1774     (void) GNUNET_DISK_directory_create (ret);
1775   return ret;
1776 }
1777
1778
1779 /**
1780  * Handle for a memory-mapping operation.
1781  */
1782 struct GNUNET_DISK_MapHandle
1783 {
1784   /**
1785    * Address where the map is in memory.
1786    */
1787   void *addr;
1788
1789 #ifdef MINGW
1790   /**
1791    * Underlying OS handle.
1792    */
1793   HANDLE h;
1794 #else
1795   /**
1796    * Number of bytes mapped.
1797    */
1798   size_t len;
1799 #endif
1800 };
1801
1802
1803 #ifndef MAP_FAILED
1804 #define MAP_FAILED ((void *) -1)
1805 #endif
1806
1807 /**
1808  * Map a file into memory
1809  *
1810  * @param h open file handle
1811  * @param m handle to the new mapping
1812  * @param access access specification, GNUNET_DISK_MAP_TYPE_xxx
1813  * @param len size of the mapping
1814  * @return pointer to the mapped memory region, NULL on failure
1815  */
1816 void *
1817 GNUNET_DISK_file_map (const struct GNUNET_DISK_FileHandle *h,
1818                       struct GNUNET_DISK_MapHandle **m,
1819                       enum GNUNET_DISK_MapType access, size_t len)
1820 {
1821   if (h == NULL)
1822   {
1823     errno = EINVAL;
1824     return NULL;
1825   }
1826
1827 #ifdef MINGW
1828   DWORD mapAccess, protect;
1829
1830   if ((access & GNUNET_DISK_MAP_TYPE_READ) &&
1831       (access & GNUNET_DISK_MAP_TYPE_WRITE))
1832   {
1833     protect = PAGE_READWRITE;
1834     mapAccess = FILE_MAP_ALL_ACCESS;
1835   }
1836   else if (access & GNUNET_DISK_MAP_TYPE_READ)
1837   {
1838     protect = PAGE_READONLY;
1839     mapAccess = FILE_MAP_READ;
1840   }
1841   else if (access & GNUNET_DISK_MAP_TYPE_WRITE)
1842   {
1843     protect = PAGE_READWRITE;
1844     mapAccess = FILE_MAP_WRITE;
1845   }
1846   else
1847   {
1848     GNUNET_break (0);
1849     return NULL;
1850   }
1851
1852   *m = GNUNET_malloc (sizeof (struct GNUNET_DISK_MapHandle));
1853   (*m)->h = CreateFileMapping (h->h, NULL, protect, 0, 0, NULL);
1854   if ((*m)->h == INVALID_HANDLE_VALUE)
1855   {
1856     SetErrnoFromWinError (GetLastError ());
1857     GNUNET_free (*m);
1858     return NULL;
1859   }
1860
1861   (*m)->addr = MapViewOfFile ((*m)->h, mapAccess, 0, 0, len);
1862   if (!(*m)->addr)
1863   {
1864     SetErrnoFromWinError (GetLastError ());
1865     CloseHandle ((*m)->h);
1866     GNUNET_free (*m);
1867   }
1868
1869   return (*m)->addr;
1870 #else
1871   int prot;
1872
1873   prot = 0;
1874   if (access & GNUNET_DISK_MAP_TYPE_READ)
1875     prot = PROT_READ;
1876   if (access & GNUNET_DISK_MAP_TYPE_WRITE)
1877     prot |= PROT_WRITE;
1878   *m = GNUNET_malloc (sizeof (struct GNUNET_DISK_MapHandle));
1879   (*m)->addr = mmap (NULL, len, prot, MAP_SHARED, h->fd, 0);
1880   GNUNET_assert (NULL != (*m)->addr);
1881   if (MAP_FAILED == (*m)->addr)
1882   {
1883     GNUNET_free (*m);
1884     return NULL;
1885   }
1886   (*m)->len = len;
1887   return (*m)->addr;
1888 #endif
1889 }
1890
1891 /**
1892  * Unmap a file
1893  * @param h mapping handle
1894  * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
1895  */
1896 int
1897 GNUNET_DISK_file_unmap (struct GNUNET_DISK_MapHandle *h)
1898 {
1899   int ret;
1900
1901   if (h == NULL)
1902   {
1903     errno = EINVAL;
1904     return GNUNET_SYSERR;
1905   }
1906
1907 #ifdef MINGW
1908   ret = UnmapViewOfFile (h->addr) ? GNUNET_OK : GNUNET_SYSERR;
1909   if (ret != GNUNET_OK)
1910     SetErrnoFromWinError (GetLastError ());
1911   if (!CloseHandle (h->h) && (ret == GNUNET_OK))
1912   {
1913     ret = GNUNET_SYSERR;
1914     SetErrnoFromWinError (GetLastError ());
1915   }
1916 #else
1917   ret = munmap (h->addr, h->len) != -1 ? GNUNET_OK : GNUNET_SYSERR;
1918 #endif
1919   GNUNET_free (h);
1920   return ret;
1921 }
1922
1923
1924 /**
1925  * Write file changes to disk
1926  * @param h handle to an open file
1927  * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
1928  */
1929 int
1930 GNUNET_DISK_file_sync (const struct GNUNET_DISK_FileHandle *h)
1931 {
1932   if (h == NULL)
1933   {
1934     errno = EINVAL;
1935     return GNUNET_SYSERR;
1936   }
1937
1938 #ifdef MINGW
1939   int ret;
1940
1941   ret = FlushFileBuffers (h->h) ? GNUNET_OK : GNUNET_SYSERR;
1942   if (ret != GNUNET_OK)
1943     SetErrnoFromWinError (GetLastError ());
1944   return ret;
1945 #elif defined(FREEBSD) || defined(OPENBSD) || defined(DARWIN)
1946   return fsync (h->fd) == -1 ? GNUNET_SYSERR : GNUNET_OK;
1947 #else
1948   return fdatasync (h->fd) == -1 ? GNUNET_SYSERR : GNUNET_OK;
1949 #endif
1950 }
1951
1952 #if WINDOWS
1953 /* Copyright Bob Byrnes  <byrnes <at> curl.com>
1954    http://permalink.gmane.org/gmane.os.cygwin.patches/2121
1955 */
1956 /* Create a pipe, and return handles to the read and write ends,
1957    just like CreatePipe, but ensure that the write end permits
1958    FILE_READ_ATTRIBUTES access, on later versions of win32 where
1959    this is supported.  This access is needed by NtQueryInformationFile,
1960    which is used to implement select and nonblocking writes.
1961    Note that the return value is either NO_ERROR or GetLastError,
1962    unlike CreatePipe, which returns a bool for success or failure.  */
1963 static int
1964 create_selectable_pipe (PHANDLE read_pipe_ptr, PHANDLE write_pipe_ptr,
1965                         LPSECURITY_ATTRIBUTES sa_ptr, DWORD psize,
1966                         DWORD dwReadMode, DWORD dwWriteMode)
1967 {
1968   /* Default to error. */
1969   *read_pipe_ptr = *write_pipe_ptr = INVALID_HANDLE_VALUE;
1970
1971   HANDLE read_pipe = INVALID_HANDLE_VALUE, write_pipe = INVALID_HANDLE_VALUE;
1972
1973   /* Ensure that there is enough pipe buffer space for atomic writes.  */
1974   if (psize < PIPE_BUF)
1975     psize = PIPE_BUF;
1976
1977   char pipename[MAX_PATH];
1978
1979   /* Retry CreateNamedPipe as long as the pipe name is in use.
1980    * Retrying will probably never be necessary, but we want
1981    * to be as robust as possible.  */
1982   while (1)
1983   {
1984     static volatile LONG pipe_unique_id;
1985
1986     snprintf (pipename, sizeof pipename, "\\\\.\\pipe\\gnunet-%d-%ld",
1987               getpid (), InterlockedIncrement ((LONG *) & pipe_unique_id));
1988 #if DEBUG_PIPE
1989     LOG (GNUNET_ERROR_TYPE_DEBUG, "CreateNamedPipe: name = %s, size = %lu\n",
1990          pipename, psize);
1991 #endif
1992     /* Use CreateNamedPipe instead of CreatePipe, because the latter
1993      * returns a write handle that does not permit FILE_READ_ATTRIBUTES
1994      * access, on versions of win32 earlier than WinXP SP2.
1995      * CreatePipe also stupidly creates a full duplex pipe, which is
1996      * a waste, since only a single direction is actually used.
1997      * It's important to only allow a single instance, to ensure that
1998      * the pipe was not created earlier by some other process, even if
1999      * the pid has been reused.  We avoid FILE_FLAG_FIRST_PIPE_INSTANCE
2000      * because that is only available for Win2k SP2 and WinXP.  */
2001     read_pipe = CreateNamedPipeA (pipename, PIPE_ACCESS_INBOUND | dwReadMode, PIPE_TYPE_BYTE | PIPE_READMODE_BYTE, 1,   /* max instances */
2002                                   psize,        /* output buffer size */
2003                                   psize,        /* input buffer size */
2004                                   NMPWAIT_USE_DEFAULT_WAIT, sa_ptr);
2005
2006     if (read_pipe != INVALID_HANDLE_VALUE)
2007     {
2008 #if DEBUG_PIPE
2009       LOG (GNUNET_ERROR_TYPE_DEBUG, "pipe read handle = %p\n", read_pipe);
2010 #endif
2011       break;
2012     }
2013
2014     DWORD err = GetLastError ();
2015
2016     switch (err)
2017     {
2018     case ERROR_PIPE_BUSY:
2019       /* The pipe is already open with compatible parameters.
2020        * Pick a new name and retry.  */
2021 #if DEBUG_PIPE
2022       LOG (GNUNET_ERROR_TYPE_DEBUG, "pipe busy, retrying\n");
2023 #endif
2024       continue;
2025     case ERROR_ACCESS_DENIED:
2026       /* The pipe is already open with incompatible parameters.
2027        * Pick a new name and retry.  */
2028 #if DEBUG_PIPE
2029       LOG (GNUNET_ERROR_TYPE_DEBUG, "pipe access denied, retrying\n");
2030 #endif
2031       continue;
2032     case ERROR_CALL_NOT_IMPLEMENTED:
2033       /* We are on an older Win9x platform without named pipes.
2034        * Return an anonymous pipe as the best approximation.  */
2035 #if DEBUG_PIPE
2036       LOG (GNUNET_ERROR_TYPE_DEBUG,
2037            "CreateNamedPipe not implemented, resorting to "
2038            "CreatePipe: size = %lu\n", psize);
2039 #endif
2040       if (CreatePipe (read_pipe_ptr, write_pipe_ptr, sa_ptr, psize))
2041       {
2042 #if DEBUG_PIPE
2043         LOG (GNUNET_ERROR_TYPE_DEBUG, "pipe read handle = %p\n",
2044              *read_pipe_ptr);
2045         LOG (GNUNET_ERROR_TYPE_DEBUG, "pipe write handle = %p\n",
2046              *write_pipe_ptr);
2047 #endif
2048         return GNUNET_OK;
2049       }
2050       err = GetLastError ();
2051       LOG (GNUNET_ERROR_TYPE_ERROR, "CreatePipe failed: %d\n", err);
2052       return err;
2053     default:
2054       LOG (GNUNET_ERROR_TYPE_ERROR, "CreateNamedPipe failed: %d\n", err);
2055       return err;
2056     }
2057     /* NOTREACHED */
2058   }
2059 #if DEBUG_PIPE
2060   LOG (GNUNET_ERROR_TYPE_DEBUG, "CreateFile: name = %s\n", pipename);
2061 #endif
2062
2063   /* Open the named pipe for writing.
2064    * Be sure to permit FILE_READ_ATTRIBUTES access.  */
2065   write_pipe = CreateFileA (pipename, GENERIC_WRITE | FILE_READ_ATTRIBUTES, 0,  /* share mode */
2066                             sa_ptr, OPEN_EXISTING, dwWriteMode, /* flags and attributes */
2067                             0); /* handle to template file */
2068
2069   if (write_pipe == INVALID_HANDLE_VALUE)
2070   {
2071     /* Failure. */
2072     DWORD err = GetLastError ();
2073
2074 #if DEBUG_PIPE
2075     LOG (GNUNET_ERROR_TYPE_DEBUG, "CreateFile failed: %d\n", err);
2076 #endif
2077     CloseHandle (read_pipe);
2078     return err;
2079   }
2080 #if DEBUG_PIPE
2081   LOG (GNUNET_ERROR_TYPE_DEBUG, "pipe write handle = %p\n", write_pipe);
2082 #endif
2083   /* Success. */
2084   *read_pipe_ptr = read_pipe;
2085   *write_pipe_ptr = write_pipe;
2086   return GNUNET_OK;
2087 }
2088 #endif
2089
2090 /**
2091  * Creates an interprocess channel
2092  *
2093  * @param blocking creates an asynchronous pipe if set to GNUNET_NO
2094  * @param inherit_read inherit the parent processes stdin (only for windows)
2095  * @param inherit_write inherit the parent processes stdout (only for windows)
2096  *
2097  * @return handle to the new pipe, NULL on error
2098  */
2099 struct GNUNET_DISK_PipeHandle *
2100 GNUNET_DISK_pipe (int blocking_read, int blocking_write, int inherit_read, int inherit_write)
2101 {
2102   struct GNUNET_DISK_PipeHandle *p;
2103   struct GNUNET_DISK_FileHandle *fds;
2104
2105   p = GNUNET_malloc (sizeof (struct GNUNET_DISK_PipeHandle) +
2106                      2 * sizeof (struct GNUNET_DISK_FileHandle));
2107   fds = (struct GNUNET_DISK_FileHandle *) &p[1];
2108   p->fd[0] = &fds[0];
2109   p->fd[1] = &fds[1];
2110 #ifndef MINGW
2111   int fd[2];
2112   int ret;
2113   int flags;
2114   int eno;
2115
2116   ret = pipe (fd);
2117   if (ret == -1)
2118   {
2119     eno = errno;
2120     LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR, "pipe");
2121     GNUNET_free (p);
2122     errno = eno;
2123     return NULL;
2124   }
2125   p->fd[0]->fd = fd[0];
2126   p->fd[1]->fd = fd[1];
2127   ret = 0;
2128   flags = fcntl (fd[0], F_GETFL);
2129   if (!blocking_read)
2130     flags |= O_NONBLOCK;
2131   if (0 > fcntl (fd[0], F_SETFL, flags))
2132     ret = -1;
2133   flags = fcntl (fd[0], F_GETFD);
2134   flags |= FD_CLOEXEC;
2135   if (0 > fcntl (fd[0], F_SETFD, flags))
2136     ret = -1;
2137
2138   flags = fcntl (fd[1], F_GETFL);
2139   if (!blocking_write)
2140     flags |= O_NONBLOCK;
2141   if (0 > fcntl (fd[1], F_SETFL, flags))
2142     ret = -1;
2143   flags = fcntl (fd[1], F_GETFD);
2144   flags |= FD_CLOEXEC;
2145   if (0 > fcntl (fd[1], F_SETFD, flags))
2146     ret = -1;
2147   if (ret == -1)
2148   {
2149     eno = errno;
2150     LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR, "fcntl");
2151     GNUNET_break (0 == close (p->fd[0]->fd));
2152     GNUNET_break (0 == close (p->fd[1]->fd));
2153     GNUNET_free (p);
2154     errno = eno;
2155     return NULL;
2156   }
2157 #else
2158   BOOL ret;
2159   HANDLE tmp_handle;
2160
2161   ret =
2162       create_selectable_pipe (&p->fd[0]->h, &p->fd[1]->h, NULL, 0,
2163                               blocking_read ? 0 : FILE_FLAG_OVERLAPPED,
2164                               blocking_write ? 0 : FILE_FLAG_OVERLAPPED);
2165   if (!ret)
2166   {
2167     GNUNET_free (p);
2168     SetErrnoFromWinError (GetLastError ());
2169     return NULL;
2170   }
2171   if (!DuplicateHandle
2172       (GetCurrentProcess (), p->fd[0]->h, GetCurrentProcess (), &tmp_handle, 0,
2173        inherit_read == GNUNET_YES ? TRUE : FALSE, DUPLICATE_SAME_ACCESS))
2174   {
2175     SetErrnoFromWinError (GetLastError ());
2176     CloseHandle (p->fd[0]->h);
2177     CloseHandle (p->fd[1]->h);
2178     GNUNET_free (p);
2179     return NULL;
2180   }
2181   CloseHandle (p->fd[0]->h);
2182   p->fd[0]->h = tmp_handle;
2183
2184   if (!DuplicateHandle
2185       (GetCurrentProcess (), p->fd[1]->h, GetCurrentProcess (), &tmp_handle, 0,
2186        inherit_write == GNUNET_YES ? TRUE : FALSE, DUPLICATE_SAME_ACCESS))
2187   {
2188     SetErrnoFromWinError (GetLastError ());
2189     CloseHandle (p->fd[0]->h);
2190     CloseHandle (p->fd[1]->h);
2191     GNUNET_free (p);
2192     return NULL;
2193   }
2194   CloseHandle (p->fd[1]->h);
2195   p->fd[1]->h = tmp_handle;
2196
2197   p->fd[0]->type = GNUNET_PIPE;
2198   p->fd[1]->type = GNUNET_PIPE;
2199
2200   p->fd[0]->oOverlapRead = GNUNET_malloc (sizeof (OVERLAPPED));
2201   p->fd[0]->oOverlapWrite = GNUNET_malloc (sizeof (OVERLAPPED));
2202   p->fd[1]->oOverlapRead = GNUNET_malloc (sizeof (OVERLAPPED));
2203   p->fd[1]->oOverlapWrite = GNUNET_malloc (sizeof (OVERLAPPED));
2204
2205   p->fd[0]->oOverlapRead->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2206   p->fd[0]->oOverlapWrite->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2207
2208   p->fd[1]->oOverlapRead->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2209   p->fd[1]->oOverlapWrite->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2210
2211 #endif
2212   return p;
2213 }
2214
2215 /**
2216  * Creates a pipe object from a couple of file descriptors.
2217  * Useful for wrapping existing pipe FDs.
2218  *
2219  * @param blocking creates an asynchronous pipe if set to GNUNET_NO
2220  * @param fd an array of two fd values. One of them may be -1 for read-only or write-only pipes
2221  *
2222  * @return handle to the new pipe, NULL on error
2223  */
2224 struct GNUNET_DISK_PipeHandle *
2225 GNUNET_DISK_pipe_from_fd (int blocking_read, int blocking_write, int fd[2])
2226 {
2227   struct GNUNET_DISK_PipeHandle *p;
2228   struct GNUNET_DISK_FileHandle *fds;
2229
2230   p = GNUNET_malloc (sizeof (struct GNUNET_DISK_PipeHandle) +
2231                      2 * sizeof (struct GNUNET_DISK_FileHandle));
2232   fds = (struct GNUNET_DISK_FileHandle *) &p[1];
2233   p->fd[0] = &fds[0];
2234   p->fd[1] = &fds[1];
2235 #ifndef MINGW
2236   int ret;
2237   int flags;
2238   int eno;
2239
2240   p->fd[0]->fd = fd[0];
2241   p->fd[1]->fd = fd[1];
2242   ret = 0;
2243   if (fd[0] >= 0)
2244   {
2245     flags = fcntl (fd[0], F_GETFL);
2246     if (!blocking_read)
2247       flags |= O_NONBLOCK;
2248     if (0 > fcntl (fd[0], F_SETFL, flags))
2249       ret = -1;
2250     flags = fcntl (fd[0], F_GETFD);
2251     flags |= FD_CLOEXEC;
2252     if (0 > fcntl (fd[0], F_SETFD, flags))
2253       ret = -1;
2254   }
2255
2256   if (fd[1] >= 0)
2257   {
2258     flags = fcntl (fd[1], F_GETFL);
2259     if (!blocking_write)
2260       flags |= O_NONBLOCK;
2261     if (0 > fcntl (fd[1], F_SETFL, flags))
2262       ret = -1;
2263     flags = fcntl (fd[1], F_GETFD);
2264     flags |= FD_CLOEXEC;
2265     if (0 > fcntl (fd[1], F_SETFD, flags))
2266       ret = -1;
2267   }
2268   if (ret == -1)
2269   {
2270     eno = errno;
2271     LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR, "fcntl");
2272     if (p->fd[0]->fd >= 0)
2273       GNUNET_break (0 == close (p->fd[0]->fd));
2274     if (p->fd[1]->fd >= 0)
2275       GNUNET_break (0 == close (p->fd[1]->fd));
2276     GNUNET_free (p);
2277     errno = eno;
2278     return NULL;
2279   }
2280 #else
2281   BOOL ret;
2282
2283   if (fd[0] >= 0)
2284     p->fd[0]->h = _get_osfhandle (fd[0]);
2285   else
2286     p->fd[0]->h = INVALID_HANDLE_VALUE;
2287   if (fd[1] >= 0)
2288     p->fd[1]->h = _get_osfhandle (fd[1]);
2289   else
2290     p->fd[1]->h = INVALID_HANDLE_VALUE;
2291
2292   if (p->fd[0]->h != INVALID_HANDLE_VALUE)
2293   {
2294     p->fd[0]->type = GNUNET_PIPE;
2295     p->fd[0]->oOverlapRead = GNUNET_malloc (sizeof (OVERLAPPED));
2296     p->fd[0]->oOverlapWrite = GNUNET_malloc (sizeof (OVERLAPPED));
2297     p->fd[0]->oOverlapRead->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2298     p->fd[0]->oOverlapWrite->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2299   }
2300
2301   if (p->fd[1]->h != INVALID_HANDLE_VALUE)
2302   {
2303     p->fd[1]->type = GNUNET_PIPE;
2304     p->fd[1]->oOverlapRead = GNUNET_malloc (sizeof (OVERLAPPED));
2305     p->fd[1]->oOverlapWrite = GNUNET_malloc (sizeof (OVERLAPPED));
2306     p->fd[1]->oOverlapRead->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2307     p->fd[1]->oOverlapWrite->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2308   }
2309 #endif
2310   return p;
2311 }
2312
2313
2314 /**
2315  * Closes an interprocess channel
2316  *
2317  * @param p pipe to close
2318  * @param end which end of the pipe to close
2319  * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
2320  */
2321 int
2322 GNUNET_DISK_pipe_close_end (struct GNUNET_DISK_PipeHandle *p,
2323                             enum GNUNET_DISK_PipeEnd end)
2324 {
2325   int ret = GNUNET_OK;
2326   int save;
2327
2328 #ifdef MINGW
2329   if (end == GNUNET_DISK_PIPE_END_READ)
2330   {
2331     if (p->fd[0]->h != INVALID_HANDLE_VALUE)
2332     {
2333       if (!CloseHandle (p->fd[0]->h))
2334       {
2335         SetErrnoFromWinError (GetLastError ());
2336         ret = GNUNET_SYSERR;
2337       }
2338       GNUNET_free (p->fd[0]->oOverlapRead);
2339       GNUNET_free (p->fd[0]->oOverlapWrite);
2340       p->fd[0]->h = INVALID_HANDLE_VALUE;
2341     }
2342   }
2343   else if (end == GNUNET_DISK_PIPE_END_WRITE)
2344   {
2345     if (p->fd[0]->h != INVALID_HANDLE_VALUE)
2346     {
2347       if (!CloseHandle (p->fd[1]->h))
2348       {
2349         SetErrnoFromWinError (GetLastError ());
2350         ret = GNUNET_SYSERR;
2351       }
2352       GNUNET_free (p->fd[1]->oOverlapRead);
2353       GNUNET_free (p->fd[1]->oOverlapWrite);
2354       p->fd[1]->h = INVALID_HANDLE_VALUE;
2355     }
2356   }
2357   save = errno;
2358 #else
2359   save = 0;
2360   if (end == GNUNET_DISK_PIPE_END_READ)
2361   {
2362     if (0 != close (p->fd[0]->fd))
2363     {
2364       ret = GNUNET_SYSERR;
2365       save = errno;
2366     }
2367     p->fd[0]->fd = -1;
2368   }
2369   else if (end == GNUNET_DISK_PIPE_END_WRITE)
2370   {
2371     if (0 != close (p->fd[1]->fd))
2372     {
2373       ret = GNUNET_SYSERR;
2374       save = errno;
2375     }
2376     p->fd[1]->fd = -1;
2377   }
2378 #endif
2379   errno = save;
2380   return ret;
2381 }
2382
2383 /**
2384  * Closes an interprocess channel
2385  *
2386  * @param p pipe to close
2387  * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
2388  */
2389 int
2390 GNUNET_DISK_pipe_close (struct GNUNET_DISK_PipeHandle *p)
2391 {
2392   int ret = GNUNET_OK;
2393   int save;
2394
2395 #ifdef MINGW
2396   if (p->fd[0]->h != INVALID_HANDLE_VALUE)
2397   {
2398     if (!CloseHandle (p->fd[0]->h))
2399     {
2400       SetErrnoFromWinError (GetLastError ());
2401       ret = GNUNET_SYSERR;
2402     }
2403     GNUNET_free (p->fd[0]->oOverlapRead);
2404     GNUNET_free (p->fd[0]->oOverlapWrite);
2405   }
2406   if (p->fd[1]->h != INVALID_HANDLE_VALUE)
2407   {
2408     if (!CloseHandle (p->fd[1]->h))
2409     {
2410       SetErrnoFromWinError (GetLastError ());
2411       ret = GNUNET_SYSERR;
2412     }
2413     GNUNET_free (p->fd[1]->oOverlapRead);
2414     GNUNET_free (p->fd[1]->oOverlapWrite);
2415   }
2416   save = errno;
2417 #else
2418   save = 0;
2419   if (p->fd[0]->fd != -1)
2420   {
2421     if (0 != close (p->fd[0]->fd))
2422     {
2423       ret = GNUNET_SYSERR;
2424       save = errno;
2425     }
2426   }
2427
2428   if (p->fd[1]->fd != -1)
2429   {
2430     if (0 != close (p->fd[1]->fd))
2431     {
2432       ret = GNUNET_SYSERR;
2433       save = errno;
2434     }
2435   }
2436 #endif
2437   GNUNET_free (p);
2438   errno = save;
2439   return ret;
2440 }
2441
2442
2443 /**
2444  * Creates a named pipe/FIFO and opens it
2445  * @param fn pointer to the name of the named pipe or to NULL
2446  * @param flags open flags
2447  * @param perm access permissions
2448  * @return pipe handle on success, NULL on error
2449  */
2450 struct GNUNET_DISK_FileHandle *
2451 GNUNET_DISK_npipe_create (char **fn, enum GNUNET_DISK_OpenFlags flags,
2452                           enum GNUNET_DISK_AccessPermissions perm)
2453 {
2454 #ifdef MINGW
2455   struct GNUNET_DISK_FileHandle *ret;
2456   HANDLE h = NULL;
2457   DWORD openMode;
2458   char *name;
2459
2460   openMode = 0;
2461   if (flags & GNUNET_DISK_OPEN_READWRITE)
2462     openMode = PIPE_ACCESS_DUPLEX;
2463   else if (flags & GNUNET_DISK_OPEN_READ)
2464     openMode = PIPE_ACCESS_INBOUND;
2465   else if (flags & GNUNET_DISK_OPEN_WRITE)
2466     openMode = PIPE_ACCESS_OUTBOUND;
2467
2468   if (flags & GNUNET_DISK_OPEN_FAILIFEXISTS)
2469     openMode |= FILE_FLAG_FIRST_PIPE_INSTANCE;
2470
2471   while (h == NULL)
2472   {
2473     DWORD error_code;
2474
2475     name = NULL;
2476     if (*fn != NULL)
2477     {
2478       GNUNET_asprintf (&name, "\\\\.\\pipe\\%.246s", fn);
2479 #if DEBUG_NPIPE
2480       LOG (GNUNET_ERROR_TYPE_DEBUG,
2481            "Trying to create an instance of named pipe `%s'\n", name);
2482 #endif
2483       /* 1) This might work just fine with UTF-8 strings as it is.
2484        * 2) This is only used by GNUnet itself, and only with latin names.
2485        */
2486       h = CreateNamedPipe (name, openMode | FILE_FLAG_OVERLAPPED,
2487                            PIPE_TYPE_BYTE | PIPE_READMODE_BYTE, 2, 1, 1, 0,
2488                            NULL);
2489     }
2490     else
2491     {
2492       GNUNET_asprintf (fn, "\\\\.\\pipe\\gnunet-%llu",
2493                        GNUNET_CRYPTO_random_u64 (GNUNET_CRYPTO_QUALITY_WEAK,
2494                                                  UINT64_MAX));
2495 #if DEBUG_NPIPE
2496       LOG (GNUNET_ERROR_TYPE_DEBUG, "Trying to create unique named pipe `%s'\n",
2497            *fn);
2498 #endif
2499       h = CreateNamedPipe (*fn,
2500                            openMode | FILE_FLAG_OVERLAPPED |
2501                            FILE_FLAG_FIRST_PIPE_INSTANCE,
2502                            PIPE_TYPE_BYTE | PIPE_READMODE_BYTE, 2, 1, 1, 0,
2503                            NULL);
2504     }
2505     error_code = GetLastError ();
2506     if (name)
2507       GNUNET_free (name);
2508     /* don't re-set name to NULL yet */
2509     if (h == INVALID_HANDLE_VALUE)
2510     {
2511       SetErrnoFromWinError (error_code);
2512 #if DEBUG_NPIPE
2513       LOG (GNUNET_ERROR_TYPE_DEBUG,
2514            "Pipe creation have failed because of %d, errno is %d\n", error_code,
2515            errno);
2516 #endif
2517       if (name == NULL)
2518       {
2519 #if DEBUG_NPIPE
2520         LOG (GNUNET_ERROR_TYPE_DEBUG,
2521              "Pipe was to be unique, considering re-creation\n");
2522 #endif
2523         GNUNET_free (*fn);
2524         *fn = NULL;
2525         if (error_code != ERROR_ACCESS_DENIED && error_code != ERROR_PIPE_BUSY)
2526         {
2527           return NULL;
2528         }
2529 #if DEBUG_NPIPE
2530         LOG (GNUNET_ERROR_TYPE_DEBUG,
2531              "Pipe name was not unique, trying again\n");
2532 #endif
2533         h = NULL;
2534       }
2535       else
2536         return NULL;
2537     }
2538   }
2539   errno = 0;
2540
2541   ret = GNUNET_malloc (sizeof (*ret));
2542   ret->h = h;
2543   ret->type = GNUNET_PIPE;
2544
2545   ret->oOverlapRead = GNUNET_malloc (sizeof (OVERLAPPED));
2546   ret->oOverlapWrite = GNUNET_malloc (sizeof (OVERLAPPED));
2547
2548   ret->oOverlapRead->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2549   ret->oOverlapWrite->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2550
2551   return ret;
2552 #else
2553   if (*fn == NULL)
2554   {
2555     char dir[] = "/tmp/gnunet-pipe-XXXXXX";
2556
2557     if (mkdtemp (dir) == NULL)
2558     {
2559       LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR, "mkdtemp");
2560       return NULL;
2561     }
2562     GNUNET_asprintf (fn, "%s/child-control", dir);
2563   }
2564
2565   if (mkfifo (*fn, translate_unix_perms (perm)) == -1)
2566   {
2567     if ((errno != EEXIST) || (0 != (flags & GNUNET_DISK_OPEN_FAILIFEXISTS)))
2568       return NULL;
2569   }
2570
2571   flags = flags & (~GNUNET_DISK_OPEN_FAILIFEXISTS);
2572   return GNUNET_DISK_file_open (*fn, flags, perm);
2573 #endif
2574 }
2575
2576
2577 /**
2578  * Opens already existing named pipe/FIFO
2579  *
2580  * @param fn name of an existing named pipe
2581  * @param flags open flags
2582  * @param perm access permissions
2583  * @return pipe handle on success, NULL on error
2584  */
2585 struct GNUNET_DISK_FileHandle *
2586 GNUNET_DISK_npipe_open (const char *fn, enum GNUNET_DISK_OpenFlags flags,
2587                         enum GNUNET_DISK_AccessPermissions perm)
2588 {
2589 #ifdef MINGW
2590   struct GNUNET_DISK_FileHandle *ret;
2591   HANDLE h;
2592   DWORD openMode;
2593
2594   openMode = 0;
2595   if (flags & GNUNET_DISK_OPEN_READWRITE)
2596     openMode = GENERIC_WRITE | GENERIC_READ;
2597   else if (flags & GNUNET_DISK_OPEN_READ)
2598     openMode = GENERIC_READ;
2599   else if (flags & GNUNET_DISK_OPEN_WRITE)
2600     openMode = GENERIC_WRITE;
2601
2602   h = CreateFile (fn, openMode, 0, NULL, OPEN_EXISTING,
2603                   FILE_FLAG_OVERLAPPED | FILE_READ_ATTRIBUTES, NULL);
2604   if (h == INVALID_HANDLE_VALUE)
2605   {
2606     SetErrnoFromWinError (GetLastError ());
2607     return NULL;
2608   }
2609
2610   ret = GNUNET_malloc (sizeof (*ret));
2611   ret->h = h;
2612   ret->type = GNUNET_PIPE;
2613   ret->oOverlapRead = GNUNET_malloc (sizeof (OVERLAPPED));
2614   ret->oOverlapWrite = GNUNET_malloc (sizeof (OVERLAPPED));
2615   ret->oOverlapRead->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2616   ret->oOverlapWrite->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2617
2618   return ret;
2619 #else
2620   flags = flags & (~GNUNET_DISK_OPEN_FAILIFEXISTS);
2621   return GNUNET_DISK_file_open (fn, flags, perm);
2622 #endif
2623 }
2624
2625 /**
2626  * Closes a named pipe/FIFO
2627  * @param pipe named pipe
2628  * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
2629  */
2630 int
2631 GNUNET_DISK_npipe_close (struct GNUNET_DISK_FileHandle *pipe)
2632 {
2633 #ifndef MINGW
2634   return close (pipe->fd) == 0 ? GNUNET_OK : GNUNET_SYSERR;
2635 #else
2636   BOOL ret;
2637
2638   ret = CloseHandle (pipe->h);
2639   if (!ret)
2640   {
2641     SetErrnoFromWinError (GetLastError ());
2642     return GNUNET_SYSERR;
2643   }
2644   else
2645     return GNUNET_OK;
2646 #endif
2647 }
2648
2649
2650 /**
2651  * Get the handle to a particular pipe end
2652  *
2653  * @param p pipe
2654  * @param n end to access
2655  * @return handle for the respective end
2656  */
2657 const struct GNUNET_DISK_FileHandle *
2658 GNUNET_DISK_pipe_handle (const struct GNUNET_DISK_PipeHandle *p,
2659                          enum GNUNET_DISK_PipeEnd n)
2660 {
2661   switch (n)
2662   {
2663   case GNUNET_DISK_PIPE_END_READ:
2664   case GNUNET_DISK_PIPE_END_WRITE:
2665     return p->fd[n];
2666   default:
2667     GNUNET_break (0);
2668     return NULL;
2669   }
2670 }
2671
2672
2673 /**
2674  * Retrieve OS file handle
2675  * @internal
2676  * @param fh GNUnet file descriptor
2677  * @param dst destination buffer
2678  * @param dst_len length of dst
2679  * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
2680  */
2681 int
2682 GNUNET_DISK_internal_file_handle_ (const struct GNUNET_DISK_FileHandle *fh,
2683                                    void *dst, size_t dst_len)
2684 {
2685 #ifdef MINGW
2686   if (dst_len < sizeof (HANDLE))
2687     return GNUNET_SYSERR;
2688   *((HANDLE *) dst) = fh->h;
2689 #else
2690   if (dst_len < sizeof (int))
2691     return GNUNET_SYSERR;
2692   *((int *) dst) = fh->fd;
2693 #endif
2694
2695   return GNUNET_OK;
2696 }
2697
2698 /* end of disk.c */