syn
[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 (flags & GNUNET_DISK_OPEN_READ)
1101     access = FILE_READ_DATA;
1102   if (flags & GNUNET_DISK_OPEN_WRITE)
1103     access = FILE_WRITE_DATA;
1104
1105   if (flags & GNUNET_DISK_OPEN_FAILIFEXISTS)
1106     {
1107       disp = CREATE_NEW;
1108     }
1109   else if (flags & GNUNET_DISK_OPEN_CREATE)
1110     {
1111       if (flags & GNUNET_DISK_OPEN_TRUNCATE)
1112         disp = CREATE_ALWAYS;
1113       else
1114         disp = OPEN_ALWAYS;
1115     }
1116   else if (flags & GNUNET_DISK_OPEN_TRUNCATE)
1117     {
1118       disp = TRUNCATE_EXISTING;
1119     }
1120   else
1121   {
1122     disp = OPEN_ALWAYS;
1123   }
1124
1125   /* TODO: access priviledges? */
1126   h = CreateFile (expfn, access, FILE_SHARE_DELETE | FILE_SHARE_READ
1127       | FILE_SHARE_WRITE, NULL, disp, FILE_ATTRIBUTE_NORMAL, NULL);
1128   if (h == INVALID_HANDLE_VALUE)
1129   {
1130     SetErrnoFromWinError (GetLastError ());
1131     GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "open", expfn);
1132     GNUNET_free (expfn);
1133     return NULL;
1134   }
1135
1136   if (flags & GNUNET_DISK_OPEN_APPEND)
1137     if (SetFilePointer (h, 0, 0, FILE_END) == INVALID_SET_FILE_POINTER)
1138     {
1139       SetErrnoFromWinError (GetLastError ());
1140       GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "SetFilePointer", expfn);
1141       CloseHandle (h);
1142       GNUNET_free (expfn);
1143       return NULL;
1144     }
1145 #endif
1146
1147   ret = GNUNET_malloc(sizeof(struct GNUNET_DISK_FileHandle));
1148 #ifdef MINGW
1149   ret->h = h;
1150 #else
1151   ret->fd = fd;
1152 #endif
1153   GNUNET_free (expfn);
1154   return ret;
1155 }
1156
1157 /**
1158  * Close an open file
1159  * @param h file handle
1160  * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
1161  */
1162 int
1163 GNUNET_DISK_file_close (struct GNUNET_DISK_FileHandle *h)
1164 {
1165   if (h == NULL)
1166     {
1167       errno = EINVAL;
1168       return GNUNET_SYSERR;
1169     }
1170
1171 #if MINGW
1172   if (!CloseHandle (h->h))
1173   {
1174     SetErrnoFromWinError (GetLastError ());
1175     GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "close");
1176     GNUNET_free (h);
1177     return GNUNET_SYSERR;
1178   }
1179 #else
1180   if (close (h->fd) != 0)
1181   {
1182     GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "close");
1183     GNUNET_free (h);
1184     return GNUNET_SYSERR;
1185   }
1186 #endif
1187   GNUNET_free (h);
1188   return GNUNET_OK;
1189 }
1190
1191 /**
1192  * Construct full path to a file inside of the private
1193  * directory used by GNUnet.  Also creates the corresponding
1194  * directory.  If the resulting name is supposed to be
1195  * a directory, end the last argument in '/' (or pass
1196  * DIR_SEPARATOR_STR as the last argument before NULL).
1197  *
1198  * @param cfg configuration to use (determines HOME)
1199  * @param serviceName name of the service
1200  * @param varargs is NULL-terminated list of
1201  *                path components to append to the
1202  *                private directory name.
1203  * @return the constructed filename
1204  */
1205 char *
1206 GNUNET_DISK_get_home_filename (const struct GNUNET_CONFIGURATION_Handle *cfg,
1207                                const char *serviceName, ...)
1208 {
1209   const char *c;
1210   char *pfx;
1211   char *ret;
1212   va_list ap;
1213   unsigned int needed;
1214
1215   if (GNUNET_OK !=
1216       GNUNET_CONFIGURATION_get_value_filename (cfg,
1217                                                serviceName, "HOME", &pfx))
1218     return NULL;
1219   if (pfx == NULL)
1220     {
1221       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1222                   _("No `%s' specified for service `%s' in configuration.\n"),
1223                   "HOME", serviceName);
1224       return NULL;
1225     }
1226   needed = strlen (pfx) + 2;
1227   if ((pfx[strlen (pfx) - 1] != '/') && (pfx[strlen (pfx) - 1] != '\\'))
1228     needed++;
1229   va_start (ap, serviceName);
1230   while (1)
1231     {
1232       c = va_arg (ap, const char *);
1233       if (c == NULL)
1234         break;
1235       needed += strlen (c);
1236       if ((c[strlen (c) - 1] != '/') && (c[strlen (c) - 1] != '\\'))
1237         needed++;
1238     }
1239   va_end (ap);
1240   ret = GNUNET_malloc (needed);
1241   strcpy (ret, pfx);
1242   GNUNET_free (pfx);
1243   va_start (ap, serviceName);
1244   while (1)
1245     {
1246       c = va_arg (ap, const char *);
1247       if (c == NULL)
1248         break;
1249       if ((c[strlen (c) - 1] != '/') && (c[strlen (c) - 1] != '\\'))
1250         strcat (ret, DIR_SEPARATOR_STR);
1251       strcat (ret, c);
1252     }
1253   va_end (ap);
1254   if ((ret[strlen (ret) - 1] != '/') && (ret[strlen (ret) - 1] != '\\'))
1255     GNUNET_DISK_directory_create_for_file (ret);
1256   else
1257     GNUNET_DISK_directory_create (ret);
1258   return ret;
1259 }
1260
1261 struct GNUNET_DISK_MapHandle
1262 {
1263 #ifdef MINGW
1264   HANDLE h;
1265 #else
1266   void *addr;
1267   size_t len;
1268 #endif
1269 };
1270
1271
1272 /**
1273  * Map a file into memory
1274  * @param h open file handle
1275  * @param m handle to the new mapping
1276  * @param access access specification, GNUNET_DISK_MAP_xxx
1277  * @param len size of the mapping
1278  * @return pointer to the mapped memory region, NULL on failure
1279  */
1280 void *
1281 GNUNET_DISK_file_map (const struct GNUNET_DISK_FileHandle *h, struct GNUNET_DISK_MapHandle **m,
1282     int access, size_t len)
1283 {
1284   if (h == NULL)
1285     {
1286       errno = EINVAL;
1287       return NULL;
1288     }
1289
1290 #ifdef MINGW
1291   DWORD mapAccess, protect;
1292   void *ret;
1293
1294   if (access & GNUNET_DISK_MAP_READ && access & GNUNET_DISK_MAP_WRITE)
1295     {
1296       protect = PAGE_READWRITE;
1297       mapAccess = FILE_MAP_ALL_ACCESS;
1298     }
1299   else if (access & GNUNET_DISK_MAP_READ)
1300     {
1301       protect = PAGE_READONLY;
1302       mapAccess = FILE_MAP_READ;
1303     }
1304   else if (access & GNUNET_DISK_MAP_WRITE)
1305     {
1306       protect = PAGE_READWRITE;
1307       mapAccess = FILE_MAP_WRITE;
1308     }
1309   else
1310     {
1311       GNUNET_break (0);
1312       return NULL;
1313     }
1314
1315   *m = GNUNET_malloc (sizeof (struct GNUNET_DISK_MapHandle));
1316   (*m)->h = CreateFileMapping (h->h, NULL, protect, 0, 0, NULL);
1317   if ((*m)->h == INVALID_HANDLE_VALUE)
1318     {
1319       SetErrnoFromWinError (GetLastError ());
1320       GNUNET_free (*m);
1321       return NULL;
1322     }
1323
1324   ret = MapViewOfFile ((*m)->h, mapAccess, 0, 0, len);
1325   if (!ret)
1326     {
1327       SetErrnoFromWinError (GetLastError ());
1328       CloseHandle ((*m)->h);
1329       GNUNET_free (*m);
1330     }
1331
1332   return ret;
1333 #else
1334   int prot;
1335
1336   prot = 0;
1337   if (access & GNUNET_DISK_MAP_READ)
1338     prot = PROT_READ;
1339   if (access & GNUNET_DISK_MAP_WRITE)
1340     prot |= PROT_WRITE;
1341   *m = GNUNET_malloc (sizeof (struct GNUNET_DISK_MapHandle));
1342   (*m)->addr = mmap (NULL, len, prot, MAP_SHARED, h->fd, 0);
1343   (*m)->len = len;
1344   return (*m)->addr;
1345 #endif
1346 }
1347
1348 /**
1349  * Unmap a file
1350  * @param h mapping handle
1351  * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
1352  */
1353 int
1354 GNUNET_DISK_file_unmap (struct GNUNET_DISK_MapHandle *h)
1355 {
1356   int ret;
1357   if (h == NULL)
1358     {
1359       errno = EINVAL;
1360       return GNUNET_SYSERR;
1361     }
1362
1363 #ifdef MINGW
1364   ret = UnmapViewOfFile (h->h) ? GNUNET_OK : GNUNET_SYSERR;
1365   if (ret != GNUNET_OK)
1366     SetErrnoFromWinError (GetLastError ());
1367   if (!CloseHandle (h->h) && (ret == GNUNET_OK))
1368     {
1369       ret = GNUNET_SYSERR;
1370       SetErrnoFromWinError (GetLastError ());
1371     }
1372 #else
1373   ret = munmap (h->addr, h->len) != -1 ? GNUNET_OK : GNUNET_SYSERR;
1374 #endif
1375   GNUNET_free (h);
1376   return ret;
1377 }
1378
1379
1380 /**
1381  * Write file changes to disk
1382  * @param h handle to an open file
1383  * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
1384  */
1385 int
1386 GNUNET_DISK_file_sync (const struct GNUNET_DISK_FileHandle *h)
1387 {
1388   if (h == NULL)
1389     {
1390       errno = EINVAL;
1391       return GNUNET_SYSERR;
1392     }
1393
1394 #ifdef MINGW
1395   int ret;
1396
1397   ret = FlushFileBuffers (h->h) ? GNUNET_OK : GNUNET_SYSERR;
1398   if (ret != GNUNET_OK)
1399     SetErrnoFromWinError (GetLastError ());
1400   return ret;
1401 #else
1402   return fsync (h->fd) == -1 ? GNUNET_SYSERR : GNUNET_OK;
1403 #endif
1404 }
1405
1406 /**
1407  * Creates an interprocess channel
1408  * @param blocking creates an asynchronous pipe if set to GNUNET_NO
1409  * @return handle to the new pipe, NULL on error
1410  */
1411 struct GNUNET_DISK_PipeHandle *
1412 GNUNET_DISK_pipe (int blocking)
1413 {
1414   struct GNUNET_DISK_PipeHandle *p;
1415   int err;
1416
1417   err = GNUNET_NO;
1418   p = GNUNET_malloc (sizeof (struct GNUNET_DISK_PipeHandle));
1419
1420 #ifndef MINGW
1421   int fd[2];
1422   int ret;
1423   int flags;
1424
1425   ret = pipe (fd);
1426   if (ret != -1)
1427     {
1428       p->fd[0].fd = fd[0];
1429       p->fd[1].fd = fd[1];
1430
1431       if (!blocking)
1432         {
1433           flags = fcntl (fd[0], F_GETFL);
1434           flags |= O_NONBLOCK;
1435           ret = fcntl (fd[0], F_SETFL, flags);
1436           if (ret != -1)
1437             {
1438               flags = fcntl (fd[1], F_GETFL);
1439               flags |= O_NONBLOCK;
1440               ret = fcntl (fd[1], F_SETFL, flags);
1441             }
1442           if (ret == -1)
1443             {
1444               GNUNET_log_strerror(GNUNET_ERROR_TYPE_ERROR, "fcntl");
1445               close (fd[0]);
1446               close (fd[1]);
1447               err = GNUNET_YES;
1448             }
1449         }
1450     }
1451   else
1452     err = GNUNET_YES;
1453 #else
1454   BOOL ret;
1455
1456   ret = CreatePipe (&p->fd[0].h, &p->fd[1].h, NULL, 0);
1457   if (ret)
1458     {
1459       if (!blocking)
1460         {
1461           DWORD mode;
1462
1463           mode = PIPE_NOWAIT;
1464           SetNamedPipeHandleState (p->fd[0].h, &mode, NULL, NULL);
1465           SetNamedPipeHandleState (p->fd[1].h, &mode, NULL, NULL);
1466           /* this always fails on Windows 95, so we don't care about error handling */
1467         }
1468     }
1469   else
1470       err = GNUNET_YES;
1471 #endif
1472
1473   if (GNUNET_YES == err)
1474     {
1475       GNUNET_free (p);
1476       p = NULL;
1477     }
1478
1479   return p;
1480 }
1481
1482 /**
1483  * Closes an interprocess channel
1484  * @param p pipe
1485  * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
1486  */
1487 int
1488 GNUNET_DISK_pipe_close (struct GNUNET_DISK_PipeHandle *p)
1489 {
1490   int ret;
1491
1492   ret = GNUNET_OK;
1493
1494 #ifdef MINGW
1495   if (!CloseHandle (p->fd[0].h))
1496     {
1497       SetErrnoFromWinError (GetLastError ());
1498       ret = GNUNET_SYSERR;
1499     }
1500
1501   if (!CloseHandle (p->fd[1].h))
1502     {
1503       SetErrnoFromWinError (GetLastError ());
1504       ret = GNUNET_SYSERR;
1505     }
1506 #else
1507   {
1508     int save;
1509
1510     if (close (p->fd[0].fd) != -1)
1511       {
1512         ret = GNUNET_SYSERR;
1513         save = errno;
1514       }
1515     else
1516       save = 0;
1517
1518     if (close (p->fd[1].fd) != -1)
1519       {
1520         ret = GNUNET_SYSERR;
1521       }
1522     else
1523       errno = save;
1524   }
1525 #endif
1526   GNUNET_free (p);
1527
1528   return ret;
1529 }
1530
1531 /**
1532  * Get the handle to a particular pipe end
1533  * @param p pipe
1534  * @param n number of the end
1535  */
1536 const struct GNUNET_DISK_FileHandle *
1537 GNUNET_DISK_pipe_handle (const struct GNUNET_DISK_PipeHandle *p, int n)
1538 {
1539   return &p->fd[n];
1540 }
1541
1542 /**
1543  * Retrieve OS file handle
1544  * @internal
1545  * @param fh GNUnet file descriptor
1546  * @param dst destination buffer
1547  * @param dst_len length of dst
1548  * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
1549  */
1550 int
1551 GNUNET_internal_disk_file_handle (const struct GNUNET_DISK_FileHandle *fh,
1552     void *dst, unsigned int dst_len)
1553 {
1554 #ifdef MINGW
1555   if (dst_len < sizeof (HANDLE))
1556     return GNUNET_SYSERR;
1557   *((HANDLE *) dst) = fh->h;
1558 #else
1559   if (dst_len < sizeof(int))
1560     return GNUNET_SYSERR;
1561   *((int *) dst) = fh->fd;
1562 #endif
1563
1564   return GNUNET_OK;
1565 }
1566
1567 /* end of disk.c */