global reindent, now with uncrustify hook enabled
[oweals/gnunet.git] / src / util / disk.c
1 /*
2      This file is part of GNUnet.
3      Copyright (C) 2001--2013, 2016, 2018 GNUnet e.V.
4
5      GNUnet is free software: you can redistribute it and/or modify it
6      under the terms of the GNU Affero General Public License as published
7      by the Free Software Foundation, either version 3 of the License,
8      or (at your 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      Affero General Public License for more details.
14
15      You should have received a copy of the GNU Affero General Public License
16      along with this program.  If not, see <http://www.gnu.org/licenses/>.
17
18      SPDX-License-Identifier: AGPL3.0-or-later
19  */
20 /**
21  * @file util/disk.c
22  * @brief disk IO convenience methods
23  * @author Christian Grothoff
24  * @author Nils Durner
25  */
26 #include "platform.h"
27 #include "disk.h"
28 #include "gnunet_strings_lib.h"
29 #include "gnunet_disk_lib.h"
30
31 #define LOG(kind, ...) GNUNET_log_from (kind, "util-disk", __VA_ARGS__)
32
33 #define LOG_STRERROR(kind, syscall) \
34   GNUNET_log_from_strerror (kind, "util-disk", syscall)
35
36 #define LOG_STRERROR_FILE(kind, syscall, filename) \
37   GNUNET_log_from_strerror_file (kind, "util-disk", syscall, filename)
38
39 /**
40  * Block size for IO for copying files.
41  */
42 #define COPY_BLK_SIZE 65536
43
44 #include <sys/types.h>
45 #if HAVE_SYS_VFS_H
46 #include <sys/vfs.h>
47 #endif
48 #if HAVE_SYS_PARAM_H
49 #include <sys/param.h>
50 #endif
51 #if HAVE_SYS_MOUNT_H
52 #include <sys/mount.h>
53 #endif
54 #if HAVE_SYS_STATVFS_H
55 #include <sys/statvfs.h>
56 #endif
57
58 #ifndef S_ISLNK
59 #define _IFMT 0170000 /* type of file */
60 #define _IFLNK 0120000 /* symbolic link */
61 #define S_ISLNK(m) (((m) & _IFMT) == _IFLNK)
62 #endif
63
64
65 /**
66  * Handle used to manage a pipe.
67  */
68 struct GNUNET_DISK_PipeHandle
69 {
70   /**
71    * File descriptors for the pipe.
72    * One or both of them could be NULL.
73    */
74   struct GNUNET_DISK_FileHandle *fd[2];
75 };
76
77
78 /**
79  * Closure for the recursion to determine the file size
80  * of a directory.
81  */
82 struct GetFileSizeData
83 {
84   /**
85    * Set to the total file size.
86    */
87   uint64_t total;
88
89   /**
90    * GNUNET_YES if symbolic links should be included.
91    */
92   int include_sym_links;
93
94   /**
95    * GNUNET_YES if mode is file-only (return total == -1 for directories).
96    */
97   int single_file_mode;
98 };
99
100
101 /**
102  * Translate GNUnet-internal permission bitmap to UNIX file
103  * access permission bitmap.
104  *
105  * @param perm file permissions, GNUnet style
106  * @return file permissions, UNIX style
107  */
108 static int
109 translate_unix_perms (enum GNUNET_DISK_AccessPermissions perm)
110 {
111   int mode;
112
113   mode = 0;
114   if (perm & GNUNET_DISK_PERM_USER_READ)
115     mode |= S_IRUSR;
116   if (perm & GNUNET_DISK_PERM_USER_WRITE)
117     mode |= S_IWUSR;
118   if (perm & GNUNET_DISK_PERM_USER_EXEC)
119     mode |= S_IXUSR;
120   if (perm & GNUNET_DISK_PERM_GROUP_READ)
121     mode |= S_IRGRP;
122   if (perm & GNUNET_DISK_PERM_GROUP_WRITE)
123     mode |= S_IWGRP;
124   if (perm & GNUNET_DISK_PERM_GROUP_EXEC)
125     mode |= S_IXGRP;
126   if (perm & GNUNET_DISK_PERM_OTHER_READ)
127     mode |= S_IROTH;
128   if (perm & GNUNET_DISK_PERM_OTHER_WRITE)
129     mode |= S_IWOTH;
130   if (perm & GNUNET_DISK_PERM_OTHER_EXEC)
131     mode |= S_IXOTH;
132
133   return mode;
134 }
135
136
137 /**
138  * Iterate over all files in the given directory and
139  * accumulate their size.
140  *
141  * @param cls closure of type `struct GetFileSizeData`
142  * @param fn current filename we are looking at
143  * @return #GNUNET_SYSERR on serious errors, otherwise #GNUNET_OK
144  */
145 static int
146 getSizeRec (void *cls, const char *fn)
147 {
148   struct GetFileSizeData *gfsd = cls;
149
150 #if defined(HAVE_STAT64) && \
151   ! (defined(_FILE_OFFSET_BITS) && _FILE_OFFSET_BITS == 64)
152   struct stat64 buf;
153
154   if (0 != stat64 (fn, &buf))
155   {
156     LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_DEBUG, "stat64", fn);
157     return GNUNET_SYSERR;
158   }
159 #else
160   struct stat buf;
161
162   if (0 != stat (fn, &buf))
163   {
164     LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_DEBUG, "stat", fn);
165     return GNUNET_SYSERR;
166   }
167 #endif
168   if ((S_ISDIR (buf.st_mode)) && (gfsd->single_file_mode == GNUNET_YES))
169   {
170     errno = EISDIR;
171     return GNUNET_SYSERR;
172   }
173   if ((! S_ISLNK (buf.st_mode)) || (gfsd->include_sym_links == GNUNET_YES))
174     gfsd->total += buf.st_size;
175   if ((S_ISDIR (buf.st_mode)) && (0 == access (fn, X_OK)) &&
176       ((! S_ISLNK (buf.st_mode)) || (gfsd->include_sym_links == GNUNET_YES)))
177   {
178     if (GNUNET_SYSERR == GNUNET_DISK_directory_scan (fn, &getSizeRec, gfsd))
179       return GNUNET_SYSERR;
180   }
181   return GNUNET_OK;
182 }
183
184
185 /**
186  * Checks whether a handle is invalid
187  *
188  * @param h handle to check
189  * @return #GNUNET_YES if invalid, #GNUNET_NO if valid
190  */
191 int
192 GNUNET_DISK_handle_invalid (const struct GNUNET_DISK_FileHandle *h)
193 {
194   return ((! h) || (h->fd == -1)) ? GNUNET_YES : GNUNET_NO;
195 }
196
197 /**
198  * Get the size of an open file.
199  *
200  * @param fh open file handle
201  * @param size where to write size of the file
202  * @return #GNUNET_OK on success, #GNUNET_SYSERR on error
203  */
204 int
205 GNUNET_DISK_file_handle_size (struct GNUNET_DISK_FileHandle *fh, off_t *size)
206 {
207   struct stat sbuf;
208
209   if (0 != fstat (fh->fd, &sbuf))
210     return GNUNET_SYSERR;
211   *size = sbuf.st_size;
212   return GNUNET_OK;
213 }
214
215
216 /**
217  * Move the read/write pointer in a file
218  *
219  * @param h handle of an open file
220  * @param offset position to move to
221  * @param whence specification to which position the offset parameter relates to
222  * @return the new position on success, #GNUNET_SYSERR otherwise
223  */
224 off_t
225 GNUNET_DISK_file_seek (const struct GNUNET_DISK_FileHandle *h,
226                        off_t offset,
227                        enum GNUNET_DISK_Seek whence)
228 {
229   if (h == NULL)
230   {
231     errno = EINVAL;
232     return GNUNET_SYSERR;
233   }
234
235   static int t[] = { SEEK_SET, SEEK_CUR, SEEK_END };
236
237   return lseek (h->fd, offset, t[whence]);
238 }
239
240
241 /**
242  * Get the size of the file (or directory) of the given file (in
243  * bytes).
244  *
245  * @param filename name of the file or directory
246  * @param size set to the size of the file (or,
247  *             in the case of directories, the sum
248  *             of all sizes of files in the directory)
249  * @param include_symbolic_links should symbolic links be
250  *        included?
251  * @param single_file_mode #GNUNET_YES to only get size of one file
252  *        and return #GNUNET_SYSERR for directories.
253  * @return #GNUNET_SYSERR on error, #GNUNET_OK on success
254  */
255 int
256 GNUNET_DISK_file_size (const char *filename,
257                        uint64_t *size,
258                        int include_symbolic_links,
259                        int single_file_mode)
260 {
261   struct GetFileSizeData gfsd;
262   int ret;
263
264   GNUNET_assert (size != NULL);
265   gfsd.total = 0;
266   gfsd.include_sym_links = include_symbolic_links;
267   gfsd.single_file_mode = single_file_mode;
268   ret = getSizeRec (&gfsd, filename);
269   *size = gfsd.total;
270   return ret;
271 }
272
273
274 /**
275  * Obtain some unique identifiers for the given file
276  * that can be used to identify it in the local system.
277  * This function is used between GNUnet processes to
278  * quickly check if two files with the same absolute path
279  * are actually identical.  The two processes represent
280  * the same peer but may communicate over the network
281  * (and the file may be on an NFS volume).  This function
282  * may not be supported on all operating systems.
283  *
284  * @param filename name of the file
285  * @param dev set to the device ID
286  * @param ino set to the inode ID
287  * @return #GNUNET_OK on success
288  */
289 int
290 GNUNET_DISK_file_get_identifiers (const char *filename,
291                                   uint64_t *dev,
292                                   uint64_t *ino)
293 {
294 #if HAVE_STAT
295   {
296     struct stat sbuf;
297
298     if (0 != stat (filename, &sbuf))
299     {
300       return GNUNET_SYSERR;
301     }
302     *ino = (uint64_t) sbuf.st_ino;
303   }
304 #else
305   *ino = 0;
306 #endif
307 #if HAVE_STATVFS
308   {
309     struct statvfs fbuf;
310
311     if (0 != statvfs (filename, &fbuf))
312     {
313       return GNUNET_SYSERR;
314     }
315     *dev = (uint64_t) fbuf.f_fsid;
316   }
317 #elif HAVE_STATFS
318   {
319     struct statfs fbuf;
320
321     if (0 != statfs (filename, &fbuf))
322     {
323       return GNUNET_SYSERR;
324     }
325     *dev =
326       ((uint64_t) fbuf.f_fsid.val[0]) << 32 || ((uint64_t) fbuf.f_fsid.val[1]);
327   }
328 #else
329   *dev = 0;
330 #endif
331   return GNUNET_OK;
332 }
333
334
335 /**
336  * Create the name for a temporary file or directory from a template.
337  *
338  * @param t template (without XXXXX or "/tmp/")
339  * @return name ready for passing to 'mktemp' or 'mkdtemp', NULL on error
340  */
341 static char *
342 mktemp_name (const char *t)
343 {
344   const char *tmpdir;
345   char *tmpl;
346   char *fn;
347
348   if ((t[0] != '/') && (t[0] != '\\'))
349   {
350     /* FIXME: This uses system codepage on W32, not UTF-8 */
351     tmpdir = getenv ("TMPDIR");
352     if (NULL == tmpdir)
353       tmpdir = getenv ("TMP");
354     if (NULL == tmpdir)
355       tmpdir = getenv ("TEMP");
356     if (NULL == tmpdir)
357       tmpdir = "/tmp";
358     GNUNET_asprintf (&tmpl, "%s/%s%s", tmpdir, t, "XXXXXX");
359   }
360   else
361   {
362     GNUNET_asprintf (&tmpl, "%s%s", t, "XXXXXX");
363   }
364   fn = tmpl;
365   return fn;
366 }
367
368
369 /**
370  * Update POSIX permissions mask of a file on disk.  If both argumets
371  * are #GNUNET_NO, the file is made world-read-write-executable (777).
372  *
373  * @param fn name of the file to update
374  * @param require_uid_match #GNUNET_YES means 700
375  * @param require_gid_match #GNUNET_YES means 770 unless @a require_uid_match is set
376  */
377 void
378 GNUNET_DISK_fix_permissions (const char *fn,
379                              int require_uid_match,
380                              int require_gid_match)
381 {
382   mode_t mode;
383
384   if (GNUNET_YES == require_uid_match)
385     mode = S_IRUSR | S_IWUSR | S_IXUSR;
386   else if (GNUNET_YES == require_gid_match)
387     mode = S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IWGRP | S_IXGRP;
388   else
389     mode = S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IWGRP | S_IXGRP | S_IROTH
390            | S_IWOTH | S_IXOTH;
391   if (0 != chmod (fn, mode))
392     GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "chmod", fn);
393 }
394
395
396 /**
397  * Create an (empty) temporary directory on disk.  If the given name is not
398  * an absolute path, the current 'TMPDIR' will be prepended.  In any case,
399  * 6 random characters will be appended to the name to create a unique
400  * filename.
401  *
402  * @param t component to use for the name;
403  *        does NOT contain "XXXXXX" or "/tmp/".
404  * @return NULL on error, otherwise name of fresh
405  *         file on disk in directory for temporary files
406  */
407 char *
408 GNUNET_DISK_mkdtemp (const char *t)
409 {
410   char *fn;
411   mode_t omask;
412
413   omask = umask (S_IWGRP | S_IWOTH | S_IRGRP | S_IROTH);
414   fn = mktemp_name (t);
415   if (fn != mkdtemp (fn))
416   {
417     LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_ERROR, "mkdtemp", fn);
418     GNUNET_free (fn);
419     umask (omask);
420     return NULL;
421   }
422   umask (omask);
423   return fn;
424 }
425
426
427 /**
428  * Move a file out of the way (create a backup) by
429  * renaming it to "orig.NUM~" where NUM is the smallest
430  * number that is not used yet.
431  *
432  * @param fil name of the file to back up
433  */
434 void
435 GNUNET_DISK_file_backup (const char *fil)
436 {
437   size_t slen;
438   char *target;
439   unsigned int num;
440
441   slen = strlen (fil) + 20;
442   target = GNUNET_malloc (slen);
443   num = 0;
444   do
445   {
446     GNUNET_snprintf (target, slen, "%s.%u~", fil, num++);
447   }
448   while (0 == access (target, F_OK));
449   if (0 != rename (fil, target))
450     GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR, "rename", fil);
451   GNUNET_free (target);
452 }
453
454
455 /**
456  * Create an (empty) temporary file on disk.  If the given name is not
457  * an absolute path, the current 'TMPDIR' will be prepended.  In any case,
458  * 6 random characters will be appended to the name to create a unique
459  * filename.
460  *
461  * @param t component to use for the name;
462  *        does NOT contain "XXXXXX" or "/tmp/".
463  * @return NULL on error, otherwise name of fresh
464  *         file on disk in directory for temporary files
465  */
466 char *
467 GNUNET_DISK_mktemp (const char *t)
468 {
469   int fd;
470   char *fn;
471   mode_t omask;
472
473   omask = umask (S_IWGRP | S_IWOTH | S_IRGRP | S_IROTH);
474   fn = mktemp_name (t);
475   if (-1 == (fd = mkstemp (fn)))
476   {
477     LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_ERROR, "mkstemp", fn);
478     GNUNET_free (fn);
479     umask (omask);
480     return NULL;
481   }
482   umask (omask);
483   if (0 != close (fd))
484     LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "close", fn);
485   return fn;
486 }
487
488
489 /**
490  * Test if @a fil is a directory and listable. Optionally, also check if the
491  * directory is readable.  Will not print an error message if the directory does
492  * not exist.  Will log errors if #GNUNET_SYSERR is returned (i.e., a file exists
493  * with the same name).
494  *
495  * @param fil filename to test
496  * @param is_readable #GNUNET_YES to additionally check if @a fil is readable;
497  *          #GNUNET_NO to disable this check
498  * @return #GNUNET_YES if yes, #GNUNET_NO if not; #GNUNET_SYSERR if it
499  *           does not exist or stat'ed
500  */
501 int
502 GNUNET_DISK_directory_test (const char *fil, int is_readable)
503 {
504   struct stat filestat;
505   int ret;
506
507   ret = stat (fil, &filestat);
508   if (ret != 0)
509   {
510     if (errno != ENOENT)
511       LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "stat", fil);
512     return GNUNET_SYSERR;
513   }
514   if (! S_ISDIR (filestat.st_mode))
515   {
516     LOG (GNUNET_ERROR_TYPE_INFO,
517          "A file already exits with the same name %s\n",
518          fil);
519     return GNUNET_NO;
520   }
521   if (GNUNET_YES == is_readable)
522     ret = access (fil, R_OK | X_OK);
523   else
524     ret = access (fil, X_OK);
525   if (ret < 0)
526   {
527     LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "access", fil);
528     return GNUNET_NO;
529   }
530   return GNUNET_YES;
531 }
532
533
534 /**
535  * Check that fil corresponds to a filename
536  * (of a file that exists and that is not a directory).
537  *
538  * @param fil filename to check
539  * @return #GNUNET_YES if yes, #GNUNET_NO if not a file, #GNUNET_SYSERR if something
540  * else (will print an error message in that case, too).
541  */
542 int
543 GNUNET_DISK_file_test (const char *fil)
544 {
545   struct stat filestat;
546   int ret;
547   char *rdir;
548
549   rdir = GNUNET_STRINGS_filename_expand (fil);
550   if (rdir == NULL)
551     return GNUNET_SYSERR;
552
553   ret = stat (rdir, &filestat);
554   if (ret != 0)
555   {
556     if (errno != ENOENT)
557     {
558       LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "stat", rdir);
559       GNUNET_free (rdir);
560       return GNUNET_SYSERR;
561     }
562     GNUNET_free (rdir);
563     return GNUNET_NO;
564   }
565   if (! S_ISREG (filestat.st_mode))
566   {
567     GNUNET_free (rdir);
568     return GNUNET_NO;
569   }
570   if (access (rdir, F_OK) < 0)
571   {
572     LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "access", rdir);
573     GNUNET_free (rdir);
574     return GNUNET_SYSERR;
575   }
576   GNUNET_free (rdir);
577   return GNUNET_YES;
578 }
579
580
581 /**
582  * Implementation of "mkdir -p"
583  *
584  * @param dir the directory to create
585  * @return #GNUNET_OK on success, #GNUNET_SYSERR on failure
586  */
587 int
588 GNUNET_DISK_directory_create (const char *dir)
589 {
590   char *rdir;
591   unsigned int len;
592   unsigned int pos;
593   unsigned int pos2;
594   int ret = GNUNET_OK;
595
596   rdir = GNUNET_STRINGS_filename_expand (dir);
597   if (rdir == NULL)
598   {
599     GNUNET_break (0);
600     return GNUNET_SYSERR;
601   }
602
603   len = strlen (rdir);
604
605   pos = 1; /* skip heading '/' */
606
607   /* Check which low level directories already exist */
608   pos2 = len;
609   rdir[len] = DIR_SEPARATOR;
610   while (pos <= pos2)
611   {
612     if (DIR_SEPARATOR == rdir[pos2])
613     {
614       rdir[pos2] = '\0';
615       ret = GNUNET_DISK_directory_test (rdir, GNUNET_NO);
616       if (GNUNET_NO == ret)
617       {
618         GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
619                     "Creating directory `%s' failed",
620                     rdir);
621         GNUNET_free (rdir);
622         return GNUNET_SYSERR;
623       }
624       rdir[pos2] = DIR_SEPARATOR;
625       if (GNUNET_YES == ret)
626       {
627         pos2++;
628         break;
629       }
630     }
631     pos2--;
632   }
633   rdir[len] = '\0';
634   if (pos < pos2)
635     pos = pos2;
636   /* Start creating directories */
637   while (pos <= len)
638   {
639     if ((rdir[pos] == DIR_SEPARATOR) || (pos == len))
640     {
641       rdir[pos] = '\0';
642       ret = GNUNET_DISK_directory_test (rdir, GNUNET_NO);
643       if (GNUNET_NO == ret)
644       {
645         GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
646                     "Creating directory `%s' failed",
647                     rdir);
648         GNUNET_free (rdir);
649         return GNUNET_SYSERR;
650       }
651       if (GNUNET_SYSERR == ret)
652       {
653         ret = mkdir (rdir,
654                      S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IXGRP | S_IROTH
655                      | S_IXOTH);    /* 755 */
656
657         if ((ret != 0) && (errno != EEXIST))
658         {
659           LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_ERROR, "mkdir", rdir);
660           GNUNET_free (rdir);
661           return GNUNET_SYSERR;
662         }
663       }
664       rdir[pos] = DIR_SEPARATOR;
665     }
666     pos++;
667   }
668   GNUNET_free (rdir);
669   return GNUNET_OK;
670 }
671
672
673 /**
674  * Create the directory structure for storing a file.
675  *
676  * @param filename name of a file in the directory
677  * @returns #GNUNET_OK on success,
678  *          #GNUNET_SYSERR on failure,
679  *          #GNUNET_NO if the directory
680  *          exists but is not writeable for us
681  */
682 int
683 GNUNET_DISK_directory_create_for_file (const char *filename)
684 {
685   char *rdir;
686   size_t len;
687   int ret;
688   int eno;
689
690   rdir = GNUNET_STRINGS_filename_expand (filename);
691   if (NULL == rdir)
692   {
693     errno = EINVAL;
694     return GNUNET_SYSERR;
695   }
696   if (0 == access (rdir, W_OK))
697   {
698     GNUNET_free (rdir);
699     return GNUNET_OK;
700   }
701
702   len = strlen (rdir);
703   while ((len > 0) && (rdir[len] != DIR_SEPARATOR))
704     len--;
705   rdir[len] = '\0';
706   /* The empty path is invalid and in this case refers to / */
707   if (0 == len)
708   {
709     GNUNET_free (rdir);
710     rdir = GNUNET_strdup ("/");
711   }
712   ret = GNUNET_DISK_directory_create (rdir);
713   if ((GNUNET_OK == ret) && (0 != access (rdir, W_OK)))
714     ret = GNUNET_NO;
715   eno = errno;
716   GNUNET_free (rdir);
717   errno = eno;
718   return ret;
719 }
720
721
722 /**
723  * Read the contents of a binary file into a buffer.
724  *
725  * @param h handle to an open file
726  * @param result the buffer to write the result to
727  * @param len the maximum number of bytes to read
728  * @return the number of bytes read on success, #GNUNET_SYSERR on failure
729  */
730 ssize_t
731 GNUNET_DISK_file_read (const struct GNUNET_DISK_FileHandle *h,
732                        void *result,
733                        size_t len)
734 {
735   if (NULL == h)
736   {
737     errno = EINVAL;
738     return GNUNET_SYSERR;
739   }
740
741   return read (h->fd, result, len);
742 }
743
744
745 /**
746  * Read the contents of a binary file into a buffer.
747  * Guarantees not to block (returns GNUNET_SYSERR and sets errno to EAGAIN
748  * when no data can be read).
749  *
750  * @param h handle to an open file
751  * @param result the buffer to write the result to
752  * @param len the maximum number of bytes to read
753  * @return the number of bytes read on success, #GNUNET_SYSERR on failure
754  */
755 ssize_t
756 GNUNET_DISK_file_read_non_blocking (const struct GNUNET_DISK_FileHandle *h,
757                                     void *result,
758                                     size_t len)
759 {
760   if (NULL == h)
761   {
762     errno = EINVAL;
763     return GNUNET_SYSERR;
764   }
765
766   int flags;
767   ssize_t ret;
768
769   /* set to non-blocking, read, then set back */
770   flags = fcntl (h->fd, F_GETFL);
771   if (0 == (flags & O_NONBLOCK))
772     (void) fcntl (h->fd, F_SETFL, flags | O_NONBLOCK);
773   ret = read (h->fd, result, len);
774   if (0 == (flags & O_NONBLOCK))
775   {
776     int eno = errno;
777     (void) fcntl (h->fd, F_SETFL, flags);
778     errno = eno;
779   }
780   return ret;
781 }
782
783
784 /**
785  * Read the contents of a binary file into a buffer.
786  *
787  * @param fn file name
788  * @param result the buffer to write the result to
789  * @param len the maximum number of bytes to read
790  * @return number of bytes read, #GNUNET_SYSERR on failure
791  */
792 ssize_t
793 GNUNET_DISK_fn_read (const char *fn, void *result, size_t len)
794 {
795   struct GNUNET_DISK_FileHandle *fh;
796   ssize_t ret;
797   int eno;
798
799   fh = GNUNET_DISK_file_open (fn, GNUNET_DISK_OPEN_READ, GNUNET_DISK_PERM_NONE);
800   if (NULL == fh)
801     return GNUNET_SYSERR;
802   ret = GNUNET_DISK_file_read (fh, result, len);
803   eno = errno;
804   GNUNET_assert (GNUNET_OK == GNUNET_DISK_file_close (fh));
805   errno = eno;
806   return ret;
807 }
808
809
810 /**
811  * Write a buffer to a file.
812  *
813  * @param h handle to open file
814  * @param buffer the data to write
815  * @param n number of bytes to write
816  * @return number of bytes written on success, #GNUNET_SYSERR on error
817  */
818 ssize_t
819 GNUNET_DISK_file_write (const struct GNUNET_DISK_FileHandle *h,
820                         const void *buffer,
821                         size_t n)
822 {
823   if (NULL == h)
824   {
825     errno = EINVAL;
826     return GNUNET_SYSERR;
827   }
828
829
830   return write (h->fd, buffer, n);
831 }
832
833
834 /**
835  * Write a buffer to a file, blocking, if necessary.
836  *
837  * @param h handle to open file
838  * @param buffer the data to write
839  * @param n number of bytes to write
840  * @return number of bytes written on success, #GNUNET_SYSERR on error
841  */
842 ssize_t
843 GNUNET_DISK_file_write_blocking (const struct GNUNET_DISK_FileHandle *h,
844                                  const void *buffer,
845                                  size_t n)
846 {
847   if (NULL == h)
848   {
849     errno = EINVAL;
850     return GNUNET_SYSERR;
851   }
852
853
854   int flags;
855   ssize_t ret;
856
857   /* set to blocking, write, then set back */
858   flags = fcntl (h->fd, F_GETFL);
859   if (0 != (flags & O_NONBLOCK))
860     (void) fcntl (h->fd, F_SETFL, flags - O_NONBLOCK);
861   ret = write (h->fd, buffer, n);
862   if (0 == (flags & O_NONBLOCK))
863     (void) fcntl (h->fd, F_SETFL, flags);
864   return ret;
865 }
866
867
868 /**
869  * Write a buffer to a file.  If the file is longer than the
870  * number of bytes that will be written, it will be truncated.
871  *
872  * @param fn file name
873  * @param buffer the data to write
874  * @param n number of bytes to write
875  * @param mode file permissions
876  * @return number of bytes written on success, #GNUNET_SYSERR on error
877  */
878 ssize_t
879 GNUNET_DISK_fn_write (const char *fn,
880                       const void *buffer,
881                       size_t n,
882                       enum GNUNET_DISK_AccessPermissions mode)
883 {
884   struct GNUNET_DISK_FileHandle *fh;
885   ssize_t ret;
886
887   fh =
888     GNUNET_DISK_file_open (fn,
889                            GNUNET_DISK_OPEN_WRITE | GNUNET_DISK_OPEN_TRUNCATE
890                            | GNUNET_DISK_OPEN_CREATE,
891                            mode);
892   if (! fh)
893     return GNUNET_SYSERR;
894   ret = GNUNET_DISK_file_write (fh, buffer, n);
895   GNUNET_assert (GNUNET_OK == GNUNET_DISK_file_close (fh));
896   return ret;
897 }
898
899
900 /**
901  * Scan a directory for files.
902  *
903  * @param dir_name the name of the directory
904  * @param callback the method to call for each file,
905  *        can be NULL, in that case, we only count
906  * @param callback_cls closure for @a callback
907  * @return the number of files found, #GNUNET_SYSERR on error or
908  *         ieration aborted by callback returning #GNUNET_SYSERR
909  */
910 int
911 GNUNET_DISK_directory_scan (const char *dir_name,
912                             GNUNET_FileNameCallback callback,
913                             void *callback_cls)
914 {
915   DIR *dinfo;
916   struct dirent *finfo;
917   struct stat istat;
918   int count = 0;
919   int ret;
920   char *name;
921   char *dname;
922   unsigned int name_len;
923   unsigned int n_size;
924
925   GNUNET_assert (NULL != dir_name);
926   dname = GNUNET_STRINGS_filename_expand (dir_name);
927   if (NULL == dname)
928     return GNUNET_SYSERR;
929   while ((strlen (dname) > 0) && (dname[strlen (dname) - 1] == DIR_SEPARATOR))
930     dname[strlen (dname) - 1] = '\0';
931   if (0 != stat (dname, &istat))
932   {
933     LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "stat", dname);
934     GNUNET_free (dname);
935     return GNUNET_SYSERR;
936   }
937   if (! S_ISDIR (istat.st_mode))
938   {
939     LOG (GNUNET_ERROR_TYPE_WARNING,
940          _ ("Expected `%s' to be a directory!\n"),
941          dir_name);
942     GNUNET_free (dname);
943     return GNUNET_SYSERR;
944   }
945   errno = 0;
946   dinfo = opendir (dname);
947   if ((EACCES == errno) || (NULL == dinfo))
948   {
949     LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "opendir", dname);
950     if (NULL != dinfo)
951       closedir (dinfo);
952     GNUNET_free (dname);
953     return GNUNET_SYSERR;
954   }
955   name_len = 256;
956   n_size = strlen (dname) + name_len + strlen (DIR_SEPARATOR_STR) + 1;
957   name = GNUNET_malloc (n_size);
958   while (NULL != (finfo = readdir (dinfo)))
959   {
960     if ((0 == strcmp (finfo->d_name, ".")) ||
961         (0 == strcmp (finfo->d_name, "..")))
962       continue;
963     if (NULL != callback)
964     {
965       if (name_len < strlen (finfo->d_name))
966       {
967         GNUNET_free (name);
968         name_len = strlen (finfo->d_name);
969         n_size = strlen (dname) + name_len + strlen (DIR_SEPARATOR_STR) + 1;
970         name = GNUNET_malloc (n_size);
971       }
972       /* dname can end in "/" only if dname == "/";
973        * if dname does not end in "/", we need to add
974        * a "/" (otherwise, we must not!) */
975       GNUNET_snprintf (name,
976                        n_size,
977                        "%s%s%s",
978                        dname,
979                        (0 == strcmp (dname, DIR_SEPARATOR_STR))
980                        ? ""
981                        : DIR_SEPARATOR_STR,
982                        finfo->d_name);
983       ret = callback (callback_cls, name);
984       if (GNUNET_OK != ret)
985       {
986         closedir (dinfo);
987         GNUNET_free (name);
988         GNUNET_free (dname);
989         if (GNUNET_NO == ret)
990           return count;
991         return GNUNET_SYSERR;
992       }
993     }
994     count++;
995   }
996   closedir (dinfo);
997   GNUNET_free (name);
998   GNUNET_free (dname);
999   return count;
1000 }
1001
1002
1003 /**
1004  * Function that removes the given directory by calling
1005  * #GNUNET_DISK_directory_remove().
1006  *
1007  * @param unused not used
1008  * @param fn directory to remove
1009  * @return #GNUNET_OK
1010  */
1011 static int
1012 remove_helper (void *unused, const char *fn)
1013 {
1014   (void) unused;
1015   (void) GNUNET_DISK_directory_remove (fn);
1016   return GNUNET_OK;
1017 }
1018
1019
1020 /**
1021  * Remove all files in a directory (rm -r). Call with
1022  * caution.
1023  *
1024  * @param filename the file to remove
1025  * @return #GNUNET_OK on success, #GNUNET_SYSERR on error
1026  */
1027 int
1028 GNUNET_DISK_directory_remove (const char *filename)
1029 {
1030   struct stat istat;
1031
1032   if (NULL == filename)
1033   {
1034     GNUNET_break (0);
1035     return GNUNET_SYSERR;
1036   }
1037   if (0 != lstat (filename, &istat))
1038     return GNUNET_NO; /* file may not exist... */
1039   (void) chmod (filename, S_IWUSR | S_IRUSR | S_IXUSR);
1040   if (0 == unlink (filename))
1041     return GNUNET_OK;
1042   if ((errno != EISDIR) &&
1043       /* EISDIR is not sufficient in all cases, e.g.
1044       * sticky /tmp directory may result in EPERM on BSD.
1045       * So we also explicitly check "isDirectory" */
1046       (GNUNET_YES != GNUNET_DISK_directory_test (filename, GNUNET_YES)))
1047   {
1048     LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "rmdir", filename);
1049     return GNUNET_SYSERR;
1050   }
1051   if (GNUNET_SYSERR ==
1052       GNUNET_DISK_directory_scan (filename, &remove_helper, NULL))
1053     return GNUNET_SYSERR;
1054   if (0 != rmdir (filename))
1055   {
1056     LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "rmdir", filename);
1057     return GNUNET_SYSERR;
1058   }
1059   return GNUNET_OK;
1060 }
1061
1062
1063 /**
1064  * Copy a file.
1065  *
1066  * @param src file to copy
1067  * @param dst destination file name
1068  * @return #GNUNET_OK on success, #GNUNET_SYSERR on error
1069  */
1070 int
1071 GNUNET_DISK_file_copy (const char *src, const char *dst)
1072 {
1073   char *buf;
1074   uint64_t pos;
1075   uint64_t size;
1076   size_t len;
1077   ssize_t sret;
1078   struct GNUNET_DISK_FileHandle *in;
1079   struct GNUNET_DISK_FileHandle *out;
1080
1081   if (GNUNET_OK != GNUNET_DISK_file_size (src, &size, GNUNET_YES, GNUNET_YES))
1082   {
1083     GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR, "stat", src);
1084     return GNUNET_SYSERR;
1085   }
1086   pos = 0;
1087   in =
1088     GNUNET_DISK_file_open (src, GNUNET_DISK_OPEN_READ, GNUNET_DISK_PERM_NONE);
1089   if (! in)
1090   {
1091     GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR, "open", src);
1092     return GNUNET_SYSERR;
1093   }
1094   out =
1095     GNUNET_DISK_file_open (dst,
1096                            GNUNET_DISK_OPEN_WRITE | GNUNET_DISK_OPEN_CREATE
1097                            | GNUNET_DISK_OPEN_FAILIFEXISTS,
1098                            GNUNET_DISK_PERM_USER_READ
1099                            | GNUNET_DISK_PERM_USER_WRITE
1100                            | GNUNET_DISK_PERM_GROUP_READ
1101                            | GNUNET_DISK_PERM_GROUP_WRITE);
1102   if (! out)
1103   {
1104     GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR, "open", dst);
1105     GNUNET_DISK_file_close (in);
1106     return GNUNET_SYSERR;
1107   }
1108   buf = GNUNET_malloc (COPY_BLK_SIZE);
1109   while (pos < size)
1110   {
1111     len = COPY_BLK_SIZE;
1112     if (len > size - pos)
1113       len = size - pos;
1114     sret = GNUNET_DISK_file_read (in, buf, len);
1115     if ((sret < 0) || (len != (size_t) sret))
1116       goto FAIL;
1117     sret = GNUNET_DISK_file_write (out, buf, len);
1118     if ((sret < 0) || (len != (size_t) sret))
1119       goto FAIL;
1120     pos += len;
1121   }
1122   GNUNET_free (buf);
1123   GNUNET_DISK_file_close (in);
1124   GNUNET_DISK_file_close (out);
1125   return GNUNET_OK;
1126 FAIL:
1127   GNUNET_free (buf);
1128   GNUNET_DISK_file_close (in);
1129   GNUNET_DISK_file_close (out);
1130   return GNUNET_SYSERR;
1131 }
1132
1133
1134 /**
1135  * @brief Removes special characters as ':' from a filename.
1136  * @param fn the filename to canonicalize
1137  */
1138 void
1139 GNUNET_DISK_filename_canonicalize (char *fn)
1140 {
1141   char *idx;
1142   char c;
1143
1144   for (idx = fn; *idx; idx++)
1145   {
1146     c = *idx;
1147
1148     if ((c == '/') ||(c == '\\') ||(c == ':') ||(c == '*') ||(c == '?') ||(c ==
1149                                                                            '"')
1150         ||
1151         (c == '<') ||(c == '>') ||(c == '|') )
1152     {
1153       *idx = '_';
1154     }
1155   }
1156 }
1157
1158
1159 /**
1160  * @brief Change owner of a file
1161  *
1162  * @param filename name of file to change the owner of
1163  * @param user name of the new owner
1164  * @return #GNUNET_OK on success, #GNUNET_SYSERR on failure
1165  */
1166 int
1167 GNUNET_DISK_file_change_owner (const char *filename, const char *user)
1168 {
1169   struct passwd *pws;
1170
1171   pws = getpwnam (user);
1172   if (NULL == pws)
1173   {
1174     LOG (GNUNET_ERROR_TYPE_ERROR,
1175          _ ("Cannot obtain information about user `%s': %s\n"),
1176          user,
1177          strerror (errno));
1178     return GNUNET_SYSERR;
1179   }
1180   if (0 != chown (filename, pws->pw_uid, pws->pw_gid))
1181   {
1182     LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "chown", filename);
1183     return GNUNET_SYSERR;
1184   }
1185   return GNUNET_OK;
1186 }
1187
1188
1189 /**
1190  * Lock a part of a file
1191  *
1192  * @param fh file handle
1193  * @param lock_start absolute position from where to lock
1194  * @param lock_end absolute position until where to lock
1195  * @param excl #GNUNET_YES for an exclusive lock
1196  * @return #GNUNET_OK on success, #GNUNET_SYSERR on error
1197  */
1198 int
1199 GNUNET_DISK_file_lock (struct GNUNET_DISK_FileHandle *fh,
1200                        off_t lock_start,
1201                        off_t lock_end,
1202                        int excl)
1203 {
1204   if (fh == NULL)
1205   {
1206     errno = EINVAL;
1207     return GNUNET_SYSERR;
1208   }
1209
1210   struct flock fl;
1211
1212   memset (&fl, 0, sizeof(struct flock));
1213   fl.l_type = excl ? F_WRLCK : F_RDLCK;
1214   fl.l_whence = SEEK_SET;
1215   fl.l_start = lock_start;
1216   fl.l_len = lock_end;
1217
1218   return fcntl (fh->fd, F_SETLK, &fl) != 0 ? GNUNET_SYSERR : GNUNET_OK;
1219 }
1220
1221
1222 /**
1223  * Unlock a part of a file
1224  *
1225  * @param fh file handle
1226  * @param unlock_start absolute position from where to unlock
1227  * @param unlock_end absolute position until where to unlock
1228  * @return #GNUNET_OK on success, #GNUNET_SYSERR on error
1229  */
1230 int
1231 GNUNET_DISK_file_unlock (struct GNUNET_DISK_FileHandle *fh,
1232                          off_t unlock_start,
1233                          off_t unlock_end)
1234 {
1235   if (fh == NULL)
1236   {
1237     errno = EINVAL;
1238     return GNUNET_SYSERR;
1239   }
1240
1241   struct flock fl;
1242
1243   memset (&fl, 0, sizeof(struct flock));
1244   fl.l_type = F_UNLCK;
1245   fl.l_whence = SEEK_SET;
1246   fl.l_start = unlock_start;
1247   fl.l_len = unlock_end;
1248
1249   return fcntl (fh->fd, F_SETLK, &fl) != 0 ? GNUNET_SYSERR : GNUNET_OK;
1250 }
1251
1252
1253 /**
1254  * Open a file.  Note that the access permissions will only be
1255  * used if a new file is created and if the underlying operating
1256  * system supports the given permissions.
1257  *
1258  * @param fn file name to be opened
1259  * @param flags opening flags, a combination of GNUNET_DISK_OPEN_xxx bit flags
1260  * @param perm permissions for the newly created file, use
1261  *             #GNUNET_DISK_PERM_NONE if a file could not be created by this
1262  *             call (because of flags)
1263  * @return IO handle on success, NULL on error
1264  */
1265 struct GNUNET_DISK_FileHandle *
1266 GNUNET_DISK_file_open (const char *fn,
1267                        enum GNUNET_DISK_OpenFlags flags,
1268                        enum GNUNET_DISK_AccessPermissions perm)
1269 {
1270   char *expfn;
1271   struct GNUNET_DISK_FileHandle *ret;
1272
1273   int oflags;
1274   int mode;
1275   int fd;
1276
1277   expfn = GNUNET_STRINGS_filename_expand (fn);
1278   if (NULL == expfn)
1279     return NULL;
1280
1281   mode = 0;
1282   if (GNUNET_DISK_OPEN_READWRITE == (flags & GNUNET_DISK_OPEN_READWRITE))
1283     oflags = O_RDWR; /* note: O_RDWR is NOT always O_RDONLY | O_WRONLY */
1284   else if (flags & GNUNET_DISK_OPEN_READ)
1285     oflags = O_RDONLY;
1286   else if (flags & GNUNET_DISK_OPEN_WRITE)
1287     oflags = O_WRONLY;
1288   else
1289   {
1290     GNUNET_break (0);
1291     GNUNET_free (expfn);
1292     return NULL;
1293   }
1294   if (flags & GNUNET_DISK_OPEN_FAILIFEXISTS)
1295     oflags |= (O_CREAT | O_EXCL);
1296   if (flags & GNUNET_DISK_OPEN_TRUNCATE)
1297     oflags |= O_TRUNC;
1298   if (flags & GNUNET_DISK_OPEN_APPEND)
1299     oflags |= O_APPEND;
1300   if (GNUNET_NO == GNUNET_DISK_file_test (fn))
1301   {
1302     if (flags & GNUNET_DISK_OPEN_CREATE)
1303     {
1304       (void) GNUNET_DISK_directory_create_for_file (expfn);
1305       oflags |= O_CREAT;
1306       mode = translate_unix_perms (perm);
1307     }
1308   }
1309
1310   fd = open (expfn,
1311              oflags
1312 #if O_CLOEXEC
1313              | O_CLOEXEC
1314 #endif
1315              | O_LARGEFILE,
1316              mode);
1317   if (fd == -1)
1318   {
1319     if (0 == (flags & GNUNET_DISK_OPEN_FAILIFEXISTS))
1320       LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "open", expfn);
1321     else
1322       LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_DEBUG, "open", expfn);
1323     GNUNET_free (expfn);
1324     return NULL;
1325   }
1326
1327   ret = GNUNET_new (struct GNUNET_DISK_FileHandle);
1328
1329   ret->fd = fd;
1330
1331   GNUNET_free (expfn);
1332   return ret;
1333 }
1334
1335
1336 /**
1337  * Close an open file.
1338  *
1339  * @param h file handle
1340  * @return #GNUNET_OK on success, #GNUNET_SYSERR otherwise
1341  */
1342 int
1343 GNUNET_DISK_file_close (struct GNUNET_DISK_FileHandle *h)
1344 {
1345   int ret;
1346
1347   if (h == NULL)
1348   {
1349     errno = EINVAL;
1350     return GNUNET_SYSERR;
1351   }
1352
1353   ret = GNUNET_OK;
1354
1355   if (close (h->fd) != 0)
1356   {
1357     LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING, "close");
1358     ret = GNUNET_SYSERR;
1359   }
1360
1361   GNUNET_free (h);
1362   return ret;
1363 }
1364
1365
1366 /**
1367  * Get a handle from a native integer FD.
1368  *
1369  * @param fno native integer file descriptor
1370  * @return file handle corresponding to the descriptor, NULL on error
1371  */
1372 struct GNUNET_DISK_FileHandle *
1373 GNUNET_DISK_get_handle_from_int_fd (int fno)
1374 {
1375   struct GNUNET_DISK_FileHandle *fh;
1376
1377   if ((((off_t) -1) == lseek (fno, 0, SEEK_CUR)) && (EBADF == errno))
1378     return NULL; /* invalid FD */
1379
1380   fh = GNUNET_new (struct GNUNET_DISK_FileHandle);
1381
1382   fh->fd = fno;
1383
1384   return fh;
1385 }
1386
1387
1388 /**
1389  * Get a handle from a native streaming FD.
1390  *
1391  * @param fd native streaming file descriptor
1392  * @return file handle corresponding to the descriptor
1393  */
1394 struct GNUNET_DISK_FileHandle *
1395 GNUNET_DISK_get_handle_from_native (FILE *fd)
1396 {
1397   int fno;
1398
1399   fno = fileno (fd);
1400   if (-1 == fno)
1401     return NULL;
1402
1403   return GNUNET_DISK_get_handle_from_int_fd (fno);
1404 }
1405
1406
1407 /**
1408  * Handle for a memory-mapping operation.
1409  */
1410 struct GNUNET_DISK_MapHandle
1411 {
1412   /**
1413    * Address where the map is in memory.
1414    */
1415   void *addr;
1416
1417   /**
1418    * Number of bytes mapped.
1419    */
1420   size_t len;
1421 };
1422
1423
1424 #ifndef MAP_FAILED
1425 #define MAP_FAILED ((void *) -1)
1426 #endif
1427
1428 /**
1429  * Map a file into memory
1430  *
1431  * @param h open file handle
1432  * @param m handle to the new mapping
1433  * @param access access specification, GNUNET_DISK_MAP_TYPE_xxx
1434  * @param len size of the mapping
1435  * @return pointer to the mapped memory region, NULL on failure
1436  */
1437 void *
1438 GNUNET_DISK_file_map (const struct GNUNET_DISK_FileHandle *h,
1439                       struct GNUNET_DISK_MapHandle **m,
1440                       enum GNUNET_DISK_MapType access,
1441                       size_t len)
1442 {
1443   if (NULL == h)
1444   {
1445     errno = EINVAL;
1446     return NULL;
1447   }
1448
1449   int prot;
1450
1451   prot = 0;
1452   if (access & GNUNET_DISK_MAP_TYPE_READ)
1453     prot = PROT_READ;
1454   if (access & GNUNET_DISK_MAP_TYPE_WRITE)
1455     prot |= PROT_WRITE;
1456   *m = GNUNET_new (struct GNUNET_DISK_MapHandle);
1457   (*m)->addr = mmap (NULL, len, prot, MAP_SHARED, h->fd, 0);
1458   GNUNET_assert (NULL != (*m)->addr);
1459   if (MAP_FAILED == (*m)->addr)
1460   {
1461     GNUNET_free (*m);
1462     return NULL;
1463   }
1464   (*m)->len = len;
1465   return (*m)->addr;
1466 }
1467
1468 /**
1469  * Unmap a file
1470  * @param h mapping handle
1471  * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
1472  */
1473 int
1474 GNUNET_DISK_file_unmap (struct GNUNET_DISK_MapHandle *h)
1475 {
1476   int ret;
1477
1478   if (h == NULL)
1479   {
1480     errno = EINVAL;
1481     return GNUNET_SYSERR;
1482   }
1483
1484   ret = munmap (h->addr, h->len) != -1 ? GNUNET_OK : GNUNET_SYSERR;
1485
1486   GNUNET_free (h);
1487   return ret;
1488 }
1489
1490
1491 /**
1492  * Write file changes to disk
1493  * @param h handle to an open file
1494  * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
1495  */
1496 int
1497 GNUNET_DISK_file_sync (const struct GNUNET_DISK_FileHandle *h)
1498 {
1499   if (h == NULL)
1500   {
1501     errno = EINVAL;
1502     return GNUNET_SYSERR;
1503   }
1504
1505 #if defined(FREEBSD) || defined(OPENBSD) || defined(DARWIN)
1506   return fsync (h->fd) == -1 ? GNUNET_SYSERR : GNUNET_OK;
1507 #else
1508   return fdatasync (h->fd) == -1 ? GNUNET_SYSERR : GNUNET_OK;
1509 #endif
1510 }
1511
1512
1513 /**
1514  * Creates an interprocess channel
1515  *
1516  * @param blocking_read creates an asynchronous pipe for reading if set to GNUNET_NO
1517  * @param blocking_write creates an asynchronous pipe for writing if set to GNUNET_NO
1518  * @param inherit_read inherit the parent processes stdin (only for windows)
1519  * @param inherit_write inherit the parent processes stdout (only for windows)
1520  * @return handle to the new pipe, NULL on error
1521  */
1522 struct GNUNET_DISK_PipeHandle *
1523 GNUNET_DISK_pipe (int blocking_read,
1524                   int blocking_write,
1525                   int inherit_read,
1526                   int inherit_write)
1527 {
1528   int fd[2];
1529   int ret;
1530   int eno;
1531
1532   (void) inherit_read;
1533   (void) inherit_write;
1534   ret = pipe (fd);
1535   if (ret == -1)
1536   {
1537     eno = errno;
1538     LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR, "pipe");
1539     errno = eno;
1540     return NULL;
1541   }
1542   return GNUNET_DISK_pipe_from_fd (blocking_read, blocking_write, fd);
1543 }
1544
1545
1546 /**
1547  * Creates a pipe object from a couple of file descriptors.
1548  * Useful for wrapping existing pipe FDs.
1549  *
1550  * @param blocking_read creates an asynchronous pipe for reading if set to GNUNET_NO
1551  * @param blocking_write creates an asynchronous pipe for writing if set to GNUNET_NO
1552  * @param fd an array of two fd values. One of them may be -1 for read-only or write-only pipes
1553  *
1554  * @return handle to the new pipe, NULL on error
1555  */
1556 struct GNUNET_DISK_PipeHandle *
1557 GNUNET_DISK_pipe_from_fd (int blocking_read, int blocking_write, int fd[2])
1558 {
1559   struct GNUNET_DISK_PipeHandle *p;
1560
1561   p = GNUNET_new (struct GNUNET_DISK_PipeHandle);
1562
1563   int ret;
1564   int flags;
1565   int eno = 0; /* make gcc happy */
1566
1567   ret = 0;
1568   if (fd[0] >= 0)
1569   {
1570     p->fd[0] = GNUNET_new (struct GNUNET_DISK_FileHandle);
1571     p->fd[0]->fd = fd[0];
1572     if (! blocking_read)
1573     {
1574       flags = fcntl (fd[0], F_GETFL);
1575       flags |= O_NONBLOCK;
1576       if (0 > fcntl (fd[0], F_SETFL, flags))
1577       {
1578         ret = -1;
1579         eno = errno;
1580       }
1581     }
1582     flags = fcntl (fd[0], F_GETFD);
1583     flags |= FD_CLOEXEC;
1584     if (0 > fcntl (fd[0], F_SETFD, flags))
1585     {
1586       ret = -1;
1587       eno = errno;
1588     }
1589   }
1590
1591   if (fd[1] >= 0)
1592   {
1593     p->fd[1] = GNUNET_new (struct GNUNET_DISK_FileHandle);
1594     p->fd[1]->fd = fd[1];
1595     if (! blocking_write)
1596     {
1597       flags = fcntl (fd[1], F_GETFL);
1598       flags |= O_NONBLOCK;
1599       if (0 > fcntl (fd[1], F_SETFL, flags))
1600       {
1601         ret = -1;
1602         eno = errno;
1603       }
1604     }
1605     flags = fcntl (fd[1], F_GETFD);
1606     flags |= FD_CLOEXEC;
1607     if (0 > fcntl (fd[1], F_SETFD, flags))
1608     {
1609       ret = -1;
1610       eno = errno;
1611     }
1612   }
1613   if (ret == -1)
1614   {
1615     errno = eno;
1616     LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR, "fcntl");
1617     if (p->fd[0]->fd >= 0)
1618       GNUNET_break (0 == close (p->fd[0]->fd));
1619     if (p->fd[1]->fd >= 0)
1620       GNUNET_break (0 == close (p->fd[1]->fd));
1621     GNUNET_free_non_null (p->fd[0]);
1622     GNUNET_free_non_null (p->fd[1]);
1623     GNUNET_free (p);
1624     errno = eno;
1625     return NULL;
1626   }
1627
1628   return p;
1629 }
1630
1631
1632 /**
1633  * Closes an interprocess channel
1634  *
1635  * @param p pipe to close
1636  * @param end which end of the pipe to close
1637  * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
1638  */
1639 int
1640 GNUNET_DISK_pipe_close_end (struct GNUNET_DISK_PipeHandle *p,
1641                             enum GNUNET_DISK_PipeEnd end)
1642 {
1643   int ret = GNUNET_OK;
1644
1645   if (end == GNUNET_DISK_PIPE_END_READ)
1646   {
1647     if (p->fd[0])
1648     {
1649       ret = GNUNET_DISK_file_close (p->fd[0]);
1650       p->fd[0] = NULL;
1651     }
1652   }
1653   else if (end == GNUNET_DISK_PIPE_END_WRITE)
1654   {
1655     if (p->fd[1])
1656     {
1657       ret = GNUNET_DISK_file_close (p->fd[1]);
1658       p->fd[1] = NULL;
1659     }
1660   }
1661
1662   return ret;
1663 }
1664
1665 /**
1666  * Detaches one of the ends from the pipe.
1667  * Detached end is a fully-functional FileHandle, it will
1668  * not be affected by anything you do with the pipe afterwards.
1669  * Each end of a pipe can only be detched from it once (i.e.
1670  * it is not duplicated).
1671  *
1672  * @param p pipe to detach an end from
1673  * @param end which end of the pipe to detach
1674  * @return Detached end on success, NULL on failure
1675  * (or if that end is not present or is closed).
1676  */
1677 struct GNUNET_DISK_FileHandle *
1678 GNUNET_DISK_pipe_detach_end (struct GNUNET_DISK_PipeHandle *p,
1679                              enum GNUNET_DISK_PipeEnd end)
1680 {
1681   struct GNUNET_DISK_FileHandle *ret = NULL;
1682
1683   if (end == GNUNET_DISK_PIPE_END_READ)
1684   {
1685     if (p->fd[0])
1686     {
1687       ret = p->fd[0];
1688       p->fd[0] = NULL;
1689     }
1690   }
1691   else if (end == GNUNET_DISK_PIPE_END_WRITE)
1692   {
1693     if (p->fd[1])
1694     {
1695       ret = p->fd[1];
1696       p->fd[1] = NULL;
1697     }
1698   }
1699
1700   return ret;
1701 }
1702
1703
1704 /**
1705  * Closes an interprocess channel
1706  *
1707  * @param p pipe to close
1708  * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
1709  */
1710 int
1711 GNUNET_DISK_pipe_close (struct GNUNET_DISK_PipeHandle *p)
1712 {
1713   int ret = GNUNET_OK;
1714
1715   int read_end_close;
1716   int write_end_close;
1717   int read_end_close_errno;
1718   int write_end_close_errno;
1719
1720   read_end_close = GNUNET_DISK_pipe_close_end (p, GNUNET_DISK_PIPE_END_READ);
1721   read_end_close_errno = errno;
1722   write_end_close = GNUNET_DISK_pipe_close_end (p, GNUNET_DISK_PIPE_END_WRITE);
1723   write_end_close_errno = errno;
1724   GNUNET_free (p);
1725
1726   if (GNUNET_OK != read_end_close)
1727   {
1728     errno = read_end_close_errno;
1729     ret = read_end_close;
1730   }
1731   else if (GNUNET_OK != write_end_close)
1732   {
1733     errno = write_end_close_errno;
1734     ret = write_end_close;
1735   }
1736
1737   return ret;
1738 }
1739
1740
1741 /**
1742  * Get the handle to a particular pipe end
1743  *
1744  * @param p pipe
1745  * @param n end to access
1746  * @return handle for the respective end
1747  */
1748 const struct GNUNET_DISK_FileHandle *
1749 GNUNET_DISK_pipe_handle (const struct GNUNET_DISK_PipeHandle *p,
1750                          enum GNUNET_DISK_PipeEnd n)
1751 {
1752   switch (n)
1753   {
1754   case GNUNET_DISK_PIPE_END_READ:
1755   case GNUNET_DISK_PIPE_END_WRITE:
1756     return p->fd[n];
1757
1758   default:
1759     GNUNET_break (0);
1760     return NULL;
1761   }
1762 }
1763
1764
1765 /**
1766  * Retrieve OS file handle
1767  * @internal
1768  * @param fh GNUnet file descriptor
1769  * @param dst destination buffer
1770  * @param dst_len length of dst
1771  * @return #GNUNET_OK on success, #GNUNET_SYSERR otherwise
1772  */
1773 int
1774 GNUNET_DISK_internal_file_handle_ (const struct GNUNET_DISK_FileHandle *fh,
1775                                    void *dst,
1776                                    size_t dst_len)
1777 {
1778   if (NULL == fh)
1779     return GNUNET_SYSERR;
1780
1781   if (dst_len < sizeof(int))
1782     return GNUNET_SYSERR;
1783   *((int *) dst) = fh->fd;
1784
1785   return GNUNET_OK;
1786 }
1787
1788
1789 /**
1790  * Helper function for #GNUNET_DISK_purge_cfg_dir.
1791  *
1792  * @param cls a `const char *` with the option to purge
1793  * @param cfg our configuration
1794  * @return #GNUNET_OK on success
1795  */
1796 static int
1797 purge_cfg_dir (void *cls, const struct GNUNET_CONFIGURATION_Handle *cfg)
1798 {
1799   const char *option = cls;
1800   char *tmpname;
1801
1802   if (GNUNET_OK !=
1803       GNUNET_CONFIGURATION_get_value_filename (cfg, "PATHS", option, &tmpname))
1804   {
1805     GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, "PATHS", option);
1806     return GNUNET_NO;
1807   }
1808   if (GNUNET_SYSERR == GNUNET_DISK_directory_remove (tmpname))
1809   {
1810     GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR, "remove", tmpname);
1811     GNUNET_free (tmpname);
1812     return GNUNET_OK;
1813   }
1814   GNUNET_free (tmpname);
1815   return GNUNET_OK;
1816 }
1817
1818
1819 /**
1820  * Remove the directory given under @a option in
1821  * section [PATHS] in configuration under @a cfg_filename
1822  *
1823  * @param cfg_filename configuration file to parse
1824  * @param option option with the dir name to purge
1825  */
1826 void
1827 GNUNET_DISK_purge_cfg_dir (const char *cfg_filename, const char *option)
1828 {
1829   GNUNET_break (GNUNET_OK ==
1830                 GNUNET_CONFIGURATION_parse_and_run (cfg_filename,
1831                                                     &purge_cfg_dir,
1832                                                     (void *) option));
1833 }
1834
1835
1836 /* end of disk.c */