fixfix
[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       if (flags & GNUNET_DISK_OPEN_TRUNCATE)
1373         disp = CREATE_ALWAYS;
1374       else
1375         disp = OPEN_ALWAYS;
1376     }
1377   else if (flags & GNUNET_DISK_OPEN_TRUNCATE)
1378     {
1379       disp = TRUNCATE_EXISTING;
1380     }
1381   else
1382     {
1383       disp = OPEN_EXISTING;
1384     }
1385
1386   /* TODO: access priviledges? */
1387   h = CreateFile (expfn, access, FILE_SHARE_DELETE | FILE_SHARE_READ
1388                   | FILE_SHARE_WRITE, NULL, disp, FILE_ATTRIBUTE_NORMAL,
1389                   NULL);
1390   if (h == INVALID_HANDLE_VALUE)
1391     {
1392       SetErrnoFromWinError (GetLastError ());
1393       GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "open", expfn);
1394       GNUNET_free (expfn);
1395       return NULL;
1396     }
1397
1398   if (flags & GNUNET_DISK_OPEN_APPEND)
1399     if (SetFilePointer (h, 0, 0, FILE_END) == INVALID_SET_FILE_POINTER)
1400       {
1401         SetErrnoFromWinError (GetLastError ());
1402         GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "SetFilePointer",
1403                                   expfn);
1404         CloseHandle (h);
1405         GNUNET_free (expfn);
1406         return NULL;
1407       }
1408 #endif
1409
1410   ret = GNUNET_malloc (sizeof (struct GNUNET_DISK_FileHandle));
1411 #ifdef MINGW
1412   ret->h = h;
1413   ret->type = GNUNET_DISK_FILE;
1414 #else
1415   ret->fd = fd;
1416 #endif
1417   GNUNET_free (expfn);
1418   return ret;
1419 }
1420
1421
1422 /**
1423  * Close an open file
1424  * @param h file handle
1425  * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
1426  */
1427 int
1428 GNUNET_DISK_file_close (struct GNUNET_DISK_FileHandle *h)
1429 {
1430   if (h == NULL)
1431     {
1432       errno = EINVAL;
1433       return GNUNET_SYSERR;
1434     }
1435
1436 #if MINGW
1437   if (!CloseHandle (h->h))
1438     {
1439       SetErrnoFromWinError (GetLastError ());
1440       GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "close");
1441       GNUNET_free (h->oOverlapRead);
1442       GNUNET_free (h->oOverlapWrite);
1443       GNUNET_free (h);
1444       return GNUNET_SYSERR;
1445     }
1446 #else
1447   if (close (h->fd) != 0)
1448     {
1449       GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "close");
1450       GNUNET_free (h);
1451       return GNUNET_SYSERR;
1452     }
1453 #endif
1454   GNUNET_free (h);
1455   return GNUNET_OK;
1456 }
1457
1458
1459 /**
1460  * Construct full path to a file inside of the private
1461  * directory used by GNUnet.  Also creates the corresponding
1462  * directory.  If the resulting name is supposed to be
1463  * a directory, end the last argument in '/' (or pass
1464  * DIR_SEPARATOR_STR as the last argument before NULL).
1465  *
1466  * @param cfg configuration to use (determines HOME)
1467  * @param serviceName name of the service
1468  * @param ... is NULL-terminated list of
1469  *                path components to append to the
1470  *                private directory name.
1471  * @return the constructed filename
1472  */
1473 char *
1474 GNUNET_DISK_get_home_filename (const struct GNUNET_CONFIGURATION_Handle *cfg,
1475                                const char *serviceName, ...)
1476 {
1477   const char *c;
1478   char *pfx;
1479   char *ret;
1480   va_list ap;
1481   unsigned int needed;
1482
1483   if (GNUNET_OK !=
1484       GNUNET_CONFIGURATION_get_value_filename (cfg,
1485                                                serviceName, "HOME", &pfx))
1486     return NULL;
1487   if (pfx == NULL)
1488     {
1489       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1490                   _("No `%s' specified for service `%s' in configuration.\n"),
1491                   "HOME", serviceName);
1492       return NULL;
1493     }
1494   needed = strlen (pfx) + 2;
1495   if ((pfx[strlen (pfx) - 1] != '/') && (pfx[strlen (pfx) - 1] != '\\'))
1496     needed++;
1497   va_start (ap, serviceName);
1498   while (1)
1499     {
1500       c = va_arg (ap, const char *);
1501       if (c == NULL)
1502         break;
1503       needed += strlen (c);
1504       if ((c[strlen (c) - 1] != '/') && (c[strlen (c) - 1] != '\\'))
1505         needed++;
1506     }
1507   va_end (ap);
1508   ret = GNUNET_malloc (needed);
1509   strcpy (ret, pfx);
1510   GNUNET_free (pfx);
1511   va_start (ap, serviceName);
1512   while (1)
1513     {
1514       c = va_arg (ap, const char *);
1515       if (c == NULL)
1516         break;
1517       if ((c[strlen (c) - 1] != '/') && (c[strlen (c) - 1] != '\\'))
1518         strcat (ret, DIR_SEPARATOR_STR);
1519       strcat (ret, c);
1520     }
1521   va_end (ap);
1522   if ((ret[strlen (ret) - 1] != '/') && (ret[strlen (ret) - 1] != '\\'))
1523     (void) GNUNET_DISK_directory_create_for_file (ret);
1524   else
1525     (void) GNUNET_DISK_directory_create (ret);
1526   return ret;
1527 }
1528
1529
1530 /**
1531  * Handle for a memory-mapping operation.
1532  */
1533 struct GNUNET_DISK_MapHandle
1534 {
1535   /**
1536    * Address where the map is in memory.
1537    */
1538   void *addr;
1539
1540 #ifdef MINGW
1541   /**
1542    * Underlying OS handle.
1543    */
1544   HANDLE h;
1545 #else
1546   /**
1547    * Number of bytes mapped.
1548    */
1549   size_t len;
1550 #endif
1551 };
1552
1553
1554 #ifndef MAP_FAILED
1555 #define MAP_FAILED ((void *) -1)
1556 #endif
1557
1558 /**
1559  * Map a file into memory
1560  *
1561  * @param h open file handle
1562  * @param m handle to the new mapping
1563  * @param access access specification, GNUNET_DISK_MAP_TYPE_xxx
1564  * @param len size of the mapping
1565  * @return pointer to the mapped memory region, NULL on failure
1566  */
1567 void *
1568 GNUNET_DISK_file_map (const struct GNUNET_DISK_FileHandle *h,
1569                       struct GNUNET_DISK_MapHandle **m,
1570                       enum GNUNET_DISK_MapType access, size_t len)
1571 {
1572   if (h == NULL)
1573     {
1574       errno = EINVAL;
1575       return NULL;
1576     }
1577
1578 #ifdef MINGW
1579   DWORD mapAccess, protect;
1580
1581   if ((access & GNUNET_DISK_MAP_TYPE_READ) &&
1582       (access & GNUNET_DISK_MAP_TYPE_WRITE))
1583     {
1584       protect = PAGE_READWRITE;
1585       mapAccess = FILE_MAP_ALL_ACCESS;
1586     }
1587   else if (access & GNUNET_DISK_MAP_TYPE_READ)
1588     {
1589       protect = PAGE_READONLY;
1590       mapAccess = FILE_MAP_READ;
1591     }
1592   else if (access & GNUNET_DISK_MAP_TYPE_WRITE)
1593     {
1594       protect = PAGE_READWRITE;
1595       mapAccess = FILE_MAP_WRITE;
1596     }
1597   else
1598     {
1599       GNUNET_break (0);
1600       return NULL;
1601     }
1602
1603   *m = GNUNET_malloc (sizeof (struct GNUNET_DISK_MapHandle));
1604   (*m)->h = CreateFileMapping (h->h, NULL, protect, 0, 0, NULL);
1605   if ((*m)->h == INVALID_HANDLE_VALUE)
1606     {
1607       SetErrnoFromWinError (GetLastError ());
1608       GNUNET_free (*m);
1609       return NULL;
1610     }
1611
1612   (*m)->addr = MapViewOfFile ((*m)->h, mapAccess, 0, 0, len);
1613   if (!(*m)->addr)
1614     {
1615       SetErrnoFromWinError (GetLastError ());
1616       CloseHandle ((*m)->h);
1617       GNUNET_free (*m);
1618     }
1619
1620   return (*m)->addr;
1621 #else
1622   int prot;
1623
1624   prot = 0;
1625   if (access & GNUNET_DISK_MAP_TYPE_READ)
1626     prot = PROT_READ;
1627   if (access & GNUNET_DISK_MAP_TYPE_WRITE)
1628     prot |= PROT_WRITE;
1629   *m = GNUNET_malloc (sizeof (struct GNUNET_DISK_MapHandle));
1630   (*m)->addr = mmap (NULL, len, prot, MAP_SHARED, h->fd, 0);
1631   GNUNET_assert (NULL != (*m)->addr);
1632   if (MAP_FAILED == (*m)->addr)
1633     {    
1634       GNUNET_free (*m);
1635       return NULL;
1636     }
1637   (*m)->len = len;
1638   return (*m)->addr;
1639 #endif
1640 }
1641
1642 /**
1643  * Unmap a file
1644  * @param h mapping handle
1645  * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
1646  */
1647 int
1648 GNUNET_DISK_file_unmap (struct GNUNET_DISK_MapHandle *h)
1649 {
1650   int ret;
1651   if (h == NULL)
1652     {
1653       errno = EINVAL;
1654       return GNUNET_SYSERR;
1655     }
1656
1657 #ifdef MINGW
1658   ret = UnmapViewOfFile (h->addr) ? GNUNET_OK : GNUNET_SYSERR;
1659   if (ret != GNUNET_OK)
1660     SetErrnoFromWinError (GetLastError ());
1661   if (!CloseHandle (h->h) && (ret == GNUNET_OK))
1662     {
1663       ret = GNUNET_SYSERR;
1664       SetErrnoFromWinError (GetLastError ());
1665     }
1666 #else
1667   ret = munmap (h->addr, h->len) != -1 ? GNUNET_OK : GNUNET_SYSERR;
1668 #endif
1669   GNUNET_free (h);
1670   return ret;
1671 }
1672
1673
1674 /**
1675  * Write file changes to disk
1676  * @param h handle to an open file
1677  * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
1678  */
1679 int
1680 GNUNET_DISK_file_sync (const struct GNUNET_DISK_FileHandle *h)
1681 {
1682   if (h == NULL)
1683     {
1684       errno = EINVAL;
1685       return GNUNET_SYSERR;
1686     }
1687
1688 #ifdef MINGW
1689   int ret;
1690
1691   ret = FlushFileBuffers (h->h) ? GNUNET_OK : GNUNET_SYSERR;
1692   if (ret != GNUNET_OK)
1693     SetErrnoFromWinError (GetLastError ());
1694   return ret;
1695 #elif defined(FREEBSD) || defined(OPENBSD) || defined(DARWIN)
1696   return fsync (h->fd) == -1 ? GNUNET_SYSERR : GNUNET_OK;
1697 #else
1698   return fdatasync (h->fd) == -1 ? GNUNET_SYSERR : GNUNET_OK;
1699 #endif
1700 }
1701
1702 #if WINDOWS
1703 /* Copyright Bob Byrnes  <byrnes <at> curl.com>
1704    http://permalink.gmane.org/gmane.os.cygwin.patches/2121
1705 */
1706 /* Create a pipe, and return handles to the read and write ends,
1707    just like CreatePipe, but ensure that the write end permits
1708    FILE_READ_ATTRIBUTES access, on later versions of win32 where
1709    this is supported.  This access is needed by NtQueryInformationFile,
1710    which is used to implement select and nonblocking writes.
1711    Note that the return value is either NO_ERROR or GetLastError,
1712    unlike CreatePipe, which returns a bool for success or failure.  */
1713 static int
1714 create_selectable_pipe (PHANDLE read_pipe_ptr,
1715                         PHANDLE write_pipe_ptr,
1716                         LPSECURITY_ATTRIBUTES sa_ptr,
1717                         DWORD psize,
1718                         DWORD dwReadMode,
1719                         DWORD dwWriteMode)
1720 {
1721   /* Default to error. */
1722   *read_pipe_ptr = *write_pipe_ptr = INVALID_HANDLE_VALUE;
1723
1724   HANDLE read_pipe = INVALID_HANDLE_VALUE, write_pipe = INVALID_HANDLE_VALUE;
1725
1726   /* Ensure that there is enough pipe buffer space for atomic writes.  */
1727   if (psize < PIPE_BUF)
1728     psize = PIPE_BUF;
1729
1730   char pipename[MAX_PATH];
1731
1732   /* Retry CreateNamedPipe as long as the pipe name is in use.
1733      Retrying will probably never be necessary, but we want
1734      to be as robust as possible.  */
1735   while (1)
1736     {
1737       static volatile LONG pipe_unique_id;
1738
1739       snprintf (pipename, sizeof pipename, "\\\\.\\pipe\\gnunet-%d-%ld",
1740                 getpid (), InterlockedIncrement ((LONG *)&pipe_unique_id));
1741 #if DEBUG_PIPE
1742       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "CreateNamedPipe: name = %s, size = %lu\n", pipename, psize);
1743 #endif
1744       /* Use CreateNamedPipe instead of CreatePipe, because the latter
1745          returns a write handle that does not permit FILE_READ_ATTRIBUTES
1746          access, on versions of win32 earlier than WinXP SP2.
1747          CreatePipe also stupidly creates a full duplex pipe, which is
1748          a waste, since only a single direction is actually used.
1749          It's important to only allow a single instance, to ensure that
1750          the pipe was not created earlier by some other process, even if
1751          the pid has been reused.  We avoid FILE_FLAG_FIRST_PIPE_INSTANCE
1752          because that is only available for Win2k SP2 and WinXP.  */
1753       read_pipe = CreateNamedPipeA (pipename,
1754                                    PIPE_ACCESS_INBOUND | dwReadMode,
1755                                    PIPE_TYPE_BYTE | PIPE_READMODE_BYTE,
1756                                    1,       /* max instances */
1757                                    psize,   /* output buffer size */
1758                                    psize,   /* input buffer size */
1759                                    NMPWAIT_USE_DEFAULT_WAIT,
1760                                    sa_ptr);
1761
1762       if (read_pipe != INVALID_HANDLE_VALUE)
1763         {
1764 #if DEBUG_PIPE
1765           GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "pipe read handle = %p\n", read_pipe);
1766 #endif
1767           break;
1768         }
1769
1770       DWORD err = GetLastError ();
1771       switch (err)
1772         {
1773         case ERROR_PIPE_BUSY:
1774           /* The pipe is already open with compatible parameters.
1775              Pick a new name and retry.  */
1776 #if DEBUG_PIPE
1777           GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "pipe busy, retrying\n");
1778 #endif
1779           continue;
1780         case ERROR_ACCESS_DENIED:
1781           /* The pipe is already open with incompatible parameters.
1782              Pick a new name and retry.  */
1783 #if DEBUG_PIPE
1784           GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "pipe access denied, retrying\n");
1785 #endif
1786           continue;
1787         case ERROR_CALL_NOT_IMPLEMENTED:
1788           /* We are on an older Win9x platform without named pipes.
1789              Return an anonymous pipe as the best approximation.  */
1790 #if DEBUG_PIPE
1791           GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "CreateNamedPipe not implemented, resorting to "
1792               "CreatePipe: size = %lu\n", psize);
1793 #endif
1794           if (CreatePipe (read_pipe_ptr, write_pipe_ptr, sa_ptr, psize))
1795             {
1796 #if DEBUG_PIPE
1797               GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "pipe read handle = %p\n", *read_pipe_ptr);
1798               GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "pipe write handle = %p\n", *write_pipe_ptr);
1799 #endif
1800               return GNUNET_OK;
1801             }
1802           err = GetLastError ();
1803           GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "CreatePipe failed: %d\n", err);
1804           return err;
1805         default:
1806           GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "CreateNamedPipe failed: %d\n", err);
1807           return err;
1808         }
1809       /* NOTREACHED */
1810     }
1811 #if DEBUG_PIPE
1812   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "CreateFile: name = %s\n", pipename);
1813 #endif
1814
1815   /* Open the named pipe for writing.
1816      Be sure to permit FILE_READ_ATTRIBUTES access.  */
1817   write_pipe = CreateFileA (pipename,
1818                            GENERIC_WRITE | FILE_READ_ATTRIBUTES,
1819                            0,       /* share mode */
1820                            sa_ptr,
1821                            OPEN_EXISTING,
1822                            dwWriteMode,       /* flags and attributes */
1823                            0);      /* handle to template file */
1824
1825   if (write_pipe == INVALID_HANDLE_VALUE)
1826     {
1827       /* Failure. */
1828       DWORD err = GetLastError ();
1829 #if DEBUG_PIPE
1830       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "CreateFile failed: %d\n", err);
1831 #endif
1832       CloseHandle (read_pipe);
1833       return err;
1834     }
1835 #if DEBUG_PIPE
1836   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "pipe write handle = %p\n", write_pipe);
1837 #endif
1838   /* Success. */
1839   *read_pipe_ptr = read_pipe;
1840   *write_pipe_ptr = write_pipe;
1841   return GNUNET_OK;
1842 }
1843 #endif
1844
1845 /**
1846  * Creates an interprocess channel
1847  *
1848  * @param blocking creates an asynchronous pipe if set to GNUNET_NO
1849  * @param inherit_read inherit the parent processes stdin (only for windows)
1850  * @param inherit_write inherit the parent processes stdout (only for windows)
1851  *
1852  * @return handle to the new pipe, NULL on error
1853  */
1854 struct GNUNET_DISK_PipeHandle *
1855 GNUNET_DISK_pipe (int blocking, int inherit_read, int inherit_write)
1856 {
1857   struct GNUNET_DISK_PipeHandle *p;
1858   struct GNUNET_DISK_FileHandle *fds;
1859
1860   p =
1861     GNUNET_malloc (sizeof (struct GNUNET_DISK_PipeHandle) +
1862                    2 * sizeof (struct GNUNET_DISK_FileHandle));
1863   fds = (struct GNUNET_DISK_FileHandle *) &p[1];
1864   p->fd[0] = &fds[0];
1865   p->fd[1] = &fds[1];
1866 #ifndef MINGW
1867   int fd[2];
1868   int ret;
1869   int flags;
1870   int eno;
1871
1872   ret = pipe (fd);
1873   if (ret == -1)
1874     {
1875       eno = errno;
1876       GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "pipe");
1877       GNUNET_free (p);
1878       errno = eno;
1879       return NULL;
1880     }
1881   p->fd[0]->fd = fd[0];
1882   p->fd[1]->fd = fd[1];
1883   ret = 0;
1884   flags = fcntl (fd[0], F_GETFL);
1885   if (!blocking)
1886     flags |= O_NONBLOCK;
1887   if (0 > fcntl (fd[0], F_SETFL, flags))
1888     ret = -1;
1889   flags = fcntl (fd[0], F_GETFD);
1890   flags |= FD_CLOEXEC;
1891   if (0 > fcntl (fd[0], F_SETFD, flags))
1892     ret = -1;
1893
1894   flags = fcntl (fd[1], F_GETFL);
1895   if (!blocking)
1896     flags |= O_NONBLOCK;
1897   if (0 > fcntl (fd[1], F_SETFL, flags))
1898     ret = -1;
1899   flags = fcntl (fd[1], F_GETFD);
1900   flags |= FD_CLOEXEC;
1901   if (0 > fcntl (fd[1], F_SETFD, flags))
1902     ret = -1;
1903   if (ret == -1)
1904     {
1905       eno = errno;
1906       GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "fcntl");
1907       GNUNET_break (0 == close (p->fd[0]->fd));
1908       GNUNET_break (0 == close (p->fd[1]->fd));
1909       GNUNET_free (p);
1910       errno = eno;
1911       return NULL;    
1912     }
1913 #else
1914   BOOL ret;
1915   HANDLE tmp_handle;
1916
1917   ret = create_selectable_pipe (&p->fd[0]->h, &p->fd[1]->h, NULL, 0, FILE_FLAG_OVERLAPPED, FILE_FLAG_OVERLAPPED);
1918   if (!ret)
1919     {
1920       GNUNET_free (p);
1921       SetErrnoFromWinError (GetLastError ());
1922       return NULL;
1923     }
1924   if (!DuplicateHandle (GetCurrentProcess (), p->fd[0]->h,
1925                 GetCurrentProcess (), &tmp_handle, 0, inherit_read == GNUNET_YES ? TRUE : FALSE,
1926                         DUPLICATE_SAME_ACCESS))
1927         {
1928           SetErrnoFromWinError (GetLastError ());
1929           CloseHandle (p->fd[0]->h);
1930           CloseHandle (p->fd[1]->h);
1931           GNUNET_free (p);
1932           return NULL;
1933         }
1934         CloseHandle (p->fd[0]->h);
1935         p->fd[0]->h = tmp_handle;
1936
1937         if (!DuplicateHandle (GetCurrentProcess (), p->fd[1]->h,
1938                         GetCurrentProcess (), &tmp_handle, 0, inherit_write == GNUNET_YES ? TRUE : FALSE,
1939                         DUPLICATE_SAME_ACCESS))
1940         {
1941           SetErrnoFromWinError (GetLastError ());
1942           CloseHandle (p->fd[0]->h);
1943           CloseHandle (p->fd[1]->h);
1944           GNUNET_free (p);
1945           return NULL;
1946         }
1947   CloseHandle (p->fd[1]->h);
1948   p->fd[1]->h = tmp_handle;
1949   if (!blocking)
1950     {
1951       DWORD mode;
1952
1953       mode = PIPE_NOWAIT;
1954       SetNamedPipeHandleState (p->fd[0]->h, &mode, NULL, NULL);
1955       SetNamedPipeHandleState (p->fd[1]->h, &mode, NULL, NULL);
1956       /* this always fails on Windows 95, so we don't care about error handling */
1957     }
1958   p->fd[0]->type = GNUNET_PIPE;
1959   p->fd[1]->type = GNUNET_PIPE;
1960
1961   p->fd[0]->oOverlapRead = GNUNET_malloc (sizeof (OVERLAPPED));
1962   p->fd[0]->oOverlapWrite = GNUNET_malloc (sizeof (OVERLAPPED));
1963   p->fd[1]->oOverlapRead = GNUNET_malloc (sizeof (OVERLAPPED));
1964   p->fd[1]->oOverlapWrite = GNUNET_malloc (sizeof (OVERLAPPED));
1965
1966   p->fd[0]->oOverlapRead->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
1967   p->fd[0]->oOverlapWrite->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
1968
1969   p->fd[1]->oOverlapRead->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
1970   p->fd[1]->oOverlapWrite->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
1971
1972 #endif
1973   return p;
1974 }
1975
1976
1977 /**
1978  * Closes an interprocess channel
1979  *
1980  * @param p pipe to close
1981  * @param end which end of the pipe to close
1982  * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
1983  */
1984 int
1985 GNUNET_DISK_pipe_close_end (struct GNUNET_DISK_PipeHandle *p,
1986                             enum GNUNET_DISK_PipeEnd end)
1987 {
1988   int ret = GNUNET_OK;
1989   int save;
1990
1991 #ifdef MINGW
1992   if (end == GNUNET_DISK_PIPE_END_READ)
1993     {
1994       if (!CloseHandle (p->fd[0]->h))
1995         {
1996           SetErrnoFromWinError (GetLastError ());
1997           ret = GNUNET_SYSERR;
1998         }
1999       p->fd[0]->h = INVALID_HANDLE_VALUE;
2000     }
2001   else if (end == GNUNET_DISK_PIPE_END_WRITE)
2002     {
2003       if (!CloseHandle (p->fd[1]->h))
2004         {
2005           SetErrnoFromWinError (GetLastError ());
2006           ret = GNUNET_SYSERR;
2007         }
2008       p->fd[1]->h = INVALID_HANDLE_VALUE;
2009     }
2010   save = errno;
2011 #else
2012   save = 0;
2013   if (end == GNUNET_DISK_PIPE_END_READ)
2014     {
2015       if (0 != close (p->fd[0]->fd))
2016         {
2017           ret = GNUNET_SYSERR;
2018           save = errno;
2019         }
2020       p->fd[0]->fd = -1;
2021     }
2022   else if (end == GNUNET_DISK_PIPE_END_WRITE)
2023     {
2024       if (0 != close (p->fd[1]->fd))
2025         {
2026           ret = GNUNET_SYSERR;
2027           save = errno;
2028         }
2029       p->fd[1]->fd = -1;
2030     }
2031 #endif
2032   errno = save;
2033   return ret;
2034 }
2035
2036 /**
2037  * Closes an interprocess channel
2038  *
2039  * @param p pipe to close
2040  * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
2041  */
2042 int
2043 GNUNET_DISK_pipe_close (struct GNUNET_DISK_PipeHandle *p)
2044 {
2045   int ret = GNUNET_OK;
2046   int save;
2047
2048 #ifdef MINGW
2049   if (!CloseHandle (p->fd[0]->h))
2050     {
2051       SetErrnoFromWinError (GetLastError ());
2052       ret = GNUNET_SYSERR;
2053     }
2054   if (!CloseHandle (p->fd[1]->h))
2055     {
2056       SetErrnoFromWinError (GetLastError ());
2057       ret = GNUNET_SYSERR;
2058     }
2059   save = errno;
2060 #else
2061   save = 0;
2062   if (p->fd[0]->fd != -1)
2063     {
2064       if (0 != close (p->fd[0]->fd))
2065         {
2066           ret = GNUNET_SYSERR;
2067           save = errno;
2068         }
2069     }
2070
2071   if (p->fd[1]->fd != -1)
2072     {
2073       if (0 != close (p->fd[1]->fd))
2074         {
2075           ret = GNUNET_SYSERR;
2076           save = errno;
2077         }
2078     }
2079 #endif
2080   GNUNET_free (p);
2081   errno = save;
2082   return ret;
2083 }
2084
2085
2086 /**
2087  * Creates a named pipe/FIFO and opens it
2088  * @param fn pointer to the name of the named pipe or to NULL
2089  * @param flags open flags
2090  * @param perm access permissions
2091  * @return pipe handle on success, NULL on error
2092  */
2093 struct GNUNET_DISK_FileHandle *
2094 GNUNET_DISK_npipe_create (char **fn,
2095                           enum GNUNET_DISK_OpenFlags flags,
2096                           enum GNUNET_DISK_AccessPermissions perm)
2097 {
2098 #ifdef MINGW
2099   struct GNUNET_DISK_FileHandle *ret;
2100   HANDLE h = NULL;
2101   DWORD openMode;
2102   char *name;
2103
2104   openMode = 0;
2105   if (flags & GNUNET_DISK_OPEN_READWRITE)
2106     openMode = PIPE_ACCESS_DUPLEX;
2107   else if (flags & GNUNET_DISK_OPEN_READ)
2108     openMode = PIPE_ACCESS_INBOUND;
2109   else if (flags & GNUNET_DISK_OPEN_WRITE)
2110     openMode = PIPE_ACCESS_OUTBOUND;
2111
2112   if (flags & GNUNET_DISK_OPEN_FAILIFEXISTS)
2113     openMode |= FILE_FLAG_FIRST_PIPE_INSTANCE;
2114
2115   while (h == NULL)
2116     {
2117       DWORD error_code;
2118       name = NULL;
2119       if (*fn != NULL)
2120         {
2121           GNUNET_asprintf(&name, "\\\\.\\pipe\\%.246s", fn);
2122 #if DEBUG_NPIPE
2123           GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Trying to create an instance of named pipe `%s'\n", name);
2124 #endif
2125           h = CreateNamedPipe (name, openMode | FILE_FLAG_OVERLAPPED,
2126               PIPE_TYPE_BYTE | PIPE_READMODE_BYTE, 2, 1, 1, 0, NULL);
2127         }
2128       else
2129         {
2130           GNUNET_asprintf(fn, "\\\\.\\pipe\\gnunet-%llu",
2131               GNUNET_CRYPTO_random_u64 (GNUNET_CRYPTO_QUALITY_WEAK, UINT64_MAX));
2132 #if DEBUG_NPIPE
2133           GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Trying to create unique named pipe `%s'\n", *fn);
2134 #endif
2135           h = CreateNamedPipe (*fn, openMode | FILE_FLAG_OVERLAPPED | FILE_FLAG_FIRST_PIPE_INSTANCE,
2136               PIPE_TYPE_BYTE | PIPE_READMODE_BYTE, 2, 1, 1, 0, NULL);
2137         }
2138       error_code = GetLastError ();
2139       if (name)
2140           GNUNET_free(name);
2141       /* don't re-set name to NULL yet */
2142       if (h == INVALID_HANDLE_VALUE)
2143         {
2144           SetErrnoFromWinError(error_code);
2145 #if DEBUG_NPIPE
2146           GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Pipe creation have failed because of %d, errno is %d\n", error_code, errno);
2147 #endif
2148           if (name == NULL)
2149             {
2150 #if DEBUG_NPIPE
2151               GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Pipe was to be unique, considering re-creation\n");
2152 #endif
2153               GNUNET_free (*fn);
2154               *fn = NULL;
2155               if (error_code != ERROR_ACCESS_DENIED && error_code != ERROR_PIPE_BUSY)
2156                 {
2157                   return NULL;
2158                 }
2159 #if DEBUG_NPIPE
2160               GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Pipe name was not unique, trying again\n");
2161 #endif
2162               h = NULL;
2163             }
2164           else
2165               return NULL;
2166         }
2167     }
2168   errno = 0;
2169
2170   ret = GNUNET_malloc(sizeof(*ret));
2171   ret->h = h;
2172   ret->type = GNUNET_PIPE;
2173
2174   ret->oOverlapRead = GNUNET_malloc (sizeof (OVERLAPPED));
2175   ret->oOverlapWrite = GNUNET_malloc (sizeof (OVERLAPPED));
2176
2177   ret->oOverlapRead->hEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
2178   ret->oOverlapWrite->hEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
2179
2180   return ret;
2181 #else
2182   if (*fn == NULL)
2183     {
2184       char dir[] = "/tmp/gnunet-pipe-XXXXXX";
2185
2186       if (mkdtemp(dir) == NULL)
2187         {
2188           GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "mkdtemp");
2189           return NULL;
2190         }
2191       GNUNET_asprintf(fn, "%s/child-control", dir);
2192     }
2193
2194   if (mkfifo(*fn, translate_unix_perms(perm)) == -1)
2195     {
2196       if ( (errno != EEXIST) ||
2197            (0 != (flags & GNUNET_DISK_OPEN_FAILIFEXISTS)) )
2198         return NULL;
2199     }
2200
2201   flags = flags & (~GNUNET_DISK_OPEN_FAILIFEXISTS);
2202   return GNUNET_DISK_file_open(*fn, flags, perm);
2203 #endif
2204 }
2205
2206
2207 /**
2208  * Opens already existing named pipe/FIFO
2209  *
2210  * @param fn name of an existing named pipe
2211  * @param flags open flags
2212  * @param perm access permissions
2213  * @return pipe handle on success, NULL on error
2214  */
2215 struct GNUNET_DISK_FileHandle *
2216 GNUNET_DISK_npipe_open (const char *fn,
2217                         enum GNUNET_DISK_OpenFlags flags,
2218                         enum GNUNET_DISK_AccessPermissions perm)
2219 {
2220 #ifdef MINGW
2221   struct GNUNET_DISK_FileHandle *ret;
2222   HANDLE h;
2223   DWORD openMode;
2224
2225   openMode = 0;
2226   if (flags & GNUNET_DISK_OPEN_READWRITE)
2227     openMode = GENERIC_WRITE | GENERIC_READ;
2228   else if (flags & GNUNET_DISK_OPEN_READ)
2229     openMode = GENERIC_READ;
2230   else if (flags & GNUNET_DISK_OPEN_WRITE)
2231     openMode = GENERIC_WRITE;
2232
2233   h = CreateFile (fn, openMode, 0, NULL, OPEN_EXISTING,
2234       FILE_FLAG_OVERLAPPED | FILE_READ_ATTRIBUTES, NULL);
2235   if (h == INVALID_HANDLE_VALUE)
2236     {
2237       SetErrnoFromWinError(GetLastError());
2238       return NULL;
2239     }
2240
2241   ret = GNUNET_malloc(sizeof(*ret));
2242   ret->h = h;
2243   ret->type = GNUNET_PIPE;
2244   ret->oOverlapRead = GNUNET_malloc (sizeof (OVERLAPPED));
2245   ret->oOverlapWrite = GNUNET_malloc (sizeof (OVERLAPPED));
2246   ret->oOverlapRead->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2247   ret->oOverlapWrite->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2248
2249   return ret;
2250 #else
2251   flags = flags & (~GNUNET_DISK_OPEN_FAILIFEXISTS);
2252   return GNUNET_DISK_file_open(fn, flags, perm);
2253 #endif
2254 }
2255
2256 /**
2257  * Closes a named pipe/FIFO
2258  * @param pipe named pipe
2259  * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
2260  */
2261 int
2262 GNUNET_DISK_npipe_close (struct GNUNET_DISK_FileHandle *pipe)
2263 {
2264 #ifndef MINGW
2265   return close(pipe->fd) == 0 ? GNUNET_OK : GNUNET_SYSERR;
2266 #else
2267   BOOL ret;
2268
2269   ret = CloseHandle(pipe->h);
2270   if (!ret)
2271     {
2272       SetErrnoFromWinError(GetLastError());
2273       return GNUNET_SYSERR;
2274     }
2275   else
2276     return GNUNET_OK;
2277 #endif
2278 }
2279
2280
2281 /**
2282  * Get the handle to a particular pipe end
2283  *
2284  * @param p pipe
2285  * @param n end to access
2286  * @return handle for the respective end
2287  */
2288 const struct GNUNET_DISK_FileHandle *
2289 GNUNET_DISK_pipe_handle (const struct GNUNET_DISK_PipeHandle *p,
2290                          enum GNUNET_DISK_PipeEnd n)
2291 {
2292   switch (n)
2293     {
2294     case GNUNET_DISK_PIPE_END_READ:
2295     case GNUNET_DISK_PIPE_END_WRITE:
2296       return p->fd[n];
2297     default:
2298       GNUNET_break (0);
2299       return NULL;
2300     }
2301 }
2302
2303
2304 /**
2305  * Retrieve OS file handle
2306  * @internal
2307  * @param fh GNUnet file descriptor
2308  * @param dst destination buffer
2309  * @param dst_len length of dst
2310  * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
2311  */
2312 int
2313 GNUNET_DISK_internal_file_handle_ (const struct GNUNET_DISK_FileHandle *fh,
2314                                    void *dst, size_t dst_len)
2315 {
2316 #ifdef MINGW
2317   if (dst_len < sizeof (HANDLE))
2318     return GNUNET_SYSERR;
2319   *((HANDLE *) dst) = fh->h;
2320 #else
2321   if (dst_len < sizeof (int))
2322     return GNUNET_SYSERR;
2323   *((int *) dst) = fh->fd;
2324 #endif
2325
2326   return GNUNET_OK;
2327 }
2328
2329 /* end of disk.c */