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