2 This file is part of GNUnet.
3 Copyright (C) 2001--2013, 2016, 2018 GNUnet e.V.
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.
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.
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/>.
18 SPDX-License-Identifier: AGPL3.0-or-later
22 * @brief disk IO convenience methods
23 * @author Christian Grothoff
28 #include "gnunet_strings_lib.h"
29 #include "gnunet_disk_lib.h"
31 #define LOG(kind, ...) GNUNET_log_from (kind, "util-disk", __VA_ARGS__)
33 #define LOG_STRERROR(kind, syscall) \
34 GNUNET_log_from_strerror (kind, "util-disk", syscall)
36 #define LOG_STRERROR_FILE(kind, syscall, filename) \
37 GNUNET_log_from_strerror_file (kind, "util-disk", syscall, filename)
40 * Block size for IO for copying files.
42 #define COPY_BLK_SIZE 65536
44 #include <sys/types.h>
49 #include <sys/param.h>
52 #include <sys/mount.h>
54 #if HAVE_SYS_STATVFS_H
55 #include <sys/statvfs.h>
59 #define _IFMT 0170000 /* type of file */
60 #define _IFLNK 0120000 /* symbolic link */
61 #define S_ISLNK(m) (((m) & _IFMT) == _IFLNK)
66 * Handle used to manage a pipe.
68 struct GNUNET_DISK_PipeHandle
71 * File descriptors for the pipe.
72 * One or both of them could be NULL.
74 struct GNUNET_DISK_FileHandle *fd[2];
79 * Closure for the recursion to determine the file size
82 struct GetFileSizeData
85 * Set to the total file size.
90 * GNUNET_YES if symbolic links should be included.
92 int include_sym_links;
95 * GNUNET_YES if mode is file-only (return total == -1 for directories).
102 * Translate GNUnet-internal permission bitmap to UNIX file
103 * access permission bitmap.
105 * @param perm file permissions, GNUnet style
106 * @return file permissions, UNIX style
109 translate_unix_perms (enum GNUNET_DISK_AccessPermissions perm)
114 if (perm & GNUNET_DISK_PERM_USER_READ)
116 if (perm & GNUNET_DISK_PERM_USER_WRITE)
118 if (perm & GNUNET_DISK_PERM_USER_EXEC)
120 if (perm & GNUNET_DISK_PERM_GROUP_READ)
122 if (perm & GNUNET_DISK_PERM_GROUP_WRITE)
124 if (perm & GNUNET_DISK_PERM_GROUP_EXEC)
126 if (perm & GNUNET_DISK_PERM_OTHER_READ)
128 if (perm & GNUNET_DISK_PERM_OTHER_WRITE)
130 if (perm & GNUNET_DISK_PERM_OTHER_EXEC)
138 * Iterate over all files in the given directory and
139 * accumulate their size.
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
146 getSizeRec (void *cls, const char *fn)
148 struct GetFileSizeData *gfsd = cls;
150 #if defined(HAVE_STAT64) && \
151 ! (defined(_FILE_OFFSET_BITS) && _FILE_OFFSET_BITS == 64)
154 if (0 != stat64 (fn, &buf))
156 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_DEBUG, "stat64", fn);
157 return GNUNET_SYSERR;
162 if (0 != stat (fn, &buf))
164 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_DEBUG, "stat", fn);
165 return GNUNET_SYSERR;
168 if ((S_ISDIR (buf.st_mode)) && (gfsd->single_file_mode == GNUNET_YES))
171 return GNUNET_SYSERR;
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)))
178 if (GNUNET_SYSERR == GNUNET_DISK_directory_scan (fn, &getSizeRec, gfsd))
179 return GNUNET_SYSERR;
186 * Checks whether a handle is invalid
188 * @param h handle to check
189 * @return #GNUNET_YES if invalid, #GNUNET_NO if valid
192 GNUNET_DISK_handle_invalid (const struct GNUNET_DISK_FileHandle *h)
194 return ((! h) || (h->fd == -1)) ? GNUNET_YES : GNUNET_NO;
198 * Get the size of an open file.
200 * @param fh open file handle
201 * @param size where to write size of the file
202 * @return #GNUNET_OK on success, #GNUNET_SYSERR on error
205 GNUNET_DISK_file_handle_size (struct GNUNET_DISK_FileHandle *fh, off_t *size)
209 if (0 != fstat (fh->fd, &sbuf))
210 return GNUNET_SYSERR;
211 *size = sbuf.st_size;
217 * Move the read/write pointer in a file
219 * @param h handle of an open file
220 * @param offset position to move to
221 * @param whence specification to which position the offset parameter relates to
222 * @return the new position on success, #GNUNET_SYSERR otherwise
225 GNUNET_DISK_file_seek (const struct GNUNET_DISK_FileHandle *h,
227 enum GNUNET_DISK_Seek whence)
232 return GNUNET_SYSERR;
235 static int t[] = { SEEK_SET, SEEK_CUR, SEEK_END };
237 return lseek (h->fd, offset, t[whence]);
242 * Get the size of the file (or directory) of the given file (in
245 * @param filename name of the file or directory
246 * @param size set to the size of the file (or,
247 * in the case of directories, the sum
248 * of all sizes of files in the directory)
249 * @param include_symbolic_links should symbolic links be
251 * @param single_file_mode #GNUNET_YES to only get size of one file
252 * and return #GNUNET_SYSERR for directories.
253 * @return #GNUNET_SYSERR on error, #GNUNET_OK on success
256 GNUNET_DISK_file_size (const char *filename,
258 int include_symbolic_links,
259 int single_file_mode)
261 struct GetFileSizeData gfsd;
264 GNUNET_assert (size != NULL);
266 gfsd.include_sym_links = include_symbolic_links;
267 gfsd.single_file_mode = single_file_mode;
268 ret = getSizeRec (&gfsd, filename);
275 * Obtain some unique identifiers for the given file
276 * that can be used to identify it in the local system.
277 * This function is used between GNUnet processes to
278 * quickly check if two files with the same absolute path
279 * are actually identical. The two processes represent
280 * the same peer but may communicate over the network
281 * (and the file may be on an NFS volume). This function
282 * may not be supported on all operating systems.
284 * @param filename name of the file
285 * @param dev set to the device ID
286 * @param ino set to the inode ID
287 * @return #GNUNET_OK on success
290 GNUNET_DISK_file_get_identifiers (const char *filename,
298 if (0 != stat (filename, &sbuf))
300 return GNUNET_SYSERR;
302 *ino = (uint64_t) sbuf.st_ino;
311 if (0 != statvfs (filename, &fbuf))
313 return GNUNET_SYSERR;
315 *dev = (uint64_t) fbuf.f_fsid;
321 if (0 != statfs (filename, &fbuf))
323 return GNUNET_SYSERR;
326 ((uint64_t) fbuf.f_fsid.val[0]) << 32 || ((uint64_t) fbuf.f_fsid.val[1]);
336 * Create the name for a temporary file or directory from a template.
338 * @param t template (without XXXXX or "/tmp/")
339 * @return name ready for passing to 'mktemp' or 'mkdtemp', NULL on error
342 mktemp_name (const char *t)
348 if ((t[0] != '/') && (t[0] != '\\'))
350 /* FIXME: This uses system codepage on W32, not UTF-8 */
351 tmpdir = getenv ("TMPDIR");
353 tmpdir = getenv ("TMP");
355 tmpdir = getenv ("TEMP");
358 GNUNET_asprintf (&tmpl, "%s/%s%s", tmpdir, t, "XXXXXX");
362 GNUNET_asprintf (&tmpl, "%s%s", t, "XXXXXX");
370 * Update POSIX permissions mask of a file on disk. If both argumets
371 * are #GNUNET_NO, the file is made world-read-write-executable (777).
373 * @param fn name of the file to update
374 * @param require_uid_match #GNUNET_YES means 700
375 * @param require_gid_match #GNUNET_YES means 770 unless @a require_uid_match is set
378 GNUNET_DISK_fix_permissions (const char *fn,
379 int require_uid_match,
380 int require_gid_match)
384 if (GNUNET_YES == require_uid_match)
385 mode = S_IRUSR | S_IWUSR | S_IXUSR;
386 else if (GNUNET_YES == require_gid_match)
387 mode = S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IWGRP | S_IXGRP;
389 mode = S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IWGRP | S_IXGRP | S_IROTH
391 if (0 != chmod (fn, mode))
392 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "chmod", fn);
397 * Create an (empty) temporary directory on disk. If the given name is not
398 * an absolute path, the current 'TMPDIR' will be prepended. In any case,
399 * 6 random characters will be appended to the name to create a unique
402 * @param t component to use for the name;
403 * does NOT contain "XXXXXX" or "/tmp/".
404 * @return NULL on error, otherwise name of fresh
405 * file on disk in directory for temporary files
408 GNUNET_DISK_mkdtemp (const char *t)
413 omask = umask (S_IWGRP | S_IWOTH | S_IRGRP | S_IROTH);
414 fn = mktemp_name (t);
415 if (fn != mkdtemp (fn))
417 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_ERROR, "mkdtemp", fn);
428 * Move a file out of the way (create a backup) by
429 * renaming it to "orig.NUM~" where NUM is the smallest
430 * number that is not used yet.
432 * @param fil name of the file to back up
435 GNUNET_DISK_file_backup (const char *fil)
441 slen = strlen (fil) + 20;
442 target = GNUNET_malloc (slen);
446 GNUNET_snprintf (target, slen, "%s.%u~", fil, num++);
448 while (0 == access (target, F_OK));
449 if (0 != rename (fil, target))
450 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR, "rename", fil);
451 GNUNET_free (target);
456 * Create an (empty) temporary file on disk. If the given name is not
457 * an absolute path, the current 'TMPDIR' will be prepended. In any case,
458 * 6 random characters will be appended to the name to create a unique
461 * @param t component to use for the name;
462 * does NOT contain "XXXXXX" or "/tmp/".
463 * @return NULL on error, otherwise name of fresh
464 * file on disk in directory for temporary files
467 GNUNET_DISK_mktemp (const char *t)
473 omask = umask (S_IWGRP | S_IWOTH | S_IRGRP | S_IROTH);
474 fn = mktemp_name (t);
475 if (-1 == (fd = mkstemp (fn)))
477 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_ERROR, "mkstemp", fn);
484 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "close", fn);
490 * Test if @a fil is a directory and listable. Optionally, also check if the
491 * directory is readable. Will not print an error message if the directory does
492 * not exist. Will log errors if #GNUNET_SYSERR is returned (i.e., a file exists
493 * with the same name).
495 * @param fil filename to test
496 * @param is_readable #GNUNET_YES to additionally check if @a fil is readable;
497 * #GNUNET_NO to disable this check
498 * @return #GNUNET_YES if yes, #GNUNET_NO if not; #GNUNET_SYSERR if it
499 * does not exist or stat'ed
502 GNUNET_DISK_directory_test (const char *fil, int is_readable)
504 struct stat filestat;
507 ret = stat (fil, &filestat);
511 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "stat", fil);
512 return GNUNET_SYSERR;
514 if (! S_ISDIR (filestat.st_mode))
516 LOG (GNUNET_ERROR_TYPE_INFO,
517 "A file already exits with the same name %s\n",
521 if (GNUNET_YES == is_readable)
522 ret = access (fil, R_OK | X_OK);
524 ret = access (fil, X_OK);
527 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "access", fil);
535 * Check that fil corresponds to a filename
536 * (of a file that exists and that is not a directory).
538 * @param fil filename to check
539 * @return #GNUNET_YES if yes, #GNUNET_NO if not a file, #GNUNET_SYSERR if something
540 * else (will print an error message in that case, too).
543 GNUNET_DISK_file_test (const char *fil)
545 struct stat filestat;
549 rdir = GNUNET_STRINGS_filename_expand (fil);
551 return GNUNET_SYSERR;
553 ret = stat (rdir, &filestat);
558 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "stat", rdir);
560 return GNUNET_SYSERR;
565 if (! S_ISREG (filestat.st_mode))
570 if (access (rdir, F_OK) < 0)
572 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "access", rdir);
574 return GNUNET_SYSERR;
582 * Implementation of "mkdir -p"
584 * @param dir the directory to create
585 * @return #GNUNET_OK on success, #GNUNET_SYSERR on failure
588 GNUNET_DISK_directory_create (const char *dir)
596 rdir = GNUNET_STRINGS_filename_expand (dir);
600 return GNUNET_SYSERR;
605 pos = 1; /* skip heading '/' */
607 /* Check which low level directories already exist */
609 rdir[len] = DIR_SEPARATOR;
612 if (DIR_SEPARATOR == rdir[pos2])
615 ret = GNUNET_DISK_directory_test (rdir, GNUNET_NO);
616 if (GNUNET_NO == ret)
618 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
619 "Creating directory `%s' failed",
622 return GNUNET_SYSERR;
624 rdir[pos2] = DIR_SEPARATOR;
625 if (GNUNET_YES == ret)
636 /* Start creating directories */
639 if ((rdir[pos] == DIR_SEPARATOR) || (pos == len))
642 ret = GNUNET_DISK_directory_test (rdir, GNUNET_NO);
643 if (GNUNET_NO == ret)
645 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
646 "Creating directory `%s' failed",
649 return GNUNET_SYSERR;
651 if (GNUNET_SYSERR == ret)
654 S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IXGRP | S_IROTH
655 | S_IXOTH); /* 755 */
657 if ((ret != 0) && (errno != EEXIST))
659 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_ERROR, "mkdir", rdir);
661 return GNUNET_SYSERR;
664 rdir[pos] = DIR_SEPARATOR;
674 * Create the directory structure for storing a file.
676 * @param filename name of a file in the directory
677 * @returns #GNUNET_OK on success,
678 * #GNUNET_SYSERR on failure,
679 * #GNUNET_NO if the directory
680 * exists but is not writeable for us
683 GNUNET_DISK_directory_create_for_file (const char *filename)
690 rdir = GNUNET_STRINGS_filename_expand (filename);
694 return GNUNET_SYSERR;
696 if (0 == access (rdir, W_OK))
703 while ((len > 0) && (rdir[len] != DIR_SEPARATOR))
706 /* The empty path is invalid and in this case refers to / */
710 rdir = GNUNET_strdup ("/");
712 ret = GNUNET_DISK_directory_create (rdir);
713 if ((GNUNET_OK == ret) && (0 != access (rdir, W_OK)))
723 * Read the contents of a binary file into a buffer.
725 * @param h handle to an open file
726 * @param result the buffer to write the result to
727 * @param len the maximum number of bytes to read
728 * @return the number of bytes read on success, #GNUNET_SYSERR on failure
731 GNUNET_DISK_file_read (const struct GNUNET_DISK_FileHandle *h,
738 return GNUNET_SYSERR;
741 return read (h->fd, result, len);
746 * Read the contents of a binary file into a buffer.
747 * Guarantees not to block (returns GNUNET_SYSERR and sets errno to EAGAIN
748 * when no data can be read).
750 * @param h handle to an open file
751 * @param result the buffer to write the result to
752 * @param len the maximum number of bytes to read
753 * @return the number of bytes read on success, #GNUNET_SYSERR on failure
756 GNUNET_DISK_file_read_non_blocking (const struct GNUNET_DISK_FileHandle *h,
763 return GNUNET_SYSERR;
769 /* set to non-blocking, read, then set back */
770 flags = fcntl (h->fd, F_GETFL);
771 if (0 == (flags & O_NONBLOCK))
772 (void) fcntl (h->fd, F_SETFL, flags | O_NONBLOCK);
773 ret = read (h->fd, result, len);
774 if (0 == (flags & O_NONBLOCK))
777 (void) fcntl (h->fd, F_SETFL, flags);
785 * Read the contents of a binary file into a buffer.
787 * @param fn file name
788 * @param result the buffer to write the result to
789 * @param len the maximum number of bytes to read
790 * @return number of bytes read, #GNUNET_SYSERR on failure
793 GNUNET_DISK_fn_read (const char *fn, void *result, size_t len)
795 struct GNUNET_DISK_FileHandle *fh;
799 fh = GNUNET_DISK_file_open (fn, GNUNET_DISK_OPEN_READ, GNUNET_DISK_PERM_NONE);
801 return GNUNET_SYSERR;
802 ret = GNUNET_DISK_file_read (fh, result, len);
804 GNUNET_assert (GNUNET_OK == GNUNET_DISK_file_close (fh));
811 * Write a buffer to a file.
813 * @param h handle to open file
814 * @param buffer the data to write
815 * @param n number of bytes to write
816 * @return number of bytes written on success, #GNUNET_SYSERR on error
819 GNUNET_DISK_file_write (const struct GNUNET_DISK_FileHandle *h,
826 return GNUNET_SYSERR;
830 return write (h->fd, buffer, n);
835 * Write a buffer to a file, blocking, if necessary.
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
843 GNUNET_DISK_file_write_blocking (const struct GNUNET_DISK_FileHandle *h,
850 return GNUNET_SYSERR;
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);
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.
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
879 GNUNET_DISK_fn_write (const char *fn,
882 enum GNUNET_DISK_AccessPermissions mode)
884 struct GNUNET_DISK_FileHandle *fh;
888 GNUNET_DISK_file_open (fn,
889 GNUNET_DISK_OPEN_WRITE | GNUNET_DISK_OPEN_TRUNCATE
890 | GNUNET_DISK_OPEN_CREATE,
893 return GNUNET_SYSERR;
894 ret = GNUNET_DISK_file_write (fh, buffer, n);
895 GNUNET_assert (GNUNET_OK == GNUNET_DISK_file_close (fh));
901 * Scan a directory for files.
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
911 GNUNET_DISK_directory_scan (const char *dir_name,
912 GNUNET_FileNameCallback callback,
916 struct dirent *finfo;
922 unsigned int name_len;
925 GNUNET_assert (NULL != dir_name);
926 dname = GNUNET_STRINGS_filename_expand (dir_name);
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))
933 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "stat", dname);
935 return GNUNET_SYSERR;
937 if (! S_ISDIR (istat.st_mode))
939 LOG (GNUNET_ERROR_TYPE_WARNING,
940 _ ("Expected `%s' to be a directory!\n"),
943 return GNUNET_SYSERR;
946 dinfo = opendir (dname);
947 if ((EACCES == errno) || (NULL == dinfo))
949 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "opendir", dname);
953 return GNUNET_SYSERR;
956 n_size = strlen (dname) + name_len + strlen (DIR_SEPARATOR_STR) + 1;
957 name = GNUNET_malloc (n_size);
958 while (NULL != (finfo = readdir (dinfo)))
960 if ((0 == strcmp (finfo->d_name, ".")) ||
961 (0 == strcmp (finfo->d_name, "..")))
963 if (NULL != callback)
965 if (name_len < strlen (finfo->d_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);
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,
979 (0 == strcmp (dname, DIR_SEPARATOR_STR))
983 ret = callback (callback_cls, name);
984 if (GNUNET_OK != ret)
989 if (GNUNET_NO == ret)
991 return GNUNET_SYSERR;
1004 * Function that removes the given directory by calling
1005 * #GNUNET_DISK_directory_remove().
1007 * @param unused not used
1008 * @param fn directory to remove
1009 * @return #GNUNET_OK
1012 remove_helper (void *unused, const char *fn)
1015 (void) GNUNET_DISK_directory_remove (fn);
1021 * Remove all files in a directory (rm -r). Call with
1024 * @param filename the file to remove
1025 * @return #GNUNET_OK on success, #GNUNET_SYSERR on error
1028 GNUNET_DISK_directory_remove (const char *filename)
1032 if (NULL == filename)
1035 return GNUNET_SYSERR;
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))
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)))
1048 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "rmdir", filename);
1049 return GNUNET_SYSERR;
1051 if (GNUNET_SYSERR ==
1052 GNUNET_DISK_directory_scan (filename, &remove_helper, NULL))
1053 return GNUNET_SYSERR;
1054 if (0 != rmdir (filename))
1056 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "rmdir", filename);
1057 return GNUNET_SYSERR;
1066 * @param src file to copy
1067 * @param dst destination file name
1068 * @return #GNUNET_OK on success, #GNUNET_SYSERR on error
1071 GNUNET_DISK_file_copy (const char *src, const char *dst)
1078 struct GNUNET_DISK_FileHandle *in;
1079 struct GNUNET_DISK_FileHandle *out;
1081 if (GNUNET_OK != GNUNET_DISK_file_size (src, &size, GNUNET_YES, GNUNET_YES))
1083 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR, "stat", src);
1084 return GNUNET_SYSERR;
1088 GNUNET_DISK_file_open (src, GNUNET_DISK_OPEN_READ, GNUNET_DISK_PERM_NONE);
1091 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR, "open", src);
1092 return GNUNET_SYSERR;
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);
1104 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR, "open", dst);
1105 GNUNET_DISK_file_close (in);
1106 return GNUNET_SYSERR;
1108 buf = GNUNET_malloc (COPY_BLK_SIZE);
1111 len = COPY_BLK_SIZE;
1112 if (len > size - pos)
1114 sret = GNUNET_DISK_file_read (in, buf, len);
1115 if ((sret < 0) || (len != (size_t) sret))
1117 sret = GNUNET_DISK_file_write (out, buf, len);
1118 if ((sret < 0) || (len != (size_t) sret))
1123 GNUNET_DISK_file_close (in);
1124 GNUNET_DISK_file_close (out);
1128 GNUNET_DISK_file_close (in);
1129 GNUNET_DISK_file_close (out);
1130 return GNUNET_SYSERR;
1135 * @brief Removes special characters as ':' from a filename.
1136 * @param fn the filename to canonicalize
1139 GNUNET_DISK_filename_canonicalize (char *fn)
1144 for (idx = fn; *idx; idx++)
1148 if ((c == '/') ||(c == '\\') ||(c == ':') ||(c == '*') ||(c == '?') ||(c ==
1151 (c == '<') ||(c == '>') ||(c == '|') )
1160 * @brief Change owner of a file
1162 * @param filename name of file to change the owner of
1163 * @param user name of the new owner
1164 * @return #GNUNET_OK on success, #GNUNET_SYSERR on failure
1167 GNUNET_DISK_file_change_owner (const char *filename, const char *user)
1171 pws = getpwnam (user);
1174 LOG (GNUNET_ERROR_TYPE_ERROR,
1175 _ ("Cannot obtain information about user `%s': %s\n"),
1178 return GNUNET_SYSERR;
1180 if (0 != chown (filename, pws->pw_uid, pws->pw_gid))
1182 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "chown", filename);
1183 return GNUNET_SYSERR;
1190 * Lock a part of a file
1192 * @param fh file handle
1193 * @param lock_start absolute position from where to lock
1194 * @param lock_end absolute position until where to lock
1195 * @param excl #GNUNET_YES for an exclusive lock
1196 * @return #GNUNET_OK on success, #GNUNET_SYSERR on error
1199 GNUNET_DISK_file_lock (struct GNUNET_DISK_FileHandle *fh,
1207 return GNUNET_SYSERR;
1212 memset (&fl, 0, sizeof(struct flock));
1213 fl.l_type = excl ? F_WRLCK : F_RDLCK;
1214 fl.l_whence = SEEK_SET;
1215 fl.l_start = lock_start;
1216 fl.l_len = lock_end;
1218 return fcntl (fh->fd, F_SETLK, &fl) != 0 ? GNUNET_SYSERR : GNUNET_OK;
1223 * Unlock a part of a file
1225 * @param fh file handle
1226 * @param unlock_start absolute position from where to unlock
1227 * @param unlock_end absolute position until where to unlock
1228 * @return #GNUNET_OK on success, #GNUNET_SYSERR on error
1231 GNUNET_DISK_file_unlock (struct GNUNET_DISK_FileHandle *fh,
1238 return GNUNET_SYSERR;
1243 memset (&fl, 0, sizeof(struct flock));
1244 fl.l_type = F_UNLCK;
1245 fl.l_whence = SEEK_SET;
1246 fl.l_start = unlock_start;
1247 fl.l_len = unlock_end;
1249 return fcntl (fh->fd, F_SETLK, &fl) != 0 ? GNUNET_SYSERR : GNUNET_OK;
1254 * Open a file. Note that the access permissions will only be
1255 * used if a new file is created and if the underlying operating
1256 * system supports the given permissions.
1258 * @param fn file name to be opened
1259 * @param flags opening flags, a combination of GNUNET_DISK_OPEN_xxx bit flags
1260 * @param perm permissions for the newly created file, use
1261 * #GNUNET_DISK_PERM_NONE if a file could not be created by this
1262 * call (because of flags)
1263 * @return IO handle on success, NULL on error
1265 struct GNUNET_DISK_FileHandle *
1266 GNUNET_DISK_file_open (const char *fn,
1267 enum GNUNET_DISK_OpenFlags flags,
1268 enum GNUNET_DISK_AccessPermissions perm)
1271 struct GNUNET_DISK_FileHandle *ret;
1277 expfn = GNUNET_STRINGS_filename_expand (fn);
1282 if (GNUNET_DISK_OPEN_READWRITE == (flags & GNUNET_DISK_OPEN_READWRITE))
1283 oflags = O_RDWR; /* note: O_RDWR is NOT always O_RDONLY | O_WRONLY */
1284 else if (flags & GNUNET_DISK_OPEN_READ)
1286 else if (flags & GNUNET_DISK_OPEN_WRITE)
1291 GNUNET_free (expfn);
1294 if (flags & GNUNET_DISK_OPEN_FAILIFEXISTS)
1295 oflags |= (O_CREAT | O_EXCL);
1296 if (flags & GNUNET_DISK_OPEN_TRUNCATE)
1298 if (flags & GNUNET_DISK_OPEN_APPEND)
1300 if (GNUNET_NO == GNUNET_DISK_file_test (fn))
1302 if (flags & GNUNET_DISK_OPEN_CREATE)
1304 (void) GNUNET_DISK_directory_create_for_file (expfn);
1306 mode = translate_unix_perms (perm);
1319 if (0 == (flags & GNUNET_DISK_OPEN_FAILIFEXISTS))
1320 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "open", expfn);
1322 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_DEBUG, "open", expfn);
1323 GNUNET_free (expfn);
1327 ret = GNUNET_new (struct GNUNET_DISK_FileHandle);
1331 GNUNET_free (expfn);
1337 * Close an open file.
1339 * @param h file handle
1340 * @return #GNUNET_OK on success, #GNUNET_SYSERR otherwise
1343 GNUNET_DISK_file_close (struct GNUNET_DISK_FileHandle *h)
1350 return GNUNET_SYSERR;
1355 if (close (h->fd) != 0)
1357 LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING, "close");
1358 ret = GNUNET_SYSERR;
1367 * Get a handle from a native integer FD.
1369 * @param fno native integer file descriptor
1370 * @return file handle corresponding to the descriptor, NULL on error
1372 struct GNUNET_DISK_FileHandle *
1373 GNUNET_DISK_get_handle_from_int_fd (int fno)
1375 struct GNUNET_DISK_FileHandle *fh;
1377 if ((((off_t) -1) == lseek (fno, 0, SEEK_CUR)) && (EBADF == errno))
1378 return NULL; /* invalid FD */
1380 fh = GNUNET_new (struct GNUNET_DISK_FileHandle);
1389 * Get a handle from a native streaming FD.
1391 * @param fd native streaming file descriptor
1392 * @return file handle corresponding to the descriptor
1394 struct GNUNET_DISK_FileHandle *
1395 GNUNET_DISK_get_handle_from_native (FILE *fd)
1403 return GNUNET_DISK_get_handle_from_int_fd (fno);
1408 * Handle for a memory-mapping operation.
1410 struct GNUNET_DISK_MapHandle
1413 * Address where the map is in memory.
1418 * Number of bytes mapped.
1425 #define MAP_FAILED ((void *) -1)
1429 * Map a file into memory
1431 * @param h open file handle
1432 * @param m handle to the new mapping
1433 * @param access access specification, GNUNET_DISK_MAP_TYPE_xxx
1434 * @param len size of the mapping
1435 * @return pointer to the mapped memory region, NULL on failure
1438 GNUNET_DISK_file_map (const struct GNUNET_DISK_FileHandle *h,
1439 struct GNUNET_DISK_MapHandle **m,
1440 enum GNUNET_DISK_MapType access,
1452 if (access & GNUNET_DISK_MAP_TYPE_READ)
1454 if (access & GNUNET_DISK_MAP_TYPE_WRITE)
1456 *m = GNUNET_new (struct GNUNET_DISK_MapHandle);
1457 (*m)->addr = mmap (NULL, len, prot, MAP_SHARED, h->fd, 0);
1458 GNUNET_assert (NULL != (*m)->addr);
1459 if (MAP_FAILED == (*m)->addr)
1470 * @param h mapping handle
1471 * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
1474 GNUNET_DISK_file_unmap (struct GNUNET_DISK_MapHandle *h)
1481 return GNUNET_SYSERR;
1484 ret = munmap (h->addr, h->len) != -1 ? GNUNET_OK : GNUNET_SYSERR;
1492 * Write file changes to disk
1493 * @param h handle to an open file
1494 * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
1497 GNUNET_DISK_file_sync (const struct GNUNET_DISK_FileHandle *h)
1502 return GNUNET_SYSERR;
1505 #if defined(FREEBSD) || defined(OPENBSD) || defined(DARWIN)
1506 return fsync (h->fd) == -1 ? GNUNET_SYSERR : GNUNET_OK;
1508 return fdatasync (h->fd) == -1 ? GNUNET_SYSERR : GNUNET_OK;
1514 * Creates an interprocess channel
1516 * @param blocking_read creates an asynchronous pipe for reading if set to GNUNET_NO
1517 * @param blocking_write creates an asynchronous pipe for writing if set to GNUNET_NO
1518 * @param inherit_read inherit the parent processes stdin (only for windows)
1519 * @param inherit_write inherit the parent processes stdout (only for windows)
1520 * @return handle to the new pipe, NULL on error
1522 struct GNUNET_DISK_PipeHandle *
1523 GNUNET_DISK_pipe (int blocking_read,
1532 (void) inherit_read;
1533 (void) inherit_write;
1538 LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR, "pipe");
1542 return GNUNET_DISK_pipe_from_fd (blocking_read, blocking_write, fd);
1547 * Creates a pipe object from a couple of file descriptors.
1548 * Useful for wrapping existing pipe FDs.
1550 * @param blocking_read creates an asynchronous pipe for reading if set to GNUNET_NO
1551 * @param blocking_write creates an asynchronous pipe for writing if set to GNUNET_NO
1552 * @param fd an array of two fd values. One of them may be -1 for read-only or write-only pipes
1554 * @return handle to the new pipe, NULL on error
1556 struct GNUNET_DISK_PipeHandle *
1557 GNUNET_DISK_pipe_from_fd (int blocking_read, int blocking_write, int fd[2])
1559 struct GNUNET_DISK_PipeHandle *p;
1561 p = GNUNET_new (struct GNUNET_DISK_PipeHandle);
1565 int eno = 0; /* make gcc happy */
1570 p->fd[0] = GNUNET_new (struct GNUNET_DISK_FileHandle);
1571 p->fd[0]->fd = fd[0];
1572 if (! blocking_read)
1574 flags = fcntl (fd[0], F_GETFL);
1575 flags |= O_NONBLOCK;
1576 if (0 > fcntl (fd[0], F_SETFL, flags))
1582 flags = fcntl (fd[0], F_GETFD);
1583 flags |= FD_CLOEXEC;
1584 if (0 > fcntl (fd[0], F_SETFD, flags))
1593 p->fd[1] = GNUNET_new (struct GNUNET_DISK_FileHandle);
1594 p->fd[1]->fd = fd[1];
1595 if (! blocking_write)
1597 flags = fcntl (fd[1], F_GETFL);
1598 flags |= O_NONBLOCK;
1599 if (0 > fcntl (fd[1], F_SETFL, flags))
1605 flags = fcntl (fd[1], F_GETFD);
1606 flags |= FD_CLOEXEC;
1607 if (0 > fcntl (fd[1], F_SETFD, flags))
1616 LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR, "fcntl");
1617 if (p->fd[0]->fd >= 0)
1618 GNUNET_break (0 == close (p->fd[0]->fd));
1619 if (p->fd[1]->fd >= 0)
1620 GNUNET_break (0 == close (p->fd[1]->fd));
1621 GNUNET_free_non_null (p->fd[0]);
1622 GNUNET_free_non_null (p->fd[1]);
1633 * Closes an interprocess channel
1635 * @param p pipe to close
1636 * @param end which end of the pipe to close
1637 * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
1640 GNUNET_DISK_pipe_close_end (struct GNUNET_DISK_PipeHandle *p,
1641 enum GNUNET_DISK_PipeEnd end)
1643 int ret = GNUNET_OK;
1645 if (end == GNUNET_DISK_PIPE_END_READ)
1649 ret = GNUNET_DISK_file_close (p->fd[0]);
1653 else if (end == GNUNET_DISK_PIPE_END_WRITE)
1657 ret = GNUNET_DISK_file_close (p->fd[1]);
1666 * Detaches one of the ends from the pipe.
1667 * Detached end is a fully-functional FileHandle, it will
1668 * not be affected by anything you do with the pipe afterwards.
1669 * Each end of a pipe can only be detched from it once (i.e.
1670 * it is not duplicated).
1672 * @param p pipe to detach an end from
1673 * @param end which end of the pipe to detach
1674 * @return Detached end on success, NULL on failure
1675 * (or if that end is not present or is closed).
1677 struct GNUNET_DISK_FileHandle *
1678 GNUNET_DISK_pipe_detach_end (struct GNUNET_DISK_PipeHandle *p,
1679 enum GNUNET_DISK_PipeEnd end)
1681 struct GNUNET_DISK_FileHandle *ret = NULL;
1683 if (end == GNUNET_DISK_PIPE_END_READ)
1691 else if (end == GNUNET_DISK_PIPE_END_WRITE)
1705 * Closes an interprocess channel
1707 * @param p pipe to close
1708 * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
1711 GNUNET_DISK_pipe_close (struct GNUNET_DISK_PipeHandle *p)
1713 int ret = GNUNET_OK;
1716 int write_end_close;
1717 int read_end_close_errno;
1718 int write_end_close_errno;
1720 read_end_close = GNUNET_DISK_pipe_close_end (p, GNUNET_DISK_PIPE_END_READ);
1721 read_end_close_errno = errno;
1722 write_end_close = GNUNET_DISK_pipe_close_end (p, GNUNET_DISK_PIPE_END_WRITE);
1723 write_end_close_errno = errno;
1726 if (GNUNET_OK != read_end_close)
1728 errno = read_end_close_errno;
1729 ret = read_end_close;
1731 else if (GNUNET_OK != write_end_close)
1733 errno = write_end_close_errno;
1734 ret = write_end_close;
1742 * Get the handle to a particular pipe end
1745 * @param n end to access
1746 * @return handle for the respective end
1748 const struct GNUNET_DISK_FileHandle *
1749 GNUNET_DISK_pipe_handle (const struct GNUNET_DISK_PipeHandle *p,
1750 enum GNUNET_DISK_PipeEnd n)
1754 case GNUNET_DISK_PIPE_END_READ:
1755 case GNUNET_DISK_PIPE_END_WRITE:
1766 * Retrieve OS file handle
1768 * @param fh GNUnet file descriptor
1769 * @param dst destination buffer
1770 * @param dst_len length of dst
1771 * @return #GNUNET_OK on success, #GNUNET_SYSERR otherwise
1774 GNUNET_DISK_internal_file_handle_ (const struct GNUNET_DISK_FileHandle *fh,
1779 return GNUNET_SYSERR;
1781 if (dst_len < sizeof(int))
1782 return GNUNET_SYSERR;
1783 *((int *) dst) = fh->fd;
1790 * Helper function for #GNUNET_DISK_purge_cfg_dir.
1792 * @param cls a `const char *` with the option to purge
1793 * @param cfg our configuration
1794 * @return #GNUNET_OK on success
1797 purge_cfg_dir (void *cls, const struct GNUNET_CONFIGURATION_Handle *cfg)
1799 const char *option = cls;
1803 GNUNET_CONFIGURATION_get_value_filename (cfg, "PATHS", option, &tmpname))
1805 GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, "PATHS", option);
1808 if (GNUNET_SYSERR == GNUNET_DISK_directory_remove (tmpname))
1810 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR, "remove", tmpname);
1811 GNUNET_free (tmpname);
1814 GNUNET_free (tmpname);
1820 * Remove the directory given under @a option in
1821 * section [PATHS] in configuration under @a cfg_filename
1823 * @param cfg_filename configuration file to parse
1824 * @param option option with the dir name to purge
1827 GNUNET_DISK_purge_cfg_dir (const char *cfg_filename, const char *option)
1829 GNUNET_break (GNUNET_OK ==
1830 GNUNET_CONFIGURATION_parse_and_run (cfg_filename,