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