indentation
[oweals/gnunet.git] / src / util / disk.c
1 /*
2      This file is part of GNUnet.
3      (C) 2001, 2002, 2005, 2006, 2009 Christian Grothoff (and other contributing authors)
4
5      GNUnet is free software; you can redistribute it and/or modify
6      it under the terms of the GNU General Public License as published
7      by the Free Software Foundation; either version 2, or (at your
8      option) any later version.
9
10      GNUnet is distributed in the hope that it will be useful, but
11      WITHOUT ANY WARRANTY; without even the implied warranty of
12      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13      General Public License for more details.
14
15      You should have received a copy of the GNU General Public License
16      along with GNUnet; see the file COPYING.  If not, write to the
17      Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18      Boston, MA 02111-1307, USA.
19 */
20
21 /**
22  * @file util/disk.c
23  * @brief disk IO convenience methods
24  * @author Christian Grothoff
25  * @author Nils Durner
26  */
27
28 #include "platform.h"
29 #include "gnunet_common.h"
30 #include "gnunet_directories.h"
31 #include "gnunet_disk_lib.h"
32 #include "gnunet_scheduler_lib.h"
33 #include "gnunet_strings_lib.h"
34 #include "gnunet_crypto_lib.h"
35 #include "disk.h"
36
37 #define DEBUG_NPIPE GNUNET_NO
38
39 #define DEBUG_PIPE GNUNET_NO
40
41 /**
42  * Block size for IO for copying files.
43  */
44 #define COPY_BLK_SIZE 65536
45
46
47
48 #if defined(LINUX) || defined(CYGWIN)
49 #include <sys/vfs.h>
50 #else
51 #if defined(SOMEBSD) || defined(DARWIN)
52 #include <sys/param.h>
53 #include <sys/mount.h>
54 #else
55 #ifdef SOLARIS
56 #include <sys/types.h>
57 #include <sys/statvfs.h>
58 #else
59 #ifdef MINGW
60 #ifndef PIPE_BUF
61 #define PIPE_BUF        512
62 ULONG PipeSerialNumber;
63 #endif
64 #define         _IFMT           0170000 /* type of file */
65 #define         _IFLNK          0120000 /* symbolic link */
66 #define  S_ISLNK(m)     (((m)&_IFMT) == _IFLNK)
67 #else
68 #error PORT-ME: need to port statfs (how much space is left on the drive?)
69 #endif
70 #endif
71 #endif
72 #endif
73
74 #if !defined(SOMEBSD) && !defined(DARWIN) && !defined(WINDOWS)
75 #include <wordexp.h>
76 #endif
77 #if LINUX
78 #include <sys/statvfs.h>
79 #endif
80
81
82 /**
83  * Handle used to manage a pipe.
84  */
85 struct GNUNET_DISK_PipeHandle
86 {
87   /**
88    * File descriptors for the pipe.
89    */
90   struct GNUNET_DISK_FileHandle *fd[2];
91 };
92
93
94 /**
95  * Closure for the recursion to determine the file size
96  * of a directory.
97  */
98 struct GetFileSizeData
99 {
100   /**
101    * Set to the total file size.
102    */
103   uint64_t total;
104
105   /**
106    * GNUNET_YES if symbolic links should be included.
107    */
108   int include_sym_links;
109 };
110
111
112 int
113 translate_unix_perms (enum GNUNET_DISK_AccessPermissions perm)
114 {
115   int mode;
116
117   mode = 0;
118   if (perm & GNUNET_DISK_PERM_USER_READ)
119     mode |= S_IRUSR;
120   if (perm & GNUNET_DISK_PERM_USER_WRITE)
121     mode |= S_IWUSR;
122   if (perm & GNUNET_DISK_PERM_USER_EXEC)
123     mode |= S_IXUSR;
124   if (perm & GNUNET_DISK_PERM_GROUP_READ)
125     mode |= S_IRGRP;
126   if (perm & GNUNET_DISK_PERM_GROUP_WRITE)
127     mode |= S_IWGRP;
128   if (perm & GNUNET_DISK_PERM_GROUP_EXEC)
129     mode |= S_IXGRP;
130   if (perm & GNUNET_DISK_PERM_OTHER_READ)
131     mode |= S_IROTH;
132   if (perm & GNUNET_DISK_PERM_OTHER_WRITE)
133     mode |= S_IWOTH;
134   if (perm & GNUNET_DISK_PERM_OTHER_EXEC)
135     mode |= S_IXOTH;
136
137   return mode;
138 }
139
140
141 /**
142  * Iterate over all files in the given directory and 
143  * accumulate their size.
144  *
145  * @param cls closure of type "struct GetFileSizeData"
146  * @param fn current filename we are looking at
147  * @return GNUNET_SYSERR on serious errors, otherwise GNUNET_OK
148  */
149 static int
150 getSizeRec (void *cls, const char *fn)
151 {
152   struct GetFileSizeData *gfsd = cls;
153
154 #ifdef HAVE_STAT64
155   struct stat64 buf;
156 #else
157   struct stat buf;
158 #endif
159
160 #ifdef HAVE_STAT64
161   if (0 != STAT64 (fn, &buf))
162   {
163     GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "stat64", fn);
164     return GNUNET_SYSERR;
165   }
166 #else
167   if (0 != STAT (fn, &buf))
168   {
169     GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "stat", fn);
170     return GNUNET_SYSERR;
171   }
172 #endif
173   if ((!S_ISLNK (buf.st_mode)) || (gfsd->include_sym_links == GNUNET_YES))
174     gfsd->total += buf.st_size;
175   if ((S_ISDIR (buf.st_mode)) &&
176       (0 == ACCESS (fn, X_OK)) &&
177       ((!S_ISLNK (buf.st_mode)) || (gfsd->include_sym_links == GNUNET_YES)))
178   {
179     if (GNUNET_SYSERR == GNUNET_DISK_directory_scan (fn, &getSizeRec, gfsd))
180       return GNUNET_SYSERR;
181   }
182   return GNUNET_OK;
183 }
184
185
186 /**
187  * Checks whether a handle is invalid
188  *
189  * @param h handle to check
190  * @return GNUNET_YES if invalid, GNUNET_NO if valid
191  */
192 int
193 GNUNET_DISK_handle_invalid (const struct GNUNET_DISK_FileHandle *h)
194 {
195 #ifdef MINGW
196   return ((!h) || (h->h == INVALID_HANDLE_VALUE)) ? GNUNET_YES : GNUNET_NO;
197 #else
198   return ((!h) || (h->fd == -1)) ? GNUNET_YES : GNUNET_NO;
199 #endif
200 }
201
202
203 /**
204  * Move the read/write pointer in a file
205  *
206  * @param h handle of an open file
207  * @param offset position to move to
208  * @param whence specification to which position the offset parameter relates to
209  * @return the new position on success, GNUNET_SYSERR otherwise
210  */
211 off_t
212 GNUNET_DISK_file_seek (const struct GNUNET_DISK_FileHandle * h, off_t offset,
213                        enum GNUNET_DISK_Seek whence)
214 {
215   if (h == NULL)
216   {
217     errno = EINVAL;
218     return GNUNET_SYSERR;
219   }
220
221 #ifdef MINGW
222   DWORD ret;
223
224   static DWORD t[] = {[GNUNET_DISK_SEEK_SET] = FILE_BEGIN,
225     [GNUNET_DISK_SEEK_CUR] = FILE_CURRENT,[GNUNET_DISK_SEEK_END] = FILE_END
226   };
227
228   ret = SetFilePointer (h->h, offset, NULL, t[whence]);
229   if (ret == INVALID_SET_FILE_POINTER)
230   {
231     SetErrnoFromWinError (GetLastError ());
232     return GNUNET_SYSERR;
233   }
234   return ret;
235 #else
236   static int t[] = {[GNUNET_DISK_SEEK_SET] = SEEK_SET,
237     [GNUNET_DISK_SEEK_CUR] = SEEK_CUR,[GNUNET_DISK_SEEK_END] = SEEK_END
238   };
239
240   return lseek (h->fd, offset, t[whence]);
241 #endif
242 }
243
244
245 /**
246  * Get the size of the file (or directory) of the given file (in
247  * bytes).
248  *
249  * @param filename name of the file or directory
250  * @param size set to the size of the file (or,
251  *             in the case of directories, the sum
252  *             of all sizes of files in the directory)
253  * @param includeSymLinks should symbolic links be
254  *        included?
255  * @return GNUNET_SYSERR on error, GNUNET_OK on success
256  */
257 int
258 GNUNET_DISK_file_size (const char *filename,
259                        uint64_t * size, int includeSymLinks)
260 {
261   struct GetFileSizeData gfsd;
262   int ret;
263
264   GNUNET_assert (size != NULL);
265   gfsd.total = 0;
266   gfsd.include_sym_links = includeSymLinks;
267   ret = getSizeRec (&gfsd, filename);
268   *size = gfsd.total;
269   return ret;
270 }
271
272
273 /**
274  * Obtain some unique identifiers for the given file
275  * that can be used to identify it in the local system.
276  * This function is used between GNUnet processes to
277  * quickly check if two files with the same absolute path
278  * are actually identical.  The two processes represent
279  * the same peer but may communicate over the network
280  * (and the file may be on an NFS volume).  This function
281  * may not be supported on all operating systems.
282  *
283  * @param filename name of the file
284  * @param dev set to the device ID
285  * @param ino set to the inode ID
286  * @return GNUNET_OK on success
287  */
288 int
289 GNUNET_DISK_file_get_identifiers (const char *filename,
290                                   uint64_t * dev, uint64_t * ino)
291 {
292 #if LINUX
293   struct stat sbuf;
294   struct statvfs fbuf;
295
296   if ((0 == stat (filename, &sbuf)) && (0 == statvfs (filename, &fbuf)))
297   {
298     *dev = (uint64_t) fbuf.f_fsid;
299     *ino = (uint64_t) sbuf.st_ino;
300     return GNUNET_OK;
301   }
302 #elif SOMEBSD
303   struct stat sbuf;
304   struct statfs fbuf;
305
306   if ((0 == stat (filename, &sbuf)) && (0 == statfs (filename, &fbuf)))
307   {
308     *dev = ((uint64_t) fbuf.f_fsid.val[0]) << 32 ||
309         ((uint64_t) fbuf.f_fsid.val[1]);
310     *ino = (uint64_t) sbuf.st_ino;
311     return GNUNET_OK;
312   }
313 #elif WINDOWS
314   // FIXME NILS: test this
315   struct GNUNET_DISK_FileHandle *fh;
316   BY_HANDLE_FILE_INFORMATION info;
317   int succ;
318
319   fh = GNUNET_DISK_file_open (filename, GNUNET_DISK_OPEN_READ, 0);
320   if (fh == NULL)
321     return GNUNET_SYSERR;
322   succ = GetFileInformationByHandle (fh->h, &info);
323   GNUNET_DISK_file_close (fh);
324   if (succ)
325   {
326     *dev = info.dwVolumeSerialNumber;
327     *ino = ((info.nFileIndexHigh << sizeof (DWORD)) | info.nFileIndexLow);
328     return GNUNET_OK;
329   }
330   else
331     return GNUNET_SYSERR;
332
333 #endif
334   return GNUNET_SYSERR;
335 }
336
337
338 /**
339  * Create an (empty) temporary file on disk.  If the given name is not
340  * an absolute path, the current 'TMPDIR' will be prepended.  In any case,
341  * 6 random characters will be appended to the name to create a unique
342  * filename.
343  * 
344  * @param t component to use for the name;
345  *        does NOT contain "XXXXXX" or "/tmp/".
346  * @return NULL on error, otherwise name of fresh
347  *         file on disk in directory for temporary files
348  */
349 char *
350 GNUNET_DISK_mktemp (const char *t)
351 {
352   const char *tmpdir;
353   int fd;
354   char *tmpl;
355   char *fn;
356
357   if ((t[0] != '/') && (t[0] != '\\')
358 #if WINDOWS
359       && !(isalpha ((int) t[0]) && (t[0] != '\0') && (t[1] == ':'))
360 #endif
361       )
362   {
363     tmpdir = getenv ("TMPDIR");
364     tmpdir = tmpdir ? tmpdir : "/tmp";
365     GNUNET_asprintf (&tmpl, "%s/%s%s", tmpdir, t, "XXXXXX");
366   }
367   else
368   {
369     GNUNET_asprintf (&tmpl, "%s%s", t, "XXXXXX");
370   }
371 #ifdef MINGW
372   fn = (char *) GNUNET_malloc (MAX_PATH + 1);
373   if (ERROR_SUCCESS != plibc_conv_to_win_path (tmpl, fn))
374   {
375     GNUNET_free (fn);
376     GNUNET_free (tmpl);
377     return NULL;
378   }
379   GNUNET_free (tmpl);
380 #else
381   fn = tmpl;
382 #endif
383   fd = mkstemp (fn);
384   if (fd == -1)
385   {
386     GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR, "mkstemp", fn);
387     GNUNET_free (fn);
388     return NULL;
389   }
390   if (0 != CLOSE (fd))
391     GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "close", fn);
392   return fn;
393 }
394
395
396 /**
397  * Get the number of blocks that are left on the partition that
398  * contains the given file (for normal users).
399  *
400  * @param part a file on the partition to check
401  * @return -1 on errors, otherwise the number of free blocks
402  */
403 long
404 GNUNET_DISK_get_blocks_available (const char *part)
405 {
406 #ifdef SOLARIS
407   struct statvfs buf;
408
409   if (0 != statvfs (part, &buf))
410   {
411     GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "statfs", part);
412     return -1;
413   }
414   return buf.f_bavail;
415 #elif MINGW
416   DWORD dwDummy;
417   DWORD dwBlocks;
418   char szDrive[4];
419   char *path;
420
421   path = GNUNET_STRINGS_filename_expand (part);
422   if (path == NULL)
423     return -1;
424   memcpy (szDrive, path, 3);
425   GNUNET_free (path);
426   szDrive[3] = 0;
427   if (!GetDiskFreeSpace (szDrive, &dwDummy, &dwDummy, &dwBlocks, &dwDummy))
428   {
429     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
430                 _("`%s' failed for drive `%s': %u\n"),
431                 "GetDiskFreeSpace", szDrive, GetLastError ());
432
433     return -1;
434   }
435   return dwBlocks;
436 #else
437   struct statfs s;
438
439   if (0 != statfs (part, &s))
440   {
441     GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "statfs", part);
442     return -1;
443   }
444   return s.f_bavail;
445 #endif
446 }
447
448
449 /**
450  * Test if "fil" is a directory.
451  * Will not print an error message if the directory
452  * does not exist.  Will log errors if GNUNET_SYSERR is
453  * returned (i.e., a file exists with the same name).
454  *
455  * @param fil filename to test
456  * @return GNUNET_YES if yes, GNUNET_NO if not, GNUNET_SYSERR if it
457  *   does not exist
458  */
459 int
460 GNUNET_DISK_directory_test (const char *fil)
461 {
462   struct stat filestat;
463   int ret;
464
465   ret = STAT (fil, &filestat);
466   if (ret != 0)
467   {
468     if (errno != ENOENT)
469     {
470       GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "stat", fil);
471       return GNUNET_SYSERR;
472     }
473     return GNUNET_NO;
474   }
475   if (!S_ISDIR (filestat.st_mode))
476     return GNUNET_NO;
477   if (ACCESS (fil, R_OK | X_OK) < 0)
478   {
479     GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "access", fil);
480     return GNUNET_SYSERR;
481   }
482   return GNUNET_YES;
483 }
484
485 /**
486  * Check that fil corresponds to a filename
487  * (of a file that exists and that is not a directory).
488  *
489  * @param fil filename to check
490  * @return GNUNET_YES if yes, GNUNET_NO if not a file, GNUNET_SYSERR if something
491  * else (will print an error message in that case, too).
492  */
493 int
494 GNUNET_DISK_file_test (const char *fil)
495 {
496   struct stat filestat;
497   int ret;
498   char *rdir;
499
500   rdir = GNUNET_STRINGS_filename_expand (fil);
501   if (rdir == NULL)
502     return GNUNET_SYSERR;
503
504   ret = STAT (rdir, &filestat);
505   if (ret != 0)
506   {
507     if (errno != ENOENT)
508     {
509       GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "stat", rdir);
510       GNUNET_free (rdir);
511       return GNUNET_SYSERR;
512     }
513     GNUNET_free (rdir);
514     return GNUNET_NO;
515   }
516   if (!S_ISREG (filestat.st_mode))
517   {
518     GNUNET_free (rdir);
519     return GNUNET_NO;
520   }
521   if (ACCESS (rdir, R_OK) < 0)
522   {
523     GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "access", rdir);
524     GNUNET_free (rdir);
525     return GNUNET_SYSERR;
526   }
527   GNUNET_free (rdir);
528   return GNUNET_YES;
529 }
530
531
532 /**
533  * Implementation of "mkdir -p"
534  * @param dir the directory to create
535  * @returns GNUNET_OK on success, GNUNET_SYSERR on failure
536  */
537 int
538 GNUNET_DISK_directory_create (const char *dir)
539 {
540   char *rdir;
541   int len;
542   int pos;
543   int ret = GNUNET_OK;
544
545   rdir = GNUNET_STRINGS_filename_expand (dir);
546   if (rdir == NULL)
547     return GNUNET_SYSERR;
548
549   len = strlen (rdir);
550 #ifndef MINGW
551   pos = 1;                      /* skip heading '/' */
552 #else
553   /* Local or Network path? */
554   if (strncmp (rdir, "\\\\", 2) == 0)
555   {
556     pos = 2;
557     while (rdir[pos])
558     {
559       if (rdir[pos] == '\\')
560       {
561         pos++;
562         break;
563       }
564       pos++;
565     }
566   }
567   else
568   {
569     pos = 3;                    /* strlen("C:\\") */
570   }
571 #endif
572   while (pos <= len)
573   {
574     if ((rdir[pos] == DIR_SEPARATOR) || (pos == len))
575     {
576       rdir[pos] = '\0';
577       ret = GNUNET_DISK_directory_test (rdir);
578       if (ret == GNUNET_SYSERR)
579       {
580         GNUNET_free (rdir);
581         return GNUNET_SYSERR;
582       }
583       if (ret == GNUNET_NO)
584       {
585 #ifndef MINGW
586         ret = mkdir (rdir, S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH);        /* 755 */
587 #else
588         ret = mkdir (rdir);
589 #endif
590         if ((ret != 0) && (errno != EEXIST))
591         {
592           GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR, "mkdir", rdir);
593           GNUNET_free (rdir);
594           return GNUNET_SYSERR;
595         }
596       }
597       rdir[pos] = DIR_SEPARATOR;
598     }
599     pos++;
600   }
601   GNUNET_free (rdir);
602   return GNUNET_OK;
603 }
604
605
606 /**
607  * Create the directory structure for storing
608  * a file.
609  *
610  * @param filename name of a file in the directory
611  * @returns GNUNET_OK on success,
612  *          GNUNET_SYSERR on failure,
613  *          GNUNET_NO if the directory
614  *          exists but is not writeable for us
615  */
616 int
617 GNUNET_DISK_directory_create_for_file (const char *filename)
618 {
619   char *rdir;
620   int len;
621   int ret;
622
623   rdir = GNUNET_STRINGS_filename_expand (filename);
624   if (rdir == NULL)
625     return GNUNET_SYSERR;
626   len = strlen (rdir);
627   while ((len > 0) && (rdir[len] != DIR_SEPARATOR))
628     len--;
629   rdir[len] = '\0';
630   ret = GNUNET_DISK_directory_create (rdir);
631   if ((ret == GNUNET_OK) && (0 != ACCESS (rdir, W_OK)))
632     ret = GNUNET_NO;
633   GNUNET_free (rdir);
634   return ret;
635 }
636
637
638 /**
639  * Read the contents of a binary file into a buffer.
640  * @param h handle to an open file
641  * @param result the buffer to write the result to
642  * @param len the maximum number of bytes to read
643  * @return the number of bytes read on success, GNUNET_SYSERR on failure
644  */
645 ssize_t
646 GNUNET_DISK_file_read (const struct GNUNET_DISK_FileHandle * h, void *result,
647                        size_t len)
648 {
649   if (h == NULL)
650   {
651     errno = EINVAL;
652     return GNUNET_SYSERR;
653   }
654
655 #ifdef MINGW
656   DWORD bytesRead;
657
658   if (h->type != GNUNET_PIPE)
659   {
660     if (!ReadFile (h->h, result, len, &bytesRead, NULL))
661     {
662       SetErrnoFromWinError (GetLastError ());
663       return GNUNET_SYSERR;
664     }
665   }
666   else
667   {
668     if (!ReadFile (h->h, result, len, NULL, h->oOverlapRead))
669     {
670       if (GetLastError () != ERROR_IO_PENDING)
671       {
672         SetErrnoFromWinError (GetLastError ());
673         return GNUNET_SYSERR;
674       }
675     }
676     GetOverlappedResult (h->h, h->oOverlapRead, &bytesRead, TRUE);
677   }
678   return bytesRead;
679 #else
680   return read (h->fd, result, len);
681 #endif
682 }
683
684
685 /**
686  * Read the contents of a binary file into a buffer.
687  *
688  * @param fn file name
689  * @param result the buffer to write the result to
690  * @param len the maximum number of bytes to read
691  * @return number of bytes read, GNUNET_SYSERR on failure
692  */
693 ssize_t
694 GNUNET_DISK_fn_read (const char *fn, void *result, size_t len)
695 {
696   struct GNUNET_DISK_FileHandle *fh;
697   ssize_t ret;
698
699   fh = GNUNET_DISK_file_open (fn, GNUNET_DISK_OPEN_READ, GNUNET_DISK_PERM_NONE);
700   if (!fh)
701     return GNUNET_SYSERR;
702   ret = GNUNET_DISK_file_read (fh, result, len);
703   GNUNET_assert (GNUNET_OK == GNUNET_DISK_file_close (fh));
704
705   return ret;
706 }
707
708
709 /**
710  * Write a buffer to a file.
711  * @param h handle to open file
712  * @param buffer the data to write
713  * @param n number of bytes to write
714  * @return number of bytes written on success, GNUNET_SYSERR on error
715  */
716 ssize_t
717 GNUNET_DISK_file_write (const struct GNUNET_DISK_FileHandle * h,
718                         const void *buffer, size_t n)
719 {
720   if (h == NULL)
721   {
722     errno = EINVAL;
723     return GNUNET_SYSERR;
724   }
725
726 #ifdef MINGW
727   DWORD bytesWritten;
728
729   if (h->type != GNUNET_PIPE)
730   {
731     if (!WriteFile (h->h, buffer, n, &bytesWritten, NULL))
732     {
733       SetErrnoFromWinError (GetLastError ());
734       return GNUNET_SYSERR;
735     }
736   }
737   else
738   {
739 #if DEBUG_PIPE
740     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "It is a pipe trying to write\n");
741 #endif
742     if (!WriteFile (h->h, buffer, n, NULL, h->oOverlapWrite))
743     {
744       if (GetLastError () != ERROR_IO_PENDING)
745       {
746         SetErrnoFromWinError (GetLastError ());
747 #if DEBUG_PIPE
748         GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Error writing to pipe\n");
749 #endif
750         return GNUNET_SYSERR;
751       }
752     }
753 #if DEBUG_PIPE
754     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Will get overlapped result\n");
755 #endif
756     GetOverlappedResult (h->h, h->oOverlapWrite, &bytesWritten, TRUE);
757   }
758   return bytesWritten;
759 #else
760   return write (h->fd, buffer, n);
761 #endif
762 }
763
764 /**
765  * Write a buffer to a file.  If the file is longer than the
766  * number of bytes that will be written, it will be truncated.
767  *
768  * @param fn file name
769  * @param buffer the data to write
770  * @param n number of bytes to write
771  * @param mode file permissions 
772  * @return number of bytes written on success, GNUNET_SYSERR on error
773  */
774 ssize_t
775 GNUNET_DISK_fn_write (const char *fn, const void *buffer,
776                       size_t n, enum GNUNET_DISK_AccessPermissions mode)
777 {
778   struct GNUNET_DISK_FileHandle *fh;
779   ssize_t ret;
780
781   fh = GNUNET_DISK_file_open (fn,
782                               GNUNET_DISK_OPEN_WRITE
783                               | GNUNET_DISK_OPEN_TRUNCATE
784                               | GNUNET_DISK_OPEN_CREATE, mode);
785   if (!fh)
786     return GNUNET_SYSERR;
787   ret = GNUNET_DISK_file_write (fh, buffer, n);
788   GNUNET_assert (GNUNET_OK == GNUNET_DISK_file_close (fh));
789   return ret;
790 }
791
792 /**
793  * Scan a directory for files. 
794  *
795  * @param dirName the name of the directory
796  * @param callback the method to call for each file,
797  *        can be NULL, in that case, we only count
798  * @param callback_cls closure for callback
799  * @return the number of files found, GNUNET_SYSERR on error or
800  *         ieration aborted by callback returning GNUNET_SYSERR
801  */
802 int
803 GNUNET_DISK_directory_scan (const char *dirName,
804                             GNUNET_FileNameCallback callback,
805                             void *callback_cls)
806 {
807   DIR *dinfo;
808   struct dirent *finfo;
809   struct stat istat;
810   int count = 0;
811   char *name;
812   char *dname;
813   unsigned int name_len;
814   unsigned int n_size;
815
816   GNUNET_assert (dirName != NULL);
817   dname = GNUNET_STRINGS_filename_expand (dirName);
818   if (dname == NULL)
819     return GNUNET_SYSERR;
820   while ((strlen (dname) > 0) && (dname[strlen (dname) - 1] == DIR_SEPARATOR))
821     dname[strlen (dname) - 1] = '\0';
822   if (0 != STAT (dname, &istat))
823   {
824     GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "stat", dname);
825     GNUNET_free (dname);
826     return GNUNET_SYSERR;
827   }
828   if (!S_ISDIR (istat.st_mode))
829   {
830     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
831                 _("Expected `%s' to be a directory!\n"), dirName);
832     GNUNET_free (dname);
833     return GNUNET_SYSERR;
834   }
835   errno = 0;
836   dinfo = OPENDIR (dname);
837   if ((errno == EACCES) || (dinfo == NULL))
838   {
839     GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "opendir", dname);
840     if (dinfo != NULL)
841       closedir (dinfo);
842     GNUNET_free (dname);
843     return GNUNET_SYSERR;
844   }
845   name_len = 256;
846   n_size = strlen (dname) + name_len + 2;
847   name = GNUNET_malloc (n_size);
848   while ((finfo = readdir (dinfo)) != NULL)
849   {
850     if ((0 == strcmp (finfo->d_name, ".")) ||
851         (0 == strcmp (finfo->d_name, "..")))
852       continue;
853     if (callback != NULL)
854     {
855       if (name_len < strlen (finfo->d_name))
856       {
857         GNUNET_free (name);
858         name_len = strlen (finfo->d_name);
859         n_size = strlen (dname) + name_len + 2;
860         name = GNUNET_malloc (n_size);
861       }
862       /* dname can end in "/" only if dname == "/";
863        * if dname does not end in "/", we need to add
864        * a "/" (otherwise, we must not!) */
865       GNUNET_snprintf (name,
866                        n_size,
867                        "%s%s%s",
868                        dname,
869                        (strcmp (dname, DIR_SEPARATOR_STR) ==
870                         0) ? "" : DIR_SEPARATOR_STR, finfo->d_name);
871       if (GNUNET_OK != callback (callback_cls, name))
872       {
873         closedir (dinfo);
874         GNUNET_free (name);
875         GNUNET_free (dname);
876         return GNUNET_SYSERR;
877       }
878     }
879     count++;
880   }
881   closedir (dinfo);
882   GNUNET_free (name);
883   GNUNET_free (dname);
884   return count;
885 }
886
887
888 /**
889  * Opaque handle used for iterating over a directory.
890  */
891 struct GNUNET_DISK_DirectoryIterator
892 {
893
894   /**
895    * Function to call on directory entries.
896    */
897   GNUNET_DISK_DirectoryIteratorCallback callback;
898
899   /**
900    * Closure for callback.
901    */
902   void *callback_cls;
903
904   /**
905    * Reference to directory.
906    */
907   DIR *directory;
908
909   /**
910    * Directory name.
911    */
912   char *dirname;
913
914   /**
915    * Next filename to process.
916    */
917   char *next_name;
918
919   /**
920    * Our priority.
921    */
922   enum GNUNET_SCHEDULER_Priority priority;
923
924 };
925
926
927 /**
928  * Task used by the directory iterator.
929  */
930 static void
931 directory_iterator_task (void *cls,
932                          const struct GNUNET_SCHEDULER_TaskContext *tc)
933 {
934   struct GNUNET_DISK_DirectoryIterator *iter = cls;
935   char *name;
936
937   name = iter->next_name;
938   GNUNET_assert (name != NULL);
939   iter->next_name = NULL;
940   iter->callback (iter->callback_cls, iter, name, iter->dirname);
941   GNUNET_free (name);
942 }
943
944
945 /**
946  * This function must be called during the DiskIteratorCallback
947  * (exactly once) to schedule the task to process the next
948  * filename in the directory (if there is one).
949  *
950  * @param iter opaque handle for the iterator
951  * @param can set to GNUNET_YES to terminate the iteration early
952  * @return GNUNET_YES if iteration will continue,
953  *         GNUNET_NO if this was the last entry (and iteration is complete),
954  *         GNUNET_SYSERR if abort was YES
955  */
956 int
957 GNUNET_DISK_directory_iterator_next (struct GNUNET_DISK_DirectoryIterator
958                                      *iter, int can)
959 {
960   struct dirent *finfo;
961
962   GNUNET_assert (iter->next_name == NULL);
963   if (can == GNUNET_YES)
964   {
965     closedir (iter->directory);
966     GNUNET_free (iter->dirname);
967     GNUNET_free (iter);
968     return GNUNET_SYSERR;
969   }
970   while (NULL != (finfo = readdir (iter->directory)))
971   {
972     if ((0 == strcmp (finfo->d_name, ".")) ||
973         (0 == strcmp (finfo->d_name, "..")))
974       continue;
975     GNUNET_asprintf (&iter->next_name,
976                      "%s%s%s", 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, &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
1305 #ifdef MINGW
1306   DWORD access;
1307   DWORD disp;
1308   HANDLE h;
1309 #else
1310   int oflags;
1311   int mode;
1312   int fd;
1313 #endif
1314
1315   expfn = GNUNET_STRINGS_filename_expand (fn);
1316   if (NULL == expfn)
1317     return NULL;
1318 #ifndef MINGW
1319   mode = 0;
1320   if (GNUNET_DISK_OPEN_READWRITE == (flags & GNUNET_DISK_OPEN_READWRITE))
1321     oflags = O_RDWR;            /* note: O_RDWR is NOT always O_RDONLY | O_WRONLY */
1322   else if (flags & GNUNET_DISK_OPEN_READ)
1323     oflags = O_RDONLY;
1324   else if (flags & GNUNET_DISK_OPEN_WRITE)
1325     oflags = O_WRONLY;
1326   else
1327   {
1328     GNUNET_break (0);
1329     GNUNET_free (expfn);
1330     return NULL;
1331   }
1332   if (flags & GNUNET_DISK_OPEN_FAILIFEXISTS)
1333     oflags |= (O_CREAT | O_EXCL);
1334   if (flags & GNUNET_DISK_OPEN_TRUNCATE)
1335     oflags |= O_TRUNC;
1336   if (flags & GNUNET_DISK_OPEN_APPEND)
1337     oflags |= O_APPEND;
1338   if (flags & GNUNET_DISK_OPEN_CREATE)
1339   {
1340     (void) GNUNET_DISK_directory_create_for_file (expfn);
1341     oflags |= O_CREAT;
1342     mode = translate_unix_perms (perm);
1343   }
1344
1345   fd = open (expfn, oflags | O_LARGEFILE, mode);
1346   if (fd == -1)
1347   {
1348     if (0 == (flags & GNUNET_DISK_OPEN_FAILIFEXISTS))
1349       GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "open", expfn);
1350     else
1351       GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_DEBUG, "open", expfn);
1352     GNUNET_free (expfn);
1353     return NULL;
1354   }
1355 #else
1356   access = 0;
1357   disp = OPEN_ALWAYS;
1358
1359   if (GNUNET_DISK_OPEN_READWRITE == (flags & GNUNET_DISK_OPEN_READWRITE))
1360     access = FILE_READ_DATA | FILE_WRITE_DATA;
1361   else if (flags & GNUNET_DISK_OPEN_READ)
1362     access = FILE_READ_DATA;
1363   else if (flags & GNUNET_DISK_OPEN_WRITE)
1364     access = FILE_WRITE_DATA;
1365
1366   if (flags & GNUNET_DISK_OPEN_FAILIFEXISTS)
1367   {
1368     disp = CREATE_NEW;
1369   }
1370   else if (flags & GNUNET_DISK_OPEN_CREATE)
1371   {
1372     (void) GNUNET_DISK_directory_create_for_file (expfn);
1373     if (flags & GNUNET_DISK_OPEN_TRUNCATE)
1374       disp = CREATE_ALWAYS;
1375     else
1376       disp = OPEN_ALWAYS;
1377   }
1378   else if (flags & GNUNET_DISK_OPEN_TRUNCATE)
1379   {
1380     disp = TRUNCATE_EXISTING;
1381   }
1382   else
1383   {
1384     disp = OPEN_EXISTING;
1385   }
1386
1387   /* TODO: access priviledges? */
1388   h = CreateFile (expfn, access, FILE_SHARE_DELETE | FILE_SHARE_READ
1389                   | FILE_SHARE_WRITE, NULL, disp, FILE_ATTRIBUTE_NORMAL, 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, 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
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
1516     if (c == NULL)
1517       break;
1518     if ((c[strlen (c) - 1] != '/') && (c[strlen (c) - 1] != '\\'))
1519       strcat (ret, DIR_SEPARATOR_STR);
1520     strcat (ret, c);
1521   }
1522   va_end (ap);
1523   if ((ret[strlen (ret) - 1] != '/') && (ret[strlen (ret) - 1] != '\\'))
1524     (void) GNUNET_DISK_directory_create_for_file (ret);
1525   else
1526     (void) GNUNET_DISK_directory_create (ret);
1527   return ret;
1528 }
1529
1530
1531 /**
1532  * Handle for a memory-mapping operation.
1533  */
1534 struct GNUNET_DISK_MapHandle
1535 {
1536   /**
1537    * Address where the map is in memory.
1538    */
1539   void *addr;
1540
1541 #ifdef MINGW
1542   /**
1543    * Underlying OS handle.
1544    */
1545   HANDLE h;
1546 #else
1547   /**
1548    * Number of bytes mapped.
1549    */
1550   size_t len;
1551 #endif
1552 };
1553
1554
1555 #ifndef MAP_FAILED
1556 #define MAP_FAILED ((void *) -1)
1557 #endif
1558
1559 /**
1560  * Map a file into memory
1561  *
1562  * @param h open file handle
1563  * @param m handle to the new mapping
1564  * @param access access specification, GNUNET_DISK_MAP_TYPE_xxx
1565  * @param len size of the mapping
1566  * @return pointer to the mapped memory region, NULL on failure
1567  */
1568 void *
1569 GNUNET_DISK_file_map (const struct GNUNET_DISK_FileHandle *h,
1570                       struct GNUNET_DISK_MapHandle **m,
1571                       enum GNUNET_DISK_MapType access, size_t len)
1572 {
1573   if (h == NULL)
1574   {
1575     errno = EINVAL;
1576     return NULL;
1577   }
1578
1579 #ifdef MINGW
1580   DWORD mapAccess, protect;
1581
1582   if ((access & GNUNET_DISK_MAP_TYPE_READ) &&
1583       (access & GNUNET_DISK_MAP_TYPE_WRITE))
1584   {
1585     protect = PAGE_READWRITE;
1586     mapAccess = FILE_MAP_ALL_ACCESS;
1587   }
1588   else if (access & GNUNET_DISK_MAP_TYPE_READ)
1589   {
1590     protect = PAGE_READONLY;
1591     mapAccess = FILE_MAP_READ;
1592   }
1593   else if (access & GNUNET_DISK_MAP_TYPE_WRITE)
1594   {
1595     protect = PAGE_READWRITE;
1596     mapAccess = FILE_MAP_WRITE;
1597   }
1598   else
1599   {
1600     GNUNET_break (0);
1601     return NULL;
1602   }
1603
1604   *m = GNUNET_malloc (sizeof (struct GNUNET_DISK_MapHandle));
1605   (*m)->h = CreateFileMapping (h->h, NULL, protect, 0, 0, NULL);
1606   if ((*m)->h == INVALID_HANDLE_VALUE)
1607   {
1608     SetErrnoFromWinError (GetLastError ());
1609     GNUNET_free (*m);
1610     return NULL;
1611   }
1612
1613   (*m)->addr = MapViewOfFile ((*m)->h, mapAccess, 0, 0, len);
1614   if (!(*m)->addr)
1615   {
1616     SetErrnoFromWinError (GetLastError ());
1617     CloseHandle ((*m)->h);
1618     GNUNET_free (*m);
1619   }
1620
1621   return (*m)->addr;
1622 #else
1623   int prot;
1624
1625   prot = 0;
1626   if (access & GNUNET_DISK_MAP_TYPE_READ)
1627     prot = PROT_READ;
1628   if (access & GNUNET_DISK_MAP_TYPE_WRITE)
1629     prot |= PROT_WRITE;
1630   *m = GNUNET_malloc (sizeof (struct GNUNET_DISK_MapHandle));
1631   (*m)->addr = mmap (NULL, len, prot, MAP_SHARED, h->fd, 0);
1632   GNUNET_assert (NULL != (*m)->addr);
1633   if (MAP_FAILED == (*m)->addr)
1634   {
1635     GNUNET_free (*m);
1636     return NULL;
1637   }
1638   (*m)->len = len;
1639   return (*m)->addr;
1640 #endif
1641 }
1642
1643 /**
1644  * Unmap a file
1645  * @param h mapping handle
1646  * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
1647  */
1648 int
1649 GNUNET_DISK_file_unmap (struct GNUNET_DISK_MapHandle *h)
1650 {
1651   int ret;
1652
1653   if (h == NULL)
1654   {
1655     errno = EINVAL;
1656     return GNUNET_SYSERR;
1657   }
1658
1659 #ifdef MINGW
1660   ret = UnmapViewOfFile (h->addr) ? GNUNET_OK : GNUNET_SYSERR;
1661   if (ret != GNUNET_OK)
1662     SetErrnoFromWinError (GetLastError ());
1663   if (!CloseHandle (h->h) && (ret == GNUNET_OK))
1664   {
1665     ret = GNUNET_SYSERR;
1666     SetErrnoFromWinError (GetLastError ());
1667   }
1668 #else
1669   ret = munmap (h->addr, h->len) != -1 ? GNUNET_OK : GNUNET_SYSERR;
1670 #endif
1671   GNUNET_free (h);
1672   return ret;
1673 }
1674
1675
1676 /**
1677  * Write file changes to disk
1678  * @param h handle to an open file
1679  * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
1680  */
1681 int
1682 GNUNET_DISK_file_sync (const struct GNUNET_DISK_FileHandle *h)
1683 {
1684   if (h == NULL)
1685   {
1686     errno = EINVAL;
1687     return GNUNET_SYSERR;
1688   }
1689
1690 #ifdef MINGW
1691   int ret;
1692
1693   ret = FlushFileBuffers (h->h) ? GNUNET_OK : GNUNET_SYSERR;
1694   if (ret != GNUNET_OK)
1695     SetErrnoFromWinError (GetLastError ());
1696   return ret;
1697 #elif defined(FREEBSD) || defined(OPENBSD) || defined(DARWIN)
1698   return fsync (h->fd) == -1 ? GNUNET_SYSERR : GNUNET_OK;
1699 #else
1700   return fdatasync (h->fd) == -1 ? GNUNET_SYSERR : GNUNET_OK;
1701 #endif
1702 }
1703
1704 #if WINDOWS
1705 /* Copyright Bob Byrnes  <byrnes <at> curl.com>
1706    http://permalink.gmane.org/gmane.os.cygwin.patches/2121
1707 */
1708 /* Create a pipe, and return handles to the read and write ends,
1709    just like CreatePipe, but ensure that the write end permits
1710    FILE_READ_ATTRIBUTES access, on later versions of win32 where
1711    this is supported.  This access is needed by NtQueryInformationFile,
1712    which is used to implement select and nonblocking writes.
1713    Note that the return value is either NO_ERROR or GetLastError,
1714    unlike CreatePipe, which returns a bool for success or failure.  */
1715 static int
1716 create_selectable_pipe (PHANDLE read_pipe_ptr,
1717                         PHANDLE write_pipe_ptr,
1718                         LPSECURITY_ATTRIBUTES sa_ptr,
1719                         DWORD psize, DWORD dwReadMode, 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,
1743                 "CreateNamedPipe: name = %s, size = %lu\n", pipename, psize);
1744 #endif
1745     /* Use CreateNamedPipe instead of CreatePipe, because the latter
1746      * returns a write handle that does not permit FILE_READ_ATTRIBUTES
1747      * access, on versions of win32 earlier than WinXP SP2.
1748      * CreatePipe also stupidly creates a full duplex pipe, which is
1749      * a waste, since only a single direction is actually used.
1750      * It's important to only allow a single instance, to ensure that
1751      * the pipe was not created earlier by some other process, even if
1752      * the pid has been reused.  We avoid FILE_FLAG_FIRST_PIPE_INSTANCE
1753      * because that is only available for Win2k SP2 and WinXP.  */
1754     read_pipe = CreateNamedPipeA (pipename, PIPE_ACCESS_INBOUND | dwReadMode, PIPE_TYPE_BYTE | PIPE_READMODE_BYTE, 1,   /* max instances */
1755                                   psize,        /* output buffer size */
1756                                   psize,        /* input buffer size */
1757                                   NMPWAIT_USE_DEFAULT_WAIT, sa_ptr);
1758
1759     if (read_pipe != INVALID_HANDLE_VALUE)
1760     {
1761 #if DEBUG_PIPE
1762       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "pipe read handle = %p\n",
1763                   read_pipe);
1764 #endif
1765       break;
1766     }
1767
1768     DWORD err = GetLastError ();
1769
1770     switch (err)
1771     {
1772     case ERROR_PIPE_BUSY:
1773       /* The pipe is already open with compatible parameters.
1774        * Pick a new name and retry.  */
1775 #if DEBUG_PIPE
1776       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,
1791                   "CreateNamedPipe not implemented, resorting to "
1792                   "CreatePipe: size = %lu\n", psize);
1793 #endif
1794       if (CreatePipe (read_pipe_ptr, write_pipe_ptr, sa_ptr, psize))
1795       {
1796 #if DEBUG_PIPE
1797         GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "pipe read handle = %p\n",
1798                     *read_pipe_ptr);
1799         GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "pipe write handle = %p\n",
1800                     *write_pipe_ptr);
1801 #endif
1802         return GNUNET_OK;
1803       }
1804       err = GetLastError ();
1805       GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "CreatePipe failed: %d\n", err);
1806       return err;
1807     default:
1808       GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "CreateNamedPipe failed: %d\n", err);
1809       return err;
1810     }
1811     /* NOTREACHED */
1812   }
1813 #if DEBUG_PIPE
1814   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "CreateFile: name = %s\n", pipename);
1815 #endif
1816
1817   /* Open the named pipe for writing.
1818    * Be sure to permit FILE_READ_ATTRIBUTES access.  */
1819   write_pipe = CreateFileA (pipename, GENERIC_WRITE | FILE_READ_ATTRIBUTES, 0,  /* share mode */
1820                             sa_ptr, OPEN_EXISTING, dwWriteMode, /* flags and attributes */
1821                             0); /* handle to template file */
1822
1823   if (write_pipe == INVALID_HANDLE_VALUE)
1824   {
1825     /* Failure. */
1826     DWORD err = GetLastError ();
1827
1828 #if DEBUG_PIPE
1829     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 = GNUNET_malloc (sizeof (struct GNUNET_DISK_PipeHandle) +
1860                      2 * sizeof (struct GNUNET_DISK_FileHandle));
1861   fds = (struct GNUNET_DISK_FileHandle *) &p[1];
1862   p->fd[0] = &fds[0];
1863   p->fd[1] = &fds[1];
1864 #ifndef MINGW
1865   int fd[2];
1866   int ret;
1867   int flags;
1868   int eno;
1869
1870   ret = pipe (fd);
1871   if (ret == -1)
1872   {
1873     eno = errno;
1874     GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "pipe");
1875     GNUNET_free (p);
1876     errno = eno;
1877     return NULL;
1878   }
1879   p->fd[0]->fd = fd[0];
1880   p->fd[1]->fd = fd[1];
1881   ret = 0;
1882   flags = fcntl (fd[0], F_GETFL);
1883   if (!blocking)
1884     flags |= O_NONBLOCK;
1885   if (0 > fcntl (fd[0], F_SETFL, flags))
1886     ret = -1;
1887   flags = fcntl (fd[0], F_GETFD);
1888   flags |= FD_CLOEXEC;
1889   if (0 > fcntl (fd[0], F_SETFD, flags))
1890     ret = -1;
1891
1892   flags = fcntl (fd[1], F_GETFL);
1893   if (!blocking)
1894     flags |= O_NONBLOCK;
1895   if (0 > fcntl (fd[1], F_SETFL, flags))
1896     ret = -1;
1897   flags = fcntl (fd[1], F_GETFD);
1898   flags |= FD_CLOEXEC;
1899   if (0 > fcntl (fd[1], F_SETFD, flags))
1900     ret = -1;
1901   if (ret == -1)
1902   {
1903     eno = errno;
1904     GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "fcntl");
1905     GNUNET_break (0 == close (p->fd[0]->fd));
1906     GNUNET_break (0 == close (p->fd[1]->fd));
1907     GNUNET_free (p);
1908     errno = eno;
1909     return NULL;
1910   }
1911 #else
1912   BOOL ret;
1913   HANDLE tmp_handle;
1914
1915   ret =
1916       create_selectable_pipe (&p->fd[0]->h, &p->fd[1]->h, NULL, 0,
1917                               FILE_FLAG_OVERLAPPED, FILE_FLAG_OVERLAPPED);
1918   if (!ret)
1919   {
1920     GNUNET_free (p);
1921     SetErrnoFromWinError (GetLastError ());
1922     return NULL;
1923   }
1924   if (!DuplicateHandle (GetCurrentProcess (), p->fd[0]->h,
1925                         GetCurrentProcess (), &tmp_handle, 0,
1926                         inherit_read == GNUNET_YES ? TRUE : FALSE,
1927                         DUPLICATE_SAME_ACCESS))
1928   {
1929     SetErrnoFromWinError (GetLastError ());
1930     CloseHandle (p->fd[0]->h);
1931     CloseHandle (p->fd[1]->h);
1932     GNUNET_free (p);
1933     return NULL;
1934   }
1935   CloseHandle (p->fd[0]->h);
1936   p->fd[0]->h = tmp_handle;
1937
1938   if (!DuplicateHandle (GetCurrentProcess (), p->fd[1]->h,
1939                         GetCurrentProcess (), &tmp_handle, 0,
1940                         inherit_write == GNUNET_YES ? TRUE : FALSE,
1941                         DUPLICATE_SAME_ACCESS))
1942   {
1943     SetErrnoFromWinError (GetLastError ());
1944     CloseHandle (p->fd[0]->h);
1945     CloseHandle (p->fd[1]->h);
1946     GNUNET_free (p);
1947     return NULL;
1948   }
1949   CloseHandle (p->fd[1]->h);
1950   p->fd[1]->h = tmp_handle;
1951   if (!blocking)
1952   {
1953     DWORD mode;
1954
1955     mode = PIPE_NOWAIT;
1956     SetNamedPipeHandleState (p->fd[0]->h, &mode, NULL, NULL);
1957     SetNamedPipeHandleState (p->fd[1]->h, &mode, NULL, NULL);
1958     /* this always fails on Windows 95, so we don't care about error handling */
1959   }
1960   p->fd[0]->type = GNUNET_PIPE;
1961   p->fd[1]->type = GNUNET_PIPE;
1962
1963   p->fd[0]->oOverlapRead = GNUNET_malloc (sizeof (OVERLAPPED));
1964   p->fd[0]->oOverlapWrite = GNUNET_malloc (sizeof (OVERLAPPED));
1965   p->fd[1]->oOverlapRead = GNUNET_malloc (sizeof (OVERLAPPED));
1966   p->fd[1]->oOverlapWrite = GNUNET_malloc (sizeof (OVERLAPPED));
1967
1968   p->fd[0]->oOverlapRead->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
1969   p->fd[0]->oOverlapWrite->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
1970
1971   p->fd[1]->oOverlapRead->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
1972   p->fd[1]->oOverlapWrite->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
1973
1974 #endif
1975   return p;
1976 }
1977
1978
1979 /**
1980  * Closes an interprocess channel
1981  *
1982  * @param p pipe to close
1983  * @param end which end of the pipe to close
1984  * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
1985  */
1986 int
1987 GNUNET_DISK_pipe_close_end (struct GNUNET_DISK_PipeHandle *p,
1988                             enum GNUNET_DISK_PipeEnd end)
1989 {
1990   int ret = GNUNET_OK;
1991   int save;
1992
1993 #ifdef MINGW
1994   if (end == GNUNET_DISK_PIPE_END_READ)
1995   {
1996     if (!CloseHandle (p->fd[0]->h))
1997     {
1998       SetErrnoFromWinError (GetLastError ());
1999       ret = GNUNET_SYSERR;
2000     }
2001     p->fd[0]->h = INVALID_HANDLE_VALUE;
2002   }
2003   else if (end == GNUNET_DISK_PIPE_END_WRITE)
2004   {
2005     if (!CloseHandle (p->fd[1]->h))
2006     {
2007       SetErrnoFromWinError (GetLastError ());
2008       ret = GNUNET_SYSERR;
2009     }
2010     p->fd[1]->h = INVALID_HANDLE_VALUE;
2011   }
2012   save = errno;
2013 #else
2014   save = 0;
2015   if (end == GNUNET_DISK_PIPE_END_READ)
2016   {
2017     if (0 != close (p->fd[0]->fd))
2018     {
2019       ret = GNUNET_SYSERR;
2020       save = errno;
2021     }
2022     p->fd[0]->fd = -1;
2023   }
2024   else if (end == GNUNET_DISK_PIPE_END_WRITE)
2025   {
2026     if (0 != close (p->fd[1]->fd))
2027     {
2028       ret = GNUNET_SYSERR;
2029       save = errno;
2030     }
2031     p->fd[1]->fd = -1;
2032   }
2033 #endif
2034   errno = save;
2035   return ret;
2036 }
2037
2038 /**
2039  * Closes an interprocess channel
2040  *
2041  * @param p pipe to close
2042  * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
2043  */
2044 int
2045 GNUNET_DISK_pipe_close (struct GNUNET_DISK_PipeHandle *p)
2046 {
2047   int ret = GNUNET_OK;
2048   int save;
2049
2050 #ifdef MINGW
2051   if (!CloseHandle (p->fd[0]->h))
2052   {
2053     SetErrnoFromWinError (GetLastError ());
2054     ret = GNUNET_SYSERR;
2055   }
2056   if (!CloseHandle (p->fd[1]->h))
2057   {
2058     SetErrnoFromWinError (GetLastError ());
2059     ret = GNUNET_SYSERR;
2060   }
2061   save = errno;
2062 #else
2063   save = 0;
2064   if (p->fd[0]->fd != -1)
2065   {
2066     if (0 != close (p->fd[0]->fd))
2067     {
2068       ret = GNUNET_SYSERR;
2069       save = errno;
2070     }
2071   }
2072
2073   if (p->fd[1]->fd != -1)
2074   {
2075     if (0 != close (p->fd[1]->fd))
2076     {
2077       ret = GNUNET_SYSERR;
2078       save = errno;
2079     }
2080   }
2081 #endif
2082   GNUNET_free (p);
2083   errno = save;
2084   return ret;
2085 }
2086
2087
2088 /**
2089  * Creates a named pipe/FIFO and opens it
2090  * @param fn pointer to the name of the named pipe or to NULL
2091  * @param flags open flags
2092  * @param perm access permissions
2093  * @return pipe handle on success, NULL on error
2094  */
2095 struct GNUNET_DISK_FileHandle *
2096 GNUNET_DISK_npipe_create (char **fn,
2097                           enum GNUNET_DISK_OpenFlags flags,
2098                           enum GNUNET_DISK_AccessPermissions perm)
2099 {
2100 #ifdef MINGW
2101   struct GNUNET_DISK_FileHandle *ret;
2102   HANDLE h = NULL;
2103   DWORD openMode;
2104   char *name;
2105
2106   openMode = 0;
2107   if (flags & GNUNET_DISK_OPEN_READWRITE)
2108     openMode = PIPE_ACCESS_DUPLEX;
2109   else if (flags & GNUNET_DISK_OPEN_READ)
2110     openMode = PIPE_ACCESS_INBOUND;
2111   else if (flags & GNUNET_DISK_OPEN_WRITE)
2112     openMode = PIPE_ACCESS_OUTBOUND;
2113
2114   if (flags & GNUNET_DISK_OPEN_FAILIFEXISTS)
2115     openMode |= FILE_FLAG_FIRST_PIPE_INSTANCE;
2116
2117   while (h == NULL)
2118   {
2119     DWORD error_code;
2120
2121     name = NULL;
2122     if (*fn != NULL)
2123     {
2124       GNUNET_asprintf (&name, "\\\\.\\pipe\\%.246s", fn);
2125 #if DEBUG_NPIPE
2126       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2127                   "Trying to create an instance of named pipe `%s'\n", name);
2128 #endif
2129       h = CreateNamedPipe (name, openMode | FILE_FLAG_OVERLAPPED,
2130                            PIPE_TYPE_BYTE | PIPE_READMODE_BYTE, 2, 1, 1, 0,
2131                            NULL);
2132     }
2133     else
2134     {
2135       GNUNET_asprintf (fn, "\\\\.\\pipe\\gnunet-%llu",
2136                        GNUNET_CRYPTO_random_u64 (GNUNET_CRYPTO_QUALITY_WEAK,
2137                                                  UINT64_MAX));
2138 #if DEBUG_NPIPE
2139       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2140                   "Trying to create unique named pipe `%s'\n", *fn);
2141 #endif
2142       h = CreateNamedPipe (*fn,
2143                            openMode | FILE_FLAG_OVERLAPPED |
2144                            FILE_FLAG_FIRST_PIPE_INSTANCE,
2145                            PIPE_TYPE_BYTE | PIPE_READMODE_BYTE, 2, 1, 1, 0,
2146                            NULL);
2147     }
2148     error_code = GetLastError ();
2149     if (name)
2150       GNUNET_free (name);
2151     /* don't re-set name to NULL yet */
2152     if (h == INVALID_HANDLE_VALUE)
2153     {
2154       SetErrnoFromWinError (error_code);
2155 #if DEBUG_NPIPE
2156       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2157                   "Pipe creation have failed because of %d, errno is %d\n",
2158                   error_code, errno);
2159 #endif
2160       if (name == NULL)
2161       {
2162 #if DEBUG_NPIPE
2163         GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2164                     "Pipe was to be unique, considering re-creation\n");
2165 #endif
2166         GNUNET_free (*fn);
2167         *fn = NULL;
2168         if (error_code != ERROR_ACCESS_DENIED && error_code != ERROR_PIPE_BUSY)
2169         {
2170           return NULL;
2171         }
2172 #if DEBUG_NPIPE
2173         GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2174                     "Pipe name was not unique, trying again\n");
2175 #endif
2176         h = NULL;
2177       }
2178       else
2179         return NULL;
2180     }
2181   }
2182   errno = 0;
2183
2184   ret = GNUNET_malloc (sizeof (*ret));
2185   ret->h = h;
2186   ret->type = GNUNET_PIPE;
2187
2188   ret->oOverlapRead = GNUNET_malloc (sizeof (OVERLAPPED));
2189   ret->oOverlapWrite = GNUNET_malloc (sizeof (OVERLAPPED));
2190
2191   ret->oOverlapRead->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2192   ret->oOverlapWrite->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2193
2194   return ret;
2195 #else
2196   if (*fn == NULL)
2197   {
2198     char dir[] = "/tmp/gnunet-pipe-XXXXXX";
2199
2200     if (mkdtemp (dir) == NULL)
2201     {
2202       GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "mkdtemp");
2203       return NULL;
2204     }
2205     GNUNET_asprintf (fn, "%s/child-control", dir);
2206   }
2207
2208   if (mkfifo (*fn, translate_unix_perms (perm)) == -1)
2209   {
2210     if ((errno != EEXIST) || (0 != (flags & GNUNET_DISK_OPEN_FAILIFEXISTS)))
2211       return NULL;
2212   }
2213
2214   flags = flags & (~GNUNET_DISK_OPEN_FAILIFEXISTS);
2215   return GNUNET_DISK_file_open (*fn, flags, perm);
2216 #endif
2217 }
2218
2219
2220 /**
2221  * Opens already existing named pipe/FIFO
2222  *
2223  * @param fn name of an existing named pipe
2224  * @param flags open flags
2225  * @param perm access permissions
2226  * @return pipe handle on success, NULL on error
2227  */
2228 struct GNUNET_DISK_FileHandle *
2229 GNUNET_DISK_npipe_open (const char *fn,
2230                         enum GNUNET_DISK_OpenFlags flags,
2231                         enum GNUNET_DISK_AccessPermissions perm)
2232 {
2233 #ifdef MINGW
2234   struct GNUNET_DISK_FileHandle *ret;
2235   HANDLE h;
2236   DWORD openMode;
2237
2238   openMode = 0;
2239   if (flags & GNUNET_DISK_OPEN_READWRITE)
2240     openMode = GENERIC_WRITE | GENERIC_READ;
2241   else if (flags & GNUNET_DISK_OPEN_READ)
2242     openMode = GENERIC_READ;
2243   else if (flags & GNUNET_DISK_OPEN_WRITE)
2244     openMode = GENERIC_WRITE;
2245
2246   h = CreateFile (fn, openMode, 0, NULL, OPEN_EXISTING,
2247                   FILE_FLAG_OVERLAPPED | FILE_READ_ATTRIBUTES, NULL);
2248   if (h == INVALID_HANDLE_VALUE)
2249   {
2250     SetErrnoFromWinError (GetLastError ());
2251     return NULL;
2252   }
2253
2254   ret = GNUNET_malloc (sizeof (*ret));
2255   ret->h = h;
2256   ret->type = GNUNET_PIPE;
2257   ret->oOverlapRead = GNUNET_malloc (sizeof (OVERLAPPED));
2258   ret->oOverlapWrite = GNUNET_malloc (sizeof (OVERLAPPED));
2259   ret->oOverlapRead->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2260   ret->oOverlapWrite->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2261
2262   return ret;
2263 #else
2264   flags = flags & (~GNUNET_DISK_OPEN_FAILIFEXISTS);
2265   return GNUNET_DISK_file_open (fn, flags, perm);
2266 #endif
2267 }
2268
2269 /**
2270  * Closes a named pipe/FIFO
2271  * @param pipe named pipe
2272  * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
2273  */
2274 int
2275 GNUNET_DISK_npipe_close (struct GNUNET_DISK_FileHandle *pipe)
2276 {
2277 #ifndef MINGW
2278   return close (pipe->fd) == 0 ? GNUNET_OK : GNUNET_SYSERR;
2279 #else
2280   BOOL ret;
2281
2282   ret = CloseHandle (pipe->h);
2283   if (!ret)
2284   {
2285     SetErrnoFromWinError (GetLastError ());
2286     return GNUNET_SYSERR;
2287   }
2288   else
2289     return GNUNET_OK;
2290 #endif
2291 }
2292
2293
2294 /**
2295  * Get the handle to a particular pipe end
2296  *
2297  * @param p pipe
2298  * @param n end to access
2299  * @return handle for the respective end
2300  */
2301 const struct GNUNET_DISK_FileHandle *
2302 GNUNET_DISK_pipe_handle (const struct GNUNET_DISK_PipeHandle *p,
2303                          enum GNUNET_DISK_PipeEnd n)
2304 {
2305   switch (n)
2306   {
2307   case GNUNET_DISK_PIPE_END_READ:
2308   case GNUNET_DISK_PIPE_END_WRITE:
2309     return p->fd[n];
2310   default:
2311     GNUNET_break (0);
2312     return NULL;
2313   }
2314 }
2315
2316
2317 /**
2318  * Retrieve OS file handle
2319  * @internal
2320  * @param fh GNUnet file descriptor
2321  * @param dst destination buffer
2322  * @param dst_len length of dst
2323  * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
2324  */
2325 int
2326 GNUNET_DISK_internal_file_handle_ (const struct GNUNET_DISK_FileHandle *fh,
2327                                    void *dst, size_t dst_len)
2328 {
2329 #ifdef MINGW
2330   if (dst_len < sizeof (HANDLE))
2331     return GNUNET_SYSERR;
2332   *((HANDLE *) dst) = fh->h;
2333 #else
2334   if (dst_len < sizeof (int))
2335     return GNUNET_SYSERR;
2336   *((int *) dst) = fh->fd;
2337 #endif
2338
2339   return GNUNET_OK;
2340 }
2341
2342 /* end of disk.c */