82cc9ddabfe22d0db982eacbfcc2d8d8adc12a35
[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.
588  * Will not print an error message if the directory
589  * does not exist.  Will log errors if GNUNET_SYSERR is
590  * returned (i.e., a file exists with the same name).
591  *
592  * @param fil filename to test
593  * @return GNUNET_YES if yes, GNUNET_NO if not, GNUNET_SYSERR if it
594  *   does not exist
595  */
596 int
597 GNUNET_DISK_directory_test (const char *fil)
598 {
599   struct stat filestat;
600   int ret;
601
602   ret = STAT (fil, &filestat);
603   if (ret != 0)
604   {
605     if (errno != ENOENT)
606     {
607       LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "stat", fil);
608       return GNUNET_SYSERR;
609     }
610     return GNUNET_NO;
611   }
612   if (!S_ISDIR (filestat.st_mode))
613     return GNUNET_NO;
614   if (ACCESS (fil, R_OK | X_OK) < 0)
615   {
616     LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "access", fil);
617     return GNUNET_SYSERR;
618   }
619   return GNUNET_YES;
620 }
621
622
623 /**
624  * Check that fil corresponds to a filename
625  * (of a file that exists and that is not a directory).
626  *
627  * @param fil filename to check
628  * @return GNUNET_YES if yes, GNUNET_NO if not a file, GNUNET_SYSERR if something
629  * else (will print an error message in that case, too).
630  */
631 int
632 GNUNET_DISK_file_test (const char *fil)
633 {
634   struct stat filestat;
635   int ret;
636   char *rdir;
637
638   rdir = GNUNET_STRINGS_filename_expand (fil);
639   if (rdir == NULL)
640     return GNUNET_SYSERR;
641
642   ret = STAT (rdir, &filestat);
643   if (ret != 0)
644   {
645     if (errno != ENOENT)
646     {
647       LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "stat", rdir);
648       GNUNET_free (rdir);
649       return GNUNET_SYSERR;
650     }
651     GNUNET_free (rdir);
652     return GNUNET_NO;
653   }
654   if (!S_ISREG (filestat.st_mode))
655   {
656     GNUNET_free (rdir);
657     return GNUNET_NO;
658   }
659   if (ACCESS (rdir, F_OK) < 0)
660   {
661     LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "access", rdir);
662     GNUNET_free (rdir);
663     return GNUNET_SYSERR;
664   }
665   GNUNET_free (rdir);
666   return GNUNET_YES;
667 }
668
669
670 /**
671  * Implementation of "mkdir -p"
672  * @param dir the directory to create
673  * @returns GNUNET_OK on success, GNUNET_SYSERR on failure
674  */
675 int
676 GNUNET_DISK_directory_create (const char *dir)
677 {
678   char *rdir;
679   unsigned int len;
680   unsigned int pos;
681   unsigned int pos2;
682   int ret = GNUNET_OK;
683
684   rdir = GNUNET_STRINGS_filename_expand (dir);
685   if (rdir == NULL)
686     return GNUNET_SYSERR;
687
688   len = strlen (rdir);
689 #ifndef MINGW
690   pos = 1;                      /* skip heading '/' */
691 #else
692   /* Local or Network path? */
693   if (strncmp (rdir, "\\\\", 2) == 0)
694   {
695     pos = 2;
696     while (rdir[pos])
697     {
698       if (rdir[pos] == '\\')
699       {
700         pos++;
701         break;
702       }
703       pos++;
704     }
705   }
706   else
707   {
708     pos = 3;                    /* strlen("C:\\") */
709   }
710 #endif
711   /* Check which low level directories already exist */
712   pos2 = len;
713   rdir[len] = DIR_SEPARATOR;
714   while (pos <= pos2)
715   {
716     if (DIR_SEPARATOR == rdir[pos2])
717     {
718       rdir[pos2] = '\0';
719       ret = GNUNET_DISK_directory_test (rdir);
720       if (GNUNET_SYSERR == ret)
721       {
722         GNUNET_free (rdir);
723         return GNUNET_SYSERR;
724       }
725       rdir[pos2] = DIR_SEPARATOR;
726       if (GNUNET_YES == ret)
727       {
728         pos2++;
729         break;
730       }
731     }
732     pos2--;
733   }
734   rdir[len] = '\0';
735   if (pos < pos2)
736     pos = pos2;
737   /* Start creating directories */
738   while (pos <= len)
739   {
740     if ((rdir[pos] == DIR_SEPARATOR) || (pos == len))
741     {
742       rdir[pos] = '\0';
743       ret = GNUNET_DISK_directory_test (rdir);
744       if (ret == GNUNET_SYSERR)
745       {
746         GNUNET_free (rdir);
747         return GNUNET_SYSERR;
748       }
749       if (ret == GNUNET_NO)
750       {
751 #ifndef MINGW
752         ret = mkdir (rdir, S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH);        /* 755 */
753 #else
754         wchar_t wrdir[MAX_PATH + 1];
755         if (ERROR_SUCCESS == plibc_conv_to_win_pathwconv(rdir, wrdir))
756           ret = !CreateDirectoryW (wrdir, NULL);
757         else
758           ret = 1;
759 #endif
760         if ((ret != 0) && (errno != EEXIST))
761         {
762           LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_ERROR, "mkdir", rdir);
763           GNUNET_free (rdir);
764           return GNUNET_SYSERR;
765         }
766       }
767       rdir[pos] = DIR_SEPARATOR;
768     }
769     pos++;
770   }
771   GNUNET_free (rdir);
772   return GNUNET_OK;
773 }
774
775
776 /**
777  * Create the directory structure for storing
778  * a file.
779  *
780  * @param filename name of a file in the directory
781  * @returns GNUNET_OK on success,
782  *          GNUNET_SYSERR on failure,
783  *          GNUNET_NO if the directory
784  *          exists but is not writeable for us
785  */
786 int
787 GNUNET_DISK_directory_create_for_file (const char *filename)
788 {
789   char *rdir;
790   int len;
791   int ret;
792
793   rdir = GNUNET_STRINGS_filename_expand (filename);
794   if (rdir == NULL)
795     return GNUNET_SYSERR;
796   len = strlen (rdir);
797   while ((len > 0) && (rdir[len] != DIR_SEPARATOR))
798     len--;
799   rdir[len] = '\0';
800   ret = GNUNET_DISK_directory_create (rdir);
801   if ((ret == GNUNET_OK) && (0 != ACCESS (rdir, W_OK)))
802     ret = GNUNET_NO;
803   GNUNET_free (rdir);
804   return ret;
805 }
806
807
808 /**
809  * Read the contents of a binary file into a buffer.
810  * @param h handle to an open file
811  * @param result the buffer to write the result to
812  * @param len the maximum number of bytes to read
813  * @return the number of bytes read on success, GNUNET_SYSERR on failure
814  */
815 ssize_t
816 GNUNET_DISK_file_read (const struct GNUNET_DISK_FileHandle * h, void *result,
817                        size_t len)
818 {
819   if (h == NULL)
820   {
821     errno = EINVAL;
822     return GNUNET_SYSERR;
823   }
824
825 #ifdef MINGW
826   DWORD bytesRead;
827
828   if (h->type != GNUNET_DISK_HANLDE_TYPE_PIPE)
829   {
830     if (!ReadFile (h->h, result, len, &bytesRead, NULL))
831     {
832       SetErrnoFromWinError (GetLastError ());
833       return GNUNET_SYSERR;
834     }
835   }
836   else
837   {
838     if (!ReadFile (h->h, result, len, &bytesRead, h->oOverlapRead))
839     {
840       if (GetLastError () != ERROR_IO_PENDING)
841       {
842         LOG (GNUNET_ERROR_TYPE_DEBUG, "Error reading from pipe: %u\n", GetLastError ());
843         SetErrnoFromWinError (GetLastError ());
844         return GNUNET_SYSERR;
845       }
846       LOG (GNUNET_ERROR_TYPE_DEBUG, "Will get overlapped result\n");
847       GetOverlappedResult (h->h, h->oOverlapRead, &bytesRead, TRUE);
848     }
849     LOG (GNUNET_ERROR_TYPE_DEBUG, "Read %u bytes from pipe\n", bytesRead);
850   }
851   return bytesRead;
852 #else
853   return read (h->fd, result, len);
854 #endif
855 }
856
857
858 /**
859  * Read the contents of a binary file into a buffer.
860  * Guarantees not to block (returns GNUNET_SYSERR and sets errno to EAGAIN
861  * when no data can be read).
862  *
863  * @param h handle to an open file
864  * @param result the buffer to write the result to
865  * @param len the maximum number of bytes to read
866  * @return the number of bytes read on success, GNUNET_SYSERR on failure
867  */
868 ssize_t
869 GNUNET_DISK_file_read_non_blocking (const struct GNUNET_DISK_FileHandle * h,
870                                     void *result, 
871                                     size_t len)
872 {
873   if (h == NULL)
874   {
875     errno = EINVAL;
876     return GNUNET_SYSERR;
877   }
878
879 #ifdef MINGW
880   DWORD bytesRead;
881
882   if (h->type != GNUNET_DISK_HANLDE_TYPE_PIPE)
883   {
884     if (!ReadFile (h->h, result, len, &bytesRead, NULL))
885     {
886       SetErrnoFromWinError (GetLastError ());
887       return GNUNET_SYSERR;
888     }
889   }
890   else
891   {
892     if (!ReadFile (h->h, result, len, &bytesRead, h->oOverlapRead))
893     {
894       if (GetLastError () != ERROR_IO_PENDING)
895       {
896         LOG (GNUNET_ERROR_TYPE_DEBUG, "Error reading from pipe: %u\n", GetLastError ());
897         SetErrnoFromWinError (GetLastError ());
898         return GNUNET_SYSERR;
899       }
900       else
901       {
902         LOG (GNUNET_ERROR_TYPE_DEBUG,
903             "ReadFile() queued a read, cancelling\n");
904         CancelIo (h->h);
905         errno = EAGAIN;
906         return GNUNET_SYSERR;
907       }
908     }
909     LOG (GNUNET_ERROR_TYPE_DEBUG, "Read %u bytes\n", bytesRead);
910   }
911   return bytesRead;
912 #else
913   int flags;
914   ssize_t ret;
915
916   /* set to non-blocking, read, then set back */
917   flags = fcntl (h->fd, F_GETFL);
918   if (0 == (flags & O_NONBLOCK))
919     (void) fcntl (h->fd, F_SETFL, flags | O_NONBLOCK);
920   ret = read (h->fd, result, len);
921   if (0 == (flags & O_NONBLOCK))
922     {
923       int eno = errno;
924       (void) fcntl (h->fd, F_SETFL, flags);
925       errno = eno;
926     }
927   return ret;
928 #endif
929 }
930
931
932 /**
933  * Read the contents of a binary file into a buffer.
934  *
935  * @param fn file name
936  * @param result the buffer to write the result to
937  * @param len the maximum number of bytes to read
938  * @return number of bytes read, GNUNET_SYSERR on failure
939  */
940 ssize_t
941 GNUNET_DISK_fn_read (const char *fn, void *result, size_t len)
942 {
943   struct GNUNET_DISK_FileHandle *fh;
944   ssize_t ret;
945
946   fh = GNUNET_DISK_file_open (fn, GNUNET_DISK_OPEN_READ, GNUNET_DISK_PERM_NONE);
947   if (!fh)
948     return GNUNET_SYSERR;
949   ret = GNUNET_DISK_file_read (fh, result, len);
950   GNUNET_assert (GNUNET_OK == GNUNET_DISK_file_close (fh));
951
952   return ret;
953 }
954
955
956 /**
957  * Write a buffer to a file.
958  * @param h handle to open file
959  * @param buffer the data to write
960  * @param n number of bytes to write
961  * @return number of bytes written on success, GNUNET_SYSERR on error
962  */
963 ssize_t
964 GNUNET_DISK_file_write (const struct GNUNET_DISK_FileHandle * h,
965                         const void *buffer, size_t n)
966 {
967   if (h == NULL)
968   {
969     errno = EINVAL;
970     return GNUNET_SYSERR;
971   }
972
973 #ifdef MINGW
974   DWORD bytesWritten;
975
976   if (h->type != GNUNET_DISK_HANLDE_TYPE_PIPE)
977   {
978     if (!WriteFile (h->h, buffer, n, &bytesWritten, NULL))
979     {
980       SetErrnoFromWinError (GetLastError ());
981       return GNUNET_SYSERR;
982     }
983   }
984   else
985   {
986     LOG (GNUNET_ERROR_TYPE_DEBUG, "It is a pipe trying to write %u bytes\n", n);
987     if (!WriteFile (h->h, buffer, n, &bytesWritten, h->oOverlapWrite))
988     {
989       if (GetLastError () != ERROR_IO_PENDING)
990       {
991         SetErrnoFromWinError (GetLastError ());
992         LOG (GNUNET_ERROR_TYPE_DEBUG, "Error writing to pipe: %u\n",
993             GetLastError ());
994         return GNUNET_SYSERR;
995       }
996       LOG (GNUNET_ERROR_TYPE_DEBUG, "Will get overlapped result\n");
997       if (!GetOverlappedResult (h->h, h->oOverlapWrite, &bytesWritten, TRUE))
998       {
999         SetErrnoFromWinError (GetLastError ());
1000         LOG (GNUNET_ERROR_TYPE_DEBUG,
1001             "Error getting overlapped result while writing to pipe: %u\n",
1002             GetLastError ());
1003         return GNUNET_SYSERR;
1004       }
1005     }
1006     else
1007     {
1008       DWORD ovr;
1009       if (!GetOverlappedResult (h->h, h->oOverlapWrite, &ovr, TRUE))
1010       {
1011         LOG (GNUNET_ERROR_TYPE_DEBUG,
1012             "Error getting control overlapped result while writing to pipe: %u\n",
1013             GetLastError ());
1014       }
1015       else
1016       {
1017         LOG (GNUNET_ERROR_TYPE_DEBUG,
1018             "Wrote %u bytes (ovr says %u), picking the greatest\n",
1019             bytesWritten, ovr);
1020       }
1021     }
1022     if (bytesWritten == 0)
1023     {
1024       if (n > 0)
1025       {
1026         LOG (GNUNET_ERROR_TYPE_DEBUG, "Wrote %u bytes, returning -1 with EAGAIN\n", bytesWritten);
1027         errno = EAGAIN;
1028         return GNUNET_SYSERR;
1029       }
1030     }
1031     LOG (GNUNET_ERROR_TYPE_DEBUG, "Wrote %u bytes\n", bytesWritten);
1032   }
1033   return bytesWritten;
1034 #else
1035   return write (h->fd, buffer, n);
1036 #endif
1037 }
1038
1039
1040 /**
1041  * Write a buffer to a file, blocking, if necessary.
1042  * @param h handle to open file
1043  * @param buffer the data to write
1044  * @param n number of bytes to write
1045  * @return number of bytes written on success, GNUNET_SYSERR on error
1046  */
1047 ssize_t
1048 GNUNET_DISK_file_write_blocking (const struct GNUNET_DISK_FileHandle * h,
1049     const void *buffer, size_t n)
1050 {
1051   if (h == NULL)
1052   {
1053     errno = EINVAL;
1054     return GNUNET_SYSERR;
1055   }
1056
1057 #ifdef MINGW
1058   DWORD bytesWritten;
1059   /* We do a non-overlapped write, which is as blocking as it gets */
1060   LOG (GNUNET_ERROR_TYPE_DEBUG, "Writing %u bytes\n", n);
1061   if (!WriteFile (h->h, buffer, n, &bytesWritten, NULL))
1062   {
1063     SetErrnoFromWinError (GetLastError ());
1064     LOG (GNUNET_ERROR_TYPE_DEBUG, "Error writing to pipe: %u\n",
1065         GetLastError ());
1066     return GNUNET_SYSERR;
1067   }
1068   if (bytesWritten == 0 && n > 0)
1069   {
1070     LOG (GNUNET_ERROR_TYPE_DEBUG, "Waiting for pipe to clean\n");
1071     WaitForSingleObject (h->h, INFINITE);
1072     if (!WriteFile (h->h, buffer, n, &bytesWritten, NULL))
1073     {
1074       SetErrnoFromWinError (GetLastError ());
1075       LOG (GNUNET_ERROR_TYPE_DEBUG, "Error writing to pipe: %u\n",
1076           GetLastError ());
1077       return GNUNET_SYSERR;
1078     }
1079   }
1080   LOG (GNUNET_ERROR_TYPE_DEBUG, "Wrote %u bytes\n", bytesWritten);
1081   return bytesWritten;
1082 #else
1083   int flags;
1084   ssize_t ret;
1085
1086   /* set to blocking, write, then set back */
1087   flags = fcntl (h->fd, F_GETFL);
1088   if (0 != (flags & O_NONBLOCK))
1089     (void) fcntl (h->fd, F_SETFL, flags - O_NONBLOCK);
1090   ret = write (h->fd, buffer, n);
1091   if (0 == (flags & O_NONBLOCK))
1092     (void) fcntl (h->fd, F_SETFL, flags);
1093   return ret;
1094 #endif
1095 }
1096
1097
1098 /**
1099  * Write a buffer to a file.  If the file is longer than the
1100  * number of bytes that will be written, it will be truncated.
1101  *
1102  * @param fn file name
1103  * @param buffer the data to write
1104  * @param n number of bytes to write
1105  * @param mode file permissions
1106  * @return number of bytes written on success, GNUNET_SYSERR on error
1107  */
1108 ssize_t
1109 GNUNET_DISK_fn_write (const char *fn, const void *buffer, size_t n,
1110                       enum GNUNET_DISK_AccessPermissions mode)
1111 {
1112   struct GNUNET_DISK_FileHandle *fh;
1113   ssize_t ret;
1114
1115   fh = GNUNET_DISK_file_open (fn,
1116                               GNUNET_DISK_OPEN_WRITE | GNUNET_DISK_OPEN_TRUNCATE
1117                               | GNUNET_DISK_OPEN_CREATE, mode);
1118   if (!fh)
1119     return GNUNET_SYSERR;
1120   ret = GNUNET_DISK_file_write (fh, buffer, n);
1121   GNUNET_assert (GNUNET_OK == GNUNET_DISK_file_close (fh));
1122   return ret;
1123 }
1124
1125
1126 /**
1127  * Scan a directory for files.
1128  *
1129  * @param dirName the name of the directory
1130  * @param callback the method to call for each file,
1131  *        can be NULL, in that case, we only count
1132  * @param callback_cls closure for callback
1133  * @return the number of files found, GNUNET_SYSERR on error or
1134  *         ieration aborted by callback returning GNUNET_SYSERR
1135  */
1136 int
1137 GNUNET_DISK_directory_scan (const char *dirName,
1138                             GNUNET_FileNameCallback callback,
1139                             void *callback_cls)
1140 {
1141   DIR *dinfo;
1142   struct dirent *finfo;
1143   struct stat istat;
1144   int count = 0;
1145   char *name;
1146   char *dname;
1147   unsigned int name_len;
1148   unsigned int n_size;
1149
1150   GNUNET_assert (dirName != NULL);
1151   dname = GNUNET_STRINGS_filename_expand (dirName);
1152   if (dname == NULL)
1153     return GNUNET_SYSERR;
1154   while ((strlen (dname) > 0) && (dname[strlen (dname) - 1] == DIR_SEPARATOR))
1155     dname[strlen (dname) - 1] = '\0';
1156   if (0 != STAT (dname, &istat))
1157   {
1158     LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "stat", dname);
1159     GNUNET_free (dname);
1160     return GNUNET_SYSERR;
1161   }
1162   if (!S_ISDIR (istat.st_mode))
1163   {
1164     LOG (GNUNET_ERROR_TYPE_WARNING, _("Expected `%s' to be a directory!\n"),
1165          dirName);
1166     GNUNET_free (dname);
1167     return GNUNET_SYSERR;
1168   }
1169   errno = 0;
1170   dinfo = OPENDIR (dname);
1171   if ((errno == EACCES) || (dinfo == NULL))
1172   {
1173     LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "opendir", dname);
1174     if (dinfo != NULL)
1175       CLOSEDIR (dinfo);
1176     GNUNET_free (dname);
1177     return GNUNET_SYSERR;
1178   }
1179   name_len = 256;
1180   n_size = strlen (dname) + name_len + 2;
1181   name = GNUNET_malloc (n_size);
1182   while ((finfo = READDIR (dinfo)) != NULL)
1183   {
1184     if ((0 == strcmp (finfo->d_name, ".")) ||
1185         (0 == strcmp (finfo->d_name, "..")))
1186       continue;
1187     if (callback != NULL)
1188     {
1189       if (name_len < strlen (finfo->d_name))
1190       {
1191         GNUNET_free (name);
1192         name_len = strlen (finfo->d_name);
1193         n_size = strlen (dname) + name_len + 2;
1194         name = GNUNET_malloc (n_size);
1195       }
1196       /* dname can end in "/" only if dname == "/";
1197        * if dname does not end in "/", we need to add
1198        * a "/" (otherwise, we must not!) */
1199       GNUNET_snprintf (name, n_size, "%s%s%s", dname,
1200                        (strcmp (dname, DIR_SEPARATOR_STR) ==
1201                         0) ? "" : DIR_SEPARATOR_STR, finfo->d_name);
1202       if (GNUNET_OK != callback (callback_cls, name))
1203       {
1204         CLOSEDIR (dinfo);
1205         GNUNET_free (name);
1206         GNUNET_free (dname);
1207         return GNUNET_SYSERR;
1208       }
1209     }
1210     count++;
1211   }
1212   CLOSEDIR (dinfo);
1213   GNUNET_free (name);
1214   GNUNET_free (dname);
1215   return count;
1216 }
1217
1218
1219 /**
1220  * Opaque handle used for iterating over a directory.
1221  */
1222 struct GNUNET_DISK_DirectoryIterator
1223 {
1224
1225   /**
1226    * Function to call on directory entries.
1227    */
1228   GNUNET_DISK_DirectoryIteratorCallback callback;
1229
1230   /**
1231    * Closure for callback.
1232    */
1233   void *callback_cls;
1234
1235   /**
1236    * Reference to directory.
1237    */
1238   DIR *directory;
1239
1240   /**
1241    * Directory name.
1242    */
1243   char *dirname;
1244
1245   /**
1246    * Next filename to process.
1247    */
1248   char *next_name;
1249
1250   /**
1251    * Our priority.
1252    */
1253   enum GNUNET_SCHEDULER_Priority priority;
1254
1255 };
1256
1257
1258 /**
1259  * Task used by the directory iterator.
1260  */
1261 static void
1262 directory_iterator_task (void *cls,
1263                          const struct GNUNET_SCHEDULER_TaskContext *tc)
1264 {
1265   struct GNUNET_DISK_DirectoryIterator *iter = cls;
1266   char *name;
1267
1268   name = iter->next_name;
1269   GNUNET_assert (name != NULL);
1270   iter->next_name = NULL;
1271   iter->callback (iter->callback_cls, iter, name, iter->dirname);
1272   GNUNET_free (name);
1273 }
1274
1275
1276 /**
1277  * This function must be called during the DiskIteratorCallback
1278  * (exactly once) to schedule the task to process the next
1279  * filename in the directory (if there is one).
1280  *
1281  * @param iter opaque handle for the iterator
1282  * @param can set to GNUNET_YES to terminate the iteration early
1283  * @return GNUNET_YES if iteration will continue,
1284  *         GNUNET_NO if this was the last entry (and iteration is complete),
1285  *         GNUNET_SYSERR if abort was YES
1286  */
1287 int
1288 GNUNET_DISK_directory_iterator_next (struct GNUNET_DISK_DirectoryIterator *iter,
1289                                      int can)
1290 {
1291   struct dirent *finfo;
1292
1293   GNUNET_assert (iter->next_name == NULL);
1294   if (can == GNUNET_YES)
1295   {
1296     CLOSEDIR (iter->directory);
1297     GNUNET_free (iter->dirname);
1298     GNUNET_free (iter);
1299     return GNUNET_SYSERR;
1300   }
1301   while (NULL != (finfo = READDIR (iter->directory)))
1302   {
1303     if ((0 == strcmp (finfo->d_name, ".")) ||
1304         (0 == strcmp (finfo->d_name, "..")))
1305       continue;
1306     GNUNET_asprintf (&iter->next_name, "%s%s%s", iter->dirname,
1307                      DIR_SEPARATOR_STR, finfo->d_name);
1308     break;
1309   }
1310   if (finfo == NULL)
1311   {
1312     GNUNET_DISK_directory_iterator_next (iter, GNUNET_YES);
1313     return GNUNET_NO;
1314   }
1315   GNUNET_SCHEDULER_add_with_priority (iter->priority, &directory_iterator_task,
1316                                       iter);
1317   return GNUNET_YES;
1318 }
1319
1320
1321 /**
1322  * Scan a directory for files using the scheduler to run a task for
1323  * each entry.  The name of the directory must be expanded first (!).
1324  * If a scheduler does not need to be used, GNUNET_DISK_directory_scan
1325  * may provide a simpler API.
1326  *
1327  * @param prio priority to use
1328  * @param dirName the name of the directory
1329  * @param callback the method to call for each file
1330  * @param callback_cls closure for callback
1331  * @return GNUNET_YES if directory is not empty and 'callback'
1332  *         will be called later, GNUNET_NO otherwise, GNUNET_SYSERR on error.
1333  */
1334 int
1335 GNUNET_DISK_directory_iterator_start (enum GNUNET_SCHEDULER_Priority prio,
1336                                       const char *dirName,
1337                                       GNUNET_DISK_DirectoryIteratorCallback
1338                                       callback, void *callback_cls)
1339 {
1340   struct GNUNET_DISK_DirectoryIterator *di;
1341
1342   di = GNUNET_malloc (sizeof (struct GNUNET_DISK_DirectoryIterator));
1343   di->callback = callback;
1344   di->callback_cls = callback_cls;
1345   di->directory = OPENDIR (dirName);
1346   if (di->directory == NULL)
1347   {
1348     GNUNET_free (di);
1349     callback (callback_cls, NULL, NULL, NULL);
1350     return GNUNET_SYSERR;
1351   }
1352   di->dirname = GNUNET_strdup (dirName);
1353   di->priority = prio;
1354   return GNUNET_DISK_directory_iterator_next (di, GNUNET_NO);
1355 }
1356
1357
1358 /**
1359  * Function that removes the given directory by calling
1360  * "GNUNET_DISK_directory_remove".
1361  *
1362  * @param unused not used
1363  * @param fn directory to remove
1364  * @return GNUNET_OK
1365  */
1366 static int
1367 remove_helper (void *unused, const char *fn)
1368 {
1369   (void) GNUNET_DISK_directory_remove (fn);
1370   return GNUNET_OK;
1371 }
1372
1373
1374 /**
1375  * Remove all files in a directory (rm -rf). Call with
1376  * caution.
1377  *
1378  *
1379  * @param filename the file to remove
1380  * @return GNUNET_OK on success, GNUNET_SYSERR on error
1381  */
1382 int
1383 GNUNET_DISK_directory_remove (const char *filename)
1384 {
1385   struct stat istat;
1386
1387   if (0 != LSTAT (filename, &istat))
1388     return GNUNET_NO;           /* file may not exist... */
1389   (void) CHMOD (filename, S_IWUSR | S_IRUSR | S_IXUSR);
1390   if (UNLINK (filename) == 0)
1391     return GNUNET_OK;
1392   if ((errno != EISDIR) &&
1393       /* EISDIR is not sufficient in all cases, e.g.
1394        * sticky /tmp directory may result in EPERM on BSD.
1395        * So we also explicitly check "isDirectory" */
1396       (GNUNET_YES != GNUNET_DISK_directory_test (filename)))
1397   {
1398     LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "rmdir", filename);
1399     return GNUNET_SYSERR;
1400   }
1401   if (GNUNET_SYSERR ==
1402       GNUNET_DISK_directory_scan (filename, &remove_helper, NULL))
1403     return GNUNET_SYSERR;
1404   if (0 != RMDIR (filename))
1405   {
1406     LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "rmdir", filename);
1407     return GNUNET_SYSERR;
1408   }
1409   return GNUNET_OK;
1410 }
1411
1412
1413 /**
1414  * Copy a file.
1415  *
1416  * @param src file to copy
1417  * @param dst destination file name
1418  * @return GNUNET_OK on success, GNUNET_SYSERR on error
1419  */
1420 int
1421 GNUNET_DISK_file_copy (const char *src, const char *dst)
1422 {
1423   char *buf;
1424   uint64_t pos;
1425   uint64_t size;
1426   size_t len;
1427   struct GNUNET_DISK_FileHandle *in;
1428   struct GNUNET_DISK_FileHandle *out;
1429
1430   if (GNUNET_OK != GNUNET_DISK_file_size (src, &size, GNUNET_YES, GNUNET_YES))
1431     return GNUNET_SYSERR;
1432   pos = 0;
1433   in = GNUNET_DISK_file_open (src, GNUNET_DISK_OPEN_READ,
1434                               GNUNET_DISK_PERM_NONE);
1435   if (!in)
1436     return GNUNET_SYSERR;
1437   out =
1438       GNUNET_DISK_file_open (dst,
1439                              GNUNET_DISK_OPEN_WRITE | GNUNET_DISK_OPEN_CREATE |
1440                              GNUNET_DISK_OPEN_FAILIFEXISTS,
1441                              GNUNET_DISK_PERM_USER_READ |
1442                              GNUNET_DISK_PERM_USER_WRITE |
1443                              GNUNET_DISK_PERM_GROUP_READ |
1444                              GNUNET_DISK_PERM_GROUP_WRITE);
1445   if (!out)
1446   {
1447     GNUNET_DISK_file_close (in);
1448     return GNUNET_SYSERR;
1449   }
1450   buf = GNUNET_malloc (COPY_BLK_SIZE);
1451   while (pos < size)
1452   {
1453     len = COPY_BLK_SIZE;
1454     if (len > size - pos)
1455       len = size - pos;
1456     if (len != GNUNET_DISK_file_read (in, buf, len))
1457       goto FAIL;
1458     if (len != GNUNET_DISK_file_write (out, buf, len))
1459       goto FAIL;
1460     pos += len;
1461   }
1462   GNUNET_free (buf);
1463   GNUNET_DISK_file_close (in);
1464   GNUNET_DISK_file_close (out);
1465   return GNUNET_OK;
1466 FAIL:
1467   GNUNET_free (buf);
1468   GNUNET_DISK_file_close (in);
1469   GNUNET_DISK_file_close (out);
1470   return GNUNET_SYSERR;
1471 }
1472
1473
1474 /**
1475  * @brief Removes special characters as ':' from a filename.
1476  * @param fn the filename to canonicalize
1477  */
1478 void
1479 GNUNET_DISK_filename_canonicalize (char *fn)
1480 {
1481   char *idx;
1482   char c;
1483
1484   idx = fn;
1485   while (*idx)
1486   {
1487     c = *idx;
1488
1489     if (c == '/' || c == '\\' || c == ':' || c == '*' || c == '?' || c == '"' ||
1490         c == '<' || c == '>' || c == '|')
1491     {
1492       *idx = '_';
1493     }
1494
1495     idx++;
1496   }
1497 }
1498
1499
1500
1501 /**
1502  * @brief Change owner of a file
1503  *
1504  * @param filename name of file to change the owner of
1505  * @param user name of the new owner
1506  * @return GNUNET_OK on success, GNUNET_SYSERR on failure
1507  */
1508 int
1509 GNUNET_DISK_file_change_owner (const char *filename, const char *user)
1510 {
1511 #ifndef MINGW
1512   struct passwd *pws;
1513
1514   pws = getpwnam (user);
1515   if (pws == NULL)
1516   {
1517     LOG (GNUNET_ERROR_TYPE_ERROR,
1518          _("Cannot obtain information about user `%s': %s\n"), user,
1519          STRERROR (errno));
1520     return GNUNET_SYSERR;
1521   }
1522   if (0 != chown (filename, pws->pw_uid, pws->pw_gid))
1523     LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "chown", filename);
1524 #endif
1525   return GNUNET_OK;
1526 }
1527
1528
1529 /**
1530  * Lock a part of a file
1531  * @param fh file handle
1532  * @param lockStart absolute position from where to lock
1533  * @param lockEnd absolute position until where to lock
1534  * @param excl GNUNET_YES for an exclusive lock
1535  * @return GNUNET_OK on success, GNUNET_SYSERR on error
1536  */
1537 int
1538 GNUNET_DISK_file_lock (struct GNUNET_DISK_FileHandle *fh, OFF_T lockStart,
1539                        OFF_T lockEnd, int excl)
1540 {
1541   if (fh == NULL)
1542   {
1543     errno = EINVAL;
1544     return GNUNET_SYSERR;
1545   }
1546
1547 #ifndef MINGW
1548   struct flock fl;
1549
1550   memset (&fl, 0, sizeof (struct flock));
1551   fl.l_type = excl ? F_WRLCK : F_RDLCK;
1552   fl.l_whence = SEEK_SET;
1553   fl.l_start = lockStart;
1554   fl.l_len = lockEnd;
1555
1556   return fcntl (fh->fd, F_SETLK, &fl) != 0 ? GNUNET_SYSERR : GNUNET_OK;
1557 #else
1558   OVERLAPPED o;
1559   OFF_T diff = lockEnd - lockStart;
1560   DWORD diff_low, diff_high;
1561   diff_low = (DWORD) (diff & 0xFFFFFFFF);
1562   diff_high = (DWORD) ((diff >> (sizeof (DWORD) * 8)) & 0xFFFFFFFF);
1563
1564   memset (&o, 0, sizeof (OVERLAPPED));
1565   o.Offset = (DWORD) (lockStart & 0xFFFFFFFF);;
1566   o.OffsetHigh = (DWORD) (((lockStart & ~0xFFFFFFFF) >> (sizeof (DWORD) * 8)) & 0xFFFFFFFF);
1567
1568   if (!LockFileEx
1569       (fh->h, (excl ? LOCKFILE_EXCLUSIVE_LOCK : 0) | LOCKFILE_FAIL_IMMEDIATELY,
1570        0, diff_low, diff_high, &o))
1571   {
1572     SetErrnoFromWinError (GetLastError ());
1573     return GNUNET_SYSERR;
1574   }
1575
1576   return GNUNET_OK;
1577 #endif
1578 }
1579
1580
1581 /**
1582  * Unlock a part of a file
1583  * @param fh file handle
1584  * @param unlockStart absolute position from where to unlock
1585  * @param unlockEnd absolute position until where to unlock
1586  * @return GNUNET_OK on success, GNUNET_SYSERR on error
1587  */
1588 int
1589 GNUNET_DISK_file_unlock (struct GNUNET_DISK_FileHandle *fh, OFF_T unlockStart,
1590                          OFF_T unlockEnd)
1591 {
1592   if (fh == NULL)
1593   {
1594     errno = EINVAL;
1595     return GNUNET_SYSERR;
1596   }
1597
1598 #ifndef MINGW
1599   struct flock fl;
1600
1601   memset (&fl, 0, sizeof (struct flock));
1602   fl.l_type = F_UNLCK;
1603   fl.l_whence = SEEK_SET;
1604   fl.l_start = unlockStart;
1605   fl.l_len = unlockEnd;
1606
1607   return fcntl (fh->fd, F_SETLK, &fl) != 0 ? GNUNET_SYSERR : GNUNET_OK;
1608 #else
1609   OVERLAPPED o;
1610   OFF_T diff = unlockEnd - unlockStart;
1611   DWORD diff_low, diff_high;
1612   diff_low = (DWORD) (diff & 0xFFFFFFFF);
1613   diff_high = (DWORD) ((diff >> (sizeof (DWORD) * 8)) & 0xFFFFFFFF);
1614
1615   memset (&o, 0, sizeof (OVERLAPPED));
1616   o.Offset = (DWORD) (unlockStart & 0xFFFFFFFF);;
1617   o.OffsetHigh = (DWORD) (((unlockStart & ~0xFFFFFFFF) >> (sizeof (DWORD) * 8)) & 0xFFFFFFFF);
1618
1619   if (!UnlockFileEx (fh->h, 0, diff_low, diff_high, &o))
1620   {
1621     SetErrnoFromWinError (GetLastError ());
1622     return GNUNET_SYSERR;
1623   }
1624
1625   return GNUNET_OK;
1626 #endif
1627 }
1628
1629
1630 /**
1631  * Open a file.  Note that the access permissions will only be
1632  * used if a new file is created and if the underlying operating
1633  * system supports the given permissions.
1634  *
1635  * @param fn file name to be opened
1636  * @param flags opening flags, a combination of GNUNET_DISK_OPEN_xxx bit flags
1637  * @param perm permissions for the newly created file, use
1638  *             GNUNET_DISK_PERM_USER_NONE if a file could not be created by this
1639  *             call (because of flags)
1640  * @return IO handle on success, NULL on error
1641  */
1642 struct GNUNET_DISK_FileHandle *
1643 GNUNET_DISK_file_open (const char *fn, enum GNUNET_DISK_OpenFlags flags,
1644                        enum GNUNET_DISK_AccessPermissions perm)
1645 {
1646   char *expfn;
1647   struct GNUNET_DISK_FileHandle *ret;
1648
1649 #ifdef MINGW
1650   DWORD access;
1651   DWORD disp;
1652   HANDLE h;
1653   wchar_t wexpfn[MAX_PATH + 1];
1654 #else
1655   int oflags;
1656   int mode;
1657   int fd;
1658 #endif
1659
1660   expfn = GNUNET_STRINGS_filename_expand (fn);
1661   if (NULL == expfn)
1662     return NULL;
1663 #ifndef MINGW
1664   mode = 0;
1665   if (GNUNET_DISK_OPEN_READWRITE == (flags & GNUNET_DISK_OPEN_READWRITE))
1666     oflags = O_RDWR;            /* note: O_RDWR is NOT always O_RDONLY | O_WRONLY */
1667   else if (flags & GNUNET_DISK_OPEN_READ)
1668     oflags = O_RDONLY;
1669   else if (flags & GNUNET_DISK_OPEN_WRITE)
1670     oflags = O_WRONLY;
1671   else
1672   {
1673     GNUNET_break (0);
1674     GNUNET_free (expfn);
1675     return NULL;
1676   }
1677   if (flags & GNUNET_DISK_OPEN_FAILIFEXISTS)
1678     oflags |= (O_CREAT | O_EXCL);
1679   if (flags & GNUNET_DISK_OPEN_TRUNCATE)
1680     oflags |= O_TRUNC;
1681   if (flags & GNUNET_DISK_OPEN_APPEND)
1682     oflags |= O_APPEND;
1683   if (flags & GNUNET_DISK_OPEN_CREATE)
1684   {
1685     (void) GNUNET_DISK_directory_create_for_file (expfn);
1686     oflags |= O_CREAT;
1687     mode = translate_unix_perms (perm);
1688   }
1689
1690   fd = open (expfn, oflags | O_LARGEFILE, mode);
1691   if (fd == -1)
1692   {
1693     if (0 == (flags & GNUNET_DISK_OPEN_FAILIFEXISTS))
1694       LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "open", expfn);
1695     else
1696       LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_DEBUG, "open", expfn);
1697     GNUNET_free (expfn);
1698     return NULL;
1699   }
1700 #else
1701   access = 0;
1702   disp = OPEN_ALWAYS;
1703
1704   if (GNUNET_DISK_OPEN_READWRITE == (flags & GNUNET_DISK_OPEN_READWRITE))
1705     access = FILE_READ_DATA | FILE_WRITE_DATA;
1706   else if (flags & GNUNET_DISK_OPEN_READ)
1707     access = FILE_READ_DATA;
1708   else if (flags & GNUNET_DISK_OPEN_WRITE)
1709     access = FILE_WRITE_DATA;
1710
1711   if (flags & GNUNET_DISK_OPEN_FAILIFEXISTS)
1712   {
1713     disp = CREATE_NEW;
1714   }
1715   else if (flags & GNUNET_DISK_OPEN_CREATE)
1716   {
1717     (void) GNUNET_DISK_directory_create_for_file (expfn);
1718     if (flags & GNUNET_DISK_OPEN_TRUNCATE)
1719       disp = CREATE_ALWAYS;
1720     else
1721       disp = OPEN_ALWAYS;
1722   }
1723   else if (flags & GNUNET_DISK_OPEN_TRUNCATE)
1724   {
1725     disp = TRUNCATE_EXISTING;
1726   }
1727   else
1728   {
1729     disp = OPEN_EXISTING;
1730   }
1731
1732   if (ERROR_SUCCESS == plibc_conv_to_win_pathwconv(expfn, wexpfn))
1733     h = CreateFileW (wexpfn, access,
1734                     FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE, NULL,
1735                     disp, FILE_ATTRIBUTE_NORMAL, NULL);
1736   else
1737     h = INVALID_HANDLE_VALUE;
1738   if (h == INVALID_HANDLE_VALUE)
1739   {
1740     int err;
1741     SetErrnoFromWinError (GetLastError ());
1742     err = errno;
1743     LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_INFO, "open", expfn);
1744     GNUNET_free (expfn);
1745     errno = err;
1746     return NULL;
1747   }
1748
1749   if (flags & GNUNET_DISK_OPEN_APPEND)
1750     if (SetFilePointer (h, 0, 0, FILE_END) == INVALID_SET_FILE_POINTER)
1751     {
1752       SetErrnoFromWinError (GetLastError ());
1753       LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "SetFilePointer", expfn);
1754       CloseHandle (h);
1755       GNUNET_free (expfn);
1756       return NULL;
1757     }
1758 #endif
1759
1760   ret = GNUNET_malloc (sizeof (struct GNUNET_DISK_FileHandle));
1761 #ifdef MINGW
1762   ret->h = h;
1763   ret->type = GNUNET_DISK_HANLDE_TYPE_FILE;
1764 #else
1765   ret->fd = fd;
1766 #endif
1767   GNUNET_free (expfn);
1768   return ret;
1769 }
1770
1771
1772 /**
1773  * Close an open file
1774  * @param h file handle
1775  * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
1776  */
1777 int
1778 GNUNET_DISK_file_close (struct GNUNET_DISK_FileHandle *h)
1779 {
1780   if (h == NULL)
1781   {
1782     errno = EINVAL;
1783     return GNUNET_SYSERR;
1784   }
1785
1786 #if MINGW
1787   if (!CloseHandle (h->h))
1788   {
1789     SetErrnoFromWinError (GetLastError ());
1790     LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING, "close");
1791     GNUNET_free (h->oOverlapRead);
1792     GNUNET_free (h->oOverlapWrite);
1793     GNUNET_free (h);
1794     return GNUNET_SYSERR;
1795   }
1796 #else
1797   if (close (h->fd) != 0)
1798   {
1799     LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING, "close");
1800     GNUNET_free (h);
1801     return GNUNET_SYSERR;
1802   }
1803 #endif
1804   GNUNET_free (h);
1805   return GNUNET_OK;
1806 }
1807
1808
1809 /**
1810  * Get a handle from a native FD.
1811  *
1812  * @param fd native file descriptor
1813  * @return file handle corresponding to the descriptor
1814  */
1815 struct GNUNET_DISK_FileHandle *
1816 GNUNET_DISK_get_handle_from_native (FILE *fd)
1817 {
1818   struct GNUNET_DISK_FileHandle *fh;
1819   int fno;
1820 #if MINGW
1821   intptr_t osfh;
1822 #endif
1823
1824   fno = fileno (fd);
1825   if (-1 == fno)
1826     return NULL;
1827
1828 #if MINGW
1829   osfh = _get_osfhandle (fno);
1830   if (INVALID_HANDLE_VALUE == (HANDLE) osfh)
1831     return NULL;
1832 #endif
1833
1834   fh = GNUNET_malloc (sizeof (struct GNUNET_DISK_FileHandle));
1835
1836 #if MINGW
1837   fh->h = (HANDLE) osfh;
1838   /* Assume it to be a pipe. TODO: use some kind of detection
1839    * function to figure out handle type.
1840    * Note that we can't make it overlapped if it isn't already.
1841    * (ReOpenFile() is only available in 2003/Vista).
1842    * The process that opened this file in the first place (usually a parent
1843    * process, if this is stdin/stdout/stderr) must make it overlapped,
1844    * otherwise we're screwed, as selecting on non-overlapped handle
1845    * will block.
1846    */
1847   fh->type = GNUNET_DISK_HANLDE_TYPE_PIPE;
1848   fh->oOverlapRead = GNUNET_malloc (sizeof (OVERLAPPED));
1849   fh->oOverlapWrite = GNUNET_malloc (sizeof (OVERLAPPED));
1850   fh->oOverlapRead->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
1851   fh->oOverlapWrite->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
1852 #else
1853   fh->fd = fno;
1854 #endif
1855
1856   return fh;
1857 }
1858
1859
1860 /**
1861  * Construct full path to a file inside of the private
1862  * directory used by GNUnet.  Also creates the corresponding
1863  * directory.  If the resulting name is supposed to be
1864  * a directory, end the last argument in '/' (or pass
1865  * DIR_SEPARATOR_STR as the last argument before NULL).
1866  *
1867  * @param cfg configuration to use (determines HOME)
1868  * @param serviceName name of the service
1869  * @param ... is NULL-terminated list of
1870  *                path components to append to the
1871  *                private directory name.
1872  * @return the constructed filename
1873  */
1874 char *
1875 GNUNET_DISK_get_home_filename (const struct GNUNET_CONFIGURATION_Handle *cfg,
1876                                const char *serviceName, ...)
1877 {
1878   const char *c;
1879   char *pfx;
1880   char *ret;
1881   va_list ap;
1882   unsigned int needed;
1883
1884   if (GNUNET_OK !=
1885       GNUNET_CONFIGURATION_get_value_filename (cfg, serviceName, "HOME", &pfx))
1886     return NULL;
1887   if (pfx == NULL)
1888   {
1889     LOG (GNUNET_ERROR_TYPE_WARNING,
1890          _("No `%s' specified for service `%s' in configuration.\n"), "HOME",
1891          serviceName);
1892     return NULL;
1893   }
1894   needed = strlen (pfx) + 2;
1895   if ((pfx[strlen (pfx) - 1] != '/') && (pfx[strlen (pfx) - 1] != '\\'))
1896     needed++;
1897   va_start (ap, serviceName);
1898   while (1)
1899   {
1900     c = va_arg (ap, const char *);
1901
1902     if (c == NULL)
1903       break;
1904     needed += strlen (c);
1905     if ((c[strlen (c) - 1] != '/') && (c[strlen (c) - 1] != '\\'))
1906       needed++;
1907   }
1908   va_end (ap);
1909   ret = GNUNET_malloc (needed);
1910   strcpy (ret, pfx);
1911   GNUNET_free (pfx);
1912   va_start (ap, serviceName);
1913   while (1)
1914   {
1915     c = va_arg (ap, const char *);
1916
1917     if (c == NULL)
1918       break;
1919     if ((c[strlen (c) - 1] != '/') && (c[strlen (c) - 1] != '\\'))
1920       strcat (ret, DIR_SEPARATOR_STR);
1921     strcat (ret, c);
1922   }
1923   va_end (ap);
1924   if ((ret[strlen (ret) - 1] != '/') && (ret[strlen (ret) - 1] != '\\'))
1925     (void) GNUNET_DISK_directory_create_for_file (ret);
1926   else
1927     (void) GNUNET_DISK_directory_create (ret);
1928   return ret;
1929 }
1930
1931
1932 /**
1933  * Handle for a memory-mapping operation.
1934  */
1935 struct GNUNET_DISK_MapHandle
1936 {
1937   /**
1938    * Address where the map is in memory.
1939    */
1940   void *addr;
1941
1942 #ifdef MINGW
1943   /**
1944    * Underlying OS handle.
1945    */
1946   HANDLE h;
1947 #else
1948   /**
1949    * Number of bytes mapped.
1950    */
1951   size_t len;
1952 #endif
1953 };
1954
1955
1956 #ifndef MAP_FAILED
1957 #define MAP_FAILED ((void *) -1)
1958 #endif
1959
1960 /**
1961  * Map a file into memory
1962  *
1963  * @param h open file handle
1964  * @param m handle to the new mapping
1965  * @param access access specification, GNUNET_DISK_MAP_TYPE_xxx
1966  * @param len size of the mapping
1967  * @return pointer to the mapped memory region, NULL on failure
1968  */
1969 void *
1970 GNUNET_DISK_file_map (const struct GNUNET_DISK_FileHandle *h,
1971                       struct GNUNET_DISK_MapHandle **m,
1972                       enum GNUNET_DISK_MapType access, size_t len)
1973 {
1974   if (h == NULL)
1975   {
1976     errno = EINVAL;
1977     return NULL;
1978   }
1979
1980 #ifdef MINGW
1981   DWORD mapAccess, protect;
1982
1983   if ((access & GNUNET_DISK_MAP_TYPE_READ) &&
1984       (access & GNUNET_DISK_MAP_TYPE_WRITE))
1985   {
1986     protect = PAGE_READWRITE;
1987     mapAccess = FILE_MAP_ALL_ACCESS;
1988   }
1989   else if (access & GNUNET_DISK_MAP_TYPE_READ)
1990   {
1991     protect = PAGE_READONLY;
1992     mapAccess = FILE_MAP_READ;
1993   }
1994   else if (access & GNUNET_DISK_MAP_TYPE_WRITE)
1995   {
1996     protect = PAGE_READWRITE;
1997     mapAccess = FILE_MAP_WRITE;
1998   }
1999   else
2000   {
2001     GNUNET_break (0);
2002     return NULL;
2003   }
2004
2005   *m = GNUNET_malloc (sizeof (struct GNUNET_DISK_MapHandle));
2006   (*m)->h = CreateFileMapping (h->h, NULL, protect, 0, 0, NULL);
2007   if ((*m)->h == INVALID_HANDLE_VALUE)
2008   {
2009     SetErrnoFromWinError (GetLastError ());
2010     GNUNET_free (*m);
2011     return NULL;
2012   }
2013
2014   (*m)->addr = MapViewOfFile ((*m)->h, mapAccess, 0, 0, len);
2015   if (!(*m)->addr)
2016   {
2017     SetErrnoFromWinError (GetLastError ());
2018     CloseHandle ((*m)->h);
2019     GNUNET_free (*m);
2020   }
2021
2022   return (*m)->addr;
2023 #else
2024   int prot;
2025
2026   prot = 0;
2027   if (access & GNUNET_DISK_MAP_TYPE_READ)
2028     prot = PROT_READ;
2029   if (access & GNUNET_DISK_MAP_TYPE_WRITE)
2030     prot |= PROT_WRITE;
2031   *m = GNUNET_malloc (sizeof (struct GNUNET_DISK_MapHandle));
2032   (*m)->addr = mmap (NULL, len, prot, MAP_SHARED, h->fd, 0);
2033   GNUNET_assert (NULL != (*m)->addr);
2034   if (MAP_FAILED == (*m)->addr)
2035   {
2036     GNUNET_free (*m);
2037     return NULL;
2038   }
2039   (*m)->len = len;
2040   return (*m)->addr;
2041 #endif
2042 }
2043
2044 /**
2045  * Unmap a file
2046  * @param h mapping handle
2047  * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
2048  */
2049 int
2050 GNUNET_DISK_file_unmap (struct GNUNET_DISK_MapHandle *h)
2051 {
2052   int ret;
2053
2054   if (h == NULL)
2055   {
2056     errno = EINVAL;
2057     return GNUNET_SYSERR;
2058   }
2059
2060 #ifdef MINGW
2061   ret = UnmapViewOfFile (h->addr) ? GNUNET_OK : GNUNET_SYSERR;
2062   if (ret != GNUNET_OK)
2063     SetErrnoFromWinError (GetLastError ());
2064   if (!CloseHandle (h->h) && (ret == GNUNET_OK))
2065   {
2066     ret = GNUNET_SYSERR;
2067     SetErrnoFromWinError (GetLastError ());
2068   }
2069 #else
2070   ret = munmap (h->addr, h->len) != -1 ? GNUNET_OK : GNUNET_SYSERR;
2071 #endif
2072   GNUNET_free (h);
2073   return ret;
2074 }
2075
2076
2077 /**
2078  * Write file changes to disk
2079  * @param h handle to an open file
2080  * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
2081  */
2082 int
2083 GNUNET_DISK_file_sync (const struct GNUNET_DISK_FileHandle *h)
2084 {
2085   if (h == NULL)
2086   {
2087     errno = EINVAL;
2088     return GNUNET_SYSERR;
2089   }
2090
2091 #ifdef MINGW
2092   int ret;
2093
2094   ret = FlushFileBuffers (h->h) ? GNUNET_OK : GNUNET_SYSERR;
2095   if (ret != GNUNET_OK)
2096     SetErrnoFromWinError (GetLastError ());
2097   return ret;
2098 #elif defined(FREEBSD) || defined(OPENBSD) || defined(DARWIN)
2099   return fsync (h->fd) == -1 ? GNUNET_SYSERR : GNUNET_OK;
2100 #else
2101   return fdatasync (h->fd) == -1 ? GNUNET_SYSERR : GNUNET_OK;
2102 #endif
2103 }
2104
2105
2106 #if WINDOWS
2107 /* Copyright Bob Byrnes  <byrnes <at> curl.com>
2108    http://permalink.gmane.org/gmane.os.cygwin.patches/2121
2109 */
2110 /* Create a pipe, and return handles to the read and write ends,
2111    just like CreatePipe, but ensure that the write end permits
2112    FILE_READ_ATTRIBUTES access, on later versions of win32 where
2113    this is supported.  This access is needed by NtQueryInformationFile,
2114    which is used to implement select and nonblocking writes.
2115    Note that the return value is either NO_ERROR or GetLastError,
2116    unlike CreatePipe, which returns a bool for success or failure.  */
2117 static int
2118 create_selectable_pipe (PHANDLE read_pipe_ptr, PHANDLE write_pipe_ptr,
2119                         LPSECURITY_ATTRIBUTES sa_ptr, DWORD psize,
2120                         DWORD dwReadMode, DWORD dwWriteMode)
2121 {
2122   /* Default to error. */
2123   *read_pipe_ptr = *write_pipe_ptr = INVALID_HANDLE_VALUE;
2124
2125   HANDLE read_pipe = INVALID_HANDLE_VALUE, write_pipe = INVALID_HANDLE_VALUE;
2126
2127   /* Ensure that there is enough pipe buffer space for atomic writes.  */
2128   if (psize < PIPE_BUF)
2129     psize = PIPE_BUF;
2130
2131   char pipename[MAX_PATH];
2132
2133   /* Retry CreateNamedPipe as long as the pipe name is in use.
2134    * Retrying will probably never be necessary, but we want
2135    * to be as robust as possible.  */
2136   while (1)
2137   {
2138     static volatile LONG pipe_unique_id;
2139
2140     snprintf (pipename, sizeof pipename, "\\\\.\\pipe\\gnunet-%d-%ld",
2141               getpid (), InterlockedIncrement ((LONG *) & pipe_unique_id));
2142     LOG (GNUNET_ERROR_TYPE_DEBUG, "CreateNamedPipe: name = %s, size = %lu\n",
2143          pipename, psize);
2144     /* Use CreateNamedPipe instead of CreatePipe, because the latter
2145      * returns a write handle that does not permit FILE_READ_ATTRIBUTES
2146      * access, on versions of win32 earlier than WinXP SP2.
2147      * CreatePipe also stupidly creates a full duplex pipe, which is
2148      * a waste, since only a single direction is actually used.
2149      * It's important to only allow a single instance, to ensure that
2150      * the pipe was not created earlier by some other process, even if
2151      * the pid has been reused.  We avoid FILE_FLAG_FIRST_PIPE_INSTANCE
2152      * because that is only available for Win2k SP2 and WinXP.  */
2153     read_pipe = CreateNamedPipeA (pipename, PIPE_ACCESS_INBOUND | dwReadMode, PIPE_TYPE_BYTE | PIPE_READMODE_BYTE, 1,   /* max instances */
2154                                   psize,        /* output buffer size */
2155                                   psize,        /* input buffer size */
2156                                   NMPWAIT_USE_DEFAULT_WAIT, sa_ptr);
2157
2158     if (read_pipe != INVALID_HANDLE_VALUE)
2159     {
2160       LOG (GNUNET_ERROR_TYPE_DEBUG, "pipe read handle = %p\n", read_pipe);
2161       break;
2162     }
2163
2164     DWORD err = GetLastError ();
2165
2166     switch (err)
2167     {
2168     case ERROR_PIPE_BUSY:
2169       /* The pipe is already open with compatible parameters.
2170        * Pick a new name and retry.  */
2171       LOG (GNUNET_ERROR_TYPE_DEBUG, "pipe busy, retrying\n");
2172       continue;
2173     case ERROR_ACCESS_DENIED:
2174       /* The pipe is already open with incompatible parameters.
2175        * Pick a new name and retry.  */
2176       LOG (GNUNET_ERROR_TYPE_DEBUG, "pipe access denied, retrying\n");
2177       continue;
2178     case ERROR_CALL_NOT_IMPLEMENTED:
2179       /* We are on an older Win9x platform without named pipes.
2180        * Return an anonymous pipe as the best approximation.  */
2181       LOG (GNUNET_ERROR_TYPE_DEBUG,
2182            "CreateNamedPipe not implemented, resorting to "
2183            "CreatePipe: size = %lu\n", psize);
2184       if (CreatePipe (read_pipe_ptr, write_pipe_ptr, sa_ptr, psize))
2185       {
2186         LOG (GNUNET_ERROR_TYPE_DEBUG, "pipe read handle = %p, write handle = %p\n",
2187              *read_pipe_ptr,
2188              *write_pipe_ptr);
2189         return GNUNET_OK;
2190       }
2191       err = GetLastError ();
2192       LOG (GNUNET_ERROR_TYPE_ERROR, "CreatePipe failed: %d\n", err);
2193       return err;
2194     default:
2195       LOG (GNUNET_ERROR_TYPE_ERROR, "CreateNamedPipe failed: %d\n", err);
2196       return err;
2197     }
2198     /* NOTREACHED */
2199   }
2200   LOG (GNUNET_ERROR_TYPE_DEBUG, "CreateFile: name = %s\n", pipename);
2201
2202   /* Open the named pipe for writing.
2203    * Be sure to permit FILE_READ_ATTRIBUTES access.  */
2204   write_pipe = CreateFileA (pipename, GENERIC_WRITE | FILE_READ_ATTRIBUTES, 0,  /* share mode */
2205                             sa_ptr, OPEN_EXISTING, dwWriteMode, /* flags and attributes */
2206                             0); /* handle to template file */
2207
2208   if (write_pipe == INVALID_HANDLE_VALUE)
2209   {
2210     /* Failure. */
2211     DWORD err = GetLastError ();
2212
2213     LOG (GNUNET_ERROR_TYPE_DEBUG, "CreateFile failed: %d\n", err);
2214     CloseHandle (read_pipe);
2215     return err;
2216   }
2217   LOG (GNUNET_ERROR_TYPE_DEBUG, "pipe write handle = %p\n", write_pipe);
2218   /* Success. */
2219   *read_pipe_ptr = read_pipe;
2220   *write_pipe_ptr = write_pipe;
2221   return GNUNET_OK;
2222 }
2223 #endif
2224
2225
2226 /**
2227  * Creates an interprocess channel
2228  *
2229  * @param blocking_read creates an asynchronous pipe for reading if set to GNUNET_NO
2230  * @param blocking_write creates an asynchronous pipe for writing if set to GNUNET_NO
2231  * @param inherit_read inherit the parent processes stdin (only for windows)
2232  * @param inherit_write inherit the parent processes stdout (only for windows)
2233  * @return handle to the new pipe, NULL on error
2234  */
2235 struct GNUNET_DISK_PipeHandle *
2236 GNUNET_DISK_pipe (int blocking_read, int blocking_write, int inherit_read, int inherit_write)
2237 {
2238 #ifndef MINGW
2239   int fd[2];
2240   int ret;
2241   int eno;
2242
2243   ret = pipe (fd);
2244   if (ret == -1)
2245   {
2246     eno = errno;
2247     LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR, "pipe");
2248     errno = eno;
2249     return NULL;
2250   }
2251   return GNUNET_DISK_pipe_from_fd (blocking_read,
2252                                    blocking_write,
2253                                    fd);
2254 #else
2255   struct GNUNET_DISK_PipeHandle *p;
2256   struct GNUNET_DISK_FileHandle *fds;
2257   BOOL ret;
2258   HANDLE tmp_handle;
2259   
2260
2261   p = GNUNET_malloc (sizeof (struct GNUNET_DISK_PipeHandle) +
2262                      2 * sizeof (struct GNUNET_DISK_FileHandle));
2263   fds = (struct GNUNET_DISK_FileHandle *) &p[1];
2264   p->fd[0] = &fds[0];
2265   p->fd[1] = &fds[1];
2266
2267   /* All pipes are overlapped. If you want them to block - just
2268    * call WriteFile() and ReadFile() with NULL overlapped pointer.
2269    */
2270   ret =
2271       create_selectable_pipe (&p->fd[0]->h, &p->fd[1]->h, NULL, 0,
2272                               FILE_FLAG_OVERLAPPED,
2273                               FILE_FLAG_OVERLAPPED);
2274   if (!ret)
2275   {
2276     GNUNET_free (p);
2277     SetErrnoFromWinError (GetLastError ());
2278     return NULL;
2279   }
2280   if (!DuplicateHandle
2281       (GetCurrentProcess (), p->fd[0]->h, GetCurrentProcess (), &tmp_handle, 0,
2282        inherit_read == GNUNET_YES ? TRUE : FALSE, DUPLICATE_SAME_ACCESS))
2283   {
2284     SetErrnoFromWinError (GetLastError ());
2285     CloseHandle (p->fd[0]->h);
2286     CloseHandle (p->fd[1]->h);
2287     GNUNET_free (p);
2288     return NULL;
2289   }
2290   CloseHandle (p->fd[0]->h);
2291   p->fd[0]->h = tmp_handle;
2292
2293   if (!DuplicateHandle
2294       (GetCurrentProcess (), p->fd[1]->h, GetCurrentProcess (), &tmp_handle, 0,
2295        inherit_write == GNUNET_YES ? TRUE : FALSE, DUPLICATE_SAME_ACCESS))
2296   {
2297     SetErrnoFromWinError (GetLastError ());
2298     CloseHandle (p->fd[0]->h);
2299     CloseHandle (p->fd[1]->h);
2300     GNUNET_free (p);
2301     return NULL;
2302   }
2303   CloseHandle (p->fd[1]->h);
2304   p->fd[1]->h = tmp_handle;
2305
2306   p->fd[0]->type = GNUNET_DISK_HANLDE_TYPE_PIPE;
2307   p->fd[1]->type = GNUNET_DISK_HANLDE_TYPE_PIPE;
2308
2309   p->fd[0]->oOverlapRead = GNUNET_malloc (sizeof (OVERLAPPED));
2310   p->fd[0]->oOverlapWrite = GNUNET_malloc (sizeof (OVERLAPPED));
2311   p->fd[1]->oOverlapRead = GNUNET_malloc (sizeof (OVERLAPPED));
2312   p->fd[1]->oOverlapWrite = GNUNET_malloc (sizeof (OVERLAPPED));
2313
2314   p->fd[0]->oOverlapRead->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2315   p->fd[0]->oOverlapWrite->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2316
2317   p->fd[1]->oOverlapRead->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2318   p->fd[1]->oOverlapWrite->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2319
2320   return p;
2321 #endif
2322 }
2323
2324
2325 /**
2326  * Creates a pipe object from a couple of file descriptors.
2327  * Useful for wrapping existing pipe FDs.
2328  *
2329  * @param blocking_read creates an asynchronous pipe for reading if set to GNUNET_NO
2330  * @param blocking_write creates an asynchronous pipe for writing if set to GNUNET_NO
2331  * @param fd an array of two fd values. One of them may be -1 for read-only or write-only pipes
2332  *
2333  * @return handle to the new pipe, NULL on error
2334  */
2335 struct GNUNET_DISK_PipeHandle *
2336 GNUNET_DISK_pipe_from_fd (int blocking_read, int blocking_write, int fd[2])
2337 {
2338   struct GNUNET_DISK_PipeHandle *p;
2339   struct GNUNET_DISK_FileHandle *fds;
2340
2341   p = GNUNET_malloc (sizeof (struct GNUNET_DISK_PipeHandle) +
2342                      2 * sizeof (struct GNUNET_DISK_FileHandle));
2343   fds = (struct GNUNET_DISK_FileHandle *) &p[1];
2344   p->fd[0] = &fds[0];
2345   p->fd[1] = &fds[1];
2346 #ifndef MINGW
2347   int ret;
2348   int flags;
2349   int eno = 0; /* make gcc happy */
2350
2351   p->fd[0]->fd = fd[0];
2352   p->fd[1]->fd = fd[1];
2353   ret = 0;
2354   if (fd[0] >= 0)
2355   {
2356     if (!blocking_read)
2357     {
2358       flags = fcntl (fd[0], F_GETFL);
2359       flags |= O_NONBLOCK;
2360       if (0 > fcntl (fd[0], F_SETFL, flags))
2361       {
2362         ret = -1;
2363         eno = errno;
2364       }
2365     }
2366     flags = fcntl (fd[0], F_GETFD);
2367     flags |= FD_CLOEXEC;
2368     if (0 > fcntl (fd[0], F_SETFD, flags))
2369     {
2370       ret = -1;
2371       eno = errno;
2372     }
2373   }
2374
2375   if (fd[1] >= 0)
2376   {
2377     if (!blocking_write)
2378     {
2379       flags = fcntl (fd[1], F_GETFL);
2380       flags |= O_NONBLOCK;
2381       if (0 > fcntl (fd[1], F_SETFL, flags))
2382       {
2383         ret = -1;
2384         eno = errno;
2385       }
2386     }
2387     flags = fcntl (fd[1], F_GETFD);
2388     flags |= FD_CLOEXEC;
2389     if (0 > fcntl (fd[1], F_SETFD, flags))
2390     {
2391       ret = -1;
2392       eno = errno;
2393     }
2394   }
2395   if (ret == -1)
2396   {
2397     errno = eno;
2398     LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR, "fcntl");
2399     if (p->fd[0]->fd >= 0)
2400       GNUNET_break (0 == close (p->fd[0]->fd));
2401     if (p->fd[1]->fd >= 0)
2402       GNUNET_break (0 == close (p->fd[1]->fd));
2403     GNUNET_free (p);
2404     errno = eno;
2405     return NULL;
2406   }
2407 #else
2408   if (fd[0] >= 0)
2409     p->fd[0]->h = (HANDLE) _get_osfhandle (fd[0]);
2410   else
2411     p->fd[0]->h = INVALID_HANDLE_VALUE;
2412   if (fd[1] >= 0)
2413     p->fd[1]->h = (HANDLE) _get_osfhandle (fd[1]);
2414   else
2415     p->fd[1]->h = INVALID_HANDLE_VALUE;
2416
2417   if (p->fd[0]->h != INVALID_HANDLE_VALUE)
2418   {
2419     p->fd[0]->type = GNUNET_DISK_HANLDE_TYPE_PIPE;
2420     p->fd[0]->oOverlapRead = GNUNET_malloc (sizeof (OVERLAPPED));
2421     p->fd[0]->oOverlapWrite = GNUNET_malloc (sizeof (OVERLAPPED));
2422     p->fd[0]->oOverlapRead->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2423     p->fd[0]->oOverlapWrite->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2424   }
2425
2426   if (p->fd[1]->h != INVALID_HANDLE_VALUE)
2427   {
2428     p->fd[1]->type = GNUNET_DISK_HANLDE_TYPE_PIPE;
2429     p->fd[1]->oOverlapRead = GNUNET_malloc (sizeof (OVERLAPPED));
2430     p->fd[1]->oOverlapWrite = GNUNET_malloc (sizeof (OVERLAPPED));
2431     p->fd[1]->oOverlapRead->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2432     p->fd[1]->oOverlapWrite->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2433   }
2434 #endif
2435   return p;
2436 }
2437
2438
2439 /**
2440  * Closes an interprocess channel
2441  *
2442  * @param p pipe to close
2443  * @param end which end of the pipe to close
2444  * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
2445  */
2446 int
2447 GNUNET_DISK_pipe_close_end (struct GNUNET_DISK_PipeHandle *p,
2448                             enum GNUNET_DISK_PipeEnd end)
2449 {
2450   int ret = GNUNET_OK;
2451   int save;
2452
2453 #ifdef MINGW
2454   if (end == GNUNET_DISK_PIPE_END_READ)
2455   {
2456     if (p->fd[0]->h != INVALID_HANDLE_VALUE)
2457     {
2458       if (!CloseHandle (p->fd[0]->h))
2459       {
2460         SetErrnoFromWinError (GetLastError ());
2461         ret = GNUNET_SYSERR;
2462       }
2463       GNUNET_free (p->fd[0]->oOverlapRead);
2464       GNUNET_free (p->fd[0]->oOverlapWrite);
2465       p->fd[0]->h = INVALID_HANDLE_VALUE;
2466     }
2467   }
2468   else if (end == GNUNET_DISK_PIPE_END_WRITE)
2469   {
2470     if (p->fd[0]->h != INVALID_HANDLE_VALUE)
2471     {
2472       if (!CloseHandle (p->fd[1]->h))
2473       {
2474         SetErrnoFromWinError (GetLastError ());
2475         ret = GNUNET_SYSERR;
2476       }
2477       GNUNET_free (p->fd[1]->oOverlapRead);
2478       GNUNET_free (p->fd[1]->oOverlapWrite);
2479       p->fd[1]->h = INVALID_HANDLE_VALUE;
2480     }
2481   }
2482   save = errno;
2483 #else
2484   save = 0;
2485   if (end == GNUNET_DISK_PIPE_END_READ)
2486   {
2487     if (0 != close (p->fd[0]->fd))
2488     {
2489       ret = GNUNET_SYSERR;
2490       save = errno;
2491     }
2492     p->fd[0]->fd = -1;
2493   }
2494   else if (end == GNUNET_DISK_PIPE_END_WRITE)
2495   {
2496     if (0 != close (p->fd[1]->fd))
2497     {
2498       ret = GNUNET_SYSERR;
2499       save = errno;
2500     }
2501     p->fd[1]->fd = -1;
2502   }
2503 #endif
2504   errno = save;
2505   return ret;
2506 }
2507
2508
2509 /**
2510  * Closes an interprocess channel
2511  *
2512  * @param p pipe to close
2513  * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
2514  */
2515 int
2516 GNUNET_DISK_pipe_close (struct GNUNET_DISK_PipeHandle *p)
2517 {
2518   int ret = GNUNET_OK;
2519   int save;
2520
2521 #ifdef MINGW
2522   if (p->fd[0]->h != INVALID_HANDLE_VALUE)
2523   {
2524     if (!CloseHandle (p->fd[0]->h))
2525     {
2526       SetErrnoFromWinError (GetLastError ());
2527       ret = GNUNET_SYSERR;
2528     }
2529     GNUNET_free (p->fd[0]->oOverlapRead);
2530     GNUNET_free (p->fd[0]->oOverlapWrite);
2531   }
2532   if (p->fd[1]->h != INVALID_HANDLE_VALUE)
2533   {
2534     if (!CloseHandle (p->fd[1]->h))
2535     {
2536       SetErrnoFromWinError (GetLastError ());
2537       ret = GNUNET_SYSERR;
2538     }
2539     GNUNET_free (p->fd[1]->oOverlapRead);
2540     GNUNET_free (p->fd[1]->oOverlapWrite);
2541   }
2542   save = errno;
2543 #else
2544   save = 0;
2545   if (p->fd[0]->fd != -1)
2546   {
2547     if (0 != close (p->fd[0]->fd))
2548     {
2549       ret = GNUNET_SYSERR;
2550       save = errno;
2551     }
2552   }
2553
2554   if (p->fd[1]->fd != -1)
2555   {
2556     if (0 != close (p->fd[1]->fd))
2557     {
2558       ret = GNUNET_SYSERR;
2559       save = errno;
2560     }
2561   }
2562 #endif
2563   GNUNET_free (p);
2564   errno = save;
2565   return ret;
2566 }
2567
2568
2569 /**
2570  * Get the handle to a particular pipe end
2571  *
2572  * @param p pipe
2573  * @param n end to access
2574  * @return handle for the respective end
2575  */
2576 const struct GNUNET_DISK_FileHandle *
2577 GNUNET_DISK_pipe_handle (const struct GNUNET_DISK_PipeHandle *p,
2578                          enum GNUNET_DISK_PipeEnd n)
2579 {
2580   switch (n)
2581   {
2582   case GNUNET_DISK_PIPE_END_READ:
2583   case GNUNET_DISK_PIPE_END_WRITE:
2584     return p->fd[n];
2585   default:
2586     GNUNET_break (0);
2587     return NULL;
2588   }
2589 }
2590
2591
2592 /**
2593  * Retrieve OS file handle
2594  * @internal
2595  * @param fh GNUnet file descriptor
2596  * @param dst destination buffer
2597  * @param dst_len length of dst
2598  * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
2599  */
2600 int
2601 GNUNET_DISK_internal_file_handle_ (const struct GNUNET_DISK_FileHandle *fh,
2602                                    void *dst, size_t dst_len)
2603 {
2604 #ifdef MINGW
2605   if (dst_len < sizeof (HANDLE))
2606     return GNUNET_SYSERR;
2607   *((HANDLE *) dst) = fh->h;
2608 #else
2609   if (dst_len < sizeof (int))
2610     return GNUNET_SYSERR;
2611   *((int *) dst) = fh->fd;
2612 #endif
2613
2614   return GNUNET_OK;
2615 }
2616
2617 /* end of disk.c */