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