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