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