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