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