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