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