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