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