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