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