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, GNUNET_DISK_OPEN_READ, 0);
333     if (fh == NULL)
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) GNUNET_DISK_directory_remove (fn);
1326   return GNUNET_OK;
1327 }
1328
1329
1330 /**
1331  * Remove all files in a directory (rm -r). Call with
1332  * caution.
1333  *
1334  * @param filename the file to remove
1335  * @return #GNUNET_OK on success, #GNUNET_SYSERR on error
1336  */
1337 int
1338 GNUNET_DISK_directory_remove (const char *filename)
1339 {
1340   struct stat istat;
1341
1342   if (NULL == filename)
1343   {
1344     GNUNET_break (0);
1345     return GNUNET_SYSERR;
1346   }
1347   if (0 != LSTAT (filename, &istat))
1348     return GNUNET_NO;           /* file may not exist... */
1349   (void) CHMOD (filename,
1350                 S_IWUSR | S_IRUSR | S_IXUSR);
1351   if (0 == UNLINK (filename))
1352     return GNUNET_OK;
1353   if ( (errno != EISDIR) &&
1354        /* EISDIR is not sufficient in all cases, e.g.
1355         * sticky /tmp directory may result in EPERM on BSD.
1356         * So we also explicitly check "isDirectory" */
1357        (GNUNET_YES !=
1358         GNUNET_DISK_directory_test (filename,
1359                                     GNUNET_YES)) )
1360   {
1361     LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING,
1362                        "rmdir",
1363                        filename);
1364     return GNUNET_SYSERR;
1365   }
1366   if (GNUNET_SYSERR ==
1367       GNUNET_DISK_directory_scan (filename,
1368                                   &remove_helper,
1369                                   NULL))
1370     return GNUNET_SYSERR;
1371   if (0 != RMDIR (filename))
1372   {
1373     LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING,
1374                        "rmdir",
1375                        filename);
1376     return GNUNET_SYSERR;
1377   }
1378   return GNUNET_OK;
1379 }
1380
1381
1382 /**
1383  * Copy a file.
1384  *
1385  * @param src file to copy
1386  * @param dst destination file name
1387  * @return #GNUNET_OK on success, #GNUNET_SYSERR on error
1388  */
1389 int
1390 GNUNET_DISK_file_copy (const char *src,
1391                        const char *dst)
1392 {
1393   char *buf;
1394   uint64_t pos;
1395   uint64_t size;
1396   size_t len;
1397   struct GNUNET_DISK_FileHandle *in;
1398   struct GNUNET_DISK_FileHandle *out;
1399
1400   if (GNUNET_OK != GNUNET_DISK_file_size (src, &size, GNUNET_YES, GNUNET_YES))
1401     return GNUNET_SYSERR;
1402   pos = 0;
1403   in = GNUNET_DISK_file_open (src, GNUNET_DISK_OPEN_READ,
1404                               GNUNET_DISK_PERM_NONE);
1405   if (!in)
1406     return GNUNET_SYSERR;
1407   out =
1408       GNUNET_DISK_file_open (dst,
1409                              GNUNET_DISK_OPEN_WRITE | GNUNET_DISK_OPEN_CREATE |
1410                              GNUNET_DISK_OPEN_FAILIFEXISTS,
1411                              GNUNET_DISK_PERM_USER_READ |
1412                              GNUNET_DISK_PERM_USER_WRITE |
1413                              GNUNET_DISK_PERM_GROUP_READ |
1414                              GNUNET_DISK_PERM_GROUP_WRITE);
1415   if (!out)
1416   {
1417     GNUNET_DISK_file_close (in);
1418     return GNUNET_SYSERR;
1419   }
1420   buf = GNUNET_malloc (COPY_BLK_SIZE);
1421   while (pos < size)
1422   {
1423     len = COPY_BLK_SIZE;
1424     if (len > size - pos)
1425       len = size - pos;
1426     if (len != GNUNET_DISK_file_read (in, buf, len))
1427       goto FAIL;
1428     if (len != GNUNET_DISK_file_write (out, buf, len))
1429       goto FAIL;
1430     pos += len;
1431   }
1432   GNUNET_free (buf);
1433   GNUNET_DISK_file_close (in);
1434   GNUNET_DISK_file_close (out);
1435   return GNUNET_OK;
1436 FAIL:
1437   GNUNET_free (buf);
1438   GNUNET_DISK_file_close (in);
1439   GNUNET_DISK_file_close (out);
1440   return GNUNET_SYSERR;
1441 }
1442
1443
1444 /**
1445  * @brief Removes special characters as ':' from a filename.
1446  * @param fn the filename to canonicalize
1447  */
1448 void
1449 GNUNET_DISK_filename_canonicalize (char *fn)
1450 {
1451   char *idx;
1452   char c;
1453
1454   for (idx = fn; *idx; idx++)
1455   {
1456     c = *idx;
1457
1458     if (c == '/' || c == '\\' || c == ':' || c == '*' || c == '?' || c == '"' ||
1459         c == '<' || c == '>' || c == '|')
1460     {
1461       *idx = '_';
1462     }
1463   }
1464 }
1465
1466
1467
1468 /**
1469  * @brief Change owner of a file
1470  *
1471  * @param filename name of file to change the owner of
1472  * @param user name of the new owner
1473  * @return #GNUNET_OK on success, #GNUNET_SYSERR on failure
1474  */
1475 int
1476 GNUNET_DISK_file_change_owner (const char *filename,
1477                                const char *user)
1478 {
1479 #ifndef MINGW
1480   struct passwd *pws;
1481
1482   pws = getpwnam (user);
1483   if (NULL == pws)
1484   {
1485     LOG (GNUNET_ERROR_TYPE_ERROR,
1486          _("Cannot obtain information about user `%s': %s\n"),
1487          user,
1488          STRERROR (errno));
1489     return GNUNET_SYSERR;
1490   }
1491   if (0 != chown (filename,
1492                   pws->pw_uid,
1493                   pws->pw_gid))
1494   {
1495     LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING,
1496                        "chown",
1497                        filename);
1498     return GNUNET_SYSERR;
1499   }
1500 #endif
1501   return GNUNET_OK;
1502 }
1503
1504
1505 /**
1506  * Lock a part of a file
1507  *
1508  * @param fh file handle
1509  * @param lock_start absolute position from where to lock
1510  * @param lock_end absolute position until where to lock
1511  * @param excl #GNUNET_YES for an exclusive lock
1512  * @return #GNUNET_OK on success, #GNUNET_SYSERR on error
1513  */
1514 int
1515 GNUNET_DISK_file_lock (struct GNUNET_DISK_FileHandle *fh,
1516                        off_t lock_start,
1517                        off_t lock_end,
1518                        int excl)
1519 {
1520   if (fh == NULL)
1521   {
1522     errno = EINVAL;
1523     return GNUNET_SYSERR;
1524   }
1525
1526 #ifndef MINGW
1527   struct flock fl;
1528
1529   memset (&fl, 0, sizeof (struct flock));
1530   fl.l_type = excl ? F_WRLCK : F_RDLCK;
1531   fl.l_whence = SEEK_SET;
1532   fl.l_start = lock_start;
1533   fl.l_len = lock_end;
1534
1535   return fcntl (fh->fd, F_SETLK, &fl) != 0 ? GNUNET_SYSERR : GNUNET_OK;
1536 #else
1537   OVERLAPPED o;
1538   off_t diff = lock_end - lock_start;
1539   DWORD diff_low, diff_high;
1540   diff_low = (DWORD) (diff & 0xFFFFFFFF);
1541   diff_high = (DWORD) ((diff >> (sizeof (DWORD) * 8)) & 0xFFFFFFFF);
1542
1543   memset (&o, 0, sizeof (OVERLAPPED));
1544   o.Offset = (DWORD) (lock_start & 0xFFFFFFFF);;
1545   o.OffsetHigh = (DWORD) (((lock_start & ~0xFFFFFFFF) >> (sizeof (DWORD) * 8)) & 0xFFFFFFFF);
1546
1547   if (!LockFileEx
1548       (fh->h, (excl ? LOCKFILE_EXCLUSIVE_LOCK : 0) | LOCKFILE_FAIL_IMMEDIATELY,
1549        0, diff_low, diff_high, &o))
1550   {
1551     SetErrnoFromWinError (GetLastError ());
1552     return GNUNET_SYSERR;
1553   }
1554
1555   return GNUNET_OK;
1556 #endif
1557 }
1558
1559
1560 /**
1561  * Unlock a part of a file
1562  *
1563  * @param fh file handle
1564  * @param unlock_start absolute position from where to unlock
1565  * @param unlock_end absolute position until where to unlock
1566  * @return #GNUNET_OK on success, #GNUNET_SYSERR on error
1567  */
1568 int
1569 GNUNET_DISK_file_unlock (struct GNUNET_DISK_FileHandle *fh,
1570                          off_t unlock_start,
1571                          off_t unlock_end)
1572 {
1573   if (fh == NULL)
1574   {
1575     errno = EINVAL;
1576     return GNUNET_SYSERR;
1577   }
1578
1579 #ifndef MINGW
1580   struct flock fl;
1581
1582   memset (&fl, 0, sizeof (struct flock));
1583   fl.l_type = F_UNLCK;
1584   fl.l_whence = SEEK_SET;
1585   fl.l_start = unlock_start;
1586   fl.l_len = unlock_end;
1587
1588   return fcntl (fh->fd, F_SETLK, &fl) != 0 ? GNUNET_SYSERR : GNUNET_OK;
1589 #else
1590   OVERLAPPED o;
1591   off_t diff = unlock_end - unlock_start;
1592   DWORD diff_low, diff_high;
1593   diff_low = (DWORD) (diff & 0xFFFFFFFF);
1594   diff_high = (DWORD) ((diff >> (sizeof (DWORD) * 8)) & 0xFFFFFFFF);
1595
1596   memset (&o, 0, sizeof (OVERLAPPED));
1597   o.Offset = (DWORD) (unlock_start & 0xFFFFFFFF);;
1598   o.OffsetHigh = (DWORD) (((unlock_start & ~0xFFFFFFFF) >> (sizeof (DWORD) * 8)) & 0xFFFFFFFF);
1599
1600   if (!UnlockFileEx (fh->h, 0, diff_low, diff_high, &o))
1601   {
1602     SetErrnoFromWinError (GetLastError ());
1603     return GNUNET_SYSERR;
1604   }
1605
1606   return GNUNET_OK;
1607 #endif
1608 }
1609
1610
1611 /**
1612  * Open a file.  Note that the access permissions will only be
1613  * used if a new file is created and if the underlying operating
1614  * system supports the given permissions.
1615  *
1616  * @param fn file name to be opened
1617  * @param flags opening flags, a combination of GNUNET_DISK_OPEN_xxx bit flags
1618  * @param perm permissions for the newly created file, use
1619  *             #GNUNET_DISK_PERM_NONE if a file could not be created by this
1620  *             call (because of flags)
1621  * @return IO handle on success, NULL on error
1622  */
1623 struct GNUNET_DISK_FileHandle *
1624 GNUNET_DISK_file_open (const char *fn,
1625                        enum GNUNET_DISK_OpenFlags flags,
1626                        enum GNUNET_DISK_AccessPermissions perm)
1627 {
1628   char *expfn;
1629   struct GNUNET_DISK_FileHandle *ret;
1630
1631 #ifdef MINGW
1632   DWORD access;
1633   DWORD disp;
1634   HANDLE h;
1635   wchar_t wexpfn[MAX_PATH + 1];
1636 #else
1637   int oflags;
1638   int mode;
1639   int fd;
1640 #endif
1641
1642   expfn = GNUNET_STRINGS_filename_expand (fn);
1643   if (NULL == expfn)
1644     return NULL;
1645 #ifndef MINGW
1646   mode = 0;
1647   if (GNUNET_DISK_OPEN_READWRITE == (flags & GNUNET_DISK_OPEN_READWRITE))
1648     oflags = O_RDWR;            /* note: O_RDWR is NOT always O_RDONLY | O_WRONLY */
1649   else if (flags & GNUNET_DISK_OPEN_READ)
1650     oflags = O_RDONLY;
1651   else if (flags & GNUNET_DISK_OPEN_WRITE)
1652     oflags = O_WRONLY;
1653   else
1654   {
1655     GNUNET_break (0);
1656     GNUNET_free (expfn);
1657     return NULL;
1658   }
1659   if (flags & GNUNET_DISK_OPEN_FAILIFEXISTS)
1660     oflags |= (O_CREAT | O_EXCL);
1661   if (flags & GNUNET_DISK_OPEN_TRUNCATE)
1662     oflags |= O_TRUNC;
1663   if (flags & GNUNET_DISK_OPEN_APPEND)
1664     oflags |= O_APPEND;
1665   if (flags & GNUNET_DISK_OPEN_CREATE)
1666   {
1667     (void) GNUNET_DISK_directory_create_for_file (expfn);
1668     oflags |= O_CREAT;
1669     mode = translate_unix_perms (perm);
1670   }
1671
1672   fd = open (expfn, oflags
1673 #if O_CLOEXEC
1674              | O_CLOEXEC
1675 #endif
1676              | O_LARGEFILE, mode);
1677   if (fd == -1)
1678   {
1679     if (0 == (flags & GNUNET_DISK_OPEN_FAILIFEXISTS))
1680       LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "open", expfn);
1681     else
1682       LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_DEBUG, "open", expfn);
1683     GNUNET_free (expfn);
1684     return NULL;
1685   }
1686 #else
1687   access = 0;
1688   disp = OPEN_ALWAYS;
1689
1690   if (GNUNET_DISK_OPEN_READWRITE == (flags & GNUNET_DISK_OPEN_READWRITE))
1691     access = FILE_READ_DATA | FILE_WRITE_DATA;
1692   else if (flags & GNUNET_DISK_OPEN_READ)
1693     access = FILE_READ_DATA;
1694   else if (flags & GNUNET_DISK_OPEN_WRITE)
1695     access = FILE_WRITE_DATA;
1696
1697   if (flags & GNUNET_DISK_OPEN_FAILIFEXISTS)
1698   {
1699     disp = CREATE_NEW;
1700   }
1701   else if (flags & GNUNET_DISK_OPEN_CREATE)
1702   {
1703     (void) GNUNET_DISK_directory_create_for_file (expfn);
1704     if (flags & GNUNET_DISK_OPEN_TRUNCATE)
1705       disp = CREATE_ALWAYS;
1706     else
1707       disp = OPEN_ALWAYS;
1708   }
1709   else if (flags & GNUNET_DISK_OPEN_TRUNCATE)
1710   {
1711     disp = TRUNCATE_EXISTING;
1712   }
1713   else
1714   {
1715     disp = OPEN_EXISTING;
1716   }
1717
1718   if (ERROR_SUCCESS == plibc_conv_to_win_pathwconv(expfn, wexpfn))
1719     h = CreateFileW (wexpfn, access,
1720                     FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE, NULL,
1721                     disp, FILE_ATTRIBUTE_NORMAL, NULL);
1722   else
1723     h = INVALID_HANDLE_VALUE;
1724   if (h == INVALID_HANDLE_VALUE)
1725   {
1726     int err;
1727     SetErrnoFromWinError (GetLastError ());
1728     err = errno;
1729     LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_INFO, "open", expfn);
1730     GNUNET_free (expfn);
1731     errno = err;
1732     return NULL;
1733   }
1734
1735   if (flags & GNUNET_DISK_OPEN_APPEND)
1736     if (SetFilePointer (h, 0, 0, FILE_END) == INVALID_SET_FILE_POINTER)
1737     {
1738       SetErrnoFromWinError (GetLastError ());
1739       LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "SetFilePointer", expfn);
1740       CloseHandle (h);
1741       GNUNET_free (expfn);
1742       return NULL;
1743     }
1744 #endif
1745
1746   ret = GNUNET_new (struct GNUNET_DISK_FileHandle);
1747 #ifdef MINGW
1748   ret->h = h;
1749   ret->type = GNUNET_DISK_HANLDE_TYPE_FILE;
1750 #else
1751   ret->fd = fd;
1752 #endif
1753   GNUNET_free (expfn);
1754   return ret;
1755 }
1756
1757
1758 /**
1759  * Close an open file
1760  * @param h file handle
1761  * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
1762  */
1763 int
1764 GNUNET_DISK_file_close (struct GNUNET_DISK_FileHandle *h)
1765 {
1766   int ret;
1767   if (h == NULL)
1768   {
1769     errno = EINVAL;
1770     return GNUNET_SYSERR;
1771   }
1772
1773   ret = GNUNET_OK;
1774
1775 #if MINGW
1776   if (!CloseHandle (h->h))
1777   {
1778     SetErrnoFromWinError (GetLastError ());
1779     LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING, "close");
1780     ret = GNUNET_SYSERR;
1781   }
1782   if (h->oOverlapRead)
1783   {
1784     if (!CloseHandle (h->oOverlapRead->hEvent))
1785     {
1786       SetErrnoFromWinError (GetLastError ());
1787       LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING, "close");
1788       ret = GNUNET_SYSERR;
1789     }
1790     GNUNET_free (h->oOverlapRead);
1791   }
1792   if (h->oOverlapWrite)
1793   {
1794     if (!CloseHandle (h->oOverlapWrite->hEvent))
1795     {
1796       SetErrnoFromWinError (GetLastError ());
1797       LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING, "close");
1798       ret = GNUNET_SYSERR;
1799     }
1800     GNUNET_free (h->oOverlapWrite);
1801   }
1802 #else
1803   if (close (h->fd) != 0)
1804   {
1805     LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING, "close");
1806     ret = GNUNET_SYSERR;
1807   }
1808 #endif
1809   GNUNET_free (h);
1810   return ret;
1811 }
1812
1813
1814 #ifdef WINDOWS
1815 /**
1816  * Get a GNUnet file handle from a W32 handle.
1817  *
1818  * @param handle native handle
1819  * @return GNUnet file handle corresponding to the W32 handle
1820  */
1821 struct GNUNET_DISK_FileHandle *
1822 GNUNET_DISK_get_handle_from_w32_handle (HANDLE osfh)
1823 {
1824   struct GNUNET_DISK_FileHandle *fh;
1825
1826   DWORD dwret;
1827   enum GNUNET_FILE_Type ftype;
1828
1829   dwret = GetFileType (osfh);
1830   switch (dwret)
1831   {
1832   case FILE_TYPE_DISK:
1833     ftype = GNUNET_DISK_HANLDE_TYPE_FILE;
1834     break;
1835   case FILE_TYPE_PIPE:
1836     ftype = GNUNET_DISK_HANLDE_TYPE_PIPE;
1837     break;
1838   case FILE_TYPE_UNKNOWN:
1839     if (GetLastError () == NO_ERROR || GetLastError () == ERROR_INVALID_HANDLE)
1840     {
1841       if (0 != ResetEvent (osfh))
1842         ftype = GNUNET_DISK_HANLDE_TYPE_EVENT;
1843       else
1844         return NULL;
1845     }
1846     else
1847       return NULL;
1848     break;
1849   default:
1850     return NULL;
1851   }
1852
1853   fh = GNUNET_new (struct GNUNET_DISK_FileHandle);
1854
1855   fh->h = osfh;
1856   fh->type = ftype;
1857   if (ftype == GNUNET_DISK_HANLDE_TYPE_PIPE)
1858   {
1859     /**
1860      * Note that we can't make it overlapped if it isn't already.
1861      * (ReOpenFile() is only available in 2003/Vista).
1862      * The process that opened this file in the first place (usually a parent
1863      * process, if this is stdin/stdout/stderr) must make it overlapped,
1864      * otherwise we're screwed, as selecting on non-overlapped handle
1865      * will block.
1866      */
1867     fh->oOverlapRead = GNUNET_new (OVERLAPPED);
1868     fh->oOverlapWrite = GNUNET_new (OVERLAPPED);
1869     fh->oOverlapRead->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
1870     fh->oOverlapWrite->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
1871   }
1872
1873   return fh;
1874 }
1875 #endif
1876
1877 /**
1878  * Get a handle from a native integer FD.
1879  *
1880  * @param fno native integer file descriptor
1881  * @return file handle corresponding to the descriptor, NULL on error
1882  */
1883 struct GNUNET_DISK_FileHandle *
1884 GNUNET_DISK_get_handle_from_int_fd (int fno)
1885 {
1886   struct GNUNET_DISK_FileHandle *fh;
1887
1888   if ( (((off_t) -1) == lseek (fno, 0, SEEK_CUR)) &&
1889        (EBADF == errno) )
1890     return NULL; /* invalid FD */
1891
1892 #ifndef WINDOWS
1893   fh = GNUNET_new (struct GNUNET_DISK_FileHandle);
1894
1895   fh->fd = fno;
1896 #else
1897   intptr_t osfh;
1898
1899   osfh = _get_osfhandle (fno);
1900   if (INVALID_HANDLE_VALUE == (HANDLE) osfh)
1901     return NULL;
1902
1903   fh = GNUNET_DISK_get_handle_from_w32_handle ((HANDLE) osfh);
1904 #endif
1905
1906   return fh;
1907 }
1908
1909
1910 /**
1911  * Get a handle from a native streaming FD.
1912  *
1913  * @param fd native streaming file descriptor
1914  * @return file handle corresponding to the descriptor
1915  */
1916 struct GNUNET_DISK_FileHandle *
1917 GNUNET_DISK_get_handle_from_native (FILE *fd)
1918 {
1919   int fno;
1920
1921   fno = fileno (fd);
1922   if (-1 == fno)
1923     return NULL;
1924
1925   return GNUNET_DISK_get_handle_from_int_fd (fno);
1926 }
1927
1928
1929 /**
1930  * Handle for a memory-mapping operation.
1931  */
1932 struct GNUNET_DISK_MapHandle
1933 {
1934   /**
1935    * Address where the map is in memory.
1936    */
1937   void *addr;
1938
1939 #ifdef MINGW
1940   /**
1941    * Underlying OS handle.
1942    */
1943   HANDLE h;
1944 #else
1945   /**
1946    * Number of bytes mapped.
1947    */
1948   size_t len;
1949 #endif
1950 };
1951
1952
1953 #ifndef MAP_FAILED
1954 #define MAP_FAILED ((void *) -1)
1955 #endif
1956
1957 /**
1958  * Map a file into memory
1959  *
1960  * @param h open file handle
1961  * @param m handle to the new mapping
1962  * @param access access specification, GNUNET_DISK_MAP_TYPE_xxx
1963  * @param len size of the mapping
1964  * @return pointer to the mapped memory region, NULL on failure
1965  */
1966 void *
1967 GNUNET_DISK_file_map (const struct GNUNET_DISK_FileHandle *h,
1968                       struct GNUNET_DISK_MapHandle **m,
1969                       enum GNUNET_DISK_MapType access, size_t len)
1970 {
1971   if (h == NULL)
1972   {
1973     errno = EINVAL;
1974     return NULL;
1975   }
1976
1977 #ifdef MINGW
1978   DWORD mapAccess, protect;
1979
1980   if ((access & GNUNET_DISK_MAP_TYPE_READ) &&
1981       (access & GNUNET_DISK_MAP_TYPE_WRITE))
1982   {
1983     protect = PAGE_READWRITE;
1984     mapAccess = FILE_MAP_ALL_ACCESS;
1985   }
1986   else if (access & GNUNET_DISK_MAP_TYPE_READ)
1987   {
1988     protect = PAGE_READONLY;
1989     mapAccess = FILE_MAP_READ;
1990   }
1991   else if (access & GNUNET_DISK_MAP_TYPE_WRITE)
1992   {
1993     protect = PAGE_READWRITE;
1994     mapAccess = FILE_MAP_WRITE;
1995   }
1996   else
1997   {
1998     GNUNET_break (0);
1999     return NULL;
2000   }
2001
2002   *m = GNUNET_new (struct GNUNET_DISK_MapHandle);
2003   (*m)->h = CreateFileMapping (h->h, NULL, protect, 0, 0, NULL);
2004   if ((*m)->h == INVALID_HANDLE_VALUE)
2005   {
2006     SetErrnoFromWinError (GetLastError ());
2007     GNUNET_free (*m);
2008     return NULL;
2009   }
2010
2011   (*m)->addr = MapViewOfFile ((*m)->h, mapAccess, 0, 0, len);
2012   if (!(*m)->addr)
2013   {
2014     SetErrnoFromWinError (GetLastError ());
2015     CloseHandle ((*m)->h);
2016     GNUNET_free (*m);
2017   }
2018
2019   return (*m)->addr;
2020 #else
2021   int prot;
2022
2023   prot = 0;
2024   if (access & GNUNET_DISK_MAP_TYPE_READ)
2025     prot = PROT_READ;
2026   if (access & GNUNET_DISK_MAP_TYPE_WRITE)
2027     prot |= PROT_WRITE;
2028   *m = GNUNET_new (struct GNUNET_DISK_MapHandle);
2029   (*m)->addr = mmap (NULL, len, prot, MAP_SHARED, h->fd, 0);
2030   GNUNET_assert (NULL != (*m)->addr);
2031   if (MAP_FAILED == (*m)->addr)
2032   {
2033     GNUNET_free (*m);
2034     return NULL;
2035   }
2036   (*m)->len = len;
2037   return (*m)->addr;
2038 #endif
2039 }
2040
2041 /**
2042  * Unmap a file
2043  * @param h mapping handle
2044  * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
2045  */
2046 int
2047 GNUNET_DISK_file_unmap (struct GNUNET_DISK_MapHandle *h)
2048 {
2049   int ret;
2050
2051   if (h == NULL)
2052   {
2053     errno = EINVAL;
2054     return GNUNET_SYSERR;
2055   }
2056
2057 #ifdef MINGW
2058   ret = UnmapViewOfFile (h->addr) ? GNUNET_OK : GNUNET_SYSERR;
2059   if (ret != GNUNET_OK)
2060     SetErrnoFromWinError (GetLastError ());
2061   if (!CloseHandle (h->h) && (ret == GNUNET_OK))
2062   {
2063     ret = GNUNET_SYSERR;
2064     SetErrnoFromWinError (GetLastError ());
2065   }
2066 #else
2067   ret = munmap (h->addr, h->len) != -1 ? GNUNET_OK : GNUNET_SYSERR;
2068 #endif
2069   GNUNET_free (h);
2070   return ret;
2071 }
2072
2073
2074 /**
2075  * Write file changes to disk
2076  * @param h handle to an open file
2077  * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
2078  */
2079 int
2080 GNUNET_DISK_file_sync (const struct GNUNET_DISK_FileHandle *h)
2081 {
2082   if (h == NULL)
2083   {
2084     errno = EINVAL;
2085     return GNUNET_SYSERR;
2086   }
2087
2088 #ifdef MINGW
2089   int ret;
2090
2091   ret = FlushFileBuffers (h->h) ? GNUNET_OK : GNUNET_SYSERR;
2092   if (ret != GNUNET_OK)
2093     SetErrnoFromWinError (GetLastError ());
2094   return ret;
2095 #elif defined(FREEBSD) || defined(OPENBSD) || defined(DARWIN)
2096   return fsync (h->fd) == -1 ? GNUNET_SYSERR : GNUNET_OK;
2097 #else
2098   return fdatasync (h->fd) == -1 ? GNUNET_SYSERR : GNUNET_OK;
2099 #endif
2100 }
2101
2102
2103 #if WINDOWS
2104 #ifndef PIPE_BUF
2105 #define PIPE_BUF        512
2106 #endif
2107 /* Copyright Bob Byrnes  <byrnes <at> curl.com>
2108    http://permalink.gmane.org/gmane.os.cygwin.patches/2121
2109 */
2110 /* Create a pipe, and return handles to the read and write ends,
2111    just like CreatePipe, but ensure that the write end permits
2112    FILE_READ_ATTRIBUTES access, on later versions of win32 where
2113    this is supported.  This access is needed by NtQueryInformationFile,
2114    which is used to implement select and nonblocking writes.
2115    Note that the return value is either NO_ERROR or GetLastError,
2116    unlike CreatePipe, which returns a bool for success or failure.  */
2117 static int
2118 create_selectable_pipe (PHANDLE read_pipe_ptr, PHANDLE write_pipe_ptr,
2119                         LPSECURITY_ATTRIBUTES sa_ptr, DWORD psize,
2120                         DWORD dwReadMode, DWORD dwWriteMode)
2121 {
2122   /* Default to error. */
2123   *read_pipe_ptr = *write_pipe_ptr = INVALID_HANDLE_VALUE;
2124
2125   HANDLE read_pipe;
2126   HANDLE write_pipe;
2127
2128   /* Ensure that there is enough pipe buffer space for atomic writes.  */
2129   if (psize < PIPE_BUF)
2130     psize = PIPE_BUF;
2131
2132   char pipename[MAX_PATH];
2133
2134   /* Retry CreateNamedPipe as long as the pipe name is in use.
2135    * Retrying will probably never be necessary, but we want
2136    * to be as robust as possible.  */
2137   while (1)
2138   {
2139     static volatile LONG pipe_unique_id;
2140
2141     snprintf (pipename, sizeof pipename, "\\\\.\\pipe\\gnunet-%d-%ld",
2142               getpid (), InterlockedIncrement ((LONG *) & pipe_unique_id));
2143     LOG (GNUNET_ERROR_TYPE_DEBUG, "CreateNamedPipe: name = %s, size = %lu\n",
2144          pipename, psize);
2145     /* Use CreateNamedPipe instead of CreatePipe, because the latter
2146      * returns a write handle that does not permit FILE_READ_ATTRIBUTES
2147      * access, on versions of win32 earlier than WinXP SP2.
2148      * CreatePipe also stupidly creates a full duplex pipe, which is
2149      * a waste, since only a single direction is actually used.
2150      * It's important to only allow a single instance, to ensure that
2151      * the pipe was not created earlier by some other process, even if
2152      * the pid has been reused.  */
2153     read_pipe = CreateNamedPipeA (pipename, PIPE_ACCESS_INBOUND | FILE_FLAG_FIRST_PIPE_INSTANCE | dwReadMode, PIPE_TYPE_BYTE | PIPE_READMODE_BYTE, 1,   /* max instances */
2154                                   psize,        /* output buffer size */
2155                                   psize,        /* input buffer size */
2156                                   NMPWAIT_USE_DEFAULT_WAIT, sa_ptr);
2157
2158     if (read_pipe != INVALID_HANDLE_VALUE)
2159     {
2160       LOG (GNUNET_ERROR_TYPE_DEBUG, "pipe read handle = %p\n", read_pipe);
2161       break;
2162     }
2163
2164     DWORD err = GetLastError ();
2165
2166     switch (err)
2167     {
2168     case ERROR_PIPE_BUSY:
2169       /* The pipe is already open with compatible parameters.
2170        * Pick a new name and retry.  */
2171       LOG (GNUNET_ERROR_TYPE_DEBUG, "pipe busy, retrying\n");
2172       continue;
2173     case ERROR_ACCESS_DENIED:
2174       /* The pipe is already open with incompatible parameters.
2175        * Pick a new name and retry.  */
2176       LOG (GNUNET_ERROR_TYPE_DEBUG, "pipe access denied, retrying\n");
2177       continue;
2178     case ERROR_CALL_NOT_IMPLEMENTED:
2179       /* We are on an older Win9x platform without named pipes.
2180        * Return an anonymous pipe as the best approximation.  */
2181       LOG (GNUNET_ERROR_TYPE_DEBUG,
2182            "CreateNamedPipe not implemented, resorting to "
2183            "CreatePipe: size = %lu\n", psize);
2184       if (CreatePipe (read_pipe_ptr, write_pipe_ptr, sa_ptr, psize))
2185       {
2186         LOG (GNUNET_ERROR_TYPE_DEBUG, "pipe read handle = %p, write handle = %p\n",
2187              *read_pipe_ptr,
2188              *write_pipe_ptr);
2189         return GNUNET_OK;
2190       }
2191       err = GetLastError ();
2192       LOG (GNUNET_ERROR_TYPE_ERROR, "CreatePipe failed: %d\n", err);
2193       return err;
2194     default:
2195       LOG (GNUNET_ERROR_TYPE_ERROR, "CreateNamedPipe failed: %d\n", err);
2196       return err;
2197     }
2198     /* NOTREACHED */
2199   }
2200   LOG (GNUNET_ERROR_TYPE_DEBUG, "CreateFile: name = %s\n", pipename);
2201
2202   /* Open the named pipe for writing.
2203    * Be sure to permit FILE_READ_ATTRIBUTES access.  */
2204   write_pipe = CreateFileA (pipename, GENERIC_WRITE | FILE_READ_ATTRIBUTES, 0,  /* share mode */
2205                             sa_ptr, OPEN_EXISTING, dwWriteMode, /* flags and attributes */
2206                             0); /* handle to template file */
2207
2208   if (write_pipe == INVALID_HANDLE_VALUE)
2209   {
2210     /* Failure. */
2211     DWORD err = GetLastError ();
2212
2213     LOG (GNUNET_ERROR_TYPE_DEBUG, "CreateFile failed: %d\n", err);
2214     CloseHandle (read_pipe);
2215     return err;
2216   }
2217   LOG (GNUNET_ERROR_TYPE_DEBUG, "pipe write handle = %p\n", write_pipe);
2218   /* Success. */
2219   *read_pipe_ptr = read_pipe;
2220   *write_pipe_ptr = write_pipe;
2221   return GNUNET_OK;
2222 }
2223 #endif
2224
2225
2226 /**
2227  * Creates an interprocess channel
2228  *
2229  * @param blocking_read creates an asynchronous pipe for reading if set to GNUNET_NO
2230  * @param blocking_write creates an asynchronous pipe for writing if set to GNUNET_NO
2231  * @param inherit_read inherit the parent processes stdin (only for windows)
2232  * @param inherit_write inherit the parent processes stdout (only for windows)
2233  * @return handle to the new pipe, NULL on error
2234  */
2235 struct GNUNET_DISK_PipeHandle *
2236 GNUNET_DISK_pipe (int blocking_read, int blocking_write, int inherit_read, int inherit_write)
2237 {
2238 #ifndef MINGW
2239   int fd[2];
2240   int ret;
2241   int eno;
2242
2243   ret = pipe (fd);
2244   if (ret == -1)
2245   {
2246     eno = errno;
2247     LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR, "pipe");
2248     errno = eno;
2249     return NULL;
2250   }
2251   return GNUNET_DISK_pipe_from_fd (blocking_read,
2252                                    blocking_write,
2253                                    fd);
2254 #else
2255   struct GNUNET_DISK_PipeHandle *p;
2256   BOOL ret;
2257   HANDLE tmp_handle;
2258   int save_errno;
2259
2260   p = GNUNET_new (struct GNUNET_DISK_PipeHandle);
2261   p->fd[0] = GNUNET_new (struct GNUNET_DISK_FileHandle);
2262   p->fd[1] = GNUNET_new (struct GNUNET_DISK_FileHandle);
2263
2264   /* All pipes are overlapped. If you want them to block - just
2265    * call WriteFile() and ReadFile() with NULL overlapped pointer.
2266    * NOTE: calling with NULL overlapped pointer works only
2267    * for pipes, and doesn't seem to be a documented feature.
2268    * It will NOT work for files, because overlapped files need
2269    * to read offsets from the overlapped structure, regardless.
2270    * Pipes are not seekable, and need no offsets, which is
2271    * probably why it works for them.
2272    */
2273   ret =
2274       create_selectable_pipe (&p->fd[0]->h, &p->fd[1]->h, NULL, 0,
2275                               FILE_FLAG_OVERLAPPED,
2276                               FILE_FLAG_OVERLAPPED);
2277   if (!ret)
2278   {
2279     SetErrnoFromWinError (GetLastError ());
2280     save_errno = errno;
2281     GNUNET_free (p->fd[0]);
2282     GNUNET_free (p->fd[1]);
2283     GNUNET_free (p);
2284     errno = save_errno;
2285     return NULL;
2286   }
2287   if (!DuplicateHandle
2288       (GetCurrentProcess (), p->fd[0]->h, GetCurrentProcess (), &tmp_handle, 0,
2289        inherit_read == GNUNET_YES ? TRUE : FALSE, DUPLICATE_SAME_ACCESS))
2290   {
2291     SetErrnoFromWinError (GetLastError ());
2292     save_errno = errno;
2293     CloseHandle (p->fd[0]->h);
2294     CloseHandle (p->fd[1]->h);
2295     GNUNET_free (p->fd[0]);
2296     GNUNET_free (p->fd[1]);
2297     GNUNET_free (p);
2298     errno = save_errno;
2299     return NULL;
2300   }
2301   CloseHandle (p->fd[0]->h);
2302   p->fd[0]->h = tmp_handle;
2303
2304   if (!DuplicateHandle
2305       (GetCurrentProcess (), p->fd[1]->h, GetCurrentProcess (), &tmp_handle, 0,
2306        inherit_write == GNUNET_YES ? TRUE : FALSE, DUPLICATE_SAME_ACCESS))
2307   {
2308     SetErrnoFromWinError (GetLastError ());
2309     save_errno = errno;
2310     CloseHandle (p->fd[0]->h);
2311     CloseHandle (p->fd[1]->h);
2312     GNUNET_free (p->fd[0]);
2313     GNUNET_free (p->fd[1]);
2314     GNUNET_free (p);
2315     errno = save_errno;
2316     return NULL;
2317   }
2318   CloseHandle (p->fd[1]->h);
2319   p->fd[1]->h = tmp_handle;
2320
2321   p->fd[0]->type = GNUNET_DISK_HANLDE_TYPE_PIPE;
2322   p->fd[1]->type = GNUNET_DISK_HANLDE_TYPE_PIPE;
2323
2324   p->fd[0]->oOverlapRead = GNUNET_new (OVERLAPPED);
2325   p->fd[0]->oOverlapWrite = GNUNET_new (OVERLAPPED);
2326   p->fd[1]->oOverlapRead = GNUNET_new (OVERLAPPED);
2327   p->fd[1]->oOverlapWrite = GNUNET_new (OVERLAPPED);
2328
2329   p->fd[0]->oOverlapRead->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2330   p->fd[0]->oOverlapWrite->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2331
2332   p->fd[1]->oOverlapRead->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2333   p->fd[1]->oOverlapWrite->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2334
2335   return p;
2336 #endif
2337 }
2338
2339
2340 /**
2341  * Creates a pipe object from a couple of file descriptors.
2342  * Useful for wrapping existing pipe FDs.
2343  *
2344  * @param blocking_read creates an asynchronous pipe for reading if set to GNUNET_NO
2345  * @param blocking_write creates an asynchronous pipe for writing if set to GNUNET_NO
2346  * @param fd an array of two fd values. One of them may be -1 for read-only or write-only pipes
2347  *
2348  * @return handle to the new pipe, NULL on error
2349  */
2350 struct GNUNET_DISK_PipeHandle *
2351 GNUNET_DISK_pipe_from_fd (int blocking_read, int blocking_write, int fd[2])
2352 {
2353   struct GNUNET_DISK_PipeHandle *p;
2354
2355   p = GNUNET_new (struct GNUNET_DISK_PipeHandle);
2356
2357 #ifndef MINGW
2358   int ret;
2359   int flags;
2360   int eno = 0; /* make gcc happy */
2361
2362   ret = 0;
2363   if (fd[0] >= 0)
2364   {
2365     p->fd[0] = GNUNET_new (struct GNUNET_DISK_FileHandle);
2366     p->fd[0]->fd = fd[0];
2367     if (!blocking_read)
2368     {
2369       flags = fcntl (fd[0], F_GETFL);
2370       flags |= O_NONBLOCK;
2371       if (0 > fcntl (fd[0], F_SETFL, flags))
2372       {
2373         ret = -1;
2374         eno = errno;
2375       }
2376     }
2377     flags = fcntl (fd[0], F_GETFD);
2378     flags |= FD_CLOEXEC;
2379     if (0 > fcntl (fd[0], F_SETFD, flags))
2380     {
2381       ret = -1;
2382       eno = errno;
2383     }
2384   }
2385
2386   if (fd[1] >= 0)
2387   {
2388     p->fd[1] = GNUNET_new (struct GNUNET_DISK_FileHandle);
2389     p->fd[1]->fd = fd[1];
2390     if (!blocking_write)
2391     {
2392       flags = fcntl (fd[1], F_GETFL);
2393       flags |= O_NONBLOCK;
2394       if (0 > fcntl (fd[1], F_SETFL, flags))
2395       {
2396         ret = -1;
2397         eno = errno;
2398       }
2399     }
2400     flags = fcntl (fd[1], F_GETFD);
2401     flags |= FD_CLOEXEC;
2402     if (0 > fcntl (fd[1], F_SETFD, flags))
2403     {
2404       ret = -1;
2405       eno = errno;
2406     }
2407   }
2408   if (ret == -1)
2409   {
2410     errno = eno;
2411     LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR, "fcntl");
2412     if (p->fd[0]->fd >= 0)
2413       GNUNET_break (0 == close (p->fd[0]->fd));
2414     if (p->fd[1]->fd >= 0)
2415       GNUNET_break (0 == close (p->fd[1]->fd));
2416     GNUNET_free_non_null (p->fd[0]);
2417     GNUNET_free_non_null (p->fd[1]);
2418     GNUNET_free (p);
2419     errno = eno;
2420     return NULL;
2421   }
2422 #else
2423   if (fd[0] >= 0)
2424   {
2425     p->fd[0] = GNUNET_new (struct GNUNET_DISK_FileHandle);
2426     p->fd[0]->h = (HANDLE) _get_osfhandle (fd[0]);
2427     if (p->fd[0]->h != INVALID_HANDLE_VALUE)
2428     {
2429       p->fd[0]->type = GNUNET_DISK_HANLDE_TYPE_PIPE;
2430       p->fd[0]->oOverlapRead = GNUNET_new (OVERLAPPED);
2431       p->fd[0]->oOverlapWrite = GNUNET_new (OVERLAPPED);
2432       p->fd[0]->oOverlapRead->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2433       p->fd[0]->oOverlapWrite->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2434     }
2435     else
2436     {
2437       GNUNET_free (p->fd[0]);
2438       p->fd[0] = NULL;
2439     }
2440   }
2441   if (fd[1] >= 0)
2442   {
2443     p->fd[1] = GNUNET_new (struct GNUNET_DISK_FileHandle);
2444     p->fd[1]->h = (HANDLE) _get_osfhandle (fd[1]);
2445     if (p->fd[1]->h != INVALID_HANDLE_VALUE)
2446     {
2447       p->fd[1]->type = GNUNET_DISK_HANLDE_TYPE_PIPE;
2448       p->fd[1]->oOverlapRead = GNUNET_new (OVERLAPPED);
2449       p->fd[1]->oOverlapWrite = GNUNET_new (OVERLAPPED);
2450       p->fd[1]->oOverlapRead->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2451       p->fd[1]->oOverlapWrite->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2452     }
2453     else
2454     {
2455       GNUNET_free (p->fd[1]);
2456       p->fd[1] = NULL;
2457     }
2458   }
2459
2460 #endif
2461   return p;
2462 }
2463
2464
2465 /**
2466  * Closes an interprocess channel
2467  *
2468  * @param p pipe to close
2469  * @param end which end of the pipe to close
2470  * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
2471  */
2472 int
2473 GNUNET_DISK_pipe_close_end (struct GNUNET_DISK_PipeHandle *p,
2474                             enum GNUNET_DISK_PipeEnd end)
2475 {
2476   int ret = GNUNET_OK;
2477
2478   if (end == GNUNET_DISK_PIPE_END_READ)
2479   {
2480     if (p->fd[0])
2481     {
2482       ret = GNUNET_DISK_file_close (p->fd[0]);
2483       p->fd[0] = NULL;
2484     }
2485   }
2486   else if (end == GNUNET_DISK_PIPE_END_WRITE)
2487   {
2488     if (p->fd[1])
2489     {
2490       ret = GNUNET_DISK_file_close (p->fd[1]);
2491       p->fd[1] = NULL;
2492     }
2493   }
2494
2495   return ret;
2496 }
2497
2498 /**
2499  * Detaches one of the ends from the pipe.
2500  * Detached end is a fully-functional FileHandle, it will
2501  * not be affected by anything you do with the pipe afterwards.
2502  * Each end of a pipe can only be detched from it once (i.e.
2503  * it is not duplicated).
2504  *
2505  * @param p pipe to detach an end from
2506  * @param end which end of the pipe to detach
2507  * @return Detached end on success, NULL on failure
2508  * (or if that end is not present or is closed).
2509  */
2510 struct GNUNET_DISK_FileHandle *
2511 GNUNET_DISK_pipe_detach_end (struct GNUNET_DISK_PipeHandle *p,
2512                              enum GNUNET_DISK_PipeEnd end)
2513 {
2514   struct GNUNET_DISK_FileHandle *ret = NULL;
2515
2516   if (end == GNUNET_DISK_PIPE_END_READ)
2517   {
2518     if (p->fd[0])
2519     {
2520       ret = p->fd[0];
2521       p->fd[0] = NULL;
2522     }
2523   }
2524   else if (end == GNUNET_DISK_PIPE_END_WRITE)
2525   {
2526     if (p->fd[1])
2527     {
2528       ret = p->fd[1];
2529       p->fd[1] = NULL;
2530     }
2531   }
2532
2533   return ret;
2534 }
2535
2536
2537 /**
2538  * Closes an interprocess channel
2539  *
2540  * @param p pipe to close
2541  * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
2542  */
2543 int
2544 GNUNET_DISK_pipe_close (struct GNUNET_DISK_PipeHandle *p)
2545 {
2546   int ret = GNUNET_OK;
2547
2548   int read_end_close;
2549   int write_end_close;
2550   int read_end_close_errno;
2551   int write_end_close_errno;
2552
2553   read_end_close = GNUNET_DISK_pipe_close_end (p, GNUNET_DISK_PIPE_END_READ);
2554   read_end_close_errno = errno;
2555   write_end_close = GNUNET_DISK_pipe_close_end (p, GNUNET_DISK_PIPE_END_WRITE);
2556   write_end_close_errno = errno;
2557   GNUNET_free (p);
2558
2559   if (GNUNET_OK != read_end_close)
2560   {
2561     errno = read_end_close_errno;
2562     ret = read_end_close;
2563   }
2564   else if (GNUNET_OK != write_end_close)
2565   {
2566     errno = write_end_close_errno;
2567     ret = write_end_close;
2568   }
2569
2570   return ret;
2571 }
2572
2573
2574 /**
2575  * Get the handle to a particular pipe end
2576  *
2577  * @param p pipe
2578  * @param n end to access
2579  * @return handle for the respective end
2580  */
2581 const struct GNUNET_DISK_FileHandle *
2582 GNUNET_DISK_pipe_handle (const struct GNUNET_DISK_PipeHandle *p,
2583                          enum GNUNET_DISK_PipeEnd n)
2584 {
2585   switch (n)
2586   {
2587   case GNUNET_DISK_PIPE_END_READ:
2588   case GNUNET_DISK_PIPE_END_WRITE:
2589     return p->fd[n];
2590   default:
2591     GNUNET_break (0);
2592     return NULL;
2593   }
2594 }
2595
2596
2597 /**
2598  * Retrieve OS file handle
2599  * @internal
2600  * @param fh GNUnet file descriptor
2601  * @param dst destination buffer
2602  * @param dst_len length of dst
2603  * @return #GNUNET_OK on success, #GNUNET_SYSERR otherwise
2604  */
2605 int
2606 GNUNET_DISK_internal_file_handle_ (const struct GNUNET_DISK_FileHandle *fh,
2607                                    void *dst, size_t dst_len)
2608 {
2609   if (NULL == fh)
2610     return GNUNET_SYSERR;
2611 #ifdef MINGW
2612   if (dst_len < sizeof (HANDLE))
2613     return GNUNET_SYSERR;
2614   *((HANDLE *) dst) = fh->h;
2615 #else
2616   if (dst_len < sizeof (int))
2617     return GNUNET_SYSERR;
2618   *((int *) dst) = fh->fd;
2619 #endif
2620
2621   return GNUNET_OK;
2622 }
2623
2624 /* end of disk.c */