Returns now GNUNET_SYSERR
[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   /**
815    * Function to call on directory entries.
816    */
817   GNUNET_DISK_DirectoryIteratorCallback callback;
818
819   /**
820    * Closure for callback.
821    */
822   void *callback_cls;
823
824   /**
825    * Reference to directory.
826    */
827   DIR *directory;
828
829   /**
830    * Directory name.
831    */
832   char *dirname;
833
834   /**
835    * Next filename to process.
836    */
837   char *next_name;
838
839   /**
840    * Our priority.
841    */
842   enum GNUNET_SCHEDULER_Priority priority;
843
844 };
845
846
847 /**
848  * Task used by the directory iterator.
849  */
850 static void
851 directory_iterator_task (void *cls,
852                          const struct GNUNET_SCHEDULER_TaskContext *tc)
853 {
854   struct GNUNET_DISK_DirectoryIterator *iter = cls;
855   char *name;
856
857   name = iter->next_name;
858   GNUNET_assert (name != NULL);
859   iter->next_name = NULL;
860   iter->callback (iter->callback_cls, iter, name, iter->dirname);
861   GNUNET_free (name);
862 }
863
864
865 /**
866  * This function must be called during the DiskIteratorCallback
867  * (exactly once) to schedule the task to process the next
868  * filename in the directory (if there is one).
869  *
870  * @param iter opaque handle for the iterator
871  * @param can set to GNUNET_YES to terminate the iteration early
872  * @return GNUNET_YES if iteration will continue,
873  *         GNUNET_NO if this was the last entry (and iteration is complete),
874  *         GNUNET_SYSERR if abort was YES
875  */
876 int
877 GNUNET_DISK_directory_iterator_next (struct GNUNET_DISK_DirectoryIterator
878                                      *iter, int can)
879 {
880   struct dirent *finfo;
881
882   GNUNET_assert (iter->next_name == NULL);
883   if (can == GNUNET_YES)
884     {
885       closedir (iter->directory);
886       GNUNET_free (iter->dirname);
887       GNUNET_free (iter);
888       return GNUNET_SYSERR;
889     }
890   while (NULL != (finfo = readdir (iter->directory)))
891     {
892       if ((0 == strcmp (finfo->d_name, ".")) ||
893           (0 == strcmp (finfo->d_name, "..")))
894         continue;
895       GNUNET_asprintf (&iter->next_name,
896                        "%s%s%s",
897                        iter->dirname, DIR_SEPARATOR_STR, finfo->d_name);
898       break;
899     }
900   if (finfo == NULL)
901     {
902       GNUNET_DISK_directory_iterator_next (iter, GNUNET_YES);
903       return GNUNET_NO;
904     }
905   GNUNET_SCHEDULER_add_with_priority (iter->priority,
906                                       &directory_iterator_task, iter);
907   return GNUNET_YES;
908 }
909
910
911 /**
912  * Scan a directory for files using the scheduler to run a task for
913  * each entry.  The name of the directory must be expanded first (!).
914  * If a scheduler does not need to be used, GNUNET_DISK_directory_scan
915  * may provide a simpler API.
916  *
917  * @param prio priority to use
918  * @param dirName the name of the directory
919  * @param callback the method to call for each file
920  * @param callback_cls closure for callback
921  */
922 void
923 GNUNET_DISK_directory_iterator_start (enum GNUNET_SCHEDULER_Priority prio,
924                                       const char *dirName,
925                                       GNUNET_DISK_DirectoryIteratorCallback
926                                       callback, void *callback_cls)
927 {
928   struct GNUNET_DISK_DirectoryIterator *di;
929
930   di = GNUNET_malloc (sizeof (struct GNUNET_DISK_DirectoryIterator));
931   di->callback = callback;
932   di->callback_cls = callback_cls;
933   di->directory = OPENDIR (dirName);
934   if (di->directory == NULL)
935     {
936       GNUNET_free (di);
937       callback (callback_cls, NULL, NULL, NULL);
938       return;
939     }
940   di->dirname = GNUNET_strdup (dirName);
941   di->priority = prio;
942   GNUNET_DISK_directory_iterator_next (di, GNUNET_NO);
943 }
944
945
946 /**
947  * Function that removes the given directory by calling
948  * "GNUNET_DISK_directory_remove".
949  *
950  * @param unused not used
951  * @param fn directory to remove
952  * @return GNUNET_OK
953  */
954 static int
955 remove_helper (void *unused, const char *fn)
956 {
957   (void) GNUNET_DISK_directory_remove (fn);
958   return GNUNET_OK;
959 }
960
961
962 /**
963  * Remove all files in a directory (rm -rf). Call with
964  * caution.
965  *
966  *
967  * @param fileName the file to remove
968  * @return GNUNET_OK on success, GNUNET_SYSERR on error
969  */
970 int
971 GNUNET_DISK_directory_remove (const char *fileName)
972 {
973   struct stat istat;
974
975   if (0 != LSTAT (fileName, &istat))
976     return GNUNET_NO;           /* file may not exist... */
977   if (UNLINK (fileName) == 0)
978     return GNUNET_OK;
979   if ((errno != EISDIR) &&
980       /* EISDIR is not sufficient in all cases, e.g.
981          sticky /tmp directory may result in EPERM on BSD.
982          So we also explicitly check "isDirectory" */
983       (GNUNET_YES != GNUNET_DISK_directory_test (fileName)))
984     {
985       GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "rmdir", fileName);
986       return GNUNET_SYSERR;
987     }
988   if (GNUNET_SYSERR ==
989       GNUNET_DISK_directory_scan (fileName, &remove_helper, NULL))
990     return GNUNET_SYSERR;
991   if (0 != RMDIR (fileName))
992     {
993       GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "rmdir", fileName);
994       return GNUNET_SYSERR;
995     }
996   return GNUNET_OK;
997 }
998
999
1000 /**
1001  * Copy a file.
1002  *
1003  * @param src file to copy
1004  * @param dst destination file name
1005  * @return GNUNET_OK on success, GNUNET_SYSERR on error
1006  */
1007 int
1008 GNUNET_DISK_file_copy (const char *src, const char *dst)
1009 {
1010   char *buf;
1011   uint64_t pos;
1012   uint64_t size;
1013   size_t len;
1014   struct GNUNET_DISK_FileHandle *in;
1015   struct GNUNET_DISK_FileHandle *out;
1016
1017   if (GNUNET_OK != GNUNET_DISK_file_size (src, &size, GNUNET_YES))
1018     return GNUNET_SYSERR;
1019   pos = 0;
1020   in = GNUNET_DISK_file_open (src, GNUNET_DISK_OPEN_READ,
1021                               GNUNET_DISK_PERM_NONE);
1022   if (!in)
1023     return GNUNET_SYSERR;
1024   out = GNUNET_DISK_file_open (dst, GNUNET_DISK_OPEN_WRITE
1025                                | GNUNET_DISK_OPEN_CREATE |
1026                                GNUNET_DISK_OPEN_FAILIFEXISTS,
1027                                GNUNET_DISK_PERM_USER_READ |
1028                                GNUNET_DISK_PERM_USER_WRITE |
1029                                GNUNET_DISK_PERM_GROUP_READ |
1030                                GNUNET_DISK_PERM_GROUP_WRITE);
1031   if (!out)
1032     {
1033       GNUNET_DISK_file_close (in);
1034       return GNUNET_SYSERR;
1035     }
1036   buf = GNUNET_malloc (COPY_BLK_SIZE);
1037   while (pos < size)
1038     {
1039       len = COPY_BLK_SIZE;
1040       if (len > size - pos)
1041         len = size - pos;
1042       if (len != GNUNET_DISK_file_read (in, buf, len))
1043         goto FAIL;
1044       if (len != GNUNET_DISK_file_write (out, buf, len))
1045         goto FAIL;
1046       pos += len;
1047     }
1048   GNUNET_free (buf);
1049   GNUNET_DISK_file_close (in);
1050   GNUNET_DISK_file_close (out);
1051   return GNUNET_OK;
1052 FAIL:
1053   GNUNET_free (buf);
1054   GNUNET_DISK_file_close (in);
1055   GNUNET_DISK_file_close (out);
1056   return GNUNET_SYSERR;
1057 }
1058
1059
1060 /**
1061  * @brief Removes special characters as ':' from a filename.
1062  * @param fn the filename to canonicalize
1063  */
1064 void
1065 GNUNET_DISK_filename_canonicalize (char *fn)
1066 {
1067   char *idx;
1068   char c;
1069
1070   idx = fn;
1071   while (*idx)
1072     {
1073       c = *idx;
1074
1075       if (c == '/' || c == '\\' || c == ':' || c == '*' || c == '?' ||
1076           c == '"' || c == '<' || c == '>' || c == '|')
1077         {
1078           *idx = '_';
1079         }
1080
1081       idx++;
1082     }
1083 }
1084
1085
1086
1087 /**
1088  * @brief Change owner of a file
1089  *
1090  * @param filename name of file to change the owner of
1091  * @param user name of the new owner
1092  * @return GNUNET_OK on success, GNUNET_SYSERR on failure
1093  */
1094 int
1095 GNUNET_DISK_file_change_owner (const char *filename, const char *user)
1096 {
1097 #ifndef MINGW
1098   struct passwd *pws;
1099
1100   pws = getpwnam (user);
1101   if (pws == NULL)
1102     {
1103       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1104                   _("Cannot obtain information about user `%s': %s\n"),
1105                   user, STRERROR (errno));
1106       return GNUNET_SYSERR;
1107     }
1108   if (0 != chown (filename, pws->pw_uid, pws->pw_gid))
1109     GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "chown", filename);
1110 #endif
1111   return GNUNET_OK;
1112 }
1113
1114
1115 /**
1116  * Lock a part of a file
1117  * @param fh file handle
1118  * @param lockStart absolute position from where to lock
1119  * @param lockEnd absolute position until where to lock
1120  * @param excl GNUNET_YES for an exclusive lock
1121  * @return GNUNET_OK on success, GNUNET_SYSERR on error
1122  */
1123 int
1124 GNUNET_DISK_file_lock (struct GNUNET_DISK_FileHandle *fh, off_t lockStart,
1125                        off_t lockEnd, int excl)
1126 {
1127   if (fh == NULL)
1128     {
1129       errno = EINVAL;
1130       return GNUNET_SYSERR;
1131     }
1132
1133 #ifndef MINGW
1134   struct flock fl;
1135
1136   memset (&fl, 0, sizeof (struct flock));
1137   fl.l_type = excl ? F_WRLCK : F_RDLCK;
1138   fl.l_whence = SEEK_SET;
1139   fl.l_start = lockStart;
1140   fl.l_len = lockEnd;
1141
1142   return fcntl (fh->fd, F_SETLK, &fl) != 0 ? GNUNET_SYSERR : GNUNET_OK;
1143 #else
1144   OVERLAPPED o;
1145
1146   memset (&o, 0, sizeof (OVERLAPPED));
1147   o.Offset = lockStart;
1148
1149   if (!LockFileEx (fh->h, (excl ? LOCKFILE_EXCLUSIVE_LOCK : 0)
1150                    | LOCKFILE_FAIL_IMMEDIATELY, 0, lockEnd - lockStart, 0,
1151                    &o))
1152     {
1153       SetErrnoFromWinError (GetLastError ());
1154       return GNUNET_SYSERR;
1155     }
1156
1157   return GNUNET_OK;
1158 #endif
1159 }
1160
1161
1162 /**
1163  * Unlock a part of a file
1164  * @param fh file handle
1165  * @param unlockStart absolute position from where to unlock
1166  * @param unlockEnd absolute position until where to unlock
1167  * @return GNUNET_OK on success, GNUNET_SYSERR on error
1168  */
1169 int
1170 GNUNET_DISK_file_unlock (struct GNUNET_DISK_FileHandle *fh, off_t unlockStart,
1171                          off_t unlockEnd)
1172 {
1173   if (fh == NULL)
1174     {
1175       errno = EINVAL;
1176       return GNUNET_SYSERR;
1177     }
1178
1179 #ifndef MINGW
1180   struct flock fl;
1181
1182   memset (&fl, 0, sizeof (struct flock));
1183   fl.l_type = F_UNLCK;
1184   fl.l_whence = SEEK_SET;
1185   fl.l_start = unlockStart;
1186   fl.l_len = unlockEnd;
1187
1188   return fcntl (fh->fd, F_SETLK, &fl) != 0 ? GNUNET_SYSERR : GNUNET_OK;
1189 #else
1190   OVERLAPPED o;
1191
1192   memset (&o, 0, sizeof (OVERLAPPED));
1193   o.Offset = unlockStart;
1194
1195   if (!UnlockFileEx (fh->h, 0, unlockEnd - unlockStart, 0, &o))
1196     {
1197       SetErrnoFromWinError (GetLastError ());
1198       return GNUNET_SYSERR;
1199     }
1200
1201   return GNUNET_OK;
1202 #endif
1203 }
1204
1205
1206 /**
1207  * Open a file.  Note that the access permissions will only be
1208  * used if a new file is created and if the underlying operating
1209  * system supports the given permissions.
1210  *
1211  * @param fn file name to be opened
1212  * @param flags opening flags, a combination of GNUNET_DISK_OPEN_xxx bit flags
1213  * @param perm permissions for the newly created file, use
1214  *             GNUNET_DISK_PERM_USER_NONE if a file could not be created by this
1215  *             call (because of flags)
1216  * @return IO handle on success, NULL on error
1217  */
1218 struct GNUNET_DISK_FileHandle *
1219 GNUNET_DISK_file_open (const char *fn,
1220                        enum GNUNET_DISK_OpenFlags flags,
1221                        enum GNUNET_DISK_AccessPermissions perm)
1222 {
1223   char *expfn;
1224   struct GNUNET_DISK_FileHandle *ret;
1225 #ifdef MINGW
1226   DWORD access;
1227   DWORD disp;
1228   HANDLE h;
1229 #else
1230   int oflags;
1231   int mode;
1232   int fd;
1233 #endif
1234
1235   expfn = GNUNET_STRINGS_filename_expand (fn);
1236   if (NULL == expfn)
1237     return NULL;
1238 #ifndef MINGW
1239   mode = 0;
1240   if (GNUNET_DISK_OPEN_READWRITE == (flags & GNUNET_DISK_OPEN_READWRITE))
1241     oflags = O_RDWR;            /* note: O_RDWR is NOT always O_RDONLY | O_WRONLY */
1242   else if (flags & GNUNET_DISK_OPEN_READ)
1243     oflags = O_RDONLY;
1244   else if (flags & GNUNET_DISK_OPEN_WRITE)
1245     oflags = O_WRONLY;
1246   else
1247     {
1248       GNUNET_break (0);
1249       GNUNET_free (expfn);
1250       return NULL;
1251     }
1252   if (flags & GNUNET_DISK_OPEN_FAILIFEXISTS)
1253     oflags |= (O_CREAT | O_EXCL);
1254   if (flags & GNUNET_DISK_OPEN_TRUNCATE)
1255     oflags |= O_TRUNC;
1256   if (flags & GNUNET_DISK_OPEN_APPEND)
1257     oflags |= O_APPEND;
1258   if (flags & GNUNET_DISK_OPEN_CREATE)
1259     {
1260       (void) GNUNET_DISK_directory_create_for_file (expfn);
1261       oflags |= O_CREAT;
1262       if (perm & GNUNET_DISK_PERM_USER_READ)
1263         mode |= S_IRUSR;
1264       if (perm & GNUNET_DISK_PERM_USER_WRITE)
1265         mode |= S_IWUSR;
1266       if (perm & GNUNET_DISK_PERM_USER_EXEC)
1267         mode |= S_IXUSR;
1268       if (perm & GNUNET_DISK_PERM_GROUP_READ)
1269         mode |= S_IRGRP;
1270       if (perm & GNUNET_DISK_PERM_GROUP_WRITE)
1271         mode |= S_IWGRP;
1272       if (perm & GNUNET_DISK_PERM_GROUP_EXEC)
1273         mode |= S_IXGRP;
1274       if (perm & GNUNET_DISK_PERM_OTHER_READ)
1275         mode |= S_IROTH;
1276       if (perm & GNUNET_DISK_PERM_OTHER_WRITE)
1277         mode |= S_IWOTH;
1278       if (perm & GNUNET_DISK_PERM_OTHER_EXEC)
1279         mode |= S_IXOTH;
1280     }
1281
1282   fd = open (expfn, oflags | O_LARGEFILE, mode);
1283   if (fd == -1)
1284     {
1285       if (0 == (flags & GNUNET_DISK_OPEN_FAILIFEXISTS))
1286         GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "open", expfn);
1287       else
1288         GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_DEBUG, "open", expfn);
1289       GNUNET_free (expfn);
1290       return NULL;
1291     }
1292 #else
1293   access = 0;
1294   disp = OPEN_ALWAYS;
1295
1296   if (GNUNET_DISK_OPEN_READWRITE == (flags & GNUNET_DISK_OPEN_READWRITE))
1297     access = FILE_READ_DATA | FILE_WRITE_DATA;
1298   else if (flags & GNUNET_DISK_OPEN_READ)
1299     access = FILE_READ_DATA;
1300   else if (flags & GNUNET_DISK_OPEN_WRITE)
1301     access = FILE_WRITE_DATA;
1302
1303   if (flags & GNUNET_DISK_OPEN_FAILIFEXISTS)
1304     {
1305       disp = CREATE_NEW;
1306     }
1307   else if (flags & GNUNET_DISK_OPEN_CREATE)
1308     {
1309       if (flags & GNUNET_DISK_OPEN_TRUNCATE)
1310         disp = CREATE_ALWAYS;
1311       else
1312         disp = OPEN_ALWAYS;
1313     }
1314   else if (flags & GNUNET_DISK_OPEN_TRUNCATE)
1315     {
1316       disp = TRUNCATE_EXISTING;
1317     }
1318   else
1319     {
1320       disp = OPEN_EXISTING;
1321     }
1322
1323   /* TODO: access priviledges? */
1324   h = CreateFile (expfn, access, FILE_SHARE_DELETE | FILE_SHARE_READ
1325                   | FILE_SHARE_WRITE, NULL, disp, FILE_ATTRIBUTE_NORMAL,
1326                   NULL);
1327   if (h == INVALID_HANDLE_VALUE)
1328     {
1329       SetErrnoFromWinError (GetLastError ());
1330       GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "open", expfn);
1331       GNUNET_free (expfn);
1332       return NULL;
1333     }
1334
1335   if (flags & GNUNET_DISK_OPEN_APPEND)
1336     if (SetFilePointer (h, 0, 0, FILE_END) == INVALID_SET_FILE_POINTER)
1337       {
1338         SetErrnoFromWinError (GetLastError ());
1339         GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "SetFilePointer",
1340                                   expfn);
1341         CloseHandle (h);
1342         GNUNET_free (expfn);
1343         return NULL;
1344       }
1345 #endif
1346
1347   ret = GNUNET_malloc (sizeof (struct GNUNET_DISK_FileHandle));
1348 #ifdef MINGW
1349   ret->h = h;
1350 #else
1351   ret->fd = fd;
1352 #endif
1353   GNUNET_free (expfn);
1354   return ret;
1355 }
1356
1357
1358 /**
1359  * Close an open file
1360  * @param h file handle
1361  * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
1362  */
1363 int
1364 GNUNET_DISK_file_close (struct GNUNET_DISK_FileHandle *h)
1365 {
1366   if (h == NULL)
1367     {
1368       errno = EINVAL;
1369       return GNUNET_SYSERR;
1370     }
1371
1372 #if MINGW
1373   if (!CloseHandle (h->h))
1374     {
1375       SetErrnoFromWinError (GetLastError ());
1376       GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "close");
1377       GNUNET_free (h);
1378       return GNUNET_SYSERR;
1379     }
1380 #else
1381   if (close (h->fd) != 0)
1382     {
1383       GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "close");
1384       GNUNET_free (h);
1385       return GNUNET_SYSERR;
1386     }
1387 #endif
1388   GNUNET_free (h);
1389   return GNUNET_OK;
1390 }
1391
1392
1393 /**
1394  * Construct full path to a file inside of the private
1395  * directory used by GNUnet.  Also creates the corresponding
1396  * directory.  If the resulting name is supposed to be
1397  * a directory, end the last argument in '/' (or pass
1398  * DIR_SEPARATOR_STR as the last argument before NULL).
1399  *
1400  * @param cfg configuration to use (determines HOME)
1401  * @param serviceName name of the service
1402  * @param ... is NULL-terminated list of
1403  *                path components to append to the
1404  *                private directory name.
1405  * @return the constructed filename
1406  */
1407 char *
1408 GNUNET_DISK_get_home_filename (const struct GNUNET_CONFIGURATION_Handle *cfg,
1409                                const char *serviceName, ...)
1410 {
1411   const char *c;
1412   char *pfx;
1413   char *ret;
1414   va_list ap;
1415   unsigned int needed;
1416
1417   if (GNUNET_OK !=
1418       GNUNET_CONFIGURATION_get_value_filename (cfg,
1419                                                serviceName, "HOME", &pfx))
1420     return NULL;
1421   if (pfx == NULL)
1422     {
1423       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1424                   _("No `%s' specified for service `%s' in configuration.\n"),
1425                   "HOME", serviceName);
1426       return NULL;
1427     }
1428   needed = strlen (pfx) + 2;
1429   if ((pfx[strlen (pfx) - 1] != '/') && (pfx[strlen (pfx) - 1] != '\\'))
1430     needed++;
1431   va_start (ap, serviceName);
1432   while (1)
1433     {
1434       c = va_arg (ap, const char *);
1435       if (c == NULL)
1436         break;
1437       needed += strlen (c);
1438       if ((c[strlen (c) - 1] != '/') && (c[strlen (c) - 1] != '\\'))
1439         needed++;
1440     }
1441   va_end (ap);
1442   ret = GNUNET_malloc (needed);
1443   strcpy (ret, pfx);
1444   GNUNET_free (pfx);
1445   va_start (ap, serviceName);
1446   while (1)
1447     {
1448       c = va_arg (ap, const char *);
1449       if (c == NULL)
1450         break;
1451       if ((c[strlen (c) - 1] != '/') && (c[strlen (c) - 1] != '\\'))
1452         strcat (ret, DIR_SEPARATOR_STR);
1453       strcat (ret, c);
1454     }
1455   va_end (ap);
1456   if ((ret[strlen (ret) - 1] != '/') && (ret[strlen (ret) - 1] != '\\'))
1457     GNUNET_DISK_directory_create_for_file (ret);
1458   else
1459     GNUNET_DISK_directory_create (ret);
1460   return ret;
1461 }
1462
1463
1464 /**
1465  * Handle for a memory-mapping operation.
1466  */
1467 struct GNUNET_DISK_MapHandle
1468 {
1469 #ifdef MINGW
1470   /**
1471    * Underlying OS handle.
1472    */
1473   HANDLE h;
1474 #else
1475   /**
1476    * Address where the map is in memory.
1477    */
1478   void *addr;
1479
1480   /**
1481    * Number of bytes mapped.
1482    */
1483   size_t len;
1484 #endif
1485 };
1486
1487
1488 #ifndef MAP_FAILED
1489 #define MAP_FAILED ((void *) -1)
1490 #endif
1491
1492 /**
1493  * Map a file into memory
1494  *
1495  * @param h open file handle
1496  * @param m handle to the new mapping
1497  * @param access access specification, GNUNET_DISK_MAP_TYPE_xxx
1498  * @param len size of the mapping
1499  * @return pointer to the mapped memory region, NULL on failure
1500  */
1501 void *
1502 GNUNET_DISK_file_map (const struct GNUNET_DISK_FileHandle *h,
1503                       struct GNUNET_DISK_MapHandle **m,
1504                       enum GNUNET_DISK_MapType access, size_t len)
1505 {
1506   if (h == NULL)
1507     {
1508       errno = EINVAL;
1509       return NULL;
1510     }
1511
1512 #ifdef MINGW
1513   DWORD mapAccess, protect;
1514   void *ret;
1515
1516   if ((access & GNUNET_DISK_MAP_TYPE_READ) &&
1517       (access & GNUNET_DISK_MAP_TYPE_WRITE))
1518     {
1519       protect = PAGE_READWRITE;
1520       mapAccess = FILE_MAP_ALL_ACCESS;
1521     }
1522   else if (access & GNUNET_DISK_MAP_TYPE_READ)
1523     {
1524       protect = PAGE_READONLY;
1525       mapAccess = FILE_MAP_READ;
1526     }
1527   else if (access & GNUNET_DISK_MAP_TYPE_WRITE)
1528     {
1529       protect = PAGE_READWRITE;
1530       mapAccess = FILE_MAP_WRITE;
1531     }
1532   else
1533     {
1534       GNUNET_break (0);
1535       return NULL;
1536     }
1537
1538   *m = GNUNET_malloc (sizeof (struct GNUNET_DISK_MapHandle));
1539   (*m)->h = CreateFileMapping (h->h, NULL, protect, 0, 0, NULL);
1540   if ((*m)->h == INVALID_HANDLE_VALUE)
1541     {
1542       SetErrnoFromWinError (GetLastError ());
1543       GNUNET_free (*m);
1544       return NULL;
1545     }
1546
1547   ret = MapViewOfFile ((*m)->h, mapAccess, 0, 0, len);
1548   if (!ret)
1549     {
1550       SetErrnoFromWinError (GetLastError ());
1551       CloseHandle ((*m)->h);
1552       GNUNET_free (*m);
1553     }
1554
1555   return ret;
1556 #else
1557   int prot;
1558
1559   prot = 0;
1560   if (access & GNUNET_DISK_MAP_TYPE_READ)
1561     prot = PROT_READ;
1562   if (access & GNUNET_DISK_MAP_TYPE_WRITE)
1563     prot |= PROT_WRITE;
1564   *m = GNUNET_malloc (sizeof (struct GNUNET_DISK_MapHandle));
1565   (*m)->addr = mmap (NULL, len, prot, MAP_SHARED, h->fd, 0);
1566   GNUNET_assert (NULL != (*m)->addr);
1567   if (MAP_FAILED == (*m)->addr)
1568     {    
1569       GNUNET_free (*m);
1570       return NULL;
1571     }
1572   (*m)->len = len;
1573   return (*m)->addr;
1574 #endif
1575 }
1576
1577 /**
1578  * Unmap a file
1579  * @param h mapping handle
1580  * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
1581  */
1582 int
1583 GNUNET_DISK_file_unmap (struct GNUNET_DISK_MapHandle *h)
1584 {
1585   int ret;
1586   if (h == NULL)
1587     {
1588       errno = EINVAL;
1589       return GNUNET_SYSERR;
1590     }
1591
1592 #ifdef MINGW
1593   ret = UnmapViewOfFile (h->h) ? GNUNET_OK : GNUNET_SYSERR;
1594   if (ret != GNUNET_OK)
1595     SetErrnoFromWinError (GetLastError ());
1596   if (!CloseHandle (h->h) && (ret == GNUNET_OK))
1597     {
1598       ret = GNUNET_SYSERR;
1599       SetErrnoFromWinError (GetLastError ());
1600     }
1601 #else
1602   ret = munmap (h->addr, h->len) != -1 ? GNUNET_OK : GNUNET_SYSERR;
1603 #endif
1604   GNUNET_free (h);
1605   return ret;
1606 }
1607
1608
1609 /**
1610  * Write file changes to disk
1611  * @param h handle to an open file
1612  * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
1613  */
1614 int
1615 GNUNET_DISK_file_sync (const struct GNUNET_DISK_FileHandle *h)
1616 {
1617   if (h == NULL)
1618     {
1619       errno = EINVAL;
1620       return GNUNET_SYSERR;
1621     }
1622
1623 #ifdef MINGW
1624   int ret;
1625
1626   ret = FlushFileBuffers (h->h) ? GNUNET_OK : GNUNET_SYSERR;
1627   if (ret != GNUNET_OK)
1628     SetErrnoFromWinError (GetLastError ());
1629   return ret;
1630 #elif defined(FREEBSD) || defined(OPENBSD) || defined(DARWIN)
1631   return fsync (h->fd) == -1 ? GNUNET_SYSERR : GNUNET_OK;
1632 #else
1633   return fdatasync (h->fd) == -1 ? GNUNET_SYSERR : GNUNET_OK;
1634 #endif
1635 }
1636
1637
1638 /**
1639  * Creates an interprocess channel
1640  *
1641  * @param blocking creates an asynchronous pipe if set to GNUNET_NO
1642  * @param inherit_read inherit the parent processes stdin (only for windows)
1643  * @param inherit_write inherit the parent processes stdout (only for windows)
1644  *
1645  * @return handle to the new pipe, NULL on error
1646  */
1647 struct GNUNET_DISK_PipeHandle *
1648 GNUNET_DISK_pipe (int blocking, int inherit_read, int inherit_write)
1649 {
1650   struct GNUNET_DISK_PipeHandle *p;
1651   struct GNUNET_DISK_FileHandle *fds;
1652
1653   p =
1654     GNUNET_malloc (sizeof (struct GNUNET_DISK_PipeHandle) +
1655                    2 * sizeof (struct GNUNET_DISK_FileHandle));
1656   fds = (struct GNUNET_DISK_FileHandle *) &p[1];
1657   p->fd[0] = &fds[0];
1658   p->fd[1] = &fds[1];
1659 #ifndef MINGW
1660   int fd[2];
1661   int ret;
1662   int flags;
1663   int eno;
1664
1665   ret = pipe (fd);
1666   if (ret == -1)
1667     {
1668       eno = errno;
1669       GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "pipe");
1670       GNUNET_free (p);
1671       errno = eno;
1672       return NULL;
1673     }
1674   p->fd[0]->fd = fd[0];
1675   p->fd[1]->fd = fd[1];
1676   ret = 0;
1677   flags = fcntl (fd[0], F_GETFL);
1678   if (!blocking)
1679     flags |= O_NONBLOCK;
1680   if (0 > fcntl (fd[0], F_SETFL, flags))
1681     ret = -1;
1682   flags = fcntl (fd[0], F_GETFD);
1683   flags |= FD_CLOEXEC;
1684   if (0 > fcntl (fd[0], F_SETFD, flags))
1685     ret = -1;
1686
1687   flags = fcntl (fd[1], F_GETFL);
1688   if (!blocking)
1689     flags |= O_NONBLOCK;
1690   if (0 > fcntl (fd[1], F_SETFL, flags))
1691     ret = -1;
1692   flags = fcntl (fd[1], F_GETFD);
1693   flags |= FD_CLOEXEC;
1694   if (0 > fcntl (fd[1], F_SETFD, flags))
1695     ret = -1;
1696   if (ret == -1)
1697     {
1698       eno = errno;
1699       GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "fcntl");
1700       GNUNET_break (0 == close (p->fd[0]->fd));
1701       GNUNET_break (0 == close (p->fd[1]->fd));
1702       GNUNET_free (p);
1703       errno = eno;
1704       return NULL;    
1705     }
1706 #else
1707   BOOL ret;
1708   HANDLE tmp_handle;
1709
1710   ret = CreatePipe (&p->fd[0]->h, &p->fd[1]->h, NULL, 0);
1711   if (!ret)
1712     {
1713       GNUNET_free (p);
1714       SetErrnoFromWinError (GetLastError ());
1715       return NULL;
1716     }
1717   if (!DuplicateHandle (GetCurrentProcess (), p->fd[0]->h,
1718                 GetCurrentProcess (), &tmp_handle, 0, inherit_read == GNUNET_YES ? TRUE : FALSE,
1719                         DUPLICATE_SAME_ACCESS))
1720         {
1721           SetErrnoFromWinError (GetLastError ());
1722           CloseHandle (p->fd[0]->h);
1723           CloseHandle (p->fd[1]->h);
1724           GNUNET_free (p);
1725           return NULL;
1726         }
1727         CloseHandle (p->fd[0]->h);
1728         p->fd[0]->h = tmp_handle;
1729
1730         if (!DuplicateHandle (GetCurrentProcess (), p->fd[1]->h,
1731                         GetCurrentProcess (), &tmp_handle, 0, inherit_write == GNUNET_YES ? TRUE : FALSE,
1732                         DUPLICATE_SAME_ACCESS))
1733         {
1734           SetErrnoFromWinError (GetLastError ());
1735           CloseHandle (p->fd[0]->h);
1736           CloseHandle (p->fd[1]->h);
1737           GNUNET_free (p);
1738           return NULL;
1739         }
1740   CloseHandle (p->fd[1]->h);
1741   p->fd[1]->h = tmp_handle;
1742   if (!blocking)
1743     {
1744       DWORD mode;
1745
1746       mode = PIPE_NOWAIT;
1747       SetNamedPipeHandleState (p->fd[0]->h, &mode, NULL, NULL);
1748       SetNamedPipeHandleState (p->fd[1]->h, &mode, NULL, NULL);
1749       /* this always fails on Windows 95, so we don't care about error handling */
1750     }
1751 #endif
1752   return p;
1753 }
1754
1755
1756 /**
1757  * Closes an interprocess channel
1758  *
1759  * @param p pipe to close
1760  * @param end which end of the pipe to close
1761  * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
1762  */
1763 int
1764 GNUNET_DISK_pipe_close_end (struct GNUNET_DISK_PipeHandle *p,
1765                             enum GNUNET_DISK_PipeEnd end)
1766 {
1767   int ret = GNUNET_OK;
1768   int save;
1769
1770 #ifdef MINGW
1771   if (end == GNUNET_DISK_PIPE_END_READ)
1772     {
1773       if (!CloseHandle (p->fd[0]->h))
1774         {
1775           SetErrnoFromWinError (GetLastError ());
1776           ret = GNUNET_SYSERR;
1777         }
1778       p->fd[0]->h = INVALID_HANDLE_VALUE;
1779     }
1780   else if (end == GNUNET_DISK_PIPE_END_WRITE)
1781     {
1782       if (!CloseHandle (p->fd[1]->h))
1783         {
1784           SetErrnoFromWinError (GetLastError ());
1785           ret = GNUNET_SYSERR;
1786         }
1787       p->fd[1]->h = INVALID_HANDLE_VALUE;
1788     }
1789   save = errno;
1790 #else
1791   save = 0;
1792   if (end == GNUNET_DISK_PIPE_END_READ)
1793     {
1794       if (0 != close (p->fd[0]->fd))
1795         {
1796           ret = GNUNET_SYSERR;
1797           save = errno;
1798         }
1799       p->fd[0]->fd = -1;
1800     }
1801   else if (end == GNUNET_DISK_PIPE_END_WRITE)
1802     {
1803       if (0 != close (p->fd[1]->fd))
1804         {
1805           ret = GNUNET_SYSERR;
1806           save = errno;
1807         }
1808       p->fd[1]->fd = -1;
1809     }
1810 #endif
1811   errno = save;
1812   return ret;
1813 }
1814
1815 /**
1816  * Closes an interprocess channel
1817  *
1818  * @param p pipe to close
1819  * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
1820  */
1821 int
1822 GNUNET_DISK_pipe_close (struct GNUNET_DISK_PipeHandle *p)
1823 {
1824   int ret = GNUNET_OK;
1825   int save;
1826
1827 #ifdef MINGW
1828   if (!CloseHandle (p->fd[0]->h))
1829     {
1830       SetErrnoFromWinError (GetLastError ());
1831       ret = GNUNET_SYSERR;
1832     }
1833   if (!CloseHandle (p->fd[1]->h))
1834     {
1835       SetErrnoFromWinError (GetLastError ());
1836       ret = GNUNET_SYSERR;
1837     }
1838   save = errno;
1839 #else
1840   save = 0;
1841   if (p->fd[0]->fd != -1)
1842     {
1843       if (0 != close (p->fd[0]->fd))
1844         {
1845           ret = GNUNET_SYSERR;
1846           save = errno;
1847         }
1848     }
1849
1850   if (p->fd[1]->fd != -1)
1851     {
1852       if (0 != close (p->fd[1]->fd))
1853         {
1854           ret = GNUNET_SYSERR;
1855           save = errno;
1856         }
1857     }
1858 #endif
1859   GNUNET_free (p);
1860   errno = save;
1861   return ret;
1862 }
1863
1864
1865 /**
1866  * Get the handle to a particular pipe end
1867  *
1868  * @param p pipe
1869  * @param n end to access
1870  * @return handle for the respective end
1871  */
1872 const struct GNUNET_DISK_FileHandle *
1873 GNUNET_DISK_pipe_handle (const struct GNUNET_DISK_PipeHandle *p,
1874                          enum GNUNET_DISK_PipeEnd n)
1875 {
1876   switch (n)
1877     {
1878     case GNUNET_DISK_PIPE_END_READ:
1879     case GNUNET_DISK_PIPE_END_WRITE:
1880       return p->fd[n];
1881     default:
1882       GNUNET_break (0);
1883       return NULL;
1884     }
1885 }
1886
1887
1888 /**
1889  * Retrieve OS file handle
1890  * @internal
1891  * @param fh GNUnet file descriptor
1892  * @param dst destination buffer
1893  * @param dst_len length of dst
1894  * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
1895  */
1896 int
1897 GNUNET_DISK_internal_file_handle_ (const struct GNUNET_DISK_FileHandle *fh,
1898                                    void *dst, size_t dst_len)
1899 {
1900 #ifdef MINGW
1901   if (dst_len < sizeof (HANDLE))
1902     return GNUNET_SYSERR;
1903   *((HANDLE *) dst) = fh->h;
1904 #else
1905   if (dst_len < sizeof (int))
1906     return GNUNET_SYSERR;
1907   *((int *) dst) = fh->fd;
1908 #endif
1909
1910   return GNUNET_OK;
1911 }
1912
1913 /* end of disk.c */