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