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