-implement blocking write / non-blocking read for non-W32 systems
[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 creates an asynchronous pipe if set to GNUNET_NO
2117  * @param inherit_read inherit the parent processes stdin (only for windows)
2118  * @param inherit_write inherit the parent processes stdout (only for windows)
2119  *
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   ret =
2185       create_selectable_pipe (&p->fd[0]->h, &p->fd[1]->h, NULL, 0,
2186                               blocking_read ? 0 : FILE_FLAG_OVERLAPPED,
2187                               blocking_write ? 0 : FILE_FLAG_OVERLAPPED);
2188   if (!ret)
2189   {
2190     GNUNET_free (p);
2191     SetErrnoFromWinError (GetLastError ());
2192     return NULL;
2193   }
2194   if (!DuplicateHandle
2195       (GetCurrentProcess (), p->fd[0]->h, GetCurrentProcess (), &tmp_handle, 0,
2196        inherit_read == GNUNET_YES ? TRUE : FALSE, DUPLICATE_SAME_ACCESS))
2197   {
2198     SetErrnoFromWinError (GetLastError ());
2199     CloseHandle (p->fd[0]->h);
2200     CloseHandle (p->fd[1]->h);
2201     GNUNET_free (p);
2202     return NULL;
2203   }
2204   CloseHandle (p->fd[0]->h);
2205   p->fd[0]->h = tmp_handle;
2206
2207   if (!DuplicateHandle
2208       (GetCurrentProcess (), p->fd[1]->h, GetCurrentProcess (), &tmp_handle, 0,
2209        inherit_write == GNUNET_YES ? TRUE : FALSE, DUPLICATE_SAME_ACCESS))
2210   {
2211     SetErrnoFromWinError (GetLastError ());
2212     CloseHandle (p->fd[0]->h);
2213     CloseHandle (p->fd[1]->h);
2214     GNUNET_free (p);
2215     return NULL;
2216   }
2217   CloseHandle (p->fd[1]->h);
2218   p->fd[1]->h = tmp_handle;
2219
2220   p->fd[0]->type = GNUNET_PIPE;
2221   p->fd[1]->type = GNUNET_PIPE;
2222
2223   p->fd[0]->oOverlapRead = GNUNET_malloc (sizeof (OVERLAPPED));
2224   p->fd[0]->oOverlapWrite = GNUNET_malloc (sizeof (OVERLAPPED));
2225   p->fd[1]->oOverlapRead = GNUNET_malloc (sizeof (OVERLAPPED));
2226   p->fd[1]->oOverlapWrite = GNUNET_malloc (sizeof (OVERLAPPED));
2227
2228   p->fd[0]->oOverlapRead->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2229   p->fd[0]->oOverlapWrite->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2230
2231   p->fd[1]->oOverlapRead->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2232   p->fd[1]->oOverlapWrite->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2233
2234 #endif
2235   return p;
2236 }
2237
2238 /**
2239  * Creates a pipe object from a couple of file descriptors.
2240  * Useful for wrapping existing pipe FDs.
2241  *
2242  * @param blocking creates an asynchronous pipe if set to GNUNET_NO
2243  * @param fd an array of two fd values. One of them may be -1 for read-only or write-only pipes
2244  *
2245  * @return handle to the new pipe, NULL on error
2246  */
2247 struct GNUNET_DISK_PipeHandle *
2248 GNUNET_DISK_pipe_from_fd (int blocking_read, int blocking_write, int fd[2])
2249 {
2250   struct GNUNET_DISK_PipeHandle *p;
2251   struct GNUNET_DISK_FileHandle *fds;
2252
2253   p = GNUNET_malloc (sizeof (struct GNUNET_DISK_PipeHandle) +
2254                      2 * sizeof (struct GNUNET_DISK_FileHandle));
2255   fds = (struct GNUNET_DISK_FileHandle *) &p[1];
2256   p->fd[0] = &fds[0];
2257   p->fd[1] = &fds[1];
2258 #ifndef MINGW
2259   int ret;
2260   int flags;
2261   int eno;
2262
2263   p->fd[0]->fd = fd[0];
2264   p->fd[1]->fd = fd[1];
2265   ret = 0;
2266   if (fd[0] >= 0)
2267   {
2268     flags = fcntl (fd[0], F_GETFL);
2269     if (!blocking_read)
2270       flags |= O_NONBLOCK;
2271     if (0 > fcntl (fd[0], F_SETFL, flags))
2272       ret = -1;
2273     flags = fcntl (fd[0], F_GETFD);
2274     flags |= FD_CLOEXEC;
2275     if (0 > fcntl (fd[0], F_SETFD, flags))
2276       ret = -1;
2277   }
2278
2279   if (fd[1] >= 0)
2280   {
2281     flags = fcntl (fd[1], F_GETFL);
2282     if (!blocking_write)
2283       flags |= O_NONBLOCK;
2284     if (0 > fcntl (fd[1], F_SETFL, flags))
2285       ret = -1;
2286     flags = fcntl (fd[1], F_GETFD);
2287     flags |= FD_CLOEXEC;
2288     if (0 > fcntl (fd[1], F_SETFD, flags))
2289       ret = -1;
2290   }
2291   if (ret == -1)
2292   {
2293     eno = errno;
2294     LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR, "fcntl");
2295     if (p->fd[0]->fd >= 0)
2296       GNUNET_break (0 == close (p->fd[0]->fd));
2297     if (p->fd[1]->fd >= 0)
2298       GNUNET_break (0 == close (p->fd[1]->fd));
2299     GNUNET_free (p);
2300     errno = eno;
2301     return NULL;
2302   }
2303 #else
2304   BOOL ret;
2305
2306   if (fd[0] >= 0)
2307     p->fd[0]->h = _get_osfhandle (fd[0]);
2308   else
2309     p->fd[0]->h = INVALID_HANDLE_VALUE;
2310   if (fd[1] >= 0)
2311     p->fd[1]->h = _get_osfhandle (fd[1]);
2312   else
2313     p->fd[1]->h = INVALID_HANDLE_VALUE;
2314
2315   if (p->fd[0]->h != INVALID_HANDLE_VALUE)
2316   {
2317     p->fd[0]->type = GNUNET_PIPE;
2318     p->fd[0]->oOverlapRead = GNUNET_malloc (sizeof (OVERLAPPED));
2319     p->fd[0]->oOverlapWrite = GNUNET_malloc (sizeof (OVERLAPPED));
2320     p->fd[0]->oOverlapRead->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2321     p->fd[0]->oOverlapWrite->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2322   }
2323
2324   if (p->fd[1]->h != INVALID_HANDLE_VALUE)
2325   {
2326     p->fd[1]->type = GNUNET_PIPE;
2327     p->fd[1]->oOverlapRead = GNUNET_malloc (sizeof (OVERLAPPED));
2328     p->fd[1]->oOverlapWrite = GNUNET_malloc (sizeof (OVERLAPPED));
2329     p->fd[1]->oOverlapRead->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2330     p->fd[1]->oOverlapWrite->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2331   }
2332 #endif
2333   return p;
2334 }
2335
2336
2337 /**
2338  * Closes an interprocess channel
2339  *
2340  * @param p pipe to close
2341  * @param end which end of the pipe to close
2342  * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
2343  */
2344 int
2345 GNUNET_DISK_pipe_close_end (struct GNUNET_DISK_PipeHandle *p,
2346                             enum GNUNET_DISK_PipeEnd end)
2347 {
2348   int ret = GNUNET_OK;
2349   int save;
2350
2351 #ifdef MINGW
2352   if (end == GNUNET_DISK_PIPE_END_READ)
2353   {
2354     if (p->fd[0]->h != INVALID_HANDLE_VALUE)
2355     {
2356       if (!CloseHandle (p->fd[0]->h))
2357       {
2358         SetErrnoFromWinError (GetLastError ());
2359         ret = GNUNET_SYSERR;
2360       }
2361       GNUNET_free (p->fd[0]->oOverlapRead);
2362       GNUNET_free (p->fd[0]->oOverlapWrite);
2363       p->fd[0]->h = INVALID_HANDLE_VALUE;
2364     }
2365   }
2366   else if (end == GNUNET_DISK_PIPE_END_WRITE)
2367   {
2368     if (p->fd[0]->h != INVALID_HANDLE_VALUE)
2369     {
2370       if (!CloseHandle (p->fd[1]->h))
2371       {
2372         SetErrnoFromWinError (GetLastError ());
2373         ret = GNUNET_SYSERR;
2374       }
2375       GNUNET_free (p->fd[1]->oOverlapRead);
2376       GNUNET_free (p->fd[1]->oOverlapWrite);
2377       p->fd[1]->h = INVALID_HANDLE_VALUE;
2378     }
2379   }
2380   save = errno;
2381 #else
2382   save = 0;
2383   if (end == GNUNET_DISK_PIPE_END_READ)
2384   {
2385     if (0 != close (p->fd[0]->fd))
2386     {
2387       ret = GNUNET_SYSERR;
2388       save = errno;
2389     }
2390     p->fd[0]->fd = -1;
2391   }
2392   else if (end == GNUNET_DISK_PIPE_END_WRITE)
2393   {
2394     if (0 != close (p->fd[1]->fd))
2395     {
2396       ret = GNUNET_SYSERR;
2397       save = errno;
2398     }
2399     p->fd[1]->fd = -1;
2400   }
2401 #endif
2402   errno = save;
2403   return ret;
2404 }
2405
2406 /**
2407  * Closes an interprocess channel
2408  *
2409  * @param p pipe to close
2410  * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
2411  */
2412 int
2413 GNUNET_DISK_pipe_close (struct GNUNET_DISK_PipeHandle *p)
2414 {
2415   int ret = GNUNET_OK;
2416   int save;
2417
2418 #ifdef MINGW
2419   if (p->fd[0]->h != INVALID_HANDLE_VALUE)
2420   {
2421     if (!CloseHandle (p->fd[0]->h))
2422     {
2423       SetErrnoFromWinError (GetLastError ());
2424       ret = GNUNET_SYSERR;
2425     }
2426     GNUNET_free (p->fd[0]->oOverlapRead);
2427     GNUNET_free (p->fd[0]->oOverlapWrite);
2428   }
2429   if (p->fd[1]->h != INVALID_HANDLE_VALUE)
2430   {
2431     if (!CloseHandle (p->fd[1]->h))
2432     {
2433       SetErrnoFromWinError (GetLastError ());
2434       ret = GNUNET_SYSERR;
2435     }
2436     GNUNET_free (p->fd[1]->oOverlapRead);
2437     GNUNET_free (p->fd[1]->oOverlapWrite);
2438   }
2439   save = errno;
2440 #else
2441   save = 0;
2442   if (p->fd[0]->fd != -1)
2443   {
2444     if (0 != close (p->fd[0]->fd))
2445     {
2446       ret = GNUNET_SYSERR;
2447       save = errno;
2448     }
2449   }
2450
2451   if (p->fd[1]->fd != -1)
2452   {
2453     if (0 != close (p->fd[1]->fd))
2454     {
2455       ret = GNUNET_SYSERR;
2456       save = errno;
2457     }
2458   }
2459 #endif
2460   GNUNET_free (p);
2461   errno = save;
2462   return ret;
2463 }
2464
2465
2466 /**
2467  * Creates a named pipe/FIFO and opens it
2468  * @param fn pointer to the name of the named pipe or to NULL
2469  * @param flags open flags
2470  * @param perm access permissions
2471  * @return pipe handle on success, NULL on error
2472  */
2473 struct GNUNET_DISK_FileHandle *
2474 GNUNET_DISK_npipe_create (char **fn, enum GNUNET_DISK_OpenFlags flags,
2475                           enum GNUNET_DISK_AccessPermissions perm)
2476 {
2477 #ifdef MINGW
2478   struct GNUNET_DISK_FileHandle *ret;
2479   HANDLE h = NULL;
2480   DWORD openMode;
2481   char *name;
2482
2483   openMode = 0;
2484   if (flags & GNUNET_DISK_OPEN_READWRITE)
2485     openMode = PIPE_ACCESS_DUPLEX;
2486   else if (flags & GNUNET_DISK_OPEN_READ)
2487     openMode = PIPE_ACCESS_INBOUND;
2488   else if (flags & GNUNET_DISK_OPEN_WRITE)
2489     openMode = PIPE_ACCESS_OUTBOUND;
2490
2491   if (flags & GNUNET_DISK_OPEN_FAILIFEXISTS)
2492     openMode |= FILE_FLAG_FIRST_PIPE_INSTANCE;
2493
2494   while (h == NULL)
2495   {
2496     DWORD error_code;
2497
2498     name = NULL;
2499     if (*fn != NULL)
2500     {
2501       GNUNET_asprintf (&name, "\\\\.\\pipe\\%.246s", fn);
2502 #if DEBUG_NPIPE
2503       LOG (GNUNET_ERROR_TYPE_DEBUG,
2504            "Trying to create an instance of named pipe `%s'\n", name);
2505 #endif
2506       /* 1) This might work just fine with UTF-8 strings as it is.
2507        * 2) This is only used by GNUnet itself, and only with latin names.
2508        */
2509       h = CreateNamedPipe (name, openMode | FILE_FLAG_OVERLAPPED,
2510                            PIPE_TYPE_BYTE | PIPE_READMODE_BYTE, 2, 1, 1, 0,
2511                            NULL);
2512     }
2513     else
2514     {
2515       GNUNET_asprintf (fn, "\\\\.\\pipe\\gnunet-%llu",
2516                        GNUNET_CRYPTO_random_u64 (GNUNET_CRYPTO_QUALITY_WEAK,
2517                                                  UINT64_MAX));
2518 #if DEBUG_NPIPE
2519       LOG (GNUNET_ERROR_TYPE_DEBUG, "Trying to create unique named pipe `%s'\n",
2520            *fn);
2521 #endif
2522       h = CreateNamedPipe (*fn,
2523                            openMode | FILE_FLAG_OVERLAPPED |
2524                            FILE_FLAG_FIRST_PIPE_INSTANCE,
2525                            PIPE_TYPE_BYTE | PIPE_READMODE_BYTE, 2, 1, 1, 0,
2526                            NULL);
2527     }
2528     error_code = GetLastError ();
2529     if (name)
2530       GNUNET_free (name);
2531     /* don't re-set name to NULL yet */
2532     if (h == INVALID_HANDLE_VALUE)
2533     {
2534       SetErrnoFromWinError (error_code);
2535 #if DEBUG_NPIPE
2536       LOG (GNUNET_ERROR_TYPE_DEBUG,
2537            "Pipe creation have failed because of %d, errno is %d\n", error_code,
2538            errno);
2539 #endif
2540       if (name == NULL)
2541       {
2542 #if DEBUG_NPIPE
2543         LOG (GNUNET_ERROR_TYPE_DEBUG,
2544              "Pipe was to be unique, considering re-creation\n");
2545 #endif
2546         GNUNET_free (*fn);
2547         *fn = NULL;
2548         if (error_code != ERROR_ACCESS_DENIED && error_code != ERROR_PIPE_BUSY)
2549         {
2550           return NULL;
2551         }
2552 #if DEBUG_NPIPE
2553         LOG (GNUNET_ERROR_TYPE_DEBUG,
2554              "Pipe name was not unique, trying again\n");
2555 #endif
2556         h = NULL;
2557       }
2558       else
2559         return NULL;
2560     }
2561   }
2562   errno = 0;
2563
2564   ret = GNUNET_malloc (sizeof (*ret));
2565   ret->h = h;
2566   ret->type = GNUNET_PIPE;
2567
2568   ret->oOverlapRead = GNUNET_malloc (sizeof (OVERLAPPED));
2569   ret->oOverlapWrite = GNUNET_malloc (sizeof (OVERLAPPED));
2570
2571   ret->oOverlapRead->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2572   ret->oOverlapWrite->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2573
2574   return ret;
2575 #else
2576   if (*fn == NULL)
2577   {
2578     char dir[] = "/tmp/gnunet-pipe-XXXXXX";
2579
2580     if (mkdtemp (dir) == NULL)
2581     {
2582       LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR, "mkdtemp");
2583       return NULL;
2584     }
2585     GNUNET_asprintf (fn, "%s/child-control", dir);
2586   }
2587
2588   if (mkfifo (*fn, translate_unix_perms (perm)) == -1)
2589   {
2590     if ((errno != EEXIST) || (0 != (flags & GNUNET_DISK_OPEN_FAILIFEXISTS)))
2591       return NULL;
2592   }
2593
2594   flags = flags & (~GNUNET_DISK_OPEN_FAILIFEXISTS);
2595   return GNUNET_DISK_file_open (*fn, flags, perm);
2596 #endif
2597 }
2598
2599
2600 /**
2601  * Opens already existing named pipe/FIFO
2602  *
2603  * @param fn name of an existing named pipe
2604  * @param flags open flags
2605  * @param perm access permissions
2606  * @return pipe handle on success, NULL on error
2607  */
2608 struct GNUNET_DISK_FileHandle *
2609 GNUNET_DISK_npipe_open (const char *fn, enum GNUNET_DISK_OpenFlags flags,
2610                         enum GNUNET_DISK_AccessPermissions perm)
2611 {
2612 #ifdef MINGW
2613   struct GNUNET_DISK_FileHandle *ret;
2614   HANDLE h;
2615   DWORD openMode;
2616
2617   openMode = 0;
2618   if (flags & GNUNET_DISK_OPEN_READWRITE)
2619     openMode = GENERIC_WRITE | GENERIC_READ;
2620   else if (flags & GNUNET_DISK_OPEN_READ)
2621     openMode = GENERIC_READ;
2622   else if (flags & GNUNET_DISK_OPEN_WRITE)
2623     openMode = GENERIC_WRITE;
2624
2625   h = CreateFile (fn, openMode, 0, NULL, OPEN_EXISTING,
2626                   FILE_FLAG_OVERLAPPED | FILE_READ_ATTRIBUTES, NULL);
2627   if (h == INVALID_HANDLE_VALUE)
2628   {
2629     SetErrnoFromWinError (GetLastError ());
2630     return NULL;
2631   }
2632
2633   ret = GNUNET_malloc (sizeof (*ret));
2634   ret->h = h;
2635   ret->type = GNUNET_PIPE;
2636   ret->oOverlapRead = GNUNET_malloc (sizeof (OVERLAPPED));
2637   ret->oOverlapWrite = GNUNET_malloc (sizeof (OVERLAPPED));
2638   ret->oOverlapRead->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2639   ret->oOverlapWrite->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2640
2641   return ret;
2642 #else
2643   flags = flags & (~GNUNET_DISK_OPEN_FAILIFEXISTS);
2644   return GNUNET_DISK_file_open (fn, flags, perm);
2645 #endif
2646 }
2647
2648 /**
2649  * Closes a named pipe/FIFO
2650  * @param pipe named pipe
2651  * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
2652  */
2653 int
2654 GNUNET_DISK_npipe_close (struct GNUNET_DISK_FileHandle *pipe)
2655 {
2656 #ifndef MINGW
2657   return close (pipe->fd) == 0 ? GNUNET_OK : GNUNET_SYSERR;
2658 #else
2659   BOOL ret;
2660
2661   ret = CloseHandle (pipe->h);
2662   if (!ret)
2663   {
2664     SetErrnoFromWinError (GetLastError ());
2665     return GNUNET_SYSERR;
2666   }
2667   else
2668     return GNUNET_OK;
2669 #endif
2670 }
2671
2672
2673 /**
2674  * Get the handle to a particular pipe end
2675  *
2676  * @param p pipe
2677  * @param n end to access
2678  * @return handle for the respective end
2679  */
2680 const struct GNUNET_DISK_FileHandle *
2681 GNUNET_DISK_pipe_handle (const struct GNUNET_DISK_PipeHandle *p,
2682                          enum GNUNET_DISK_PipeEnd n)
2683 {
2684   switch (n)
2685   {
2686   case GNUNET_DISK_PIPE_END_READ:
2687   case GNUNET_DISK_PIPE_END_WRITE:
2688     return p->fd[n];
2689   default:
2690     GNUNET_break (0);
2691     return NULL;
2692   }
2693 }
2694
2695
2696 /**
2697  * Retrieve OS file handle
2698  * @internal
2699  * @param fh GNUnet file descriptor
2700  * @param dst destination buffer
2701  * @param dst_len length of dst
2702  * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
2703  */
2704 int
2705 GNUNET_DISK_internal_file_handle_ (const struct GNUNET_DISK_FileHandle *fh,
2706                                    void *dst, size_t dst_len)
2707 {
2708 #ifdef MINGW
2709   if (dst_len < sizeof (HANDLE))
2710     return GNUNET_SYSERR;
2711   *((HANDLE *) dst) = fh->h;
2712 #else
2713   if (dst_len < sizeof (int))
2714     return GNUNET_SYSERR;
2715   *((int *) dst) = fh->fd;
2716 #endif
2717
2718   return GNUNET_OK;
2719 }
2720
2721 /* end of disk.c */