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