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