Added documenting comments
[oweals/gnunet.git] / src / util / disk.c
1 /*
2      This file is part of GNUnet.
3      (C) 2001--2013 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 3, 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_util_lib.h"
30 #include "disk.h"
31
32 #define LOG(kind,...) GNUNET_log_from (kind, "util", __VA_ARGS__)
33
34 #define LOG_STRERROR(kind,syscall) GNUNET_log_from_strerror (kind, "util", syscall)
35
36 #define LOG_STRERROR_FILE(kind,syscall,filename) GNUNET_log_from_strerror_file (kind, "util", syscall, filename)
37
38 /**
39  * Block size for IO for copying files.
40  */
41 #define COPY_BLK_SIZE 65536
42
43 #include <sys/types.h>
44 #if HAVE_SYS_VFS_H
45 #include <sys/vfs.h>
46 #endif
47 #if HAVE_SYS_PARAM_H
48 #include <sys/param.h>
49 #endif
50 #if HAVE_SYS_MOUNT_H
51 #include <sys/mount.h>
52 #endif
53 #if HAVE_SYS_STATVFS_H
54 #include <sys/statvfs.h>
55 #endif
56
57 #ifndef S_ISLNK
58 #define         _IFMT           0170000 /* type of file */
59 #define         _IFLNK          0120000 /* symbolic link */
60 #define  S_ISLNK(m)     (((m)&_IFMT) == _IFLNK)
61 #endif
62
63
64 /**
65  * Handle used to manage a pipe.
66  */
67 struct GNUNET_DISK_PipeHandle
68 {
69   /**
70    * File descriptors for the pipe.
71    * One or both of them could be NULL.
72    */
73   struct GNUNET_DISK_FileHandle *fd[2];
74 };
75
76
77 /**
78  * Closure for the recursion to determine the file size
79  * of a directory.
80  */
81 struct GetFileSizeData
82 {
83   /**
84    * Set to the total file size.
85    */
86   uint64_t total;
87
88   /**
89    * GNUNET_YES if symbolic links should be included.
90    */
91   int include_sym_links;
92
93   /**
94    * GNUNET_YES if mode is file-only (return total == -1 for directories).
95    */
96   int single_file_mode;
97 };
98
99
100 #ifndef MINGW
101 /**
102  * Translate GNUnet-internal permission bitmap to UNIX file
103  * access permission bitmap.
104  *
105  * @param perm file permissions, GNUnet style
106  * @return file permissions, UNIX style
107  */
108 static int
109 translate_unix_perms (enum GNUNET_DISK_AccessPermissions perm)
110 {
111   int mode;
112
113   mode = 0;
114   if (perm & GNUNET_DISK_PERM_USER_READ)
115     mode |= S_IRUSR;
116   if (perm & GNUNET_DISK_PERM_USER_WRITE)
117     mode |= S_IWUSR;
118   if (perm & GNUNET_DISK_PERM_USER_EXEC)
119     mode |= S_IXUSR;
120   if (perm & GNUNET_DISK_PERM_GROUP_READ)
121     mode |= S_IRGRP;
122   if (perm & GNUNET_DISK_PERM_GROUP_WRITE)
123     mode |= S_IWGRP;
124   if (perm & GNUNET_DISK_PERM_GROUP_EXEC)
125     mode |= S_IXGRP;
126   if (perm & GNUNET_DISK_PERM_OTHER_READ)
127     mode |= S_IROTH;
128   if (perm & GNUNET_DISK_PERM_OTHER_WRITE)
129     mode |= S_IWOTH;
130   if (perm & GNUNET_DISK_PERM_OTHER_EXEC)
131     mode |= S_IXOTH;
132
133   return mode;
134 }
135 #endif
136
137
138 /**
139  * Iterate over all files in the given directory and
140  * accumulate their size.
141  *
142  * @param cls closure of type `struct GetFileSizeData`
143  * @param fn current filename we are looking at
144  * @return #GNUNET_SYSERR on serious errors, otherwise #GNUNET_OK
145  */
146 static int
147 getSizeRec (void *cls, const char *fn)
148 {
149   struct GetFileSizeData *gfsd = cls;
150
151 #if defined (HAVE_STAT64) && !(defined (_FILE_OFFSET_BITS) && _FILE_OFFSET_BITS == 64)
152   STRUCT_STAT64 buf;
153
154   if (0 != STAT64 (fn, &buf))
155   {
156     LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_DEBUG, "stat64", fn);
157     return GNUNET_SYSERR;
158   }
159 #else
160   struct stat buf;
161
162   if (0 != STAT (fn, &buf))
163   {
164     LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_DEBUG, "stat", fn);
165     return GNUNET_SYSERR;
166   }
167 #endif
168   if ((S_ISDIR (buf.st_mode)) && (gfsd->single_file_mode == GNUNET_YES))
169   {
170     errno = EISDIR;
171     return GNUNET_SYSERR;
172   }
173   if ((!S_ISLNK (buf.st_mode)) || (gfsd->include_sym_links == GNUNET_YES))
174     gfsd->total += buf.st_size;
175   if ((S_ISDIR (buf.st_mode)) && (0 == ACCESS (fn, X_OK)) &&
176       ((!S_ISLNK (buf.st_mode)) || (gfsd->include_sym_links == GNUNET_YES)))
177   {
178     if (GNUNET_SYSERR == GNUNET_DISK_directory_scan (fn, &getSizeRec, gfsd))
179       return GNUNET_SYSERR;
180   }
181   return GNUNET_OK;
182 }
183
184
185 /**
186  * Checks whether a handle is invalid
187  *
188  * @param h handle to check
189  * @return #GNUNET_YES if invalid, #GNUNET_NO if valid
190  */
191 int
192 GNUNET_DISK_handle_invalid (const struct GNUNET_DISK_FileHandle *h)
193 {
194 #ifdef MINGW
195   return ((!h) || (h->h == INVALID_HANDLE_VALUE)) ? GNUNET_YES : GNUNET_NO;
196 #else
197   return ((!h) || (h->fd == -1)) ? GNUNET_YES : GNUNET_NO;
198 #endif
199 }
200
201 /**
202  * Get the size of an open file.
203  *
204  * @param fh open file handle
205  * @param size where to write size of the file
206  * @return #GNUNET_OK on success, #GNUNET_SYSERR on error
207  */
208 int
209 GNUNET_DISK_file_handle_size (struct GNUNET_DISK_FileHandle *fh,
210                               off_t *size)
211 {
212 #if WINDOWS
213   BOOL b;
214   LARGE_INTEGER li;
215   b = GetFileSizeEx (fh->h, &li);
216   if (!b)
217   {
218     SetErrnoFromWinError (GetLastError ());
219     return GNUNET_SYSERR;
220   }
221   *size = (off_t) li.QuadPart;
222 #else
223   struct stat sbuf;
224
225   if (0 != FSTAT (fh->fd, &sbuf))
226     return GNUNET_SYSERR;
227   *size = sbuf.st_size;
228 #endif
229   return GNUNET_OK;
230 }
231
232
233 /**
234  * Move the read/write pointer in a file
235  *
236  * @param h handle of an open file
237  * @param offset position to move to
238  * @param whence specification to which position the offset parameter relates to
239  * @return the new position on success, #GNUNET_SYSERR otherwise
240  */
241 off_t
242 GNUNET_DISK_file_seek (const struct GNUNET_DISK_FileHandle * h, off_t offset,
243                        enum GNUNET_DISK_Seek whence)
244 {
245   if (h == NULL)
246   {
247     errno = EINVAL;
248     return GNUNET_SYSERR;
249   }
250
251 #ifdef MINGW
252   LARGE_INTEGER li;
253   LARGE_INTEGER new_pos;
254   BOOL b;
255
256   static DWORD t[] = { FILE_BEGIN, FILE_CURRENT, FILE_END };
257   li.QuadPart = offset;
258
259   b = SetFilePointerEx (h->h, li, &new_pos, t[whence]);
260   if (b == 0)
261   {
262     SetErrnoFromWinError (GetLastError ());
263     return GNUNET_SYSERR;
264   }
265   return (off_t) new_pos.QuadPart;
266 #else
267   static int t[] = { SEEK_SET, SEEK_CUR, SEEK_END };
268
269   return lseek (h->fd, offset, t[whence]);
270 #endif
271 }
272
273
274 /**
275  * Get the size of the file (or directory) of the given file (in
276  * bytes).
277  *
278  * @param filename name of the file or directory
279  * @param size set to the size of the file (or,
280  *             in the case of directories, the sum
281  *             of all sizes of files in the directory)
282  * @param include_symbolic_links should symbolic links be
283  *        included?
284  * @param single_file_mode #GNUNET_YES to only get size of one file
285  *        and return #GNUNET_SYSERR for directories.
286  * @return #GNUNET_SYSERR on error, #GNUNET_OK on success
287  */
288 int
289 GNUNET_DISK_file_size (const char *filename,
290                        uint64_t * size,
291                        int include_symbolic_links,
292                        int single_file_mode)
293 {
294   struct GetFileSizeData gfsd;
295   int ret;
296
297   GNUNET_assert (size != NULL);
298   gfsd.total = 0;
299   gfsd.include_sym_links = include_symbolic_links;
300   gfsd.single_file_mode = single_file_mode;
301   ret = getSizeRec (&gfsd, filename);
302   *size = gfsd.total;
303   return ret;
304 }
305
306
307 /**
308  * Obtain some unique identifiers for the given file
309  * that can be used to identify it in the local system.
310  * This function is used between GNUnet processes to
311  * quickly check if two files with the same absolute path
312  * are actually identical.  The two processes represent
313  * the same peer but may communicate over the network
314  * (and the file may be on an NFS volume).  This function
315  * may not be supported on all operating systems.
316  *
317  * @param filename name of the file
318  * @param dev set to the device ID
319  * @param ino set to the inode ID
320  * @return #GNUNET_OK on success
321  */
322 int
323 GNUNET_DISK_file_get_identifiers (const char *filename, uint64_t * dev,
324                                   uint64_t * ino)
325 {
326 #if WINDOWS
327   {
328     // FIXME NILS: test this
329     struct GNUNET_DISK_FileHandle *fh;
330     BY_HANDLE_FILE_INFORMATION info;
331     int succ;
332
333     fh = GNUNET_DISK_file_open (filename, GNUNET_DISK_OPEN_READ, 0);
334     if (fh == NULL)
335       return GNUNET_SYSERR;
336     succ = GetFileInformationByHandle (fh->h, &info);
337     GNUNET_DISK_file_close (fh);
338     if (!succ)
339     {
340       return GNUNET_SYSERR;
341     }
342     *dev = info.dwVolumeSerialNumber;
343     *ino = ((((uint64_t) info.nFileIndexHigh) << (sizeof (DWORD) * 8)) | info.nFileIndexLow);
344   }
345 #else /* !WINDOWS */
346 #if HAVE_STAT
347   {
348     struct stat sbuf;
349
350     if (0 != stat (filename, &sbuf))
351     {
352       return GNUNET_SYSERR;
353     }
354     *ino = (uint64_t) sbuf.st_ino;
355   }
356 #else
357   *ino = 0;
358 #endif
359 #if HAVE_STATVFS
360   {
361     struct statvfs fbuf;
362
363     if (0 != statvfs (filename, &fbuf))
364     {
365       return GNUNET_SYSERR;
366     }
367     *dev = (uint64_t) fbuf.f_fsid;
368   }
369 #elif HAVE_STATFS
370   {
371     struct statfs fbuf;
372
373     if (0 != statfs (filename, &fbuf))
374     {
375       return GNUNET_SYSERR;
376     }
377     *dev = ((uint64_t) fbuf.f_fsid.val[0]) << 32 ||
378         ((uint64_t) fbuf.f_fsid.val[1]);
379   }
380 #else
381   *dev = 0;
382 #endif
383 #endif /* !WINDOWS */
384   return GNUNET_OK;
385 }
386
387
388 /**
389  * Create the name for a temporary file or directory from a template.
390  *
391  * @param t template (without XXXXX or "/tmp/")
392  * @return name ready for passing to 'mktemp' or 'mkdtemp', NULL on error
393  */
394 static char *
395 mktemp_name (const char *t)
396 {
397   const char *tmpdir;
398   char *tmpl;
399   char *fn;
400
401   if ((t[0] != '/') && (t[0] != '\\')
402 #if WINDOWS
403       && !(isalpha ((int) t[0]) && (t[0] != '\0') && (t[1] == ':'))
404 #endif
405       )
406   {
407     /* FIXME: This uses system codepage on W32, not UTF-8 */
408     tmpdir = getenv ("TMPDIR");
409     if (NULL == tmpdir)
410       tmpdir = getenv ("TMP");
411     if (NULL == tmpdir)
412       tmpdir = getenv ("TEMP");
413     if (NULL == tmpdir)
414       tmpdir = "/tmp";
415     GNUNET_asprintf (&tmpl, "%s/%s%s", tmpdir, t, "XXXXXX");
416   }
417   else
418   {
419     GNUNET_asprintf (&tmpl, "%s%s", t, "XXXXXX");
420   }
421 #ifdef MINGW
422   fn = (char *) GNUNET_malloc (MAX_PATH + 1);
423   if (ERROR_SUCCESS != plibc_conv_to_win_path (tmpl, fn))
424   {
425     GNUNET_free (fn);
426     GNUNET_free (tmpl);
427     return NULL;
428   }
429   GNUNET_free (tmpl);
430 #else
431   fn = tmpl;
432 #endif
433   return fn;
434 }
435
436
437 #if WINDOWS
438 static char *
439 mkdtemp (char *fn)
440 {
441   char *random_fn;
442   char *tfn;
443
444   while (1)
445   {
446     tfn = GNUNET_strdup (fn);
447     random_fn = _mktemp (tfn);
448     if (NULL == random_fn)
449     {
450       GNUNET_free (tfn);
451       return NULL;
452     }
453     /* FIXME: assume fn to be UTF-8-encoded and do the right thing */
454     if (0 == CreateDirectoryA (tfn, NULL))
455     {
456       DWORD error = GetLastError ();
457       GNUNET_free (tfn);
458       if (ERROR_ALREADY_EXISTS == error)
459         continue;
460       return NULL;
461     }
462     break;
463   }
464   strcpy (fn, tfn);
465   return fn;
466 }
467
468 /**
469  * Update POSIX permissions mask of a file on disk.  If both argumets
470  * are #GNUNET_NO, the file is made world-read-write-executable (777).
471  * Does nothing on W32.
472  *
473  * @param fn name of the file to update
474  * @param require_uid_match #GNUNET_YES means 700
475  * @param require_gid_match #GNUNET_YES means 770 unless @a require_uid_match is set
476  */
477 void
478 GNUNET_DISK_fix_permissions (const char *fn,
479                              int require_uid_match,
480                              int require_gid_match)
481 {
482   /* nothing on W32 */
483 }
484
485 #else
486
487 /**
488  * Update POSIX permissions mask of a file on disk.  If both argumets
489  * are #GNUNET_NO, the file is made world-read-write-executable (777).
490  *
491  * @param fn name of the file to update
492  * @param require_uid_match #GNUNET_YES means 700
493  * @param require_gid_match #GNUNET_YES means 770 unless @a require_uid_match is set
494  */
495 void
496 GNUNET_DISK_fix_permissions (const char *fn,
497                              int require_uid_match,
498                              int require_gid_match)
499 {
500   mode_t mode;
501
502   if (GNUNET_YES == require_uid_match)
503     mode = S_IRUSR | S_IWUSR | S_IXUSR;
504   else if (GNUNET_YES == require_gid_match)
505     mode = S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IWGRP | S_IXGRP;
506   else
507     mode = S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IWGRP | S_IXGRP | S_IROTH | S_IWOTH | S_IXOTH;
508   if (0 != chmod (fn, mode))
509     GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING,
510                               "chmod",
511                               fn);
512 }
513
514 #endif
515
516 /**
517  * Create an (empty) temporary directory on disk.  If the given name is not
518  * an absolute path, the current 'TMPDIR' will be prepended.  In any case,
519  * 6 random characters will be appended to the name to create a unique
520  * filename.
521  *
522  * @param t component to use for the name;
523  *        does NOT contain "XXXXXX" or "/tmp/".
524  * @return NULL on error, otherwise name of fresh
525  *         file on disk in directory for temporary files
526  */
527 char *
528 GNUNET_DISK_mkdtemp (const char *t)
529 {
530   char *fn;
531
532   fn = mktemp_name (t);
533   if (fn != mkdtemp (fn))
534   {
535     LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_ERROR, "mkstemp", fn);
536     GNUNET_free (fn);
537     return NULL;
538   }
539   return fn;
540 }
541
542
543 /**
544  * Move a file out of the way (create a backup) by
545  * renaming it to "orig.NUM~" where NUM is the smallest
546  * number that is not used yet.
547  *
548  * @param fil name of the file to back up
549  */
550 void
551 GNUNET_DISK_file_backup (const char *fil)
552 {
553   size_t slen;
554   char *target;
555   unsigned int num;
556
557   slen = strlen (fil) + 20;
558   target = GNUNET_malloc (slen);
559   num = 0;
560   do
561   {
562     GNUNET_snprintf (target, slen,
563                      "%s.%u~",
564                      fil,
565                      num++);
566   } while (0 == access (target, F_OK));
567   if (0 != rename (fil, target))
568     GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR,
569                               "rename",
570                               fil);
571   GNUNET_free (target);
572 }
573
574
575 /**
576  * Create an (empty) temporary file on disk.  If the given name is not
577  * an absolute path, the current 'TMPDIR' will be prepended.  In any case,
578  * 6 random characters will be appended to the name to create a unique
579  * filename.
580  *
581  * @param t component to use for the name;
582  *        does NOT contain "XXXXXX" or "/tmp/".
583  * @return NULL on error, otherwise name of fresh
584  *         file on disk in directory for temporary files
585  */
586 char *
587 GNUNET_DISK_mktemp (const char *t)
588 {
589   int fd;
590   char *fn;
591
592   fn = mktemp_name (t);
593   if (-1 == (fd = mkstemp (fn)))
594   {
595     LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_ERROR, "mkstemp", fn);
596     GNUNET_free (fn);
597     return NULL;
598   }
599   if (0 != CLOSE (fd))
600     LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "close", fn);
601   return fn;
602 }
603
604
605 /**
606  * Test if @a fil is a directory and listable. Optionally, also check if the
607  * directory is readable.  Will not print an error message if the directory does
608  * not exist.  Will log errors if #GNUNET_SYSERR is returned (i.e., a file exists
609  * with the same name).
610  *
611  * @param fil filename to test
612  * @param is_readable GNUNET_YES to additionally check if @a fil is readable;
613  *          #GNUNET_NO to disable this check
614  * @return #GNUNET_YES if yes, #GNUNET_NO if not; #GNUNET_SYSERR if it
615  *           does not exist or stat'ed
616  */
617 int
618 GNUNET_DISK_directory_test (const char *fil, int is_readable)
619 {
620   struct stat filestat;
621   int ret;
622
623   ret = STAT (fil, &filestat);
624   if (ret != 0)
625   {
626     if (errno != ENOENT)
627       LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "stat", fil);
628     return GNUNET_SYSERR;
629   }
630   if (!S_ISDIR (filestat.st_mode))
631   {
632     LOG (GNUNET_ERROR_TYPE_DEBUG,
633          "A file already exits with the same name %s\n", fil);
634     return GNUNET_NO;
635   }
636   if (GNUNET_YES == is_readable)
637     ret = ACCESS (fil, R_OK | X_OK);
638   else
639     ret = ACCESS (fil, X_OK);
640   if (ret < 0)
641   {
642     LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "access", fil);
643     return GNUNET_NO;
644   }
645   return GNUNET_YES;
646 }
647
648
649 /**
650  * Check that fil corresponds to a filename
651  * (of a file that exists and that is not a directory).
652  *
653  * @param fil filename to check
654  * @return #GNUNET_YES if yes, #GNUNET_NO if not a file, #GNUNET_SYSERR if something
655  * else (will print an error message in that case, too).
656  */
657 int
658 GNUNET_DISK_file_test (const char *fil)
659 {
660   struct stat filestat;
661   int ret;
662   char *rdir;
663
664   rdir = GNUNET_STRINGS_filename_expand (fil);
665   if (rdir == NULL)
666     return GNUNET_SYSERR;
667
668   ret = STAT (rdir, &filestat);
669   if (ret != 0)
670   {
671     if (errno != ENOENT)
672     {
673       LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "stat", rdir);
674       GNUNET_free (rdir);
675       return GNUNET_SYSERR;
676     }
677     GNUNET_free (rdir);
678     return GNUNET_NO;
679   }
680   if (!S_ISREG (filestat.st_mode))
681   {
682     GNUNET_free (rdir);
683     return GNUNET_NO;
684   }
685   if (ACCESS (rdir, F_OK) < 0)
686   {
687     LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "access", rdir);
688     GNUNET_free (rdir);
689     return GNUNET_SYSERR;
690   }
691   GNUNET_free (rdir);
692   return GNUNET_YES;
693 }
694
695
696 /**
697  * Implementation of "mkdir -p"
698  *
699  * @param dir the directory to create
700  * @return #GNUNET_OK on success, #GNUNET_SYSERR on failure
701  */
702 int
703 GNUNET_DISK_directory_create (const char *dir)
704 {
705   char *rdir;
706   unsigned int len;
707   unsigned int pos;
708   unsigned int pos2;
709   int ret = GNUNET_OK;
710
711   rdir = GNUNET_STRINGS_filename_expand (dir);
712   if (rdir == NULL)
713     return GNUNET_SYSERR;
714
715   len = strlen (rdir);
716 #ifndef MINGW
717   pos = 1;                      /* skip heading '/' */
718 #else
719   /* Local or Network path? */
720   if (strncmp (rdir, "\\\\", 2) == 0)
721   {
722     pos = 2;
723     while (rdir[pos])
724     {
725       if (rdir[pos] == '\\')
726       {
727         pos++;
728         break;
729       }
730       pos++;
731     }
732   }
733   else
734   {
735     pos = 3;                    /* strlen("C:\\") */
736   }
737 #endif
738   /* Check which low level directories already exist */
739   pos2 = len;
740   rdir[len] = DIR_SEPARATOR;
741   while (pos <= pos2)
742   {
743     if (DIR_SEPARATOR == rdir[pos2])
744     {
745       rdir[pos2] = '\0';
746       ret = GNUNET_DISK_directory_test (rdir, GNUNET_NO);
747       if (GNUNET_NO == ret)
748       {
749         GNUNET_free (rdir);
750         return GNUNET_SYSERR;
751       }
752       rdir[pos2] = DIR_SEPARATOR;
753       if (GNUNET_YES == ret)
754       {
755         pos2++;
756         break;
757       }
758     }
759     pos2--;
760   }
761   rdir[len] = '\0';
762   if (pos < pos2)
763     pos = pos2;
764   /* Start creating directories */
765   while (pos <= len)
766   {
767     if ((rdir[pos] == DIR_SEPARATOR) || (pos == len))
768     {
769       rdir[pos] = '\0';
770       ret = GNUNET_DISK_directory_test (rdir, GNUNET_NO);
771       if (GNUNET_NO == ret)
772       {
773         GNUNET_free (rdir);
774         return GNUNET_SYSERR;
775       }
776       if (GNUNET_SYSERR == ret)
777       {
778 #ifndef MINGW
779         ret = mkdir (rdir, S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH);        /* 755 */
780 #else
781         wchar_t wrdir[MAX_PATH + 1];
782         if (ERROR_SUCCESS == plibc_conv_to_win_pathwconv(rdir, wrdir))
783           ret = !CreateDirectoryW (wrdir, NULL);
784         else
785           ret = 1;
786 #endif
787         if ((ret != 0) && (errno != EEXIST))
788         {
789           LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_ERROR, "mkdir", rdir);
790           GNUNET_free (rdir);
791           return GNUNET_SYSERR;
792         }
793       }
794       rdir[pos] = DIR_SEPARATOR;
795     }
796     pos++;
797   }
798   GNUNET_free (rdir);
799   return GNUNET_OK;
800 }
801
802
803 /**
804  * Create the directory structure for storing
805  * a file.
806  *
807  * @param filename name of a file in the directory
808  * @returns #GNUNET_OK on success,
809  *          #GNUNET_SYSERR on failure,
810  *          #GNUNET_NO if the directory
811  *          exists but is not writeable for us
812  */
813 int
814 GNUNET_DISK_directory_create_for_file (const char *filename)
815 {
816   char *rdir;
817   size_t len;
818   int ret;
819
820   rdir = GNUNET_STRINGS_filename_expand (filename);
821   if (rdir == NULL)
822     return GNUNET_SYSERR;
823   len = strlen (rdir);
824   while ((len > 0) && (rdir[len] != DIR_SEPARATOR))
825     len--;
826   rdir[len] = '\0';
827   ret = GNUNET_DISK_directory_create (rdir);
828   if ((ret == GNUNET_OK) && (0 != ACCESS (rdir, W_OK)))
829     ret = GNUNET_NO;
830   GNUNET_free (rdir);
831   return ret;
832 }
833
834
835 /**
836  * Read the contents of a binary file into a buffer.
837  *
838  * @param h handle to an open file
839  * @param result the buffer to write the result to
840  * @param len the maximum number of bytes to read
841  * @return the number of bytes read on success, #GNUNET_SYSERR on failure
842  */
843 ssize_t
844 GNUNET_DISK_file_read (const struct GNUNET_DISK_FileHandle *h,
845                        void *result,
846                        size_t len)
847 {
848   if (NULL == h)
849   {
850     errno = EINVAL;
851     return GNUNET_SYSERR;
852   }
853
854 #ifdef MINGW
855   DWORD bytes_read;
856
857   if (h->type != GNUNET_DISK_HANLDE_TYPE_PIPE)
858   {
859     if (!ReadFile (h->h, result, len, &bytes_read, NULL))
860     {
861       SetErrnoFromWinError (GetLastError ());
862       return GNUNET_SYSERR;
863     }
864   }
865   else
866   {
867     if (!ReadFile (h->h, result, len, &bytes_read, h->oOverlapRead))
868     {
869       if (GetLastError () != ERROR_IO_PENDING)
870       {
871         LOG (GNUNET_ERROR_TYPE_DEBUG, "Error reading from pipe: %u\n", GetLastError ());
872         SetErrnoFromWinError (GetLastError ());
873         return GNUNET_SYSERR;
874       }
875       LOG (GNUNET_ERROR_TYPE_DEBUG, "Will get overlapped result\n");
876       GetOverlappedResult (h->h, h->oOverlapRead, &bytes_read, TRUE);
877     }
878     LOG (GNUNET_ERROR_TYPE_DEBUG, "Read %u bytes from pipe\n", bytes_read);
879   }
880   return bytes_read;
881 #else
882   return read (h->fd, result, len);
883 #endif
884 }
885
886
887 /**
888  * Read the contents of a binary file into a buffer.
889  * Guarantees not to block (returns GNUNET_SYSERR and sets errno to EAGAIN
890  * when no data can be read).
891  *
892  * @param h handle to an open file
893  * @param result the buffer to write the result to
894  * @param len the maximum number of bytes to read
895  * @return the number of bytes read on success, #GNUNET_SYSERR on failure
896  */
897 ssize_t
898 GNUNET_DISK_file_read_non_blocking (const struct GNUNET_DISK_FileHandle *h,
899                                     void *result,
900                                     size_t len)
901 {
902   if (NULL == h)
903   {
904     errno = EINVAL;
905     return GNUNET_SYSERR;
906   }
907
908 #ifdef MINGW
909   DWORD bytes_read;
910
911   if (h->type != GNUNET_DISK_HANLDE_TYPE_PIPE)
912   {
913     if (!ReadFile (h->h, result, len, &bytes_read, NULL))
914     {
915       SetErrnoFromWinError (GetLastError ());
916       return GNUNET_SYSERR;
917     }
918   }
919   else
920   {
921     if (!ReadFile (h->h, result, len, &bytes_read, h->oOverlapRead))
922     {
923       if (GetLastError () != ERROR_IO_PENDING)
924       {
925         LOG (GNUNET_ERROR_TYPE_DEBUG, "Error reading from pipe: %u\n", GetLastError ());
926         SetErrnoFromWinError (GetLastError ());
927         return GNUNET_SYSERR;
928       }
929       else
930       {
931         LOG (GNUNET_ERROR_TYPE_DEBUG,
932             "ReadFile() queued a read, cancelling\n");
933         CancelIo (h->h);
934         errno = EAGAIN;
935         return GNUNET_SYSERR;
936       }
937     }
938     LOG (GNUNET_ERROR_TYPE_DEBUG, "Read %u bytes\n", bytes_read);
939   }
940   return bytes_read;
941 #else
942   int flags;
943   ssize_t ret;
944
945   /* set to non-blocking, read, then set back */
946   flags = fcntl (h->fd, F_GETFL);
947   if (0 == (flags & O_NONBLOCK))
948     (void) fcntl (h->fd, F_SETFL, flags | O_NONBLOCK);
949   ret = read (h->fd, result, len);
950   if (0 == (flags & O_NONBLOCK))
951     {
952       int eno = errno;
953       (void) fcntl (h->fd, F_SETFL, flags);
954       errno = eno;
955     }
956   return ret;
957 #endif
958 }
959
960
961 /**
962  * Read the contents of a binary file into a buffer.
963  *
964  * @param fn file name
965  * @param result the buffer to write the result to
966  * @param len the maximum number of bytes to read
967  * @return number of bytes read, #GNUNET_SYSERR on failure
968  */
969 ssize_t
970 GNUNET_DISK_fn_read (const char *fn,
971                      void *result,
972                      size_t len)
973 {
974   struct GNUNET_DISK_FileHandle *fh;
975   ssize_t ret;
976
977   fh = GNUNET_DISK_file_open (fn, GNUNET_DISK_OPEN_READ, GNUNET_DISK_PERM_NONE);
978   if (!fh)
979     return GNUNET_SYSERR;
980   ret = GNUNET_DISK_file_read (fh, result, len);
981   GNUNET_assert (GNUNET_OK == GNUNET_DISK_file_close (fh));
982
983   return ret;
984 }
985
986
987 /**
988  * Write a buffer to a file.
989  *
990  * @param h handle to open file
991  * @param buffer the data to write
992  * @param n number of bytes to write
993  * @return number of bytes written on success, #GNUNET_SYSERR on error
994  */
995 ssize_t
996 GNUNET_DISK_file_write (const struct GNUNET_DISK_FileHandle * h,
997                         const void *buffer, size_t n)
998 {
999   if (NULL == h)
1000   {
1001     errno = EINVAL;
1002     return GNUNET_SYSERR;
1003   }
1004
1005 #ifdef MINGW
1006   DWORD bytes_written;
1007
1008   if (h->type != GNUNET_DISK_HANLDE_TYPE_PIPE)
1009   {
1010     if (!WriteFile (h->h, buffer, n, &bytes_written, NULL))
1011     {
1012       SetErrnoFromWinError (GetLastError ());
1013       return GNUNET_SYSERR;
1014     }
1015   }
1016   else
1017   {
1018     LOG (GNUNET_ERROR_TYPE_DEBUG, "It is a pipe trying to write %u bytes\n", n);
1019     if (!WriteFile (h->h, buffer, n, &bytes_written, h->oOverlapWrite))
1020     {
1021       if (GetLastError () != ERROR_IO_PENDING)
1022       {
1023         SetErrnoFromWinError (GetLastError ());
1024         LOG (GNUNET_ERROR_TYPE_DEBUG, "Error writing to pipe: %u\n",
1025             GetLastError ());
1026         return GNUNET_SYSERR;
1027       }
1028       LOG (GNUNET_ERROR_TYPE_DEBUG, "Will get overlapped result\n");
1029       if (!GetOverlappedResult (h->h, h->oOverlapWrite, &bytes_written, TRUE))
1030       {
1031         SetErrnoFromWinError (GetLastError ());
1032         LOG (GNUNET_ERROR_TYPE_DEBUG,
1033             "Error getting overlapped result while writing to pipe: %u\n",
1034             GetLastError ());
1035         return GNUNET_SYSERR;
1036       }
1037     }
1038     else
1039     {
1040       DWORD ovr;
1041       if (!GetOverlappedResult (h->h, h->oOverlapWrite, &ovr, TRUE))
1042       {
1043         LOG (GNUNET_ERROR_TYPE_DEBUG,
1044             "Error getting control overlapped result while writing to pipe: %u\n",
1045             GetLastError ());
1046       }
1047       else
1048       {
1049         LOG (GNUNET_ERROR_TYPE_DEBUG,
1050             "Wrote %u bytes (ovr says %u), picking the greatest\n",
1051             bytes_written, ovr);
1052       }
1053     }
1054     if (bytes_written == 0)
1055     {
1056       if (n > 0)
1057       {
1058         LOG (GNUNET_ERROR_TYPE_DEBUG, "Wrote %u bytes, returning -1 with EAGAIN\n", bytes_written);
1059         errno = EAGAIN;
1060         return GNUNET_SYSERR;
1061       }
1062     }
1063     LOG (GNUNET_ERROR_TYPE_DEBUG, "Wrote %u bytes\n", bytes_written);
1064   }
1065   return bytes_written;
1066 #else
1067   return write (h->fd, buffer, n);
1068 #endif
1069 }
1070
1071
1072 /**
1073  * Write a buffer to a file, blocking, if necessary.
1074  *
1075  * @param h handle to open file
1076  * @param buffer the data to write
1077  * @param n number of bytes to write
1078  * @return number of bytes written on success, #GNUNET_SYSERR on error
1079  */
1080 ssize_t
1081 GNUNET_DISK_file_write_blocking (const struct GNUNET_DISK_FileHandle * h,
1082                                  const void *buffer,
1083                                  size_t n)
1084 {
1085   if (NULL == h)
1086   {
1087     errno = EINVAL;
1088     return GNUNET_SYSERR;
1089   }
1090
1091 #ifdef MINGW
1092   DWORD bytes_written;
1093   /* We do a non-overlapped write, which is as blocking as it gets */
1094   LOG (GNUNET_ERROR_TYPE_DEBUG, "Writing %u bytes\n", n);
1095   if (!WriteFile (h->h, buffer, n, &bytes_written, NULL))
1096   {
1097     SetErrnoFromWinError (GetLastError ());
1098     LOG (GNUNET_ERROR_TYPE_DEBUG, "Error writing to pipe: %u\n",
1099         GetLastError ());
1100     return GNUNET_SYSERR;
1101   }
1102   if (bytes_written == 0 && n > 0)
1103   {
1104     LOG (GNUNET_ERROR_TYPE_DEBUG, "Waiting for pipe to clean\n");
1105     WaitForSingleObject (h->h, INFINITE);
1106     if (!WriteFile (h->h, buffer, n, &bytes_written, NULL))
1107     {
1108       SetErrnoFromWinError (GetLastError ());
1109       LOG (GNUNET_ERROR_TYPE_DEBUG, "Error writing to pipe: %u\n",
1110           GetLastError ());
1111       return GNUNET_SYSERR;
1112     }
1113   }
1114   LOG (GNUNET_ERROR_TYPE_DEBUG,
1115        "Wrote %u bytes\n",
1116        bytes_written);
1117   return bytes_written;
1118 #else
1119   int flags;
1120   ssize_t ret;
1121
1122   /* set to blocking, write, then set back */
1123   flags = fcntl (h->fd, F_GETFL);
1124   if (0 != (flags & O_NONBLOCK))
1125     (void) fcntl (h->fd, F_SETFL, flags - O_NONBLOCK);
1126   ret = write (h->fd, buffer, n);
1127   if (0 == (flags & O_NONBLOCK))
1128     (void) fcntl (h->fd, F_SETFL, flags);
1129   return ret;
1130 #endif
1131 }
1132
1133
1134 /**
1135  * Write a buffer to a file.  If the file is longer than the
1136  * number of bytes that will be written, it will be truncated.
1137  *
1138  * @param fn file name
1139  * @param buffer the data to write
1140  * @param n number of bytes to write
1141  * @param mode file permissions
1142  * @return number of bytes written on success, #GNUNET_SYSERR on error
1143  */
1144 ssize_t
1145 GNUNET_DISK_fn_write (const char *fn, const void *buffer, size_t n,
1146                       enum GNUNET_DISK_AccessPermissions mode)
1147 {
1148   struct GNUNET_DISK_FileHandle *fh;
1149   ssize_t ret;
1150
1151   fh = GNUNET_DISK_file_open (fn,
1152                               GNUNET_DISK_OPEN_WRITE | GNUNET_DISK_OPEN_TRUNCATE
1153                               | GNUNET_DISK_OPEN_CREATE, mode);
1154   if (!fh)
1155     return GNUNET_SYSERR;
1156   ret = GNUNET_DISK_file_write (fh, buffer, n);
1157   GNUNET_assert (GNUNET_OK == GNUNET_DISK_file_close (fh));
1158   return ret;
1159 }
1160
1161
1162 /**
1163  * Scan a directory for files.
1164  *
1165  * @param dir_name the name of the directory
1166  * @param callback the method to call for each file,
1167  *        can be NULL, in that case, we only count
1168  * @param callback_cls closure for @a callback
1169  * @return the number of files found, #GNUNET_SYSERR on error or
1170  *         ieration aborted by callback returning #GNUNET_SYSERR
1171  */
1172 int
1173 GNUNET_DISK_directory_scan (const char *dir_name,
1174                             GNUNET_FileNameCallback callback,
1175                             void *callback_cls)
1176 {
1177   DIR *dinfo;
1178   struct dirent *finfo;
1179   struct stat istat;
1180   int count = 0;
1181   int ret;
1182   char *name;
1183   char *dname;
1184   unsigned int name_len;
1185   unsigned int n_size;
1186
1187   GNUNET_assert (dir_name != NULL);
1188   dname = GNUNET_STRINGS_filename_expand (dir_name);
1189   if (dname == NULL)
1190     return GNUNET_SYSERR;
1191   while ((strlen (dname) > 0) && (dname[strlen (dname) - 1] == DIR_SEPARATOR))
1192     dname[strlen (dname) - 1] = '\0';
1193   if (0 != STAT (dname, &istat))
1194   {
1195     LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "stat", dname);
1196     GNUNET_free (dname);
1197     return GNUNET_SYSERR;
1198   }
1199   if (!S_ISDIR (istat.st_mode))
1200   {
1201     LOG (GNUNET_ERROR_TYPE_WARNING,
1202          _("Expected `%s' to be a directory!\n"),
1203          dir_name);
1204     GNUNET_free (dname);
1205     return GNUNET_SYSERR;
1206   }
1207   errno = 0;
1208   dinfo = OPENDIR (dname);
1209   if ((errno == EACCES) || (dinfo == NULL))
1210   {
1211     LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "opendir", dname);
1212     if (dinfo != NULL)
1213       CLOSEDIR (dinfo);
1214     GNUNET_free (dname);
1215     return GNUNET_SYSERR;
1216   }
1217   name_len = 256;
1218   n_size = strlen (dname) + name_len + 2;
1219   name = GNUNET_malloc (n_size);
1220   while ((finfo = READDIR (dinfo)) != NULL)
1221   {
1222     if ((0 == strcmp (finfo->d_name, ".")) ||
1223         (0 == strcmp (finfo->d_name, "..")))
1224       continue;
1225     if (callback != NULL)
1226     {
1227       if (name_len < strlen (finfo->d_name))
1228       {
1229         GNUNET_free (name);
1230         name_len = strlen (finfo->d_name);
1231         n_size = strlen (dname) + name_len + 2;
1232         name = GNUNET_malloc (n_size);
1233       }
1234       /* dname can end in "/" only if dname == "/";
1235        * if dname does not end in "/", we need to add
1236        * a "/" (otherwise, we must not!) */
1237       GNUNET_snprintf (name, n_size, "%s%s%s", dname,
1238                        (strcmp (dname, DIR_SEPARATOR_STR) ==
1239                         0) ? "" : DIR_SEPARATOR_STR, finfo->d_name);
1240       ret = callback (callback_cls, name);
1241       if (GNUNET_OK != ret)
1242       {
1243         CLOSEDIR (dinfo);
1244         GNUNET_free (name);
1245         GNUNET_free (dname);
1246         if (GNUNET_NO == ret)
1247           return count;
1248         return GNUNET_SYSERR;
1249       }
1250     }
1251     count++;
1252   }
1253   CLOSEDIR (dinfo);
1254   GNUNET_free (name);
1255   GNUNET_free (dname);
1256   return count;
1257 }
1258
1259
1260 /**
1261  * Opaque handle used for iterating over a directory.
1262  */
1263 struct GNUNET_DISK_DirectoryIterator
1264 {
1265
1266   /**
1267    * Function to call on directory entries.
1268    */
1269   GNUNET_DISK_DirectoryIteratorCallback callback;
1270
1271   /**
1272    * Closure for callback.
1273    */
1274   void *callback_cls;
1275
1276   /**
1277    * Reference to directory.
1278    */
1279   DIR *directory;
1280
1281   /**
1282    * Directory name.
1283    */
1284   char *dirname;
1285
1286   /**
1287    * Next filename to process.
1288    */
1289   char *next_name;
1290
1291   /**
1292    * Our priority.
1293    */
1294   enum GNUNET_SCHEDULER_Priority priority;
1295
1296 };
1297
1298
1299 /**
1300  * Task used by the directory iterator.
1301  */
1302 static void
1303 directory_iterator_task (void *cls,
1304                          const struct GNUNET_SCHEDULER_TaskContext *tc)
1305 {
1306   struct GNUNET_DISK_DirectoryIterator *iter = cls;
1307   char *name;
1308
1309   name = iter->next_name;
1310   GNUNET_assert (name != NULL);
1311   iter->next_name = NULL;
1312   iter->callback (iter->callback_cls, iter, name, iter->dirname);
1313   GNUNET_free (name);
1314 }
1315
1316
1317 /**
1318  * This function must be called during the DiskIteratorCallback
1319  * (exactly once) to schedule the task to process the next
1320  * filename in the directory (if there is one).
1321  *
1322  * @param iter opaque handle for the iterator
1323  * @param can set to GNUNET_YES to terminate the iteration early
1324  * @return GNUNET_YES if iteration will continue,
1325  *         GNUNET_NO if this was the last entry (and iteration is complete),
1326  *         GNUNET_SYSERR if abort was YES
1327  */
1328 int
1329 GNUNET_DISK_directory_iterator_next (struct GNUNET_DISK_DirectoryIterator *iter,
1330                                      int can)
1331 {
1332   struct dirent *finfo;
1333
1334   GNUNET_assert (iter->next_name == NULL);
1335   if (can == GNUNET_YES)
1336   {
1337     CLOSEDIR (iter->directory);
1338     GNUNET_free (iter->dirname);
1339     GNUNET_free (iter);
1340     return GNUNET_SYSERR;
1341   }
1342   while (NULL != (finfo = READDIR (iter->directory)))
1343   {
1344     if ((0 == strcmp (finfo->d_name, ".")) ||
1345         (0 == strcmp (finfo->d_name, "..")))
1346       continue;
1347     GNUNET_asprintf (&iter->next_name, "%s%s%s", iter->dirname,
1348                      DIR_SEPARATOR_STR, finfo->d_name);
1349     break;
1350   }
1351   if (finfo == NULL)
1352   {
1353     GNUNET_DISK_directory_iterator_next (iter, GNUNET_YES);
1354     return GNUNET_NO;
1355   }
1356   GNUNET_SCHEDULER_add_with_priority (iter->priority, &directory_iterator_task,
1357                                       iter);
1358   return GNUNET_YES;
1359 }
1360
1361
1362 /**
1363  * Scan a directory for files using the scheduler to run a task for
1364  * each entry.  The name of the directory must be expanded first (!).
1365  * If a scheduler does not need to be used, GNUNET_DISK_directory_scan
1366  * may provide a simpler API.
1367  *
1368  * @param prio priority to use
1369  * @param dir_name the name of the directory
1370  * @param callback the method to call for each file
1371  * @param callback_cls closure for callback
1372  * @return GNUNET_YES if directory is not empty and 'callback'
1373  *         will be called later, GNUNET_NO otherwise, GNUNET_SYSERR on error.
1374  */
1375 int
1376 GNUNET_DISK_directory_iterator_start (enum GNUNET_SCHEDULER_Priority prio,
1377                                       const char *dir_name,
1378                                       GNUNET_DISK_DirectoryIteratorCallback
1379                                       callback, void *callback_cls)
1380 {
1381   struct GNUNET_DISK_DirectoryIterator *di;
1382
1383   di = GNUNET_new (struct GNUNET_DISK_DirectoryIterator);
1384   di->callback = callback;
1385   di->callback_cls = callback_cls;
1386   di->directory = OPENDIR (dir_name);
1387   if (di->directory == NULL)
1388   {
1389     GNUNET_free (di);
1390     callback (callback_cls, NULL, NULL, NULL);
1391     return GNUNET_SYSERR;
1392   }
1393   di->dirname = GNUNET_strdup (dir_name);
1394   di->priority = prio;
1395   return GNUNET_DISK_directory_iterator_next (di, GNUNET_NO);
1396 }
1397
1398
1399 /**
1400  * Function that removes the given directory by calling
1401  * "GNUNET_DISK_directory_remove".
1402  *
1403  * @param unused not used
1404  * @param fn directory to remove
1405  * @return #GNUNET_OK
1406  */
1407 static int
1408 remove_helper (void *unused, const char *fn)
1409 {
1410   (void) GNUNET_DISK_directory_remove (fn);
1411   return GNUNET_OK;
1412 }
1413
1414
1415 /**
1416  * Remove all files in a directory (rm -rf). Call with
1417  * caution.
1418  *
1419  * @param filename the file to remove
1420  * @return #GNUNET_OK on success, #GNUNET_SYSERR on error
1421  */
1422 int
1423 GNUNET_DISK_directory_remove (const char *filename)
1424 {
1425   struct stat istat;
1426
1427   if (NULL == filename)
1428   {
1429     GNUNET_break (0);
1430     return GNUNET_SYSERR;
1431   }
1432   if (0 != LSTAT (filename, &istat))
1433     return GNUNET_NO;           /* file may not exist... */
1434   (void) CHMOD (filename, S_IWUSR | S_IRUSR | S_IXUSR);
1435   if (UNLINK (filename) == 0)
1436     return GNUNET_OK;
1437   if ((errno != EISDIR) &&
1438       /* EISDIR is not sufficient in all cases, e.g.
1439        * sticky /tmp directory may result in EPERM on BSD.
1440        * So we also explicitly check "isDirectory" */
1441       (GNUNET_YES != GNUNET_DISK_directory_test (filename, GNUNET_YES)))
1442   {
1443     LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "rmdir", filename);
1444     return GNUNET_SYSERR;
1445   }
1446   if (GNUNET_SYSERR ==
1447       GNUNET_DISK_directory_scan (filename, &remove_helper, NULL))
1448     return GNUNET_SYSERR;
1449   if (0 != RMDIR (filename))
1450   {
1451     LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "rmdir", filename);
1452     return GNUNET_SYSERR;
1453   }
1454   return GNUNET_OK;
1455 }
1456
1457
1458 /**
1459  * Copy a file.
1460  *
1461  * @param src file to copy
1462  * @param dst destination file name
1463  * @return #GNUNET_OK on success, #GNUNET_SYSERR on error
1464  */
1465 int
1466 GNUNET_DISK_file_copy (const char *src,
1467                        const char *dst)
1468 {
1469   char *buf;
1470   uint64_t pos;
1471   uint64_t size;
1472   size_t len;
1473   struct GNUNET_DISK_FileHandle *in;
1474   struct GNUNET_DISK_FileHandle *out;
1475
1476   if (GNUNET_OK != GNUNET_DISK_file_size (src, &size, GNUNET_YES, GNUNET_YES))
1477     return GNUNET_SYSERR;
1478   pos = 0;
1479   in = GNUNET_DISK_file_open (src, GNUNET_DISK_OPEN_READ,
1480                               GNUNET_DISK_PERM_NONE);
1481   if (!in)
1482     return GNUNET_SYSERR;
1483   out =
1484       GNUNET_DISK_file_open (dst,
1485                              GNUNET_DISK_OPEN_WRITE | GNUNET_DISK_OPEN_CREATE |
1486                              GNUNET_DISK_OPEN_FAILIFEXISTS,
1487                              GNUNET_DISK_PERM_USER_READ |
1488                              GNUNET_DISK_PERM_USER_WRITE |
1489                              GNUNET_DISK_PERM_GROUP_READ |
1490                              GNUNET_DISK_PERM_GROUP_WRITE);
1491   if (!out)
1492   {
1493     GNUNET_DISK_file_close (in);
1494     return GNUNET_SYSERR;
1495   }
1496   buf = GNUNET_malloc (COPY_BLK_SIZE);
1497   while (pos < size)
1498   {
1499     len = COPY_BLK_SIZE;
1500     if (len > size - pos)
1501       len = size - pos;
1502     if (len != GNUNET_DISK_file_read (in, buf, len))
1503       goto FAIL;
1504     if (len != GNUNET_DISK_file_write (out, buf, len))
1505       goto FAIL;
1506     pos += len;
1507   }
1508   GNUNET_free (buf);
1509   GNUNET_DISK_file_close (in);
1510   GNUNET_DISK_file_close (out);
1511   return GNUNET_OK;
1512 FAIL:
1513   GNUNET_free (buf);
1514   GNUNET_DISK_file_close (in);
1515   GNUNET_DISK_file_close (out);
1516   return GNUNET_SYSERR;
1517 }
1518
1519
1520 /**
1521  * @brief Removes special characters as ':' from a filename.
1522  * @param fn the filename to canonicalize
1523  */
1524 void
1525 GNUNET_DISK_filename_canonicalize (char *fn)
1526 {
1527   char *idx;
1528   char c;
1529
1530   idx = fn;
1531   while (*idx)
1532   {
1533     c = *idx;
1534
1535     if (c == '/' || c == '\\' || c == ':' || c == '*' || c == '?' || c == '"' ||
1536         c == '<' || c == '>' || c == '|')
1537     {
1538       *idx = '_';
1539     }
1540
1541     idx++;
1542   }
1543 }
1544
1545
1546
1547 /**
1548  * @brief Change owner of a file
1549  *
1550  * @param filename name of file to change the owner of
1551  * @param user name of the new owner
1552  * @return GNUNET_OK on success, GNUNET_SYSERR on failure
1553  */
1554 int
1555 GNUNET_DISK_file_change_owner (const char *filename, const char *user)
1556 {
1557 #ifndef MINGW
1558   struct passwd *pws;
1559
1560   pws = getpwnam (user);
1561   if (pws == NULL)
1562   {
1563     LOG (GNUNET_ERROR_TYPE_ERROR,
1564          _("Cannot obtain information about user `%s': %s\n"), user,
1565          STRERROR (errno));
1566     return GNUNET_SYSERR;
1567   }
1568   if (0 != chown (filename, pws->pw_uid, pws->pw_gid))
1569     LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "chown", filename);
1570 #endif
1571   return GNUNET_OK;
1572 }
1573
1574
1575 /**
1576  * Lock a part of a file
1577  * @param fh file handle
1578  * @param lock_start absolute position from where to lock
1579  * @param lock_end absolute position until where to lock
1580  * @param excl GNUNET_YES for an exclusive lock
1581  * @return GNUNET_OK on success, GNUNET_SYSERR on error
1582  */
1583 int
1584 GNUNET_DISK_file_lock (struct GNUNET_DISK_FileHandle *fh, off_t lock_start,
1585                        off_t lock_end, int excl)
1586 {
1587   if (fh == NULL)
1588   {
1589     errno = EINVAL;
1590     return GNUNET_SYSERR;
1591   }
1592
1593 #ifndef MINGW
1594   struct flock fl;
1595
1596   memset (&fl, 0, sizeof (struct flock));
1597   fl.l_type = excl ? F_WRLCK : F_RDLCK;
1598   fl.l_whence = SEEK_SET;
1599   fl.l_start = lock_start;
1600   fl.l_len = lock_end;
1601
1602   return fcntl (fh->fd, F_SETLK, &fl) != 0 ? GNUNET_SYSERR : GNUNET_OK;
1603 #else
1604   OVERLAPPED o;
1605   off_t diff = lock_end - lock_start;
1606   DWORD diff_low, diff_high;
1607   diff_low = (DWORD) (diff & 0xFFFFFFFF);
1608   diff_high = (DWORD) ((diff >> (sizeof (DWORD) * 8)) & 0xFFFFFFFF);
1609
1610   memset (&o, 0, sizeof (OVERLAPPED));
1611   o.Offset = (DWORD) (lock_start & 0xFFFFFFFF);;
1612   o.OffsetHigh = (DWORD) (((lock_start & ~0xFFFFFFFF) >> (sizeof (DWORD) * 8)) & 0xFFFFFFFF);
1613
1614   if (!LockFileEx
1615       (fh->h, (excl ? LOCKFILE_EXCLUSIVE_LOCK : 0) | LOCKFILE_FAIL_IMMEDIATELY,
1616        0, diff_low, diff_high, &o))
1617   {
1618     SetErrnoFromWinError (GetLastError ());
1619     return GNUNET_SYSERR;
1620   }
1621
1622   return GNUNET_OK;
1623 #endif
1624 }
1625
1626
1627 /**
1628  * Unlock a part of a file
1629  * @param fh file handle
1630  * @param unlock_start absolute position from where to unlock
1631  * @param unlock_end absolute position until where to unlock
1632  * @return #GNUNET_OK on success, #GNUNET_SYSERR on error
1633  */
1634 int
1635 GNUNET_DISK_file_unlock (struct GNUNET_DISK_FileHandle *fh, off_t unlock_start,
1636                          off_t unlock_end)
1637 {
1638   if (fh == NULL)
1639   {
1640     errno = EINVAL;
1641     return GNUNET_SYSERR;
1642   }
1643
1644 #ifndef MINGW
1645   struct flock fl;
1646
1647   memset (&fl, 0, sizeof (struct flock));
1648   fl.l_type = F_UNLCK;
1649   fl.l_whence = SEEK_SET;
1650   fl.l_start = unlock_start;
1651   fl.l_len = unlock_end;
1652
1653   return fcntl (fh->fd, F_SETLK, &fl) != 0 ? GNUNET_SYSERR : GNUNET_OK;
1654 #else
1655   OVERLAPPED o;
1656   off_t diff = unlock_end - unlock_start;
1657   DWORD diff_low, diff_high;
1658   diff_low = (DWORD) (diff & 0xFFFFFFFF);
1659   diff_high = (DWORD) ((diff >> (sizeof (DWORD) * 8)) & 0xFFFFFFFF);
1660
1661   memset (&o, 0, sizeof (OVERLAPPED));
1662   o.Offset = (DWORD) (unlock_start & 0xFFFFFFFF);;
1663   o.OffsetHigh = (DWORD) (((unlock_start & ~0xFFFFFFFF) >> (sizeof (DWORD) * 8)) & 0xFFFFFFFF);
1664
1665   if (!UnlockFileEx (fh->h, 0, diff_low, diff_high, &o))
1666   {
1667     SetErrnoFromWinError (GetLastError ());
1668     return GNUNET_SYSERR;
1669   }
1670
1671   return GNUNET_OK;
1672 #endif
1673 }
1674
1675
1676 /**
1677  * Open a file.  Note that the access permissions will only be
1678  * used if a new file is created and if the underlying operating
1679  * system supports the given permissions.
1680  *
1681  * @param fn file name to be opened
1682  * @param flags opening flags, a combination of GNUNET_DISK_OPEN_xxx bit flags
1683  * @param perm permissions for the newly created file, use
1684  *             #GNUNET_DISK_PERM_NONE if a file could not be created by this
1685  *             call (because of flags)
1686  * @return IO handle on success, NULL on error
1687  */
1688 struct GNUNET_DISK_FileHandle *
1689 GNUNET_DISK_file_open (const char *fn,
1690                        enum GNUNET_DISK_OpenFlags flags,
1691                        enum GNUNET_DISK_AccessPermissions perm)
1692 {
1693   char *expfn;
1694   struct GNUNET_DISK_FileHandle *ret;
1695
1696 #ifdef MINGW
1697   DWORD access;
1698   DWORD disp;
1699   HANDLE h;
1700   wchar_t wexpfn[MAX_PATH + 1];
1701 #else
1702   int oflags;
1703   int mode;
1704   int fd;
1705 #endif
1706
1707   expfn = GNUNET_STRINGS_filename_expand (fn);
1708   if (NULL == expfn)
1709     return NULL;
1710 #ifndef MINGW
1711   mode = 0;
1712   if (GNUNET_DISK_OPEN_READWRITE == (flags & GNUNET_DISK_OPEN_READWRITE))
1713     oflags = O_RDWR;            /* note: O_RDWR is NOT always O_RDONLY | O_WRONLY */
1714   else if (flags & GNUNET_DISK_OPEN_READ)
1715     oflags = O_RDONLY;
1716   else if (flags & GNUNET_DISK_OPEN_WRITE)
1717     oflags = O_WRONLY;
1718   else
1719   {
1720     GNUNET_break (0);
1721     GNUNET_free (expfn);
1722     return NULL;
1723   }
1724   if (flags & GNUNET_DISK_OPEN_FAILIFEXISTS)
1725     oflags |= (O_CREAT | O_EXCL);
1726   if (flags & GNUNET_DISK_OPEN_TRUNCATE)
1727     oflags |= O_TRUNC;
1728   if (flags & GNUNET_DISK_OPEN_APPEND)
1729     oflags |= O_APPEND;
1730   if (flags & GNUNET_DISK_OPEN_CREATE)
1731   {
1732     (void) GNUNET_DISK_directory_create_for_file (expfn);
1733     oflags |= O_CREAT;
1734     mode = translate_unix_perms (perm);
1735   }
1736
1737   fd = open (expfn, oflags
1738 #if O_CLOEXEC
1739              | O_CLOEXEC
1740 #endif
1741              | O_LARGEFILE, mode);
1742   if (fd == -1)
1743   {
1744     if (0 == (flags & GNUNET_DISK_OPEN_FAILIFEXISTS))
1745       LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "open", expfn);
1746     else
1747       LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_DEBUG, "open", expfn);
1748     GNUNET_free (expfn);
1749     return NULL;
1750   }
1751 #else
1752   access = 0;
1753   disp = OPEN_ALWAYS;
1754
1755   if (GNUNET_DISK_OPEN_READWRITE == (flags & GNUNET_DISK_OPEN_READWRITE))
1756     access = FILE_READ_DATA | FILE_WRITE_DATA;
1757   else if (flags & GNUNET_DISK_OPEN_READ)
1758     access = FILE_READ_DATA;
1759   else if (flags & GNUNET_DISK_OPEN_WRITE)
1760     access = FILE_WRITE_DATA;
1761
1762   if (flags & GNUNET_DISK_OPEN_FAILIFEXISTS)
1763   {
1764     disp = CREATE_NEW;
1765   }
1766   else if (flags & GNUNET_DISK_OPEN_CREATE)
1767   {
1768     (void) GNUNET_DISK_directory_create_for_file (expfn);
1769     if (flags & GNUNET_DISK_OPEN_TRUNCATE)
1770       disp = CREATE_ALWAYS;
1771     else
1772       disp = OPEN_ALWAYS;
1773   }
1774   else if (flags & GNUNET_DISK_OPEN_TRUNCATE)
1775   {
1776     disp = TRUNCATE_EXISTING;
1777   }
1778   else
1779   {
1780     disp = OPEN_EXISTING;
1781   }
1782
1783   if (ERROR_SUCCESS == plibc_conv_to_win_pathwconv(expfn, wexpfn))
1784     h = CreateFileW (wexpfn, access,
1785                     FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE, NULL,
1786                     disp, FILE_ATTRIBUTE_NORMAL, NULL);
1787   else
1788     h = INVALID_HANDLE_VALUE;
1789   if (h == INVALID_HANDLE_VALUE)
1790   {
1791     int err;
1792     SetErrnoFromWinError (GetLastError ());
1793     err = errno;
1794     LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_INFO, "open", expfn);
1795     GNUNET_free (expfn);
1796     errno = err;
1797     return NULL;
1798   }
1799
1800   if (flags & GNUNET_DISK_OPEN_APPEND)
1801     if (SetFilePointer (h, 0, 0, FILE_END) == INVALID_SET_FILE_POINTER)
1802     {
1803       SetErrnoFromWinError (GetLastError ());
1804       LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "SetFilePointer", expfn);
1805       CloseHandle (h);
1806       GNUNET_free (expfn);
1807       return NULL;
1808     }
1809 #endif
1810
1811   ret = GNUNET_new (struct GNUNET_DISK_FileHandle);
1812 #ifdef MINGW
1813   ret->h = h;
1814   ret->type = GNUNET_DISK_HANLDE_TYPE_FILE;
1815 #else
1816   ret->fd = fd;
1817 #endif
1818   GNUNET_free (expfn);
1819   return ret;
1820 }
1821
1822
1823 /**
1824  * Close an open file
1825  * @param h file handle
1826  * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
1827  */
1828 int
1829 GNUNET_DISK_file_close (struct GNUNET_DISK_FileHandle *h)
1830 {
1831   int ret;
1832   if (h == NULL)
1833   {
1834     errno = EINVAL;
1835     return GNUNET_SYSERR;
1836   }
1837
1838   ret = GNUNET_OK;
1839
1840 #if MINGW
1841   if (!CloseHandle (h->h))
1842   {
1843     SetErrnoFromWinError (GetLastError ());
1844     LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING, "close");
1845     ret = GNUNET_SYSERR;
1846   }
1847   if (h->oOverlapRead)
1848   {
1849     if (!CloseHandle (h->oOverlapRead->hEvent))
1850     {
1851       SetErrnoFromWinError (GetLastError ());
1852       LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING, "close");
1853       ret = GNUNET_SYSERR;
1854     }
1855     GNUNET_free (h->oOverlapRead);
1856   }
1857   if (h->oOverlapWrite)
1858   {
1859     if (!CloseHandle (h->oOverlapWrite->hEvent))
1860     {
1861       SetErrnoFromWinError (GetLastError ());
1862       LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING, "close");
1863       ret = GNUNET_SYSERR;
1864     }
1865     GNUNET_free (h->oOverlapWrite);
1866   }
1867 #else
1868   if (close (h->fd) != 0)
1869   {
1870     LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING, "close");
1871     ret = GNUNET_SYSERR;
1872   }
1873 #endif
1874   GNUNET_free (h);
1875   return ret;
1876 }
1877
1878 #ifdef WINDOWS
1879 /**
1880  * Get a GNUnet file handle from a W32 handle.
1881  *
1882  * @param handle native handle
1883  * @return GNUnet file handle corresponding to the W32 handle
1884  */
1885 struct GNUNET_DISK_FileHandle *
1886 GNUNET_DISK_get_handle_from_w32_handle (HANDLE osfh)
1887 {
1888   struct GNUNET_DISK_FileHandle *fh;
1889
1890   DWORD dwret;
1891   enum GNUNET_FILE_Type ftype;
1892
1893   dwret = GetFileType (osfh);
1894   switch (dwret)
1895   {
1896   case FILE_TYPE_DISK:
1897     ftype = GNUNET_DISK_HANLDE_TYPE_FILE;
1898     break;
1899   case FILE_TYPE_PIPE:
1900     ftype = GNUNET_DISK_HANLDE_TYPE_PIPE;
1901     break;
1902   default:
1903     return NULL;
1904   }
1905
1906   fh = GNUNET_new (struct GNUNET_DISK_FileHandle);
1907
1908   fh->h = osfh;
1909   fh->type = ftype;
1910   if (ftype == GNUNET_DISK_HANLDE_TYPE_PIPE)
1911   {
1912     /**
1913      * Note that we can't make it overlapped if it isn't already.
1914      * (ReOpenFile() is only available in 2003/Vista).
1915      * The process that opened this file in the first place (usually a parent
1916      * process, if this is stdin/stdout/stderr) must make it overlapped,
1917      * otherwise we're screwed, as selecting on non-overlapped handle
1918      * will block.
1919      */
1920     fh->oOverlapRead = GNUNET_new (OVERLAPPED);
1921     fh->oOverlapWrite = GNUNET_new (OVERLAPPED);
1922     fh->oOverlapRead->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
1923     fh->oOverlapWrite->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
1924   }
1925
1926   return fh;
1927 }
1928 #endif
1929
1930 /**
1931  * Get a handle from a native integer FD.
1932  *
1933  * @param fno native integer file descriptor
1934  * @return file handle corresponding to the descriptor, NULL on error
1935  */
1936 struct GNUNET_DISK_FileHandle *
1937 GNUNET_DISK_get_handle_from_int_fd (int fno)
1938 {
1939   struct GNUNET_DISK_FileHandle *fh;
1940
1941   if ( (((off_t) -1) == lseek (fno, 0, SEEK_CUR)) &&
1942        (EBADF == errno) )
1943     return NULL; /* invalid FD */
1944
1945 #ifndef WINDOWS
1946   fh = GNUNET_new (struct GNUNET_DISK_FileHandle);
1947
1948   fh->fd = fno;
1949 #else
1950   intptr_t osfh;
1951
1952   osfh = _get_osfhandle (fno);
1953   if (INVALID_HANDLE_VALUE == (HANDLE) osfh)
1954     return NULL;
1955
1956   fh = GNUNET_DISK_get_handle_from_w32_handle ((HANDLE) osfh);
1957 #endif
1958
1959   return fh;
1960 }
1961
1962
1963 /**
1964  * Get a handle from a native streaming FD.
1965  *
1966  * @param fd native streaming file descriptor
1967  * @return file handle corresponding to the descriptor
1968  */
1969 struct GNUNET_DISK_FileHandle *
1970 GNUNET_DISK_get_handle_from_native (FILE *fd)
1971 {
1972   int fno;
1973
1974   fno = fileno (fd);
1975   if (-1 == fno)
1976     return NULL;
1977
1978   return GNUNET_DISK_get_handle_from_int_fd (fno);
1979 }
1980
1981
1982 /**
1983  * Handle for a memory-mapping operation.
1984  */
1985 struct GNUNET_DISK_MapHandle
1986 {
1987   /**
1988    * Address where the map is in memory.
1989    */
1990   void *addr;
1991
1992 #ifdef MINGW
1993   /**
1994    * Underlying OS handle.
1995    */
1996   HANDLE h;
1997 #else
1998   /**
1999    * Number of bytes mapped.
2000    */
2001   size_t len;
2002 #endif
2003 };
2004
2005
2006 #ifndef MAP_FAILED
2007 #define MAP_FAILED ((void *) -1)
2008 #endif
2009
2010 /**
2011  * Map a file into memory
2012  *
2013  * @param h open file handle
2014  * @param m handle to the new mapping
2015  * @param access access specification, GNUNET_DISK_MAP_TYPE_xxx
2016  * @param len size of the mapping
2017  * @return pointer to the mapped memory region, NULL on failure
2018  */
2019 void *
2020 GNUNET_DISK_file_map (const struct GNUNET_DISK_FileHandle *h,
2021                       struct GNUNET_DISK_MapHandle **m,
2022                       enum GNUNET_DISK_MapType access, size_t len)
2023 {
2024   if (h == NULL)
2025   {
2026     errno = EINVAL;
2027     return NULL;
2028   }
2029
2030 #ifdef MINGW
2031   DWORD mapAccess, protect;
2032
2033   if ((access & GNUNET_DISK_MAP_TYPE_READ) &&
2034       (access & GNUNET_DISK_MAP_TYPE_WRITE))
2035   {
2036     protect = PAGE_READWRITE;
2037     mapAccess = FILE_MAP_ALL_ACCESS;
2038   }
2039   else if (access & GNUNET_DISK_MAP_TYPE_READ)
2040   {
2041     protect = PAGE_READONLY;
2042     mapAccess = FILE_MAP_READ;
2043   }
2044   else if (access & GNUNET_DISK_MAP_TYPE_WRITE)
2045   {
2046     protect = PAGE_READWRITE;
2047     mapAccess = FILE_MAP_WRITE;
2048   }
2049   else
2050   {
2051     GNUNET_break (0);
2052     return NULL;
2053   }
2054
2055   *m = GNUNET_new (struct GNUNET_DISK_MapHandle);
2056   (*m)->h = CreateFileMapping (h->h, NULL, protect, 0, 0, NULL);
2057   if ((*m)->h == INVALID_HANDLE_VALUE)
2058   {
2059     SetErrnoFromWinError (GetLastError ());
2060     GNUNET_free (*m);
2061     return NULL;
2062   }
2063
2064   (*m)->addr = MapViewOfFile ((*m)->h, mapAccess, 0, 0, len);
2065   if (!(*m)->addr)
2066   {
2067     SetErrnoFromWinError (GetLastError ());
2068     CloseHandle ((*m)->h);
2069     GNUNET_free (*m);
2070   }
2071
2072   return (*m)->addr;
2073 #else
2074   int prot;
2075
2076   prot = 0;
2077   if (access & GNUNET_DISK_MAP_TYPE_READ)
2078     prot = PROT_READ;
2079   if (access & GNUNET_DISK_MAP_TYPE_WRITE)
2080     prot |= PROT_WRITE;
2081   *m = GNUNET_new (struct GNUNET_DISK_MapHandle);
2082   (*m)->addr = mmap (NULL, len, prot, MAP_SHARED, h->fd, 0);
2083   GNUNET_assert (NULL != (*m)->addr);
2084   if (MAP_FAILED == (*m)->addr)
2085   {
2086     GNUNET_free (*m);
2087     return NULL;
2088   }
2089   (*m)->len = len;
2090   return (*m)->addr;
2091 #endif
2092 }
2093
2094 /**
2095  * Unmap a file
2096  * @param h mapping handle
2097  * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
2098  */
2099 int
2100 GNUNET_DISK_file_unmap (struct GNUNET_DISK_MapHandle *h)
2101 {
2102   int ret;
2103
2104   if (h == NULL)
2105   {
2106     errno = EINVAL;
2107     return GNUNET_SYSERR;
2108   }
2109
2110 #ifdef MINGW
2111   ret = UnmapViewOfFile (h->addr) ? GNUNET_OK : GNUNET_SYSERR;
2112   if (ret != GNUNET_OK)
2113     SetErrnoFromWinError (GetLastError ());
2114   if (!CloseHandle (h->h) && (ret == GNUNET_OK))
2115   {
2116     ret = GNUNET_SYSERR;
2117     SetErrnoFromWinError (GetLastError ());
2118   }
2119 #else
2120   ret = munmap (h->addr, h->len) != -1 ? GNUNET_OK : GNUNET_SYSERR;
2121 #endif
2122   GNUNET_free (h);
2123   return ret;
2124 }
2125
2126
2127 /**
2128  * Write file changes to disk
2129  * @param h handle to an open file
2130  * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
2131  */
2132 int
2133 GNUNET_DISK_file_sync (const struct GNUNET_DISK_FileHandle *h)
2134 {
2135   if (h == NULL)
2136   {
2137     errno = EINVAL;
2138     return GNUNET_SYSERR;
2139   }
2140
2141 #ifdef MINGW
2142   int ret;
2143
2144   ret = FlushFileBuffers (h->h) ? GNUNET_OK : GNUNET_SYSERR;
2145   if (ret != GNUNET_OK)
2146     SetErrnoFromWinError (GetLastError ());
2147   return ret;
2148 #elif defined(FREEBSD) || defined(OPENBSD) || defined(DARWIN)
2149   return fsync (h->fd) == -1 ? GNUNET_SYSERR : GNUNET_OK;
2150 #else
2151   return fdatasync (h->fd) == -1 ? GNUNET_SYSERR : GNUNET_OK;
2152 #endif
2153 }
2154
2155
2156 #if WINDOWS
2157 #ifndef PIPE_BUF
2158 #define PIPE_BUF        512
2159 #endif
2160 /* Copyright Bob Byrnes  <byrnes <at> curl.com>
2161    http://permalink.gmane.org/gmane.os.cygwin.patches/2121
2162 */
2163 /* Create a pipe, and return handles to the read and write ends,
2164    just like CreatePipe, but ensure that the write end permits
2165    FILE_READ_ATTRIBUTES access, on later versions of win32 where
2166    this is supported.  This access is needed by NtQueryInformationFile,
2167    which is used to implement select and nonblocking writes.
2168    Note that the return value is either NO_ERROR or GetLastError,
2169    unlike CreatePipe, which returns a bool for success or failure.  */
2170 static int
2171 create_selectable_pipe (PHANDLE read_pipe_ptr, PHANDLE write_pipe_ptr,
2172                         LPSECURITY_ATTRIBUTES sa_ptr, DWORD psize,
2173                         DWORD dwReadMode, DWORD dwWriteMode)
2174 {
2175   /* Default to error. */
2176   *read_pipe_ptr = *write_pipe_ptr = INVALID_HANDLE_VALUE;
2177
2178   HANDLE read_pipe;
2179   HANDLE write_pipe;
2180
2181   /* Ensure that there is enough pipe buffer space for atomic writes.  */
2182   if (psize < PIPE_BUF)
2183     psize = PIPE_BUF;
2184
2185   char pipename[MAX_PATH];
2186
2187   /* Retry CreateNamedPipe as long as the pipe name is in use.
2188    * Retrying will probably never be necessary, but we want
2189    * to be as robust as possible.  */
2190   while (1)
2191   {
2192     static volatile LONG pipe_unique_id;
2193
2194     snprintf (pipename, sizeof pipename, "\\\\.\\pipe\\gnunet-%d-%ld",
2195               getpid (), InterlockedIncrement ((LONG *) & pipe_unique_id));
2196     LOG (GNUNET_ERROR_TYPE_DEBUG, "CreateNamedPipe: name = %s, size = %lu\n",
2197          pipename, psize);
2198     /* Use CreateNamedPipe instead of CreatePipe, because the latter
2199      * returns a write handle that does not permit FILE_READ_ATTRIBUTES
2200      * access, on versions of win32 earlier than WinXP SP2.
2201      * CreatePipe also stupidly creates a full duplex pipe, which is
2202      * a waste, since only a single direction is actually used.
2203      * It's important to only allow a single instance, to ensure that
2204      * the pipe was not created earlier by some other process, even if
2205      * the pid has been reused.  */
2206     read_pipe = CreateNamedPipeA (pipename, PIPE_ACCESS_INBOUND | FILE_FLAG_FIRST_PIPE_INSTANCE | dwReadMode, PIPE_TYPE_BYTE | PIPE_READMODE_BYTE, 1,   /* max instances */
2207                                   psize,        /* output buffer size */
2208                                   psize,        /* input buffer size */
2209                                   NMPWAIT_USE_DEFAULT_WAIT, sa_ptr);
2210
2211     if (read_pipe != INVALID_HANDLE_VALUE)
2212     {
2213       LOG (GNUNET_ERROR_TYPE_DEBUG, "pipe read handle = %p\n", read_pipe);
2214       break;
2215     }
2216
2217     DWORD err = GetLastError ();
2218
2219     switch (err)
2220     {
2221     case ERROR_PIPE_BUSY:
2222       /* The pipe is already open with compatible parameters.
2223        * Pick a new name and retry.  */
2224       LOG (GNUNET_ERROR_TYPE_DEBUG, "pipe busy, retrying\n");
2225       continue;
2226     case ERROR_ACCESS_DENIED:
2227       /* The pipe is already open with incompatible parameters.
2228        * Pick a new name and retry.  */
2229       LOG (GNUNET_ERROR_TYPE_DEBUG, "pipe access denied, retrying\n");
2230       continue;
2231     case ERROR_CALL_NOT_IMPLEMENTED:
2232       /* We are on an older Win9x platform without named pipes.
2233        * Return an anonymous pipe as the best approximation.  */
2234       LOG (GNUNET_ERROR_TYPE_DEBUG,
2235            "CreateNamedPipe not implemented, resorting to "
2236            "CreatePipe: size = %lu\n", psize);
2237       if (CreatePipe (read_pipe_ptr, write_pipe_ptr, sa_ptr, psize))
2238       {
2239         LOG (GNUNET_ERROR_TYPE_DEBUG, "pipe read handle = %p, write handle = %p\n",
2240              *read_pipe_ptr,
2241              *write_pipe_ptr);
2242         return GNUNET_OK;
2243       }
2244       err = GetLastError ();
2245       LOG (GNUNET_ERROR_TYPE_ERROR, "CreatePipe failed: %d\n", err);
2246       return err;
2247     default:
2248       LOG (GNUNET_ERROR_TYPE_ERROR, "CreateNamedPipe failed: %d\n", err);
2249       return err;
2250     }
2251     /* NOTREACHED */
2252   }
2253   LOG (GNUNET_ERROR_TYPE_DEBUG, "CreateFile: name = %s\n", pipename);
2254
2255   /* Open the named pipe for writing.
2256    * Be sure to permit FILE_READ_ATTRIBUTES access.  */
2257   write_pipe = CreateFileA (pipename, GENERIC_WRITE | FILE_READ_ATTRIBUTES, 0,  /* share mode */
2258                             sa_ptr, OPEN_EXISTING, dwWriteMode, /* flags and attributes */
2259                             0); /* handle to template file */
2260
2261   if (write_pipe == INVALID_HANDLE_VALUE)
2262   {
2263     /* Failure. */
2264     DWORD err = GetLastError ();
2265
2266     LOG (GNUNET_ERROR_TYPE_DEBUG, "CreateFile failed: %d\n", err);
2267     CloseHandle (read_pipe);
2268     return err;
2269   }
2270   LOG (GNUNET_ERROR_TYPE_DEBUG, "pipe write handle = %p\n", write_pipe);
2271   /* Success. */
2272   *read_pipe_ptr = read_pipe;
2273   *write_pipe_ptr = write_pipe;
2274   return GNUNET_OK;
2275 }
2276 #endif
2277
2278
2279 /**
2280  * Creates an interprocess channel
2281  *
2282  * @param blocking_read creates an asynchronous pipe for reading if set to GNUNET_NO
2283  * @param blocking_write creates an asynchronous pipe for writing if set to GNUNET_NO
2284  * @param inherit_read inherit the parent processes stdin (only for windows)
2285  * @param inherit_write inherit the parent processes stdout (only for windows)
2286  * @return handle to the new pipe, NULL on error
2287  */
2288 struct GNUNET_DISK_PipeHandle *
2289 GNUNET_DISK_pipe (int blocking_read, int blocking_write, int inherit_read, int inherit_write)
2290 {
2291 #ifndef MINGW
2292   int fd[2];
2293   int ret;
2294   int eno;
2295
2296   ret = pipe (fd);
2297   if (ret == -1)
2298   {
2299     eno = errno;
2300     LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR, "pipe");
2301     errno = eno;
2302     return NULL;
2303   }
2304   return GNUNET_DISK_pipe_from_fd (blocking_read,
2305                                    blocking_write,
2306                                    fd);
2307 #else
2308   struct GNUNET_DISK_PipeHandle *p;
2309   BOOL ret;
2310   HANDLE tmp_handle;
2311   int save_errno;
2312
2313   p = GNUNET_new (struct GNUNET_DISK_PipeHandle);
2314   p->fd[0] = GNUNET_new (struct GNUNET_DISK_FileHandle);
2315   p->fd[1] = GNUNET_new (struct GNUNET_DISK_FileHandle);
2316
2317   /* All pipes are overlapped. If you want them to block - just
2318    * call WriteFile() and ReadFile() with NULL overlapped pointer.
2319    * NOTE: calling with NULL overlapped pointer works only
2320    * for pipes, and doesn't seem to be a documented feature.
2321    * It will NOT work for files, because overlapped files need
2322    * to read offsets from the overlapped structure, regardless.
2323    * Pipes are not seekable, and need no offsets, which is
2324    * probably why it works for them.
2325    */
2326   ret =
2327       create_selectable_pipe (&p->fd[0]->h, &p->fd[1]->h, NULL, 0,
2328                               FILE_FLAG_OVERLAPPED,
2329                               FILE_FLAG_OVERLAPPED);
2330   if (!ret)
2331   {
2332     SetErrnoFromWinError (GetLastError ());
2333     save_errno = errno;
2334     GNUNET_free (p->fd[0]);
2335     GNUNET_free (p->fd[1]);
2336     GNUNET_free (p);
2337     errno = save_errno;
2338     return NULL;
2339   }
2340   if (!DuplicateHandle
2341       (GetCurrentProcess (), p->fd[0]->h, GetCurrentProcess (), &tmp_handle, 0,
2342        inherit_read == GNUNET_YES ? TRUE : FALSE, DUPLICATE_SAME_ACCESS))
2343   {
2344     SetErrnoFromWinError (GetLastError ());
2345     save_errno = errno;
2346     CloseHandle (p->fd[0]->h);
2347     CloseHandle (p->fd[1]->h);
2348     GNUNET_free (p->fd[0]);
2349     GNUNET_free (p->fd[1]);
2350     GNUNET_free (p);
2351     errno = save_errno;
2352     return NULL;
2353   }
2354   CloseHandle (p->fd[0]->h);
2355   p->fd[0]->h = tmp_handle;
2356
2357   if (!DuplicateHandle
2358       (GetCurrentProcess (), p->fd[1]->h, GetCurrentProcess (), &tmp_handle, 0,
2359        inherit_write == GNUNET_YES ? TRUE : FALSE, DUPLICATE_SAME_ACCESS))
2360   {
2361     SetErrnoFromWinError (GetLastError ());
2362     save_errno = errno;
2363     CloseHandle (p->fd[0]->h);
2364     CloseHandle (p->fd[1]->h);
2365     GNUNET_free (p->fd[0]);
2366     GNUNET_free (p->fd[1]);
2367     GNUNET_free (p);
2368     errno = save_errno;
2369     return NULL;
2370   }
2371   CloseHandle (p->fd[1]->h);
2372   p->fd[1]->h = tmp_handle;
2373
2374   p->fd[0]->type = GNUNET_DISK_HANLDE_TYPE_PIPE;
2375   p->fd[1]->type = GNUNET_DISK_HANLDE_TYPE_PIPE;
2376
2377   p->fd[0]->oOverlapRead = GNUNET_new (OVERLAPPED);
2378   p->fd[0]->oOverlapWrite = GNUNET_new (OVERLAPPED);
2379   p->fd[1]->oOverlapRead = GNUNET_new (OVERLAPPED);
2380   p->fd[1]->oOverlapWrite = GNUNET_new (OVERLAPPED);
2381
2382   p->fd[0]->oOverlapRead->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2383   p->fd[0]->oOverlapWrite->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2384
2385   p->fd[1]->oOverlapRead->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2386   p->fd[1]->oOverlapWrite->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2387
2388   return p;
2389 #endif
2390 }
2391
2392
2393 /**
2394  * Creates a pipe object from a couple of file descriptors.
2395  * Useful for wrapping existing pipe FDs.
2396  *
2397  * @param blocking_read creates an asynchronous pipe for reading if set to GNUNET_NO
2398  * @param blocking_write creates an asynchronous pipe for writing if set to GNUNET_NO
2399  * @param fd an array of two fd values. One of them may be -1 for read-only or write-only pipes
2400  *
2401  * @return handle to the new pipe, NULL on error
2402  */
2403 struct GNUNET_DISK_PipeHandle *
2404 GNUNET_DISK_pipe_from_fd (int blocking_read, int blocking_write, int fd[2])
2405 {
2406   struct GNUNET_DISK_PipeHandle *p;
2407
2408   p = GNUNET_new (struct GNUNET_DISK_PipeHandle);
2409
2410 #ifndef MINGW
2411   int ret;
2412   int flags;
2413   int eno = 0; /* make gcc happy */
2414
2415   ret = 0;
2416   if (fd[0] >= 0)
2417   {
2418     p->fd[0] = GNUNET_new (struct GNUNET_DISK_FileHandle);
2419     p->fd[0]->fd = fd[0];
2420     if (!blocking_read)
2421     {
2422       flags = fcntl (fd[0], F_GETFL);
2423       flags |= O_NONBLOCK;
2424       if (0 > fcntl (fd[0], F_SETFL, flags))
2425       {
2426         ret = -1;
2427         eno = errno;
2428       }
2429     }
2430     flags = fcntl (fd[0], F_GETFD);
2431     flags |= FD_CLOEXEC;
2432     if (0 > fcntl (fd[0], F_SETFD, flags))
2433     {
2434       ret = -1;
2435       eno = errno;
2436     }
2437   }
2438
2439   if (fd[1] >= 0)
2440   {
2441     p->fd[1] = GNUNET_new (struct GNUNET_DISK_FileHandle);
2442     p->fd[1]->fd = fd[1];
2443     if (!blocking_write)
2444     {
2445       flags = fcntl (fd[1], F_GETFL);
2446       flags |= O_NONBLOCK;
2447       if (0 > fcntl (fd[1], F_SETFL, flags))
2448       {
2449         ret = -1;
2450         eno = errno;
2451       }
2452     }
2453     flags = fcntl (fd[1], F_GETFD);
2454     flags |= FD_CLOEXEC;
2455     if (0 > fcntl (fd[1], F_SETFD, flags))
2456     {
2457       ret = -1;
2458       eno = errno;
2459     }
2460   }
2461   if (ret == -1)
2462   {
2463     errno = eno;
2464     LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR, "fcntl");
2465     if (p->fd[0]->fd >= 0)
2466       GNUNET_break (0 == close (p->fd[0]->fd));
2467     if (p->fd[1]->fd >= 0)
2468       GNUNET_break (0 == close (p->fd[1]->fd));
2469     GNUNET_free_non_null (p->fd[0]);
2470     GNUNET_free_non_null (p->fd[1]);
2471     GNUNET_free (p);
2472     errno = eno;
2473     return NULL;
2474   }
2475 #else
2476   if (fd[0] >= 0)
2477   {
2478     p->fd[0] = GNUNET_new (struct GNUNET_DISK_FileHandle);
2479     p->fd[0]->h = (HANDLE) _get_osfhandle (fd[0]);
2480     if (p->fd[0]->h != INVALID_HANDLE_VALUE)
2481     {
2482       p->fd[0]->type = GNUNET_DISK_HANLDE_TYPE_PIPE;
2483       p->fd[0]->oOverlapRead = GNUNET_new (OVERLAPPED);
2484       p->fd[0]->oOverlapWrite = GNUNET_new (OVERLAPPED);
2485       p->fd[0]->oOverlapRead->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2486       p->fd[0]->oOverlapWrite->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2487     }
2488     else
2489     {
2490       GNUNET_free (p->fd[0]);
2491       p->fd[0] = NULL;
2492     }
2493   }
2494   if (fd[1] >= 0)
2495   {
2496     p->fd[1] = GNUNET_new (struct GNUNET_DISK_FileHandle);
2497     p->fd[1]->h = (HANDLE) _get_osfhandle (fd[1]);
2498     if (p->fd[1]->h != INVALID_HANDLE_VALUE)
2499     {
2500       p->fd[1]->type = GNUNET_DISK_HANLDE_TYPE_PIPE;
2501       p->fd[1]->oOverlapRead = GNUNET_new (OVERLAPPED);
2502       p->fd[1]->oOverlapWrite = GNUNET_new (OVERLAPPED);
2503       p->fd[1]->oOverlapRead->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2504       p->fd[1]->oOverlapWrite->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2505     }
2506     else
2507     {
2508       GNUNET_free (p->fd[1]);
2509       p->fd[1] = NULL;
2510     }
2511   }
2512
2513 #endif
2514   return p;
2515 }
2516
2517
2518 /**
2519  * Closes an interprocess channel
2520  *
2521  * @param p pipe to close
2522  * @param end which end of the pipe to close
2523  * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
2524  */
2525 int
2526 GNUNET_DISK_pipe_close_end (struct GNUNET_DISK_PipeHandle *p,
2527                             enum GNUNET_DISK_PipeEnd end)
2528 {
2529   int ret = GNUNET_OK;
2530
2531   if (end == GNUNET_DISK_PIPE_END_READ)
2532   {
2533     if (p->fd[0])
2534     {
2535       ret = GNUNET_DISK_file_close (p->fd[0]);
2536       p->fd[0] = NULL;
2537     }
2538   }
2539   else if (end == GNUNET_DISK_PIPE_END_WRITE)
2540   {
2541     if (p->fd[1])
2542     {
2543       ret = GNUNET_DISK_file_close (p->fd[1]);
2544       p->fd[1] = NULL;
2545     }
2546   }
2547
2548   return ret;
2549 }
2550
2551 /**
2552  * Detaches one of the ends from the pipe.
2553  * Detached end is a fully-functional FileHandle, it will
2554  * not be affected by anything you do with the pipe afterwards.
2555  * Each end of a pipe can only be detched from it once (i.e.
2556  * it is not duplicated).
2557  *
2558  * @param p pipe to detach an end from
2559  * @param end which end of the pipe to detach
2560  * @return Detached end on success, NULL on failure
2561  * (or if that end is not present or is closed).
2562  */
2563 struct GNUNET_DISK_FileHandle *
2564 GNUNET_DISK_pipe_detach_end (struct GNUNET_DISK_PipeHandle *p,
2565                              enum GNUNET_DISK_PipeEnd end)
2566 {
2567   struct GNUNET_DISK_FileHandle *ret = NULL;
2568
2569   if (end == GNUNET_DISK_PIPE_END_READ)
2570   {
2571     if (p->fd[0])
2572     {
2573       ret = p->fd[0];
2574       p->fd[0] = NULL;
2575     }
2576   }
2577   else if (end == GNUNET_DISK_PIPE_END_WRITE)
2578   {
2579     if (p->fd[1])
2580     {
2581       ret = p->fd[1];
2582       p->fd[1] = NULL;
2583     }
2584   }
2585
2586   return ret;
2587 }
2588
2589
2590 /**
2591  * Closes an interprocess channel
2592  *
2593  * @param p pipe to close
2594  * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
2595  */
2596 int
2597 GNUNET_DISK_pipe_close (struct GNUNET_DISK_PipeHandle *p)
2598 {
2599   int ret = GNUNET_OK;
2600
2601   int read_end_close;
2602   int write_end_close;
2603   int read_end_close_errno;
2604   int write_end_close_errno;
2605
2606   read_end_close = GNUNET_DISK_pipe_close_end (p, GNUNET_DISK_PIPE_END_READ);
2607   read_end_close_errno = errno;
2608   write_end_close = GNUNET_DISK_pipe_close_end (p, GNUNET_DISK_PIPE_END_WRITE);
2609   write_end_close_errno = errno;
2610   GNUNET_free (p);
2611
2612   if (GNUNET_OK != read_end_close)
2613   {
2614     errno = read_end_close_errno;
2615     ret = read_end_close;
2616   }
2617   else if (GNUNET_OK != write_end_close)
2618   {
2619     errno = write_end_close_errno;
2620     ret = write_end_close;
2621   }
2622
2623   return ret;
2624 }
2625
2626
2627 /**
2628  * Get the handle to a particular pipe end
2629  *
2630  * @param p pipe
2631  * @param n end to access
2632  * @return handle for the respective end
2633  */
2634 const struct GNUNET_DISK_FileHandle *
2635 GNUNET_DISK_pipe_handle (const struct GNUNET_DISK_PipeHandle *p,
2636                          enum GNUNET_DISK_PipeEnd n)
2637 {
2638   switch (n)
2639   {
2640   case GNUNET_DISK_PIPE_END_READ:
2641   case GNUNET_DISK_PIPE_END_WRITE:
2642     return p->fd[n];
2643   default:
2644     GNUNET_break (0);
2645     return NULL;
2646   }
2647 }
2648
2649
2650 /**
2651  * Retrieve OS file handle
2652  * @internal
2653  * @param fh GNUnet file descriptor
2654  * @param dst destination buffer
2655  * @param dst_len length of dst
2656  * @return #GNUNET_OK on success, #GNUNET_SYSERR otherwise
2657  */
2658 int
2659 GNUNET_DISK_internal_file_handle_ (const struct GNUNET_DISK_FileHandle *fh,
2660                                    void *dst, size_t dst_len)
2661 {
2662   if (NULL == fh)
2663     return GNUNET_SYSERR;
2664 #ifdef MINGW
2665   if (dst_len < sizeof (HANDLE))
2666     return GNUNET_SYSERR;
2667   *((HANDLE *) dst) = fh->h;
2668 #else
2669   if (dst_len < sizeof (int))
2670     return GNUNET_SYSERR;
2671   *((int *) dst) = fh->fd;
2672 #endif
2673
2674   return GNUNET_OK;
2675 }
2676
2677 /* end of disk.c */