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