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