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