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