fix file open flag mapping on Windows
[oweals/gnunet.git] / src / util / disk.c
1 /*
2      This file is part of GNUnet.
3      (C) 2001, 2002, 2005, 2006, 2009 Christian Grothoff (and other contributing authors)
4
5      GNUnet is free software; you can redistribute it and/or modify
6      it under the terms of the GNU General Public License as published
7      by the Free Software Foundation; either version 2, or (at your
8      option) any later version.
9
10      GNUnet is distributed in the hope that it will be useful, but
11      WITHOUT ANY WARRANTY; without even the implied warranty of
12      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13      General Public License for more details.
14
15      You should have received a copy of the GNU General Public License
16      along with GNUnet; see the file COPYING.  If not, write to the
17      Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18      Boston, MA 02111-1307, USA.
19 */
20
21 /**
22  * @file util/disk.c
23  * @brief disk IO convenience methods
24  * @author Christian Grothoff
25  * @author Nils Durner
26  */
27
28 #include "platform.h"
29 #include "gnunet_common.h"
30 #include "gnunet_directories.h"
31 #include "gnunet_disk_lib.h"
32 #include "gnunet_scheduler_lib.h"
33 #include "gnunet_strings_lib.h"
34 #include "disk.h"
35
36
37 #if LINUX || CYGWIN
38 #include <sys/vfs.h>
39 #else
40 #ifdef SOMEBSD
41 #include <sys/param.h>
42 #include <sys/mount.h>
43 #else
44 #ifdef OSX
45 #include <sys/param.h>
46 #include <sys/mount.h>
47 #else
48 #ifdef SOLARIS
49 #include <sys/types.h>
50 #include <sys/statvfs.h>
51 #else
52 #ifdef MINGW
53 #define         _IFMT           0170000 /* type of file */
54 #define         _IFLNK          0120000 /* symbolic link */
55 #define  S_ISLNK(m)     (((m)&_IFMT) == _IFLNK)
56 #else
57 #error PORT-ME: need to port statfs (how much space is left on the drive?)
58 #endif
59 #endif
60 #endif
61 #endif
62 #endif
63
64 #ifndef SOMEBSD
65 #ifndef WINDOWS
66 #ifndef OSX
67 #include <wordexp.h>
68 #endif
69 #endif
70 #endif
71
72 typedef struct
73 {
74   unsigned long long total;
75   int include_sym_links;
76 } GetFileSizeData;
77
78 struct GNUNET_DISK_PipeHandle
79 {
80   struct GNUNET_DISK_FileHandle fd[2];
81 };
82
83 static int
84 getSizeRec (void *ptr, const char *fn)
85 {
86   GetFileSizeData *gfsd = ptr;
87 #ifdef HAVE_STAT64
88   struct stat64 buf;
89 #else
90   struct stat buf;
91 #endif
92
93 #ifdef HAVE_STAT64
94   if (0 != STAT64 (fn, &buf))
95     {
96       GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "stat64", fn);
97       return GNUNET_SYSERR;
98     }
99 #else
100   if (0 != STAT (fn, &buf))
101     {
102       GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "stat", fn);
103       return GNUNET_SYSERR;
104     }
105 #endif
106   if ((!S_ISLNK (buf.st_mode)) || (gfsd->include_sym_links == GNUNET_YES))
107     gfsd->total += buf.st_size;
108   if ((S_ISDIR (buf.st_mode)) &&
109       (0 == ACCESS (fn, X_OK)) &&
110       ((!S_ISLNK (buf.st_mode)) || (gfsd->include_sym_links == GNUNET_YES)))
111     {
112       if (GNUNET_SYSERR == GNUNET_DISK_directory_scan (fn, &getSizeRec, gfsd))
113         return GNUNET_SYSERR;
114     }
115   return GNUNET_OK;
116 }
117
118 /**
119  * Checks whether a handle is invalid
120  * @param h handle to check
121  * @return GNUNET_YES if invalid, GNUNET_NO if valid
122  */
123 int
124 GNUNET_DISK_handle_invalid (const struct GNUNET_DISK_FileHandle *h)
125 {
126 #ifdef MINGW
127   return !h || h->h == INVALID_HANDLE_VALUE ? GNUNET_YES : GNUNET_NO;
128 #else
129   return !h || h->fd == -1 ? GNUNET_YES : GNUNET_NO;
130 #endif
131 }
132
133
134 /**
135  * Move the read/write pointer in a file
136  * @param h handle of an open file
137  * @param offset position to move to
138  * @param whence specification to which position the offset parameter relates to
139  * @return the new position on success, GNUNET_SYSERR otherwise
140  */
141 off_t
142 GNUNET_DISK_file_seek (const struct GNUNET_DISK_FileHandle *h, off_t offset,
143     enum GNUNET_DISK_Seek whence)
144 {
145   if (h == NULL)
146     {
147       errno = EINVAL;
148       return GNUNET_SYSERR;
149     }
150
151 #ifdef MINGW
152   DWORD ret;
153   static DWORD t[] = { [GNUNET_DISK_SEEK_SET] = FILE_BEGIN,
154       [GNUNET_DISK_SEEK_CUR] = FILE_CURRENT, [GNUNET_DISK_SEEK_END] = FILE_END };
155
156   ret = SetFilePointer (h->h, offset, NULL, t[whence]);
157   if (ret == INVALID_SET_FILE_POINTER)
158     {
159       SetErrnoFromWinError (GetLastError ());
160       return GNUNET_SYSERR;
161     }
162   return ret;
163 #else
164   static int t[] = { [GNUNET_DISK_SEEK_SET] = SEEK_SET,
165       [GNUNET_DISK_SEEK_CUR] = SEEK_CUR, [GNUNET_DISK_SEEK_END] = SEEK_END };
166
167   return lseek (h->fd, offset, t[whence]);
168 #endif
169 }
170
171 /**
172  * Get the size of the file (or directory)
173  * of the given file (in bytes).
174  *
175  * @return GNUNET_SYSERR on error, GNUNET_OK on success
176  */
177 int
178 GNUNET_DISK_file_size (const char *filename,
179                        unsigned long long *size, int includeSymLinks)
180 {
181   GetFileSizeData gfsd;
182   int ret;
183
184   GNUNET_assert (size != NULL);
185   gfsd.total = 0;
186   gfsd.include_sym_links = includeSymLinks;
187   ret = getSizeRec (&gfsd, filename);
188   *size = gfsd.total;
189   return ret;
190 }
191
192
193 /**
194  * Create an (empty) temporary file on disk.
195  * 
196  * @param template component to use for the name;
197  *        does NOT contain "XXXXXX" or "/tmp/".
198  * @return NULL on error, otherwise name of fresh
199  *         file on disk in directory for temporary files
200  */
201 char *
202 GNUNET_DISK_mktemp (const char *template)
203 {
204   const char *tmpdir;
205   int fd;
206   char *tmpl;
207   char *fn;
208
209   tmpdir = getenv ("TMPDIR");
210   tmpdir = tmpdir ? tmpdir : "/tmp";
211
212   GNUNET_asprintf (&tmpl,
213                    "%s%s%s%s",
214                    tmpdir,
215                    DIR_SEPARATOR_STR,
216                    template,
217                    "XXXXXX");
218 #ifdef MINGW
219   fn = (char *) GNUNET_malloc (MAX_PATH + 1);
220   plibc_conv_to_win_path (tmpl, fn);
221   GNUNET_free (tmpl);
222 #else
223   fn = tmpl;
224 #endif
225   fd = mkstemp (fn);
226   if (fd == -1)
227     {
228       GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR,
229                                 "mkstemp",
230                                 fn);
231       GNUNET_free (fn);
232       return NULL;
233     }
234   if (0 != CLOSE (fd))
235     GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING,
236                               "close",
237                               fn);
238   return fn;
239 }
240
241
242 /**
243  * Get the number of blocks that are left on the partition that
244  * contains the given file (for normal users).
245  *
246  * @param part a file on the partition to check
247  * @return -1 on errors, otherwise the number of free blocks
248  */
249 long
250 GNUNET_DISK_get_blocks_available (const char *part)
251 {
252 #ifdef SOLARIS
253   struct statvfs buf;
254
255   if (0 != statvfs (part, &buf))
256     {
257       GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "statfs", part);
258       return -1;
259     }
260   return buf.f_bavail;
261 #elif MINGW
262   DWORD dwDummy;
263   DWORD dwBlocks;
264   char szDrive[4];
265
266   memcpy (szDrive, part, 3);
267   szDrive[3] = 0;
268   if (!GetDiskFreeSpace (szDrive, &dwDummy, &dwDummy, &dwBlocks, &dwDummy))
269     {
270       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
271                      _("`%s' failed for drive `%s': %u\n"),
272                      "GetDiskFreeSpace", szDrive, GetLastError ());
273
274       return -1;
275     }
276   return dwBlocks;
277 #else
278   struct statfs s;
279   if (0 != statfs (part, &s))
280     {
281       GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "statfs", part);
282       return -1;
283     }
284   return s.f_bavail;
285 #endif
286 }
287
288 /**
289  * Test if fil is a directory.
290  *
291  * @return GNUNET_YES if yes, GNUNET_NO if not, GNUNET_SYSERR if it
292  *   does not exist
293  */
294 int
295 GNUNET_DISK_directory_test (const char *fil)
296 {
297   struct stat filestat;
298   int ret;
299
300   ret = STAT (fil, &filestat);
301   if (ret != 0)
302     {
303       if (errno != ENOENT)
304         {
305           GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "stat", fil);
306           return GNUNET_SYSERR;
307         }
308       return GNUNET_NO;
309     }
310   if (!S_ISDIR (filestat.st_mode))
311     return GNUNET_NO;
312   if (ACCESS (fil, R_OK | X_OK) < 0)
313     {
314       GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "access", fil);
315       return GNUNET_SYSERR;
316     }
317   return GNUNET_YES;
318 }
319
320 /**
321  * Check that fil corresponds to a filename
322  * (of a file that exists and that is not a directory).
323  * @returns GNUNET_YES if yes, GNUNET_NO if not a file, GNUNET_SYSERR if something
324  * else (will print an error message in that case, too).
325  */
326 int
327 GNUNET_DISK_file_test (const char *fil)
328 {
329   struct stat filestat;
330   int ret;
331   char *rdir;
332
333   rdir = GNUNET_STRINGS_filename_expand (fil);
334   if (rdir == NULL)
335     return GNUNET_SYSERR;
336
337   ret = STAT (rdir, &filestat);
338   if (ret != 0)
339     {
340       if (errno != ENOENT)
341         {
342           GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "stat", rdir);
343           GNUNET_free (rdir);
344           return GNUNET_SYSERR;
345         }
346       GNUNET_free (rdir);
347       return GNUNET_NO;
348     }
349   if (!S_ISREG (filestat.st_mode))
350     {
351       GNUNET_free (rdir);
352       return GNUNET_NO;
353     }
354   if (ACCESS (rdir, R_OK) < 0)
355     {
356       GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "access", rdir);
357       GNUNET_free (rdir);
358       return GNUNET_SYSERR;
359     }
360   GNUNET_free (rdir);
361   return GNUNET_YES;
362 }
363
364 /**
365  * Implementation of "mkdir -p"
366  * @param dir the directory to create
367  * @returns GNUNET_OK on success, GNUNET_SYSERR on failure
368  */
369 int
370 GNUNET_DISK_directory_create (const char *dir)
371 {
372   char *rdir;
373   int len;
374   int pos;
375   int ret = GNUNET_OK;
376
377   rdir = GNUNET_STRINGS_filename_expand (dir);
378   if (rdir == NULL)
379     return GNUNET_SYSERR;
380
381   len = strlen (rdir);
382 #ifndef MINGW
383   pos = 1;                      /* skip heading '/' */
384 #else
385   /* Local or Network path? */
386   if (strncmp (rdir, "\\\\", 2) == 0)
387     {
388       pos = 2;
389       while (rdir[pos])
390         {
391           if (rdir[pos] == '\\')
392             {
393               pos++;
394               break;
395             }
396           pos++;
397         }
398     }
399   else
400     {
401       pos = 3;                  /* strlen("C:\\") */
402     }
403 #endif
404   while (pos <= len)
405     {
406       if ((rdir[pos] == DIR_SEPARATOR) || (pos == len))
407         {
408           rdir[pos] = '\0';
409           ret = GNUNET_DISK_directory_test (rdir);
410           if (ret == GNUNET_SYSERR)
411             {
412               GNUNET_free (rdir);
413               return GNUNET_SYSERR;
414             }
415           if (ret == GNUNET_NO)
416             {
417 #ifndef MINGW
418               ret = mkdir (rdir, S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH);  /* 755 */
419 #else
420               ret = mkdir (rdir);
421 #endif
422               if ((ret != 0) && (errno != EEXIST))
423                 {
424                   GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR, "mkdir",
425                                             rdir);
426                   GNUNET_free (rdir);
427                   return GNUNET_SYSERR;
428                 }
429             }
430           rdir[pos] = DIR_SEPARATOR;
431         }
432       pos++;
433     }
434   GNUNET_free (rdir);
435   return GNUNET_OK;
436 }
437
438
439 /**
440  * Create the directory structure for storing
441  * a file.
442  *
443  * @param filename name of a file in the directory
444  * @returns GNUNET_OK on success,
445  *          GNUNET_SYSERR on failure,
446  *          GNUNET_NO if the directory
447  *          exists but is not writeable for us
448  */
449 int
450 GNUNET_DISK_directory_create_for_file (const char *dir)
451 {
452   char *rdir;
453   int len;
454   int ret;
455
456   rdir = GNUNET_STRINGS_filename_expand (dir);
457   if (rdir == NULL)
458     return GNUNET_SYSERR;
459   len = strlen (rdir);
460   while ((len > 0) && (rdir[len] != DIR_SEPARATOR))
461     len--;
462   rdir[len] = '\0';
463   ret = GNUNET_DISK_directory_create (rdir);
464   if ((ret == GNUNET_OK) && (0 != ACCESS (rdir, W_OK)))
465     ret = GNUNET_NO;
466   GNUNET_free (rdir);
467   return ret;
468 }
469
470 /**
471  * Read the contents of a binary file into a buffer.
472  * @param h handle to an open file
473  * @param result the buffer to write the result to
474  * @param len the maximum number of bytes to read
475  * @return the number of bytes read on success, GNUNET_SYSERR on failure
476  */
477 ssize_t
478 GNUNET_DISK_file_read (const struct GNUNET_DISK_FileHandle *h, void *result, 
479                        size_t len)
480 {
481   if (h == NULL)
482     {
483       errno = EINVAL;
484       return GNUNET_SYSERR;
485     }
486
487 #ifdef MINGW
488   DWORD bytesRead;
489
490   if (!ReadFile (h->h, result, len, &bytesRead, NULL))
491     {
492       SetErrnoFromWinError (GetLastError ());
493       return GNUNET_SYSERR;
494     }
495   return bytesRead;
496 #else
497   return read (h->fd, result, len);
498 #endif
499 }
500
501
502 /**
503  * Read the contents of a binary file into a buffer.
504  *
505  * @param fn file name
506  * @param result the buffer to write the result to
507  * @param len the maximum number of bytes to read
508  * @return number of bytes read, GNUNET_SYSERR on failure
509  */
510 ssize_t
511 GNUNET_DISK_fn_read (const char * const fn, 
512                      void *result,
513                      size_t len)
514 {
515   struct GNUNET_DISK_FileHandle *fh;
516   ssize_t ret;
517
518   fh = GNUNET_DISK_file_open (fn, GNUNET_DISK_OPEN_READ);
519   if (!fh)
520     return GNUNET_SYSERR;
521   ret = GNUNET_DISK_file_read (fh, result, len);
522   GNUNET_assert(GNUNET_OK == GNUNET_DISK_file_close(fh));
523
524   return ret;
525 }
526
527
528 /**
529  * Write a buffer to a file.
530  * @param h handle to open file
531  * @param buffer the data to write
532  * @param n number of bytes to write
533  * @return number of bytes written on success, GNUNET_SYSERR on error
534  */
535 ssize_t
536 GNUNET_DISK_file_write (const struct GNUNET_DISK_FileHandle *h, const void *buffer,
537                         size_t n)
538 {
539   if (h == NULL)
540     {
541       errno = EINVAL;
542       return GNUNET_SYSERR;
543     }
544
545 #ifdef MINGW
546   DWORD bytesWritten;
547
548   if (!WriteFile (h->h, buffer, n, &bytesWritten, NULL))
549     {
550       SetErrnoFromWinError (GetLastError ());
551       return GNUNET_SYSERR;
552     }
553   return bytesWritten;
554 #else
555   return write (h->fd, buffer, n);
556 #endif
557 }
558
559 /**
560  * Write a buffer to a file.  If the file is longer than the
561  * number of bytes that will be written, iit will be truncated.
562  *
563  * @param fn file name
564  * @param buffer the data to write
565  * @param n number of bytes to write
566  * @return GNUNET_OK on success, GNUNET_SYSERR on error
567  */
568 ssize_t
569 GNUNET_DISK_fn_write (const char * const fn, const void *buffer,
570     size_t n, int mode)
571 {
572   struct GNUNET_DISK_FileHandle *fh;
573   int ret;
574
575   fh = GNUNET_DISK_file_open (fn, 
576                               GNUNET_DISK_OPEN_WRITE 
577                               | GNUNET_DISK_OPEN_TRUNCATE
578                               | GNUNET_DISK_OPEN_CREATE, mode);
579   if (!fh)
580     return GNUNET_SYSERR;
581   ret = (n == GNUNET_DISK_file_write (fh, buffer, n)) ? GNUNET_OK : GNUNET_SYSERR;
582   GNUNET_assert(GNUNET_OK == GNUNET_DISK_file_close(fh));
583
584   return ret;
585 }
586
587 /**
588  * Scan a directory for files. The name of the directory
589  * must be expanded first (!).
590  * @param dirName the name of the directory
591  * @param callback the method to call for each file,
592  *        can be NULL, in that case, we only count
593  * @param data argument to pass to callback
594  * @return the number of files found, GNUNET_SYSERR on error or
595  *         ieration aborted by callback returning GNUNET_SYSERR
596  */
597 int
598 GNUNET_DISK_directory_scan (const char *dirName,
599                             GNUNET_FileNameCallback callback, void *data)
600 {
601   DIR *dinfo;
602   struct dirent *finfo;
603   struct stat istat;
604   int count = 0;
605   char *name;
606   char *dname;
607   unsigned int name_len;
608   unsigned int n_size;
609
610   GNUNET_assert (dirName != NULL);
611   dname = GNUNET_STRINGS_filename_expand (dirName);
612   while ((strlen (dname) > 0) && (dname[strlen (dname) - 1] == DIR_SEPARATOR))
613     dname[strlen (dname) - 1] = '\0';
614   if (0 != STAT (dname, &istat))
615     {
616       GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "stat", dname);
617       GNUNET_free (dname);
618       return GNUNET_SYSERR;
619     }
620   if (!S_ISDIR (istat.st_mode))
621     {
622       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
623                   _("Expected `%s' to be a directory!\n"), dirName);
624       GNUNET_free (dname);
625       return GNUNET_SYSERR;
626     }
627   errno = 0;
628   dinfo = OPENDIR (dname);
629   if ((errno == EACCES) || (dinfo == NULL))
630     {
631       GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "opendir", dname);
632       if (dinfo != NULL)
633         closedir (dinfo);
634       GNUNET_free (dname);
635       return GNUNET_SYSERR;
636     }
637   name_len = 256;
638   n_size = strlen (dname) + name_len + 2;
639   name = GNUNET_malloc (n_size);
640   while ((finfo = readdir (dinfo)) != NULL)
641     {
642       if ((0 == strcmp (finfo->d_name, ".")) ||
643           (0 == strcmp (finfo->d_name, "..")))
644         continue;
645       if (callback != NULL)
646         {
647           if (name_len < strlen (finfo->d_name))
648             {
649               GNUNET_free (name);
650               name_len = strlen (finfo->d_name);
651               n_size = strlen (dname) + name_len + 2;
652               name = GNUNET_malloc (n_size);
653             }
654           /* dname can end in "/" only if dname == "/";
655              if dname does not end in "/", we need to add
656              a "/" (otherwise, we must not!) */
657           GNUNET_snprintf (name,
658                            n_size,
659                            "%s%s%s",
660                            dname,
661                            (strcmp (dname, DIR_SEPARATOR_STR) ==
662                             0) ? "" : DIR_SEPARATOR_STR, finfo->d_name);
663           if (GNUNET_OK != callback (data, name))
664             {
665               closedir (dinfo);
666               GNUNET_free (name);
667               GNUNET_free (dname);
668               return GNUNET_SYSERR;
669             }
670         }
671       count++;
672     }
673   closedir (dinfo);
674   GNUNET_free (name);
675   GNUNET_free (dname);
676   return count;
677 }
678
679
680 /**
681  * Opaque handle used for iterating over a directory.
682  */
683 struct GNUNET_DISK_DirectoryIterator
684 {
685   /**
686    * Our scheduler.
687    */
688   struct GNUNET_SCHEDULER_Handle *sched;
689
690   /**
691    * Function to call on directory entries.
692    */
693   GNUNET_DISK_DirectoryIteratorCallback callback;
694
695   /**
696    * Closure for callback.
697    */
698   void *callback_cls;
699
700   /**
701    * Reference to directory.
702    */
703   DIR *directory;
704
705   /**
706    * Directory name.
707    */
708   char *dirname;
709
710   /**
711    * Next filename to process.
712    */
713   char *next_name;
714
715   /**
716    * Our priority.
717    */
718   enum GNUNET_SCHEDULER_Priority priority;
719
720 };
721
722
723 /**
724  * Task used by the directory iterator.
725  */
726 static void
727 directory_iterator_task (void *cls,
728                          const struct GNUNET_SCHEDULER_TaskContext *tc)
729 {
730   struct GNUNET_DISK_DirectoryIterator *iter = cls;
731   char *name;
732
733   name = iter->next_name;
734   GNUNET_assert (name != NULL);
735   iter->next_name = NULL;
736   iter->callback (iter->callback_cls, iter, name, iter->dirname);
737   GNUNET_free (name);
738 }
739
740
741 /**
742  * This function must be called during the DiskIteratorCallback
743  * (exactly once) to schedule the task to process the next
744  * filename in the directory (if there is one).
745  *
746  * @param iter opaque handle for the iterator
747  * @param can set to GNUNET_YES to terminate the iteration early
748  * @return GNUNET_YES if iteration will continue,
749  *         GNUNET_NO if this was the last entry (and iteration is complete),
750  *         GNUNET_SYSERR if abort was YES
751  */
752 int
753 GNUNET_DISK_directory_iterator_next (struct GNUNET_DISK_DirectoryIterator
754                                      *iter, int can)
755 {
756   struct dirent *finfo;
757
758   GNUNET_assert (iter->next_name == NULL);
759   if (can == GNUNET_YES)
760     {
761       closedir (iter->directory);
762       GNUNET_free (iter->dirname);
763       GNUNET_free (iter);
764       return GNUNET_SYSERR;
765     }
766   while (NULL != (finfo = readdir (iter->directory)))
767     {
768       if ((0 == strcmp (finfo->d_name, ".")) ||
769           (0 == strcmp (finfo->d_name, "..")))
770         continue;
771       GNUNET_asprintf (&iter->next_name,
772                        "%s%s%s",
773                        iter->dirname, DIR_SEPARATOR_STR, finfo->d_name);
774       break;
775     }
776   if (finfo == NULL)
777     {
778       GNUNET_DISK_directory_iterator_next (iter, GNUNET_YES);
779       return GNUNET_NO;
780     }
781   GNUNET_SCHEDULER_add_after (iter->sched,
782                               GNUNET_YES,
783                               iter->priority,
784                               GNUNET_SCHEDULER_NO_TASK,
785                               &directory_iterator_task, iter);
786   return GNUNET_YES;
787 }
788
789
790 /**
791  * Scan a directory for files using the scheduler to run a task for
792  * each entry.  The name of the directory must be expanded first (!).
793  * If a scheduler does not need to be used, GNUNET_DISK_directory_scan
794  * may provide a simpler API.
795  *
796  * @param sched scheduler to use
797  * @param prio priority to use
798  * @param dirName the name of the directory
799  * @param callback the method to call for each file
800  * @param callback_cls closure for callback
801  */
802 void
803 GNUNET_DISK_directory_iterator_start (struct GNUNET_SCHEDULER_Handle *sched,
804                                       enum GNUNET_SCHEDULER_Priority prio,
805                                       const char *dirName,
806                                       GNUNET_DISK_DirectoryIteratorCallback
807                                       callback, void *callback_cls)
808 {
809   struct GNUNET_DISK_DirectoryIterator *di;
810
811   di = GNUNET_malloc (sizeof (struct GNUNET_DISK_DirectoryIterator));
812   di->sched = sched;
813   di->callback = callback;
814   di->callback_cls = callback_cls;
815   di->directory = OPENDIR (dirName);
816   di->dirname = GNUNET_strdup (dirName);
817   di->priority = prio;
818   GNUNET_DISK_directory_iterator_next (di, GNUNET_NO);
819 }
820
821
822 static int
823 remove_helper (void *unused, const char *fn)
824 {
825   GNUNET_DISK_directory_remove (fn);
826   return GNUNET_OK;
827 }
828
829 /**
830  * Remove all files in a directory (rm -rf). Call with
831  * caution.
832  *
833  *
834  * @param fileName the file to remove
835  * @return GNUNET_OK on success, GNUNET_SYSERR on error
836  */
837 int
838 GNUNET_DISK_directory_remove (const char *fileName)
839 {
840   struct stat istat;
841
842   if (0 != LSTAT (fileName, &istat))
843     return GNUNET_NO;           /* file may not exist... */
844   if (UNLINK (fileName) == 0)
845     return GNUNET_OK;
846   if ((errno != EISDIR) &&
847       /* EISDIR is not sufficient in all cases, e.g.
848          sticky /tmp directory may result in EPERM on BSD.
849          So we also explicitly check "isDirectory" */
850       (GNUNET_YES != GNUNET_DISK_directory_test (fileName)))
851     {
852       GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "rmdir", fileName);
853       return GNUNET_SYSERR;
854     }
855   if (GNUNET_SYSERR ==
856       GNUNET_DISK_directory_scan (fileName, remove_helper, NULL))
857     return GNUNET_SYSERR;
858   if (0 != RMDIR (fileName))
859     {
860       GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "rmdir", fileName);
861       return GNUNET_SYSERR;
862     }
863   return GNUNET_OK;
864 }
865
866 #define COPY_BLK_SIZE 65536
867
868 /**
869  * Copy a file.
870  * @return GNUNET_OK on success, GNUNET_SYSERR on error
871  */
872 int
873 GNUNET_DISK_file_copy (const char *src, const char *dst)
874 {
875   char *buf;
876   unsigned long long pos;
877   unsigned long long size;
878   unsigned long long len;
879   struct GNUNET_DISK_FileHandle *in, *out;
880
881   if (GNUNET_OK != GNUNET_DISK_file_size (src, &size, GNUNET_YES))
882     return GNUNET_SYSERR;
883   pos = 0;
884   in = GNUNET_DISK_file_open (src, GNUNET_DISK_OPEN_READ);
885   if (!in)
886     return GNUNET_SYSERR;
887   out = GNUNET_DISK_file_open (dst, GNUNET_DISK_OPEN_WRITE
888                                | GNUNET_DISK_OPEN_CREATE | GNUNET_DISK_OPEN_FAILIFEXISTS,
889                                GNUNET_DISK_PERM_USER_READ | GNUNET_DISK_PERM_USER_WRITE
890                                | GNUNET_DISK_PERM_GROUP_READ | GNUNET_DISK_PERM_GROUP_WRITE);
891   if (!out)
892     {
893       GNUNET_DISK_file_close (in);
894       return GNUNET_SYSERR;
895     }
896   buf = GNUNET_malloc (COPY_BLK_SIZE);
897   while (pos < size)
898     {
899       len = COPY_BLK_SIZE;
900       if (len > size - pos)
901         len = size - pos;
902       if (len != GNUNET_DISK_file_read (in, buf, len))
903         goto FAIL;
904       if (len != GNUNET_DISK_file_write (out, buf, len))
905         goto FAIL;
906       pos += len;
907     }
908   GNUNET_free (buf);
909   GNUNET_DISK_file_close (in);
910   GNUNET_DISK_file_close (out);
911   return GNUNET_OK;
912 FAIL:
913   GNUNET_free (buf);
914   GNUNET_DISK_file_close (in);
915   GNUNET_DISK_file_close (out);
916   return GNUNET_SYSERR;
917 }
918
919
920 /**
921  * @brief Removes special characters as ':' from a filename.
922  * @param fn the filename to canonicalize
923  */
924 void
925 GNUNET_DISK_filename_canonicalize (char *fn)
926 {
927   char *idx;
928   char c;
929
930   idx = fn;
931   while (*idx)
932     {
933       c = *idx;
934
935       if (c == '/' || c == '\\' || c == ':' || c == '*' || c == '?' ||
936           c == '"' || c == '<' || c == '>' || c == '|')
937         {
938           *idx = '_';
939         }
940
941       idx++;
942     }
943 }
944
945
946
947 /**
948  * @brief Change owner of a file
949  */
950 int
951 GNUNET_DISK_file_change_owner (const char *filename, const char *user)
952 {
953 #ifndef MINGW
954   struct passwd *pws;
955
956   pws = getpwnam (user);
957   if (pws == NULL)
958     {
959       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
960                   _("Cannot obtain information about user `%s': %s\n"),
961                   user, STRERROR (errno));
962       return GNUNET_SYSERR;
963     }
964   if (0 != chown (filename, pws->pw_uid, pws->pw_gid))
965     GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "chown", filename);
966 #endif
967   return GNUNET_OK;
968 }
969
970
971 /**
972  * Lock a part of a file
973  * @param fh file handle
974  * @lockStart absolute position from where to lock
975  * @lockEnd absolute position until where to lock
976  * @return GNUNET_OK on success, GNUNET_SYSERR on error
977  */
978 int
979 GNUNET_DISK_file_lock(struct GNUNET_DISK_FileHandle *fh, off_t lockStart,
980     off_t lockEnd)
981 {
982   if (fh == NULL)
983     {
984       errno = EINVAL;
985       return GNUNET_SYSERR;
986     }
987
988 #ifndef MINGW
989   struct flock fl;
990
991   memset(&fl, 0, sizeof(struct flock));
992   fl.l_type = F_WRLCK;
993   fl.l_whence = SEEK_SET;
994   fl.l_start = lockStart;
995   fl.l_len = lockEnd;
996
997   return fcntl(fh->fd, F_SETLK, &fl) != 0 ? GNUNET_SYSERR : GNUNET_OK;
998 #else
999   if (!LockFile(fh->h, 0, lockStart, 0, lockEnd))
1000   {
1001     SetErrnoFromWinError(GetLastError());
1002     return GNUNET_SYSERR;
1003   }
1004
1005   return GNUNET_OK;
1006 #endif
1007 }
1008
1009
1010 /**
1011  * Open a file
1012  * @param fn file name to be opened
1013  * @param flags opening flags, a combination of GNUNET_DISK_OPEN_xxx bit flags
1014  * @param perm permissions for the newly created file
1015  * @return IO handle on success, NULL on error
1016  */
1017 struct GNUNET_DISK_FileHandle *
1018 GNUNET_DISK_file_open (const char *fn, int flags, ...)
1019 {
1020   char *expfn;
1021   struct GNUNET_DISK_FileHandle *ret;
1022 #ifdef MINGW
1023   DWORD access;
1024   DWORD disp;
1025   HANDLE h;
1026 #else
1027   int oflags;
1028   int mode;
1029   int fd;
1030 #endif
1031
1032   expfn = GNUNET_STRINGS_filename_expand (fn);
1033
1034 #ifndef MINGW
1035   mode = 0;
1036   oflags = 0;
1037   if (GNUNET_DISK_OPEN_READWRITE == (flags & GNUNET_DISK_OPEN_READWRITE))
1038     oflags = O_RDWR; /* note: O_RDWR is NOT always O_RDONLY | O_WRONLY */
1039   else if (flags & GNUNET_DISK_OPEN_READ)
1040     oflags = O_RDONLY;
1041   else if (flags & GNUNET_DISK_OPEN_WRITE)
1042     oflags = O_WRONLY;
1043   else
1044     {
1045       GNUNET_break (0);
1046       GNUNET_free (expfn);
1047       return NULL;
1048     }
1049   if (flags & GNUNET_DISK_OPEN_FAILIFEXISTS)
1050     oflags |= (O_CREAT & O_EXCL);
1051   if (flags & GNUNET_DISK_OPEN_TRUNCATE)
1052     oflags |= O_TRUNC;
1053   if (flags & GNUNET_DISK_OPEN_APPEND)
1054     oflags |= O_APPEND;
1055   if (flags & GNUNET_DISK_OPEN_CREATE)
1056     {
1057       int perm;
1058
1059       oflags |= O_CREAT;
1060
1061       va_list arg;
1062       va_start (arg, flags);
1063       perm = va_arg (arg, int);
1064       va_end (arg);
1065
1066       if (perm & GNUNET_DISK_PERM_USER_READ)
1067         mode |= S_IRUSR;
1068       if (perm & GNUNET_DISK_PERM_USER_WRITE)
1069         mode |= S_IWUSR;
1070       if (perm & GNUNET_DISK_PERM_USER_EXEC)
1071         mode |= S_IXUSR;
1072       if (perm & GNUNET_DISK_PERM_GROUP_READ)
1073         mode |= S_IRGRP;
1074       if (perm & GNUNET_DISK_PERM_GROUP_WRITE)
1075         mode |= S_IWGRP;
1076       if (perm & GNUNET_DISK_PERM_GROUP_EXEC)
1077         mode |= S_IXGRP;
1078       if (perm & GNUNET_DISK_PERM_OTHER_READ)
1079         mode |= S_IROTH;
1080       if (perm & GNUNET_DISK_PERM_OTHER_WRITE)
1081         mode |= S_IWOTH;
1082       if (perm & GNUNET_DISK_PERM_OTHER_EXEC)
1083         mode |= S_IXOTH;
1084     }
1085
1086   fd = open (expfn, oflags | O_LARGEFILE, mode);
1087   if (fd == -1)
1088   {
1089     GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "open", expfn);
1090     GNUNET_free (expfn);
1091     return NULL;
1092   }
1093 #else
1094   access = 0;
1095   disp = OPEN_ALWAYS;
1096
1097   if (flags & GNUNET_DISK_OPEN_READ)
1098     access = FILE_READ_DATA;
1099   if (flags & GNUNET_DISK_OPEN_WRITE)
1100     access = FILE_WRITE_DATA;
1101
1102   if (flags & GNUNET_DISK_OPEN_FAILIFEXISTS)
1103     {
1104       disp = CREATE_NEW;
1105     }
1106   else if (flags & GNUNET_DISK_OPEN_CREATE)
1107     {
1108       if (flags & GNUNET_DISK_OPEN_TRUNCATE)
1109         disp = CREATE_ALWAYS;
1110       else
1111         disp = OPEN_ALWAYS;
1112     }
1113   else if (flags & GNUNET_DISK_OPEN_TRUNCATE)
1114     {
1115       disp = TRUNCATE_EXISTING;
1116     }
1117   else
1118   {
1119     disp = OPEN_ALWAYS;
1120   }
1121
1122   /* TODO: access priviledges? */
1123   h = CreateFile (expfn, access, FILE_SHARE_DELETE | FILE_SHARE_READ
1124       | FILE_SHARE_WRITE, NULL, disp, FILE_ATTRIBUTE_NORMAL, NULL);
1125   if (h == INVALID_HANDLE_VALUE)
1126   {
1127     SetErrnoFromWinError (GetLastError ());
1128     GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "open", expfn);
1129     GNUNET_free (expfn);
1130     return NULL;
1131   }
1132
1133   if (flags & GNUNET_DISK_OPEN_APPEND)
1134     if (SetFilePointer (h, 0, 0, FILE_END) == INVALID_SET_FILE_POINTER)
1135     {
1136       SetErrnoFromWinError (GetLastError ());
1137       GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "SetFilePointer", expfn);
1138       CloseHandle (h);
1139       GNUNET_free (expfn);
1140       return NULL;
1141     }
1142 #endif
1143
1144   ret = GNUNET_malloc(sizeof(struct GNUNET_DISK_FileHandle));
1145 #ifdef MINGW
1146   ret->h = h;
1147 #else
1148   ret->fd = fd;
1149 #endif
1150   GNUNET_free (expfn);
1151   return ret;
1152 }
1153
1154 /**
1155  * Close an open file
1156  * @param h file handle
1157  * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
1158  */
1159 int
1160 GNUNET_DISK_file_close (struct GNUNET_DISK_FileHandle *h)
1161 {
1162   if (h == NULL)
1163     {
1164       errno = EINVAL;
1165       return GNUNET_SYSERR;
1166     }
1167
1168 #if MINGW
1169   if (!CloseHandle (h->h))
1170   {
1171     SetErrnoFromWinError (GetLastError ());
1172     GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "close");
1173     GNUNET_free (h);
1174     return GNUNET_SYSERR;
1175   }
1176 #else
1177   if (close (h->fd) != 0)
1178   {
1179     GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "close");
1180     GNUNET_free (h);
1181     return GNUNET_SYSERR;
1182   }
1183 #endif
1184   GNUNET_free (h);
1185   return GNUNET_OK;
1186 }
1187
1188 /**
1189  * Construct full path to a file inside of the private
1190  * directory used by GNUnet.  Also creates the corresponding
1191  * directory.  If the resulting name is supposed to be
1192  * a directory, end the last argument in '/' (or pass
1193  * DIR_SEPARATOR_STR as the last argument before NULL).
1194  *
1195  * @param cfg configuration to use (determines HOME)
1196  * @param serviceName name of the service
1197  * @param varargs is NULL-terminated list of
1198  *                path components to append to the
1199  *                private directory name.
1200  * @return the constructed filename
1201  */
1202 char *
1203 GNUNET_DISK_get_home_filename (const struct GNUNET_CONFIGURATION_Handle *cfg,
1204                                const char *serviceName, ...)
1205 {
1206   const char *c;
1207   char *pfx;
1208   char *ret;
1209   va_list ap;
1210   unsigned int needed;
1211
1212   if (GNUNET_OK !=
1213       GNUNET_CONFIGURATION_get_value_filename (cfg,
1214                                                serviceName, "HOME", &pfx))
1215     return NULL;
1216   if (pfx == NULL)
1217     {
1218       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1219                   _("No `%s' specified for service `%s' in configuration.\n"),
1220                   "HOME", serviceName);
1221       return NULL;
1222     }
1223   needed = strlen (pfx) + 2;
1224   if ((pfx[strlen (pfx) - 1] != '/') && (pfx[strlen (pfx) - 1] != '\\'))
1225     needed++;
1226   va_start (ap, serviceName);
1227   while (1)
1228     {
1229       c = va_arg (ap, const char *);
1230       if (c == NULL)
1231         break;
1232       needed += strlen (c);
1233       if ((c[strlen (c) - 1] != '/') && (c[strlen (c) - 1] != '\\'))
1234         needed++;
1235     }
1236   va_end (ap);
1237   ret = GNUNET_malloc (needed);
1238   strcpy (ret, pfx);
1239   GNUNET_free (pfx);
1240   va_start (ap, serviceName);
1241   while (1)
1242     {
1243       c = va_arg (ap, const char *);
1244       if (c == NULL)
1245         break;
1246       if ((c[strlen (c) - 1] != '/') && (c[strlen (c) - 1] != '\\'))
1247         strcat (ret, DIR_SEPARATOR_STR);
1248       strcat (ret, c);
1249     }
1250   va_end (ap);
1251   if ((ret[strlen (ret) - 1] != '/') && (ret[strlen (ret) - 1] != '\\'))
1252     GNUNET_DISK_directory_create_for_file (ret);
1253   else
1254     GNUNET_DISK_directory_create (ret);
1255   return ret;
1256 }
1257
1258 struct GNUNET_DISK_MapHandle
1259 {
1260 #ifdef MINGW
1261   HANDLE h;
1262 #else
1263   void *addr;
1264   size_t len;
1265 #endif
1266 };
1267
1268
1269 /**
1270  * Map a file into memory
1271  * @param h open file handle
1272  * @param m handle to the new mapping
1273  * @param access access specification, GNUNET_DISK_MAP_xxx
1274  * @param len size of the mapping
1275  * @return pointer to the mapped memory region, NULL on failure
1276  */
1277 void *
1278 GNUNET_DISK_file_map (const struct GNUNET_DISK_FileHandle *h, struct GNUNET_DISK_MapHandle **m,
1279     int access, size_t len)
1280 {
1281   if (h == NULL)
1282     {
1283       errno = EINVAL;
1284       return NULL;
1285     }
1286
1287 #ifdef MINGW
1288   DWORD mapAccess, protect;
1289   void *ret;
1290
1291   if (access & GNUNET_DISK_MAP_READ && access & GNUNET_DISK_MAP_WRITE)
1292     {
1293       protect = PAGE_READWRITE;
1294       mapAccess = FILE_MAP_ALL_ACCESS;
1295     }
1296   else if (access & GNUNET_DISK_MAP_READ)
1297     {
1298       protect = PAGE_READONLY;
1299       mapAccess = FILE_MAP_READ;
1300     }
1301   else if (access & GNUNET_DISK_MAP_WRITE)
1302     {
1303       protect = PAGE_READWRITE;
1304       mapAccess = FILE_MAP_WRITE;
1305     }
1306   else
1307     {
1308       GNUNET_break (0);
1309       return NULL;
1310     }
1311
1312   *m = GNUNET_malloc (sizeof (struct GNUNET_DISK_MapHandle));
1313   (*m)->h = CreateFileMapping (h->h, NULL, protect, 0, 0, NULL);
1314   if ((*m)->h == INVALID_HANDLE_VALUE)
1315     {
1316       SetErrnoFromWinError (GetLastError ());
1317       GNUNET_free (*m);
1318       return NULL;
1319     }
1320
1321   ret = MapViewOfFile ((*m)->h, mapAccess, 0, 0, len);
1322   if (!ret)
1323     {
1324       SetErrnoFromWinError (GetLastError ());
1325       CloseHandle ((*m)->h);
1326       GNUNET_free (*m);
1327     }
1328
1329   return ret;
1330 #else
1331   int prot;
1332
1333   prot = 0;
1334   if (access & GNUNET_DISK_MAP_READ)
1335     prot = PROT_READ;
1336   if (access & GNUNET_DISK_MAP_WRITE)
1337     prot |= PROT_WRITE;
1338   *m = GNUNET_malloc (sizeof (struct GNUNET_DISK_MapHandle));
1339   (*m)->addr = mmap (NULL, len, prot, MAP_SHARED, h->fd, 0);
1340   (*m)->len = len;
1341   return (*m)->addr;
1342 #endif
1343 }
1344
1345 /**
1346  * Unmap a file
1347  * @param h mapping handle
1348  * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
1349  */
1350 int
1351 GNUNET_DISK_file_unmap (struct GNUNET_DISK_MapHandle *h)
1352 {
1353   int ret;
1354   if (h == NULL)
1355     {
1356       errno = EINVAL;
1357       return GNUNET_SYSERR;
1358     }
1359
1360 #ifdef MINGW
1361   ret = UnmapViewOfFile (h->h) ? GNUNET_OK : GNUNET_SYSERR;
1362   if (ret != GNUNET_OK)
1363     SetErrnoFromWinError (GetLastError ());
1364   if (!CloseHandle (h->h) && (ret == GNUNET_OK))
1365     {
1366       ret = GNUNET_SYSERR;
1367       SetErrnoFromWinError (GetLastError ());
1368     }
1369 #else
1370   ret = munmap (h->addr, h->len) != -1 ? GNUNET_OK : GNUNET_SYSERR;
1371 #endif
1372   GNUNET_free (h);
1373   return ret;
1374 }
1375
1376
1377 /**
1378  * Write file changes to disk
1379  * @param h handle to an open file
1380  * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
1381  */
1382 int
1383 GNUNET_DISK_file_sync (const struct GNUNET_DISK_FileHandle *h)
1384 {
1385   if (h == NULL)
1386     {
1387       errno = EINVAL;
1388       return GNUNET_SYSERR;
1389     }
1390
1391 #ifdef MINGW
1392   int ret;
1393
1394   ret = FlushFileBuffers (h->h) ? GNUNET_OK : GNUNET_SYSERR;
1395   if (ret != GNUNET_OK)
1396     SetErrnoFromWinError (GetLastError ());
1397   return ret;
1398 #else
1399   return fsync (h->fd) == -1 ? GNUNET_SYSERR : GNUNET_OK;
1400 #endif
1401 }
1402
1403 /**
1404  * Creates an interprocess channel
1405  * @param blocking creates an asynchronous pipe if set to GNUNET_NO
1406  * @return handle to the new pipe, NULL on error
1407  */
1408 struct GNUNET_DISK_PipeHandle *
1409 GNUNET_DISK_pipe (int blocking)
1410 {
1411   struct GNUNET_DISK_PipeHandle *p;
1412   int err;
1413
1414   err = GNUNET_NO;
1415   p = GNUNET_malloc (sizeof (struct GNUNET_DISK_PipeHandle));
1416
1417 #ifndef MINGW
1418   int fd[2];
1419   int ret;
1420   int flags;
1421
1422   ret = pipe (fd);
1423   if (ret != -1)
1424     {
1425       p->fd[0].fd = fd[0];
1426       p->fd[1].fd = fd[1];
1427
1428       if (!blocking)
1429         {
1430           flags = fcntl (fd[0], F_GETFL);
1431           flags |= O_NONBLOCK;
1432           ret = fcntl (fd[0], F_SETFL, flags);
1433           if (ret != -1)
1434             {
1435               flags = fcntl (fd[1], F_GETFL);
1436               flags |= O_NONBLOCK;
1437               ret = fcntl (fd[1], F_SETFL, flags);
1438             }
1439           if (ret == -1)
1440             {
1441               GNUNET_log_strerror(GNUNET_ERROR_TYPE_ERROR, "fcntl");
1442               close (fd[0]);
1443               close (fd[1]);
1444               err = GNUNET_YES;
1445             }
1446         }
1447     }
1448   else
1449     err = GNUNET_YES;
1450 #else
1451   BOOL ret;
1452
1453   ret = CreatePipe (&p->fd[0].h, &p->fd[1].h, NULL, 0);
1454   if (ret)
1455     {
1456       if (!blocking)
1457         {
1458           DWORD mode;
1459
1460           mode = PIPE_NOWAIT;
1461           SetNamedPipeHandleState (p->fd[0].h, &mode, NULL, NULL);
1462           SetNamedPipeHandleState (p->fd[1].h, &mode, NULL, NULL);
1463           /* this always fails on Windows 95, so we don't care about error handling */
1464         }
1465     }
1466   else
1467       err = GNUNET_YES;
1468 #endif
1469
1470   if (GNUNET_YES == err)
1471     {
1472       GNUNET_free (p);
1473       p = NULL;
1474     }
1475
1476   return p;
1477 }
1478
1479 /**
1480  * Closes an interprocess channel
1481  * @param p pipe
1482  * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
1483  */
1484 int
1485 GNUNET_DISK_pipe_close (struct GNUNET_DISK_PipeHandle *p)
1486 {
1487   int ret;
1488
1489   ret = GNUNET_OK;
1490
1491 #ifdef MINGW
1492   if (!CloseHandle (p->fd[0].h))
1493     {
1494       SetErrnoFromWinError (GetLastError ());
1495       ret = GNUNET_SYSERR;
1496     }
1497
1498   if (!CloseHandle (p->fd[1].h))
1499     {
1500       SetErrnoFromWinError (GetLastError ());
1501       ret = GNUNET_SYSERR;
1502     }
1503 #else
1504   {
1505     int save;
1506
1507     if (close (p->fd[0].fd) != -1)
1508       {
1509         ret = GNUNET_SYSERR;
1510         save = errno;
1511       }
1512     else
1513       save = 0;
1514
1515     if (close (p->fd[1].fd) != -1)
1516       {
1517         ret = GNUNET_SYSERR;
1518       }
1519     else
1520       errno = save;
1521   }
1522 #endif
1523   GNUNET_free (p);
1524
1525   return ret;
1526 }
1527
1528 /**
1529  * Get the handle to a particular pipe end
1530  * @param p pipe
1531  * @param n number of the end
1532  */
1533 const struct GNUNET_DISK_FileHandle *
1534 GNUNET_DISK_pipe_handle (const struct GNUNET_DISK_PipeHandle *p, int n)
1535 {
1536   return &p->fd[n];
1537 }
1538
1539 /**
1540  * Retrieve OS file handle
1541  * @internal
1542  * @param fh GNUnet file descriptor
1543  * @param dst destination buffer
1544  * @param dst_len length of dst
1545  * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
1546  */
1547 int
1548 GNUNET_internal_disk_file_handle (const struct GNUNET_DISK_FileHandle *fh,
1549     void *dst, unsigned int dst_len)
1550 {
1551 #ifdef MINGW
1552   if (dst_len < sizeof (HANDLE))
1553     return GNUNET_SYSERR;
1554   *((HANDLE *) dst) = fh->h;
1555 #else
1556   if (dst_len < sizeof(int))
1557     return GNUNET_SYSERR;
1558   *((int *) dst) = fh->fd;
1559 #endif
1560
1561   return GNUNET_OK;
1562 }
1563
1564 /* end of disk.c */