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