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