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