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