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