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