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