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