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