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