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