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