force passing of sanity check
[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   if (UNLINK (fileName) == 0)
1057     return GNUNET_OK;
1058   if ((errno != EISDIR) &&
1059       /* EISDIR is not sufficient in all cases, e.g.
1060          sticky /tmp directory may result in EPERM on BSD.
1061          So we also explicitly check "isDirectory" */
1062       (GNUNET_YES != GNUNET_DISK_directory_test (fileName)))
1063     {
1064       GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "rmdir", fileName);
1065       return GNUNET_SYSERR;
1066     }
1067   if (GNUNET_SYSERR ==
1068       GNUNET_DISK_directory_scan (fileName, &remove_helper, NULL))
1069     return GNUNET_SYSERR;
1070   if (0 != RMDIR (fileName))
1071     {
1072       GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "rmdir", fileName);
1073       return GNUNET_SYSERR;
1074     }
1075   return GNUNET_OK;
1076 }
1077
1078
1079 /**
1080  * Copy a file.
1081  *
1082  * @param src file to copy
1083  * @param dst destination file name
1084  * @return GNUNET_OK on success, GNUNET_SYSERR on error
1085  */
1086 int
1087 GNUNET_DISK_file_copy (const char *src, const char *dst)
1088 {
1089   char *buf;
1090   uint64_t pos;
1091   uint64_t size;
1092   size_t len;
1093   struct GNUNET_DISK_FileHandle *in;
1094   struct GNUNET_DISK_FileHandle *out;
1095
1096   if (GNUNET_OK != GNUNET_DISK_file_size (src, &size, GNUNET_YES))
1097     return GNUNET_SYSERR;
1098   pos = 0;
1099   in = GNUNET_DISK_file_open (src, GNUNET_DISK_OPEN_READ,
1100                               GNUNET_DISK_PERM_NONE);
1101   if (!in)
1102     return GNUNET_SYSERR;
1103   out = GNUNET_DISK_file_open (dst, GNUNET_DISK_OPEN_WRITE
1104                                | GNUNET_DISK_OPEN_CREATE |
1105                                GNUNET_DISK_OPEN_FAILIFEXISTS,
1106                                GNUNET_DISK_PERM_USER_READ |
1107                                GNUNET_DISK_PERM_USER_WRITE |
1108                                GNUNET_DISK_PERM_GROUP_READ |
1109                                GNUNET_DISK_PERM_GROUP_WRITE);
1110   if (!out)
1111     {
1112       GNUNET_DISK_file_close (in);
1113       return GNUNET_SYSERR;
1114     }
1115   buf = GNUNET_malloc (COPY_BLK_SIZE);
1116   while (pos < size)
1117     {
1118       len = COPY_BLK_SIZE;
1119       if (len > size - pos)
1120         len = size - pos;
1121       if (len != GNUNET_DISK_file_read (in, buf, len))
1122         goto FAIL;
1123       if (len != GNUNET_DISK_file_write (out, buf, len))
1124         goto FAIL;
1125       pos += len;
1126     }
1127   GNUNET_free (buf);
1128   GNUNET_DISK_file_close (in);
1129   GNUNET_DISK_file_close (out);
1130   return GNUNET_OK;
1131 FAIL:
1132   GNUNET_free (buf);
1133   GNUNET_DISK_file_close (in);
1134   GNUNET_DISK_file_close (out);
1135   return GNUNET_SYSERR;
1136 }
1137
1138
1139 /**
1140  * @brief Removes special characters as ':' from a filename.
1141  * @param fn the filename to canonicalize
1142  */
1143 void
1144 GNUNET_DISK_filename_canonicalize (char *fn)
1145 {
1146   char *idx;
1147   char c;
1148
1149   idx = fn;
1150   while (*idx)
1151     {
1152       c = *idx;
1153
1154       if (c == '/' || c == '\\' || c == ':' || c == '*' || c == '?' ||
1155           c == '"' || c == '<' || c == '>' || c == '|')
1156         {
1157           *idx = '_';
1158         }
1159
1160       idx++;
1161     }
1162 }
1163
1164
1165
1166 /**
1167  * @brief Change owner of a file
1168  *
1169  * @param filename name of file to change the owner of
1170  * @param user name of the new owner
1171  * @return GNUNET_OK on success, GNUNET_SYSERR on failure
1172  */
1173 int
1174 GNUNET_DISK_file_change_owner (const char *filename, const char *user)
1175 {
1176 #ifndef MINGW
1177   struct passwd *pws;
1178
1179   pws = getpwnam (user);
1180   if (pws == NULL)
1181     {
1182       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1183                   _("Cannot obtain information about user `%s': %s\n"),
1184                   user, STRERROR (errno));
1185       return GNUNET_SYSERR;
1186     }
1187   if (0 != chown (filename, pws->pw_uid, pws->pw_gid))
1188     GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "chown", filename);
1189 #endif
1190   return GNUNET_OK;
1191 }
1192
1193
1194 /**
1195  * Lock a part of a file
1196  * @param fh file handle
1197  * @param lockStart absolute position from where to lock
1198  * @param lockEnd absolute position until where to lock
1199  * @param excl GNUNET_YES for an exclusive lock
1200  * @return GNUNET_OK on success, GNUNET_SYSERR on error
1201  */
1202 int
1203 GNUNET_DISK_file_lock (struct GNUNET_DISK_FileHandle *fh, off_t lockStart,
1204                        off_t lockEnd, int excl)
1205 {
1206   if (fh == NULL)
1207     {
1208       errno = EINVAL;
1209       return GNUNET_SYSERR;
1210     }
1211
1212 #ifndef MINGW
1213   struct flock fl;
1214
1215   memset (&fl, 0, sizeof (struct flock));
1216   fl.l_type = excl ? F_WRLCK : F_RDLCK;
1217   fl.l_whence = SEEK_SET;
1218   fl.l_start = lockStart;
1219   fl.l_len = lockEnd;
1220
1221   return fcntl (fh->fd, F_SETLK, &fl) != 0 ? GNUNET_SYSERR : GNUNET_OK;
1222 #else
1223   OVERLAPPED o;
1224
1225   memset (&o, 0, sizeof (OVERLAPPED));
1226   o.Offset = lockStart;
1227
1228   if (!LockFileEx (fh->h, (excl ? LOCKFILE_EXCLUSIVE_LOCK : 0)
1229                    | LOCKFILE_FAIL_IMMEDIATELY, 0, lockEnd - lockStart, 0,
1230                    &o))
1231     {
1232       SetErrnoFromWinError (GetLastError ());
1233       return GNUNET_SYSERR;
1234     }
1235
1236   return GNUNET_OK;
1237 #endif
1238 }
1239
1240
1241 /**
1242  * Unlock a part of a file
1243  * @param fh file handle
1244  * @param unlockStart absolute position from where to unlock
1245  * @param unlockEnd absolute position until where to unlock
1246  * @return GNUNET_OK on success, GNUNET_SYSERR on error
1247  */
1248 int
1249 GNUNET_DISK_file_unlock (struct GNUNET_DISK_FileHandle *fh, off_t unlockStart,
1250                          off_t unlockEnd)
1251 {
1252   if (fh == NULL)
1253     {
1254       errno = EINVAL;
1255       return GNUNET_SYSERR;
1256     }
1257
1258 #ifndef MINGW
1259   struct flock fl;
1260
1261   memset (&fl, 0, sizeof (struct flock));
1262   fl.l_type = F_UNLCK;
1263   fl.l_whence = SEEK_SET;
1264   fl.l_start = unlockStart;
1265   fl.l_len = unlockEnd;
1266
1267   return fcntl (fh->fd, F_SETLK, &fl) != 0 ? GNUNET_SYSERR : GNUNET_OK;
1268 #else
1269   OVERLAPPED o;
1270
1271   memset (&o, 0, sizeof (OVERLAPPED));
1272   o.Offset = unlockStart;
1273
1274   if (!UnlockFileEx (fh->h, 0, unlockEnd - unlockStart, 0, &o))
1275     {
1276       SetErrnoFromWinError (GetLastError ());
1277       return GNUNET_SYSERR;
1278     }
1279
1280   return GNUNET_OK;
1281 #endif
1282 }
1283
1284
1285 /**
1286  * Open a file.  Note that the access permissions will only be
1287  * used if a new file is created and if the underlying operating
1288  * system supports the given permissions.
1289  *
1290  * @param fn file name to be opened
1291  * @param flags opening flags, a combination of GNUNET_DISK_OPEN_xxx bit flags
1292  * @param perm permissions for the newly created file, use
1293  *             GNUNET_DISK_PERM_USER_NONE if a file could not be created by this
1294  *             call (because of flags)
1295  * @return IO handle on success, NULL on error
1296  */
1297 struct GNUNET_DISK_FileHandle *
1298 GNUNET_DISK_file_open (const char *fn,
1299                        enum GNUNET_DISK_OpenFlags flags,
1300                        enum GNUNET_DISK_AccessPermissions perm)
1301 {
1302   char *expfn;
1303   struct GNUNET_DISK_FileHandle *ret;
1304 #ifdef MINGW
1305   DWORD access;
1306   DWORD disp;
1307   HANDLE h;
1308 #else
1309   int oflags;
1310   int mode;
1311   int fd;
1312 #endif
1313
1314   expfn = GNUNET_STRINGS_filename_expand (fn);
1315   if (NULL == expfn)
1316     return NULL;
1317 #ifndef MINGW
1318   mode = 0;
1319   if (GNUNET_DISK_OPEN_READWRITE == (flags & GNUNET_DISK_OPEN_READWRITE))
1320     oflags = O_RDWR;            /* note: O_RDWR is NOT always O_RDONLY | O_WRONLY */
1321   else if (flags & GNUNET_DISK_OPEN_READ)
1322     oflags = O_RDONLY;
1323   else if (flags & GNUNET_DISK_OPEN_WRITE)
1324     oflags = O_WRONLY;
1325   else
1326     {
1327       GNUNET_break (0);
1328       GNUNET_free (expfn);
1329       return NULL;
1330     }
1331   if (flags & GNUNET_DISK_OPEN_FAILIFEXISTS)
1332     oflags |= (O_CREAT | O_EXCL);
1333   if (flags & GNUNET_DISK_OPEN_TRUNCATE)
1334     oflags |= O_TRUNC;
1335   if (flags & GNUNET_DISK_OPEN_APPEND)
1336     oflags |= O_APPEND;
1337   if (flags & GNUNET_DISK_OPEN_CREATE)
1338     {
1339       (void) GNUNET_DISK_directory_create_for_file (expfn);
1340       oflags |= O_CREAT;
1341       mode = translate_unix_perms(perm);
1342     }
1343
1344   fd = open (expfn, oflags | O_LARGEFILE, mode);
1345   if (fd == -1)
1346     {
1347       if (0 == (flags & GNUNET_DISK_OPEN_FAILIFEXISTS))
1348         GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "open", expfn);
1349       else
1350         GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_DEBUG, "open", expfn);
1351       GNUNET_free (expfn);
1352       return NULL;
1353     }
1354 #else
1355   access = 0;
1356   disp = OPEN_ALWAYS;
1357
1358   if (GNUNET_DISK_OPEN_READWRITE == (flags & GNUNET_DISK_OPEN_READWRITE))
1359     access = FILE_READ_DATA | FILE_WRITE_DATA;
1360   else if (flags & GNUNET_DISK_OPEN_READ)
1361     access = FILE_READ_DATA;
1362   else if (flags & GNUNET_DISK_OPEN_WRITE)
1363     access = FILE_WRITE_DATA;
1364
1365   if (flags & GNUNET_DISK_OPEN_FAILIFEXISTS)
1366     {
1367       disp = CREATE_NEW;
1368     }
1369   else if (flags & GNUNET_DISK_OPEN_CREATE)
1370     {
1371       if (flags & GNUNET_DISK_OPEN_TRUNCATE)
1372         disp = CREATE_ALWAYS;
1373       else
1374         disp = OPEN_ALWAYS;
1375     }
1376   else if (flags & GNUNET_DISK_OPEN_TRUNCATE)
1377     {
1378       disp = TRUNCATE_EXISTING;
1379     }
1380   else
1381     {
1382       disp = OPEN_EXISTING;
1383     }
1384
1385   /* TODO: access priviledges? */
1386   h = CreateFile (expfn, access, FILE_SHARE_DELETE | FILE_SHARE_READ
1387                   | FILE_SHARE_WRITE, NULL, disp, FILE_ATTRIBUTE_NORMAL,
1388                   NULL);
1389   if (h == INVALID_HANDLE_VALUE)
1390     {
1391       SetErrnoFromWinError (GetLastError ());
1392       GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "open", expfn);
1393       GNUNET_free (expfn);
1394       return NULL;
1395     }
1396
1397   if (flags & GNUNET_DISK_OPEN_APPEND)
1398     if (SetFilePointer (h, 0, 0, FILE_END) == INVALID_SET_FILE_POINTER)
1399       {
1400         SetErrnoFromWinError (GetLastError ());
1401         GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "SetFilePointer",
1402                                   expfn);
1403         CloseHandle (h);
1404         GNUNET_free (expfn);
1405         return NULL;
1406       }
1407 #endif
1408
1409   ret = GNUNET_malloc (sizeof (struct GNUNET_DISK_FileHandle));
1410 #ifdef MINGW
1411   ret->h = h;
1412   ret->type = GNUNET_DISK_FILE;
1413 #else
1414   ret->fd = fd;
1415 #endif
1416   GNUNET_free (expfn);
1417   return ret;
1418 }
1419
1420
1421 /**
1422  * Close an open file
1423  * @param h file handle
1424  * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
1425  */
1426 int
1427 GNUNET_DISK_file_close (struct GNUNET_DISK_FileHandle *h)
1428 {
1429   if (h == NULL)
1430     {
1431       errno = EINVAL;
1432       return GNUNET_SYSERR;
1433     }
1434
1435 #if MINGW
1436   if (!CloseHandle (h->h))
1437     {
1438       SetErrnoFromWinError (GetLastError ());
1439       GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "close");
1440       GNUNET_free (h->oOverlapRead);
1441       GNUNET_free (h->oOverlapWrite);
1442       GNUNET_free (h);
1443       return GNUNET_SYSERR;
1444     }
1445 #else
1446   if (close (h->fd) != 0)
1447     {
1448       GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "close");
1449       GNUNET_free (h);
1450       return GNUNET_SYSERR;
1451     }
1452 #endif
1453   GNUNET_free (h);
1454   return GNUNET_OK;
1455 }
1456
1457
1458 /**
1459  * Construct full path to a file inside of the private
1460  * directory used by GNUnet.  Also creates the corresponding
1461  * directory.  If the resulting name is supposed to be
1462  * a directory, end the last argument in '/' (or pass
1463  * DIR_SEPARATOR_STR as the last argument before NULL).
1464  *
1465  * @param cfg configuration to use (determines HOME)
1466  * @param serviceName name of the service
1467  * @param ... is NULL-terminated list of
1468  *                path components to append to the
1469  *                private directory name.
1470  * @return the constructed filename
1471  */
1472 char *
1473 GNUNET_DISK_get_home_filename (const struct GNUNET_CONFIGURATION_Handle *cfg,
1474                                const char *serviceName, ...)
1475 {
1476   const char *c;
1477   char *pfx;
1478   char *ret;
1479   va_list ap;
1480   unsigned int needed;
1481
1482   if (GNUNET_OK !=
1483       GNUNET_CONFIGURATION_get_value_filename (cfg,
1484                                                serviceName, "HOME", &pfx))
1485     return NULL;
1486   if (pfx == NULL)
1487     {
1488       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1489                   _("No `%s' specified for service `%s' in configuration.\n"),
1490                   "HOME", serviceName);
1491       return NULL;
1492     }
1493   needed = strlen (pfx) + 2;
1494   if ((pfx[strlen (pfx) - 1] != '/') && (pfx[strlen (pfx) - 1] != '\\'))
1495     needed++;
1496   va_start (ap, serviceName);
1497   while (1)
1498     {
1499       c = va_arg (ap, const char *);
1500       if (c == NULL)
1501         break;
1502       needed += strlen (c);
1503       if ((c[strlen (c) - 1] != '/') && (c[strlen (c) - 1] != '\\'))
1504         needed++;
1505     }
1506   va_end (ap);
1507   ret = GNUNET_malloc (needed);
1508   strcpy (ret, pfx);
1509   GNUNET_free (pfx);
1510   va_start (ap, serviceName);
1511   while (1)
1512     {
1513       c = va_arg (ap, const char *);
1514       if (c == NULL)
1515         break;
1516       if ((c[strlen (c) - 1] != '/') && (c[strlen (c) - 1] != '\\'))
1517         strcat (ret, DIR_SEPARATOR_STR);
1518       strcat (ret, c);
1519     }
1520   va_end (ap);
1521   if ((ret[strlen (ret) - 1] != '/') && (ret[strlen (ret) - 1] != '\\'))
1522     (void) GNUNET_DISK_directory_create_for_file (ret);
1523   else
1524     (void) GNUNET_DISK_directory_create (ret);
1525   return ret;
1526 }
1527
1528
1529 /**
1530  * Handle for a memory-mapping operation.
1531  */
1532 struct GNUNET_DISK_MapHandle
1533 {
1534   /**
1535    * Address where the map is in memory.
1536    */
1537   void *addr;
1538
1539 #ifdef MINGW
1540   /**
1541    * Underlying OS handle.
1542    */
1543   HANDLE h;
1544 #else
1545   /**
1546    * Number of bytes mapped.
1547    */
1548   size_t len;
1549 #endif
1550 };
1551
1552
1553 #ifndef MAP_FAILED
1554 #define MAP_FAILED ((void *) -1)
1555 #endif
1556
1557 /**
1558  * Map a file into memory
1559  *
1560  * @param h open file handle
1561  * @param m handle to the new mapping
1562  * @param access access specification, GNUNET_DISK_MAP_TYPE_xxx
1563  * @param len size of the mapping
1564  * @return pointer to the mapped memory region, NULL on failure
1565  */
1566 void *
1567 GNUNET_DISK_file_map (const struct GNUNET_DISK_FileHandle *h,
1568                       struct GNUNET_DISK_MapHandle **m,
1569                       enum GNUNET_DISK_MapType access, size_t len)
1570 {
1571   if (h == NULL)
1572     {
1573       errno = EINVAL;
1574       return NULL;
1575     }
1576
1577 #ifdef MINGW
1578   DWORD mapAccess, protect;
1579
1580   if ((access & GNUNET_DISK_MAP_TYPE_READ) &&
1581       (access & GNUNET_DISK_MAP_TYPE_WRITE))
1582     {
1583       protect = PAGE_READWRITE;
1584       mapAccess = FILE_MAP_ALL_ACCESS;
1585     }
1586   else if (access & GNUNET_DISK_MAP_TYPE_READ)
1587     {
1588       protect = PAGE_READONLY;
1589       mapAccess = FILE_MAP_READ;
1590     }
1591   else if (access & GNUNET_DISK_MAP_TYPE_WRITE)
1592     {
1593       protect = PAGE_READWRITE;
1594       mapAccess = FILE_MAP_WRITE;
1595     }
1596   else
1597     {
1598       GNUNET_break (0);
1599       return NULL;
1600     }
1601
1602   *m = GNUNET_malloc (sizeof (struct GNUNET_DISK_MapHandle));
1603   (*m)->h = CreateFileMapping (h->h, NULL, protect, 0, 0, NULL);
1604   if ((*m)->h == INVALID_HANDLE_VALUE)
1605     {
1606       SetErrnoFromWinError (GetLastError ());
1607       GNUNET_free (*m);
1608       return NULL;
1609     }
1610
1611   (*m)->addr = MapViewOfFile ((*m)->h, mapAccess, 0, 0, len);
1612   if (!(*m)->addr)
1613     {
1614       SetErrnoFromWinError (GetLastError ());
1615       CloseHandle ((*m)->h);
1616       GNUNET_free (*m);
1617     }
1618
1619   return (*m)->addr;
1620 #else
1621   int prot;
1622
1623   prot = 0;
1624   if (access & GNUNET_DISK_MAP_TYPE_READ)
1625     prot = PROT_READ;
1626   if (access & GNUNET_DISK_MAP_TYPE_WRITE)
1627     prot |= PROT_WRITE;
1628   *m = GNUNET_malloc (sizeof (struct GNUNET_DISK_MapHandle));
1629   (*m)->addr = mmap (NULL, len, prot, MAP_SHARED, h->fd, 0);
1630   GNUNET_assert (NULL != (*m)->addr);
1631   if (MAP_FAILED == (*m)->addr)
1632     {    
1633       GNUNET_free (*m);
1634       return NULL;
1635     }
1636   (*m)->len = len;
1637   return (*m)->addr;
1638 #endif
1639 }
1640
1641 /**
1642  * Unmap a file
1643  * @param h mapping handle
1644  * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
1645  */
1646 int
1647 GNUNET_DISK_file_unmap (struct GNUNET_DISK_MapHandle *h)
1648 {
1649   int ret;
1650   if (h == NULL)
1651     {
1652       errno = EINVAL;
1653       return GNUNET_SYSERR;
1654     }
1655
1656 #ifdef MINGW
1657   ret = UnmapViewOfFile (h->addr) ? GNUNET_OK : GNUNET_SYSERR;
1658   if (ret != GNUNET_OK)
1659     SetErrnoFromWinError (GetLastError ());
1660   if (!CloseHandle (h->h) && (ret == GNUNET_OK))
1661     {
1662       ret = GNUNET_SYSERR;
1663       SetErrnoFromWinError (GetLastError ());
1664     }
1665 #else
1666   ret = munmap (h->addr, h->len) != -1 ? GNUNET_OK : GNUNET_SYSERR;
1667 #endif
1668   GNUNET_free (h);
1669   return ret;
1670 }
1671
1672
1673 /**
1674  * Write file changes to disk
1675  * @param h handle to an open file
1676  * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
1677  */
1678 int
1679 GNUNET_DISK_file_sync (const struct GNUNET_DISK_FileHandle *h)
1680 {
1681   if (h == NULL)
1682     {
1683       errno = EINVAL;
1684       return GNUNET_SYSERR;
1685     }
1686
1687 #ifdef MINGW
1688   int ret;
1689
1690   ret = FlushFileBuffers (h->h) ? GNUNET_OK : GNUNET_SYSERR;
1691   if (ret != GNUNET_OK)
1692     SetErrnoFromWinError (GetLastError ());
1693   return ret;
1694 #elif defined(FREEBSD) || defined(OPENBSD) || defined(DARWIN)
1695   return fsync (h->fd) == -1 ? GNUNET_SYSERR : GNUNET_OK;
1696 #else
1697   return fdatasync (h->fd) == -1 ? GNUNET_SYSERR : GNUNET_OK;
1698 #endif
1699 }
1700
1701 #if WINDOWS
1702 /* Copyright Bob Byrnes  <byrnes <at> curl.com>
1703    http://permalink.gmane.org/gmane.os.cygwin.patches/2121
1704 */
1705 /* Create a pipe, and return handles to the read and write ends,
1706    just like CreatePipe, but ensure that the write end permits
1707    FILE_READ_ATTRIBUTES access, on later versions of win32 where
1708    this is supported.  This access is needed by NtQueryInformationFile,
1709    which is used to implement select and nonblocking writes.
1710    Note that the return value is either NO_ERROR or GetLastError,
1711    unlike CreatePipe, which returns a bool for success or failure.  */
1712 static int
1713 create_selectable_pipe (PHANDLE read_pipe_ptr,
1714                         PHANDLE write_pipe_ptr,
1715                         LPSECURITY_ATTRIBUTES sa_ptr,
1716                         DWORD psize,
1717                         DWORD dwReadMode,
1718                         DWORD dwWriteMode)
1719 {
1720   /* Default to error. */
1721   *read_pipe_ptr = *write_pipe_ptr = INVALID_HANDLE_VALUE;
1722
1723   HANDLE read_pipe = INVALID_HANDLE_VALUE, write_pipe = INVALID_HANDLE_VALUE;
1724
1725   /* Ensure that there is enough pipe buffer space for atomic writes.  */
1726   if (psize < PIPE_BUF)
1727     psize = PIPE_BUF;
1728
1729   char pipename[MAX_PATH];
1730
1731   /* Retry CreateNamedPipe as long as the pipe name is in use.
1732      Retrying will probably never be necessary, but we want
1733      to be as robust as possible.  */
1734   while (1)
1735     {
1736       static volatile LONG pipe_unique_id;
1737
1738       snprintf (pipename, sizeof pipename, "\\\\.\\pipe\\gnunet-%d-%ld",
1739                 getpid (), InterlockedIncrement ((LONG *)&pipe_unique_id));
1740 #if DEBUG_PIPE
1741       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "CreateNamedPipe: name = %s, size = %lu\n", pipename, psize);
1742 #endif
1743       /* Use CreateNamedPipe instead of CreatePipe, because the latter
1744          returns a write handle that does not permit FILE_READ_ATTRIBUTES
1745          access, on versions of win32 earlier than WinXP SP2.
1746          CreatePipe also stupidly creates a full duplex pipe, which is
1747          a waste, since only a single direction is actually used.
1748          It's important to only allow a single instance, to ensure that
1749          the pipe was not created earlier by some other process, even if
1750          the pid has been reused.  We avoid FILE_FLAG_FIRST_PIPE_INSTANCE
1751          because that is only available for Win2k SP2 and WinXP.  */
1752       read_pipe = CreateNamedPipeA (pipename,
1753                                    PIPE_ACCESS_INBOUND | dwReadMode,
1754                                    PIPE_TYPE_BYTE | PIPE_READMODE_BYTE,
1755                                    1,       /* max instances */
1756                                    psize,   /* output buffer size */
1757                                    psize,   /* input buffer size */
1758                                    NMPWAIT_USE_DEFAULT_WAIT,
1759                                    sa_ptr);
1760
1761       if (read_pipe != INVALID_HANDLE_VALUE)
1762         {
1763 #if DEBUG_PIPE
1764           GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "pipe read handle = %p\n", read_pipe);
1765 #endif
1766           break;
1767         }
1768
1769       DWORD err = GetLastError ();
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           GNUNET_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           GNUNET_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           GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "CreateNamedPipe not implemented, resorting to "
1791               "CreatePipe: size = %lu\n", psize);
1792 #endif
1793           if (CreatePipe (read_pipe_ptr, write_pipe_ptr, sa_ptr, psize))
1794             {
1795 #if DEBUG_PIPE
1796               GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "pipe read handle = %p\n", *read_pipe_ptr);
1797               GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "pipe write handle = %p\n", *write_pipe_ptr);
1798 #endif
1799               return GNUNET_OK;
1800             }
1801           err = GetLastError ();
1802           GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "CreatePipe failed: %d\n", err);
1803           return err;
1804         default:
1805           GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "CreateNamedPipe failed: %d\n", err);
1806           return err;
1807         }
1808       /* NOTREACHED */
1809     }
1810 #if DEBUG_PIPE
1811   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "CreateFile: name = %s\n", pipename);
1812 #endif
1813
1814   /* Open the named pipe for writing.
1815      Be sure to permit FILE_READ_ATTRIBUTES access.  */
1816   write_pipe = CreateFileA (pipename,
1817                            GENERIC_WRITE | FILE_READ_ATTRIBUTES,
1818                            0,       /* share mode */
1819                            sa_ptr,
1820                            OPEN_EXISTING,
1821                            dwWriteMode,       /* flags and attributes */
1822                            0);      /* handle to template file */
1823
1824   if (write_pipe == INVALID_HANDLE_VALUE)
1825     {
1826       /* Failure. */
1827       DWORD err = GetLastError ();
1828 #if DEBUG_PIPE
1829       GNUNET_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   GNUNET_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 =
1860     GNUNET_malloc (sizeof (struct GNUNET_DISK_PipeHandle) +
1861                    2 * sizeof (struct GNUNET_DISK_FileHandle));
1862   fds = (struct GNUNET_DISK_FileHandle *) &p[1];
1863   p->fd[0] = &fds[0];
1864   p->fd[1] = &fds[1];
1865 #ifndef MINGW
1866   int fd[2];
1867   int ret;
1868   int flags;
1869   int eno;
1870
1871   ret = pipe (fd);
1872   if (ret == -1)
1873     {
1874       eno = errno;
1875       GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "pipe");
1876       GNUNET_free (p);
1877       errno = eno;
1878       return NULL;
1879     }
1880   p->fd[0]->fd = fd[0];
1881   p->fd[1]->fd = fd[1];
1882   ret = 0;
1883   flags = fcntl (fd[0], F_GETFL);
1884   if (!blocking)
1885     flags |= O_NONBLOCK;
1886   if (0 > fcntl (fd[0], F_SETFL, flags))
1887     ret = -1;
1888   flags = fcntl (fd[0], F_GETFD);
1889   flags |= FD_CLOEXEC;
1890   if (0 > fcntl (fd[0], F_SETFD, flags))
1891     ret = -1;
1892
1893   flags = fcntl (fd[1], F_GETFL);
1894   if (!blocking)
1895     flags |= O_NONBLOCK;
1896   if (0 > fcntl (fd[1], F_SETFL, flags))
1897     ret = -1;
1898   flags = fcntl (fd[1], F_GETFD);
1899   flags |= FD_CLOEXEC;
1900   if (0 > fcntl (fd[1], F_SETFD, flags))
1901     ret = -1;
1902   if (ret == -1)
1903     {
1904       eno = errno;
1905       GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "fcntl");
1906       GNUNET_break (0 == close (p->fd[0]->fd));
1907       GNUNET_break (0 == close (p->fd[1]->fd));
1908       GNUNET_free (p);
1909       errno = eno;
1910       return NULL;    
1911     }
1912 #else
1913   BOOL ret;
1914   HANDLE tmp_handle;
1915
1916   ret = create_selectable_pipe (&p->fd[0]->h, &p->fd[1]->h, NULL, 0, FILE_FLAG_OVERLAPPED, FILE_FLAG_OVERLAPPED);
1917   if (!ret)
1918     {
1919       GNUNET_free (p);
1920       SetErrnoFromWinError (GetLastError ());
1921       return NULL;
1922     }
1923   if (!DuplicateHandle (GetCurrentProcess (), p->fd[0]->h,
1924                 GetCurrentProcess (), &tmp_handle, 0, inherit_read == GNUNET_YES ? TRUE : FALSE,
1925                         DUPLICATE_SAME_ACCESS))
1926         {
1927           SetErrnoFromWinError (GetLastError ());
1928           CloseHandle (p->fd[0]->h);
1929           CloseHandle (p->fd[1]->h);
1930           GNUNET_free (p);
1931           return NULL;
1932         }
1933         CloseHandle (p->fd[0]->h);
1934         p->fd[0]->h = tmp_handle;
1935
1936         if (!DuplicateHandle (GetCurrentProcess (), p->fd[1]->h,
1937                         GetCurrentProcess (), &tmp_handle, 0, inherit_write == GNUNET_YES ? TRUE : FALSE,
1938                         DUPLICATE_SAME_ACCESS))
1939         {
1940           SetErrnoFromWinError (GetLastError ());
1941           CloseHandle (p->fd[0]->h);
1942           CloseHandle (p->fd[1]->h);
1943           GNUNET_free (p);
1944           return NULL;
1945         }
1946   CloseHandle (p->fd[1]->h);
1947   p->fd[1]->h = tmp_handle;
1948   if (!blocking)
1949     {
1950       DWORD mode;
1951
1952       mode = PIPE_NOWAIT;
1953       SetNamedPipeHandleState (p->fd[0]->h, &mode, NULL, NULL);
1954       SetNamedPipeHandleState (p->fd[1]->h, &mode, NULL, NULL);
1955       /* this always fails on Windows 95, so we don't care about error handling */
1956     }
1957   p->fd[0]->type = GNUNET_PIPE;
1958   p->fd[1]->type = GNUNET_PIPE;
1959
1960   p->fd[0]->oOverlapRead = GNUNET_malloc (sizeof (OVERLAPPED));
1961   p->fd[0]->oOverlapWrite = GNUNET_malloc (sizeof (OVERLAPPED));
1962   p->fd[1]->oOverlapRead = GNUNET_malloc (sizeof (OVERLAPPED));
1963   p->fd[1]->oOverlapWrite = GNUNET_malloc (sizeof (OVERLAPPED));
1964
1965   p->fd[0]->oOverlapRead->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
1966   p->fd[0]->oOverlapWrite->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
1967
1968   p->fd[1]->oOverlapRead->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
1969   p->fd[1]->oOverlapWrite->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
1970
1971 #endif
1972   return p;
1973 }
1974
1975
1976 /**
1977  * Closes an interprocess channel
1978  *
1979  * @param p pipe to close
1980  * @param end which end of the pipe to close
1981  * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
1982  */
1983 int
1984 GNUNET_DISK_pipe_close_end (struct GNUNET_DISK_PipeHandle *p,
1985                             enum GNUNET_DISK_PipeEnd end)
1986 {
1987   int ret = GNUNET_OK;
1988   int save;
1989
1990 #ifdef MINGW
1991   if (end == GNUNET_DISK_PIPE_END_READ)
1992     {
1993       if (!CloseHandle (p->fd[0]->h))
1994         {
1995           SetErrnoFromWinError (GetLastError ());
1996           ret = GNUNET_SYSERR;
1997         }
1998       p->fd[0]->h = INVALID_HANDLE_VALUE;
1999     }
2000   else if (end == GNUNET_DISK_PIPE_END_WRITE)
2001     {
2002       if (!CloseHandle (p->fd[1]->h))
2003         {
2004           SetErrnoFromWinError (GetLastError ());
2005           ret = GNUNET_SYSERR;
2006         }
2007       p->fd[1]->h = INVALID_HANDLE_VALUE;
2008     }
2009   save = errno;
2010 #else
2011   save = 0;
2012   if (end == GNUNET_DISK_PIPE_END_READ)
2013     {
2014       if (0 != close (p->fd[0]->fd))
2015         {
2016           ret = GNUNET_SYSERR;
2017           save = errno;
2018         }
2019       p->fd[0]->fd = -1;
2020     }
2021   else if (end == GNUNET_DISK_PIPE_END_WRITE)
2022     {
2023       if (0 != close (p->fd[1]->fd))
2024         {
2025           ret = GNUNET_SYSERR;
2026           save = errno;
2027         }
2028       p->fd[1]->fd = -1;
2029     }
2030 #endif
2031   errno = save;
2032   return ret;
2033 }
2034
2035 /**
2036  * Closes an interprocess channel
2037  *
2038  * @param p pipe to close
2039  * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
2040  */
2041 int
2042 GNUNET_DISK_pipe_close (struct GNUNET_DISK_PipeHandle *p)
2043 {
2044   int ret = GNUNET_OK;
2045   int save;
2046
2047 #ifdef MINGW
2048   if (!CloseHandle (p->fd[0]->h))
2049     {
2050       SetErrnoFromWinError (GetLastError ());
2051       ret = GNUNET_SYSERR;
2052     }
2053   if (!CloseHandle (p->fd[1]->h))
2054     {
2055       SetErrnoFromWinError (GetLastError ());
2056       ret = GNUNET_SYSERR;
2057     }
2058   save = errno;
2059 #else
2060   save = 0;
2061   if (p->fd[0]->fd != -1)
2062     {
2063       if (0 != close (p->fd[0]->fd))
2064         {
2065           ret = GNUNET_SYSERR;
2066           save = errno;
2067         }
2068     }
2069
2070   if (p->fd[1]->fd != -1)
2071     {
2072       if (0 != close (p->fd[1]->fd))
2073         {
2074           ret = GNUNET_SYSERR;
2075           save = errno;
2076         }
2077     }
2078 #endif
2079   GNUNET_free (p);
2080   errno = save;
2081   return ret;
2082 }
2083
2084
2085 /**
2086  * Creates a named pipe/FIFO and opens it
2087  * @param fn pointer to the name of the named pipe or to NULL
2088  * @param flags open flags
2089  * @param perm access permissions
2090  * @return pipe handle on success, NULL on error
2091  */
2092 struct GNUNET_DISK_FileHandle *
2093 GNUNET_DISK_npipe_create (char **fn,
2094                           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       name = NULL;
2118       if (*fn != NULL)
2119         {
2120           GNUNET_asprintf(&name, "\\\\.\\pipe\\%.246s", fn);
2121 #if DEBUG_NPIPE
2122           GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Trying to create an instance of named pipe `%s'\n", name);
2123 #endif
2124           h = CreateNamedPipe (name, openMode | FILE_FLAG_OVERLAPPED,
2125               PIPE_TYPE_BYTE | PIPE_READMODE_BYTE, 2, 1, 1, 0, NULL);
2126         }
2127       else
2128         {
2129           GNUNET_asprintf(fn, "\\\\.\\pipe\\gnunet-%llu",
2130               GNUNET_CRYPTO_random_u64 (GNUNET_CRYPTO_QUALITY_WEAK, UINT64_MAX));
2131 #if DEBUG_NPIPE
2132           GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Trying to create unique named pipe `%s'\n", *fn);
2133 #endif
2134           h = CreateNamedPipe (*fn, openMode | FILE_FLAG_OVERLAPPED | FILE_FLAG_FIRST_PIPE_INSTANCE,
2135               PIPE_TYPE_BYTE | PIPE_READMODE_BYTE, 2, 1, 1, 0, NULL);
2136         }
2137       error_code = GetLastError ();
2138       if (name)
2139           GNUNET_free(name);
2140       /* don't re-set name to NULL yet */
2141       if (h == INVALID_HANDLE_VALUE)
2142         {
2143           SetErrnoFromWinError(error_code);
2144 #if DEBUG_NPIPE
2145           GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Pipe creation have failed because of %d, errno is %d\n", error_code, errno);
2146 #endif
2147           if (name == NULL)
2148             {
2149 #if DEBUG_NPIPE
2150               GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Pipe was to be unique, considering re-creation\n");
2151 #endif
2152               GNUNET_free (*fn);
2153               *fn = NULL;
2154               if (error_code != ERROR_ACCESS_DENIED && error_code != ERROR_PIPE_BUSY)
2155                 {
2156                   return NULL;
2157                 }
2158 #if DEBUG_NPIPE
2159               GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Pipe name was not unique, trying again\n");
2160 #endif
2161               h = NULL;
2162             }
2163           else
2164               return NULL;
2165         }
2166     }
2167   errno = 0;
2168
2169   ret = GNUNET_malloc(sizeof(*ret));
2170   ret->h = h;
2171   ret->type = GNUNET_PIPE;
2172
2173   ret->oOverlapRead = GNUNET_malloc (sizeof (OVERLAPPED));
2174   ret->oOverlapWrite = GNUNET_malloc (sizeof (OVERLAPPED));
2175
2176   ret->oOverlapRead->hEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
2177   ret->oOverlapWrite->hEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
2178
2179   return ret;
2180 #else
2181   if (*fn == NULL)
2182     {
2183       char dir[] = "/tmp/gnunet-pipe-XXXXXX";
2184
2185       if (mkdtemp(dir) == NULL)
2186         {
2187           GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "mkdtemp");
2188           return NULL;
2189         }
2190       GNUNET_asprintf(fn, "%s/child-control", dir);
2191     }
2192
2193   if (mkfifo(*fn, translate_unix_perms(perm)) == -1)
2194     {
2195       if ( (errno != EEXIST) ||
2196            (0 != (flags & GNUNET_DISK_OPEN_FAILIFEXISTS)) )
2197         return NULL;
2198     }
2199
2200   flags = flags & (~GNUNET_DISK_OPEN_FAILIFEXISTS);
2201   return GNUNET_DISK_file_open(*fn, flags, perm);
2202 #endif
2203 }
2204
2205
2206 /**
2207  * Opens already existing named pipe/FIFO
2208  *
2209  * @param fn name of an existing named pipe
2210  * @param flags open flags
2211  * @param perm access permissions
2212  * @return pipe handle on success, NULL on error
2213  */
2214 struct GNUNET_DISK_FileHandle *
2215 GNUNET_DISK_npipe_open (const char *fn,
2216                         enum GNUNET_DISK_OpenFlags flags,
2217                         enum GNUNET_DISK_AccessPermissions perm)
2218 {
2219 #ifdef MINGW
2220   struct GNUNET_DISK_FileHandle *ret;
2221   HANDLE h;
2222   DWORD openMode;
2223
2224   openMode = 0;
2225   if (flags & GNUNET_DISK_OPEN_READWRITE)
2226     openMode = GENERIC_WRITE | GENERIC_READ;
2227   else if (flags & GNUNET_DISK_OPEN_READ)
2228     openMode = GENERIC_READ;
2229   else if (flags & GNUNET_DISK_OPEN_WRITE)
2230     openMode = GENERIC_WRITE;
2231
2232   h = CreateFile (fn, openMode, 0, NULL, OPEN_EXISTING,
2233       FILE_FLAG_OVERLAPPED | FILE_READ_ATTRIBUTES, NULL);
2234   if (h == INVALID_HANDLE_VALUE)
2235     {
2236       SetErrnoFromWinError(GetLastError());
2237       return NULL;
2238     }
2239
2240   ret = GNUNET_malloc(sizeof(*ret));
2241   ret->h = h;
2242   ret->type = GNUNET_PIPE;
2243   ret->oOverlapRead = GNUNET_malloc (sizeof (OVERLAPPED));
2244   ret->oOverlapWrite = GNUNET_malloc (sizeof (OVERLAPPED));
2245   ret->oOverlapRead->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2246   ret->oOverlapWrite->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2247
2248   return ret;
2249 #else
2250   flags = flags & (~GNUNET_DISK_OPEN_FAILIFEXISTS);
2251   return GNUNET_DISK_file_open(fn, flags, perm);
2252 #endif
2253 }
2254
2255 /**
2256  * Closes a named pipe/FIFO
2257  * @param pipe named pipe
2258  * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
2259  */
2260 int
2261 GNUNET_DISK_npipe_close (struct GNUNET_DISK_FileHandle *pipe)
2262 {
2263 #ifndef MINGW
2264   return close(pipe->fd) == 0 ? GNUNET_OK : GNUNET_SYSERR;
2265 #else
2266   BOOL ret;
2267
2268   ret = CloseHandle(pipe->h);
2269   if (!ret)
2270     {
2271       SetErrnoFromWinError(GetLastError());
2272       return GNUNET_SYSERR;
2273     }
2274   else
2275     return GNUNET_OK;
2276 #endif
2277 }
2278
2279
2280 /**
2281  * Get the handle to a particular pipe end
2282  *
2283  * @param p pipe
2284  * @param n end to access
2285  * @return handle for the respective end
2286  */
2287 const struct GNUNET_DISK_FileHandle *
2288 GNUNET_DISK_pipe_handle (const struct GNUNET_DISK_PipeHandle *p,
2289                          enum GNUNET_DISK_PipeEnd n)
2290 {
2291   switch (n)
2292     {
2293     case GNUNET_DISK_PIPE_END_READ:
2294     case GNUNET_DISK_PIPE_END_WRITE:
2295       return p->fd[n];
2296     default:
2297       GNUNET_break (0);
2298       return NULL;
2299     }
2300 }
2301
2302
2303 /**
2304  * Retrieve OS file handle
2305  * @internal
2306  * @param fh GNUnet file descriptor
2307  * @param dst destination buffer
2308  * @param dst_len length of dst
2309  * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
2310  */
2311 int
2312 GNUNET_DISK_internal_file_handle_ (const struct GNUNET_DISK_FileHandle *fh,
2313                                    void *dst, size_t dst_len)
2314 {
2315 #ifdef MINGW
2316   if (dst_len < sizeof (HANDLE))
2317     return GNUNET_SYSERR;
2318   *((HANDLE *) dst) = fh->h;
2319 #else
2320   if (dst_len < sizeof (int))
2321     return GNUNET_SYSERR;
2322   *((int *) dst) = fh->fd;
2323 #endif
2324
2325   return GNUNET_OK;
2326 }
2327
2328 /* end of disk.c */