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