error handling
[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 /**
199  * Get the size of an open file.
200  *
201  * @param fh open file handle
202  * @param size where to write size of the file
203  * @return #GNUNET_OK on success, #GNUNET_SYSERR on error
204  */
205 int
206 GNUNET_DISK_file_handle_size (struct GNUNET_DISK_FileHandle *fh, off_t *size)
207 {
208   struct stat sbuf;
209
210   if (0 != fstat (fh->fd, &sbuf))
211     return GNUNET_SYSERR;
212   *size = sbuf.st_size;
213   return GNUNET_OK;
214 }
215
216
217 /**
218  * Move the read/write pointer in a file
219  *
220  * @param h handle of an open file
221  * @param offset position to move to
222  * @param whence specification to which position the offset parameter relates to
223  * @return the new position on success, #GNUNET_SYSERR otherwise
224  */
225 off_t
226 GNUNET_DISK_file_seek (const struct GNUNET_DISK_FileHandle *h,
227                        off_t offset,
228                        enum GNUNET_DISK_Seek whence)
229 {
230   if (h == NULL)
231   {
232     errno = EINVAL;
233     return GNUNET_SYSERR;
234   }
235
236   static int t[] = { SEEK_SET, SEEK_CUR, SEEK_END };
237
238   return lseek (h->fd, offset, t[whence]);
239 }
240
241
242 /**
243  * Get the size of the file (or directory) of the given file (in
244  * bytes).
245  *
246  * @param filename name of the file or directory
247  * @param size set to the size of the file (or,
248  *             in the case of directories, the sum
249  *             of all sizes of files in the directory)
250  * @param include_symbolic_links should symbolic links be
251  *        included?
252  * @param single_file_mode #GNUNET_YES to only get size of one file
253  *        and return #GNUNET_SYSERR for directories.
254  * @return #GNUNET_SYSERR on error, #GNUNET_OK on success
255  */
256 int
257 GNUNET_DISK_file_size (const char *filename,
258                        uint64_t *size,
259                        int include_symbolic_links,
260                        int single_file_mode)
261 {
262   struct GetFileSizeData gfsd;
263   int ret;
264
265   GNUNET_assert (size != NULL);
266   gfsd.total = 0;
267   gfsd.include_sym_links = include_symbolic_links;
268   gfsd.single_file_mode = single_file_mode;
269   ret = getSizeRec (&gfsd, filename);
270   *size = gfsd.total;
271   return ret;
272 }
273
274
275 /**
276  * Obtain some unique identifiers for the given file
277  * that can be used to identify it in the local system.
278  * This function is used between GNUnet processes to
279  * quickly check if two files with the same absolute path
280  * are actually identical.  The two processes represent
281  * the same peer but may communicate over the network
282  * (and the file may be on an NFS volume).  This function
283  * may not be supported on all operating systems.
284  *
285  * @param filename name of the file
286  * @param dev set to the device ID
287  * @param ino set to the inode ID
288  * @return #GNUNET_OK on success
289  */
290 int
291 GNUNET_DISK_file_get_identifiers (const char *filename,
292                                   uint64_t *dev,
293                                   uint64_t *ino)
294 {
295 #if HAVE_STAT
296   {
297     struct stat sbuf;
298
299     if (0 != stat (filename, &sbuf))
300     {
301       return GNUNET_SYSERR;
302     }
303     *ino = (uint64_t) sbuf.st_ino;
304   }
305 #else
306   *ino = 0;
307 #endif
308 #if HAVE_STATVFS
309   {
310     struct statvfs fbuf;
311
312     if (0 != statvfs (filename, &fbuf))
313     {
314       return GNUNET_SYSERR;
315     }
316     *dev = (uint64_t) fbuf.f_fsid;
317   }
318 #elif HAVE_STATFS
319   {
320     struct statfs fbuf;
321
322     if (0 != statfs (filename, &fbuf))
323     {
324       return GNUNET_SYSERR;
325     }
326     *dev =
327       ((uint64_t) fbuf.f_fsid.val[0]) << 32 || ((uint64_t) fbuf.f_fsid.val[1]);
328   }
329 #else
330   *dev = 0;
331 #endif
332   return GNUNET_OK;
333 }
334
335
336 /**
337  * Create the name for a temporary file or directory from a template.
338  *
339  * @param t template (without XXXXX or "/tmp/")
340  * @return name ready for passing to 'mktemp' or 'mkdtemp', NULL on error
341  */
342 static char *
343 mktemp_name (const char *t)
344 {
345   const char *tmpdir;
346   char *tmpl;
347   char *fn;
348
349   if ((t[0] != '/') && (t[0] != '\\'))
350   {
351     /* FIXME: This uses system codepage on W32, not UTF-8 */
352     tmpdir = getenv ("TMPDIR");
353     if (NULL == tmpdir)
354       tmpdir = getenv ("TMP");
355     if (NULL == tmpdir)
356       tmpdir = getenv ("TEMP");
357     if (NULL == tmpdir)
358       tmpdir = "/tmp";
359     GNUNET_asprintf (&tmpl, "%s/%s%s", tmpdir, t, "XXXXXX");
360   }
361   else
362   {
363     GNUNET_asprintf (&tmpl, "%s%s", t, "XXXXXX");
364   }
365   fn = tmpl;
366   return fn;
367 }
368
369
370 /**
371  * Update POSIX permissions mask of a file on disk.  If both argumets
372  * are #GNUNET_NO, the file is made world-read-write-executable (777).
373  *
374  * @param fn name of the file to update
375  * @param require_uid_match #GNUNET_YES means 700
376  * @param require_gid_match #GNUNET_YES means 770 unless @a require_uid_match is set
377  */
378 void
379 GNUNET_DISK_fix_permissions (const char *fn,
380                              int require_uid_match,
381                              int require_gid_match)
382 {
383   mode_t mode;
384
385   if (GNUNET_YES == require_uid_match)
386     mode = S_IRUSR | S_IWUSR | S_IXUSR;
387   else if (GNUNET_YES == require_gid_match)
388     mode = S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IWGRP | S_IXGRP;
389   else
390     mode = S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IWGRP | S_IXGRP | S_IROTH
391            | S_IWOTH | S_IXOTH;
392   if (0 != chmod (fn, mode))
393     GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "chmod", fn);
394 }
395
396
397 /**
398  * Create an (empty) temporary directory on disk.  If the given name is not
399  * an absolute path, the current 'TMPDIR' will be prepended.  In any case,
400  * 6 random characters will be appended to the name to create a unique
401  * filename.
402  *
403  * @param t component to use for the name;
404  *        does NOT contain "XXXXXX" or "/tmp/".
405  * @return NULL on error, otherwise name of fresh
406  *         file on disk in directory for temporary files
407  */
408 char *
409 GNUNET_DISK_mkdtemp (const char *t)
410 {
411   char *fn;
412   mode_t omask;
413
414   omask = umask (S_IWGRP | S_IWOTH | S_IRGRP | S_IROTH);
415   fn = mktemp_name (t);
416   if (fn != mkdtemp (fn))
417   {
418     LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_ERROR, "mkdtemp", fn);
419     GNUNET_free (fn);
420     umask (omask);
421     return NULL;
422   }
423   umask (omask);
424   return fn;
425 }
426
427
428 /**
429  * Move a file out of the way (create a backup) by
430  * renaming it to "orig.NUM~" where NUM is the smallest
431  * number that is not used yet.
432  *
433  * @param fil name of the file to back up
434  */
435 void
436 GNUNET_DISK_file_backup (const char *fil)
437 {
438   size_t slen;
439   char *target;
440   unsigned int num;
441
442   slen = strlen (fil) + 20;
443   target = GNUNET_malloc (slen);
444   num = 0;
445   do
446   {
447     GNUNET_snprintf (target, slen, "%s.%u~", fil, num++);
448   }
449   while (0 == access (target, F_OK));
450   if (0 != rename (fil, target))
451     GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR, "rename", fil);
452   GNUNET_free (target);
453 }
454
455
456 /**
457  * Create an (empty) temporary file on disk.  If the given name is not
458  * an absolute path, the current 'TMPDIR' will be prepended.  In any case,
459  * 6 random characters will be appended to the name to create a unique
460  * filename.
461  *
462  * @param t component to use for the name;
463  *        does NOT contain "XXXXXX" or "/tmp/".
464  * @return NULL on error, otherwise name of fresh
465  *         file on disk in directory for temporary files
466  */
467 char *
468 GNUNET_DISK_mktemp (const char *t)
469 {
470   int fd;
471   char *fn;
472   mode_t omask;
473
474   omask = umask (S_IWGRP | S_IWOTH | S_IRGRP | S_IROTH);
475   fn = mktemp_name (t);
476   if (-1 == (fd = mkstemp (fn)))
477   {
478     LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_ERROR, "mkstemp", fn);
479     GNUNET_free (fn);
480     umask (omask);
481     return NULL;
482   }
483   umask (omask);
484   if (0 != close (fd))
485     LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "close", fn);
486   return fn;
487 }
488
489
490 /**
491  * Test if @a fil is a directory and listable. Optionally, also check if the
492  * directory is readable.  Will not print an error message if the directory does
493  * not exist.  Will log errors if #GNUNET_SYSERR is returned (i.e., a file exists
494  * with the same name).
495  *
496  * @param fil filename to test
497  * @param is_readable #GNUNET_YES to additionally check if @a fil is readable;
498  *          #GNUNET_NO to disable this check
499  * @return #GNUNET_YES if yes, #GNUNET_NO if not; #GNUNET_SYSERR if it
500  *           does not exist or stat'ed
501  */
502 int
503 GNUNET_DISK_directory_test (const char *fil, int is_readable)
504 {
505   struct stat filestat;
506   int ret;
507
508   ret = stat (fil, &filestat);
509   if (ret != 0)
510   {
511     if (errno != ENOENT)
512       LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "stat", fil);
513     return GNUNET_SYSERR;
514   }
515   if (! S_ISDIR (filestat.st_mode))
516   {
517     LOG (GNUNET_ERROR_TYPE_INFO,
518          "A file already exits with the same name %s\n",
519          fil);
520     return GNUNET_NO;
521   }
522   if (GNUNET_YES == is_readable)
523     ret = access (fil, R_OK | X_OK);
524   else
525     ret = access (fil, X_OK);
526   if (ret < 0)
527   {
528     LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "access", fil);
529     return GNUNET_NO;
530   }
531   return GNUNET_YES;
532 }
533
534
535 /**
536  * Check that fil corresponds to a filename
537  * (of a file that exists and that is not a directory).
538  *
539  * @param fil filename to check
540  * @return #GNUNET_YES if yes, #GNUNET_NO if not a file, #GNUNET_SYSERR if something
541  * else (will print an error message in that case, too).
542  */
543 int
544 GNUNET_DISK_file_test (const char *fil)
545 {
546   struct stat filestat;
547   int ret;
548   char *rdir;
549
550   rdir = GNUNET_STRINGS_filename_expand (fil);
551   if (rdir == NULL)
552     return GNUNET_SYSERR;
553
554   ret = stat (rdir, &filestat);
555   if (ret != 0)
556   {
557     if (errno != ENOENT)
558     {
559       LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "stat", rdir);
560       GNUNET_free (rdir);
561       return GNUNET_SYSERR;
562     }
563     GNUNET_free (rdir);
564     return GNUNET_NO;
565   }
566   if (! S_ISREG (filestat.st_mode))
567   {
568     GNUNET_free (rdir);
569     return GNUNET_NO;
570   }
571   if (access (rdir, F_OK) < 0)
572   {
573     LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "access", rdir);
574     GNUNET_free (rdir);
575     return GNUNET_SYSERR;
576   }
577   GNUNET_free (rdir);
578   return GNUNET_YES;
579 }
580
581
582 /**
583  * Implementation of "mkdir -p"
584  *
585  * @param dir the directory to create
586  * @return #GNUNET_OK on success, #GNUNET_SYSERR on failure
587  */
588 int
589 GNUNET_DISK_directory_create (const char *dir)
590 {
591   char *rdir;
592   unsigned int len;
593   unsigned int pos;
594   unsigned int pos2;
595   int ret = GNUNET_OK;
596
597   rdir = GNUNET_STRINGS_filename_expand (dir);
598   if (rdir == NULL)
599   {
600     GNUNET_break (0);
601     return GNUNET_SYSERR;
602   }
603
604   len = strlen (rdir);
605
606   pos = 1; /* skip heading '/' */
607
608   /* Check which low level directories already exist */
609   pos2 = len;
610   rdir[len] = DIR_SEPARATOR;
611   while (pos <= pos2)
612   {
613     if (DIR_SEPARATOR == rdir[pos2])
614     {
615       rdir[pos2] = '\0';
616       ret = GNUNET_DISK_directory_test (rdir, GNUNET_NO);
617       if (GNUNET_NO == ret)
618       {
619         GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
620                     "Creating directory `%s' failed",
621                     rdir);
622         GNUNET_free (rdir);
623         return GNUNET_SYSERR;
624       }
625       rdir[pos2] = DIR_SEPARATOR;
626       if (GNUNET_YES == ret)
627       {
628         pos2++;
629         break;
630       }
631     }
632     pos2--;
633   }
634   rdir[len] = '\0';
635   if (pos < pos2)
636     pos = pos2;
637   /* Start creating directories */
638   while (pos <= len)
639   {
640     if ((rdir[pos] == DIR_SEPARATOR) || (pos == len))
641     {
642       rdir[pos] = '\0';
643       ret = GNUNET_DISK_directory_test (rdir, GNUNET_NO);
644       if (GNUNET_NO == ret)
645       {
646         GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
647                     "Creating directory `%s' failed",
648                     rdir);
649         GNUNET_free (rdir);
650         return GNUNET_SYSERR;
651       }
652       if (GNUNET_SYSERR == ret)
653       {
654         ret = mkdir (rdir,
655                      S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IXGRP | S_IROTH
656                      | S_IXOTH);    /* 755 */
657
658         if ((ret != 0) && (errno != EEXIST))
659         {
660           LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_ERROR, "mkdir", rdir);
661           GNUNET_free (rdir);
662           return GNUNET_SYSERR;
663         }
664       }
665       rdir[pos] = DIR_SEPARATOR;
666     }
667     pos++;
668   }
669   GNUNET_free (rdir);
670   return GNUNET_OK;
671 }
672
673
674 /**
675  * Create the directory structure for storing a file.
676  *
677  * @param filename name of a file in the directory
678  * @returns #GNUNET_OK on success,
679  *          #GNUNET_SYSERR on failure,
680  *          #GNUNET_NO if the directory
681  *          exists but is not writeable for us
682  */
683 int
684 GNUNET_DISK_directory_create_for_file (const char *filename)
685 {
686   char *rdir;
687   size_t len;
688   int ret;
689   int eno;
690
691   rdir = GNUNET_STRINGS_filename_expand (filename);
692   if (NULL == rdir)
693   {
694     errno = EINVAL;
695     return GNUNET_SYSERR;
696   }
697   if (0 == access (rdir, W_OK))
698   {
699     GNUNET_free (rdir);
700     return GNUNET_OK;
701   }
702
703   len = strlen (rdir);
704   while ((len > 0) && (rdir[len] != DIR_SEPARATOR))
705     len--;
706   rdir[len] = '\0';
707   /* The empty path is invalid and in this case refers to / */
708   if (0 == len)
709   {
710     GNUNET_free (rdir);
711     rdir = GNUNET_strdup ("/");
712   }
713   ret = GNUNET_DISK_directory_create (rdir);
714   if ((GNUNET_OK == ret) && (0 != access (rdir, W_OK)))
715     ret = GNUNET_NO;
716   eno = errno;
717   GNUNET_free (rdir);
718   errno = eno;
719   return ret;
720 }
721
722
723 /**
724  * Read the contents of a binary file into a buffer.
725  *
726  * @param h handle to an open file
727  * @param result the buffer to write the result to
728  * @param len the maximum number of bytes to read
729  * @return the number of bytes read on success, #GNUNET_SYSERR on failure
730  */
731 ssize_t
732 GNUNET_DISK_file_read (const struct GNUNET_DISK_FileHandle *h,
733                        void *result,
734                        size_t len)
735 {
736   if (NULL == h)
737   {
738     errno = EINVAL;
739     return GNUNET_SYSERR;
740   }
741
742   return read (h->fd, result, len);
743 }
744
745
746 /**
747  * Read the contents of a binary file into a buffer.
748  * Guarantees not to block (returns GNUNET_SYSERR and sets errno to EAGAIN
749  * when no data can be read).
750  *
751  * @param h handle to an open file
752  * @param result the buffer to write the result to
753  * @param len the maximum number of bytes to read
754  * @return the number of bytes read on success, #GNUNET_SYSERR on failure
755  */
756 ssize_t
757 GNUNET_DISK_file_read_non_blocking (const struct GNUNET_DISK_FileHandle *h,
758                                     void *result,
759                                     size_t len)
760 {
761   if (NULL == h)
762   {
763     errno = EINVAL;
764     return GNUNET_SYSERR;
765   }
766
767   int flags;
768   ssize_t ret;
769
770   /* set to non-blocking, read, then set back */
771   flags = fcntl (h->fd, F_GETFL);
772   if (0 == (flags & O_NONBLOCK))
773     (void) fcntl (h->fd, F_SETFL, flags | O_NONBLOCK);
774   ret = read (h->fd, result, len);
775   if (0 == (flags & O_NONBLOCK))
776   {
777     int eno = errno;
778     (void) fcntl (h->fd, F_SETFL, flags);
779     errno = eno;
780   }
781   return ret;
782 }
783
784
785 /**
786  * Read the contents of a binary file into a buffer.
787  *
788  * @param fn file name
789  * @param result the buffer to write the result to
790  * @param len the maximum number of bytes to read
791  * @return number of bytes read, #GNUNET_SYSERR on failure
792  */
793 ssize_t
794 GNUNET_DISK_fn_read (const char *fn, void *result, size_t len)
795 {
796   struct GNUNET_DISK_FileHandle *fh;
797   ssize_t ret;
798   int eno;
799
800   fh = GNUNET_DISK_file_open (fn, GNUNET_DISK_OPEN_READ, GNUNET_DISK_PERM_NONE);
801   if (NULL == fh)
802     return GNUNET_SYSERR;
803   ret = GNUNET_DISK_file_read (fh, result, len);
804   eno = errno;
805   GNUNET_assert (GNUNET_OK == GNUNET_DISK_file_close (fh));
806   errno = eno;
807   return ret;
808 }
809
810
811 /**
812  * Write a buffer to a file.
813  *
814  * @param h handle to open file
815  * @param buffer the data to write
816  * @param n number of bytes to write
817  * @return number of bytes written on success, #GNUNET_SYSERR on error
818  */
819 ssize_t
820 GNUNET_DISK_file_write (const struct GNUNET_DISK_FileHandle *h,
821                         const void *buffer,
822                         size_t n)
823 {
824   if (NULL == h)
825   {
826     errno = EINVAL;
827     return GNUNET_SYSERR;
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 == '?') ||
1149         (c ==
1150          '"')
1151         ||
1152         (c == '<') || (c == '>') || (c == '|') )
1153     {
1154       *idx = '_';
1155     }
1156   }
1157 }
1158
1159
1160 /**
1161  * @brief Change owner of a file
1162  *
1163  * @param filename name of file to change the owner of
1164  * @param user name of the new owner
1165  * @return #GNUNET_OK on success, #GNUNET_SYSERR on failure
1166  */
1167 int
1168 GNUNET_DISK_file_change_owner (const char *filename, const char *user)
1169 {
1170   struct passwd *pws;
1171
1172   pws = getpwnam (user);
1173   if (NULL == pws)
1174   {
1175     LOG (GNUNET_ERROR_TYPE_ERROR,
1176          _ ("Cannot obtain information about user `%s': %s\n"),
1177          user,
1178          strerror (errno));
1179     return GNUNET_SYSERR;
1180   }
1181   if (0 != chown (filename, pws->pw_uid, pws->pw_gid))
1182   {
1183     LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "chown", filename);
1184     return GNUNET_SYSERR;
1185   }
1186   return GNUNET_OK;
1187 }
1188
1189
1190 /**
1191  * Lock a part of a file
1192  *
1193  * @param fh file handle
1194  * @param lock_start absolute position from where to lock
1195  * @param lock_end absolute position until where to lock
1196  * @param excl #GNUNET_YES for an exclusive lock
1197  * @return #GNUNET_OK on success, #GNUNET_SYSERR on error
1198  */
1199 int
1200 GNUNET_DISK_file_lock (struct GNUNET_DISK_FileHandle *fh,
1201                        off_t lock_start,
1202                        off_t lock_end,
1203                        int excl)
1204 {
1205   if (fh == NULL)
1206   {
1207     errno = EINVAL;
1208     return GNUNET_SYSERR;
1209   }
1210
1211   struct flock fl;
1212
1213   memset (&fl, 0, sizeof(struct flock));
1214   fl.l_type = excl ? F_WRLCK : F_RDLCK;
1215   fl.l_whence = SEEK_SET;
1216   fl.l_start = lock_start;
1217   fl.l_len = lock_end;
1218
1219   return fcntl (fh->fd, F_SETLK, &fl) != 0 ? GNUNET_SYSERR : GNUNET_OK;
1220 }
1221
1222
1223 /**
1224  * Unlock a part of a file
1225  *
1226  * @param fh file handle
1227  * @param unlock_start absolute position from where to unlock
1228  * @param unlock_end absolute position until where to unlock
1229  * @return #GNUNET_OK on success, #GNUNET_SYSERR on error
1230  */
1231 int
1232 GNUNET_DISK_file_unlock (struct GNUNET_DISK_FileHandle *fh,
1233                          off_t unlock_start,
1234                          off_t unlock_end)
1235 {
1236   if (fh == NULL)
1237   {
1238     errno = EINVAL;
1239     return GNUNET_SYSERR;
1240   }
1241
1242   struct flock fl;
1243
1244   memset (&fl, 0, sizeof(struct flock));
1245   fl.l_type = F_UNLCK;
1246   fl.l_whence = SEEK_SET;
1247   fl.l_start = unlock_start;
1248   fl.l_len = unlock_end;
1249
1250   return fcntl (fh->fd, F_SETLK, &fl) != 0 ? GNUNET_SYSERR : GNUNET_OK;
1251 }
1252
1253
1254 /**
1255  * Open a file.  Note that the access permissions will only be
1256  * used if a new file is created and if the underlying operating
1257  * system supports the given permissions.
1258  *
1259  * @param fn file name to be opened
1260  * @param flags opening flags, a combination of GNUNET_DISK_OPEN_xxx bit flags
1261  * @param perm permissions for the newly created file, use
1262  *             #GNUNET_DISK_PERM_NONE if a file could not be created by this
1263  *             call (because of flags)
1264  * @return IO handle on success, NULL on error
1265  */
1266 struct GNUNET_DISK_FileHandle *
1267 GNUNET_DISK_file_open (const char *fn,
1268                        enum GNUNET_DISK_OpenFlags flags,
1269                        enum GNUNET_DISK_AccessPermissions perm)
1270 {
1271   char *expfn;
1272   struct GNUNET_DISK_FileHandle *ret;
1273
1274   int oflags;
1275   int mode;
1276   int fd;
1277
1278   expfn = GNUNET_STRINGS_filename_expand (fn);
1279   if (NULL == expfn)
1280     return NULL;
1281
1282   mode = 0;
1283   if (GNUNET_DISK_OPEN_READWRITE == (flags & GNUNET_DISK_OPEN_READWRITE))
1284     oflags = O_RDWR; /* note: O_RDWR is NOT always O_RDONLY | O_WRONLY */
1285   else if (flags & GNUNET_DISK_OPEN_READ)
1286     oflags = O_RDONLY;
1287   else if (flags & GNUNET_DISK_OPEN_WRITE)
1288     oflags = O_WRONLY;
1289   else
1290   {
1291     GNUNET_break (0);
1292     GNUNET_free (expfn);
1293     return NULL;
1294   }
1295   if (flags & GNUNET_DISK_OPEN_FAILIFEXISTS)
1296     oflags |= (O_CREAT | O_EXCL);
1297   if (flags & GNUNET_DISK_OPEN_TRUNCATE)
1298     oflags |= O_TRUNC;
1299   if (flags & GNUNET_DISK_OPEN_APPEND)
1300     oflags |= O_APPEND;
1301   if (GNUNET_NO == GNUNET_DISK_file_test (fn))
1302   {
1303     if (flags & GNUNET_DISK_OPEN_CREATE)
1304     {
1305       (void) GNUNET_DISK_directory_create_for_file (expfn);
1306       oflags |= O_CREAT;
1307       mode = translate_unix_perms (perm);
1308     }
1309   }
1310
1311   fd = open (expfn,
1312              oflags
1313 #if O_CLOEXEC
1314              | O_CLOEXEC
1315 #endif
1316              | O_LARGEFILE,
1317              mode);
1318   if (fd == -1)
1319   {
1320     if (0 == (flags & GNUNET_DISK_OPEN_FAILIFEXISTS))
1321       LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "open", expfn);
1322     else
1323       LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_DEBUG, "open", expfn);
1324     GNUNET_free (expfn);
1325     return NULL;
1326   }
1327
1328   ret = GNUNET_new (struct GNUNET_DISK_FileHandle);
1329
1330   ret->fd = fd;
1331
1332   GNUNET_free (expfn);
1333   return ret;
1334 }
1335
1336
1337 /**
1338  * Close an open file.
1339  *
1340  * @param h file handle
1341  * @return #GNUNET_OK on success, #GNUNET_SYSERR otherwise
1342  */
1343 int
1344 GNUNET_DISK_file_close (struct GNUNET_DISK_FileHandle *h)
1345 {
1346   int ret;
1347
1348   if (h == NULL)
1349   {
1350     errno = EINVAL;
1351     return GNUNET_SYSERR;
1352   }
1353
1354   ret = GNUNET_OK;
1355
1356   if (close (h->fd) != 0)
1357   {
1358     LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING, "close");
1359     ret = GNUNET_SYSERR;
1360   }
1361
1362   GNUNET_free (h);
1363   return ret;
1364 }
1365
1366
1367 /**
1368  * Get a handle from a native integer FD.
1369  *
1370  * @param fno native integer file descriptor
1371  * @return file handle corresponding to the descriptor, NULL on error
1372  */
1373 struct GNUNET_DISK_FileHandle *
1374 GNUNET_DISK_get_handle_from_int_fd (int fno)
1375 {
1376   struct GNUNET_DISK_FileHandle *fh;
1377
1378   if ((((off_t) -1) == lseek (fno, 0, SEEK_CUR)) && (EBADF == errno))
1379     return NULL; /* invalid FD */
1380
1381   fh = GNUNET_new (struct GNUNET_DISK_FileHandle);
1382
1383   fh->fd = fno;
1384
1385   return fh;
1386 }
1387
1388
1389 /**
1390  * Get a handle from a native streaming FD.
1391  *
1392  * @param fd native streaming file descriptor
1393  * @return file handle corresponding to the descriptor
1394  */
1395 struct GNUNET_DISK_FileHandle *
1396 GNUNET_DISK_get_handle_from_native (FILE *fd)
1397 {
1398   int fno;
1399
1400   fno = fileno (fd);
1401   if (-1 == fno)
1402     return NULL;
1403
1404   return GNUNET_DISK_get_handle_from_int_fd (fno);
1405 }
1406
1407
1408 /**
1409  * Handle for a memory-mapping operation.
1410  */
1411 struct GNUNET_DISK_MapHandle
1412 {
1413   /**
1414    * Address where the map is in memory.
1415    */
1416   void *addr;
1417
1418   /**
1419    * Number of bytes mapped.
1420    */
1421   size_t len;
1422 };
1423
1424
1425 #ifndef MAP_FAILED
1426 #define MAP_FAILED ((void *) -1)
1427 #endif
1428
1429 /**
1430  * Map a file into memory
1431  *
1432  * @param h open file handle
1433  * @param m handle to the new mapping
1434  * @param access access specification, GNUNET_DISK_MAP_TYPE_xxx
1435  * @param len size of the mapping
1436  * @return pointer to the mapped memory region, NULL on failure
1437  */
1438 void *
1439 GNUNET_DISK_file_map (const struct GNUNET_DISK_FileHandle *h,
1440                       struct GNUNET_DISK_MapHandle **m,
1441                       enum GNUNET_DISK_MapType access,
1442                       size_t len)
1443 {
1444   if (NULL == h)
1445   {
1446     errno = EINVAL;
1447     return NULL;
1448   }
1449
1450   int prot;
1451
1452   prot = 0;
1453   if (access & GNUNET_DISK_MAP_TYPE_READ)
1454     prot = PROT_READ;
1455   if (access & GNUNET_DISK_MAP_TYPE_WRITE)
1456     prot |= PROT_WRITE;
1457   *m = GNUNET_new (struct GNUNET_DISK_MapHandle);
1458   (*m)->addr = mmap (NULL, len, prot, MAP_SHARED, h->fd, 0);
1459   GNUNET_assert (NULL != (*m)->addr);
1460   if (MAP_FAILED == (*m)->addr)
1461   {
1462     GNUNET_free (*m);
1463     return NULL;
1464   }
1465   (*m)->len = len;
1466   return (*m)->addr;
1467 }
1468
1469
1470 /**
1471  * Unmap a file
1472  * @param h mapping handle
1473  * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
1474  */
1475 int
1476 GNUNET_DISK_file_unmap (struct GNUNET_DISK_MapHandle *h)
1477 {
1478   int ret;
1479
1480   if (h == NULL)
1481   {
1482     errno = EINVAL;
1483     return GNUNET_SYSERR;
1484   }
1485
1486   ret = munmap (h->addr, h->len) != -1 ? GNUNET_OK : GNUNET_SYSERR;
1487
1488   GNUNET_free (h);
1489   return ret;
1490 }
1491
1492
1493 /**
1494  * Write file changes to disk
1495  * @param h handle to an open file
1496  * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
1497  */
1498 int
1499 GNUNET_DISK_file_sync (const struct GNUNET_DISK_FileHandle *h)
1500 {
1501   if (h == NULL)
1502   {
1503     errno = EINVAL;
1504     return GNUNET_SYSERR;
1505   }
1506
1507 #if ! defined(__linux__) || ! defined(GNU)
1508   return fsync (h->fd) == -1 ? GNUNET_SYSERR : GNUNET_OK;
1509 #else
1510   return fdatasync (h->fd) == -1 ? GNUNET_SYSERR : GNUNET_OK;
1511 #endif
1512 }
1513
1514
1515 /**
1516  * Creates an interprocess channel
1517  *
1518  * @param blocking_read creates an asynchronous pipe for reading if set to GNUNET_NO
1519  * @param blocking_write creates an asynchronous pipe for writing if set to GNUNET_NO
1520  * @param inherit_read inherit the parent processes stdin (only for windows)
1521  * @param inherit_write inherit the parent processes stdout (only for windows)
1522  * @return handle to the new pipe, NULL on error
1523  */
1524 struct GNUNET_DISK_PipeHandle *
1525 GNUNET_DISK_pipe (int blocking_read,
1526                   int blocking_write,
1527                   int inherit_read,
1528                   int inherit_write)
1529 {
1530   int fd[2];
1531   int ret;
1532   int eno;
1533
1534   (void) inherit_read;
1535   (void) inherit_write;
1536   ret = pipe (fd);
1537   if (ret == -1)
1538   {
1539     eno = errno;
1540     LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR, "pipe");
1541     errno = eno;
1542     return NULL;
1543   }
1544   return GNUNET_DISK_pipe_from_fd (blocking_read, blocking_write, fd);
1545 }
1546
1547
1548 /**
1549  * Creates a pipe object from a couple of file descriptors.
1550  * Useful for wrapping existing pipe FDs.
1551  *
1552  * @param blocking_read creates an asynchronous pipe for reading if set to GNUNET_NO
1553  * @param blocking_write creates an asynchronous pipe for writing if set to GNUNET_NO
1554  * @param fd an array of two fd values. One of them may be -1 for read-only or write-only pipes
1555  *
1556  * @return handle to the new pipe, NULL on error
1557  */
1558 struct GNUNET_DISK_PipeHandle *
1559 GNUNET_DISK_pipe_from_fd (int blocking_read, int blocking_write, int fd[2])
1560 {
1561   struct GNUNET_DISK_PipeHandle *p;
1562
1563   p = GNUNET_new (struct GNUNET_DISK_PipeHandle);
1564
1565   int ret;
1566   int flags;
1567   int eno = 0; /* make gcc happy */
1568
1569   ret = 0;
1570   if (fd[0] >= 0)
1571   {
1572     p->fd[0] = GNUNET_new (struct GNUNET_DISK_FileHandle);
1573     p->fd[0]->fd = fd[0];
1574     if (! blocking_read)
1575     {
1576       flags = fcntl (fd[0], F_GETFL);
1577       flags |= O_NONBLOCK;
1578       if (0 > fcntl (fd[0], F_SETFL, flags))
1579       {
1580         ret = -1;
1581         eno = errno;
1582       }
1583     }
1584     flags = fcntl (fd[0], F_GETFD);
1585     flags |= FD_CLOEXEC;
1586     if (0 > fcntl (fd[0], F_SETFD, flags))
1587     {
1588       ret = -1;
1589       eno = errno;
1590     }
1591   }
1592
1593   if (fd[1] >= 0)
1594   {
1595     p->fd[1] = GNUNET_new (struct GNUNET_DISK_FileHandle);
1596     p->fd[1]->fd = fd[1];
1597     if (! blocking_write)
1598     {
1599       flags = fcntl (fd[1], F_GETFL);
1600       flags |= O_NONBLOCK;
1601       if (0 > fcntl (fd[1], F_SETFL, flags))
1602       {
1603         ret = -1;
1604         eno = errno;
1605       }
1606     }
1607     flags = fcntl (fd[1], F_GETFD);
1608     flags |= FD_CLOEXEC;
1609     if (0 > fcntl (fd[1], F_SETFD, flags))
1610     {
1611       ret = -1;
1612       eno = errno;
1613     }
1614   }
1615   if (ret == -1)
1616   {
1617     errno = eno;
1618     LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR, "fcntl");
1619     if (p->fd[0]->fd >= 0)
1620       GNUNET_break (0 == close (p->fd[0]->fd));
1621     if (p->fd[1]->fd >= 0)
1622       GNUNET_break (0 == close (p->fd[1]->fd));
1623     GNUNET_free_non_null (p->fd[0]);
1624     GNUNET_free_non_null (p->fd[1]);
1625     GNUNET_free (p);
1626     errno = eno;
1627     return NULL;
1628   }
1629
1630   return p;
1631 }
1632
1633
1634 /**
1635  * Closes an interprocess channel
1636  *
1637  * @param p pipe to close
1638  * @param end which end of the pipe to close
1639  * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
1640  */
1641 int
1642 GNUNET_DISK_pipe_close_end (struct GNUNET_DISK_PipeHandle *p,
1643                             enum GNUNET_DISK_PipeEnd end)
1644 {
1645   int ret = GNUNET_OK;
1646
1647   if (end == GNUNET_DISK_PIPE_END_READ)
1648   {
1649     if (p->fd[0])
1650     {
1651       ret = GNUNET_DISK_file_close (p->fd[0]);
1652       p->fd[0] = NULL;
1653     }
1654   }
1655   else if (end == GNUNET_DISK_PIPE_END_WRITE)
1656   {
1657     if (p->fd[1])
1658     {
1659       ret = GNUNET_DISK_file_close (p->fd[1]);
1660       p->fd[1] = NULL;
1661     }
1662   }
1663
1664   return ret;
1665 }
1666
1667
1668 /**
1669  * Detaches one of the ends from the pipe.
1670  * Detached end is a fully-functional FileHandle, it will
1671  * not be affected by anything you do with the pipe afterwards.
1672  * Each end of a pipe can only be detched from it once (i.e.
1673  * it is not duplicated).
1674  *
1675  * @param p pipe to detach an end from
1676  * @param end which end of the pipe to detach
1677  * @return Detached end on success, NULL on failure
1678  * (or if that end is not present or is closed).
1679  */
1680 struct GNUNET_DISK_FileHandle *
1681 GNUNET_DISK_pipe_detach_end (struct GNUNET_DISK_PipeHandle *p,
1682                              enum GNUNET_DISK_PipeEnd end)
1683 {
1684   struct GNUNET_DISK_FileHandle *ret = NULL;
1685
1686   if (end == GNUNET_DISK_PIPE_END_READ)
1687   {
1688     if (p->fd[0])
1689     {
1690       ret = p->fd[0];
1691       p->fd[0] = NULL;
1692     }
1693   }
1694   else if (end == GNUNET_DISK_PIPE_END_WRITE)
1695   {
1696     if (p->fd[1])
1697     {
1698       ret = p->fd[1];
1699       p->fd[1] = NULL;
1700     }
1701   }
1702
1703   return ret;
1704 }
1705
1706
1707 /**
1708  * Closes an interprocess channel
1709  *
1710  * @param p pipe to close
1711  * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
1712  */
1713 int
1714 GNUNET_DISK_pipe_close (struct GNUNET_DISK_PipeHandle *p)
1715 {
1716   int ret = GNUNET_OK;
1717
1718   int read_end_close;
1719   int write_end_close;
1720   int read_end_close_errno;
1721   int write_end_close_errno;
1722
1723   read_end_close = GNUNET_DISK_pipe_close_end (p, GNUNET_DISK_PIPE_END_READ);
1724   read_end_close_errno = errno;
1725   write_end_close = GNUNET_DISK_pipe_close_end (p, GNUNET_DISK_PIPE_END_WRITE);
1726   write_end_close_errno = errno;
1727   GNUNET_free (p);
1728
1729   if (GNUNET_OK != read_end_close)
1730   {
1731     errno = read_end_close_errno;
1732     ret = read_end_close;
1733   }
1734   else if (GNUNET_OK != write_end_close)
1735   {
1736     errno = write_end_close_errno;
1737     ret = write_end_close;
1738   }
1739
1740   return ret;
1741 }
1742
1743
1744 /**
1745  * Get the handle to a particular pipe end
1746  *
1747  * @param p pipe
1748  * @param n end to access
1749  * @return handle for the respective end
1750  */
1751 const struct GNUNET_DISK_FileHandle *
1752 GNUNET_DISK_pipe_handle (const struct GNUNET_DISK_PipeHandle *p,
1753                          enum GNUNET_DISK_PipeEnd n)
1754 {
1755   switch (n)
1756   {
1757   case GNUNET_DISK_PIPE_END_READ:
1758   case GNUNET_DISK_PIPE_END_WRITE:
1759     return p->fd[n];
1760
1761   default:
1762     GNUNET_break (0);
1763     return NULL;
1764   }
1765 }
1766
1767
1768 /**
1769  * Retrieve OS file handle
1770  * @internal
1771  * @param fh GNUnet file descriptor
1772  * @param dst destination buffer
1773  * @param dst_len length of dst
1774  * @return #GNUNET_OK on success, #GNUNET_SYSERR otherwise
1775  */
1776 int
1777 GNUNET_DISK_internal_file_handle_ (const struct GNUNET_DISK_FileHandle *fh,
1778                                    void *dst,
1779                                    size_t dst_len)
1780 {
1781   if (NULL == fh)
1782     return GNUNET_SYSERR;
1783
1784   if (dst_len < sizeof(int))
1785     return GNUNET_SYSERR;
1786   *((int *) dst) = fh->fd;
1787
1788   return GNUNET_OK;
1789 }
1790
1791
1792 /**
1793  * Helper function for #GNUNET_DISK_purge_cfg_dir.
1794  *
1795  * @param cls a `const char *` with the option to purge
1796  * @param cfg our configuration
1797  * @return #GNUNET_OK on success
1798  */
1799 static int
1800 purge_cfg_dir (void *cls, const struct GNUNET_CONFIGURATION_Handle *cfg)
1801 {
1802   const char *option = cls;
1803   char *tmpname;
1804
1805   if (GNUNET_OK !=
1806       GNUNET_CONFIGURATION_get_value_filename (cfg, "PATHS", option, &tmpname))
1807   {
1808     GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, "PATHS", option);
1809     return GNUNET_NO;
1810   }
1811   if (GNUNET_SYSERR == GNUNET_DISK_directory_remove (tmpname))
1812   {
1813     GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR, "remove", tmpname);
1814     GNUNET_free (tmpname);
1815     return GNUNET_OK;
1816   }
1817   GNUNET_free (tmpname);
1818   return GNUNET_OK;
1819 }
1820
1821
1822 /**
1823  * Remove the directory given under @a option in
1824  * section [PATHS] in configuration under @a cfg_filename
1825  *
1826  * @param cfg_filename configuration file to parse
1827  * @param option option with the dir name to purge
1828  */
1829 void
1830 GNUNET_DISK_purge_cfg_dir (const char *cfg_filename, const char *option)
1831 {
1832   GNUNET_break (GNUNET_OK ==
1833                 GNUNET_CONFIGURATION_parse_and_run (cfg_filename,
1834                                                     &purge_cfg_dir,
1835                                                     (void *) option));
1836 }
1837
1838
1839 /* end of disk.c */