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