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