tighten formatting rules
[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
831   return write (h->fd, buffer, n);
832 }
833
834
835 /**
836  * Write a buffer to a file, blocking, if necessary.
837  *
838  * @param h handle to open file
839  * @param buffer the data to write
840  * @param n number of bytes to write
841  * @return number of bytes written on success, #GNUNET_SYSERR on error
842  */
843 ssize_t
844 GNUNET_DISK_file_write_blocking (const struct GNUNET_DISK_FileHandle *h,
845                                  const void *buffer,
846                                  size_t n)
847 {
848   if (NULL == h)
849   {
850     errno = EINVAL;
851     return GNUNET_SYSERR;
852   }
853
854
855   int flags;
856   ssize_t ret;
857
858   /* set to blocking, write, then set back */
859   flags = fcntl (h->fd, F_GETFL);
860   if (0 != (flags & O_NONBLOCK))
861     (void) fcntl (h->fd, F_SETFL, flags - O_NONBLOCK);
862   ret = write (h->fd, buffer, n);
863   if (0 == (flags & O_NONBLOCK))
864     (void) fcntl (h->fd, F_SETFL, flags);
865   return ret;
866 }
867
868
869 /**
870  * Write a buffer to a file.  If the file is longer than the
871  * number of bytes that will be written, it will be truncated.
872  *
873  * @param fn file name
874  * @param buffer the data to write
875  * @param n number of bytes to write
876  * @param mode file permissions
877  * @return number of bytes written on success, #GNUNET_SYSERR on error
878  */
879 ssize_t
880 GNUNET_DISK_fn_write (const char *fn,
881                       const void *buffer,
882                       size_t n,
883                       enum GNUNET_DISK_AccessPermissions mode)
884 {
885   struct GNUNET_DISK_FileHandle *fh;
886   ssize_t ret;
887
888   fh =
889     GNUNET_DISK_file_open (fn,
890                            GNUNET_DISK_OPEN_WRITE | GNUNET_DISK_OPEN_TRUNCATE
891                            | GNUNET_DISK_OPEN_CREATE,
892                            mode);
893   if (! fh)
894     return GNUNET_SYSERR;
895   ret = GNUNET_DISK_file_write (fh, buffer, n);
896   GNUNET_assert (GNUNET_OK == GNUNET_DISK_file_close (fh));
897   return ret;
898 }
899
900
901 /**
902  * Scan a directory for files.
903  *
904  * @param dir_name the name of the directory
905  * @param callback the method to call for each file,
906  *        can be NULL, in that case, we only count
907  * @param callback_cls closure for @a callback
908  * @return the number of files found, #GNUNET_SYSERR on error or
909  *         ieration aborted by callback returning #GNUNET_SYSERR
910  */
911 int
912 GNUNET_DISK_directory_scan (const char *dir_name,
913                             GNUNET_FileNameCallback callback,
914                             void *callback_cls)
915 {
916   DIR *dinfo;
917   struct dirent *finfo;
918   struct stat istat;
919   int count = 0;
920   int ret;
921   char *name;
922   char *dname;
923   unsigned int name_len;
924   unsigned int n_size;
925
926   GNUNET_assert (NULL != dir_name);
927   dname = GNUNET_STRINGS_filename_expand (dir_name);
928   if (NULL == dname)
929     return GNUNET_SYSERR;
930   while ((strlen (dname) > 0) && (dname[strlen (dname) - 1] == DIR_SEPARATOR))
931     dname[strlen (dname) - 1] = '\0';
932   if (0 != stat (dname, &istat))
933   {
934     LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "stat", dname);
935     GNUNET_free (dname);
936     return GNUNET_SYSERR;
937   }
938   if (! S_ISDIR (istat.st_mode))
939   {
940     LOG (GNUNET_ERROR_TYPE_WARNING,
941          _ ("Expected `%s' to be a directory!\n"),
942          dir_name);
943     GNUNET_free (dname);
944     return GNUNET_SYSERR;
945   }
946   errno = 0;
947   dinfo = opendir (dname);
948   if ((EACCES == errno) || (NULL == dinfo))
949   {
950     LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "opendir", dname);
951     if (NULL != dinfo)
952       closedir (dinfo);
953     GNUNET_free (dname);
954     return GNUNET_SYSERR;
955   }
956   name_len = 256;
957   n_size = strlen (dname) + name_len + strlen (DIR_SEPARATOR_STR) + 1;
958   name = GNUNET_malloc (n_size);
959   while (NULL != (finfo = readdir (dinfo)))
960   {
961     if ((0 == strcmp (finfo->d_name, ".")) ||
962         (0 == strcmp (finfo->d_name, "..")))
963       continue;
964     if (NULL != callback)
965     {
966       if (name_len < strlen (finfo->d_name))
967       {
968         GNUNET_free (name);
969         name_len = strlen (finfo->d_name);
970         n_size = strlen (dname) + name_len + strlen (DIR_SEPARATOR_STR) + 1;
971         name = GNUNET_malloc (n_size);
972       }
973       /* dname can end in "/" only if dname == "/";
974        * if dname does not end in "/", we need to add
975        * a "/" (otherwise, we must not!) */
976       GNUNET_snprintf (name,
977                        n_size,
978                        "%s%s%s",
979                        dname,
980                        (0 == strcmp (dname, DIR_SEPARATOR_STR))
981                        ? ""
982                        : DIR_SEPARATOR_STR,
983                        finfo->d_name);
984       ret = callback (callback_cls, name);
985       if (GNUNET_OK != ret)
986       {
987         closedir (dinfo);
988         GNUNET_free (name);
989         GNUNET_free (dname);
990         if (GNUNET_NO == ret)
991           return count;
992         return GNUNET_SYSERR;
993       }
994     }
995     count++;
996   }
997   closedir (dinfo);
998   GNUNET_free (name);
999   GNUNET_free (dname);
1000   return count;
1001 }
1002
1003
1004 /**
1005  * Function that removes the given directory by calling
1006  * #GNUNET_DISK_directory_remove().
1007  *
1008  * @param unused not used
1009  * @param fn directory to remove
1010  * @return #GNUNET_OK
1011  */
1012 static int
1013 remove_helper (void *unused, const char *fn)
1014 {
1015   (void) unused;
1016   (void) GNUNET_DISK_directory_remove (fn);
1017   return GNUNET_OK;
1018 }
1019
1020
1021 /**
1022  * Remove all files in a directory (rm -r). Call with
1023  * caution.
1024  *
1025  * @param filename the file to remove
1026  * @return #GNUNET_OK on success, #GNUNET_SYSERR on error
1027  */
1028 int
1029 GNUNET_DISK_directory_remove (const char *filename)
1030 {
1031   struct stat istat;
1032
1033   if (NULL == filename)
1034   {
1035     GNUNET_break (0);
1036     return GNUNET_SYSERR;
1037   }
1038   if (0 != lstat (filename, &istat))
1039     return GNUNET_NO; /* file may not exist... */
1040   (void) chmod (filename, S_IWUSR | S_IRUSR | S_IXUSR);
1041   if (0 == unlink (filename))
1042     return GNUNET_OK;
1043   if ((errno != EISDIR) &&
1044       /* EISDIR is not sufficient in all cases, e.g.
1045       * sticky /tmp directory may result in EPERM on BSD.
1046       * So we also explicitly check "isDirectory" */
1047       (GNUNET_YES != GNUNET_DISK_directory_test (filename, GNUNET_YES)))
1048   {
1049     LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "rmdir", filename);
1050     return GNUNET_SYSERR;
1051   }
1052   if (GNUNET_SYSERR ==
1053       GNUNET_DISK_directory_scan (filename, &remove_helper, NULL))
1054     return GNUNET_SYSERR;
1055   if (0 != rmdir (filename))
1056   {
1057     LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "rmdir", filename);
1058     return GNUNET_SYSERR;
1059   }
1060   return GNUNET_OK;
1061 }
1062
1063
1064 /**
1065  * Copy a file.
1066  *
1067  * @param src file to copy
1068  * @param dst destination file name
1069  * @return #GNUNET_OK on success, #GNUNET_SYSERR on error
1070  */
1071 int
1072 GNUNET_DISK_file_copy (const char *src, const char *dst)
1073 {
1074   char *buf;
1075   uint64_t pos;
1076   uint64_t size;
1077   size_t len;
1078   ssize_t sret;
1079   struct GNUNET_DISK_FileHandle *in;
1080   struct GNUNET_DISK_FileHandle *out;
1081
1082   if (GNUNET_OK != GNUNET_DISK_file_size (src, &size, GNUNET_YES, GNUNET_YES))
1083   {
1084     GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR, "stat", src);
1085     return GNUNET_SYSERR;
1086   }
1087   pos = 0;
1088   in =
1089     GNUNET_DISK_file_open (src, GNUNET_DISK_OPEN_READ, GNUNET_DISK_PERM_NONE);
1090   if (! in)
1091   {
1092     GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR, "open", src);
1093     return GNUNET_SYSERR;
1094   }
1095   out =
1096     GNUNET_DISK_file_open (dst,
1097                            GNUNET_DISK_OPEN_WRITE | GNUNET_DISK_OPEN_CREATE
1098                            | GNUNET_DISK_OPEN_FAILIFEXISTS,
1099                            GNUNET_DISK_PERM_USER_READ
1100                            | GNUNET_DISK_PERM_USER_WRITE
1101                            | GNUNET_DISK_PERM_GROUP_READ
1102                            | GNUNET_DISK_PERM_GROUP_WRITE);
1103   if (! out)
1104   {
1105     GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR, "open", dst);
1106     GNUNET_DISK_file_close (in);
1107     return GNUNET_SYSERR;
1108   }
1109   buf = GNUNET_malloc (COPY_BLK_SIZE);
1110   while (pos < size)
1111   {
1112     len = COPY_BLK_SIZE;
1113     if (len > size - pos)
1114       len = size - pos;
1115     sret = GNUNET_DISK_file_read (in, buf, len);
1116     if ((sret < 0) || (len != (size_t) sret))
1117       goto FAIL;
1118     sret = GNUNET_DISK_file_write (out, buf, len);
1119     if ((sret < 0) || (len != (size_t) sret))
1120       goto FAIL;
1121     pos += len;
1122   }
1123   GNUNET_free (buf);
1124   GNUNET_DISK_file_close (in);
1125   GNUNET_DISK_file_close (out);
1126   return GNUNET_OK;
1127 FAIL:
1128   GNUNET_free (buf);
1129   GNUNET_DISK_file_close (in);
1130   GNUNET_DISK_file_close (out);
1131   return GNUNET_SYSERR;
1132 }
1133
1134
1135 /**
1136  * @brief Removes special characters as ':' from a filename.
1137  * @param fn the filename to canonicalize
1138  */
1139 void
1140 GNUNET_DISK_filename_canonicalize (char *fn)
1141 {
1142   char *idx;
1143   char c;
1144
1145   for (idx = fn; *idx; idx++)
1146   {
1147     c = *idx;
1148
1149     if ((c == '/') || (c == '\\') || (c == ':') || (c == '*') || (c == '?') ||
1150         (c ==
1151          '"')
1152         ||
1153         (c == '<') || (c == '>') || (c == '|') )
1154     {
1155       *idx = '_';
1156     }
1157   }
1158 }
1159
1160
1161 /**
1162  * @brief Change owner of a file
1163  *
1164  * @param filename name of file to change the owner of
1165  * @param user name of the new owner
1166  * @return #GNUNET_OK on success, #GNUNET_SYSERR on failure
1167  */
1168 int
1169 GNUNET_DISK_file_change_owner (const char *filename, const char *user)
1170 {
1171   struct passwd *pws;
1172
1173   pws = getpwnam (user);
1174   if (NULL == pws)
1175   {
1176     LOG (GNUNET_ERROR_TYPE_ERROR,
1177          _ ("Cannot obtain information about user `%s': %s\n"),
1178          user,
1179          strerror (errno));
1180     return GNUNET_SYSERR;
1181   }
1182   if (0 != chown (filename, pws->pw_uid, pws->pw_gid))
1183   {
1184     LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "chown", filename);
1185     return GNUNET_SYSERR;
1186   }
1187   return GNUNET_OK;
1188 }
1189
1190
1191 /**
1192  * Lock a part of a file
1193  *
1194  * @param fh file handle
1195  * @param lock_start absolute position from where to lock
1196  * @param lock_end absolute position until where to lock
1197  * @param excl #GNUNET_YES for an exclusive lock
1198  * @return #GNUNET_OK on success, #GNUNET_SYSERR on error
1199  */
1200 int
1201 GNUNET_DISK_file_lock (struct GNUNET_DISK_FileHandle *fh,
1202                        off_t lock_start,
1203                        off_t lock_end,
1204                        int excl)
1205 {
1206   if (fh == NULL)
1207   {
1208     errno = EINVAL;
1209     return GNUNET_SYSERR;
1210   }
1211
1212   struct flock fl;
1213
1214   memset (&fl, 0, sizeof(struct flock));
1215   fl.l_type = excl ? F_WRLCK : F_RDLCK;
1216   fl.l_whence = SEEK_SET;
1217   fl.l_start = lock_start;
1218   fl.l_len = lock_end;
1219
1220   return fcntl (fh->fd, F_SETLK, &fl) != 0 ? GNUNET_SYSERR : GNUNET_OK;
1221 }
1222
1223
1224 /**
1225  * Unlock a part of a file
1226  *
1227  * @param fh file handle
1228  * @param unlock_start absolute position from where to unlock
1229  * @param unlock_end absolute position until where to unlock
1230  * @return #GNUNET_OK on success, #GNUNET_SYSERR on error
1231  */
1232 int
1233 GNUNET_DISK_file_unlock (struct GNUNET_DISK_FileHandle *fh,
1234                          off_t unlock_start,
1235                          off_t unlock_end)
1236 {
1237   if (fh == NULL)
1238   {
1239     errno = EINVAL;
1240     return GNUNET_SYSERR;
1241   }
1242
1243   struct flock fl;
1244
1245   memset (&fl, 0, sizeof(struct flock));
1246   fl.l_type = F_UNLCK;
1247   fl.l_whence = SEEK_SET;
1248   fl.l_start = unlock_start;
1249   fl.l_len = unlock_end;
1250
1251   return fcntl (fh->fd, F_SETLK, &fl) != 0 ? GNUNET_SYSERR : GNUNET_OK;
1252 }
1253
1254
1255 /**
1256  * Open a file.  Note that the access permissions will only be
1257  * used if a new file is created and if the underlying operating
1258  * system supports the given permissions.
1259  *
1260  * @param fn file name to be opened
1261  * @param flags opening flags, a combination of GNUNET_DISK_OPEN_xxx bit flags
1262  * @param perm permissions for the newly created file, use
1263  *             #GNUNET_DISK_PERM_NONE if a file could not be created by this
1264  *             call (because of flags)
1265  * @return IO handle on success, NULL on error
1266  */
1267 struct GNUNET_DISK_FileHandle *
1268 GNUNET_DISK_file_open (const char *fn,
1269                        enum GNUNET_DISK_OpenFlags flags,
1270                        enum GNUNET_DISK_AccessPermissions perm)
1271 {
1272   char *expfn;
1273   struct GNUNET_DISK_FileHandle *ret;
1274
1275   int oflags;
1276   int mode;
1277   int fd;
1278
1279   expfn = GNUNET_STRINGS_filename_expand (fn);
1280   if (NULL == expfn)
1281     return NULL;
1282
1283   mode = 0;
1284   if (GNUNET_DISK_OPEN_READWRITE == (flags & GNUNET_DISK_OPEN_READWRITE))
1285     oflags = O_RDWR; /* note: O_RDWR is NOT always O_RDONLY | O_WRONLY */
1286   else if (flags & GNUNET_DISK_OPEN_READ)
1287     oflags = O_RDONLY;
1288   else if (flags & GNUNET_DISK_OPEN_WRITE)
1289     oflags = O_WRONLY;
1290   else
1291   {
1292     GNUNET_break (0);
1293     GNUNET_free (expfn);
1294     return NULL;
1295   }
1296   if (flags & GNUNET_DISK_OPEN_FAILIFEXISTS)
1297     oflags |= (O_CREAT | O_EXCL);
1298   if (flags & GNUNET_DISK_OPEN_TRUNCATE)
1299     oflags |= O_TRUNC;
1300   if (flags & GNUNET_DISK_OPEN_APPEND)
1301     oflags |= O_APPEND;
1302   if (GNUNET_NO == GNUNET_DISK_file_test (fn))
1303   {
1304     if (flags & GNUNET_DISK_OPEN_CREATE)
1305     {
1306       (void) GNUNET_DISK_directory_create_for_file (expfn);
1307       oflags |= O_CREAT;
1308       mode = translate_unix_perms (perm);
1309     }
1310   }
1311
1312   fd = open (expfn,
1313              oflags
1314 #if O_CLOEXEC
1315              | O_CLOEXEC
1316 #endif
1317              | O_LARGEFILE,
1318              mode);
1319   if (fd == -1)
1320   {
1321     if (0 == (flags & GNUNET_DISK_OPEN_FAILIFEXISTS))
1322       LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "open", expfn);
1323     else
1324       LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_DEBUG, "open", expfn);
1325     GNUNET_free (expfn);
1326     return NULL;
1327   }
1328
1329   ret = GNUNET_new (struct GNUNET_DISK_FileHandle);
1330
1331   ret->fd = fd;
1332
1333   GNUNET_free (expfn);
1334   return ret;
1335 }
1336
1337
1338 /**
1339  * Close an open file.
1340  *
1341  * @param h file handle
1342  * @return #GNUNET_OK on success, #GNUNET_SYSERR otherwise
1343  */
1344 int
1345 GNUNET_DISK_file_close (struct GNUNET_DISK_FileHandle *h)
1346 {
1347   int ret;
1348
1349   if (h == NULL)
1350   {
1351     errno = EINVAL;
1352     return GNUNET_SYSERR;
1353   }
1354
1355   ret = GNUNET_OK;
1356
1357   if (close (h->fd) != 0)
1358   {
1359     LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING, "close");
1360     ret = GNUNET_SYSERR;
1361   }
1362
1363   GNUNET_free (h);
1364   return ret;
1365 }
1366
1367
1368 /**
1369  * Get a handle from a native integer FD.
1370  *
1371  * @param fno native integer file descriptor
1372  * @return file handle corresponding to the descriptor, NULL on error
1373  */
1374 struct GNUNET_DISK_FileHandle *
1375 GNUNET_DISK_get_handle_from_int_fd (int fno)
1376 {
1377   struct GNUNET_DISK_FileHandle *fh;
1378
1379   if ((((off_t) -1) == lseek (fno, 0, SEEK_CUR)) && (EBADF == errno))
1380     return NULL; /* invalid FD */
1381
1382   fh = GNUNET_new (struct GNUNET_DISK_FileHandle);
1383
1384   fh->fd = fno;
1385
1386   return fh;
1387 }
1388
1389
1390 /**
1391  * Get a handle from a native streaming FD.
1392  *
1393  * @param fd native streaming file descriptor
1394  * @return file handle corresponding to the descriptor
1395  */
1396 struct GNUNET_DISK_FileHandle *
1397 GNUNET_DISK_get_handle_from_native (FILE *fd)
1398 {
1399   int fno;
1400
1401   fno = fileno (fd);
1402   if (-1 == fno)
1403     return NULL;
1404
1405   return GNUNET_DISK_get_handle_from_int_fd (fno);
1406 }
1407
1408
1409 /**
1410  * Handle for a memory-mapping operation.
1411  */
1412 struct GNUNET_DISK_MapHandle
1413 {
1414   /**
1415    * Address where the map is in memory.
1416    */
1417   void *addr;
1418
1419   /**
1420    * Number of bytes mapped.
1421    */
1422   size_t len;
1423 };
1424
1425
1426 #ifndef MAP_FAILED
1427 #define MAP_FAILED ((void *) -1)
1428 #endif
1429
1430 /**
1431  * Map a file into memory
1432  *
1433  * @param h open file handle
1434  * @param m handle to the new mapping
1435  * @param access access specification, GNUNET_DISK_MAP_TYPE_xxx
1436  * @param len size of the mapping
1437  * @return pointer to the mapped memory region, NULL on failure
1438  */
1439 void *
1440 GNUNET_DISK_file_map (const struct GNUNET_DISK_FileHandle *h,
1441                       struct GNUNET_DISK_MapHandle **m,
1442                       enum GNUNET_DISK_MapType access,
1443                       size_t len)
1444 {
1445   if (NULL == h)
1446   {
1447     errno = EINVAL;
1448     return NULL;
1449   }
1450
1451   int prot;
1452
1453   prot = 0;
1454   if (access & GNUNET_DISK_MAP_TYPE_READ)
1455     prot = PROT_READ;
1456   if (access & GNUNET_DISK_MAP_TYPE_WRITE)
1457     prot |= PROT_WRITE;
1458   *m = GNUNET_new (struct GNUNET_DISK_MapHandle);
1459   (*m)->addr = mmap (NULL, len, prot, MAP_SHARED, h->fd, 0);
1460   GNUNET_assert (NULL != (*m)->addr);
1461   if (MAP_FAILED == (*m)->addr)
1462   {
1463     GNUNET_free (*m);
1464     return NULL;
1465   }
1466   (*m)->len = len;
1467   return (*m)->addr;
1468 }
1469
1470
1471 /**
1472  * Unmap a file
1473  * @param h mapping handle
1474  * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
1475  */
1476 int
1477 GNUNET_DISK_file_unmap (struct GNUNET_DISK_MapHandle *h)
1478 {
1479   int ret;
1480
1481   if (h == NULL)
1482   {
1483     errno = EINVAL;
1484     return GNUNET_SYSERR;
1485   }
1486
1487   ret = munmap (h->addr, h->len) != -1 ? GNUNET_OK : GNUNET_SYSERR;
1488
1489   GNUNET_free (h);
1490   return ret;
1491 }
1492
1493
1494 /**
1495  * Write file changes to disk
1496  * @param h handle to an open file
1497  * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
1498  */
1499 int
1500 GNUNET_DISK_file_sync (const struct GNUNET_DISK_FileHandle *h)
1501 {
1502   if (h == NULL)
1503   {
1504     errno = EINVAL;
1505     return GNUNET_SYSERR;
1506   }
1507
1508 #if defined(FREEBSD) || defined(OPENBSD) || defined(DARWIN)
1509   return fsync (h->fd) == -1 ? GNUNET_SYSERR : GNUNET_OK;
1510 #else
1511   return fdatasync (h->fd) == -1 ? GNUNET_SYSERR : GNUNET_OK;
1512 #endif
1513 }
1514
1515
1516 /**
1517  * Creates an interprocess channel
1518  *
1519  * @param blocking_read creates an asynchronous pipe for reading if set to GNUNET_NO
1520  * @param blocking_write creates an asynchronous pipe for writing if set to GNUNET_NO
1521  * @param inherit_read inherit the parent processes stdin (only for windows)
1522  * @param inherit_write inherit the parent processes stdout (only for windows)
1523  * @return handle to the new pipe, NULL on error
1524  */
1525 struct GNUNET_DISK_PipeHandle *
1526 GNUNET_DISK_pipe (int blocking_read,
1527                   int blocking_write,
1528                   int inherit_read,
1529                   int inherit_write)
1530 {
1531   int fd[2];
1532   int ret;
1533   int eno;
1534
1535   (void) inherit_read;
1536   (void) inherit_write;
1537   ret = pipe (fd);
1538   if (ret == -1)
1539   {
1540     eno = errno;
1541     LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR, "pipe");
1542     errno = eno;
1543     return NULL;
1544   }
1545   return GNUNET_DISK_pipe_from_fd (blocking_read, blocking_write, fd);
1546 }
1547
1548
1549 /**
1550  * Creates a pipe object from a couple of file descriptors.
1551  * Useful for wrapping existing pipe FDs.
1552  *
1553  * @param blocking_read creates an asynchronous pipe for reading if set to GNUNET_NO
1554  * @param blocking_write creates an asynchronous pipe for writing if set to GNUNET_NO
1555  * @param fd an array of two fd values. One of them may be -1 for read-only or write-only pipes
1556  *
1557  * @return handle to the new pipe, NULL on error
1558  */
1559 struct GNUNET_DISK_PipeHandle *
1560 GNUNET_DISK_pipe_from_fd (int blocking_read, int blocking_write, int fd[2])
1561 {
1562   struct GNUNET_DISK_PipeHandle *p;
1563
1564   p = GNUNET_new (struct GNUNET_DISK_PipeHandle);
1565
1566   int ret;
1567   int flags;
1568   int eno = 0; /* make gcc happy */
1569
1570   ret = 0;
1571   if (fd[0] >= 0)
1572   {
1573     p->fd[0] = GNUNET_new (struct GNUNET_DISK_FileHandle);
1574     p->fd[0]->fd = fd[0];
1575     if (! blocking_read)
1576     {
1577       flags = fcntl (fd[0], F_GETFL);
1578       flags |= O_NONBLOCK;
1579       if (0 > fcntl (fd[0], F_SETFL, flags))
1580       {
1581         ret = -1;
1582         eno = errno;
1583       }
1584     }
1585     flags = fcntl (fd[0], F_GETFD);
1586     flags |= FD_CLOEXEC;
1587     if (0 > fcntl (fd[0], F_SETFD, flags))
1588     {
1589       ret = -1;
1590       eno = errno;
1591     }
1592   }
1593
1594   if (fd[1] >= 0)
1595   {
1596     p->fd[1] = GNUNET_new (struct GNUNET_DISK_FileHandle);
1597     p->fd[1]->fd = fd[1];
1598     if (! blocking_write)
1599     {
1600       flags = fcntl (fd[1], F_GETFL);
1601       flags |= O_NONBLOCK;
1602       if (0 > fcntl (fd[1], F_SETFL, flags))
1603       {
1604         ret = -1;
1605         eno = errno;
1606       }
1607     }
1608     flags = fcntl (fd[1], F_GETFD);
1609     flags |= FD_CLOEXEC;
1610     if (0 > fcntl (fd[1], F_SETFD, flags))
1611     {
1612       ret = -1;
1613       eno = errno;
1614     }
1615   }
1616   if (ret == -1)
1617   {
1618     errno = eno;
1619     LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR, "fcntl");
1620     if (p->fd[0]->fd >= 0)
1621       GNUNET_break (0 == close (p->fd[0]->fd));
1622     if (p->fd[1]->fd >= 0)
1623       GNUNET_break (0 == close (p->fd[1]->fd));
1624     GNUNET_free_non_null (p->fd[0]);
1625     GNUNET_free_non_null (p->fd[1]);
1626     GNUNET_free (p);
1627     errno = eno;
1628     return NULL;
1629   }
1630
1631   return p;
1632 }
1633
1634
1635 /**
1636  * Closes an interprocess channel
1637  *
1638  * @param p pipe to close
1639  * @param end which end of the pipe to close
1640  * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
1641  */
1642 int
1643 GNUNET_DISK_pipe_close_end (struct GNUNET_DISK_PipeHandle *p,
1644                             enum GNUNET_DISK_PipeEnd end)
1645 {
1646   int ret = GNUNET_OK;
1647
1648   if (end == GNUNET_DISK_PIPE_END_READ)
1649   {
1650     if (p->fd[0])
1651     {
1652       ret = GNUNET_DISK_file_close (p->fd[0]);
1653       p->fd[0] = NULL;
1654     }
1655   }
1656   else if (end == GNUNET_DISK_PIPE_END_WRITE)
1657   {
1658     if (p->fd[1])
1659     {
1660       ret = GNUNET_DISK_file_close (p->fd[1]);
1661       p->fd[1] = NULL;
1662     }
1663   }
1664
1665   return ret;
1666 }
1667
1668
1669 /**
1670  * Detaches one of the ends from the pipe.
1671  * Detached end is a fully-functional FileHandle, it will
1672  * not be affected by anything you do with the pipe afterwards.
1673  * Each end of a pipe can only be detched from it once (i.e.
1674  * it is not duplicated).
1675  *
1676  * @param p pipe to detach an end from
1677  * @param end which end of the pipe to detach
1678  * @return Detached end on success, NULL on failure
1679  * (or if that end is not present or is closed).
1680  */
1681 struct GNUNET_DISK_FileHandle *
1682 GNUNET_DISK_pipe_detach_end (struct GNUNET_DISK_PipeHandle *p,
1683                              enum GNUNET_DISK_PipeEnd end)
1684 {
1685   struct GNUNET_DISK_FileHandle *ret = NULL;
1686
1687   if (end == GNUNET_DISK_PIPE_END_READ)
1688   {
1689     if (p->fd[0])
1690     {
1691       ret = p->fd[0];
1692       p->fd[0] = NULL;
1693     }
1694   }
1695   else if (end == GNUNET_DISK_PIPE_END_WRITE)
1696   {
1697     if (p->fd[1])
1698     {
1699       ret = p->fd[1];
1700       p->fd[1] = NULL;
1701     }
1702   }
1703
1704   return ret;
1705 }
1706
1707
1708 /**
1709  * Closes an interprocess channel
1710  *
1711  * @param p pipe to close
1712  * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
1713  */
1714 int
1715 GNUNET_DISK_pipe_close (struct GNUNET_DISK_PipeHandle *p)
1716 {
1717   int ret = GNUNET_OK;
1718
1719   int read_end_close;
1720   int write_end_close;
1721   int read_end_close_errno;
1722   int write_end_close_errno;
1723
1724   read_end_close = GNUNET_DISK_pipe_close_end (p, GNUNET_DISK_PIPE_END_READ);
1725   read_end_close_errno = errno;
1726   write_end_close = GNUNET_DISK_pipe_close_end (p, GNUNET_DISK_PIPE_END_WRITE);
1727   write_end_close_errno = errno;
1728   GNUNET_free (p);
1729
1730   if (GNUNET_OK != read_end_close)
1731   {
1732     errno = read_end_close_errno;
1733     ret = read_end_close;
1734   }
1735   else if (GNUNET_OK != write_end_close)
1736   {
1737     errno = write_end_close_errno;
1738     ret = write_end_close;
1739   }
1740
1741   return ret;
1742 }
1743
1744
1745 /**
1746  * Get the handle to a particular pipe end
1747  *
1748  * @param p pipe
1749  * @param n end to access
1750  * @return handle for the respective end
1751  */
1752 const struct GNUNET_DISK_FileHandle *
1753 GNUNET_DISK_pipe_handle (const struct GNUNET_DISK_PipeHandle *p,
1754                          enum GNUNET_DISK_PipeEnd n)
1755 {
1756   switch (n)
1757   {
1758   case GNUNET_DISK_PIPE_END_READ:
1759   case GNUNET_DISK_PIPE_END_WRITE:
1760     return p->fd[n];
1761
1762   default:
1763     GNUNET_break (0);
1764     return NULL;
1765   }
1766 }
1767
1768
1769 /**
1770  * Retrieve OS file handle
1771  * @internal
1772  * @param fh GNUnet file descriptor
1773  * @param dst destination buffer
1774  * @param dst_len length of dst
1775  * @return #GNUNET_OK on success, #GNUNET_SYSERR otherwise
1776  */
1777 int
1778 GNUNET_DISK_internal_file_handle_ (const struct GNUNET_DISK_FileHandle *fh,
1779                                    void *dst,
1780                                    size_t dst_len)
1781 {
1782   if (NULL == fh)
1783     return GNUNET_SYSERR;
1784
1785   if (dst_len < sizeof(int))
1786     return GNUNET_SYSERR;
1787   *((int *) dst) = fh->fd;
1788
1789   return GNUNET_OK;
1790 }
1791
1792
1793 /**
1794  * Helper function for #GNUNET_DISK_purge_cfg_dir.
1795  *
1796  * @param cls a `const char *` with the option to purge
1797  * @param cfg our configuration
1798  * @return #GNUNET_OK on success
1799  */
1800 static int
1801 purge_cfg_dir (void *cls, const struct GNUNET_CONFIGURATION_Handle *cfg)
1802 {
1803   const char *option = cls;
1804   char *tmpname;
1805
1806   if (GNUNET_OK !=
1807       GNUNET_CONFIGURATION_get_value_filename (cfg, "PATHS", option, &tmpname))
1808   {
1809     GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, "PATHS", option);
1810     return GNUNET_NO;
1811   }
1812   if (GNUNET_SYSERR == GNUNET_DISK_directory_remove (tmpname))
1813   {
1814     GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR, "remove", tmpname);
1815     GNUNET_free (tmpname);
1816     return GNUNET_OK;
1817   }
1818   GNUNET_free (tmpname);
1819   return GNUNET_OK;
1820 }
1821
1822
1823 /**
1824  * Remove the directory given under @a option in
1825  * section [PATHS] in configuration under @a cfg_filename
1826  *
1827  * @param cfg_filename configuration file to parse
1828  * @param option option with the dir name to purge
1829  */
1830 void
1831 GNUNET_DISK_purge_cfg_dir (const char *cfg_filename, const char *option)
1832 {
1833   GNUNET_break (GNUNET_OK ==
1834                 GNUNET_CONFIGURATION_parse_and_run (cfg_filename,
1835                                                     &purge_cfg_dir,
1836                                                     (void *) option));
1837 }
1838
1839
1840 /* end of disk.c */