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