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