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