-doxygen fixes
[oweals/gnunet.git] / src / util / disk.c
1 /*
2      This file is part of GNUnet.
3      (C) 2001, 2002, 2005, 2006, 2009 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 #if WINDOWS
1976 /* Copyright Bob Byrnes  <byrnes <at> curl.com>
1977    http://permalink.gmane.org/gmane.os.cygwin.patches/2121
1978 */
1979 /* Create a pipe, and return handles to the read and write ends,
1980    just like CreatePipe, but ensure that the write end permits
1981    FILE_READ_ATTRIBUTES access, on later versions of win32 where
1982    this is supported.  This access is needed by NtQueryInformationFile,
1983    which is used to implement select and nonblocking writes.
1984    Note that the return value is either NO_ERROR or GetLastError,
1985    unlike CreatePipe, which returns a bool for success or failure.  */
1986 static int
1987 create_selectable_pipe (PHANDLE read_pipe_ptr, PHANDLE write_pipe_ptr,
1988                         LPSECURITY_ATTRIBUTES sa_ptr, DWORD psize,
1989                         DWORD dwReadMode, DWORD dwWriteMode)
1990 {
1991   /* Default to error. */
1992   *read_pipe_ptr = *write_pipe_ptr = INVALID_HANDLE_VALUE;
1993
1994   HANDLE read_pipe = INVALID_HANDLE_VALUE, write_pipe = INVALID_HANDLE_VALUE;
1995
1996   /* Ensure that there is enough pipe buffer space for atomic writes.  */
1997   if (psize < PIPE_BUF)
1998     psize = PIPE_BUF;
1999
2000   char pipename[MAX_PATH];
2001
2002   /* Retry CreateNamedPipe as long as the pipe name is in use.
2003    * Retrying will probably never be necessary, but we want
2004    * to be as robust as possible.  */
2005   while (1)
2006   {
2007     static volatile LONG pipe_unique_id;
2008
2009     snprintf (pipename, sizeof pipename, "\\\\.\\pipe\\gnunet-%d-%ld",
2010               getpid (), InterlockedIncrement ((LONG *) & pipe_unique_id));
2011 #if DEBUG_PIPE
2012     LOG (GNUNET_ERROR_TYPE_DEBUG, "CreateNamedPipe: name = %s, size = %lu\n",
2013          pipename, psize);
2014 #endif
2015     /* Use CreateNamedPipe instead of CreatePipe, because the latter
2016      * returns a write handle that does not permit FILE_READ_ATTRIBUTES
2017      * access, on versions of win32 earlier than WinXP SP2.
2018      * CreatePipe also stupidly creates a full duplex pipe, which is
2019      * a waste, since only a single direction is actually used.
2020      * It's important to only allow a single instance, to ensure that
2021      * the pipe was not created earlier by some other process, even if
2022      * the pid has been reused.  We avoid FILE_FLAG_FIRST_PIPE_INSTANCE
2023      * because that is only available for Win2k SP2 and WinXP.  */
2024     read_pipe = CreateNamedPipeA (pipename, PIPE_ACCESS_INBOUND | dwReadMode, PIPE_TYPE_BYTE | PIPE_READMODE_BYTE, 1,   /* max instances */
2025                                   psize,        /* output buffer size */
2026                                   psize,        /* input buffer size */
2027                                   NMPWAIT_USE_DEFAULT_WAIT, sa_ptr);
2028
2029     if (read_pipe != INVALID_HANDLE_VALUE)
2030     {
2031 #if DEBUG_PIPE
2032       LOG (GNUNET_ERROR_TYPE_DEBUG, "pipe read handle = %p\n", read_pipe);
2033 #endif
2034       break;
2035     }
2036
2037     DWORD err = GetLastError ();
2038
2039     switch (err)
2040     {
2041     case ERROR_PIPE_BUSY:
2042       /* The pipe is already open with compatible parameters.
2043        * Pick a new name and retry.  */
2044 #if DEBUG_PIPE
2045       LOG (GNUNET_ERROR_TYPE_DEBUG, "pipe busy, retrying\n");
2046 #endif
2047       continue;
2048     case ERROR_ACCESS_DENIED:
2049       /* The pipe is already open with incompatible parameters.
2050        * Pick a new name and retry.  */
2051 #if DEBUG_PIPE
2052       LOG (GNUNET_ERROR_TYPE_DEBUG, "pipe access denied, retrying\n");
2053 #endif
2054       continue;
2055     case ERROR_CALL_NOT_IMPLEMENTED:
2056       /* We are on an older Win9x platform without named pipes.
2057        * Return an anonymous pipe as the best approximation.  */
2058 #if DEBUG_PIPE
2059       LOG (GNUNET_ERROR_TYPE_DEBUG,
2060            "CreateNamedPipe not implemented, resorting to "
2061            "CreatePipe: size = %lu\n", psize);
2062 #endif
2063       if (CreatePipe (read_pipe_ptr, write_pipe_ptr, sa_ptr, psize))
2064       {
2065 #if DEBUG_PIPE
2066         LOG (GNUNET_ERROR_TYPE_DEBUG, "pipe read handle = %p\n",
2067              *read_pipe_ptr);
2068         LOG (GNUNET_ERROR_TYPE_DEBUG, "pipe write handle = %p\n",
2069              *write_pipe_ptr);
2070 #endif
2071         return GNUNET_OK;
2072       }
2073       err = GetLastError ();
2074       LOG (GNUNET_ERROR_TYPE_ERROR, "CreatePipe failed: %d\n", err);
2075       return err;
2076     default:
2077       LOG (GNUNET_ERROR_TYPE_ERROR, "CreateNamedPipe failed: %d\n", err);
2078       return err;
2079     }
2080     /* NOTREACHED */
2081   }
2082 #if DEBUG_PIPE
2083   LOG (GNUNET_ERROR_TYPE_DEBUG, "CreateFile: name = %s\n", pipename);
2084 #endif
2085
2086   /* Open the named pipe for writing.
2087    * Be sure to permit FILE_READ_ATTRIBUTES access.  */
2088   write_pipe = CreateFileA (pipename, GENERIC_WRITE | FILE_READ_ATTRIBUTES, 0,  /* share mode */
2089                             sa_ptr, OPEN_EXISTING, dwWriteMode, /* flags and attributes */
2090                             0); /* handle to template file */
2091
2092   if (write_pipe == INVALID_HANDLE_VALUE)
2093   {
2094     /* Failure. */
2095     DWORD err = GetLastError ();
2096
2097 #if DEBUG_PIPE
2098     LOG (GNUNET_ERROR_TYPE_DEBUG, "CreateFile failed: %d\n", err);
2099 #endif
2100     CloseHandle (read_pipe);
2101     return err;
2102   }
2103 #if DEBUG_PIPE
2104   LOG (GNUNET_ERROR_TYPE_DEBUG, "pipe write handle = %p\n", write_pipe);
2105 #endif
2106   /* Success. */
2107   *read_pipe_ptr = read_pipe;
2108   *write_pipe_ptr = write_pipe;
2109   return GNUNET_OK;
2110 }
2111 #endif
2112
2113 /**
2114  * Creates an interprocess channel
2115  *
2116  * @param blocking_read creates an asynchronous pipe for reading if set to GNUNET_NO
2117  * @param blocking_write creates an asynchronous pipe for writing if set to GNUNET_NO
2118  * @param inherit_read inherit the parent processes stdin (only for windows)
2119  * @param inherit_write inherit the parent processes stdout (only for windows)
2120  * @return handle to the new pipe, NULL on error
2121  */
2122 struct GNUNET_DISK_PipeHandle *
2123 GNUNET_DISK_pipe (int blocking_read, int blocking_write, int inherit_read, int inherit_write)
2124 {
2125   struct GNUNET_DISK_PipeHandle *p;
2126   struct GNUNET_DISK_FileHandle *fds;
2127
2128   p = GNUNET_malloc (sizeof (struct GNUNET_DISK_PipeHandle) +
2129                      2 * sizeof (struct GNUNET_DISK_FileHandle));
2130   fds = (struct GNUNET_DISK_FileHandle *) &p[1];
2131   p->fd[0] = &fds[0];
2132   p->fd[1] = &fds[1];
2133 #ifndef MINGW
2134   int fd[2];
2135   int ret;
2136   int flags;
2137   int eno;
2138
2139   ret = pipe (fd);
2140   if (ret == -1)
2141   {
2142     eno = errno;
2143     LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR, "pipe");
2144     GNUNET_free (p);
2145     errno = eno;
2146     return NULL;
2147   }
2148   p->fd[0]->fd = fd[0];
2149   p->fd[1]->fd = fd[1];
2150   ret = 0;
2151   flags = fcntl (fd[0], F_GETFL);
2152   if (!blocking_read)
2153     flags |= O_NONBLOCK;
2154   if (0 > fcntl (fd[0], F_SETFL, flags))
2155     ret = -1;
2156   flags = fcntl (fd[0], F_GETFD);
2157   flags |= FD_CLOEXEC;
2158   if (0 > fcntl (fd[0], F_SETFD, flags))
2159     ret = -1;
2160
2161   flags = fcntl (fd[1], F_GETFL);
2162   if (!blocking_write)
2163     flags |= O_NONBLOCK;
2164   if (0 > fcntl (fd[1], F_SETFL, flags))
2165     ret = -1;
2166   flags = fcntl (fd[1], F_GETFD);
2167   flags |= FD_CLOEXEC;
2168   if (0 > fcntl (fd[1], F_SETFD, flags))
2169     ret = -1;
2170   if (ret == -1)
2171   {
2172     eno = errno;
2173     LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR, "fcntl");
2174     GNUNET_break (0 == close (p->fd[0]->fd));
2175     GNUNET_break (0 == close (p->fd[1]->fd));
2176     GNUNET_free (p);
2177     errno = eno;
2178     return NULL;
2179   }
2180 #else
2181   BOOL ret;
2182   HANDLE tmp_handle;
2183
2184   /* All pipes are overlapped. If you want them to block - just
2185    * call WriteFile() and ReadFile() with NULL overlapped pointer.
2186    */
2187   ret =
2188       create_selectable_pipe (&p->fd[0]->h, &p->fd[1]->h, NULL, 0,
2189                               FILE_FLAG_OVERLAPPED,
2190                               FILE_FLAG_OVERLAPPED);
2191   if (!ret)
2192   {
2193     GNUNET_free (p);
2194     SetErrnoFromWinError (GetLastError ());
2195     return NULL;
2196   }
2197   if (!DuplicateHandle
2198       (GetCurrentProcess (), p->fd[0]->h, GetCurrentProcess (), &tmp_handle, 0,
2199        inherit_read == GNUNET_YES ? TRUE : FALSE, DUPLICATE_SAME_ACCESS))
2200   {
2201     SetErrnoFromWinError (GetLastError ());
2202     CloseHandle (p->fd[0]->h);
2203     CloseHandle (p->fd[1]->h);
2204     GNUNET_free (p);
2205     return NULL;
2206   }
2207   CloseHandle (p->fd[0]->h);
2208   p->fd[0]->h = tmp_handle;
2209
2210   if (!DuplicateHandle
2211       (GetCurrentProcess (), p->fd[1]->h, GetCurrentProcess (), &tmp_handle, 0,
2212        inherit_write == GNUNET_YES ? TRUE : FALSE, DUPLICATE_SAME_ACCESS))
2213   {
2214     SetErrnoFromWinError (GetLastError ());
2215     CloseHandle (p->fd[0]->h);
2216     CloseHandle (p->fd[1]->h);
2217     GNUNET_free (p);
2218     return NULL;
2219   }
2220   CloseHandle (p->fd[1]->h);
2221   p->fd[1]->h = tmp_handle;
2222
2223   p->fd[0]->type = GNUNET_PIPE;
2224   p->fd[1]->type = GNUNET_PIPE;
2225
2226   p->fd[0]->oOverlapRead = GNUNET_malloc (sizeof (OVERLAPPED));
2227   p->fd[0]->oOverlapWrite = GNUNET_malloc (sizeof (OVERLAPPED));
2228   p->fd[1]->oOverlapRead = GNUNET_malloc (sizeof (OVERLAPPED));
2229   p->fd[1]->oOverlapWrite = GNUNET_malloc (sizeof (OVERLAPPED));
2230
2231   p->fd[0]->oOverlapRead->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2232   p->fd[0]->oOverlapWrite->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2233
2234   p->fd[1]->oOverlapRead->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2235   p->fd[1]->oOverlapWrite->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2236
2237 #endif
2238   return p;
2239 }
2240
2241 /**
2242  * Creates a pipe object from a couple of file descriptors.
2243  * Useful for wrapping existing pipe FDs.
2244  *
2245  * @param blocking creates an asynchronous pipe if set to GNUNET_NO
2246  * @param fd an array of two fd values. One of them may be -1 for read-only or write-only pipes
2247  *
2248  * @return handle to the new pipe, NULL on error
2249  */
2250 struct GNUNET_DISK_PipeHandle *
2251 GNUNET_DISK_pipe_from_fd (int blocking_read, int blocking_write, int fd[2])
2252 {
2253   struct GNUNET_DISK_PipeHandle *p;
2254   struct GNUNET_DISK_FileHandle *fds;
2255
2256   p = GNUNET_malloc (sizeof (struct GNUNET_DISK_PipeHandle) +
2257                      2 * sizeof (struct GNUNET_DISK_FileHandle));
2258   fds = (struct GNUNET_DISK_FileHandle *) &p[1];
2259   p->fd[0] = &fds[0];
2260   p->fd[1] = &fds[1];
2261 #ifndef MINGW
2262   int ret;
2263   int flags;
2264   int eno;
2265
2266   p->fd[0]->fd = fd[0];
2267   p->fd[1]->fd = fd[1];
2268   ret = 0;
2269   if (fd[0] >= 0)
2270   {
2271     flags = fcntl (fd[0], F_GETFL);
2272     if (!blocking_read)
2273       flags |= O_NONBLOCK;
2274     if (0 > fcntl (fd[0], F_SETFL, flags))
2275       ret = -1;
2276     flags = fcntl (fd[0], F_GETFD);
2277     flags |= FD_CLOEXEC;
2278     if (0 > fcntl (fd[0], F_SETFD, flags))
2279       ret = -1;
2280   }
2281
2282   if (fd[1] >= 0)
2283   {
2284     flags = fcntl (fd[1], F_GETFL);
2285     if (!blocking_write)
2286       flags |= O_NONBLOCK;
2287     if (0 > fcntl (fd[1], F_SETFL, flags))
2288       ret = -1;
2289     flags = fcntl (fd[1], F_GETFD);
2290     flags |= FD_CLOEXEC;
2291     if (0 > fcntl (fd[1], F_SETFD, flags))
2292       ret = -1;
2293   }
2294   if (ret == -1)
2295   {
2296     eno = errno;
2297     LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR, "fcntl");
2298     if (p->fd[0]->fd >= 0)
2299       GNUNET_break (0 == close (p->fd[0]->fd));
2300     if (p->fd[1]->fd >= 0)
2301       GNUNET_break (0 == close (p->fd[1]->fd));
2302     GNUNET_free (p);
2303     errno = eno;
2304     return NULL;
2305   }
2306 #else
2307   BOOL ret;
2308
2309   if (fd[0] >= 0)
2310     p->fd[0]->h = _get_osfhandle (fd[0]);
2311   else
2312     p->fd[0]->h = INVALID_HANDLE_VALUE;
2313   if (fd[1] >= 0)
2314     p->fd[1]->h = _get_osfhandle (fd[1]);
2315   else
2316     p->fd[1]->h = INVALID_HANDLE_VALUE;
2317
2318   if (p->fd[0]->h != INVALID_HANDLE_VALUE)
2319   {
2320     p->fd[0]->type = GNUNET_PIPE;
2321     p->fd[0]->oOverlapRead = GNUNET_malloc (sizeof (OVERLAPPED));
2322     p->fd[0]->oOverlapWrite = GNUNET_malloc (sizeof (OVERLAPPED));
2323     p->fd[0]->oOverlapRead->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2324     p->fd[0]->oOverlapWrite->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2325   }
2326
2327   if (p->fd[1]->h != INVALID_HANDLE_VALUE)
2328   {
2329     p->fd[1]->type = GNUNET_PIPE;
2330     p->fd[1]->oOverlapRead = GNUNET_malloc (sizeof (OVERLAPPED));
2331     p->fd[1]->oOverlapWrite = GNUNET_malloc (sizeof (OVERLAPPED));
2332     p->fd[1]->oOverlapRead->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2333     p->fd[1]->oOverlapWrite->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2334   }
2335 #endif
2336   return p;
2337 }
2338
2339
2340 /**
2341  * Closes an interprocess channel
2342  *
2343  * @param p pipe to close
2344  * @param end which end of the pipe to close
2345  * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
2346  */
2347 int
2348 GNUNET_DISK_pipe_close_end (struct GNUNET_DISK_PipeHandle *p,
2349                             enum GNUNET_DISK_PipeEnd end)
2350 {
2351   int ret = GNUNET_OK;
2352   int save;
2353
2354 #ifdef MINGW
2355   if (end == GNUNET_DISK_PIPE_END_READ)
2356   {
2357     if (p->fd[0]->h != INVALID_HANDLE_VALUE)
2358     {
2359       if (!CloseHandle (p->fd[0]->h))
2360       {
2361         SetErrnoFromWinError (GetLastError ());
2362         ret = GNUNET_SYSERR;
2363       }
2364       GNUNET_free (p->fd[0]->oOverlapRead);
2365       GNUNET_free (p->fd[0]->oOverlapWrite);
2366       p->fd[0]->h = INVALID_HANDLE_VALUE;
2367     }
2368   }
2369   else if (end == GNUNET_DISK_PIPE_END_WRITE)
2370   {
2371     if (p->fd[0]->h != INVALID_HANDLE_VALUE)
2372     {
2373       if (!CloseHandle (p->fd[1]->h))
2374       {
2375         SetErrnoFromWinError (GetLastError ());
2376         ret = GNUNET_SYSERR;
2377       }
2378       GNUNET_free (p->fd[1]->oOverlapRead);
2379       GNUNET_free (p->fd[1]->oOverlapWrite);
2380       p->fd[1]->h = INVALID_HANDLE_VALUE;
2381     }
2382   }
2383   save = errno;
2384 #else
2385   save = 0;
2386   if (end == GNUNET_DISK_PIPE_END_READ)
2387   {
2388     if (0 != close (p->fd[0]->fd))
2389     {
2390       ret = GNUNET_SYSERR;
2391       save = errno;
2392     }
2393     p->fd[0]->fd = -1;
2394   }
2395   else if (end == GNUNET_DISK_PIPE_END_WRITE)
2396   {
2397     if (0 != close (p->fd[1]->fd))
2398     {
2399       ret = GNUNET_SYSERR;
2400       save = errno;
2401     }
2402     p->fd[1]->fd = -1;
2403   }
2404 #endif
2405   errno = save;
2406   return ret;
2407 }
2408
2409 /**
2410  * Closes an interprocess channel
2411  *
2412  * @param p pipe to close
2413  * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
2414  */
2415 int
2416 GNUNET_DISK_pipe_close (struct GNUNET_DISK_PipeHandle *p)
2417 {
2418   int ret = GNUNET_OK;
2419   int save;
2420
2421 #ifdef MINGW
2422   if (p->fd[0]->h != INVALID_HANDLE_VALUE)
2423   {
2424     if (!CloseHandle (p->fd[0]->h))
2425     {
2426       SetErrnoFromWinError (GetLastError ());
2427       ret = GNUNET_SYSERR;
2428     }
2429     GNUNET_free (p->fd[0]->oOverlapRead);
2430     GNUNET_free (p->fd[0]->oOverlapWrite);
2431   }
2432   if (p->fd[1]->h != INVALID_HANDLE_VALUE)
2433   {
2434     if (!CloseHandle (p->fd[1]->h))
2435     {
2436       SetErrnoFromWinError (GetLastError ());
2437       ret = GNUNET_SYSERR;
2438     }
2439     GNUNET_free (p->fd[1]->oOverlapRead);
2440     GNUNET_free (p->fd[1]->oOverlapWrite);
2441   }
2442   save = errno;
2443 #else
2444   save = 0;
2445   if (p->fd[0]->fd != -1)
2446   {
2447     if (0 != close (p->fd[0]->fd))
2448     {
2449       ret = GNUNET_SYSERR;
2450       save = errno;
2451     }
2452   }
2453
2454   if (p->fd[1]->fd != -1)
2455   {
2456     if (0 != close (p->fd[1]->fd))
2457     {
2458       ret = GNUNET_SYSERR;
2459       save = errno;
2460     }
2461   }
2462 #endif
2463   GNUNET_free (p);
2464   errno = save;
2465   return ret;
2466 }
2467
2468
2469 /**
2470  * Creates a named pipe/FIFO and opens it
2471  * @param fn pointer to the name of the named pipe or to NULL
2472  * @param flags open flags
2473  * @param perm access permissions
2474  * @return pipe handle on success, NULL on error
2475  */
2476 struct GNUNET_DISK_FileHandle *
2477 GNUNET_DISK_npipe_create (char **fn, enum GNUNET_DISK_OpenFlags flags,
2478                           enum GNUNET_DISK_AccessPermissions perm)
2479 {
2480 #ifdef MINGW
2481   struct GNUNET_DISK_FileHandle *ret;
2482   HANDLE h = NULL;
2483   DWORD openMode;
2484   char *name;
2485
2486   openMode = 0;
2487   if (flags & GNUNET_DISK_OPEN_READWRITE)
2488     openMode = PIPE_ACCESS_DUPLEX;
2489   else if (flags & GNUNET_DISK_OPEN_READ)
2490     openMode = PIPE_ACCESS_INBOUND;
2491   else if (flags & GNUNET_DISK_OPEN_WRITE)
2492     openMode = PIPE_ACCESS_OUTBOUND;
2493
2494   if (flags & GNUNET_DISK_OPEN_FAILIFEXISTS)
2495     openMode |= FILE_FLAG_FIRST_PIPE_INSTANCE;
2496
2497   while (h == NULL)
2498   {
2499     DWORD error_code;
2500
2501     name = NULL;
2502     if (*fn != NULL)
2503     {
2504       GNUNET_asprintf (&name, "\\\\.\\pipe\\%.246s", fn);
2505 #if DEBUG_NPIPE
2506       LOG (GNUNET_ERROR_TYPE_DEBUG,
2507            "Trying to create an instance of named pipe `%s'\n", name);
2508 #endif
2509       /* 1) This might work just fine with UTF-8 strings as it is.
2510        * 2) This is only used by GNUnet itself, and only with latin names.
2511        */
2512       h = CreateNamedPipe (name, openMode | FILE_FLAG_OVERLAPPED,
2513                            PIPE_TYPE_BYTE | PIPE_READMODE_BYTE, 2, 1, 1, 0,
2514                            NULL);
2515     }
2516     else
2517     {
2518       GNUNET_asprintf (fn, "\\\\.\\pipe\\gnunet-%llu",
2519                        GNUNET_CRYPTO_random_u64 (GNUNET_CRYPTO_QUALITY_WEAK,
2520                                                  UINT64_MAX));
2521 #if DEBUG_NPIPE
2522       LOG (GNUNET_ERROR_TYPE_DEBUG, "Trying to create unique named pipe `%s'\n",
2523            *fn);
2524 #endif
2525       h = CreateNamedPipe (*fn,
2526                            openMode | FILE_FLAG_OVERLAPPED |
2527                            FILE_FLAG_FIRST_PIPE_INSTANCE,
2528                            PIPE_TYPE_BYTE | PIPE_READMODE_BYTE, 2, 1, 1, 0,
2529                            NULL);
2530     }
2531     error_code = GetLastError ();
2532     if (name)
2533       GNUNET_free (name);
2534     /* don't re-set name to NULL yet */
2535     if (h == INVALID_HANDLE_VALUE)
2536     {
2537       SetErrnoFromWinError (error_code);
2538 #if DEBUG_NPIPE
2539       LOG (GNUNET_ERROR_TYPE_DEBUG,
2540            "Pipe creation have failed because of %d, errno is %d\n", error_code,
2541            errno);
2542 #endif
2543       if (name == NULL)
2544       {
2545 #if DEBUG_NPIPE
2546         LOG (GNUNET_ERROR_TYPE_DEBUG,
2547              "Pipe was to be unique, considering re-creation\n");
2548 #endif
2549         GNUNET_free (*fn);
2550         *fn = NULL;
2551         if (error_code != ERROR_ACCESS_DENIED && error_code != ERROR_PIPE_BUSY)
2552         {
2553           return NULL;
2554         }
2555 #if DEBUG_NPIPE
2556         LOG (GNUNET_ERROR_TYPE_DEBUG,
2557              "Pipe name was not unique, trying again\n");
2558 #endif
2559         h = NULL;
2560       }
2561       else
2562         return NULL;
2563     }
2564   }
2565   errno = 0;
2566
2567   ret = GNUNET_malloc (sizeof (*ret));
2568   ret->h = h;
2569   ret->type = GNUNET_PIPE;
2570
2571   ret->oOverlapRead = GNUNET_malloc (sizeof (OVERLAPPED));
2572   ret->oOverlapWrite = GNUNET_malloc (sizeof (OVERLAPPED));
2573
2574   ret->oOverlapRead->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2575   ret->oOverlapWrite->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2576
2577   return ret;
2578 #else
2579   if (*fn == NULL)
2580   {
2581     char dir[] = "/tmp/gnunet-pipe-XXXXXX";
2582
2583     if (mkdtemp (dir) == NULL)
2584     {
2585       LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR, "mkdtemp");
2586       return NULL;
2587     }
2588     GNUNET_asprintf (fn, "%s/child-control", dir);
2589   }
2590
2591   if (mkfifo (*fn, translate_unix_perms (perm)) == -1)
2592   {
2593     if ((errno != EEXIST) || (0 != (flags & GNUNET_DISK_OPEN_FAILIFEXISTS)))
2594       return NULL;
2595   }
2596
2597   flags = flags & (~GNUNET_DISK_OPEN_FAILIFEXISTS);
2598   return GNUNET_DISK_file_open (*fn, flags, perm);
2599 #endif
2600 }
2601
2602
2603 /**
2604  * Opens already existing named pipe/FIFO
2605  *
2606  * @param fn name of an existing named pipe
2607  * @param flags open flags
2608  * @param perm access permissions
2609  * @return pipe handle on success, NULL on error
2610  */
2611 struct GNUNET_DISK_FileHandle *
2612 GNUNET_DISK_npipe_open (const char *fn, enum GNUNET_DISK_OpenFlags flags,
2613                         enum GNUNET_DISK_AccessPermissions perm)
2614 {
2615 #ifdef MINGW
2616   struct GNUNET_DISK_FileHandle *ret;
2617   HANDLE h;
2618   DWORD openMode;
2619
2620   openMode = 0;
2621   if (flags & GNUNET_DISK_OPEN_READWRITE)
2622     openMode = GENERIC_WRITE | GENERIC_READ;
2623   else if (flags & GNUNET_DISK_OPEN_READ)
2624     openMode = GENERIC_READ;
2625   else if (flags & GNUNET_DISK_OPEN_WRITE)
2626     openMode = GENERIC_WRITE;
2627
2628   h = CreateFile (fn, openMode, 0, NULL, OPEN_EXISTING,
2629                   FILE_FLAG_OVERLAPPED | FILE_READ_ATTRIBUTES, NULL);
2630   if (h == INVALID_HANDLE_VALUE)
2631   {
2632     SetErrnoFromWinError (GetLastError ());
2633     return NULL;
2634   }
2635
2636   ret = GNUNET_malloc (sizeof (*ret));
2637   ret->h = h;
2638   ret->type = GNUNET_PIPE;
2639   ret->oOverlapRead = GNUNET_malloc (sizeof (OVERLAPPED));
2640   ret->oOverlapWrite = GNUNET_malloc (sizeof (OVERLAPPED));
2641   ret->oOverlapRead->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2642   ret->oOverlapWrite->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2643
2644   return ret;
2645 #else
2646   flags = flags & (~GNUNET_DISK_OPEN_FAILIFEXISTS);
2647   return GNUNET_DISK_file_open (fn, flags, perm);
2648 #endif
2649 }
2650
2651 /**
2652  * Closes a named pipe/FIFO
2653  * @param pipe named pipe
2654  * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
2655  */
2656 int
2657 GNUNET_DISK_npipe_close (struct GNUNET_DISK_FileHandle *pipe)
2658 {
2659 #ifndef MINGW
2660   return close (pipe->fd) == 0 ? GNUNET_OK : GNUNET_SYSERR;
2661 #else
2662   BOOL ret;
2663
2664   ret = CloseHandle (pipe->h);
2665   if (!ret)
2666   {
2667     SetErrnoFromWinError (GetLastError ());
2668     return GNUNET_SYSERR;
2669   }
2670   else
2671     return GNUNET_OK;
2672 #endif
2673 }
2674
2675
2676 /**
2677  * Get the handle to a particular pipe end
2678  *
2679  * @param p pipe
2680  * @param n end to access
2681  * @return handle for the respective end
2682  */
2683 const struct GNUNET_DISK_FileHandle *
2684 GNUNET_DISK_pipe_handle (const struct GNUNET_DISK_PipeHandle *p,
2685                          enum GNUNET_DISK_PipeEnd n)
2686 {
2687   switch (n)
2688   {
2689   case GNUNET_DISK_PIPE_END_READ:
2690   case GNUNET_DISK_PIPE_END_WRITE:
2691     return p->fd[n];
2692   default:
2693     GNUNET_break (0);
2694     return NULL;
2695   }
2696 }
2697
2698
2699 /**
2700  * Retrieve OS file handle
2701  * @internal
2702  * @param fh GNUnet file descriptor
2703  * @param dst destination buffer
2704  * @param dst_len length of dst
2705  * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
2706  */
2707 int
2708 GNUNET_DISK_internal_file_handle_ (const struct GNUNET_DISK_FileHandle *fh,
2709                                    void *dst, size_t dst_len)
2710 {
2711 #ifdef MINGW
2712   if (dst_len < sizeof (HANDLE))
2713     return GNUNET_SYSERR;
2714   *((HANDLE *) dst) = fh->h;
2715 #else
2716   if (dst_len < sizeof (int))
2717     return GNUNET_SYSERR;
2718   *((int *) dst) = fh->fd;
2719 #endif
2720
2721   return GNUNET_OK;
2722 }
2723
2724 /* end of disk.c */