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