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