keep addr
[oweals/gnunet.git] / src / util / disk.c
1 /*
2      This file is part of GNUnet.
3      (C) 2001, 2002, 2005, 2006 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  */
26
27 #include "platform.h"
28 #include "gnunet_common.h"
29 #include "gnunet_directories.h"
30 #include "gnunet_disk_lib.h"
31 #include "gnunet_scheduler_lib.h"
32 #include "gnunet_strings_lib.h"
33
34
35 #if LINUX || CYGWIN
36 #include <sys/vfs.h>
37 #else
38 #ifdef SOMEBSD
39 #include <sys/param.h>
40 #include <sys/mount.h>
41 #else
42 #ifdef OSX
43 #include <sys/param.h>
44 #include <sys/mount.h>
45 #else
46 #ifdef SOLARIS
47 #include <sys/types.h>
48 #include <sys/statvfs.h>
49 #else
50 #ifdef MINGW
51 #define         _IFMT           0170000 /* type of file */
52 #define         _IFLNK          0120000 /* symbolic link */
53 #define  S_ISLNK(m)     (((m)&_IFMT) == _IFLNK)
54 #else
55 #error PORT-ME: need to port statfs (how much space is left on the drive?)
56 #endif
57 #endif
58 #endif
59 #endif
60 #endif
61
62 #ifndef SOMEBSD
63 #ifndef WINDOWS
64 #ifndef OSX
65 #include <wordexp.h>
66 #endif
67 #endif
68 #endif
69
70 typedef struct
71 {
72   unsigned long long total;
73   int include_sym_links;
74 } GetFileSizeData;
75
76 static int
77 getSizeRec (void *ptr, const char *fn)
78 {
79   GetFileSizeData *gfsd = ptr;
80 #ifdef HAVE_STAT64
81   struct stat64 buf;
82 #else
83   struct stat buf;
84 #endif
85
86 #ifdef HAVE_STAT64
87   if (0 != STAT64 (fn, &buf))
88     {
89       GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "stat64", fn);
90       return GNUNET_SYSERR;
91     }
92 #else
93   if (0 != STAT (fn, &buf))
94     {
95       GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "stat", fn);
96       return GNUNET_SYSERR;
97     }
98 #endif
99   if ((!S_ISLNK (buf.st_mode)) || (gfsd->include_sym_links == GNUNET_YES))
100     gfsd->total += buf.st_size;
101   if ((S_ISDIR (buf.st_mode)) &&
102       (0 == ACCESS (fn, X_OK)) &&
103       ((!S_ISLNK (buf.st_mode)) || (gfsd->include_sym_links == GNUNET_YES)))
104     {
105       if (GNUNET_SYSERR == GNUNET_DISK_directory_scan (fn, &getSizeRec, gfsd))
106         return GNUNET_SYSERR;
107     }
108   return GNUNET_OK;
109 }
110
111 /**
112  * Get the size of the file (or directory)
113  * of the given file (in bytes).
114  *
115  * @return GNUNET_SYSERR on error, GNUNET_OK on success
116  */
117 int
118 GNUNET_DISK_file_size (const char *filename,
119                        unsigned long long *size, int includeSymLinks)
120 {
121   GetFileSizeData gfsd;
122   int ret;
123
124   GNUNET_assert (size != NULL);
125   gfsd.total = 0;
126   gfsd.include_sym_links = includeSymLinks;
127   ret = getSizeRec (&gfsd, filename);
128   *size = gfsd.total;
129   return ret;
130 }
131
132 /**
133  * Get the number of blocks that are left on the partition that
134  * contains the given file (for normal users).
135  *
136  * @param part a file on the partition to check
137  * @return -1 on errors, otherwise the number of free blocks
138  */
139 long
140 GNUNET_DISK_get_blocks_available (const char *part)
141 {
142 #ifdef SOLARIS
143   struct statvfs buf;
144
145   if (0 != statvfs (part, &buf))
146     {
147       GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "statfs", part);
148       return -1;
149     }
150   return buf.f_bavail;
151 #elif MINGW
152   DWORD dwDummy;
153   DWORD dwBlocks;
154   char szDrive[4];
155
156   memcpy (szDrive, part, 3);
157   szDrive[3] = 0;
158   if (!GetDiskFreeSpace (szDrive, &dwDummy, &dwDummy, &dwBlocks, &dwDummy))
159     {
160       GNUNET_GE_LOG (GNUNET_ERROR_TYPE_WARNING,
161                      _("`%s' failed for drive `%s': %u\n"),
162                      "GetDiskFreeSpace", szDrive, GetLastError ());
163
164       return -1;
165     }
166   return dwBlocks;
167 #else
168   struct statfs s;
169   if (0 != statfs (part, &s))
170     {
171       GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "statfs", part);
172       return -1;
173     }
174   return s.f_bavail;
175 #endif
176 }
177
178 /**
179  * Test if fil is a directory.
180  *
181  * @return GNUNET_YES if yes, GNUNET_NO if not, GNUNET_SYSERR if it
182  *   does not exist
183  */
184 int
185 GNUNET_DISK_directory_test (const char *fil)
186 {
187   struct stat filestat;
188   int ret;
189
190   ret = STAT (fil, &filestat);
191   if (ret != 0)
192     {
193       if (errno != ENOENT)
194         {
195           GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "stat", fil);
196           return GNUNET_SYSERR;
197         }
198       return GNUNET_NO;
199     }
200   if (!S_ISDIR (filestat.st_mode))
201     return GNUNET_NO;
202   if (ACCESS (fil, R_OK | X_OK) < 0)
203     {
204       GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "access", fil);
205       return GNUNET_SYSERR;
206     }
207   return GNUNET_YES;
208 }
209
210 /**
211  * Check that fil corresponds to a filename
212  * (of a file that exists and that is not a directory).
213  * @returns GNUNET_YES if yes, GNUNET_NO if not a file, GNUNET_SYSERR if something
214  * else (will print an error message in that case, too).
215  */
216 int
217 GNUNET_DISK_file_test (const char *fil)
218 {
219   struct stat filestat;
220   int ret;
221   char *rdir;
222
223   rdir = GNUNET_STRINGS_filename_expand (fil);
224   if (rdir == NULL)
225     return GNUNET_SYSERR;
226
227   ret = STAT (rdir, &filestat);
228   if (ret != 0)
229     {
230       if (errno != ENOENT)
231         {
232           GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "stat", rdir);
233           GNUNET_free (rdir);
234           return GNUNET_SYSERR;
235         }
236       GNUNET_free (rdir);
237       return GNUNET_NO;
238     }
239   if (!S_ISREG (filestat.st_mode))
240     {
241       GNUNET_free (rdir);
242       return GNUNET_NO;
243     }
244   if (ACCESS (rdir, R_OK) < 0)
245     {
246       GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "access", rdir);
247       GNUNET_free (rdir);
248       return GNUNET_SYSERR;
249     }
250   GNUNET_free (rdir);
251   return GNUNET_YES;
252 }
253
254 /**
255  * Implementation of "mkdir -p"
256  * @param dir the directory to create
257  * @returns GNUNET_OK on success, GNUNET_SYSERR on failure
258  */
259 int
260 GNUNET_DISK_directory_create (const char *dir)
261 {
262   char *rdir;
263   int len;
264   int pos;
265   int ret = GNUNET_OK;
266
267   rdir = GNUNET_STRINGS_filename_expand (dir);
268   if (rdir == NULL)
269     return GNUNET_SYSERR;
270
271   len = strlen (rdir);
272 #ifndef MINGW
273   pos = 1;                      /* skip heading '/' */
274 #else
275   /* Local or Network path? */
276   if (strncmp (rdir, "\\\\", 2) == 0)
277     {
278       pos = 2;
279       while (rdir[pos])
280         {
281           if (rdir[pos] == '\\')
282             {
283               pos++;
284               break;
285             }
286           pos++;
287         }
288     }
289   else
290     {
291       pos = 3;                  /* strlen("C:\\") */
292     }
293 #endif
294   while (pos <= len)
295     {
296       if ((rdir[pos] == DIR_SEPARATOR) || (pos == len))
297         {
298           rdir[pos] = '\0';
299           ret = GNUNET_DISK_directory_test (rdir);
300           if (ret == GNUNET_SYSERR)
301             {
302               GNUNET_free (rdir);
303               return GNUNET_SYSERR;
304             }
305           if (ret == GNUNET_NO)
306             {
307 #ifndef MINGW
308               ret = mkdir (rdir, S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH);  /* 755 */
309 #else
310               ret = mkdir (rdir);
311 #endif
312               if ((ret != 0) && (errno != EEXIST))
313                 {
314                   GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR, "mkdir",
315                                             rdir);
316                   GNUNET_free (rdir);
317                   return GNUNET_SYSERR;
318                 }
319             }
320           rdir[pos] = DIR_SEPARATOR;
321         }
322       pos++;
323     }
324   GNUNET_free (rdir);
325   return GNUNET_OK;
326 }
327
328
329 /**
330  * Create the directory structure for storing
331  * a file.
332  *
333  * @param filename name of a file in the directory
334  * @returns GNUNET_OK on success,
335  *          GNUNET_SYSERR on failure,
336  *          GNUNET_NO if the directory
337  *          exists but is not writeable for us
338  */
339 int
340 GNUNET_DISK_directory_create_for_file (const char *dir)
341 {
342   char *rdir;
343   int len;
344   int ret;
345
346   rdir = GNUNET_STRINGS_filename_expand (dir);
347   if (rdir == NULL)
348     return GNUNET_SYSERR;
349   len = strlen (rdir);
350   while ((len > 0) && (rdir[len] != DIR_SEPARATOR))
351     len--;
352   rdir[len] = '\0';
353   ret = GNUNET_DISK_directory_create (rdir);
354   if ((ret == GNUNET_OK) && (0 != ACCESS (rdir, W_OK)))
355     ret = GNUNET_NO;
356   GNUNET_free (rdir);
357   return ret;
358 }
359
360 /**
361  * Read the contents of a binary file into a buffer.
362  * @param fileName the name of the file, not freed,
363  *        must already be expanded!
364  * @param len the maximum number of bytes to read
365  * @param result the buffer to write the result to
366  * @return the number of bytes read on success, -1 on failure
367  */
368 int
369 GNUNET_DISK_file_read (const char *fileName, int len, void *result)
370 {
371   /* open file, must exist, open read only */
372   int handle;
373   int size;
374
375   GNUNET_assert (fileName != NULL);
376   GNUNET_assert (len > 0);
377   if (len == 0)
378     return 0;
379   GNUNET_assert (result != NULL);
380   handle = GNUNET_DISK_file_open (fileName, O_RDONLY, S_IRUSR);
381   if (handle < 0)
382     return -1;
383   size = READ (handle, result, len);
384   GNUNET_DISK_file_close (fileName, handle);
385   return size;
386 }
387
388
389 /**
390  * Convert string to value ('755' for chmod-call)
391  */
392 static int
393 atoo (const char *s)
394 {
395   int n = 0;
396
397   while (('0' <= *s) && (*s < '8'))
398     {
399       n <<= 3;
400       n += *s++ - '0';
401     }
402   return n;
403 }
404
405 /**
406  * Write a buffer to a file.
407  * @param fileName the name of the file, NOT freed!
408  * @param buffer the data to write
409  * @param n number of bytes to write
410  * @param mode permissions to set on the file
411  * @return GNUNET_OK on success, GNUNET_SYSERR on error
412  */
413 int
414 GNUNET_DISK_file_write (const char *fileName,
415                         const void *buffer, unsigned int n, const char *mode)
416 {
417   int handle;
418   char *fn;
419
420   /* open file, open with 600, create if not
421      present, otherwise overwrite */
422   GNUNET_assert (fileName != NULL);
423   fn = GNUNET_STRINGS_filename_expand (fileName);
424   handle = GNUNET_DISK_file_open (fn, O_CREAT | O_WRONLY, S_IRUSR | S_IWUSR);
425   if (handle == -1)
426     {
427       GNUNET_free (fn);
428       return GNUNET_SYSERR;
429     }
430   GNUNET_assert ((n == 0) || (buffer != NULL));
431   /* write the buffer take length from the beginning */
432   if (n != WRITE (handle, buffer, n))
433     {
434       GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "write", fn);
435       GNUNET_DISK_file_close (fn, handle);
436       GNUNET_free (fn);
437       return GNUNET_SYSERR;
438     }
439   GNUNET_DISK_file_close (fn, handle);
440   if (0 != CHMOD (fn, atoo (mode)))
441     {
442       GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "chmod", fn);
443     }
444   GNUNET_free (fn);
445   return GNUNET_OK;
446 }
447
448 /**
449  * Scan a directory for files. The name of the directory
450  * must be expanded first (!).
451  * @param dirName the name of the directory
452  * @param callback the method to call for each file,
453  *        can be NULL, in that case, we only count
454  * @param data argument to pass to callback
455  * @return the number of files found, GNUNET_SYSERR on error or
456  *         ieration aborted by callback returning GNUNET_SYSERR
457  */
458 int
459 GNUNET_DISK_directory_scan (const char *dirName,
460                             GNUNET_FileNameCallback callback, void *data)
461 {
462   DIR *dinfo;
463   struct dirent *finfo;
464   struct stat istat;
465   int count = 0;
466   char *name;
467   char *dname;
468   unsigned int name_len;
469   unsigned int n_size;
470
471   GNUNET_assert (dirName != NULL);
472   dname = GNUNET_STRINGS_filename_expand (dirName);
473   while ((strlen (dname) > 0) && (dname[strlen (dname) - 1] == DIR_SEPARATOR))
474     dname[strlen (dname) - 1] = '\0';
475   if (0 != STAT (dname, &istat))
476     {
477       GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "stat", dname);
478       GNUNET_free (dname);
479       return GNUNET_SYSERR;
480     }
481   if (!S_ISDIR (istat.st_mode))
482     {
483       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
484                   _("Expected `%s' to be a directory!\n"), dirName);
485       GNUNET_free (dname);
486       return GNUNET_SYSERR;
487     }
488   errno = 0;
489   dinfo = OPENDIR (dname);
490   if ((errno == EACCES) || (dinfo == NULL))
491     {
492       GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "opendir", dname);
493       if (dinfo != NULL)
494         closedir (dinfo);
495       GNUNET_free (dname);
496       return GNUNET_SYSERR;
497     }
498   name_len = 256;
499   n_size = strlen (dname) + name_len + 2;
500   name = GNUNET_malloc (n_size);
501   while ((finfo = readdir (dinfo)) != NULL)
502     {
503       if ((0 == strcmp (finfo->d_name, ".")) ||
504           (0 == strcmp (finfo->d_name, "..")))
505         continue;
506       if (callback != NULL)
507         {
508           if (name_len < strlen (finfo->d_name))
509             {
510               GNUNET_free (name);
511               name_len = strlen (finfo->d_name);
512               n_size = strlen (dname) + name_len + 2;
513               name = GNUNET_malloc (n_size);
514             }
515           /* dname can end in "/" only if dname == "/";
516              if dname does not end in "/", we need to add
517              a "/" (otherwise, we must not!) */
518           GNUNET_snprintf (name,
519                            n_size,
520                            "%s%s%s",
521                            dname,
522                            (strcmp (dname, DIR_SEPARATOR_STR) ==
523                             0) ? "" : DIR_SEPARATOR_STR, finfo->d_name);
524           if (GNUNET_OK != callback (data, name))
525             {
526               closedir (dinfo);
527               GNUNET_free (name);
528               GNUNET_free (dname);
529               return GNUNET_SYSERR;
530             }
531         }
532       count++;
533     }
534   closedir (dinfo);
535   GNUNET_free (name);
536   GNUNET_free (dname);
537   return count;
538 }
539
540
541 /**
542  * Opaque handle used for iterating over a directory.
543  */
544 struct GNUNET_DISK_DirectoryIterator
545 {
546   /**
547    * Our scheduler.
548    */
549   struct GNUNET_SCHEDULER_Handle *sched;
550
551   /**
552    * Function to call on directory entries.
553    */
554   GNUNET_DISK_DirectoryIteratorCallback callback;
555
556   /**
557    * Closure for callback.
558    */
559   void *callback_cls;
560
561   /**
562    * Reference to directory.
563    */
564   DIR *directory;
565
566   /**
567    * Directory name.
568    */
569   char *dirname;
570
571   /**
572    * Next filename to process.
573    */
574   char *next_name;
575
576   /**
577    * Our priority.
578    */
579   enum GNUNET_SCHEDULER_Priority priority;
580
581 };
582
583
584 /**
585  * Task used by the directory iterator.
586  */
587 static void
588 directory_iterator_task (void *cls,
589                          const struct GNUNET_SCHEDULER_TaskContext *tc)
590 {
591   struct GNUNET_DISK_DirectoryIterator *iter = cls;
592   char *name;
593
594   name = iter->next_name;
595   GNUNET_assert (name != NULL);
596   iter->next_name = NULL;
597   iter->callback (iter->callback_cls, iter, name, iter->dirname);
598   GNUNET_free (name);
599 }
600
601
602 /**
603  * This function must be called during the DiskIteratorCallback
604  * (exactly once) to schedule the task to process the next
605  * filename in the directory (if there is one).
606  *
607  * @param iter opaque handle for the iterator
608  * @param can set to GNUNET_YES to terminate the iteration early
609  * @return GNUNET_YES if iteration will continue,
610  *         GNUNET_NO if this was the last entry (and iteration is complete),
611  *         GNUNET_SYSERR if abort was YES
612  */
613 int
614 GNUNET_DISK_directory_iterator_next (struct GNUNET_DISK_DirectoryIterator
615                                      *iter, int can)
616 {
617   struct dirent *finfo;
618
619   GNUNET_assert (iter->next_name == NULL);
620   if (can == GNUNET_YES)
621     {
622       closedir (iter->directory);
623       GNUNET_free (iter->dirname);
624       GNUNET_free (iter);
625       return GNUNET_SYSERR;
626     }
627   while (NULL != (finfo = readdir (iter->directory)))
628     {
629       if ((0 == strcmp (finfo->d_name, ".")) ||
630           (0 == strcmp (finfo->d_name, "..")))
631         continue;
632       GNUNET_asprintf (&iter->next_name,
633                        "%s%s%s",
634                        iter->dirname, DIR_SEPARATOR_STR, finfo->d_name);
635       break;
636     }
637   if (finfo == NULL)
638     {
639       GNUNET_DISK_directory_iterator_next (iter, GNUNET_YES);
640       return GNUNET_NO;
641     }
642   GNUNET_SCHEDULER_add_after (iter->sched,
643                               GNUNET_YES,
644                               iter->priority,
645                               GNUNET_SCHEDULER_NO_PREREQUISITE_TASK,
646                               &directory_iterator_task, iter);
647   return GNUNET_YES;
648 }
649
650
651 /**
652  * Scan a directory for files using the scheduler to run a task for
653  * each entry.  The name of the directory must be expanded first (!).
654  * If a scheduler does not need to be used, GNUNET_DISK_directory_scan
655  * may provide a simpler API.
656  *
657  * @param sched scheduler to use
658  * @param prio priority to use
659  * @param dirName the name of the directory
660  * @param callback the method to call for each file
661  * @param callback_cls closure for callback
662  */
663 void
664 GNUNET_DISK_directory_iterator_start (struct GNUNET_SCHEDULER_Handle *sched,
665                                       enum GNUNET_SCHEDULER_Priority prio,
666                                       const char *dirName,
667                                       GNUNET_DISK_DirectoryIteratorCallback
668                                       callback, void *callback_cls)
669 {
670   struct GNUNET_DISK_DirectoryIterator *di;
671
672   di = GNUNET_malloc (sizeof (struct GNUNET_DISK_DirectoryIterator));
673   di->sched = sched;
674   di->callback = callback;
675   di->callback_cls = callback_cls;
676   di->directory = OPENDIR (dirName);
677   di->dirname = GNUNET_strdup (dirName);
678   di->priority = prio;
679   GNUNET_DISK_directory_iterator_next (di, GNUNET_NO);
680 }
681
682
683 static int
684 remove_helper (void *unused, const char *fn)
685 {
686   GNUNET_DISK_directory_remove (fn);
687   return GNUNET_OK;
688 }
689
690 /**
691  * Remove all files in a directory (rm -rf). Call with
692  * caution.
693  *
694  *
695  * @param fileName the file to remove
696  * @return GNUNET_OK on success, GNUNET_SYSERR on error
697  */
698 int
699 GNUNET_DISK_directory_remove (const char *fileName)
700 {
701   struct stat istat;
702
703   if (0 != LSTAT (fileName, &istat))
704     return GNUNET_NO;           /* file may not exist... */
705   if (UNLINK (fileName) == 0)
706     return GNUNET_OK;
707   if ((errno != EISDIR) &&
708       /* EISDIR is not sufficient in all cases, e.g.
709          sticky /tmp directory may result in EPERM on BSD.
710          So we also explicitly check "isDirectory" */
711       (GNUNET_YES != GNUNET_DISK_directory_test (fileName)))
712     {
713       GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "rmdir", fileName);
714       return GNUNET_SYSERR;
715     }
716   if (GNUNET_SYSERR ==
717       GNUNET_DISK_directory_scan (fileName, remove_helper, NULL))
718     return GNUNET_SYSERR;
719   if (0 != RMDIR (fileName))
720     {
721       GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "rmdir", fileName);
722       return GNUNET_SYSERR;
723     }
724   return GNUNET_OK;
725 }
726
727 void
728 GNUNET_DISK_file_close (const char *filename, int fd)
729 {
730   if (0 != CLOSE (fd))
731     GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "close", filename);
732 }
733
734 int
735 GNUNET_DISK_file_open (const char *filename, int oflag, ...)
736 {
737   char *fn;
738   int mode;
739   int ret;
740 #ifdef MINGW
741   char szFile[_MAX_PATH + 1];
742   long lRet;
743
744   if ((lRet = plibc_conv_to_win_path (filename, szFile)) != ERROR_SUCCESS)
745     {
746       errno = ENOENT;
747       SetLastError (lRet);
748       GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING,
749                                 "plibc_conv_to_win_path", filename);
750       return -1;
751     }
752   fn = GNUNET_strdup (szFile);
753 #else
754   fn = GNUNET_STRINGS_filename_expand (filename);
755 #endif
756   if (oflag & O_CREAT)
757     {
758       va_list arg;
759       va_start (arg, oflag);
760       mode = va_arg (arg, int);
761       va_end (arg);
762     }
763   else
764     {
765       mode = 0;
766     }
767 #ifdef MINGW
768   /* set binary mode */
769   oflag |= O_BINARY;
770 #endif
771   ret = OPEN (fn, oflag, mode);
772   if (ret == -1)
773     GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "open", fn);
774   GNUNET_free (fn);
775   return ret;
776 }
777
778 #define COPY_BLK_SIZE 65536
779
780 /**
781  * Copy a file.
782  * @return GNUNET_OK on success, GNUNET_SYSERR on error
783  */
784 int
785 GNUNET_DISK_file_copy (const char *src, const char *dst)
786 {
787   char *buf;
788   unsigned long long pos;
789   unsigned long long size;
790   unsigned long long len;
791   int in;
792   int out;
793
794   if (GNUNET_OK != GNUNET_DISK_file_size (src, &size, GNUNET_YES))
795     return GNUNET_SYSERR;
796   pos = 0;
797   in = GNUNET_DISK_file_open (src, O_RDONLY | O_LARGEFILE);
798   if (in == -1)
799     return GNUNET_SYSERR;
800   out = GNUNET_DISK_file_open (dst,
801                                O_LARGEFILE | O_WRONLY | O_CREAT | O_EXCL,
802                                S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP);
803   if (out == -1)
804     {
805       GNUNET_DISK_file_close (src, in);
806       return GNUNET_SYSERR;
807     }
808   buf = GNUNET_malloc (COPY_BLK_SIZE);
809   while (pos < size)
810     {
811       len = COPY_BLK_SIZE;
812       if (len > size - pos)
813         len = size - pos;
814       if (len != READ (in, buf, len))
815         goto FAIL;
816       if (len != WRITE (out, buf, len))
817         goto FAIL;
818       pos += len;
819     }
820   GNUNET_free (buf);
821   GNUNET_DISK_file_close (src, in);
822   GNUNET_DISK_file_close (dst, out);
823   return GNUNET_OK;
824 FAIL:
825   GNUNET_free (buf);
826   GNUNET_DISK_file_close (src, in);
827   GNUNET_DISK_file_close (dst, out);
828   return GNUNET_SYSERR;
829 }
830
831
832 /**
833  * @brief Removes special characters as ':' from a filename.
834  * @param fn the filename to canonicalize
835  */
836 void
837 GNUNET_DISK_filename_canonicalize (char *fn)
838 {
839   char *idx;
840   char c;
841
842   idx = fn;
843   while (*idx)
844     {
845       c = *idx;
846
847       if (c == '/' || c == '\\' || c == ':' || c == '*' || c == '?' ||
848           c == '"' || c == '<' || c == '>' || c == '|')
849         {
850           *idx = '_';
851         }
852
853       idx++;
854     }
855 }
856
857
858
859 /**
860  * @brief Change owner of a file
861  */
862 int
863 GNUNET_DISK_file_change_owner (const char *filename, const char *user)
864 {
865 #ifndef MINGW
866   struct passwd *pws;
867
868   pws = getpwnam (user);
869   if (pws == NULL)
870     {
871       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
872                   _("Cannot obtain information about user `%s': %s\n"),
873                   user, STRERROR (errno));
874       return GNUNET_SYSERR;
875     }
876   if (0 != chown (filename, pws->pw_uid, pws->pw_gid))
877     GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "chown", filename);
878 #endif
879   return GNUNET_OK;
880 }
881
882
883 /**
884  * Construct full path to a file inside of the private
885  * directory used by GNUnet.  Also creates the corresponding
886  * directory.  If the resulting name is supposed to be
887  * a directory, end the last argument in '/' (or pass
888  * DIR_SEPARATOR_STR as the last argument before NULL).
889  *
890  * @param serviceName name of the service
891  * @param varargs is NULL-terminated list of
892  *                path components to append to the
893  *                private directory name.
894  * @return the constructed filename
895  */
896 char *
897 GNUNET_DISK_get_home_filename (struct GNUNET_CONFIGURATION_Handle *cfg,
898                                const char *serviceName, ...)
899 {
900   const char *c;
901   char *pfx;
902   char *ret;
903   va_list ap;
904   unsigned int needed;
905
906   if (GNUNET_OK !=
907       GNUNET_CONFIGURATION_get_value_filename (cfg,
908                                                serviceName, "HOME", &pfx))
909     return NULL;
910   if (pfx == NULL)
911     {
912       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
913                   _("No `%s' specified for service `%s' in configuration.\n"),
914                   "HOME", serviceName);
915       return NULL;
916     }
917   needed = strlen (pfx) + 2;
918   if ((pfx[strlen (pfx) - 1] != '/') && (pfx[strlen (pfx) - 1] != '\\'))
919     needed++;
920   va_start (ap, serviceName);
921   while (1)
922     {
923       c = va_arg (ap, const char *);
924       if (c == NULL)
925         break;
926       needed += strlen (c);
927       if ((c[strlen (c) - 1] != '/') && (c[strlen (c) - 1] != '\\'))
928         needed++;
929     }
930   va_end (ap);
931   ret = GNUNET_malloc (needed);
932   strcpy (ret, pfx);
933   GNUNET_free (pfx);
934   va_start (ap, serviceName);
935   while (1)
936     {
937       c = va_arg (ap, const char *);
938       if (c == NULL)
939         break;
940       if ((c[strlen (c) - 1] != '/') && (c[strlen (c) - 1] != '\\'))
941         strcat (ret, DIR_SEPARATOR_STR);
942       strcat (ret, c);
943     }
944   va_end (ap);
945   if ((ret[strlen (ret) - 1] != '/') && (ret[strlen (ret) - 1] != '\\'))
946     GNUNET_DISK_directory_create_for_file (ret);
947   else
948     GNUNET_DISK_directory_create (ret);
949   return ret;
950 }
951
952
953
954 /* end of disk.c */