- fix 2699
[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   char *name;
1188   char *dname;
1189   unsigned int name_len;
1190   unsigned int n_size;
1191
1192   GNUNET_assert (dirName != NULL);
1193   dname = GNUNET_STRINGS_filename_expand (dirName);
1194   if (dname == NULL)
1195     return GNUNET_SYSERR;
1196   while ((strlen (dname) > 0) && (dname[strlen (dname) - 1] == DIR_SEPARATOR))
1197     dname[strlen (dname) - 1] = '\0';
1198   if (0 != STAT (dname, &istat))
1199   {
1200     LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "stat", dname);
1201     GNUNET_free (dname);
1202     return GNUNET_SYSERR;
1203   }
1204   if (!S_ISDIR (istat.st_mode))
1205   {
1206     LOG (GNUNET_ERROR_TYPE_WARNING, _("Expected `%s' to be a directory!\n"),
1207          dirName);
1208     GNUNET_free (dname);
1209     return GNUNET_SYSERR;
1210   }
1211   errno = 0;
1212   dinfo = OPENDIR (dname);
1213   if ((errno == EACCES) || (dinfo == NULL))
1214   {
1215     LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "opendir", dname);
1216     if (dinfo != NULL)
1217       CLOSEDIR (dinfo);
1218     GNUNET_free (dname);
1219     return GNUNET_SYSERR;
1220   }
1221   name_len = 256;
1222   n_size = strlen (dname) + name_len + 2;
1223   name = GNUNET_malloc (n_size);
1224   while ((finfo = READDIR (dinfo)) != NULL)
1225   {
1226     if ((0 == strcmp (finfo->d_name, ".")) ||
1227         (0 == strcmp (finfo->d_name, "..")))
1228       continue;
1229     if (callback != NULL)
1230     {
1231       if (name_len < strlen (finfo->d_name))
1232       {
1233         GNUNET_free (name);
1234         name_len = strlen (finfo->d_name);
1235         n_size = strlen (dname) + name_len + 2;
1236         name = GNUNET_malloc (n_size);
1237       }
1238       /* dname can end in "/" only if dname == "/";
1239        * if dname does not end in "/", we need to add
1240        * a "/" (otherwise, we must not!) */
1241       GNUNET_snprintf (name, n_size, "%s%s%s", dname,
1242                        (strcmp (dname, DIR_SEPARATOR_STR) ==
1243                         0) ? "" : DIR_SEPARATOR_STR, finfo->d_name);
1244       if (GNUNET_OK != callback (callback_cls, name))
1245       {
1246         CLOSEDIR (dinfo);
1247         GNUNET_free (name);
1248         GNUNET_free (dname);
1249         return GNUNET_SYSERR;
1250       }
1251     }
1252     count++;
1253   }
1254   CLOSEDIR (dinfo);
1255   GNUNET_free (name);
1256   GNUNET_free (dname);
1257   return count;
1258 }
1259
1260
1261 /**
1262  * Opaque handle used for iterating over a directory.
1263  */
1264 struct GNUNET_DISK_DirectoryIterator
1265 {
1266
1267   /**
1268    * Function to call on directory entries.
1269    */
1270   GNUNET_DISK_DirectoryIteratorCallback callback;
1271
1272   /**
1273    * Closure for callback.
1274    */
1275   void *callback_cls;
1276
1277   /**
1278    * Reference to directory.
1279    */
1280   DIR *directory;
1281
1282   /**
1283    * Directory name.
1284    */
1285   char *dirname;
1286
1287   /**
1288    * Next filename to process.
1289    */
1290   char *next_name;
1291
1292   /**
1293    * Our priority.
1294    */
1295   enum GNUNET_SCHEDULER_Priority priority;
1296
1297 };
1298
1299
1300 /**
1301  * Task used by the directory iterator.
1302  */
1303 static void
1304 directory_iterator_task (void *cls,
1305                          const struct GNUNET_SCHEDULER_TaskContext *tc)
1306 {
1307   struct GNUNET_DISK_DirectoryIterator *iter = cls;
1308   char *name;
1309
1310   name = iter->next_name;
1311   GNUNET_assert (name != NULL);
1312   iter->next_name = NULL;
1313   iter->callback (iter->callback_cls, iter, name, iter->dirname);
1314   GNUNET_free (name);
1315 }
1316
1317
1318 /**
1319  * This function must be called during the DiskIteratorCallback
1320  * (exactly once) to schedule the task to process the next
1321  * filename in the directory (if there is one).
1322  *
1323  * @param iter opaque handle for the iterator
1324  * @param can set to GNUNET_YES to terminate the iteration early
1325  * @return GNUNET_YES if iteration will continue,
1326  *         GNUNET_NO if this was the last entry (and iteration is complete),
1327  *         GNUNET_SYSERR if abort was YES
1328  */
1329 int
1330 GNUNET_DISK_directory_iterator_next (struct GNUNET_DISK_DirectoryIterator *iter,
1331                                      int can)
1332 {
1333   struct dirent *finfo;
1334
1335   GNUNET_assert (iter->next_name == NULL);
1336   if (can == GNUNET_YES)
1337   {
1338     CLOSEDIR (iter->directory);
1339     GNUNET_free (iter->dirname);
1340     GNUNET_free (iter);
1341     return GNUNET_SYSERR;
1342   }
1343   while (NULL != (finfo = READDIR (iter->directory)))
1344   {
1345     if ((0 == strcmp (finfo->d_name, ".")) ||
1346         (0 == strcmp (finfo->d_name, "..")))
1347       continue;
1348     GNUNET_asprintf (&iter->next_name, "%s%s%s", iter->dirname,
1349                      DIR_SEPARATOR_STR, finfo->d_name);
1350     break;
1351   }
1352   if (finfo == NULL)
1353   {
1354     GNUNET_DISK_directory_iterator_next (iter, GNUNET_YES);
1355     return GNUNET_NO;
1356   }
1357   GNUNET_SCHEDULER_add_with_priority (iter->priority, &directory_iterator_task,
1358                                       iter);
1359   return GNUNET_YES;
1360 }
1361
1362
1363 /**
1364  * Scan a directory for files using the scheduler to run a task for
1365  * each entry.  The name of the directory must be expanded first (!).
1366  * If a scheduler does not need to be used, GNUNET_DISK_directory_scan
1367  * may provide a simpler API.
1368  *
1369  * @param prio priority to use
1370  * @param dirName the name of the directory
1371  * @param callback the method to call for each file
1372  * @param callback_cls closure for callback
1373  * @return GNUNET_YES if directory is not empty and 'callback'
1374  *         will be called later, GNUNET_NO otherwise, GNUNET_SYSERR on error.
1375  */
1376 int
1377 GNUNET_DISK_directory_iterator_start (enum GNUNET_SCHEDULER_Priority prio,
1378                                       const char *dirName,
1379                                       GNUNET_DISK_DirectoryIteratorCallback
1380                                       callback, void *callback_cls)
1381 {
1382   struct GNUNET_DISK_DirectoryIterator *di;
1383
1384   di = GNUNET_malloc (sizeof (struct GNUNET_DISK_DirectoryIterator));
1385   di->callback = callback;
1386   di->callback_cls = callback_cls;
1387   di->directory = OPENDIR (dirName);
1388   if (di->directory == NULL)
1389   {
1390     GNUNET_free (di);
1391     callback (callback_cls, NULL, NULL, NULL);
1392     return GNUNET_SYSERR;
1393   }
1394   di->dirname = GNUNET_strdup (dirName);
1395   di->priority = prio;
1396   return GNUNET_DISK_directory_iterator_next (di, GNUNET_NO);
1397 }
1398
1399
1400 /**
1401  * Function that removes the given directory by calling
1402  * "GNUNET_DISK_directory_remove".
1403  *
1404  * @param unused not used
1405  * @param fn directory to remove
1406  * @return GNUNET_OK
1407  */
1408 static int
1409 remove_helper (void *unused, const char *fn)
1410 {
1411   (void) GNUNET_DISK_directory_remove (fn);
1412   return GNUNET_OK;
1413 }
1414
1415
1416 /**
1417  * Remove all files in a directory (rm -rf). Call with
1418  * caution.
1419  *
1420  *
1421  * @param filename the file to remove
1422  * @return GNUNET_OK on success, GNUNET_SYSERR on error
1423  */
1424 int
1425 GNUNET_DISK_directory_remove (const char *filename)
1426 {
1427   struct stat istat;
1428
1429   if (0 != LSTAT (filename, &istat))
1430     return GNUNET_NO;           /* file may not exist... */
1431   (void) CHMOD (filename, S_IWUSR | S_IRUSR | S_IXUSR);
1432   if (UNLINK (filename) == 0)
1433     return GNUNET_OK;
1434   if ((errno != EISDIR) &&
1435       /* EISDIR is not sufficient in all cases, e.g.
1436        * sticky /tmp directory may result in EPERM on BSD.
1437        * So we also explicitly check "isDirectory" */
1438       (GNUNET_YES != GNUNET_DISK_directory_test (filename, GNUNET_YES)))
1439   {
1440     LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "rmdir", filename);
1441     return GNUNET_SYSERR;
1442   }
1443   if (GNUNET_SYSERR ==
1444       GNUNET_DISK_directory_scan (filename, &remove_helper, NULL))
1445     return GNUNET_SYSERR;
1446   if (0 != RMDIR (filename))
1447   {
1448     LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "rmdir", filename);
1449     return GNUNET_SYSERR;
1450   }
1451   return GNUNET_OK;
1452 }
1453
1454
1455 /**
1456  * Copy a file.
1457  *
1458  * @param src file to copy
1459  * @param dst destination file name
1460  * @return GNUNET_OK on success, GNUNET_SYSERR on error
1461  */
1462 int
1463 GNUNET_DISK_file_copy (const char *src, const char *dst)
1464 {
1465   char *buf;
1466   uint64_t pos;
1467   uint64_t size;
1468   size_t len;
1469   struct GNUNET_DISK_FileHandle *in;
1470   struct GNUNET_DISK_FileHandle *out;
1471
1472   if (GNUNET_OK != GNUNET_DISK_file_size (src, &size, GNUNET_YES, GNUNET_YES))
1473     return GNUNET_SYSERR;
1474   pos = 0;
1475   in = GNUNET_DISK_file_open (src, GNUNET_DISK_OPEN_READ,
1476                               GNUNET_DISK_PERM_NONE);
1477   if (!in)
1478     return GNUNET_SYSERR;
1479   out =
1480       GNUNET_DISK_file_open (dst,
1481                              GNUNET_DISK_OPEN_WRITE | GNUNET_DISK_OPEN_CREATE |
1482                              GNUNET_DISK_OPEN_FAILIFEXISTS,
1483                              GNUNET_DISK_PERM_USER_READ |
1484                              GNUNET_DISK_PERM_USER_WRITE |
1485                              GNUNET_DISK_PERM_GROUP_READ |
1486                              GNUNET_DISK_PERM_GROUP_WRITE);
1487   if (!out)
1488   {
1489     GNUNET_DISK_file_close (in);
1490     return GNUNET_SYSERR;
1491   }
1492   buf = GNUNET_malloc (COPY_BLK_SIZE);
1493   while (pos < size)
1494   {
1495     len = COPY_BLK_SIZE;
1496     if (len > size - pos)
1497       len = size - pos;
1498     if (len != GNUNET_DISK_file_read (in, buf, len))
1499       goto FAIL;
1500     if (len != GNUNET_DISK_file_write (out, buf, len))
1501       goto FAIL;
1502     pos += len;
1503   }
1504   GNUNET_free (buf);
1505   GNUNET_DISK_file_close (in);
1506   GNUNET_DISK_file_close (out);
1507   return GNUNET_OK;
1508 FAIL:
1509   GNUNET_free (buf);
1510   GNUNET_DISK_file_close (in);
1511   GNUNET_DISK_file_close (out);
1512   return GNUNET_SYSERR;
1513 }
1514
1515
1516 /**
1517  * @brief Removes special characters as ':' from a filename.
1518  * @param fn the filename to canonicalize
1519  */
1520 void
1521 GNUNET_DISK_filename_canonicalize (char *fn)
1522 {
1523   char *idx;
1524   char c;
1525
1526   idx = fn;
1527   while (*idx)
1528   {
1529     c = *idx;
1530
1531     if (c == '/' || c == '\\' || c == ':' || c == '*' || c == '?' || c == '"' ||
1532         c == '<' || c == '>' || c == '|')
1533     {
1534       *idx = '_';
1535     }
1536
1537     idx++;
1538   }
1539 }
1540
1541
1542
1543 /**
1544  * @brief Change owner of a file
1545  *
1546  * @param filename name of file to change the owner of
1547  * @param user name of the new owner
1548  * @return GNUNET_OK on success, GNUNET_SYSERR on failure
1549  */
1550 int
1551 GNUNET_DISK_file_change_owner (const char *filename, const char *user)
1552 {
1553 #ifndef MINGW
1554   struct passwd *pws;
1555
1556   pws = getpwnam (user);
1557   if (pws == NULL)
1558   {
1559     LOG (GNUNET_ERROR_TYPE_ERROR,
1560          _("Cannot obtain information about user `%s': %s\n"), user,
1561          STRERROR (errno));
1562     return GNUNET_SYSERR;
1563   }
1564   if (0 != chown (filename, pws->pw_uid, pws->pw_gid))
1565     LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "chown", filename);
1566 #endif
1567   return GNUNET_OK;
1568 }
1569
1570
1571 /**
1572  * Lock a part of a file
1573  * @param fh file handle
1574  * @param lockStart absolute position from where to lock
1575  * @param lockEnd absolute position until where to lock
1576  * @param excl GNUNET_YES for an exclusive lock
1577  * @return GNUNET_OK on success, GNUNET_SYSERR on error
1578  */
1579 int
1580 GNUNET_DISK_file_lock (struct GNUNET_DISK_FileHandle *fh, OFF_T lockStart,
1581                        OFF_T lockEnd, int excl)
1582 {
1583   if (fh == NULL)
1584   {
1585     errno = EINVAL;
1586     return GNUNET_SYSERR;
1587   }
1588
1589 #ifndef MINGW
1590   struct flock fl;
1591
1592   memset (&fl, 0, sizeof (struct flock));
1593   fl.l_type = excl ? F_WRLCK : F_RDLCK;
1594   fl.l_whence = SEEK_SET;
1595   fl.l_start = lockStart;
1596   fl.l_len = lockEnd;
1597
1598   return fcntl (fh->fd, F_SETLK, &fl) != 0 ? GNUNET_SYSERR : GNUNET_OK;
1599 #else
1600   OVERLAPPED o;
1601   OFF_T diff = lockEnd - lockStart;
1602   DWORD diff_low, diff_high;
1603   diff_low = (DWORD) (diff & 0xFFFFFFFF);
1604   diff_high = (DWORD) ((diff >> (sizeof (DWORD) * 8)) & 0xFFFFFFFF);
1605
1606   memset (&o, 0, sizeof (OVERLAPPED));
1607   o.Offset = (DWORD) (lockStart & 0xFFFFFFFF);;
1608   o.OffsetHigh = (DWORD) (((lockStart & ~0xFFFFFFFF) >> (sizeof (DWORD) * 8)) & 0xFFFFFFFF);
1609
1610   if (!LockFileEx
1611       (fh->h, (excl ? LOCKFILE_EXCLUSIVE_LOCK : 0) | LOCKFILE_FAIL_IMMEDIATELY,
1612        0, diff_low, diff_high, &o))
1613   {
1614     SetErrnoFromWinError (GetLastError ());
1615     return GNUNET_SYSERR;
1616   }
1617
1618   return GNUNET_OK;
1619 #endif
1620 }
1621
1622
1623 /**
1624  * Unlock a part of a file
1625  * @param fh file handle
1626  * @param unlockStart absolute position from where to unlock
1627  * @param unlockEnd absolute position until where to unlock
1628  * @return GNUNET_OK on success, GNUNET_SYSERR on error
1629  */
1630 int
1631 GNUNET_DISK_file_unlock (struct GNUNET_DISK_FileHandle *fh, OFF_T unlockStart,
1632                          OFF_T unlockEnd)
1633 {
1634   if (fh == NULL)
1635   {
1636     errno = EINVAL;
1637     return GNUNET_SYSERR;
1638   }
1639
1640 #ifndef MINGW
1641   struct flock fl;
1642
1643   memset (&fl, 0, sizeof (struct flock));
1644   fl.l_type = F_UNLCK;
1645   fl.l_whence = SEEK_SET;
1646   fl.l_start = unlockStart;
1647   fl.l_len = unlockEnd;
1648
1649   return fcntl (fh->fd, F_SETLK, &fl) != 0 ? GNUNET_SYSERR : GNUNET_OK;
1650 #else
1651   OVERLAPPED o;
1652   OFF_T diff = unlockEnd - unlockStart;
1653   DWORD diff_low, diff_high;
1654   diff_low = (DWORD) (diff & 0xFFFFFFFF);
1655   diff_high = (DWORD) ((diff >> (sizeof (DWORD) * 8)) & 0xFFFFFFFF);
1656
1657   memset (&o, 0, sizeof (OVERLAPPED));
1658   o.Offset = (DWORD) (unlockStart & 0xFFFFFFFF);;
1659   o.OffsetHigh = (DWORD) (((unlockStart & ~0xFFFFFFFF) >> (sizeof (DWORD) * 8)) & 0xFFFFFFFF);
1660
1661   if (!UnlockFileEx (fh->h, 0, diff_low, diff_high, &o))
1662   {
1663     SetErrnoFromWinError (GetLastError ());
1664     return GNUNET_SYSERR;
1665   }
1666
1667   return GNUNET_OK;
1668 #endif
1669 }
1670
1671
1672 /**
1673  * Open a file.  Note that the access permissions will only be
1674  * used if a new file is created and if the underlying operating
1675  * system supports the given permissions.
1676  *
1677  * @param fn file name to be opened
1678  * @param flags opening flags, a combination of GNUNET_DISK_OPEN_xxx bit flags
1679  * @param perm permissions for the newly created file, use
1680  *             GNUNET_DISK_PERM_USER_NONE if a file could not be created by this
1681  *             call (because of flags)
1682  * @return IO handle on success, NULL on error
1683  */
1684 struct GNUNET_DISK_FileHandle *
1685 GNUNET_DISK_file_open (const char *fn, enum GNUNET_DISK_OpenFlags flags,
1686                        enum GNUNET_DISK_AccessPermissions perm)
1687 {
1688   char *expfn;
1689   struct GNUNET_DISK_FileHandle *ret;
1690
1691 #ifdef MINGW
1692   DWORD access;
1693   DWORD disp;
1694   HANDLE h;
1695   wchar_t wexpfn[MAX_PATH + 1];
1696 #else
1697   int oflags;
1698   int mode;
1699   int fd;
1700 #endif
1701
1702   expfn = GNUNET_STRINGS_filename_expand (fn);
1703   if (NULL == expfn)
1704     return NULL;
1705 #ifndef MINGW
1706   mode = 0;
1707   if (GNUNET_DISK_OPEN_READWRITE == (flags & GNUNET_DISK_OPEN_READWRITE))
1708     oflags = O_RDWR;            /* note: O_RDWR is NOT always O_RDONLY | O_WRONLY */
1709   else if (flags & GNUNET_DISK_OPEN_READ)
1710     oflags = O_RDONLY;
1711   else if (flags & GNUNET_DISK_OPEN_WRITE)
1712     oflags = O_WRONLY;
1713   else
1714   {
1715     GNUNET_break (0);
1716     GNUNET_free (expfn);
1717     return NULL;
1718   }
1719   if (flags & GNUNET_DISK_OPEN_FAILIFEXISTS)
1720     oflags |= (O_CREAT | O_EXCL);
1721   if (flags & GNUNET_DISK_OPEN_TRUNCATE)
1722     oflags |= O_TRUNC;
1723   if (flags & GNUNET_DISK_OPEN_APPEND)
1724     oflags |= O_APPEND;
1725   if (flags & GNUNET_DISK_OPEN_CREATE)
1726   {
1727     (void) GNUNET_DISK_directory_create_for_file (expfn);
1728     oflags |= O_CREAT;
1729     mode = translate_unix_perms (perm);
1730   }
1731
1732   fd = open (expfn, oflags | O_LARGEFILE, mode);
1733   if (fd == -1)
1734   {
1735     if (0 == (flags & GNUNET_DISK_OPEN_FAILIFEXISTS))
1736       LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "open", expfn);
1737     else
1738       LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_DEBUG, "open", expfn);
1739     GNUNET_free (expfn);
1740     return NULL;
1741   }
1742 #else
1743   access = 0;
1744   disp = OPEN_ALWAYS;
1745
1746   if (GNUNET_DISK_OPEN_READWRITE == (flags & GNUNET_DISK_OPEN_READWRITE))
1747     access = FILE_READ_DATA | FILE_WRITE_DATA;
1748   else if (flags & GNUNET_DISK_OPEN_READ)
1749     access = FILE_READ_DATA;
1750   else if (flags & GNUNET_DISK_OPEN_WRITE)
1751     access = FILE_WRITE_DATA;
1752
1753   if (flags & GNUNET_DISK_OPEN_FAILIFEXISTS)
1754   {
1755     disp = CREATE_NEW;
1756   }
1757   else if (flags & GNUNET_DISK_OPEN_CREATE)
1758   {
1759     (void) GNUNET_DISK_directory_create_for_file (expfn);
1760     if (flags & GNUNET_DISK_OPEN_TRUNCATE)
1761       disp = CREATE_ALWAYS;
1762     else
1763       disp = OPEN_ALWAYS;
1764   }
1765   else if (flags & GNUNET_DISK_OPEN_TRUNCATE)
1766   {
1767     disp = TRUNCATE_EXISTING;
1768   }
1769   else
1770   {
1771     disp = OPEN_EXISTING;
1772   }
1773
1774   if (ERROR_SUCCESS == plibc_conv_to_win_pathwconv(expfn, wexpfn))
1775     h = CreateFileW (wexpfn, access,
1776                     FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE, NULL,
1777                     disp, FILE_ATTRIBUTE_NORMAL, NULL);
1778   else
1779     h = INVALID_HANDLE_VALUE;
1780   if (h == INVALID_HANDLE_VALUE)
1781   {
1782     int err;
1783     SetErrnoFromWinError (GetLastError ());
1784     err = errno;
1785     LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_INFO, "open", expfn);
1786     GNUNET_free (expfn);
1787     errno = err;
1788     return NULL;
1789   }
1790
1791   if (flags & GNUNET_DISK_OPEN_APPEND)
1792     if (SetFilePointer (h, 0, 0, FILE_END) == INVALID_SET_FILE_POINTER)
1793     {
1794       SetErrnoFromWinError (GetLastError ());
1795       LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "SetFilePointer", expfn);
1796       CloseHandle (h);
1797       GNUNET_free (expfn);
1798       return NULL;
1799     }
1800 #endif
1801
1802   ret = GNUNET_malloc (sizeof (struct GNUNET_DISK_FileHandle));
1803 #ifdef MINGW
1804   ret->h = h;
1805   ret->type = GNUNET_DISK_HANLDE_TYPE_FILE;
1806 #else
1807   ret->fd = fd;
1808 #endif
1809   GNUNET_free (expfn);
1810   return ret;
1811 }
1812
1813
1814 /**
1815  * Close an open file
1816  * @param h file handle
1817  * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
1818  */
1819 int
1820 GNUNET_DISK_file_close (struct GNUNET_DISK_FileHandle *h)
1821 {
1822   int ret;
1823   if (h == NULL)
1824   {
1825     errno = EINVAL;
1826     return GNUNET_SYSERR;
1827   }
1828
1829   ret = GNUNET_OK;
1830
1831 #if MINGW
1832   if (!CloseHandle (h->h))
1833   {
1834     SetErrnoFromWinError (GetLastError ());
1835     LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING, "close");
1836     ret = GNUNET_SYSERR;
1837   }
1838   if (h->oOverlapRead)
1839   {
1840     if (!CloseHandle (h->oOverlapRead->hEvent))
1841     {
1842       SetErrnoFromWinError (GetLastError ());
1843       LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING, "close");
1844       ret = GNUNET_SYSERR;
1845     }
1846     GNUNET_free (h->oOverlapRead);
1847   }
1848   if (h->oOverlapWrite)
1849   {
1850     if (!CloseHandle (h->oOverlapWrite->hEvent))
1851     {
1852       SetErrnoFromWinError (GetLastError ());
1853       LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING, "close");
1854       ret = GNUNET_SYSERR;
1855     }
1856     GNUNET_free (h->oOverlapWrite);
1857   }
1858 #else
1859   if (close (h->fd) != 0)
1860   {
1861     LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING, "close");
1862     ret = GNUNET_SYSERR;
1863   }
1864 #endif
1865   GNUNET_free (h);
1866   return ret;
1867 }
1868
1869 #ifdef WINDOWS
1870 /**
1871  * Get a GNUnet file handle from a W32 handle.
1872  *
1873  * @param handle native handle
1874  * @return GNUnet file handle corresponding to the W32 handle
1875  */
1876 struct GNUNET_DISK_FileHandle *
1877 GNUNET_DISK_get_handle_from_w32_handle (HANDLE osfh)
1878 {
1879   struct GNUNET_DISK_FileHandle *fh;
1880
1881   DWORD dwret;
1882   enum GNUNET_FILE_Type ftype;
1883
1884   dwret = GetFileType (osfh);
1885   switch (dwret)
1886   {
1887   case FILE_TYPE_DISK:
1888     ftype = GNUNET_DISK_HANLDE_TYPE_FILE;
1889     break;
1890   case FILE_TYPE_PIPE:
1891     ftype = GNUNET_DISK_HANLDE_TYPE_PIPE;
1892     break;
1893   default:
1894     return NULL;
1895   }
1896
1897   fh = GNUNET_malloc (sizeof (struct GNUNET_DISK_FileHandle));
1898
1899   fh->h = osfh;
1900   fh->type = ftype;
1901   if (ftype == GNUNET_DISK_HANLDE_TYPE_PIPE)
1902   {
1903     /**
1904      * Note that we can't make it overlapped if it isn't already.
1905      * (ReOpenFile() is only available in 2003/Vista).
1906      * The process that opened this file in the first place (usually a parent
1907      * process, if this is stdin/stdout/stderr) must make it overlapped,
1908      * otherwise we're screwed, as selecting on non-overlapped handle
1909      * will block.
1910      */
1911     fh->oOverlapRead = GNUNET_malloc (sizeof (OVERLAPPED));
1912     fh->oOverlapWrite = GNUNET_malloc (sizeof (OVERLAPPED));
1913     fh->oOverlapRead->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
1914     fh->oOverlapWrite->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
1915   }
1916
1917   return fh;
1918 }
1919 #endif
1920
1921 /**
1922  * Get a handle from a native integer FD.
1923  *
1924  * @param fd native integer file descriptor
1925  * @return file handle corresponding to the descriptor
1926  */
1927 struct GNUNET_DISK_FileHandle *
1928 GNUNET_DISK_get_handle_from_int_fd (int fno)
1929 {
1930   struct GNUNET_DISK_FileHandle *fh;
1931
1932 #ifndef WINDOWS
1933   fh = GNUNET_malloc (sizeof (struct GNUNET_DISK_FileHandle));
1934
1935   fh->fd = fno;
1936 #else
1937   intptr_t osfh;
1938
1939   osfh = _get_osfhandle (fno);
1940   if (INVALID_HANDLE_VALUE == (HANDLE) osfh)
1941     return NULL;
1942
1943   fh = GNUNET_DISK_get_handle_from_w32_handle ((HANDLE) osfh);
1944 #endif
1945
1946   return fh;
1947 }
1948
1949
1950 /**
1951  * Get a handle from a native streaming FD.
1952  *
1953  * @param fd native streaming file descriptor
1954  * @return file handle corresponding to the descriptor
1955  */
1956 struct GNUNET_DISK_FileHandle *
1957 GNUNET_DISK_get_handle_from_native (FILE *fd)
1958 {
1959   int fno;
1960
1961   fno = fileno (fd);
1962   if (-1 == fno)
1963     return NULL;
1964
1965   return GNUNET_DISK_get_handle_from_int_fd (fno);
1966 }
1967
1968
1969 /**
1970  * Construct full path to a file inside of the private
1971  * directory used by GNUnet.  Also creates the corresponding
1972  * directory.  If the resulting name is supposed to be
1973  * a directory, end the last argument in '/' (or pass
1974  * DIR_SEPARATOR_STR as the last argument before NULL).
1975  *
1976  * @param cfg configuration to use (determines HOME)
1977  * @param serviceName name of the service
1978  * @param ... is NULL-terminated list of
1979  *                path components to append to the
1980  *                private directory name.
1981  * @return the constructed filename
1982  */
1983 char *
1984 GNUNET_DISK_get_home_filename (const struct GNUNET_CONFIGURATION_Handle *cfg,
1985                                const char *serviceName, ...)
1986 {
1987   const char *c;
1988   char *pfx;
1989   char *ret;
1990   va_list ap;
1991   unsigned int needed;
1992
1993   if (GNUNET_OK !=
1994       GNUNET_CONFIGURATION_get_value_filename (cfg, serviceName, "HOME", &pfx))
1995     return NULL;
1996   if (pfx == NULL)
1997   {
1998     LOG (GNUNET_ERROR_TYPE_WARNING,
1999          _("No `%s' specified for service `%s' in configuration.\n"), "HOME",
2000          serviceName);
2001     return NULL;
2002   }
2003   needed = strlen (pfx) + 2;
2004   if ((pfx[strlen (pfx) - 1] != '/') && (pfx[strlen (pfx) - 1] != '\\'))
2005     needed++;
2006   va_start (ap, serviceName);
2007   while (1)
2008   {
2009     c = va_arg (ap, const char *);
2010
2011     if (c == NULL)
2012       break;
2013     needed += strlen (c);
2014     if ((c[strlen (c) - 1] != '/') && (c[strlen (c) - 1] != '\\'))
2015       needed++;
2016   }
2017   va_end (ap);
2018   ret = GNUNET_malloc (needed);
2019   strcpy (ret, pfx);
2020   GNUNET_free (pfx);
2021   va_start (ap, serviceName);
2022   while (1)
2023   {
2024     c = va_arg (ap, const char *);
2025
2026     if (c == NULL)
2027       break;
2028     if ((c[strlen (c) - 1] != '/') && (c[strlen (c) - 1] != '\\'))
2029       strcat (ret, DIR_SEPARATOR_STR);
2030     strcat (ret, c);
2031   }
2032   va_end (ap);
2033   if ((ret[strlen (ret) - 1] != '/') && (ret[strlen (ret) - 1] != '\\'))
2034     (void) GNUNET_DISK_directory_create_for_file (ret);
2035   else
2036     (void) GNUNET_DISK_directory_create (ret);
2037   return ret;
2038 }
2039
2040
2041 /**
2042  * Handle for a memory-mapping operation.
2043  */
2044 struct GNUNET_DISK_MapHandle
2045 {
2046   /**
2047    * Address where the map is in memory.
2048    */
2049   void *addr;
2050
2051 #ifdef MINGW
2052   /**
2053    * Underlying OS handle.
2054    */
2055   HANDLE h;
2056 #else
2057   /**
2058    * Number of bytes mapped.
2059    */
2060   size_t len;
2061 #endif
2062 };
2063
2064
2065 #ifndef MAP_FAILED
2066 #define MAP_FAILED ((void *) -1)
2067 #endif
2068
2069 /**
2070  * Map a file into memory
2071  *
2072  * @param h open file handle
2073  * @param m handle to the new mapping
2074  * @param access access specification, GNUNET_DISK_MAP_TYPE_xxx
2075  * @param len size of the mapping
2076  * @return pointer to the mapped memory region, NULL on failure
2077  */
2078 void *
2079 GNUNET_DISK_file_map (const struct GNUNET_DISK_FileHandle *h,
2080                       struct GNUNET_DISK_MapHandle **m,
2081                       enum GNUNET_DISK_MapType access, size_t len)
2082 {
2083   if (h == NULL)
2084   {
2085     errno = EINVAL;
2086     return NULL;
2087   }
2088
2089 #ifdef MINGW
2090   DWORD mapAccess, protect;
2091
2092   if ((access & GNUNET_DISK_MAP_TYPE_READ) &&
2093       (access & GNUNET_DISK_MAP_TYPE_WRITE))
2094   {
2095     protect = PAGE_READWRITE;
2096     mapAccess = FILE_MAP_ALL_ACCESS;
2097   }
2098   else if (access & GNUNET_DISK_MAP_TYPE_READ)
2099   {
2100     protect = PAGE_READONLY;
2101     mapAccess = FILE_MAP_READ;
2102   }
2103   else if (access & GNUNET_DISK_MAP_TYPE_WRITE)
2104   {
2105     protect = PAGE_READWRITE;
2106     mapAccess = FILE_MAP_WRITE;
2107   }
2108   else
2109   {
2110     GNUNET_break (0);
2111     return NULL;
2112   }
2113
2114   *m = GNUNET_malloc (sizeof (struct GNUNET_DISK_MapHandle));
2115   (*m)->h = CreateFileMapping (h->h, NULL, protect, 0, 0, NULL);
2116   if ((*m)->h == INVALID_HANDLE_VALUE)
2117   {
2118     SetErrnoFromWinError (GetLastError ());
2119     GNUNET_free (*m);
2120     return NULL;
2121   }
2122
2123   (*m)->addr = MapViewOfFile ((*m)->h, mapAccess, 0, 0, len);
2124   if (!(*m)->addr)
2125   {
2126     SetErrnoFromWinError (GetLastError ());
2127     CloseHandle ((*m)->h);
2128     GNUNET_free (*m);
2129   }
2130
2131   return (*m)->addr;
2132 #else
2133   int prot;
2134
2135   prot = 0;
2136   if (access & GNUNET_DISK_MAP_TYPE_READ)
2137     prot = PROT_READ;
2138   if (access & GNUNET_DISK_MAP_TYPE_WRITE)
2139     prot |= PROT_WRITE;
2140   *m = GNUNET_malloc (sizeof (struct GNUNET_DISK_MapHandle));
2141   (*m)->addr = mmap (NULL, len, prot, MAP_SHARED, h->fd, 0);
2142   GNUNET_assert (NULL != (*m)->addr);
2143   if (MAP_FAILED == (*m)->addr)
2144   {
2145     GNUNET_free (*m);
2146     return NULL;
2147   }
2148   (*m)->len = len;
2149   return (*m)->addr;
2150 #endif
2151 }
2152
2153 /**
2154  * Unmap a file
2155  * @param h mapping handle
2156  * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
2157  */
2158 int
2159 GNUNET_DISK_file_unmap (struct GNUNET_DISK_MapHandle *h)
2160 {
2161   int ret;
2162
2163   if (h == NULL)
2164   {
2165     errno = EINVAL;
2166     return GNUNET_SYSERR;
2167   }
2168
2169 #ifdef MINGW
2170   ret = UnmapViewOfFile (h->addr) ? GNUNET_OK : GNUNET_SYSERR;
2171   if (ret != GNUNET_OK)
2172     SetErrnoFromWinError (GetLastError ());
2173   if (!CloseHandle (h->h) && (ret == GNUNET_OK))
2174   {
2175     ret = GNUNET_SYSERR;
2176     SetErrnoFromWinError (GetLastError ());
2177   }
2178 #else
2179   ret = munmap (h->addr, h->len) != -1 ? GNUNET_OK : GNUNET_SYSERR;
2180 #endif
2181   GNUNET_free (h);
2182   return ret;
2183 }
2184
2185
2186 /**
2187  * Write file changes to disk
2188  * @param h handle to an open file
2189  * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
2190  */
2191 int
2192 GNUNET_DISK_file_sync (const struct GNUNET_DISK_FileHandle *h)
2193 {
2194   if (h == NULL)
2195   {
2196     errno = EINVAL;
2197     return GNUNET_SYSERR;
2198   }
2199
2200 #ifdef MINGW
2201   int ret;
2202
2203   ret = FlushFileBuffers (h->h) ? GNUNET_OK : GNUNET_SYSERR;
2204   if (ret != GNUNET_OK)
2205     SetErrnoFromWinError (GetLastError ());
2206   return ret;
2207 #elif defined(FREEBSD) || defined(OPENBSD) || defined(DARWIN)
2208   return fsync (h->fd) == -1 ? GNUNET_SYSERR : GNUNET_OK;
2209 #else
2210   return fdatasync (h->fd) == -1 ? GNUNET_SYSERR : GNUNET_OK;
2211 #endif
2212 }
2213
2214
2215 #if WINDOWS
2216 /* Copyright Bob Byrnes  <byrnes <at> curl.com>
2217    http://permalink.gmane.org/gmane.os.cygwin.patches/2121
2218 */
2219 /* Create a pipe, and return handles to the read and write ends,
2220    just like CreatePipe, but ensure that the write end permits
2221    FILE_READ_ATTRIBUTES access, on later versions of win32 where
2222    this is supported.  This access is needed by NtQueryInformationFile,
2223    which is used to implement select and nonblocking writes.
2224    Note that the return value is either NO_ERROR or GetLastError,
2225    unlike CreatePipe, which returns a bool for success or failure.  */
2226 static int
2227 create_selectable_pipe (PHANDLE read_pipe_ptr, PHANDLE write_pipe_ptr,
2228                         LPSECURITY_ATTRIBUTES sa_ptr, DWORD psize,
2229                         DWORD dwReadMode, DWORD dwWriteMode)
2230 {
2231   /* Default to error. */
2232   *read_pipe_ptr = *write_pipe_ptr = INVALID_HANDLE_VALUE;
2233
2234   HANDLE read_pipe;
2235   HANDLE write_pipe;
2236
2237   /* Ensure that there is enough pipe buffer space for atomic writes.  */
2238   if (psize < PIPE_BUF)
2239     psize = PIPE_BUF;
2240
2241   char pipename[MAX_PATH];
2242
2243   /* Retry CreateNamedPipe as long as the pipe name is in use.
2244    * Retrying will probably never be necessary, but we want
2245    * to be as robust as possible.  */
2246   while (1)
2247   {
2248     static volatile LONG pipe_unique_id;
2249
2250     snprintf (pipename, sizeof pipename, "\\\\.\\pipe\\gnunet-%d-%ld",
2251               getpid (), InterlockedIncrement ((LONG *) & pipe_unique_id));
2252     LOG (GNUNET_ERROR_TYPE_DEBUG, "CreateNamedPipe: name = %s, size = %lu\n",
2253          pipename, psize);
2254     /* Use CreateNamedPipe instead of CreatePipe, because the latter
2255      * returns a write handle that does not permit FILE_READ_ATTRIBUTES
2256      * access, on versions of win32 earlier than WinXP SP2.
2257      * CreatePipe also stupidly creates a full duplex pipe, which is
2258      * a waste, since only a single direction is actually used.
2259      * It's important to only allow a single instance, to ensure that
2260      * the pipe was not created earlier by some other process, even if
2261      * the pid has been reused.  */
2262     read_pipe = CreateNamedPipeA (pipename, PIPE_ACCESS_INBOUND | FILE_FLAG_FIRST_PIPE_INSTANCE | dwReadMode, PIPE_TYPE_BYTE | PIPE_READMODE_BYTE, 1,   /* max instances */
2263                                   psize,        /* output buffer size */
2264                                   psize,        /* input buffer size */
2265                                   NMPWAIT_USE_DEFAULT_WAIT, sa_ptr);
2266
2267     if (read_pipe != INVALID_HANDLE_VALUE)
2268     {
2269       LOG (GNUNET_ERROR_TYPE_DEBUG, "pipe read handle = %p\n", read_pipe);
2270       break;
2271     }
2272
2273     DWORD err = GetLastError ();
2274
2275     switch (err)
2276     {
2277     case ERROR_PIPE_BUSY:
2278       /* The pipe is already open with compatible parameters.
2279        * Pick a new name and retry.  */
2280       LOG (GNUNET_ERROR_TYPE_DEBUG, "pipe busy, retrying\n");
2281       continue;
2282     case ERROR_ACCESS_DENIED:
2283       /* The pipe is already open with incompatible parameters.
2284        * Pick a new name and retry.  */
2285       LOG (GNUNET_ERROR_TYPE_DEBUG, "pipe access denied, retrying\n");
2286       continue;
2287     case ERROR_CALL_NOT_IMPLEMENTED:
2288       /* We are on an older Win9x platform without named pipes.
2289        * Return an anonymous pipe as the best approximation.  */
2290       LOG (GNUNET_ERROR_TYPE_DEBUG,
2291            "CreateNamedPipe not implemented, resorting to "
2292            "CreatePipe: size = %lu\n", psize);
2293       if (CreatePipe (read_pipe_ptr, write_pipe_ptr, sa_ptr, psize))
2294       {
2295         LOG (GNUNET_ERROR_TYPE_DEBUG, "pipe read handle = %p, write handle = %p\n",
2296              *read_pipe_ptr,
2297              *write_pipe_ptr);
2298         return GNUNET_OK;
2299       }
2300       err = GetLastError ();
2301       LOG (GNUNET_ERROR_TYPE_ERROR, "CreatePipe failed: %d\n", err);
2302       return err;
2303     default:
2304       LOG (GNUNET_ERROR_TYPE_ERROR, "CreateNamedPipe failed: %d\n", err);
2305       return err;
2306     }
2307     /* NOTREACHED */
2308   }
2309   LOG (GNUNET_ERROR_TYPE_DEBUG, "CreateFile: name = %s\n", pipename);
2310
2311   /* Open the named pipe for writing.
2312    * Be sure to permit FILE_READ_ATTRIBUTES access.  */
2313   write_pipe = CreateFileA (pipename, GENERIC_WRITE | FILE_READ_ATTRIBUTES, 0,  /* share mode */
2314                             sa_ptr, OPEN_EXISTING, dwWriteMode, /* flags and attributes */
2315                             0); /* handle to template file */
2316
2317   if (write_pipe == INVALID_HANDLE_VALUE)
2318   {
2319     /* Failure. */
2320     DWORD err = GetLastError ();
2321
2322     LOG (GNUNET_ERROR_TYPE_DEBUG, "CreateFile failed: %d\n", err);
2323     CloseHandle (read_pipe);
2324     return err;
2325   }
2326   LOG (GNUNET_ERROR_TYPE_DEBUG, "pipe write handle = %p\n", write_pipe);
2327   /* Success. */
2328   *read_pipe_ptr = read_pipe;
2329   *write_pipe_ptr = write_pipe;
2330   return GNUNET_OK;
2331 }
2332 #endif
2333
2334
2335 /**
2336  * Creates an interprocess channel
2337  *
2338  * @param blocking_read creates an asynchronous pipe for reading if set to GNUNET_NO
2339  * @param blocking_write creates an asynchronous pipe for writing if set to GNUNET_NO
2340  * @param inherit_read inherit the parent processes stdin (only for windows)
2341  * @param inherit_write inherit the parent processes stdout (only for windows)
2342  * @return handle to the new pipe, NULL on error
2343  */
2344 struct GNUNET_DISK_PipeHandle *
2345 GNUNET_DISK_pipe (int blocking_read, int blocking_write, int inherit_read, int inherit_write)
2346 {
2347 #ifndef MINGW
2348   int fd[2];
2349   int ret;
2350   int eno;
2351
2352   ret = pipe (fd);
2353   if (ret == -1)
2354   {
2355     eno = errno;
2356     LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR, "pipe");
2357     errno = eno;
2358     return NULL;
2359   }
2360   return GNUNET_DISK_pipe_from_fd (blocking_read,
2361                                    blocking_write,
2362                                    fd);
2363 #else
2364   struct GNUNET_DISK_PipeHandle *p;
2365   BOOL ret;
2366   HANDLE tmp_handle;
2367   int save_errno;
2368
2369   p = GNUNET_malloc (sizeof (struct GNUNET_DISK_PipeHandle));
2370   p->fd[0] = GNUNET_malloc (sizeof (struct GNUNET_DISK_FileHandle));
2371   p->fd[1] = GNUNET_malloc (sizeof (struct GNUNET_DISK_FileHandle));
2372
2373   /* All pipes are overlapped. If you want them to block - just
2374    * call WriteFile() and ReadFile() with NULL overlapped pointer.
2375    * NOTE: calling with NULL overlapped pointer works only
2376    * for pipes, and doesn't seem to be a documented feature.
2377    * It will NOT work for files, because overlapped files need
2378    * to read offsets from the overlapped structure, regardless.
2379    * Pipes are not seekable, and need no offsets, which is
2380    * probably why it works for them.
2381    */
2382   ret =
2383       create_selectable_pipe (&p->fd[0]->h, &p->fd[1]->h, NULL, 0,
2384                               FILE_FLAG_OVERLAPPED,
2385                               FILE_FLAG_OVERLAPPED);
2386   if (!ret)
2387   {
2388     SetErrnoFromWinError (GetLastError ());
2389     save_errno = errno;
2390     GNUNET_free (p->fd[0]);
2391     GNUNET_free (p->fd[1]);
2392     GNUNET_free (p);
2393     errno = save_errno;
2394     return NULL;
2395   }
2396   if (!DuplicateHandle
2397       (GetCurrentProcess (), p->fd[0]->h, GetCurrentProcess (), &tmp_handle, 0,
2398        inherit_read == GNUNET_YES ? TRUE : FALSE, DUPLICATE_SAME_ACCESS))
2399   {
2400     SetErrnoFromWinError (GetLastError ());
2401     save_errno = errno;
2402     CloseHandle (p->fd[0]->h);
2403     CloseHandle (p->fd[1]->h);
2404     GNUNET_free (p->fd[0]);
2405     GNUNET_free (p->fd[1]);
2406     GNUNET_free (p);
2407     errno = save_errno;
2408     return NULL;
2409   }
2410   CloseHandle (p->fd[0]->h);
2411   p->fd[0]->h = tmp_handle;
2412
2413   if (!DuplicateHandle
2414       (GetCurrentProcess (), p->fd[1]->h, GetCurrentProcess (), &tmp_handle, 0,
2415        inherit_write == GNUNET_YES ? TRUE : FALSE, DUPLICATE_SAME_ACCESS))
2416   {
2417     SetErrnoFromWinError (GetLastError ());
2418     save_errno = errno;
2419     CloseHandle (p->fd[0]->h);
2420     CloseHandle (p->fd[1]->h);
2421     GNUNET_free (p->fd[0]);
2422     GNUNET_free (p->fd[1]);
2423     GNUNET_free (p);
2424     errno = save_errno;
2425     return NULL;
2426   }
2427   CloseHandle (p->fd[1]->h);
2428   p->fd[1]->h = tmp_handle;
2429
2430   p->fd[0]->type = GNUNET_DISK_HANLDE_TYPE_PIPE;
2431   p->fd[1]->type = GNUNET_DISK_HANLDE_TYPE_PIPE;
2432
2433   p->fd[0]->oOverlapRead = GNUNET_malloc (sizeof (OVERLAPPED));
2434   p->fd[0]->oOverlapWrite = GNUNET_malloc (sizeof (OVERLAPPED));
2435   p->fd[1]->oOverlapRead = GNUNET_malloc (sizeof (OVERLAPPED));
2436   p->fd[1]->oOverlapWrite = GNUNET_malloc (sizeof (OVERLAPPED));
2437
2438   p->fd[0]->oOverlapRead->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2439   p->fd[0]->oOverlapWrite->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2440
2441   p->fd[1]->oOverlapRead->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2442   p->fd[1]->oOverlapWrite->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2443
2444   return p;
2445 #endif
2446 }
2447
2448
2449 /**
2450  * Creates a pipe object from a couple of file descriptors.
2451  * Useful for wrapping existing pipe FDs.
2452  *
2453  * @param blocking_read creates an asynchronous pipe for reading if set to GNUNET_NO
2454  * @param blocking_write creates an asynchronous pipe for writing if set to GNUNET_NO
2455  * @param fd an array of two fd values. One of them may be -1 for read-only or write-only pipes
2456  *
2457  * @return handle to the new pipe, NULL on error
2458  */
2459 struct GNUNET_DISK_PipeHandle *
2460 GNUNET_DISK_pipe_from_fd (int blocking_read, int blocking_write, int fd[2])
2461 {
2462   struct GNUNET_DISK_PipeHandle *p;
2463
2464   p = GNUNET_malloc (sizeof (struct GNUNET_DISK_PipeHandle));
2465
2466 #ifndef MINGW
2467   int ret;
2468   int flags;
2469   int eno = 0; /* make gcc happy */
2470
2471   ret = 0;
2472   if (fd[0] >= 0)
2473   {
2474     p->fd[0] = GNUNET_malloc (sizeof (struct GNUNET_DISK_FileHandle));
2475     p->fd[0]->fd = fd[0];
2476     if (!blocking_read)
2477     {
2478       flags = fcntl (fd[0], F_GETFL);
2479       flags |= O_NONBLOCK;
2480       if (0 > fcntl (fd[0], F_SETFL, flags))
2481       {
2482         ret = -1;
2483         eno = errno;
2484       }
2485     }
2486     flags = fcntl (fd[0], F_GETFD);
2487     flags |= FD_CLOEXEC;
2488     if (0 > fcntl (fd[0], F_SETFD, flags))
2489     {
2490       ret = -1;
2491       eno = errno;
2492     }
2493   }
2494
2495   if (fd[1] >= 0)
2496   {
2497     p->fd[1] = GNUNET_malloc (sizeof (struct GNUNET_DISK_FileHandle));
2498     p->fd[1]->fd = fd[1];
2499     if (!blocking_write)
2500     {
2501       flags = fcntl (fd[1], F_GETFL);
2502       flags |= O_NONBLOCK;
2503       if (0 > fcntl (fd[1], F_SETFL, flags))
2504       {
2505         ret = -1;
2506         eno = errno;
2507       }
2508     }
2509     flags = fcntl (fd[1], F_GETFD);
2510     flags |= FD_CLOEXEC;
2511     if (0 > fcntl (fd[1], F_SETFD, flags))
2512     {
2513       ret = -1;
2514       eno = errno;
2515     }
2516   }
2517   if (ret == -1)
2518   {
2519     errno = eno;
2520     LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR, "fcntl");
2521     if (p->fd[0]->fd >= 0)
2522       GNUNET_break (0 == close (p->fd[0]->fd));
2523     if (p->fd[1]->fd >= 0)
2524       GNUNET_break (0 == close (p->fd[1]->fd));
2525     GNUNET_free_non_null (p->fd[0]);
2526     GNUNET_free_non_null (p->fd[1]);
2527     GNUNET_free (p);
2528     errno = eno;
2529     return NULL;
2530   }
2531 #else
2532   if (fd[0] >= 0)
2533   {
2534     p->fd[0] = GNUNET_malloc (sizeof (struct GNUNET_DISK_FileHandle));
2535     p->fd[0]->h = (HANDLE) _get_osfhandle (fd[0]);
2536     if (p->fd[0]->h != INVALID_HANDLE_VALUE)
2537     {
2538       p->fd[0]->type = GNUNET_DISK_HANLDE_TYPE_PIPE;
2539       p->fd[0]->oOverlapRead = GNUNET_malloc (sizeof (OVERLAPPED));
2540       p->fd[0]->oOverlapWrite = GNUNET_malloc (sizeof (OVERLAPPED));
2541       p->fd[0]->oOverlapRead->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2542       p->fd[0]->oOverlapWrite->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2543     }
2544     else
2545     {
2546       GNUNET_free (p->fd[0]);
2547       p->fd[0] = NULL;
2548     }
2549   }
2550   if (fd[1] >= 0)
2551   {
2552     p->fd[1] = GNUNET_malloc (sizeof (struct GNUNET_DISK_FileHandle));
2553     p->fd[1]->h = (HANDLE) _get_osfhandle (fd[1]);
2554     if (p->fd[1]->h != INVALID_HANDLE_VALUE)
2555     {
2556       p->fd[1]->type = GNUNET_DISK_HANLDE_TYPE_PIPE;
2557       p->fd[1]->oOverlapRead = GNUNET_malloc (sizeof (OVERLAPPED));
2558       p->fd[1]->oOverlapWrite = GNUNET_malloc (sizeof (OVERLAPPED));
2559       p->fd[1]->oOverlapRead->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2560       p->fd[1]->oOverlapWrite->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2561     }
2562     else
2563     {
2564       GNUNET_free (p->fd[1]);
2565       p->fd[1] = NULL;
2566     }
2567   }
2568
2569 #endif
2570   return p;
2571 }
2572
2573
2574 /**
2575  * Closes an interprocess channel
2576  *
2577  * @param p pipe to close
2578  * @param end which end of the pipe to close
2579  * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
2580  */
2581 int
2582 GNUNET_DISK_pipe_close_end (struct GNUNET_DISK_PipeHandle *p,
2583                             enum GNUNET_DISK_PipeEnd end)
2584 {
2585   int ret = GNUNET_OK;
2586
2587   if (end == GNUNET_DISK_PIPE_END_READ)
2588   {
2589     if (p->fd[0])
2590     {
2591       ret = GNUNET_DISK_file_close (p->fd[0]);
2592       p->fd[0] = NULL;
2593     }
2594   }
2595   else if (end == GNUNET_DISK_PIPE_END_WRITE)
2596   {
2597     if (p->fd[1])
2598     {
2599       ret = GNUNET_DISK_file_close (p->fd[1]);
2600       p->fd[1] = NULL;
2601     }
2602   }
2603
2604   return ret;
2605 }
2606
2607 /**
2608  * Detaches one of the ends from the pipe.
2609  * Detached end is a fully-functional FileHandle, it will
2610  * not be affected by anything you do with the pipe afterwards.
2611  * Each end of a pipe can only be detched from it once (i.e.
2612  * it is not duplicated).
2613  *
2614  * @param p pipe to detach an end from
2615  * @param end which end of the pipe to detach
2616  * @return Detached end on success, NULL on failure
2617  * (or if that end is not present or is closed).
2618  */
2619 struct GNUNET_DISK_FileHandle *
2620 GNUNET_DISK_pipe_detach_end (struct GNUNET_DISK_PipeHandle *p,
2621                              enum GNUNET_DISK_PipeEnd end)
2622 {
2623   struct GNUNET_DISK_FileHandle *ret = NULL;
2624
2625   if (end == GNUNET_DISK_PIPE_END_READ)
2626   {
2627     if (p->fd[0])
2628     {
2629       ret = p->fd[0];
2630       p->fd[0] = NULL;
2631     }
2632   }
2633   else if (end == GNUNET_DISK_PIPE_END_WRITE)
2634   {
2635     if (p->fd[1])
2636     {
2637       ret = p->fd[1];
2638       p->fd[1] = NULL;
2639     }
2640   }
2641
2642   return ret;
2643 }
2644
2645
2646 /**
2647  * Closes an interprocess channel
2648  *
2649  * @param p pipe to close
2650  * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
2651  */
2652 int
2653 GNUNET_DISK_pipe_close (struct GNUNET_DISK_PipeHandle *p)
2654 {
2655   int ret = GNUNET_OK;
2656
2657   int read_end_close;
2658   int write_end_close;
2659   int read_end_close_errno;
2660   int write_end_close_errno;
2661
2662   read_end_close = GNUNET_DISK_pipe_close_end (p, GNUNET_DISK_PIPE_END_READ);
2663   read_end_close_errno = errno;
2664   write_end_close = GNUNET_DISK_pipe_close_end (p, GNUNET_DISK_PIPE_END_WRITE);
2665   write_end_close_errno = errno;
2666   GNUNET_free (p);
2667
2668   if (GNUNET_OK != read_end_close)
2669   {
2670     errno = read_end_close_errno;
2671     ret = read_end_close;
2672   }
2673   else if (GNUNET_OK != write_end_close)
2674   {
2675     errno = write_end_close_errno;
2676     ret = write_end_close;
2677   }
2678
2679   return ret;
2680 }
2681
2682
2683 /**
2684  * Get the handle to a particular pipe end
2685  *
2686  * @param p pipe
2687  * @param n end to access
2688  * @return handle for the respective end
2689  */
2690 const struct GNUNET_DISK_FileHandle *
2691 GNUNET_DISK_pipe_handle (const struct GNUNET_DISK_PipeHandle *p,
2692                          enum GNUNET_DISK_PipeEnd n)
2693 {
2694   switch (n)
2695   {
2696   case GNUNET_DISK_PIPE_END_READ:
2697   case GNUNET_DISK_PIPE_END_WRITE:
2698     return p->fd[n];
2699   default:
2700     GNUNET_break (0);
2701     return NULL;
2702   }
2703 }
2704
2705
2706 /**
2707  * Retrieve OS file handle
2708  * @internal
2709  * @param fh GNUnet file descriptor
2710  * @param dst destination buffer
2711  * @param dst_len length of dst
2712  * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
2713  */
2714 int
2715 GNUNET_DISK_internal_file_handle_ (const struct GNUNET_DISK_FileHandle *fh,
2716                                    void *dst, size_t dst_len)
2717 {
2718 #ifdef MINGW
2719   if (dst_len < sizeof (HANDLE))
2720     return GNUNET_SYSERR;
2721   *((HANDLE *) dst) = fh->h;
2722 #else
2723   if (dst_len < sizeof (int))
2724     return GNUNET_SYSERR;
2725   *((int *) dst) = fh->fd;
2726 #endif
2727
2728   return GNUNET_OK;
2729 }
2730
2731 /* end of disk.c */