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