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