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