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