paragraph for gnunet devs that don't know how to use the web
[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   return fn;
609 }
610
611
612 /**
613  * Test if @a fil is a directory and listable. Optionally, also check if the
614  * directory is readable.  Will not print an error message if the directory does
615  * not exist.  Will log errors if #GNUNET_SYSERR is returned (i.e., a file exists
616  * with the same name).
617  *
618  * @param fil filename to test
619  * @param is_readable #GNUNET_YES to additionally check if @a fil is readable;
620  *          #GNUNET_NO to disable this check
621  * @return #GNUNET_YES if yes, #GNUNET_NO if not; #GNUNET_SYSERR if it
622  *           does not exist or stat'ed
623  */
624 int
625 GNUNET_DISK_directory_test (const char *fil, int is_readable)
626 {
627   struct stat filestat;
628   int ret;
629
630   ret = STAT (fil, &filestat);
631   if (ret != 0)
632   {
633     if (errno != ENOENT)
634       LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "stat", fil);
635     return GNUNET_SYSERR;
636   }
637   if (!S_ISDIR (filestat.st_mode))
638   {
639     LOG (GNUNET_ERROR_TYPE_DEBUG,
640          "A file already exits with the same name %s\n", fil);
641     return GNUNET_NO;
642   }
643   if (GNUNET_YES == is_readable)
644     ret = ACCESS (fil, R_OK | X_OK);
645   else
646     ret = ACCESS (fil, X_OK);
647   if (ret < 0)
648   {
649     LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "access", fil);
650     return GNUNET_NO;
651   }
652   return GNUNET_YES;
653 }
654
655
656 /**
657  * Check that fil corresponds to a filename
658  * (of a file that exists and that is not a directory).
659  *
660  * @param fil filename to check
661  * @return #GNUNET_YES if yes, #GNUNET_NO if not a file, #GNUNET_SYSERR if something
662  * else (will print an error message in that case, too).
663  */
664 int
665 GNUNET_DISK_file_test (const char *fil)
666 {
667   struct stat filestat;
668   int ret;
669   char *rdir;
670
671   rdir = GNUNET_STRINGS_filename_expand (fil);
672   if (rdir == NULL)
673     return GNUNET_SYSERR;
674
675   ret = STAT (rdir, &filestat);
676   if (ret != 0)
677   {
678     if (errno != ENOENT)
679     {
680       LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "stat", rdir);
681       GNUNET_free (rdir);
682       return GNUNET_SYSERR;
683     }
684     GNUNET_free (rdir);
685     return GNUNET_NO;
686   }
687   if (!S_ISREG (filestat.st_mode))
688   {
689     GNUNET_free (rdir);
690     return GNUNET_NO;
691   }
692   if (ACCESS (rdir, F_OK) < 0)
693   {
694     LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "access", rdir);
695     GNUNET_free (rdir);
696     return GNUNET_SYSERR;
697   }
698   GNUNET_free (rdir);
699   return GNUNET_YES;
700 }
701
702
703 /**
704  * Implementation of "mkdir -p"
705  *
706  * @param dir the directory to create
707  * @return #GNUNET_OK on success, #GNUNET_SYSERR on failure
708  */
709 int
710 GNUNET_DISK_directory_create (const char *dir)
711 {
712   char *rdir;
713   unsigned int len;
714   unsigned int pos;
715   unsigned int pos2;
716   int ret = GNUNET_OK;
717
718   rdir = GNUNET_STRINGS_filename_expand (dir);
719   if (rdir == NULL)
720     return GNUNET_SYSERR;
721
722   len = strlen (rdir);
723 #ifndef MINGW
724   pos = 1;                      /* skip heading '/' */
725 #else
726   /* Local or Network path? */
727   if (strncmp (rdir, "\\\\", 2) == 0)
728   {
729     pos = 2;
730     while (rdir[pos])
731     {
732       if (rdir[pos] == '\\')
733       {
734         pos++;
735         break;
736       }
737       pos++;
738     }
739   }
740   else
741   {
742     pos = 3;                    /* strlen("C:\\") */
743   }
744 #endif
745   /* Check which low level directories already exist */
746   pos2 = len;
747   rdir[len] = DIR_SEPARATOR;
748   while (pos <= pos2)
749   {
750     if (DIR_SEPARATOR == rdir[pos2])
751     {
752       rdir[pos2] = '\0';
753       ret = GNUNET_DISK_directory_test (rdir, GNUNET_NO);
754       if (GNUNET_NO == ret)
755       {
756         GNUNET_free (rdir);
757         return GNUNET_SYSERR;
758       }
759       rdir[pos2] = DIR_SEPARATOR;
760       if (GNUNET_YES == ret)
761       {
762         pos2++;
763         break;
764       }
765     }
766     pos2--;
767   }
768   rdir[len] = '\0';
769   if (pos < pos2)
770     pos = pos2;
771   /* Start creating directories */
772   while (pos <= len)
773   {
774     if ((rdir[pos] == DIR_SEPARATOR) || (pos == len))
775     {
776       rdir[pos] = '\0';
777       ret = GNUNET_DISK_directory_test (rdir, GNUNET_NO);
778       if (GNUNET_NO == ret)
779       {
780         GNUNET_free (rdir);
781         return GNUNET_SYSERR;
782       }
783       if (GNUNET_SYSERR == ret)
784       {
785 #ifndef MINGW
786         ret = mkdir (rdir, S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH);        /* 755 */
787 #else
788         wchar_t wrdir[MAX_PATH + 1];
789         if (ERROR_SUCCESS == plibc_conv_to_win_pathwconv(rdir, wrdir))
790           ret = !CreateDirectoryW (wrdir, NULL);
791         else
792           ret = 1;
793 #endif
794         if ((ret != 0) && (errno != EEXIST))
795         {
796           LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_ERROR, "mkdir", rdir);
797           GNUNET_free (rdir);
798           return GNUNET_SYSERR;
799         }
800       }
801       rdir[pos] = DIR_SEPARATOR;
802     }
803     pos++;
804   }
805   GNUNET_free (rdir);
806   return GNUNET_OK;
807 }
808
809
810 /**
811  * Create the directory structure for storing a file.
812  *
813  * @param filename name of a file in the directory
814  * @returns #GNUNET_OK on success,
815  *          #GNUNET_SYSERR on failure,
816  *          #GNUNET_NO if the directory
817  *          exists but is not writeable for us
818  */
819 int
820 GNUNET_DISK_directory_create_for_file (const char *filename)
821 {
822   char *rdir;
823   size_t len;
824   int ret;
825   int eno;
826
827   rdir = GNUNET_STRINGS_filename_expand (filename);
828   if (NULL == rdir)
829   {
830     errno = EINVAL;
831     return GNUNET_SYSERR;
832   }
833   len = strlen (rdir);
834   while ((len > 0) && (rdir[len] != DIR_SEPARATOR))
835     len--;
836   rdir[len] = '\0';
837   /* The empty path is invalid and in this case refers to / */
838   if (0 == len)
839   {
840     GNUNET_free (rdir);
841     rdir = GNUNET_strdup ("/");
842   }
843   ret = GNUNET_DISK_directory_create (rdir);
844   if ((GNUNET_OK == ret) && (0 != ACCESS (rdir, W_OK)))
845     ret = GNUNET_NO;
846   eno = errno;
847   GNUNET_free (rdir);
848   errno = eno;
849   return ret;
850 }
851
852
853 /**
854  * Read the contents of a binary file into a buffer.
855  *
856  * @param h handle to an open file
857  * @param result the buffer to write the result to
858  * @param len the maximum number of bytes to read
859  * @return the number of bytes read on success, #GNUNET_SYSERR on failure
860  */
861 ssize_t
862 GNUNET_DISK_file_read (const struct GNUNET_DISK_FileHandle *h,
863                        void *result,
864                        size_t len)
865 {
866   if (NULL == h)
867   {
868     errno = EINVAL;
869     return GNUNET_SYSERR;
870   }
871
872 #ifdef MINGW
873   DWORD bytes_read;
874
875   if (h->type == GNUNET_DISK_HANLDE_TYPE_FILE)
876   {
877     if (!ReadFile (h->h, result, len, &bytes_read, NULL))
878     {
879       SetErrnoFromWinError (GetLastError ());
880       return GNUNET_SYSERR;
881     }
882   }
883   else if (h->type == GNUNET_DISK_HANLDE_TYPE_PIPE)
884   {
885     if (!ReadFile (h->h, result, len, &bytes_read, h->oOverlapRead))
886     {
887       if (GetLastError () != ERROR_IO_PENDING)
888       {
889         LOG (GNUNET_ERROR_TYPE_DEBUG,
890              "Error reading from pipe: %u\n",
891              GetLastError ());
892         SetErrnoFromWinError (GetLastError ());
893         return GNUNET_SYSERR;
894       }
895       LOG (GNUNET_ERROR_TYPE_DEBUG, "Will get overlapped result\n");
896       GetOverlappedResult (h->h, h->oOverlapRead, &bytes_read, TRUE);
897     }
898     LOG (GNUNET_ERROR_TYPE_DEBUG, "Read %u bytes from pipe\n", bytes_read);
899   }
900   else
901   {
902     bytes_read = 0;
903   }
904   return bytes_read;
905 #else
906   return read (h->fd, result, len);
907 #endif
908 }
909
910
911 /**
912  * Read the contents of a binary file into a buffer.
913  * Guarantees not to block (returns GNUNET_SYSERR and sets errno to EAGAIN
914  * when no data can be read).
915  *
916  * @param h handle to an open file
917  * @param result the buffer to write the result to
918  * @param len the maximum number of bytes to read
919  * @return the number of bytes read on success, #GNUNET_SYSERR on failure
920  */
921 ssize_t
922 GNUNET_DISK_file_read_non_blocking (const struct GNUNET_DISK_FileHandle *h,
923                                     void *result,
924                                     size_t len)
925 {
926   if (NULL == h)
927   {
928     errno = EINVAL;
929     return GNUNET_SYSERR;
930   }
931
932 #ifdef MINGW
933   DWORD bytes_read;
934
935   if (h->type == GNUNET_DISK_HANLDE_TYPE_FILE)
936   {
937     if (!ReadFile (h->h, result, len, &bytes_read, NULL))
938     {
939       SetErrnoFromWinError (GetLastError ());
940       return GNUNET_SYSERR;
941     }
942   }
943   else if (h->type == GNUNET_DISK_HANLDE_TYPE_PIPE)
944   {
945     if (!ReadFile (h->h, result, len, &bytes_read, h->oOverlapRead))
946     {
947       if (GetLastError () != ERROR_IO_PENDING)
948       {
949         LOG (GNUNET_ERROR_TYPE_DEBUG, "Error reading from pipe: %u\n", GetLastError ());
950         SetErrnoFromWinError (GetLastError ());
951         return GNUNET_SYSERR;
952       }
953       else
954       {
955         LOG (GNUNET_ERROR_TYPE_DEBUG,
956             "ReadFile() queued a read, cancelling\n");
957         CancelIo (h->h);
958         errno = EAGAIN;
959         return GNUNET_SYSERR;
960       }
961     }
962     LOG (GNUNET_ERROR_TYPE_DEBUG,
963          "Read %u bytes\n",
964          bytes_read);
965   }
966   else
967   {
968     bytes_read = 0;
969   }
970   return bytes_read;
971 #else
972   int flags;
973   ssize_t ret;
974
975   /* set to non-blocking, read, then set back */
976   flags = fcntl (h->fd, F_GETFL);
977   if (0 == (flags & O_NONBLOCK))
978     (void) fcntl (h->fd, F_SETFL, flags | O_NONBLOCK);
979   ret = read (h->fd, result, len);
980   if (0 == (flags & O_NONBLOCK))
981     {
982       int eno = errno;
983       (void) fcntl (h->fd, F_SETFL, flags);
984       errno = eno;
985     }
986   return ret;
987 #endif
988 }
989
990
991 /**
992  * Read the contents of a binary file into a buffer.
993  *
994  * @param fn file name
995  * @param result the buffer to write the result to
996  * @param len the maximum number of bytes to read
997  * @return number of bytes read, #GNUNET_SYSERR on failure
998  */
999 ssize_t
1000 GNUNET_DISK_fn_read (const char *fn,
1001                      void *result,
1002                      size_t len)
1003 {
1004   struct GNUNET_DISK_FileHandle *fh;
1005   ssize_t ret;
1006   int eno;
1007
1008   fh = GNUNET_DISK_file_open (fn,
1009                               GNUNET_DISK_OPEN_READ,
1010                               GNUNET_DISK_PERM_NONE);
1011   if (NULL == fh)
1012     return GNUNET_SYSERR;
1013   ret = GNUNET_DISK_file_read (fh, result, len);
1014   eno = errno;
1015   GNUNET_assert (GNUNET_OK == GNUNET_DISK_file_close (fh));
1016   errno = eno;
1017   return ret;
1018 }
1019
1020
1021 /**
1022  * Write a buffer to a file.
1023  *
1024  * @param h handle to open file
1025  * @param buffer the data to write
1026  * @param n number of bytes to write
1027  * @return number of bytes written on success, #GNUNET_SYSERR on error
1028  */
1029 ssize_t
1030 GNUNET_DISK_file_write (const struct GNUNET_DISK_FileHandle * h,
1031                         const void *buffer, size_t n)
1032 {
1033   if (NULL == h)
1034   {
1035     errno = EINVAL;
1036     return GNUNET_SYSERR;
1037   }
1038
1039 #ifdef MINGW
1040   DWORD bytes_written;
1041
1042   if (h->type == GNUNET_DISK_HANLDE_TYPE_FILE)
1043   {
1044     if (!WriteFile (h->h, buffer, n, &bytes_written, NULL))
1045     {
1046       SetErrnoFromWinError (GetLastError ());
1047       return GNUNET_SYSERR;
1048     }
1049   }
1050   else if (h->type == GNUNET_DISK_HANLDE_TYPE_PIPE)
1051   {
1052     LOG (GNUNET_ERROR_TYPE_DEBUG, "It is a pipe trying to write %u bytes\n", n);
1053     if (!WriteFile (h->h, buffer, n, &bytes_written, h->oOverlapWrite))
1054     {
1055       if (GetLastError () != ERROR_IO_PENDING)
1056       {
1057         SetErrnoFromWinError (GetLastError ());
1058         LOG (GNUNET_ERROR_TYPE_DEBUG, "Error writing to pipe: %u\n",
1059             GetLastError ());
1060         return GNUNET_SYSERR;
1061       }
1062       LOG (GNUNET_ERROR_TYPE_DEBUG, "Will get overlapped result\n");
1063       if (!GetOverlappedResult (h->h, h->oOverlapWrite, &bytes_written, TRUE))
1064       {
1065         SetErrnoFromWinError (GetLastError ());
1066         LOG (GNUNET_ERROR_TYPE_DEBUG,
1067             "Error getting overlapped result while writing to pipe: %u\n",
1068             GetLastError ());
1069         return GNUNET_SYSERR;
1070       }
1071     }
1072     else
1073     {
1074       DWORD ovr;
1075       if (!GetOverlappedResult (h->h, h->oOverlapWrite, &ovr, TRUE))
1076       {
1077         LOG (GNUNET_ERROR_TYPE_DEBUG,
1078             "Error getting control overlapped result while writing to pipe: %u\n",
1079             GetLastError ());
1080       }
1081       else
1082       {
1083         LOG (GNUNET_ERROR_TYPE_DEBUG,
1084             "Wrote %u bytes (ovr says %u), picking the greatest\n",
1085             bytes_written, ovr);
1086       }
1087     }
1088     if (bytes_written == 0)
1089     {
1090       if (n > 0)
1091       {
1092         LOG (GNUNET_ERROR_TYPE_DEBUG, "Wrote %u bytes, returning -1 with EAGAIN\n", bytes_written);
1093         errno = EAGAIN;
1094         return GNUNET_SYSERR;
1095       }
1096     }
1097     LOG (GNUNET_ERROR_TYPE_DEBUG, "Wrote %u bytes\n", bytes_written);
1098   }
1099   else
1100   {
1101     bytes_written = 0;
1102   }
1103   return bytes_written;
1104 #else
1105   return write (h->fd, buffer, n);
1106 #endif
1107 }
1108
1109
1110 /**
1111  * Write a buffer to a file, blocking, if necessary.
1112  *
1113  * @param h handle to open file
1114  * @param buffer the data to write
1115  * @param n number of bytes to write
1116  * @return number of bytes written on success, #GNUNET_SYSERR on error
1117  */
1118 ssize_t
1119 GNUNET_DISK_file_write_blocking (const struct GNUNET_DISK_FileHandle * h,
1120                                  const void *buffer,
1121                                  size_t n)
1122 {
1123   if (NULL == h)
1124   {
1125     errno = EINVAL;
1126     return GNUNET_SYSERR;
1127   }
1128
1129 #ifdef MINGW
1130   DWORD bytes_written;
1131   /* We do a non-overlapped write, which is as blocking as it gets */
1132   LOG (GNUNET_ERROR_TYPE_DEBUG, "Writing %u bytes\n", n);
1133   if (!WriteFile (h->h, buffer, n, &bytes_written, NULL))
1134   {
1135     SetErrnoFromWinError (GetLastError ());
1136     LOG (GNUNET_ERROR_TYPE_DEBUG, "Error writing to pipe: %u\n",
1137         GetLastError ());
1138     return GNUNET_SYSERR;
1139   }
1140   if (bytes_written == 0 && n > 0)
1141   {
1142     LOG (GNUNET_ERROR_TYPE_DEBUG, "Waiting for pipe to clean\n");
1143     WaitForSingleObject (h->h, INFINITE);
1144     if (!WriteFile (h->h, buffer, n, &bytes_written, NULL))
1145     {
1146       SetErrnoFromWinError (GetLastError ());
1147       LOG (GNUNET_ERROR_TYPE_DEBUG, "Error writing to pipe: %u\n",
1148           GetLastError ());
1149       return GNUNET_SYSERR;
1150     }
1151   }
1152   LOG (GNUNET_ERROR_TYPE_DEBUG,
1153        "Wrote %u bytes\n",
1154        bytes_written);
1155   return bytes_written;
1156 #else
1157   int flags;
1158   ssize_t ret;
1159
1160   /* set to blocking, write, then set back */
1161   flags = fcntl (h->fd, F_GETFL);
1162   if (0 != (flags & O_NONBLOCK))
1163     (void) fcntl (h->fd, F_SETFL, flags - O_NONBLOCK);
1164   ret = write (h->fd, buffer, n);
1165   if (0 == (flags & O_NONBLOCK))
1166     (void) fcntl (h->fd, F_SETFL, flags);
1167   return ret;
1168 #endif
1169 }
1170
1171
1172 /**
1173  * Write a buffer to a file.  If the file is longer than the
1174  * number of bytes that will be written, it will be truncated.
1175  *
1176  * @param fn file name
1177  * @param buffer the data to write
1178  * @param n number of bytes to write
1179  * @param mode file permissions
1180  * @return number of bytes written on success, #GNUNET_SYSERR on error
1181  */
1182 ssize_t
1183 GNUNET_DISK_fn_write (const char *fn,
1184                       const void *buffer,
1185                       size_t n,
1186                       enum GNUNET_DISK_AccessPermissions mode)
1187 {
1188   struct GNUNET_DISK_FileHandle *fh;
1189   ssize_t ret;
1190
1191   fh = GNUNET_DISK_file_open (fn,
1192                               GNUNET_DISK_OPEN_WRITE | GNUNET_DISK_OPEN_TRUNCATE
1193                               | GNUNET_DISK_OPEN_CREATE, mode);
1194   if (! fh)
1195     return GNUNET_SYSERR;
1196   ret = GNUNET_DISK_file_write (fh, buffer, n);
1197   GNUNET_assert (GNUNET_OK == GNUNET_DISK_file_close (fh));
1198   return ret;
1199 }
1200
1201
1202 /**
1203  * Scan a directory for files.
1204  *
1205  * @param dir_name the name of the directory
1206  * @param callback the method to call for each file,
1207  *        can be NULL, in that case, we only count
1208  * @param callback_cls closure for @a callback
1209  * @return the number of files found, #GNUNET_SYSERR on error or
1210  *         ieration aborted by callback returning #GNUNET_SYSERR
1211  */
1212 int
1213 GNUNET_DISK_directory_scan (const char *dir_name,
1214                             GNUNET_FileNameCallback callback,
1215                             void *callback_cls)
1216 {
1217   DIR *dinfo;
1218   struct dirent *finfo;
1219   struct stat istat;
1220   int count = 0;
1221   int ret;
1222   char *name;
1223   char *dname;
1224   unsigned int name_len;
1225   unsigned int n_size;
1226
1227   GNUNET_assert (NULL != dir_name);
1228   dname = GNUNET_STRINGS_filename_expand (dir_name);
1229   if (NULL == dname)
1230     return GNUNET_SYSERR;
1231   while ( (strlen (dname) > 0) &&
1232           (dname[strlen (dname) - 1] == DIR_SEPARATOR) )
1233     dname[strlen (dname) - 1] = '\0';
1234   if (0 != STAT (dname, &istat))
1235   {
1236     LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING,
1237                        "stat",
1238                        dname);
1239     GNUNET_free (dname);
1240     return GNUNET_SYSERR;
1241   }
1242   if (! S_ISDIR (istat.st_mode))
1243   {
1244     LOG (GNUNET_ERROR_TYPE_WARNING,
1245          _("Expected `%s' to be a directory!\n"),
1246          dir_name);
1247     GNUNET_free (dname);
1248     return GNUNET_SYSERR;
1249   }
1250   errno = 0;
1251   dinfo = OPENDIR (dname);
1252   if ( (EACCES == errno) ||
1253        (NULL == dinfo) )
1254   {
1255     LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING,
1256                        "opendir",
1257                        dname);
1258     if (NULL != dinfo)
1259       CLOSEDIR (dinfo);
1260     GNUNET_free (dname);
1261     return GNUNET_SYSERR;
1262   }
1263   name_len = 256;
1264   n_size = strlen (dname) + name_len + strlen (DIR_SEPARATOR_STR) + 1;
1265   name = GNUNET_malloc (n_size);
1266   while (NULL != (finfo = READDIR (dinfo)))
1267   {
1268     if ( (0 == strcmp (finfo->d_name, ".")) ||
1269          (0 == strcmp (finfo->d_name, "..")) )
1270       continue;
1271     if (NULL != callback)
1272     {
1273       if (name_len < strlen (finfo->d_name))
1274       {
1275         GNUNET_free (name);
1276         name_len = strlen (finfo->d_name);
1277         n_size = strlen (dname) + name_len + strlen (DIR_SEPARATOR_STR) + 1;
1278         name = GNUNET_malloc (n_size);
1279       }
1280       /* dname can end in "/" only if dname == "/";
1281        * if dname does not end in "/", we need to add
1282        * a "/" (otherwise, we must not!) */
1283       GNUNET_snprintf (name,
1284                        n_size,
1285                        "%s%s%s",
1286                        dname,
1287                        (0 == strcmp (dname,
1288                                      DIR_SEPARATOR_STR))
1289                        ? ""
1290                        : DIR_SEPARATOR_STR,
1291                        finfo->d_name);
1292       ret = callback (callback_cls,
1293                       name);
1294       if (GNUNET_OK != ret)
1295       {
1296         CLOSEDIR (dinfo);
1297         GNUNET_free (name);
1298         GNUNET_free (dname);
1299         if (GNUNET_NO == ret)
1300           return count;
1301         return GNUNET_SYSERR;
1302       }
1303     }
1304     count++;
1305   }
1306   CLOSEDIR (dinfo);
1307   GNUNET_free (name);
1308   GNUNET_free (dname);
1309   return count;
1310 }
1311
1312
1313 /**
1314  * Function that removes the given directory by calling
1315  * #GNUNET_DISK_directory_remove().
1316  *
1317  * @param unused not used
1318  * @param fn directory to remove
1319  * @return #GNUNET_OK
1320  */
1321 static int
1322 remove_helper (void *unused,
1323                const char *fn)
1324 {
1325   (void) unused;
1326   (void) GNUNET_DISK_directory_remove (fn);
1327   return GNUNET_OK;
1328 }
1329
1330
1331 /**
1332  * Remove all files in a directory (rm -r). Call with
1333  * caution.
1334  *
1335  * @param filename the file to remove
1336  * @return #GNUNET_OK on success, #GNUNET_SYSERR on error
1337  */
1338 int
1339 GNUNET_DISK_directory_remove (const char *filename)
1340 {
1341   struct stat istat;
1342
1343   if (NULL == filename)
1344   {
1345     GNUNET_break (0);
1346     return GNUNET_SYSERR;
1347   }
1348   if (0 != LSTAT (filename, &istat))
1349     return GNUNET_NO;           /* file may not exist... */
1350   (void) CHMOD (filename,
1351                 S_IWUSR | S_IRUSR | S_IXUSR);
1352   if (0 == UNLINK (filename))
1353     return GNUNET_OK;
1354   if ( (errno != EISDIR) &&
1355        /* EISDIR is not sufficient in all cases, e.g.
1356         * sticky /tmp directory may result in EPERM on BSD.
1357         * So we also explicitly check "isDirectory" */
1358        (GNUNET_YES !=
1359         GNUNET_DISK_directory_test (filename,
1360                                     GNUNET_YES)) )
1361   {
1362     LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING,
1363                        "rmdir",
1364                        filename);
1365     return GNUNET_SYSERR;
1366   }
1367   if (GNUNET_SYSERR ==
1368       GNUNET_DISK_directory_scan (filename,
1369                                   &remove_helper,
1370                                   NULL))
1371     return GNUNET_SYSERR;
1372   if (0 != RMDIR (filename))
1373   {
1374     LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING,
1375                        "rmdir",
1376                        filename);
1377     return GNUNET_SYSERR;
1378   }
1379   return GNUNET_OK;
1380 }
1381
1382
1383 /**
1384  * Copy a file.
1385  *
1386  * @param src file to copy
1387  * @param dst destination file name
1388  * @return #GNUNET_OK on success, #GNUNET_SYSERR on error
1389  */
1390 int
1391 GNUNET_DISK_file_copy (const char *src,
1392                        const char *dst)
1393 {
1394   char *buf;
1395   uint64_t pos;
1396   uint64_t size;
1397   size_t len;
1398   ssize_t sret;
1399   struct GNUNET_DISK_FileHandle *in;
1400   struct GNUNET_DISK_FileHandle *out;
1401
1402   if (GNUNET_OK !=
1403       GNUNET_DISK_file_size (src,
1404                              &size,
1405                              GNUNET_YES,
1406                              GNUNET_YES))
1407   {
1408     GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR,
1409                               "stat",
1410                               src);
1411     return GNUNET_SYSERR;
1412   }
1413   pos = 0;
1414   in = GNUNET_DISK_file_open (src,
1415                               GNUNET_DISK_OPEN_READ,
1416                               GNUNET_DISK_PERM_NONE);
1417   if (! in)
1418   {
1419     GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR,
1420                               "open",
1421                               src);
1422     return GNUNET_SYSERR;
1423   }
1424   out =
1425       GNUNET_DISK_file_open (dst,
1426                              GNUNET_DISK_OPEN_WRITE | GNUNET_DISK_OPEN_CREATE |
1427                              GNUNET_DISK_OPEN_FAILIFEXISTS,
1428                              GNUNET_DISK_PERM_USER_READ |
1429                              GNUNET_DISK_PERM_USER_WRITE |
1430                              GNUNET_DISK_PERM_GROUP_READ |
1431                              GNUNET_DISK_PERM_GROUP_WRITE);
1432   if (!out)
1433   {
1434     GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR,
1435                               "open",
1436                               dst);
1437     GNUNET_DISK_file_close (in);
1438     return GNUNET_SYSERR;
1439   }
1440   buf = GNUNET_malloc (COPY_BLK_SIZE);
1441   while (pos < size)
1442   {
1443     len = COPY_BLK_SIZE;
1444     if (len > size - pos)
1445       len = size - pos;
1446     sret = GNUNET_DISK_file_read (in,
1447                                   buf,
1448                                   len);
1449     if ( (sret < 0) ||
1450          (len != (size_t) sret) )
1451       goto FAIL;
1452     sret = GNUNET_DISK_file_write (out,
1453                                    buf,
1454                                    len);
1455     if ( (sret < 0) ||
1456          (len != (size_t) sret) )
1457       goto FAIL;
1458     pos += len;
1459   }
1460   GNUNET_free (buf);
1461   GNUNET_DISK_file_close (in);
1462   GNUNET_DISK_file_close (out);
1463   return GNUNET_OK;
1464 FAIL:
1465   GNUNET_free (buf);
1466   GNUNET_DISK_file_close (in);
1467   GNUNET_DISK_file_close (out);
1468   return GNUNET_SYSERR;
1469 }
1470
1471
1472 /**
1473  * @brief Removes special characters as ':' from a filename.
1474  * @param fn the filename to canonicalize
1475  */
1476 void
1477 GNUNET_DISK_filename_canonicalize (char *fn)
1478 {
1479   char *idx;
1480   char c;
1481
1482   for (idx = fn; *idx; idx++)
1483   {
1484     c = *idx;
1485
1486     if (c == '/' || c == '\\' || c == ':' ||
1487         c == '*' || c == '?' || c == '"' ||
1488         c == '<' || c == '>' || c == '|')
1489     {
1490       *idx = '_';
1491     }
1492   }
1493 }
1494
1495
1496
1497 /**
1498  * @brief Change owner of a file
1499  *
1500  * @param filename name of file to change the owner of
1501  * @param user name of the new owner
1502  * @return #GNUNET_OK on success, #GNUNET_SYSERR on failure
1503  */
1504 int
1505 GNUNET_DISK_file_change_owner (const char *filename,
1506                                const char *user)
1507 {
1508 #ifndef MINGW
1509   struct passwd *pws;
1510
1511   pws = getpwnam (user);
1512   if (NULL == pws)
1513   {
1514     LOG (GNUNET_ERROR_TYPE_ERROR,
1515          _("Cannot obtain information about user `%s': %s\n"),
1516          user,
1517          STRERROR (errno));
1518     return GNUNET_SYSERR;
1519   }
1520   if (0 != chown (filename,
1521                   pws->pw_uid,
1522                   pws->pw_gid))
1523   {
1524     LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING,
1525                        "chown",
1526                        filename);
1527     return GNUNET_SYSERR;
1528   }
1529 #endif
1530   return GNUNET_OK;
1531 }
1532
1533
1534 /**
1535  * Lock a part of a file
1536  *
1537  * @param fh file handle
1538  * @param lock_start absolute position from where to lock
1539  * @param lock_end absolute position until where to lock
1540  * @param excl #GNUNET_YES for an exclusive lock
1541  * @return #GNUNET_OK on success, #GNUNET_SYSERR on error
1542  */
1543 int
1544 GNUNET_DISK_file_lock (struct GNUNET_DISK_FileHandle *fh,
1545                        off_t lock_start,
1546                        off_t lock_end,
1547                        int excl)
1548 {
1549   if (fh == NULL)
1550   {
1551     errno = EINVAL;
1552     return GNUNET_SYSERR;
1553   }
1554
1555 #ifndef MINGW
1556   struct flock fl;
1557
1558   memset (&fl, 0, sizeof (struct flock));
1559   fl.l_type = excl ? F_WRLCK : F_RDLCK;
1560   fl.l_whence = SEEK_SET;
1561   fl.l_start = lock_start;
1562   fl.l_len = lock_end;
1563
1564   return fcntl (fh->fd, F_SETLK, &fl) != 0 ? GNUNET_SYSERR : GNUNET_OK;
1565 #else
1566   OVERLAPPED o;
1567   off_t diff = lock_end - lock_start;
1568   DWORD diff_low, diff_high;
1569   diff_low = (DWORD) (diff & 0xFFFFFFFF);
1570   diff_high = (DWORD) ((diff >> (sizeof (DWORD) * 8)) & 0xFFFFFFFF);
1571
1572   memset (&o, 0, sizeof (OVERLAPPED));
1573   o.Offset = (DWORD) (lock_start & 0xFFFFFFFF);;
1574   o.OffsetHigh = (DWORD) (((lock_start & ~0xFFFFFFFF) >> (sizeof (DWORD) * 8)) & 0xFFFFFFFF);
1575
1576   if (!LockFileEx
1577       (fh->h, (excl ? LOCKFILE_EXCLUSIVE_LOCK : 0) | LOCKFILE_FAIL_IMMEDIATELY,
1578        0, diff_low, diff_high, &o))
1579   {
1580     SetErrnoFromWinError (GetLastError ());
1581     return GNUNET_SYSERR;
1582   }
1583
1584   return GNUNET_OK;
1585 #endif
1586 }
1587
1588
1589 /**
1590  * Unlock a part of a file
1591  *
1592  * @param fh file handle
1593  * @param unlock_start absolute position from where to unlock
1594  * @param unlock_end absolute position until where to unlock
1595  * @return #GNUNET_OK on success, #GNUNET_SYSERR on error
1596  */
1597 int
1598 GNUNET_DISK_file_unlock (struct GNUNET_DISK_FileHandle *fh,
1599                          off_t unlock_start,
1600                          off_t unlock_end)
1601 {
1602   if (fh == NULL)
1603   {
1604     errno = EINVAL;
1605     return GNUNET_SYSERR;
1606   }
1607
1608 #ifndef MINGW
1609   struct flock fl;
1610
1611   memset (&fl, 0, sizeof (struct flock));
1612   fl.l_type = F_UNLCK;
1613   fl.l_whence = SEEK_SET;
1614   fl.l_start = unlock_start;
1615   fl.l_len = unlock_end;
1616
1617   return fcntl (fh->fd, F_SETLK, &fl) != 0 ? GNUNET_SYSERR : GNUNET_OK;
1618 #else
1619   OVERLAPPED o;
1620   off_t diff = unlock_end - unlock_start;
1621   DWORD diff_low, diff_high;
1622   diff_low = (DWORD) (diff & 0xFFFFFFFF);
1623   diff_high = (DWORD) ((diff >> (sizeof (DWORD) * 8)) & 0xFFFFFFFF);
1624
1625   memset (&o, 0, sizeof (OVERLAPPED));
1626   o.Offset = (DWORD) (unlock_start & 0xFFFFFFFF);;
1627   o.OffsetHigh = (DWORD) (((unlock_start & ~0xFFFFFFFF) >> (sizeof (DWORD) * 8)) & 0xFFFFFFFF);
1628
1629   if (!UnlockFileEx (fh->h, 0, diff_low, diff_high, &o))
1630   {
1631     SetErrnoFromWinError (GetLastError ());
1632     return GNUNET_SYSERR;
1633   }
1634
1635   return GNUNET_OK;
1636 #endif
1637 }
1638
1639
1640 /**
1641  * Open a file.  Note that the access permissions will only be
1642  * used if a new file is created and if the underlying operating
1643  * system supports the given permissions.
1644  *
1645  * @param fn file name to be opened
1646  * @param flags opening flags, a combination of GNUNET_DISK_OPEN_xxx bit flags
1647  * @param perm permissions for the newly created file, use
1648  *             #GNUNET_DISK_PERM_NONE if a file could not be created by this
1649  *             call (because of flags)
1650  * @return IO handle on success, NULL on error
1651  */
1652 struct GNUNET_DISK_FileHandle *
1653 GNUNET_DISK_file_open (const char *fn,
1654                        enum GNUNET_DISK_OpenFlags flags,
1655                        enum GNUNET_DISK_AccessPermissions perm)
1656 {
1657   char *expfn;
1658   struct GNUNET_DISK_FileHandle *ret;
1659
1660 #ifdef MINGW
1661   DWORD access;
1662   DWORD disp;
1663   HANDLE h;
1664   wchar_t wexpfn[MAX_PATH + 1];
1665 #else
1666   int oflags;
1667   int mode;
1668   int fd;
1669 #endif
1670
1671   expfn = GNUNET_STRINGS_filename_expand (fn);
1672   if (NULL == expfn)
1673     return NULL;
1674 #ifndef MINGW
1675   mode = 0;
1676   if (GNUNET_DISK_OPEN_READWRITE == (flags & GNUNET_DISK_OPEN_READWRITE))
1677     oflags = O_RDWR;            /* note: O_RDWR is NOT always O_RDONLY | O_WRONLY */
1678   else if (flags & GNUNET_DISK_OPEN_READ)
1679     oflags = O_RDONLY;
1680   else if (flags & GNUNET_DISK_OPEN_WRITE)
1681     oflags = O_WRONLY;
1682   else
1683   {
1684     GNUNET_break (0);
1685     GNUNET_free (expfn);
1686     return NULL;
1687   }
1688   if (flags & GNUNET_DISK_OPEN_FAILIFEXISTS)
1689     oflags |= (O_CREAT | O_EXCL);
1690   if (flags & GNUNET_DISK_OPEN_TRUNCATE)
1691     oflags |= O_TRUNC;
1692   if (flags & GNUNET_DISK_OPEN_APPEND)
1693     oflags |= O_APPEND;
1694   if (flags & GNUNET_DISK_OPEN_CREATE)
1695   {
1696     (void) GNUNET_DISK_directory_create_for_file (expfn);
1697     oflags |= O_CREAT;
1698     mode = translate_unix_perms (perm);
1699   }
1700
1701   fd = open (expfn, oflags
1702 #if O_CLOEXEC
1703              | O_CLOEXEC
1704 #endif
1705              | O_LARGEFILE, mode);
1706   if (fd == -1)
1707   {
1708     if (0 == (flags & GNUNET_DISK_OPEN_FAILIFEXISTS))
1709       LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "open", expfn);
1710     else
1711       LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_DEBUG, "open", expfn);
1712     GNUNET_free (expfn);
1713     return NULL;
1714   }
1715 #else
1716   access = 0;
1717   disp = OPEN_ALWAYS;
1718
1719   if (GNUNET_DISK_OPEN_READWRITE == (flags & GNUNET_DISK_OPEN_READWRITE))
1720     access = FILE_READ_DATA | FILE_WRITE_DATA;
1721   else if (flags & GNUNET_DISK_OPEN_READ)
1722     access = FILE_READ_DATA;
1723   else if (flags & GNUNET_DISK_OPEN_WRITE)
1724     access = FILE_WRITE_DATA;
1725
1726   if (flags & GNUNET_DISK_OPEN_FAILIFEXISTS)
1727   {
1728     disp = CREATE_NEW;
1729   }
1730   else if (flags & GNUNET_DISK_OPEN_CREATE)
1731   {
1732     (void) GNUNET_DISK_directory_create_for_file (expfn);
1733     if (flags & GNUNET_DISK_OPEN_TRUNCATE)
1734       disp = CREATE_ALWAYS;
1735     else
1736       disp = OPEN_ALWAYS;
1737   }
1738   else if (flags & GNUNET_DISK_OPEN_TRUNCATE)
1739   {
1740     disp = TRUNCATE_EXISTING;
1741   }
1742   else
1743   {
1744     disp = OPEN_EXISTING;
1745   }
1746
1747   if (ERROR_SUCCESS == plibc_conv_to_win_pathwconv(expfn, wexpfn))
1748     h = CreateFileW (wexpfn, access,
1749                     FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE, NULL,
1750                     disp, FILE_ATTRIBUTE_NORMAL, NULL);
1751   else
1752     h = INVALID_HANDLE_VALUE;
1753   if (h == INVALID_HANDLE_VALUE)
1754   {
1755     int err;
1756     SetErrnoFromWinError (GetLastError ());
1757     err = errno;
1758     LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_INFO, "open", expfn);
1759     GNUNET_free (expfn);
1760     errno = err;
1761     return NULL;
1762   }
1763
1764   if (flags & GNUNET_DISK_OPEN_APPEND)
1765     if (SetFilePointer (h, 0, 0, FILE_END) == INVALID_SET_FILE_POINTER)
1766     {
1767       SetErrnoFromWinError (GetLastError ());
1768       LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "SetFilePointer", expfn);
1769       CloseHandle (h);
1770       GNUNET_free (expfn);
1771       return NULL;
1772     }
1773 #endif
1774
1775   ret = GNUNET_new (struct GNUNET_DISK_FileHandle);
1776 #ifdef MINGW
1777   ret->h = h;
1778   ret->type = GNUNET_DISK_HANLDE_TYPE_FILE;
1779 #else
1780   ret->fd = fd;
1781 #endif
1782   GNUNET_free (expfn);
1783   return ret;
1784 }
1785
1786
1787 /**
1788  * Close an open file.
1789  *
1790  * @param h file handle
1791  * @return #GNUNET_OK on success, #GNUNET_SYSERR otherwise
1792  */
1793 int
1794 GNUNET_DISK_file_close (struct GNUNET_DISK_FileHandle *h)
1795 {
1796   int ret;
1797   if (h == NULL)
1798   {
1799     errno = EINVAL;
1800     return GNUNET_SYSERR;
1801   }
1802
1803   ret = GNUNET_OK;
1804
1805 #if MINGW
1806   if (! CloseHandle (h->h))
1807   {
1808     SetErrnoFromWinError (GetLastError ());
1809     LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING, "close");
1810     ret = GNUNET_SYSERR;
1811   }
1812   if (h->oOverlapRead)
1813   {
1814     if (! CloseHandle (h->oOverlapRead->hEvent))
1815     {
1816       SetErrnoFromWinError (GetLastError ());
1817       LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING, "close");
1818       ret = GNUNET_SYSERR;
1819     }
1820     GNUNET_free (h->oOverlapRead);
1821   }
1822   if (h->oOverlapWrite)
1823   {
1824     if (!CloseHandle (h->oOverlapWrite->hEvent))
1825     {
1826       SetErrnoFromWinError (GetLastError ());
1827       LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING, "close");
1828       ret = GNUNET_SYSERR;
1829     }
1830     GNUNET_free (h->oOverlapWrite);
1831   }
1832 #else
1833   if (close (h->fd) != 0)
1834   {
1835     LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING, "close");
1836     ret = GNUNET_SYSERR;
1837   }
1838 #endif
1839   GNUNET_free (h);
1840   return ret;
1841 }
1842
1843
1844 #ifdef WINDOWS
1845 /**
1846  * Get a GNUnet file handle from a W32 handle.
1847  *
1848  * @param handle native handle
1849  * @return GNUnet file handle corresponding to the W32 handle
1850  */
1851 struct GNUNET_DISK_FileHandle *
1852 GNUNET_DISK_get_handle_from_w32_handle (HANDLE osfh)
1853 {
1854   struct GNUNET_DISK_FileHandle *fh;
1855   DWORD dwret;
1856   enum GNUNET_FILE_Type ftype;
1857
1858   dwret = GetFileType (osfh);
1859   switch (dwret)
1860   {
1861   case FILE_TYPE_DISK:
1862     ftype = GNUNET_DISK_HANLDE_TYPE_FILE;
1863     break;
1864   case FILE_TYPE_PIPE:
1865     ftype = GNUNET_DISK_HANLDE_TYPE_PIPE;
1866     break;
1867   case FILE_TYPE_UNKNOWN:
1868     if ( (GetLastError () == NO_ERROR) ||
1869          (GetLastError () == ERROR_INVALID_HANDLE) )
1870     {
1871       if (0 != ResetEvent (osfh))
1872         ftype = GNUNET_DISK_HANLDE_TYPE_EVENT;
1873       else
1874         return NULL;
1875     }
1876     else
1877       return NULL;
1878     break;
1879   default:
1880     return NULL;
1881   }
1882
1883   fh = GNUNET_new (struct GNUNET_DISK_FileHandle);
1884
1885   fh->h = osfh;
1886   fh->type = ftype;
1887   if (ftype == GNUNET_DISK_HANLDE_TYPE_PIPE)
1888   {
1889     /**
1890      * Note that we can't make it overlapped if it isn't already.
1891      * (ReOpenFile() is only available in 2003/Vista).
1892      * The process that opened this file in the first place (usually a parent
1893      * process, if this is stdin/stdout/stderr) must make it overlapped,
1894      * otherwise we're screwed, as selecting on non-overlapped handle
1895      * will block.
1896      */
1897     fh->oOverlapRead = GNUNET_new (OVERLAPPED);
1898     fh->oOverlapWrite = GNUNET_new (OVERLAPPED);
1899     fh->oOverlapRead->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
1900     fh->oOverlapWrite->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
1901   }
1902
1903   return fh;
1904 }
1905 #endif
1906
1907 /**
1908  * Get a handle from a native integer FD.
1909  *
1910  * @param fno native integer file descriptor
1911  * @return file handle corresponding to the descriptor, NULL on error
1912  */
1913 struct GNUNET_DISK_FileHandle *
1914 GNUNET_DISK_get_handle_from_int_fd (int fno)
1915 {
1916   struct GNUNET_DISK_FileHandle *fh;
1917
1918   if ( (((off_t) -1) == lseek (fno, 0, SEEK_CUR)) &&
1919        (EBADF == errno) )
1920     return NULL; /* invalid FD */
1921
1922 #ifndef WINDOWS
1923   fh = GNUNET_new (struct GNUNET_DISK_FileHandle);
1924
1925   fh->fd = fno;
1926 #else
1927   intptr_t osfh;
1928
1929   osfh = _get_osfhandle (fno);
1930   if (INVALID_HANDLE_VALUE == (HANDLE) osfh)
1931     return NULL;
1932
1933   fh = GNUNET_DISK_get_handle_from_w32_handle ((HANDLE) osfh);
1934 #endif
1935
1936   return fh;
1937 }
1938
1939
1940 /**
1941  * Get a handle from a native streaming FD.
1942  *
1943  * @param fd native streaming file descriptor
1944  * @return file handle corresponding to the descriptor
1945  */
1946 struct GNUNET_DISK_FileHandle *
1947 GNUNET_DISK_get_handle_from_native (FILE *fd)
1948 {
1949   int fno;
1950
1951   fno = fileno (fd);
1952   if (-1 == fno)
1953     return NULL;
1954
1955   return GNUNET_DISK_get_handle_from_int_fd (fno);
1956 }
1957
1958
1959 /**
1960  * Handle for a memory-mapping operation.
1961  */
1962 struct GNUNET_DISK_MapHandle
1963 {
1964   /**
1965    * Address where the map is in memory.
1966    */
1967   void *addr;
1968
1969 #ifdef MINGW
1970   /**
1971    * Underlying OS handle.
1972    */
1973   HANDLE h;
1974 #else
1975   /**
1976    * Number of bytes mapped.
1977    */
1978   size_t len;
1979 #endif
1980 };
1981
1982
1983 #ifndef MAP_FAILED
1984 #define MAP_FAILED ((void *) -1)
1985 #endif
1986
1987 /**
1988  * Map a file into memory
1989  *
1990  * @param h open file handle
1991  * @param m handle to the new mapping
1992  * @param access access specification, GNUNET_DISK_MAP_TYPE_xxx
1993  * @param len size of the mapping
1994  * @return pointer to the mapped memory region, NULL on failure
1995  */
1996 void *
1997 GNUNET_DISK_file_map (const struct GNUNET_DISK_FileHandle *h,
1998                       struct GNUNET_DISK_MapHandle **m,
1999                       enum GNUNET_DISK_MapType access, size_t len)
2000 {
2001   if (h == NULL)
2002   {
2003     errno = EINVAL;
2004     return NULL;
2005   }
2006
2007 #ifdef MINGW
2008   DWORD mapAccess, protect;
2009
2010   if ((access & GNUNET_DISK_MAP_TYPE_READ) &&
2011       (access & GNUNET_DISK_MAP_TYPE_WRITE))
2012   {
2013     protect = PAGE_READWRITE;
2014     mapAccess = FILE_MAP_ALL_ACCESS;
2015   }
2016   else if (access & GNUNET_DISK_MAP_TYPE_READ)
2017   {
2018     protect = PAGE_READONLY;
2019     mapAccess = FILE_MAP_READ;
2020   }
2021   else if (access & GNUNET_DISK_MAP_TYPE_WRITE)
2022   {
2023     protect = PAGE_READWRITE;
2024     mapAccess = FILE_MAP_WRITE;
2025   }
2026   else
2027   {
2028     GNUNET_break (0);
2029     return NULL;
2030   }
2031
2032   *m = GNUNET_new (struct GNUNET_DISK_MapHandle);
2033   (*m)->h = CreateFileMapping (h->h, NULL, protect, 0, 0, NULL);
2034   if ((*m)->h == INVALID_HANDLE_VALUE)
2035   {
2036     SetErrnoFromWinError (GetLastError ());
2037     GNUNET_free (*m);
2038     return NULL;
2039   }
2040
2041   (*m)->addr = MapViewOfFile ((*m)->h, mapAccess, 0, 0, len);
2042   if (!(*m)->addr)
2043   {
2044     SetErrnoFromWinError (GetLastError ());
2045     CloseHandle ((*m)->h);
2046     GNUNET_free (*m);
2047   }
2048
2049   return (*m)->addr;
2050 #else
2051   int prot;
2052
2053   prot = 0;
2054   if (access & GNUNET_DISK_MAP_TYPE_READ)
2055     prot = PROT_READ;
2056   if (access & GNUNET_DISK_MAP_TYPE_WRITE)
2057     prot |= PROT_WRITE;
2058   *m = GNUNET_new (struct GNUNET_DISK_MapHandle);
2059   (*m)->addr = mmap (NULL, len, prot, MAP_SHARED, h->fd, 0);
2060   GNUNET_assert (NULL != (*m)->addr);
2061   if (MAP_FAILED == (*m)->addr)
2062   {
2063     GNUNET_free (*m);
2064     return NULL;
2065   }
2066   (*m)->len = len;
2067   return (*m)->addr;
2068 #endif
2069 }
2070
2071 /**
2072  * Unmap a file
2073  * @param h mapping handle
2074  * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
2075  */
2076 int
2077 GNUNET_DISK_file_unmap (struct GNUNET_DISK_MapHandle *h)
2078 {
2079   int ret;
2080
2081   if (h == NULL)
2082   {
2083     errno = EINVAL;
2084     return GNUNET_SYSERR;
2085   }
2086
2087 #ifdef MINGW
2088   ret = UnmapViewOfFile (h->addr) ? GNUNET_OK : GNUNET_SYSERR;
2089   if (ret != GNUNET_OK)
2090     SetErrnoFromWinError (GetLastError ());
2091   if (!CloseHandle (h->h) && (ret == GNUNET_OK))
2092   {
2093     ret = GNUNET_SYSERR;
2094     SetErrnoFromWinError (GetLastError ());
2095   }
2096 #else
2097   ret = munmap (h->addr, h->len) != -1 ? GNUNET_OK : GNUNET_SYSERR;
2098 #endif
2099   GNUNET_free (h);
2100   return ret;
2101 }
2102
2103
2104 /**
2105  * Write file changes to disk
2106  * @param h handle to an open file
2107  * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
2108  */
2109 int
2110 GNUNET_DISK_file_sync (const struct GNUNET_DISK_FileHandle *h)
2111 {
2112   if (h == NULL)
2113   {
2114     errno = EINVAL;
2115     return GNUNET_SYSERR;
2116   }
2117
2118 #ifdef MINGW
2119   int ret;
2120
2121   ret = FlushFileBuffers (h->h) ? GNUNET_OK : GNUNET_SYSERR;
2122   if (ret != GNUNET_OK)
2123     SetErrnoFromWinError (GetLastError ());
2124   return ret;
2125 #elif defined(FREEBSD) || defined(OPENBSD) || defined(DARWIN)
2126   return fsync (h->fd) == -1 ? GNUNET_SYSERR : GNUNET_OK;
2127 #else
2128   return fdatasync (h->fd) == -1 ? GNUNET_SYSERR : GNUNET_OK;
2129 #endif
2130 }
2131
2132
2133 #if WINDOWS
2134 #ifndef PIPE_BUF
2135 #define PIPE_BUF        512
2136 #endif
2137 /* Copyright Bob Byrnes  <byrnes <at> curl.com>
2138    http://permalink.gmane.org/gmane.os.cygwin.patches/2121
2139 */
2140 /* Create a pipe, and return handles to the read and write ends,
2141    just like CreatePipe, but ensure that the write end permits
2142    FILE_READ_ATTRIBUTES access, on later versions of win32 where
2143    this is supported.  This access is needed by NtQueryInformationFile,
2144    which is used to implement select and nonblocking writes.
2145    Note that the return value is either NO_ERROR or GetLastError,
2146    unlike CreatePipe, which returns a bool for success or failure.  */
2147 static int
2148 create_selectable_pipe (PHANDLE read_pipe_ptr, PHANDLE write_pipe_ptr,
2149                         LPSECURITY_ATTRIBUTES sa_ptr, DWORD psize,
2150                         DWORD dwReadMode, DWORD dwWriteMode)
2151 {
2152   /* Default to error. */
2153   *read_pipe_ptr = *write_pipe_ptr = INVALID_HANDLE_VALUE;
2154
2155   HANDLE read_pipe;
2156   HANDLE write_pipe;
2157
2158   /* Ensure that there is enough pipe buffer space for atomic writes.  */
2159   if (psize < PIPE_BUF)
2160     psize = PIPE_BUF;
2161
2162   char pipename[MAX_PATH];
2163
2164   /* Retry CreateNamedPipe as long as the pipe name is in use.
2165    * Retrying will probably never be necessary, but we want
2166    * to be as robust as possible.  */
2167   while (1)
2168   {
2169     static volatile LONG pipe_unique_id;
2170
2171     snprintf (pipename, sizeof pipename, "\\\\.\\pipe\\gnunet-%d-%ld",
2172               getpid (), InterlockedIncrement ((LONG *) & pipe_unique_id));
2173     LOG (GNUNET_ERROR_TYPE_DEBUG, "CreateNamedPipe: name = %s, size = %lu\n",
2174          pipename, psize);
2175     /* Use CreateNamedPipe instead of CreatePipe, because the latter
2176      * returns a write handle that does not permit FILE_READ_ATTRIBUTES
2177      * access, on versions of win32 earlier than WinXP SP2.
2178      * CreatePipe also stupidly creates a full duplex pipe, which is
2179      * a waste, since only a single direction is actually used.
2180      * It's important to only allow a single instance, to ensure that
2181      * the pipe was not created earlier by some other process, even if
2182      * the pid has been reused.  */
2183     read_pipe = CreateNamedPipeA (pipename, PIPE_ACCESS_INBOUND | FILE_FLAG_FIRST_PIPE_INSTANCE | dwReadMode, PIPE_TYPE_BYTE | PIPE_READMODE_BYTE, 1,   /* max instances */
2184                                   psize,        /* output buffer size */
2185                                   psize,        /* input buffer size */
2186                                   NMPWAIT_USE_DEFAULT_WAIT, sa_ptr);
2187
2188     if (read_pipe != INVALID_HANDLE_VALUE)
2189     {
2190       LOG (GNUNET_ERROR_TYPE_DEBUG, "pipe read handle = %p\n", read_pipe);
2191       break;
2192     }
2193
2194     DWORD err = GetLastError ();
2195
2196     switch (err)
2197     {
2198     case ERROR_PIPE_BUSY:
2199       /* The pipe is already open with compatible parameters.
2200        * Pick a new name and retry.  */
2201       LOG (GNUNET_ERROR_TYPE_DEBUG, "pipe busy, retrying\n");
2202       continue;
2203     case ERROR_ACCESS_DENIED:
2204       /* The pipe is already open with incompatible parameters.
2205        * Pick a new name and retry.  */
2206       LOG (GNUNET_ERROR_TYPE_DEBUG, "pipe access denied, retrying\n");
2207       continue;
2208     case ERROR_CALL_NOT_IMPLEMENTED:
2209       /* We are on an older Win9x platform without named pipes.
2210        * Return an anonymous pipe as the best approximation.  */
2211       LOG (GNUNET_ERROR_TYPE_DEBUG,
2212            "CreateNamedPipe not implemented, resorting to "
2213            "CreatePipe: size = %lu\n", psize);
2214       if (CreatePipe (read_pipe_ptr, write_pipe_ptr, sa_ptr, psize))
2215       {
2216         LOG (GNUNET_ERROR_TYPE_DEBUG, "pipe read handle = %p, write handle = %p\n",
2217              *read_pipe_ptr,
2218              *write_pipe_ptr);
2219         return GNUNET_OK;
2220       }
2221       err = GetLastError ();
2222       LOG (GNUNET_ERROR_TYPE_ERROR, "CreatePipe failed: %d\n", err);
2223       return err;
2224     default:
2225       LOG (GNUNET_ERROR_TYPE_ERROR, "CreateNamedPipe failed: %d\n", err);
2226       return err;
2227     }
2228     /* NOTREACHED */
2229   }
2230   LOG (GNUNET_ERROR_TYPE_DEBUG, "CreateFile: name = %s\n", pipename);
2231
2232   /* Open the named pipe for writing.
2233    * Be sure to permit FILE_READ_ATTRIBUTES access.  */
2234   write_pipe = CreateFileA (pipename, GENERIC_WRITE | FILE_READ_ATTRIBUTES, 0,  /* share mode */
2235                             sa_ptr, OPEN_EXISTING, dwWriteMode, /* flags and attributes */
2236                             0); /* handle to template file */
2237
2238   if (write_pipe == INVALID_HANDLE_VALUE)
2239   {
2240     /* Failure. */
2241     DWORD err = GetLastError ();
2242
2243     LOG (GNUNET_ERROR_TYPE_DEBUG, "CreateFile failed: %d\n", err);
2244     CloseHandle (read_pipe);
2245     return err;
2246   }
2247   LOG (GNUNET_ERROR_TYPE_DEBUG, "pipe write handle = %p\n", write_pipe);
2248   /* Success. */
2249   *read_pipe_ptr = read_pipe;
2250   *write_pipe_ptr = write_pipe;
2251   return GNUNET_OK;
2252 }
2253 #endif
2254
2255
2256 /**
2257  * Creates an interprocess channel
2258  *
2259  * @param blocking_read creates an asynchronous pipe for reading if set to GNUNET_NO
2260  * @param blocking_write creates an asynchronous pipe for writing if set to GNUNET_NO
2261  * @param inherit_read inherit the parent processes stdin (only for windows)
2262  * @param inherit_write inherit the parent processes stdout (only for windows)
2263  * @return handle to the new pipe, NULL on error
2264  */
2265 struct GNUNET_DISK_PipeHandle *
2266 GNUNET_DISK_pipe (int blocking_read,
2267                   int blocking_write,
2268                   int inherit_read,
2269                   int inherit_write)
2270 {
2271 #ifndef MINGW
2272   int fd[2];
2273   int ret;
2274   int eno;
2275
2276   (void) inherit_read;
2277   (void) inherit_write;
2278   ret = pipe (fd);
2279   if (ret == -1)
2280   {
2281     eno = errno;
2282     LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR,
2283                   "pipe");
2284     errno = eno;
2285     return NULL;
2286   }
2287   return GNUNET_DISK_pipe_from_fd (blocking_read,
2288                                    blocking_write,
2289                                    fd);
2290 #else
2291   struct GNUNET_DISK_PipeHandle *p;
2292   BOOL ret;
2293   HANDLE tmp_handle;
2294   int save_errno;
2295
2296   p = GNUNET_new (struct GNUNET_DISK_PipeHandle);
2297   p->fd[0] = GNUNET_new (struct GNUNET_DISK_FileHandle);
2298   p->fd[1] = GNUNET_new (struct GNUNET_DISK_FileHandle);
2299
2300   /* All pipes are overlapped. If you want them to block - just
2301    * call WriteFile() and ReadFile() with NULL overlapped pointer.
2302    * NOTE: calling with NULL overlapped pointer works only
2303    * for pipes, and doesn't seem to be a documented feature.
2304    * It will NOT work for files, because overlapped files need
2305    * to read offsets from the overlapped structure, regardless.
2306    * Pipes are not seekable, and need no offsets, which is
2307    * probably why it works for them.
2308    */
2309   ret =
2310       create_selectable_pipe (&p->fd[0]->h, &p->fd[1]->h, NULL, 0,
2311                               FILE_FLAG_OVERLAPPED,
2312                               FILE_FLAG_OVERLAPPED);
2313   if (!ret)
2314   {
2315     SetErrnoFromWinError (GetLastError ());
2316     save_errno = errno;
2317     GNUNET_free (p->fd[0]);
2318     GNUNET_free (p->fd[1]);
2319     GNUNET_free (p);
2320     errno = save_errno;
2321     return NULL;
2322   }
2323   if (!DuplicateHandle
2324       (GetCurrentProcess (), p->fd[0]->h, GetCurrentProcess (), &tmp_handle, 0,
2325        inherit_read == GNUNET_YES ? TRUE : FALSE, DUPLICATE_SAME_ACCESS))
2326   {
2327     SetErrnoFromWinError (GetLastError ());
2328     save_errno = errno;
2329     CloseHandle (p->fd[0]->h);
2330     CloseHandle (p->fd[1]->h);
2331     GNUNET_free (p->fd[0]);
2332     GNUNET_free (p->fd[1]);
2333     GNUNET_free (p);
2334     errno = save_errno;
2335     return NULL;
2336   }
2337   CloseHandle (p->fd[0]->h);
2338   p->fd[0]->h = tmp_handle;
2339
2340   if (!DuplicateHandle
2341       (GetCurrentProcess (), p->fd[1]->h, GetCurrentProcess (), &tmp_handle, 0,
2342        inherit_write == GNUNET_YES ? TRUE : FALSE, DUPLICATE_SAME_ACCESS))
2343   {
2344     SetErrnoFromWinError (GetLastError ());
2345     save_errno = errno;
2346     CloseHandle (p->fd[0]->h);
2347     CloseHandle (p->fd[1]->h);
2348     GNUNET_free (p->fd[0]);
2349     GNUNET_free (p->fd[1]);
2350     GNUNET_free (p);
2351     errno = save_errno;
2352     return NULL;
2353   }
2354   CloseHandle (p->fd[1]->h);
2355   p->fd[1]->h = tmp_handle;
2356
2357   p->fd[0]->type = GNUNET_DISK_HANLDE_TYPE_PIPE;
2358   p->fd[1]->type = GNUNET_DISK_HANLDE_TYPE_PIPE;
2359
2360   p->fd[0]->oOverlapRead = GNUNET_new (OVERLAPPED);
2361   p->fd[0]->oOverlapWrite = GNUNET_new (OVERLAPPED);
2362   p->fd[1]->oOverlapRead = GNUNET_new (OVERLAPPED);
2363   p->fd[1]->oOverlapWrite = GNUNET_new (OVERLAPPED);
2364
2365   p->fd[0]->oOverlapRead->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2366   p->fd[0]->oOverlapWrite->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2367
2368   p->fd[1]->oOverlapRead->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2369   p->fd[1]->oOverlapWrite->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2370
2371   return p;
2372 #endif
2373 }
2374
2375
2376 /**
2377  * Creates a pipe object from a couple of file descriptors.
2378  * Useful for wrapping existing pipe FDs.
2379  *
2380  * @param blocking_read creates an asynchronous pipe for reading if set to GNUNET_NO
2381  * @param blocking_write creates an asynchronous pipe for writing if set to GNUNET_NO
2382  * @param fd an array of two fd values. One of them may be -1 for read-only or write-only pipes
2383  *
2384  * @return handle to the new pipe, NULL on error
2385  */
2386 struct GNUNET_DISK_PipeHandle *
2387 GNUNET_DISK_pipe_from_fd (int blocking_read, int blocking_write, int fd[2])
2388 {
2389   struct GNUNET_DISK_PipeHandle *p;
2390
2391   p = GNUNET_new (struct GNUNET_DISK_PipeHandle);
2392
2393 #ifndef MINGW
2394   int ret;
2395   int flags;
2396   int eno = 0; /* make gcc happy */
2397
2398   ret = 0;
2399   if (fd[0] >= 0)
2400   {
2401     p->fd[0] = GNUNET_new (struct GNUNET_DISK_FileHandle);
2402     p->fd[0]->fd = fd[0];
2403     if (!blocking_read)
2404     {
2405       flags = fcntl (fd[0], F_GETFL);
2406       flags |= O_NONBLOCK;
2407       if (0 > fcntl (fd[0], F_SETFL, flags))
2408       {
2409         ret = -1;
2410         eno = errno;
2411       }
2412     }
2413     flags = fcntl (fd[0], F_GETFD);
2414     flags |= FD_CLOEXEC;
2415     if (0 > fcntl (fd[0], F_SETFD, flags))
2416     {
2417       ret = -1;
2418       eno = errno;
2419     }
2420   }
2421
2422   if (fd[1] >= 0)
2423   {
2424     p->fd[1] = GNUNET_new (struct GNUNET_DISK_FileHandle);
2425     p->fd[1]->fd = fd[1];
2426     if (!blocking_write)
2427     {
2428       flags = fcntl (fd[1], F_GETFL);
2429       flags |= O_NONBLOCK;
2430       if (0 > fcntl (fd[1], F_SETFL, flags))
2431       {
2432         ret = -1;
2433         eno = errno;
2434       }
2435     }
2436     flags = fcntl (fd[1], F_GETFD);
2437     flags |= FD_CLOEXEC;
2438     if (0 > fcntl (fd[1], F_SETFD, flags))
2439     {
2440       ret = -1;
2441       eno = errno;
2442     }
2443   }
2444   if (ret == -1)
2445   {
2446     errno = eno;
2447     LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR, "fcntl");
2448     if (p->fd[0]->fd >= 0)
2449       GNUNET_break (0 == close (p->fd[0]->fd));
2450     if (p->fd[1]->fd >= 0)
2451       GNUNET_break (0 == close (p->fd[1]->fd));
2452     GNUNET_free_non_null (p->fd[0]);
2453     GNUNET_free_non_null (p->fd[1]);
2454     GNUNET_free (p);
2455     errno = eno;
2456     return NULL;
2457   }
2458 #else
2459   if (fd[0] >= 0)
2460   {
2461     p->fd[0] = GNUNET_new (struct GNUNET_DISK_FileHandle);
2462     p->fd[0]->h = (HANDLE) _get_osfhandle (fd[0]);
2463     if (p->fd[0]->h != INVALID_HANDLE_VALUE)
2464     {
2465       p->fd[0]->type = GNUNET_DISK_HANLDE_TYPE_PIPE;
2466       p->fd[0]->oOverlapRead = GNUNET_new (OVERLAPPED);
2467       p->fd[0]->oOverlapWrite = GNUNET_new (OVERLAPPED);
2468       p->fd[0]->oOverlapRead->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2469       p->fd[0]->oOverlapWrite->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2470     }
2471     else
2472     {
2473       GNUNET_free (p->fd[0]);
2474       p->fd[0] = NULL;
2475     }
2476   }
2477   if (fd[1] >= 0)
2478   {
2479     p->fd[1] = GNUNET_new (struct GNUNET_DISK_FileHandle);
2480     p->fd[1]->h = (HANDLE) _get_osfhandle (fd[1]);
2481     if (p->fd[1]->h != INVALID_HANDLE_VALUE)
2482     {
2483       p->fd[1]->type = GNUNET_DISK_HANLDE_TYPE_PIPE;
2484       p->fd[1]->oOverlapRead = GNUNET_new (OVERLAPPED);
2485       p->fd[1]->oOverlapWrite = GNUNET_new (OVERLAPPED);
2486       p->fd[1]->oOverlapRead->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2487       p->fd[1]->oOverlapWrite->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2488     }
2489     else
2490     {
2491       GNUNET_free (p->fd[1]);
2492       p->fd[1] = NULL;
2493     }
2494   }
2495
2496 #endif
2497   return p;
2498 }
2499
2500
2501 /**
2502  * Closes an interprocess channel
2503  *
2504  * @param p pipe to close
2505  * @param end which end of the pipe to close
2506  * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
2507  */
2508 int
2509 GNUNET_DISK_pipe_close_end (struct GNUNET_DISK_PipeHandle *p,
2510                             enum GNUNET_DISK_PipeEnd end)
2511 {
2512   int ret = GNUNET_OK;
2513
2514   if (end == GNUNET_DISK_PIPE_END_READ)
2515   {
2516     if (p->fd[0])
2517     {
2518       ret = GNUNET_DISK_file_close (p->fd[0]);
2519       p->fd[0] = NULL;
2520     }
2521   }
2522   else if (end == GNUNET_DISK_PIPE_END_WRITE)
2523   {
2524     if (p->fd[1])
2525     {
2526       ret = GNUNET_DISK_file_close (p->fd[1]);
2527       p->fd[1] = NULL;
2528     }
2529   }
2530
2531   return ret;
2532 }
2533
2534 /**
2535  * Detaches one of the ends from the pipe.
2536  * Detached end is a fully-functional FileHandle, it will
2537  * not be affected by anything you do with the pipe afterwards.
2538  * Each end of a pipe can only be detched from it once (i.e.
2539  * it is not duplicated).
2540  *
2541  * @param p pipe to detach an end from
2542  * @param end which end of the pipe to detach
2543  * @return Detached end on success, NULL on failure
2544  * (or if that end is not present or is closed).
2545  */
2546 struct GNUNET_DISK_FileHandle *
2547 GNUNET_DISK_pipe_detach_end (struct GNUNET_DISK_PipeHandle *p,
2548                              enum GNUNET_DISK_PipeEnd end)
2549 {
2550   struct GNUNET_DISK_FileHandle *ret = NULL;
2551
2552   if (end == GNUNET_DISK_PIPE_END_READ)
2553   {
2554     if (p->fd[0])
2555     {
2556       ret = p->fd[0];
2557       p->fd[0] = NULL;
2558     }
2559   }
2560   else if (end == GNUNET_DISK_PIPE_END_WRITE)
2561   {
2562     if (p->fd[1])
2563     {
2564       ret = p->fd[1];
2565       p->fd[1] = NULL;
2566     }
2567   }
2568
2569   return ret;
2570 }
2571
2572
2573 /**
2574  * Closes an interprocess channel
2575  *
2576  * @param p pipe to close
2577  * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
2578  */
2579 int
2580 GNUNET_DISK_pipe_close (struct GNUNET_DISK_PipeHandle *p)
2581 {
2582   int ret = GNUNET_OK;
2583
2584   int read_end_close;
2585   int write_end_close;
2586   int read_end_close_errno;
2587   int write_end_close_errno;
2588
2589   read_end_close = GNUNET_DISK_pipe_close_end (p, GNUNET_DISK_PIPE_END_READ);
2590   read_end_close_errno = errno;
2591   write_end_close = GNUNET_DISK_pipe_close_end (p, GNUNET_DISK_PIPE_END_WRITE);
2592   write_end_close_errno = errno;
2593   GNUNET_free (p);
2594
2595   if (GNUNET_OK != read_end_close)
2596   {
2597     errno = read_end_close_errno;
2598     ret = read_end_close;
2599   }
2600   else if (GNUNET_OK != write_end_close)
2601   {
2602     errno = write_end_close_errno;
2603     ret = write_end_close;
2604   }
2605
2606   return ret;
2607 }
2608
2609
2610 /**
2611  * Get the handle to a particular pipe end
2612  *
2613  * @param p pipe
2614  * @param n end to access
2615  * @return handle for the respective end
2616  */
2617 const struct GNUNET_DISK_FileHandle *
2618 GNUNET_DISK_pipe_handle (const struct GNUNET_DISK_PipeHandle *p,
2619                          enum GNUNET_DISK_PipeEnd n)
2620 {
2621   switch (n)
2622   {
2623   case GNUNET_DISK_PIPE_END_READ:
2624   case GNUNET_DISK_PIPE_END_WRITE:
2625     return p->fd[n];
2626   default:
2627     GNUNET_break (0);
2628     return NULL;
2629   }
2630 }
2631
2632
2633 /**
2634  * Retrieve OS file handle
2635  * @internal
2636  * @param fh GNUnet file descriptor
2637  * @param dst destination buffer
2638  * @param dst_len length of dst
2639  * @return #GNUNET_OK on success, #GNUNET_SYSERR otherwise
2640  */
2641 int
2642 GNUNET_DISK_internal_file_handle_ (const struct GNUNET_DISK_FileHandle *fh,
2643                                    void *dst, size_t dst_len)
2644 {
2645   if (NULL == fh)
2646     return GNUNET_SYSERR;
2647 #ifdef MINGW
2648   if (dst_len < sizeof (HANDLE))
2649     return GNUNET_SYSERR;
2650   *((HANDLE *) dst) = fh->h;
2651 #else
2652   if (dst_len < sizeof (int))
2653     return GNUNET_SYSERR;
2654   *((int *) dst) = fh->fd;
2655 #endif
2656
2657   return GNUNET_OK;
2658 }
2659
2660
2661 /**
2662  * Remove the directory given under @a option in
2663  * section [PATHS] in configuration under @a cfg_filename
2664  *
2665  * @param cfg_filename configuration file to parse
2666  * @param option option with the dir name to purge
2667  */
2668 void
2669 GNUNET_DISK_purge_cfg_dir (const char *cfg_filename,
2670                            const char *option)
2671 {
2672   struct GNUNET_CONFIGURATION_Handle *cfg;
2673   char *tmpname;
2674
2675   cfg = GNUNET_CONFIGURATION_create ();
2676   if (GNUNET_OK !=
2677       GNUNET_CONFIGURATION_load (cfg,
2678                                  cfg_filename))
2679   {
2680     GNUNET_break (0);
2681     GNUNET_CONFIGURATION_destroy (cfg);
2682     return;
2683   }
2684   if (GNUNET_OK !=
2685       GNUNET_CONFIGURATION_get_value_filename (cfg,
2686                                                "PATHS",
2687                                                option,
2688                                                &tmpname))
2689   {
2690     GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
2691                                "PATHS",
2692                                option);
2693     GNUNET_CONFIGURATION_destroy (cfg);
2694     return;
2695   }
2696   GNUNET_CONFIGURATION_destroy (cfg);
2697   if (GNUNET_SYSERR ==
2698       GNUNET_DISK_directory_remove (tmpname))
2699   {
2700     GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR,
2701                               "remove",
2702                               tmpname);
2703     GNUNET_free (tmpname);
2704     return;
2705   }
2706   GNUNET_free (tmpname);
2707 }
2708
2709
2710
2711 /* end of disk.c */