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