use correct PRNG initializer
[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 "disk.h"
35
36
37 #if LINUX || CYGWIN
38 #include <sys/vfs.h>
39 #else
40 #ifdef SOMEBSD
41 #include <sys/param.h>
42 #include <sys/mount.h>
43 #else
44 #ifdef OSX
45 #include <sys/param.h>
46 #include <sys/mount.h>
47 #else
48 #ifdef SOLARIS
49 #include <sys/types.h>
50 #include <sys/statvfs.h>
51 #else
52 #ifdef MINGW
53 #define         _IFMT           0170000 /* type of file */
54 #define         _IFLNK          0120000 /* symbolic link */
55 #define  S_ISLNK(m)     (((m)&_IFMT) == _IFLNK)
56 #else
57 #error PORT-ME: need to port statfs (how much space is left on the drive?)
58 #endif
59 #endif
60 #endif
61 #endif
62 #endif
63
64 #ifndef SOMEBSD
65 #ifndef WINDOWS
66 #ifndef OSX
67 #include <wordexp.h>
68 #endif
69 #endif
70 #endif
71
72 typedef struct
73 {
74   uint64_t total;
75   int include_sym_links;
76 } GetFileSizeData;
77
78 struct GNUNET_DISK_PipeHandle
79 {
80   struct GNUNET_DISK_FileHandle fd[2];
81 };
82
83 static int
84 getSizeRec (void *ptr, const char *fn)
85 {
86   GetFileSizeData *gfsd = ptr;
87 #ifdef HAVE_STAT64
88   struct stat64 buf;
89 #else
90   struct stat buf;
91 #endif
92
93 #ifdef HAVE_STAT64
94   if (0 != STAT64 (fn, &buf))
95     {
96       GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "stat64", fn);
97       return GNUNET_SYSERR;
98     }
99 #else
100   if (0 != STAT (fn, &buf))
101     {
102       GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "stat", fn);
103       return GNUNET_SYSERR;
104     }
105 #endif
106   if ((!S_ISLNK (buf.st_mode)) || (gfsd->include_sym_links == GNUNET_YES))
107     gfsd->total += buf.st_size;
108   if ((S_ISDIR (buf.st_mode)) &&
109       (0 == ACCESS (fn, X_OK)) &&
110       ((!S_ISLNK (buf.st_mode)) || (gfsd->include_sym_links == GNUNET_YES)))
111     {
112       if (GNUNET_SYSERR == GNUNET_DISK_directory_scan (fn, &getSizeRec, gfsd))
113         return GNUNET_SYSERR;
114     }
115   return GNUNET_OK;
116 }
117
118 /**
119  * Checks whether a handle is invalid
120  * @param h handle to check
121  * @return GNUNET_YES if invalid, GNUNET_NO if valid
122  */
123 int
124 GNUNET_DISK_handle_invalid (const struct GNUNET_DISK_FileHandle *h)
125 {
126 #ifdef MINGW
127   return !h || h->h == INVALID_HANDLE_VALUE ? GNUNET_YES : GNUNET_NO;
128 #else
129   return !h || h->fd == -1 ? GNUNET_YES : GNUNET_NO;
130 #endif
131 }
132
133
134 /**
135  * Move the read/write pointer in a file
136  * @param h handle of an open file
137  * @param offset position to move to
138  * @param whence specification to which position the offset parameter relates to
139  * @return the new position on success, GNUNET_SYSERR otherwise
140  */
141 off_t
142 GNUNET_DISK_file_seek (const struct GNUNET_DISK_FileHandle *h, off_t offset,
143     enum GNUNET_DISK_Seek whence)
144 {
145   if (h == NULL)
146     {
147       errno = EINVAL;
148       return GNUNET_SYSERR;
149     }
150
151 #ifdef MINGW
152   DWORD ret;
153   static DWORD t[] = { [GNUNET_DISK_SEEK_SET] = FILE_BEGIN,
154       [GNUNET_DISK_SEEK_CUR] = FILE_CURRENT, [GNUNET_DISK_SEEK_END] = FILE_END };
155
156   ret = SetFilePointer (h->h, offset, NULL, t[whence]);
157   if (ret == INVALID_SET_FILE_POINTER)
158     {
159       SetErrnoFromWinError (GetLastError ());
160       return GNUNET_SYSERR;
161     }
162   return ret;
163 #else
164   static int t[] = { [GNUNET_DISK_SEEK_SET] = SEEK_SET,
165       [GNUNET_DISK_SEEK_CUR] = SEEK_CUR, [GNUNET_DISK_SEEK_END] = SEEK_END };
166
167   return lseek (h->fd, offset, t[whence]);
168 #endif
169 }
170
171 /**
172  * Get the size of the file (or directory)
173  * of the given file (in bytes).
174  *
175  * @return GNUNET_SYSERR on error, GNUNET_OK on success
176  */
177 int
178 GNUNET_DISK_file_size (const char *filename,
179                        uint64_t *size, 
180                        int includeSymLinks)
181 {
182   GetFileSizeData gfsd;
183   int ret;
184
185   GNUNET_assert (size != NULL);
186   gfsd.total = 0;
187   gfsd.include_sym_links = includeSymLinks;
188   ret = getSizeRec (&gfsd, filename);
189   *size = gfsd.total;
190   return ret;
191 }
192
193
194
195 #if LINUX
196 #include <sys/statvfs.h>
197 #endif
198
199
200
201 /**
202  * FIXME.
203  *
204  * @param filename name of the file
205  * @param dev set to the device ID
206  * @param ino set to the inode ID
207  * @return GNUNET_OK on success
208  */
209 int GNUNET_DISK_file_get_identifiers (const char *filename,
210                                       uint32_t *dev,
211                                       uint64_t *ino)
212 {
213 #if LINUX
214   struct stat sbuf;
215   struct statvfs fbuf;
216
217   if ( (0 == stat(filename,
218                   &sbuf)) &&
219        (0 == statvfs (filename,
220                       &fbuf) ) )
221     {
222       *dev = (uint32_t) fbuf.f_fsid;
223       *ino = (uint64_t) sbuf.st_ino;
224     }
225 #endif 
226   return GNUNET_SYSERR;
227 }
228  
229
230 /**
231  * Create an (empty) temporary file on disk.
232  * 
233  * @param template component to use for the name;
234  *        does NOT contain "XXXXXX" or "/tmp/".
235  * @return NULL on error, otherwise name of fresh
236  *         file on disk in directory for temporary files
237  */
238 char *
239 GNUNET_DISK_mktemp (const char *t)
240 {
241   const char *tmpdir;
242   int fd;
243   char *tmpl;
244   char *fn;
245
246   tmpdir = getenv ("TMPDIR");
247   tmpdir = tmpdir ? tmpdir : "/tmp";
248
249   GNUNET_asprintf (&tmpl,
250                    "%s%s%s%s",
251                    tmpdir,
252                    DIR_SEPARATOR_STR,
253                    t,
254                    "XXXXXX");
255 #ifdef MINGW
256   fn = (char *) GNUNET_malloc (MAX_PATH + 1);
257   plibc_conv_to_win_path (tmpl, fn);
258   GNUNET_free (tmpl);
259 #else
260   fn = tmpl;
261 #endif
262   fd = mkstemp (fn);
263   if (fd == -1)
264     {
265       GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR,
266                                 "mkstemp",
267                                 fn);
268       GNUNET_free (fn);
269       return NULL;
270     }
271   if (0 != CLOSE (fd))
272     GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING,
273                               "close",
274                               fn);
275   return fn;
276 }
277
278
279 /**
280  * Get the number of blocks that are left on the partition that
281  * contains the given file (for normal users).
282  *
283  * @param part a file on the partition to check
284  * @return -1 on errors, otherwise the number of free blocks
285  */
286 long
287 GNUNET_DISK_get_blocks_available (const char *part)
288 {
289 #ifdef SOLARIS
290   struct statvfs buf;
291
292   if (0 != statvfs (part, &buf))
293     {
294       GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "statfs", part);
295       return -1;
296     }
297   return buf.f_bavail;
298 #elif MINGW
299   DWORD dwDummy;
300   DWORD dwBlocks;
301   char szDrive[4];
302   char *path;
303
304   path = GNUNET_STRINGS_filename_expand (part);
305   memcpy (szDrive, path, 3);
306   GNUNET_free (path);
307   szDrive[3] = 0;
308   if (!GetDiskFreeSpace (szDrive, &dwDummy, &dwDummy, &dwBlocks, &dwDummy))
309     {
310       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
311                      _("`%s' failed for drive `%s': %u\n"),
312                      "GetDiskFreeSpace", szDrive, GetLastError ());
313
314       return -1;
315     }
316   return dwBlocks;
317 #else
318   struct statfs s;
319   if (0 != statfs (part, &s))
320     {
321       GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "statfs", part);
322       return -1;
323     }
324   return s.f_bavail;
325 #endif
326 }
327
328 /**
329  * Test if fil is a directory.
330  *
331  * @return GNUNET_YES if yes, GNUNET_NO if not, GNUNET_SYSERR if it
332  *   does not exist
333  */
334 int
335 GNUNET_DISK_directory_test (const char *fil)
336 {
337   struct stat filestat;
338   int ret;
339
340   ret = STAT (fil, &filestat);
341   if (ret != 0)
342     {
343       if (errno != ENOENT)
344         {
345           GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "stat", fil);
346           return GNUNET_SYSERR;
347         }
348       return GNUNET_NO;
349     }
350   if (!S_ISDIR (filestat.st_mode))
351     return GNUNET_NO;
352   if (ACCESS (fil, R_OK | X_OK) < 0)
353     {
354       GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "access", fil);
355       return GNUNET_SYSERR;
356     }
357   return GNUNET_YES;
358 }
359
360 /**
361  * Check that fil corresponds to a filename
362  * (of a file that exists and that is not a directory).
363  * @returns GNUNET_YES if yes, GNUNET_NO if not a file, GNUNET_SYSERR if something
364  * else (will print an error message in that case, too).
365  */
366 int
367 GNUNET_DISK_file_test (const char *fil)
368 {
369   struct stat filestat;
370   int ret;
371   char *rdir;
372
373   rdir = GNUNET_STRINGS_filename_expand (fil);
374   if (rdir == NULL)
375     return GNUNET_SYSERR;
376
377   ret = STAT (rdir, &filestat);
378   if (ret != 0)
379     {
380       if (errno != ENOENT)
381         {
382           GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "stat", rdir);
383           GNUNET_free (rdir);
384           return GNUNET_SYSERR;
385         }
386       GNUNET_free (rdir);
387       return GNUNET_NO;
388     }
389   if (!S_ISREG (filestat.st_mode))
390     {
391       GNUNET_free (rdir);
392       return GNUNET_NO;
393     }
394   if (ACCESS (rdir, R_OK) < 0)
395     {
396       GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "access", rdir);
397       GNUNET_free (rdir);
398       return GNUNET_SYSERR;
399     }
400   GNUNET_free (rdir);
401   return GNUNET_YES;
402 }
403
404 /**
405  * Implementation of "mkdir -p"
406  * @param dir the directory to create
407  * @returns GNUNET_OK on success, GNUNET_SYSERR on failure
408  */
409 int
410 GNUNET_DISK_directory_create (const char *dir)
411 {
412   char *rdir;
413   int len;
414   int pos;
415   int ret = GNUNET_OK;
416
417   rdir = GNUNET_STRINGS_filename_expand (dir);
418   if (rdir == NULL)
419     return GNUNET_SYSERR;
420
421   len = strlen (rdir);
422 #ifndef MINGW
423   pos = 1;                      /* skip heading '/' */
424 #else
425   /* Local or Network path? */
426   if (strncmp (rdir, "\\\\", 2) == 0)
427     {
428       pos = 2;
429       while (rdir[pos])
430         {
431           if (rdir[pos] == '\\')
432             {
433               pos++;
434               break;
435             }
436           pos++;
437         }
438     }
439   else
440     {
441       pos = 3;                  /* strlen("C:\\") */
442     }
443 #endif
444   while (pos <= len)
445     {
446       if ((rdir[pos] == DIR_SEPARATOR) || (pos == len))
447         {
448           rdir[pos] = '\0';
449           ret = GNUNET_DISK_directory_test (rdir);
450           if (ret == GNUNET_SYSERR)
451             {
452               GNUNET_free (rdir);
453               return GNUNET_SYSERR;
454             }
455           if (ret == GNUNET_NO)
456             {
457 #ifndef MINGW
458               ret = mkdir (rdir, S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH);  /* 755 */
459 #else
460               ret = mkdir (rdir);
461 #endif
462               if ((ret != 0) && (errno != EEXIST))
463                 {
464                   GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR, "mkdir",
465                                             rdir);
466                   GNUNET_free (rdir);
467                   return GNUNET_SYSERR;
468                 }
469             }
470           rdir[pos] = DIR_SEPARATOR;
471         }
472       pos++;
473     }
474   GNUNET_free (rdir);
475   return GNUNET_OK;
476 }
477
478
479 /**
480  * Create the directory structure for storing
481  * a file.
482  *
483  * @param filename name of a file in the directory
484  * @returns GNUNET_OK on success,
485  *          GNUNET_SYSERR on failure,
486  *          GNUNET_NO if the directory
487  *          exists but is not writeable for us
488  */
489 int
490 GNUNET_DISK_directory_create_for_file (const char *dir)
491 {
492   char *rdir;
493   int len;
494   int ret;
495
496   rdir = GNUNET_STRINGS_filename_expand (dir);
497   if (rdir == NULL)
498     return GNUNET_SYSERR;
499   len = strlen (rdir);
500   while ((len > 0) && (rdir[len] != DIR_SEPARATOR))
501     len--;
502   rdir[len] = '\0';
503   ret = GNUNET_DISK_directory_create (rdir);
504   if ((ret == GNUNET_OK) && (0 != ACCESS (rdir, W_OK)))
505     ret = GNUNET_NO;
506   GNUNET_free (rdir);
507   return ret;
508 }
509
510 /**
511  * Read the contents of a binary file into a buffer.
512  * @param h handle to an open file
513  * @param result the buffer to write the result to
514  * @param len the maximum number of bytes to read
515  * @return the number of bytes read on success, GNUNET_SYSERR on failure
516  */
517 ssize_t
518 GNUNET_DISK_file_read (const struct GNUNET_DISK_FileHandle *h, void *result, 
519                        size_t len)
520 {
521   if (h == NULL)
522     {
523       errno = EINVAL;
524       return GNUNET_SYSERR;
525     }
526
527 #ifdef MINGW
528   DWORD bytesRead;
529
530   if (!ReadFile (h->h, result, len, &bytesRead, NULL))
531     {
532       SetErrnoFromWinError (GetLastError ());
533       return GNUNET_SYSERR;
534     }
535   return bytesRead;
536 #else
537   return read (h->fd, result, len);
538 #endif
539 }
540
541
542 /**
543  * Read the contents of a binary file into a buffer.
544  *
545  * @param fn file name
546  * @param result the buffer to write the result to
547  * @param len the maximum number of bytes to read
548  * @return number of bytes read, GNUNET_SYSERR on failure
549  */
550 ssize_t
551 GNUNET_DISK_fn_read (const char * const fn, 
552                      void *result,
553                      size_t len)
554 {
555   struct GNUNET_DISK_FileHandle *fh;
556   ssize_t ret;
557
558   fh = GNUNET_DISK_file_open (fn, GNUNET_DISK_OPEN_READ);
559   if (!fh)
560     return GNUNET_SYSERR;
561   ret = GNUNET_DISK_file_read (fh, result, len);
562   GNUNET_assert(GNUNET_OK == GNUNET_DISK_file_close(fh));
563
564   return ret;
565 }
566
567
568 /**
569  * Write a buffer to a file.
570  * @param h handle to open file
571  * @param buffer the data to write
572  * @param n number of bytes to write
573  * @return number of bytes written on success, GNUNET_SYSERR on error
574  */
575 ssize_t
576 GNUNET_DISK_file_write (const struct GNUNET_DISK_FileHandle *h, const void *buffer,
577                         size_t n)
578 {
579   if (h == NULL)
580     {
581       errno = EINVAL;
582       return GNUNET_SYSERR;
583     }
584
585 #ifdef MINGW
586   DWORD bytesWritten;
587
588   if (!WriteFile (h->h, buffer, n, &bytesWritten, NULL))
589     {
590       SetErrnoFromWinError (GetLastError ());
591       return GNUNET_SYSERR;
592     }
593   return bytesWritten;
594 #else
595   return write (h->fd, buffer, n);
596 #endif
597 }
598
599 /**
600  * Write a buffer to a file.  If the file is longer than the
601  * number of bytes that will be written, iit will be truncated.
602  *
603  * @param fn file name
604  * @param buffer the data to write
605  * @param n number of bytes to write
606  * @return GNUNET_OK on success, GNUNET_SYSERR on error
607  */
608 ssize_t
609 GNUNET_DISK_fn_write (const char * const fn, const void *buffer,
610     size_t n, int mode)
611 {
612   struct GNUNET_DISK_FileHandle *fh;
613   int ret;
614
615   fh = GNUNET_DISK_file_open (fn, 
616                               GNUNET_DISK_OPEN_WRITE 
617                               | GNUNET_DISK_OPEN_TRUNCATE
618                               | GNUNET_DISK_OPEN_CREATE, mode);
619   if (!fh)
620     return GNUNET_SYSERR;
621   ret = (n == GNUNET_DISK_file_write (fh, buffer, n)) ? GNUNET_OK : GNUNET_SYSERR;
622   GNUNET_assert(GNUNET_OK == GNUNET_DISK_file_close(fh));
623
624   return ret;
625 }
626
627 /**
628  * Scan a directory for files. The name of the directory
629  * must be expanded first (!).
630  * @param dirName the name of the directory
631  * @param callback the method to call for each file,
632  *        can be NULL, in that case, we only count
633  * @param data argument to pass to callback
634  * @return the number of files found, GNUNET_SYSERR on error or
635  *         ieration aborted by callback returning GNUNET_SYSERR
636  */
637 int
638 GNUNET_DISK_directory_scan (const char *dirName,
639                             GNUNET_FileNameCallback callback, void *data)
640 {
641   DIR *dinfo;
642   struct dirent *finfo;
643   struct stat istat;
644   int count = 0;
645   char *name;
646   char *dname;
647   unsigned int name_len;
648   unsigned int n_size;
649
650   GNUNET_assert (dirName != NULL);
651   dname = GNUNET_STRINGS_filename_expand (dirName);
652   while ((strlen (dname) > 0) && (dname[strlen (dname) - 1] == DIR_SEPARATOR))
653     dname[strlen (dname) - 1] = '\0';
654   if (0 != STAT (dname, &istat))
655     {
656       GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "stat", dname);
657       GNUNET_free (dname);
658       return GNUNET_SYSERR;
659     }
660   if (!S_ISDIR (istat.st_mode))
661     {
662       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
663                   _("Expected `%s' to be a directory!\n"), dirName);
664       GNUNET_free (dname);
665       return GNUNET_SYSERR;
666     }
667   errno = 0;
668   dinfo = OPENDIR (dname);
669   if ((errno == EACCES) || (dinfo == NULL))
670     {
671       GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "opendir", dname);
672       if (dinfo != NULL)
673         closedir (dinfo);
674       GNUNET_free (dname);
675       return GNUNET_SYSERR;
676     }
677   name_len = 256;
678   n_size = strlen (dname) + name_len + 2;
679   name = GNUNET_malloc (n_size);
680   while ((finfo = readdir (dinfo)) != NULL)
681     {
682       if ((0 == strcmp (finfo->d_name, ".")) ||
683           (0 == strcmp (finfo->d_name, "..")))
684         continue;
685       if (callback != NULL)
686         {
687           if (name_len < strlen (finfo->d_name))
688             {
689               GNUNET_free (name);
690               name_len = strlen (finfo->d_name);
691               n_size = strlen (dname) + name_len + 2;
692               name = GNUNET_malloc (n_size);
693             }
694           /* dname can end in "/" only if dname == "/";
695              if dname does not end in "/", we need to add
696              a "/" (otherwise, we must not!) */
697           GNUNET_snprintf (name,
698                            n_size,
699                            "%s%s%s",
700                            dname,
701                            (strcmp (dname, DIR_SEPARATOR_STR) ==
702                             0) ? "" : DIR_SEPARATOR_STR, finfo->d_name);
703           if (GNUNET_OK != callback (data, name))
704             {
705               closedir (dinfo);
706               GNUNET_free (name);
707               GNUNET_free (dname);
708               return GNUNET_SYSERR;
709             }
710         }
711       count++;
712     }
713   closedir (dinfo);
714   GNUNET_free (name);
715   GNUNET_free (dname);
716   return count;
717 }
718
719
720 /**
721  * Opaque handle used for iterating over a directory.
722  */
723 struct GNUNET_DISK_DirectoryIterator
724 {
725   /**
726    * Our scheduler.
727    */
728   struct GNUNET_SCHEDULER_Handle *sched;
729
730   /**
731    * Function to call on directory entries.
732    */
733   GNUNET_DISK_DirectoryIteratorCallback callback;
734
735   /**
736    * Closure for callback.
737    */
738   void *callback_cls;
739
740   /**
741    * Reference to directory.
742    */
743   DIR *directory;
744
745   /**
746    * Directory name.
747    */
748   char *dirname;
749
750   /**
751    * Next filename to process.
752    */
753   char *next_name;
754
755   /**
756    * Our priority.
757    */
758   enum GNUNET_SCHEDULER_Priority priority;
759
760 };
761
762
763 /**
764  * Task used by the directory iterator.
765  */
766 static void
767 directory_iterator_task (void *cls,
768                          const struct GNUNET_SCHEDULER_TaskContext *tc)
769 {
770   struct GNUNET_DISK_DirectoryIterator *iter = cls;
771   char *name;
772
773   name = iter->next_name;
774   GNUNET_assert (name != NULL);
775   iter->next_name = NULL;
776   iter->callback (iter->callback_cls, iter, name, iter->dirname);
777   GNUNET_free (name);
778 }
779
780
781 /**
782  * This function must be called during the DiskIteratorCallback
783  * (exactly once) to schedule the task to process the next
784  * filename in the directory (if there is one).
785  *
786  * @param iter opaque handle for the iterator
787  * @param can set to GNUNET_YES to terminate the iteration early
788  * @return GNUNET_YES if iteration will continue,
789  *         GNUNET_NO if this was the last entry (and iteration is complete),
790  *         GNUNET_SYSERR if abort was YES
791  */
792 int
793 GNUNET_DISK_directory_iterator_next (struct GNUNET_DISK_DirectoryIterator
794                                      *iter, int can)
795 {
796   struct dirent *finfo;
797
798   GNUNET_assert (iter->next_name == NULL);
799   if (can == GNUNET_YES)
800     {
801       closedir (iter->directory);
802       GNUNET_free (iter->dirname);
803       GNUNET_free (iter);
804       return GNUNET_SYSERR;
805     }
806   while (NULL != (finfo = readdir (iter->directory)))
807     {
808       if ((0 == strcmp (finfo->d_name, ".")) ||
809           (0 == strcmp (finfo->d_name, "..")))
810         continue;
811       GNUNET_asprintf (&iter->next_name,
812                        "%s%s%s",
813                        iter->dirname, DIR_SEPARATOR_STR, finfo->d_name);
814       break;
815     }
816   if (finfo == NULL)
817     {
818       GNUNET_DISK_directory_iterator_next (iter, GNUNET_YES);
819       return GNUNET_NO;
820     }
821   GNUNET_SCHEDULER_add_after (iter->sched,
822                               GNUNET_YES,
823                               iter->priority,
824                               GNUNET_SCHEDULER_NO_TASK,
825                               &directory_iterator_task, iter);
826   return GNUNET_YES;
827 }
828
829
830 /**
831  * Scan a directory for files using the scheduler to run a task for
832  * each entry.  The name of the directory must be expanded first (!).
833  * If a scheduler does not need to be used, GNUNET_DISK_directory_scan
834  * may provide a simpler API.
835  *
836  * @param sched scheduler to use
837  * @param prio priority to use
838  * @param dirName the name of the directory
839  * @param callback the method to call for each file
840  * @param callback_cls closure for callback
841  */
842 void
843 GNUNET_DISK_directory_iterator_start (struct GNUNET_SCHEDULER_Handle *sched,
844                                       enum GNUNET_SCHEDULER_Priority prio,
845                                       const char *dirName,
846                                       GNUNET_DISK_DirectoryIteratorCallback
847                                       callback, void *callback_cls)
848 {
849   struct GNUNET_DISK_DirectoryIterator *di;
850
851   di = GNUNET_malloc (sizeof (struct GNUNET_DISK_DirectoryIterator));
852   di->sched = sched;
853   di->callback = callback;
854   di->callback_cls = callback_cls;
855   di->directory = OPENDIR (dirName);
856   di->dirname = GNUNET_strdup (dirName);
857   di->priority = prio;
858   GNUNET_DISK_directory_iterator_next (di, GNUNET_NO);
859 }
860
861
862 static int
863 remove_helper (void *unused, const char *fn)
864 {
865   GNUNET_DISK_directory_remove (fn);
866   return GNUNET_OK;
867 }
868
869 /**
870  * Remove all files in a directory (rm -rf). Call with
871  * caution.
872  *
873  *
874  * @param fileName the file to remove
875  * @return GNUNET_OK on success, GNUNET_SYSERR on error
876  */
877 int
878 GNUNET_DISK_directory_remove (const char *fileName)
879 {
880   struct stat istat;
881
882   if (0 != LSTAT (fileName, &istat))
883     return GNUNET_NO;           /* file may not exist... */
884   if (UNLINK (fileName) == 0)
885     return GNUNET_OK;
886   if ((errno != EISDIR) &&
887       /* EISDIR is not sufficient in all cases, e.g.
888          sticky /tmp directory may result in EPERM on BSD.
889          So we also explicitly check "isDirectory" */
890       (GNUNET_YES != GNUNET_DISK_directory_test (fileName)))
891     {
892       GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "rmdir", fileName);
893       return GNUNET_SYSERR;
894     }
895   if (GNUNET_SYSERR ==
896       GNUNET_DISK_directory_scan (fileName, remove_helper, NULL))
897     return GNUNET_SYSERR;
898   if (0 != RMDIR (fileName))
899     {
900       GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "rmdir", fileName);
901       return GNUNET_SYSERR;
902     }
903   return GNUNET_OK;
904 }
905
906 #define COPY_BLK_SIZE 65536
907
908 /**
909  * Copy a file.
910  * @return GNUNET_OK on success, GNUNET_SYSERR on error
911  */
912 int
913 GNUNET_DISK_file_copy (const char *src, const char *dst)
914 {
915   char *buf;
916   uint64_t pos;
917   uint64_t size;
918   size_t len;
919   struct GNUNET_DISK_FileHandle *in;
920   struct GNUNET_DISK_FileHandle *out;
921
922   if (GNUNET_OK != GNUNET_DISK_file_size (src, &size, GNUNET_YES))
923     return GNUNET_SYSERR;
924   pos = 0;
925   in = GNUNET_DISK_file_open (src, GNUNET_DISK_OPEN_READ);
926   if (!in)
927     return GNUNET_SYSERR;
928   out = GNUNET_DISK_file_open (dst, GNUNET_DISK_OPEN_WRITE
929                                | GNUNET_DISK_OPEN_CREATE | GNUNET_DISK_OPEN_FAILIFEXISTS,
930                                GNUNET_DISK_PERM_USER_READ | GNUNET_DISK_PERM_USER_WRITE
931                                | GNUNET_DISK_PERM_GROUP_READ | GNUNET_DISK_PERM_GROUP_WRITE);
932   if (!out)
933     {
934       GNUNET_DISK_file_close (in);
935       return GNUNET_SYSERR;
936     }
937   buf = GNUNET_malloc (COPY_BLK_SIZE);
938   while (pos < size)
939     {
940       len = COPY_BLK_SIZE;
941       if (len > size - pos)
942         len = size - pos;
943       if (len != GNUNET_DISK_file_read (in, buf, len))
944         goto FAIL;
945       if (len != GNUNET_DISK_file_write (out, buf, len))
946         goto FAIL;
947       pos += len;
948     }
949   GNUNET_free (buf);
950   GNUNET_DISK_file_close (in);
951   GNUNET_DISK_file_close (out);
952   return GNUNET_OK;
953 FAIL:
954   GNUNET_free (buf);
955   GNUNET_DISK_file_close (in);
956   GNUNET_DISK_file_close (out);
957   return GNUNET_SYSERR;
958 }
959
960
961 /**
962  * @brief Removes special characters as ':' from a filename.
963  * @param fn the filename to canonicalize
964  */
965 void
966 GNUNET_DISK_filename_canonicalize (char *fn)
967 {
968   char *idx;
969   char c;
970
971   idx = fn;
972   while (*idx)
973     {
974       c = *idx;
975
976       if (c == '/' || c == '\\' || c == ':' || c == '*' || c == '?' ||
977           c == '"' || c == '<' || c == '>' || c == '|')
978         {
979           *idx = '_';
980         }
981
982       idx++;
983     }
984 }
985
986
987
988 /**
989  * @brief Change owner of a file
990  */
991 int
992 GNUNET_DISK_file_change_owner (const char *filename, const char *user)
993 {
994 #ifndef MINGW
995   struct passwd *pws;
996
997   pws = getpwnam (user);
998   if (pws == NULL)
999     {
1000       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1001                   _("Cannot obtain information about user `%s': %s\n"),
1002                   user, STRERROR (errno));
1003       return GNUNET_SYSERR;
1004     }
1005   if (0 != chown (filename, pws->pw_uid, pws->pw_gid))
1006     GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "chown", filename);
1007 #endif
1008   return GNUNET_OK;
1009 }
1010
1011
1012 /**
1013  * Lock a part of a file
1014  * @param fh file handle
1015  * @lockStart absolute position from where to lock
1016  * @lockEnd absolute position until where to lock
1017  * @excl GNUNET_YES for an exclusive lock
1018  * @return GNUNET_OK on success, GNUNET_SYSERR on error
1019  */
1020 int
1021 GNUNET_DISK_file_lock (struct GNUNET_DISK_FileHandle *fh, off_t lockStart,
1022     off_t lockEnd, int excl)
1023 {
1024   if (fh == NULL)
1025     {
1026       errno = EINVAL;
1027       return GNUNET_SYSERR;
1028     }
1029
1030 #ifndef MINGW
1031   struct flock fl;
1032
1033   memset (&fl, 0, sizeof(struct flock));
1034   fl.l_type = excl ? F_WRLCK : F_RDLCK;
1035   fl.l_whence = SEEK_SET;
1036   fl.l_start = lockStart;
1037   fl.l_len = lockEnd;
1038
1039   return fcntl (fh->fd, F_SETLK, &fl) != 0 ? GNUNET_SYSERR : GNUNET_OK;
1040 #else
1041   OVERLAPPED o;
1042
1043   memset (&o, 0, sizeof(OVERLAPPED));
1044   o.Offset = lockStart;
1045
1046   if (!LockFileEx (fh->h, (excl ? LOCKFILE_EXCLUSIVE_LOCK : 0)
1047       | LOCKFILE_FAIL_IMMEDIATELY, 0, lockEnd - lockStart, 0, &o))
1048     {
1049       SetErrnoFromWinError (GetLastError ());
1050       return GNUNET_SYSERR;
1051     }
1052
1053   return GNUNET_OK;
1054 #endif
1055 }
1056
1057
1058 /**
1059  * Unlock a part of a file
1060  * @param fh file handle
1061  * @lockStart absolute position from where to unlock
1062  * @lockEnd absolute position until where to unlock
1063  * @return GNUNET_OK on success, GNUNET_SYSERR on error
1064  */
1065 int
1066 GNUNET_DISK_file_unlock (struct GNUNET_DISK_FileHandle *fh, off_t unlockStart,
1067     off_t unlockEnd)
1068 {
1069   if (fh == NULL)
1070     {
1071       errno = EINVAL;
1072       return GNUNET_SYSERR;
1073     }
1074
1075 #ifndef MINGW
1076   struct flock fl;
1077
1078   memset (&fl, 0, sizeof(struct flock));
1079   fl.l_type = F_UNLCK;
1080   fl.l_whence = SEEK_SET;
1081   fl.l_start = unlockStart;
1082   fl.l_len = unlockEnd;
1083
1084   return fcntl (fh->fd, F_SETLK, &fl) != 0 ? GNUNET_SYSERR : GNUNET_OK;
1085 #else
1086   OVERLAPPED o;
1087
1088   memset (&o, 0, sizeof(OVERLAPPED));
1089   o.Offset = unlockStart;
1090
1091   if (!UnlockFileEx (fh->h, 0, unlockEnd - unlockStart, 0, &o))
1092     {
1093       SetErrnoFromWinError (GetLastError ());
1094       return GNUNET_SYSERR;
1095     }
1096
1097   return GNUNET_OK;
1098 #endif
1099 }
1100
1101
1102 /**
1103  * Open a file
1104  * @param fn file name to be opened
1105  * @param flags opening flags, a combination of GNUNET_DISK_OPEN_xxx bit flags
1106  * @param perm permissions for the newly created file
1107  * @return IO handle on success, NULL on error
1108  */
1109 struct GNUNET_DISK_FileHandle *
1110 GNUNET_DISK_file_open (const char *fn, int flags, ...)
1111 {
1112   char *expfn;
1113   struct GNUNET_DISK_FileHandle *ret;
1114 #ifdef MINGW
1115   DWORD access;
1116   DWORD disp;
1117   HANDLE h;
1118 #else
1119   int oflags;
1120   int mode;
1121   int fd;
1122 #endif
1123
1124   expfn = GNUNET_STRINGS_filename_expand (fn);
1125
1126 #ifndef MINGW
1127   mode = 0;
1128   if (GNUNET_DISK_OPEN_READWRITE == (flags & GNUNET_DISK_OPEN_READWRITE))
1129     oflags = O_RDWR; /* note: O_RDWR is NOT always O_RDONLY | O_WRONLY */
1130   else if (flags & GNUNET_DISK_OPEN_READ)
1131     oflags = O_RDONLY;
1132   else if (flags & GNUNET_DISK_OPEN_WRITE)
1133     oflags = O_WRONLY;
1134   else
1135     {
1136       GNUNET_break (0);
1137       GNUNET_free (expfn);
1138       return NULL;
1139     }
1140   if (flags & GNUNET_DISK_OPEN_FAILIFEXISTS)
1141     oflags |= (O_CREAT & O_EXCL);
1142   if (flags & GNUNET_DISK_OPEN_TRUNCATE)
1143     oflags |= O_TRUNC;
1144   if (flags & GNUNET_DISK_OPEN_APPEND)
1145     oflags |= O_APPEND;
1146   if (flags & GNUNET_DISK_OPEN_CREATE)
1147     {
1148       int perm;
1149
1150       oflags |= O_CREAT;
1151
1152       va_list arg;
1153       va_start (arg, flags);
1154       perm = va_arg (arg, int);
1155       va_end (arg);
1156
1157       if (perm & GNUNET_DISK_PERM_USER_READ)
1158         mode |= S_IRUSR;
1159       if (perm & GNUNET_DISK_PERM_USER_WRITE)
1160         mode |= S_IWUSR;
1161       if (perm & GNUNET_DISK_PERM_USER_EXEC)
1162         mode |= S_IXUSR;
1163       if (perm & GNUNET_DISK_PERM_GROUP_READ)
1164         mode |= S_IRGRP;
1165       if (perm & GNUNET_DISK_PERM_GROUP_WRITE)
1166         mode |= S_IWGRP;
1167       if (perm & GNUNET_DISK_PERM_GROUP_EXEC)
1168         mode |= S_IXGRP;
1169       if (perm & GNUNET_DISK_PERM_OTHER_READ)
1170         mode |= S_IROTH;
1171       if (perm & GNUNET_DISK_PERM_OTHER_WRITE)
1172         mode |= S_IWOTH;
1173       if (perm & GNUNET_DISK_PERM_OTHER_EXEC)
1174         mode |= S_IXOTH;
1175     }
1176
1177   fd = open (expfn, oflags | O_LARGEFILE, mode);
1178   if (fd == -1)
1179   {
1180     GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "open", expfn);
1181     GNUNET_free (expfn);
1182     return NULL;
1183   }
1184 #else
1185   access = 0;
1186   disp = OPEN_ALWAYS;
1187
1188   if (GNUNET_DISK_OPEN_READWRITE == (flags & GNUNET_DISK_OPEN_READWRITE))
1189     access = FILE_READ_DATA | FILE_WRITE_DATA;
1190   else if (flags & GNUNET_DISK_OPEN_READ)
1191     access = FILE_READ_DATA;
1192   else if (flags & GNUNET_DISK_OPEN_WRITE)
1193     access = FILE_WRITE_DATA;
1194
1195   if (flags & GNUNET_DISK_OPEN_FAILIFEXISTS)
1196     {
1197       disp = CREATE_NEW;
1198     }
1199   else if (flags & GNUNET_DISK_OPEN_CREATE)
1200     {
1201       if (flags & GNUNET_DISK_OPEN_TRUNCATE)
1202         disp = CREATE_ALWAYS;
1203       else
1204         disp = OPEN_ALWAYS;
1205     }
1206   else if (flags & GNUNET_DISK_OPEN_TRUNCATE)
1207     {
1208       disp = TRUNCATE_EXISTING;
1209     }
1210   else
1211   {
1212     disp = OPEN_ALWAYS;
1213   }
1214
1215   /* TODO: access priviledges? */
1216   h = CreateFile (expfn, access, FILE_SHARE_DELETE | FILE_SHARE_READ
1217       | FILE_SHARE_WRITE, NULL, disp, FILE_ATTRIBUTE_NORMAL, NULL);
1218   if (h == INVALID_HANDLE_VALUE)
1219   {
1220     SetErrnoFromWinError (GetLastError ());
1221     GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "open", expfn);
1222     GNUNET_free (expfn);
1223     return NULL;
1224   }
1225
1226   if (flags & GNUNET_DISK_OPEN_APPEND)
1227     if (SetFilePointer (h, 0, 0, FILE_END) == INVALID_SET_FILE_POINTER)
1228     {
1229       SetErrnoFromWinError (GetLastError ());
1230       GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "SetFilePointer", expfn);
1231       CloseHandle (h);
1232       GNUNET_free (expfn);
1233       return NULL;
1234     }
1235 #endif
1236
1237   ret = GNUNET_malloc(sizeof(struct GNUNET_DISK_FileHandle));
1238 #ifdef MINGW
1239   ret->h = h;
1240 #else
1241   ret->fd = fd;
1242 #endif
1243   GNUNET_free (expfn);
1244   return ret;
1245 }
1246
1247 /**
1248  * Close an open file
1249  * @param h file handle
1250  * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
1251  */
1252 int
1253 GNUNET_DISK_file_close (struct GNUNET_DISK_FileHandle *h)
1254 {
1255   if (h == NULL)
1256     {
1257       errno = EINVAL;
1258       return GNUNET_SYSERR;
1259     }
1260
1261 #if MINGW
1262   if (!CloseHandle (h->h))
1263   {
1264     SetErrnoFromWinError (GetLastError ());
1265     GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "close");
1266     GNUNET_free (h);
1267     return GNUNET_SYSERR;
1268   }
1269 #else
1270   if (close (h->fd) != 0)
1271   {
1272     GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "close");
1273     GNUNET_free (h);
1274     return GNUNET_SYSERR;
1275   }
1276 #endif
1277   GNUNET_free (h);
1278   return GNUNET_OK;
1279 }
1280
1281 /**
1282  * Construct full path to a file inside of the private
1283  * directory used by GNUnet.  Also creates the corresponding
1284  * directory.  If the resulting name is supposed to be
1285  * a directory, end the last argument in '/' (or pass
1286  * DIR_SEPARATOR_STR as the last argument before NULL).
1287  *
1288  * @param cfg configuration to use (determines HOME)
1289  * @param serviceName name of the service
1290  * @param varargs is NULL-terminated list of
1291  *                path components to append to the
1292  *                private directory name.
1293  * @return the constructed filename
1294  */
1295 char *
1296 GNUNET_DISK_get_home_filename (const struct GNUNET_CONFIGURATION_Handle *cfg,
1297                                const char *serviceName, ...)
1298 {
1299   const char *c;
1300   char *pfx;
1301   char *ret;
1302   va_list ap;
1303   unsigned int needed;
1304
1305   if (GNUNET_OK !=
1306       GNUNET_CONFIGURATION_get_value_filename (cfg,
1307                                                serviceName, "HOME", &pfx))
1308     return NULL;
1309   if (pfx == NULL)
1310     {
1311       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1312                   _("No `%s' specified for service `%s' in configuration.\n"),
1313                   "HOME", serviceName);
1314       return NULL;
1315     }
1316   needed = strlen (pfx) + 2;
1317   if ((pfx[strlen (pfx) - 1] != '/') && (pfx[strlen (pfx) - 1] != '\\'))
1318     needed++;
1319   va_start (ap, serviceName);
1320   while (1)
1321     {
1322       c = va_arg (ap, const char *);
1323       if (c == NULL)
1324         break;
1325       needed += strlen (c);
1326       if ((c[strlen (c) - 1] != '/') && (c[strlen (c) - 1] != '\\'))
1327         needed++;
1328     }
1329   va_end (ap);
1330   ret = GNUNET_malloc (needed);
1331   strcpy (ret, pfx);
1332   GNUNET_free (pfx);
1333   va_start (ap, serviceName);
1334   while (1)
1335     {
1336       c = va_arg (ap, const char *);
1337       if (c == NULL)
1338         break;
1339       if ((c[strlen (c) - 1] != '/') && (c[strlen (c) - 1] != '\\'))
1340         strcat (ret, DIR_SEPARATOR_STR);
1341       strcat (ret, c);
1342     }
1343   va_end (ap);
1344   if ((ret[strlen (ret) - 1] != '/') && (ret[strlen (ret) - 1] != '\\'))
1345     GNUNET_DISK_directory_create_for_file (ret);
1346   else
1347     GNUNET_DISK_directory_create (ret);
1348   return ret;
1349 }
1350
1351 struct GNUNET_DISK_MapHandle
1352 {
1353 #ifdef MINGW
1354   HANDLE h;
1355 #else
1356   void *addr;
1357   size_t len;
1358 #endif
1359 };
1360
1361
1362 /**
1363  * Map a file into memory
1364  * @param h open file handle
1365  * @param m handle to the new mapping
1366  * @param access access specification, GNUNET_DISK_MAP_xxx
1367  * @param len size of the mapping
1368  * @return pointer to the mapped memory region, NULL on failure
1369  */
1370 void *
1371 GNUNET_DISK_file_map (const struct GNUNET_DISK_FileHandle *h, struct GNUNET_DISK_MapHandle **m,
1372     int access, size_t len)
1373 {
1374   if (h == NULL)
1375     {
1376       errno = EINVAL;
1377       return NULL;
1378     }
1379
1380 #ifdef MINGW
1381   DWORD mapAccess, protect;
1382   void *ret;
1383
1384   if (access & GNUNET_DISK_MAP_READ && access & GNUNET_DISK_MAP_WRITE)
1385     {
1386       protect = PAGE_READWRITE;
1387       mapAccess = FILE_MAP_ALL_ACCESS;
1388     }
1389   else if (access & GNUNET_DISK_MAP_READ)
1390     {
1391       protect = PAGE_READONLY;
1392       mapAccess = FILE_MAP_READ;
1393     }
1394   else if (access & GNUNET_DISK_MAP_WRITE)
1395     {
1396       protect = PAGE_READWRITE;
1397       mapAccess = FILE_MAP_WRITE;
1398     }
1399   else
1400     {
1401       GNUNET_break (0);
1402       return NULL;
1403     }
1404
1405   *m = GNUNET_malloc (sizeof (struct GNUNET_DISK_MapHandle));
1406   (*m)->h = CreateFileMapping (h->h, NULL, protect, 0, 0, NULL);
1407   if ((*m)->h == INVALID_HANDLE_VALUE)
1408     {
1409       SetErrnoFromWinError (GetLastError ());
1410       GNUNET_free (*m);
1411       return NULL;
1412     }
1413
1414   ret = MapViewOfFile ((*m)->h, mapAccess, 0, 0, len);
1415   if (!ret)
1416     {
1417       SetErrnoFromWinError (GetLastError ());
1418       CloseHandle ((*m)->h);
1419       GNUNET_free (*m);
1420     }
1421
1422   return ret;
1423 #else
1424   int prot;
1425
1426   prot = 0;
1427   if (access & GNUNET_DISK_MAP_READ)
1428     prot = PROT_READ;
1429   if (access & GNUNET_DISK_MAP_WRITE)
1430     prot |= PROT_WRITE;
1431   *m = GNUNET_malloc (sizeof (struct GNUNET_DISK_MapHandle));
1432   (*m)->addr = mmap (NULL, len, prot, MAP_SHARED, h->fd, 0);
1433   (*m)->len = len;
1434   return (*m)->addr;
1435 #endif
1436 }
1437
1438 /**
1439  * Unmap a file
1440  * @param h mapping handle
1441  * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
1442  */
1443 int
1444 GNUNET_DISK_file_unmap (struct GNUNET_DISK_MapHandle *h)
1445 {
1446   int ret;
1447   if (h == NULL)
1448     {
1449       errno = EINVAL;
1450       return GNUNET_SYSERR;
1451     }
1452
1453 #ifdef MINGW
1454   ret = UnmapViewOfFile (h->h) ? GNUNET_OK : GNUNET_SYSERR;
1455   if (ret != GNUNET_OK)
1456     SetErrnoFromWinError (GetLastError ());
1457   if (!CloseHandle (h->h) && (ret == GNUNET_OK))
1458     {
1459       ret = GNUNET_SYSERR;
1460       SetErrnoFromWinError (GetLastError ());
1461     }
1462 #else
1463   ret = munmap (h->addr, h->len) != -1 ? GNUNET_OK : GNUNET_SYSERR;
1464 #endif
1465   GNUNET_free (h);
1466   return ret;
1467 }
1468
1469
1470 /**
1471  * Write file changes to disk
1472  * @param h handle to an open file
1473  * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
1474  */
1475 int
1476 GNUNET_DISK_file_sync (const struct GNUNET_DISK_FileHandle *h)
1477 {
1478   if (h == NULL)
1479     {
1480       errno = EINVAL;
1481       return GNUNET_SYSERR;
1482     }
1483
1484 #ifdef MINGW
1485   int ret;
1486
1487   ret = FlushFileBuffers (h->h) ? GNUNET_OK : GNUNET_SYSERR;
1488   if (ret != GNUNET_OK)
1489     SetErrnoFromWinError (GetLastError ());
1490   return ret;
1491 #elif FREEBSD || OPENBSD
1492   return fsync (h->fd) == -1 ? GNUNET_SYSERR : GNUNET_OK;
1493 #else
1494   return fdatasync (h->fd) == -1 ? GNUNET_SYSERR : GNUNET_OK;
1495 #endif
1496 }
1497
1498 /**
1499  * Creates an interprocess channel
1500  * @param blocking creates an asynchronous pipe if set to GNUNET_NO
1501  * @return handle to the new pipe, NULL on error
1502  */
1503 struct GNUNET_DISK_PipeHandle *
1504 GNUNET_DISK_pipe (int blocking)
1505 {
1506   struct GNUNET_DISK_PipeHandle *p;
1507   int err;
1508
1509   err = GNUNET_NO;
1510   p = GNUNET_malloc (sizeof (struct GNUNET_DISK_PipeHandle));
1511
1512 #ifndef MINGW
1513   int fd[2];
1514   int ret;
1515   int flags;
1516
1517   ret = pipe (fd);
1518   if (ret != -1)
1519     {
1520       p->fd[0].fd = fd[0];
1521       p->fd[1].fd = fd[1];
1522
1523       if (!blocking)
1524         {
1525           flags = fcntl (fd[0], F_GETFL);
1526           flags |= O_NONBLOCK;
1527           ret = fcntl (fd[0], F_SETFL, flags);
1528           if (ret != -1)
1529             {
1530               flags = fcntl (fd[1], F_GETFL);
1531               flags |= O_NONBLOCK;
1532               ret = fcntl (fd[1], F_SETFL, flags);
1533             }
1534           if (ret == -1)
1535             {
1536               GNUNET_log_strerror(GNUNET_ERROR_TYPE_ERROR, "fcntl");
1537               close (fd[0]);
1538               close (fd[1]);
1539               err = GNUNET_YES;
1540             }
1541         }
1542     }
1543   else
1544     err = GNUNET_YES;
1545 #else
1546   BOOL ret;
1547
1548   ret = CreatePipe (&p->fd[0].h, &p->fd[1].h, NULL, 0);
1549   if (ret)
1550     {
1551       if (!blocking)
1552         {
1553           DWORD mode;
1554
1555           mode = PIPE_NOWAIT;
1556           SetNamedPipeHandleState (p->fd[0].h, &mode, NULL, NULL);
1557           SetNamedPipeHandleState (p->fd[1].h, &mode, NULL, NULL);
1558           /* this always fails on Windows 95, so we don't care about error handling */
1559         }
1560     }
1561   else
1562       err = GNUNET_YES;
1563 #endif
1564
1565   if (GNUNET_YES == err)
1566     {
1567       GNUNET_free (p);
1568       p = NULL;
1569     }
1570
1571   return p;
1572 }
1573
1574
1575 /**
1576  * Closes an interprocess channel
1577  *
1578  * @param p pipe to close
1579  * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
1580  */
1581 int
1582 GNUNET_DISK_pipe_close (struct GNUNET_DISK_PipeHandle *p)
1583 {
1584   int ret = GNUNET_OK;
1585 #ifdef MINGW
1586   if (!CloseHandle (p->fd[0].h))
1587     {
1588       SetErrnoFromWinError (GetLastError ());
1589       ret = GNUNET_SYSERR;
1590     }
1591
1592   if (!CloseHandle (p->fd[1].h))
1593     {
1594       SetErrnoFromWinError (GetLastError ());
1595       ret = GNUNET_SYSERR;
1596     }
1597 #else
1598   int save;
1599   
1600   if (0 != close (p->fd[0].fd))
1601     {
1602       ret = GNUNET_SYSERR;
1603       save = errno;
1604     }
1605   else
1606     save = 0;
1607   
1608   if (0 != close (p->fd[1].fd))
1609     ret = GNUNET_SYSERR;
1610   else
1611     errno = save;
1612 #endif
1613   GNUNET_free (p);
1614   return ret;
1615 }
1616
1617
1618 /**
1619  * Get the handle to a particular pipe end
1620  * @param p pipe
1621  * @param n number of the end
1622  */
1623 const struct GNUNET_DISK_FileHandle *
1624 GNUNET_DISK_pipe_handle (const struct GNUNET_DISK_PipeHandle *p, int n)
1625 {
1626   return &p->fd[n];
1627 }
1628
1629 /**
1630  * Retrieve OS file handle
1631  * @internal
1632  * @param fh GNUnet file descriptor
1633  * @param dst destination buffer
1634  * @param dst_len length of dst
1635  * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
1636  */
1637 int
1638 GNUNET_internal_disk_file_handle (const struct GNUNET_DISK_FileHandle *fh,
1639     void *dst, unsigned int dst_len)
1640 {
1641 #ifdef MINGW
1642   if (dst_len < sizeof (HANDLE))
1643     return GNUNET_SYSERR;
1644   *((HANDLE *) dst) = fh->h;
1645 #else
1646   if (dst_len < sizeof(int))
1647     return GNUNET_SYSERR;
1648   *((int *) dst) = fh->fd;
1649 #endif
1650
1651   return GNUNET_OK;
1652 }
1653
1654 /* end of disk.c */