2 This file is part of GNUnet.
3 (C) 2001, 2002, 2005, 2006, 2009 Christian Grothoff (and other contributing authors)
5 GNUnet is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 2, or (at your
8 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 General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with GNUnet; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
23 * @brief disk IO convenience methods
24 * @author Christian Grothoff
29 #include "gnunet_common.h"
30 #include "gnunet_directories.h"
31 #include "gnunet_disk_lib.h"
32 #include "gnunet_scheduler_lib.h"
33 #include "gnunet_strings_lib.h"
38 * Block size for IO for copying files.
40 #define COPY_BLK_SIZE 65536
44 #if defined(LINUX) || defined(CYGWIN)
47 #if defined(SOMEBSD) || defined(DARWIN)
48 #include <sys/param.h>
49 #include <sys/mount.h>
52 #include <sys/types.h>
53 #include <sys/statvfs.h>
56 #define _IFMT 0170000 /* type of file */
57 #define _IFLNK 0120000 /* symbolic link */
58 #define S_ISLNK(m) (((m)&_IFMT) == _IFLNK)
60 #error PORT-ME: need to port statfs (how much space is left on the drive?)
66 #if !defined(SOMEBSD) && !defined(DARWIN) && !defined(WINDOWS)
70 #include <sys/statvfs.h>
75 * Handle used to manage a pipe.
77 struct GNUNET_DISK_PipeHandle
80 * File descriptors for the pipe.
82 struct GNUNET_DISK_FileHandle *fd[2];
87 * Closure for the recursion to determine the file size
90 struct GetFileSizeData
93 * Set to the total file size.
98 * GNUNET_YES if symbolic links should be included.
100 int include_sym_links;
105 * Iterate over all files in the given directory and
106 * accumulate their size.
108 * @param cls closure of type "struct GetFileSizeData"
109 * @param fn current filename we are looking at
110 * @return GNUNET_SYSERR on serious errors, otherwise GNUNET_OK
113 getSizeRec (void *cls, const char *fn)
115 struct GetFileSizeData *gfsd = cls;
123 if (0 != STAT64 (fn, &buf))
125 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "stat64", fn);
126 return GNUNET_SYSERR;
129 if (0 != STAT (fn, &buf))
131 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "stat", fn);
132 return GNUNET_SYSERR;
135 if ((!S_ISLNK (buf.st_mode)) || (gfsd->include_sym_links == GNUNET_YES))
136 gfsd->total += buf.st_size;
137 if ((S_ISDIR (buf.st_mode)) &&
138 (0 == ACCESS (fn, X_OK)) &&
139 ((!S_ISLNK (buf.st_mode)) || (gfsd->include_sym_links == GNUNET_YES)))
141 if (GNUNET_SYSERR == GNUNET_DISK_directory_scan (fn, &getSizeRec, gfsd))
142 return GNUNET_SYSERR;
149 * Checks whether a handle is invalid
151 * @param h handle to check
152 * @return GNUNET_YES if invalid, GNUNET_NO if valid
155 GNUNET_DISK_handle_invalid (const struct GNUNET_DISK_FileHandle *h)
158 return ((!h) || (h->h == INVALID_HANDLE_VALUE)) ? GNUNET_YES : GNUNET_NO;
160 return ((!h) || (h->fd == -1)) ? GNUNET_YES : GNUNET_NO;
166 * Move the read/write pointer in a file
168 * @param h handle of an open file
169 * @param offset position to move to
170 * @param whence specification to which position the offset parameter relates to
171 * @return the new position on success, GNUNET_SYSERR otherwise
174 GNUNET_DISK_file_seek (const struct GNUNET_DISK_FileHandle * h, off_t offset,
175 enum GNUNET_DISK_Seek whence)
180 return GNUNET_SYSERR;
185 static DWORD t[] = {[GNUNET_DISK_SEEK_SET] = FILE_BEGIN,
186 [GNUNET_DISK_SEEK_CUR] = FILE_CURRENT,[GNUNET_DISK_SEEK_END] = FILE_END
189 ret = SetFilePointer (h->h, offset, NULL, t[whence]);
190 if (ret == INVALID_SET_FILE_POINTER)
192 SetErrnoFromWinError (GetLastError ());
193 return GNUNET_SYSERR;
197 static int t[] = {[GNUNET_DISK_SEEK_SET] = SEEK_SET,
198 [GNUNET_DISK_SEEK_CUR] = SEEK_CUR,[GNUNET_DISK_SEEK_END] = SEEK_END
201 return lseek (h->fd, offset, t[whence]);
207 * Get the size of the file (or directory) of the given file (in
210 * @param filename name of the file or directory
211 * @param size set to the size of the file (or,
212 * in the case of directories, the sum
213 * of all sizes of files in the directory)
214 * @param includeSymLinks should symbolic links be
216 * @return GNUNET_SYSERR on error, GNUNET_OK on success
219 GNUNET_DISK_file_size (const char *filename,
220 uint64_t * size, int includeSymLinks)
222 struct GetFileSizeData gfsd;
225 GNUNET_assert (size != NULL);
227 gfsd.include_sym_links = includeSymLinks;
228 ret = getSizeRec (&gfsd, filename);
235 * Obtain some unique identifiers for the given file
236 * that can be used to identify it in the local system.
237 * This function is used between GNUnet processes to
238 * quickly check if two files with the same absolute path
239 * are actually identical. The two processes represent
240 * the same peer but may communicate over the network
241 * (and the file may be on an NFS volume). This function
242 * may not be supported on all operating systems.
244 * @param filename name of the file
245 * @param dev set to the device ID
246 * @param ino set to the inode ID
247 * @return GNUNET_OK on success
250 GNUNET_DISK_file_get_identifiers (const char *filename,
251 uint32_t * dev, uint64_t * ino)
257 if ((0 == stat (filename, &sbuf)) && (0 == statvfs (filename, &fbuf)))
259 *dev = (uint32_t) fbuf.f_fsid;
260 *ino = (uint64_t) sbuf.st_ino;
264 // FIXME NILS: test this
265 struct GNUNET_DISK_FileHandle *fh;
266 BY_HANDLE_FILE_INFORMATION info;
269 fh = GNUNET_DISK_file_open(filename, GNUNET_DISK_OPEN_READ, 0);
271 return GNUNET_SYSERR;
272 succ = GetFileInformationByHandle(fh->h, &info);
273 GNUNET_DISK_file_close(fh);
276 *dev = info.dwVolumeSerialNumber;
277 *ino = ((info.nFileIndexHigh << sizeof(DWORD)) | info.nFileIndexLow);
281 return GNUNET_SYSERR;
284 return GNUNET_SYSERR;
289 * Create an (empty) temporary file on disk. If the given name is not
290 * an absolute path, the current 'TMPDIR' will be prepended. In any case,
291 * 6 random characters will be appended to the name to create a unique
294 * @param t component to use for the name;
295 * does NOT contain "XXXXXX" or "/tmp/".
296 * @return NULL on error, otherwise name of fresh
297 * file on disk in directory for temporary files
300 GNUNET_DISK_mktemp (const char *t)
307 if ( (t[0] != '/') &&
310 tmpdir = getenv ("TMPDIR");
311 tmpdir = tmpdir ? tmpdir : "/tmp";
312 GNUNET_asprintf (&tmpl, "%s/%s%s", tmpdir, t, "XXXXXX");
316 GNUNET_asprintf (&tmpl, "%s%s", t, "XXXXXX");
319 fn = (char *) GNUNET_malloc (MAX_PATH + 1);
320 if (ERROR_SUCCESS != plibc_conv_to_win_path (tmpl, fn))
333 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR, "mkstemp", fn);
338 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "close", fn);
344 * Get the number of blocks that are left on the partition that
345 * contains the given file (for normal users).
347 * @param part a file on the partition to check
348 * @return -1 on errors, otherwise the number of free blocks
351 GNUNET_DISK_get_blocks_available (const char *part)
356 if (0 != statvfs (part, &buf))
358 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "statfs", part);
368 path = GNUNET_STRINGS_filename_expand (part);
371 memcpy (szDrive, path, 3);
374 if (!GetDiskFreeSpace (szDrive, &dwDummy, &dwDummy, &dwBlocks, &dwDummy))
376 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
377 _("`%s' failed for drive `%s': %u\n"),
378 "GetDiskFreeSpace", szDrive, GetLastError ());
385 if (0 != statfs (part, &s))
387 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "statfs", part);
396 * Test if "fil" is a directory.
397 * Will not print an error message if the directory
398 * does not exist. Will log errors if GNUNET_SYSERR is
399 * returned (i.e., a file exists with the same name).
401 * @param fil filename to test
402 * @return GNUNET_YES if yes, GNUNET_NO if not, GNUNET_SYSERR if it
406 GNUNET_DISK_directory_test (const char *fil)
408 struct stat filestat;
411 ret = STAT (fil, &filestat);
416 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "stat", fil);
417 return GNUNET_SYSERR;
421 if (!S_ISDIR (filestat.st_mode))
423 if (ACCESS (fil, R_OK | X_OK) < 0)
425 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "access", fil);
426 return GNUNET_SYSERR;
432 * Check that fil corresponds to a filename
433 * (of a file that exists and that is not a directory).
435 * @param fil filename to check
436 * @return GNUNET_YES if yes, GNUNET_NO if not a file, GNUNET_SYSERR if something
437 * else (will print an error message in that case, too).
440 GNUNET_DISK_file_test (const char *fil)
442 struct stat filestat;
446 rdir = GNUNET_STRINGS_filename_expand (fil);
448 return GNUNET_SYSERR;
450 ret = STAT (rdir, &filestat);
455 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "stat", rdir);
457 return GNUNET_SYSERR;
462 if (!S_ISREG (filestat.st_mode))
467 if (ACCESS (rdir, R_OK) < 0)
469 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "access", rdir);
471 return GNUNET_SYSERR;
479 * Implementation of "mkdir -p"
480 * @param dir the directory to create
481 * @returns GNUNET_OK on success, GNUNET_SYSERR on failure
484 GNUNET_DISK_directory_create (const char *dir)
491 rdir = GNUNET_STRINGS_filename_expand (dir);
493 return GNUNET_SYSERR;
497 pos = 1; /* skip heading '/' */
499 /* Local or Network path? */
500 if (strncmp (rdir, "\\\\", 2) == 0)
505 if (rdir[pos] == '\\')
515 pos = 3; /* strlen("C:\\") */
520 if ((rdir[pos] == DIR_SEPARATOR) || (pos == len))
523 ret = GNUNET_DISK_directory_test (rdir);
524 if (ret == GNUNET_SYSERR)
527 return GNUNET_SYSERR;
529 if (ret == GNUNET_NO)
532 ret = mkdir (rdir, S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH); /* 755 */
536 if ((ret != 0) && (errno != EEXIST))
538 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR, "mkdir",
541 return GNUNET_SYSERR;
544 rdir[pos] = DIR_SEPARATOR;
554 * Create the directory structure for storing
557 * @param filename name of a file in the directory
558 * @returns GNUNET_OK on success,
559 * GNUNET_SYSERR on failure,
560 * GNUNET_NO if the directory
561 * exists but is not writeable for us
564 GNUNET_DISK_directory_create_for_file (const char *filename)
570 rdir = GNUNET_STRINGS_filename_expand (filename);
572 return GNUNET_SYSERR;
574 while ((len > 0) && (rdir[len] != DIR_SEPARATOR))
577 ret = GNUNET_DISK_directory_create (rdir);
578 if ((ret == GNUNET_OK) && (0 != ACCESS (rdir, W_OK)))
586 * Read the contents of a binary file into a buffer.
587 * @param h handle to an open file
588 * @param result the buffer to write the result to
589 * @param len the maximum number of bytes to read
590 * @return the number of bytes read on success, GNUNET_SYSERR on failure
593 GNUNET_DISK_file_read (const struct GNUNET_DISK_FileHandle * h, void *result,
599 return GNUNET_SYSERR;
605 if (!ReadFile (h->h, result, len, &bytesRead, NULL))
607 SetErrnoFromWinError (GetLastError ());
608 return GNUNET_SYSERR;
612 return read (h->fd, result, len);
618 * Read the contents of a binary file into a buffer.
620 * @param fn file name
621 * @param result the buffer to write the result to
622 * @param len the maximum number of bytes to read
623 * @return number of bytes read, GNUNET_SYSERR on failure
626 GNUNET_DISK_fn_read (const char *fn, void *result, size_t len)
628 struct GNUNET_DISK_FileHandle *fh;
631 fh = GNUNET_DISK_file_open (fn, GNUNET_DISK_OPEN_READ,
632 GNUNET_DISK_PERM_NONE);
634 return GNUNET_SYSERR;
635 ret = GNUNET_DISK_file_read (fh, result, len);
636 GNUNET_assert (GNUNET_OK == GNUNET_DISK_file_close (fh));
643 * Write a buffer to a file.
644 * @param h handle to open file
645 * @param buffer the data to write
646 * @param n number of bytes to write
647 * @return number of bytes written on success, GNUNET_SYSERR on error
650 GNUNET_DISK_file_write (const struct GNUNET_DISK_FileHandle * h,
651 const void *buffer, size_t n)
656 return GNUNET_SYSERR;
662 if (!WriteFile (h->h, buffer, n, &bytesWritten, NULL))
664 SetErrnoFromWinError (GetLastError ());
665 return GNUNET_SYSERR;
669 return write (h->fd, buffer, n);
674 * Write a buffer to a file. If the file is longer than the
675 * number of bytes that will be written, it will be truncated.
677 * @param fn file name
678 * @param buffer the data to write
679 * @param n number of bytes to write
680 * @param mode file permissions
681 * @return number of bytes written on success, GNUNET_SYSERR on error
684 GNUNET_DISK_fn_write (const char *fn, const void *buffer,
685 size_t n, enum GNUNET_DISK_AccessPermissions mode)
687 struct GNUNET_DISK_FileHandle *fh;
690 fh = GNUNET_DISK_file_open (fn,
691 GNUNET_DISK_OPEN_WRITE
692 | GNUNET_DISK_OPEN_TRUNCATE
693 | GNUNET_DISK_OPEN_CREATE, mode);
695 return GNUNET_SYSERR;
696 ret = GNUNET_DISK_file_write (fh, buffer, n);
697 GNUNET_assert (GNUNET_OK == GNUNET_DISK_file_close (fh));
702 * Scan a directory for files.
704 * @param dirName the name of the directory
705 * @param callback the method to call for each file,
706 * can be NULL, in that case, we only count
707 * @param callback_cls closure for callback
708 * @return the number of files found, GNUNET_SYSERR on error or
709 * ieration aborted by callback returning GNUNET_SYSERR
712 GNUNET_DISK_directory_scan (const char *dirName,
713 GNUNET_FileNameCallback callback,
717 struct dirent *finfo;
722 unsigned int name_len;
725 GNUNET_assert (dirName != NULL);
726 dname = GNUNET_STRINGS_filename_expand (dirName);
728 return GNUNET_SYSERR;
729 while ((strlen (dname) > 0) && (dname[strlen (dname) - 1] == DIR_SEPARATOR))
730 dname[strlen (dname) - 1] = '\0';
731 if (0 != STAT (dname, &istat))
733 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "stat", dname);
735 return GNUNET_SYSERR;
737 if (!S_ISDIR (istat.st_mode))
739 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
740 _("Expected `%s' to be a directory!\n"), dirName);
742 return GNUNET_SYSERR;
745 dinfo = OPENDIR (dname);
746 if ((errno == EACCES) || (dinfo == NULL))
748 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "opendir", dname);
752 return GNUNET_SYSERR;
755 n_size = strlen (dname) + name_len + 2;
756 name = GNUNET_malloc (n_size);
757 while ((finfo = readdir (dinfo)) != NULL)
759 if ((0 == strcmp (finfo->d_name, ".")) ||
760 (0 == strcmp (finfo->d_name, "..")))
762 if (callback != NULL)
764 if (name_len < strlen (finfo->d_name))
767 name_len = strlen (finfo->d_name);
768 n_size = strlen (dname) + name_len + 2;
769 name = GNUNET_malloc (n_size);
771 /* dname can end in "/" only if dname == "/";
772 if dname does not end in "/", we need to add
773 a "/" (otherwise, we must not!) */
774 GNUNET_snprintf (name,
778 (strcmp (dname, DIR_SEPARATOR_STR) ==
779 0) ? "" : DIR_SEPARATOR_STR, finfo->d_name);
780 if (GNUNET_OK != callback (callback_cls, name))
785 return GNUNET_SYSERR;
798 * Opaque handle used for iterating over a directory.
800 struct GNUNET_DISK_DirectoryIterator
805 struct GNUNET_SCHEDULER_Handle *sched;
808 * Function to call on directory entries.
810 GNUNET_DISK_DirectoryIteratorCallback callback;
813 * Closure for callback.
818 * Reference to directory.
828 * Next filename to process.
835 enum GNUNET_SCHEDULER_Priority priority;
841 * Task used by the directory iterator.
844 directory_iterator_task (void *cls,
845 const struct GNUNET_SCHEDULER_TaskContext *tc)
847 struct GNUNET_DISK_DirectoryIterator *iter = cls;
850 name = iter->next_name;
851 GNUNET_assert (name != NULL);
852 iter->next_name = NULL;
853 iter->callback (iter->callback_cls, iter, name, iter->dirname);
859 * This function must be called during the DiskIteratorCallback
860 * (exactly once) to schedule the task to process the next
861 * filename in the directory (if there is one).
863 * @param iter opaque handle for the iterator
864 * @param can set to GNUNET_YES to terminate the iteration early
865 * @return GNUNET_YES if iteration will continue,
866 * GNUNET_NO if this was the last entry (and iteration is complete),
867 * GNUNET_SYSERR if abort was YES
870 GNUNET_DISK_directory_iterator_next (struct GNUNET_DISK_DirectoryIterator
873 struct dirent *finfo;
875 GNUNET_assert (iter->next_name == NULL);
876 if (can == GNUNET_YES)
878 closedir (iter->directory);
879 GNUNET_free (iter->dirname);
881 return GNUNET_SYSERR;
883 while (NULL != (finfo = readdir (iter->directory)))
885 if ((0 == strcmp (finfo->d_name, ".")) ||
886 (0 == strcmp (finfo->d_name, "..")))
888 GNUNET_asprintf (&iter->next_name,
890 iter->dirname, DIR_SEPARATOR_STR, finfo->d_name);
895 GNUNET_DISK_directory_iterator_next (iter, GNUNET_YES);
898 GNUNET_SCHEDULER_add_with_priority (iter->sched,
900 &directory_iterator_task, iter);
906 * Scan a directory for files using the scheduler to run a task for
907 * each entry. The name of the directory must be expanded first (!).
908 * If a scheduler does not need to be used, GNUNET_DISK_directory_scan
909 * may provide a simpler API.
911 * @param sched scheduler to use
912 * @param prio priority to use
913 * @param dirName the name of the directory
914 * @param callback the method to call for each file
915 * @param callback_cls closure for callback
918 GNUNET_DISK_directory_iterator_start (struct GNUNET_SCHEDULER_Handle *sched,
919 enum GNUNET_SCHEDULER_Priority prio,
921 GNUNET_DISK_DirectoryIteratorCallback
922 callback, void *callback_cls)
924 struct GNUNET_DISK_DirectoryIterator *di;
926 di = GNUNET_malloc (sizeof (struct GNUNET_DISK_DirectoryIterator));
928 di->callback = callback;
929 di->callback_cls = callback_cls;
930 di->directory = OPENDIR (dirName);
931 if (di->directory == NULL)
934 callback (callback_cls, NULL, NULL, NULL);
937 di->dirname = GNUNET_strdup (dirName);
939 GNUNET_DISK_directory_iterator_next (di, GNUNET_NO);
944 * Function that removes the given directory by calling
945 * "GNUNET_DISK_directory_remove".
947 * @param unused not used
948 * @param fn directory to remove
952 remove_helper (void *unused, const char *fn)
954 (void) GNUNET_DISK_directory_remove (fn);
960 * Remove all files in a directory (rm -rf). Call with
964 * @param fileName the file to remove
965 * @return GNUNET_OK on success, GNUNET_SYSERR on error
968 GNUNET_DISK_directory_remove (const char *fileName)
972 if (0 != LSTAT (fileName, &istat))
973 return GNUNET_NO; /* file may not exist... */
974 if (UNLINK (fileName) == 0)
976 if ((errno != EISDIR) &&
977 /* EISDIR is not sufficient in all cases, e.g.
978 sticky /tmp directory may result in EPERM on BSD.
979 So we also explicitly check "isDirectory" */
980 (GNUNET_YES != GNUNET_DISK_directory_test (fileName)))
982 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "rmdir", fileName);
983 return GNUNET_SYSERR;
986 GNUNET_DISK_directory_scan (fileName, &remove_helper, NULL))
987 return GNUNET_SYSERR;
988 if (0 != RMDIR (fileName))
990 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "rmdir", fileName);
991 return GNUNET_SYSERR;
1000 * @param src file to copy
1001 * @param dst destination file name
1002 * @return GNUNET_OK on success, GNUNET_SYSERR on error
1005 GNUNET_DISK_file_copy (const char *src, const char *dst)
1011 struct GNUNET_DISK_FileHandle *in;
1012 struct GNUNET_DISK_FileHandle *out;
1014 if (GNUNET_OK != GNUNET_DISK_file_size (src, &size, GNUNET_YES))
1015 return GNUNET_SYSERR;
1017 in = GNUNET_DISK_file_open (src, GNUNET_DISK_OPEN_READ,
1018 GNUNET_DISK_PERM_NONE);
1020 return GNUNET_SYSERR;
1021 out = GNUNET_DISK_file_open (dst, GNUNET_DISK_OPEN_WRITE
1022 | GNUNET_DISK_OPEN_CREATE |
1023 GNUNET_DISK_OPEN_FAILIFEXISTS,
1024 GNUNET_DISK_PERM_USER_READ |
1025 GNUNET_DISK_PERM_USER_WRITE |
1026 GNUNET_DISK_PERM_GROUP_READ |
1027 GNUNET_DISK_PERM_GROUP_WRITE);
1030 GNUNET_DISK_file_close (in);
1031 return GNUNET_SYSERR;
1033 buf = GNUNET_malloc (COPY_BLK_SIZE);
1036 len = COPY_BLK_SIZE;
1037 if (len > size - pos)
1039 if (len != GNUNET_DISK_file_read (in, buf, len))
1041 if (len != GNUNET_DISK_file_write (out, buf, len))
1046 GNUNET_DISK_file_close (in);
1047 GNUNET_DISK_file_close (out);
1051 GNUNET_DISK_file_close (in);
1052 GNUNET_DISK_file_close (out);
1053 return GNUNET_SYSERR;
1058 * @brief Removes special characters as ':' from a filename.
1059 * @param fn the filename to canonicalize
1062 GNUNET_DISK_filename_canonicalize (char *fn)
1072 if (c == '/' || c == '\\' || c == ':' || c == '*' || c == '?' ||
1073 c == '"' || c == '<' || c == '>' || c == '|')
1085 * @brief Change owner of a file
1087 * @param filename name of file to change the owner of
1088 * @param user name of the new owner
1089 * @return GNUNET_OK on success, GNUNET_SYSERR on failure
1092 GNUNET_DISK_file_change_owner (const char *filename, const char *user)
1097 pws = getpwnam (user);
1100 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1101 _("Cannot obtain information about user `%s': %s\n"),
1102 user, STRERROR (errno));
1103 return GNUNET_SYSERR;
1105 if (0 != chown (filename, pws->pw_uid, pws->pw_gid))
1106 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "chown", filename);
1113 * Lock a part of a file
1114 * @param fh file handle
1115 * @param lockStart absolute position from where to lock
1116 * @param lockEnd absolute position until where to lock
1117 * @param excl GNUNET_YES for an exclusive lock
1118 * @return GNUNET_OK on success, GNUNET_SYSERR on error
1121 GNUNET_DISK_file_lock (struct GNUNET_DISK_FileHandle *fh, off_t lockStart,
1122 off_t lockEnd, int excl)
1127 return GNUNET_SYSERR;
1133 memset (&fl, 0, sizeof (struct flock));
1134 fl.l_type = excl ? F_WRLCK : F_RDLCK;
1135 fl.l_whence = SEEK_SET;
1136 fl.l_start = lockStart;
1139 return fcntl (fh->fd, F_SETLK, &fl) != 0 ? GNUNET_SYSERR : GNUNET_OK;
1143 memset (&o, 0, sizeof (OVERLAPPED));
1144 o.Offset = lockStart;
1146 if (!LockFileEx (fh->h, (excl ? LOCKFILE_EXCLUSIVE_LOCK : 0)
1147 | LOCKFILE_FAIL_IMMEDIATELY, 0, lockEnd - lockStart, 0,
1150 SetErrnoFromWinError (GetLastError ());
1151 return GNUNET_SYSERR;
1160 * Unlock a part of a file
1161 * @param fh file handle
1162 * @param unlockStart absolute position from where to unlock
1163 * @param unlockEnd absolute position until where to unlock
1164 * @return GNUNET_OK on success, GNUNET_SYSERR on error
1167 GNUNET_DISK_file_unlock (struct GNUNET_DISK_FileHandle *fh, off_t unlockStart,
1173 return GNUNET_SYSERR;
1179 memset (&fl, 0, sizeof (struct flock));
1180 fl.l_type = F_UNLCK;
1181 fl.l_whence = SEEK_SET;
1182 fl.l_start = unlockStart;
1183 fl.l_len = unlockEnd;
1185 return fcntl (fh->fd, F_SETLK, &fl) != 0 ? GNUNET_SYSERR : GNUNET_OK;
1189 memset (&o, 0, sizeof (OVERLAPPED));
1190 o.Offset = unlockStart;
1192 if (!UnlockFileEx (fh->h, 0, unlockEnd - unlockStart, 0, &o))
1194 SetErrnoFromWinError (GetLastError ());
1195 return GNUNET_SYSERR;
1204 * Open a file. Note that the access permissions will only be
1205 * used if a new file is created and if the underlying operating
1206 * system supports the given permissions.
1208 * @param fn file name to be opened
1209 * @param flags opening flags, a combination of GNUNET_DISK_OPEN_xxx bit flags
1210 * @param perm permissions for the newly created file, use
1211 * GNUNET_DISK_PERM_USER_NONE if a file could not be created by this
1212 * call (because of flags)
1213 * @return IO handle on success, NULL on error
1215 struct GNUNET_DISK_FileHandle *
1216 GNUNET_DISK_file_open (const char *fn,
1217 enum GNUNET_DISK_OpenFlags flags,
1218 enum GNUNET_DISK_AccessPermissions perm)
1221 struct GNUNET_DISK_FileHandle *ret;
1232 expfn = GNUNET_STRINGS_filename_expand (fn);
1237 if (GNUNET_DISK_OPEN_READWRITE == (flags & GNUNET_DISK_OPEN_READWRITE))
1238 oflags = O_RDWR; /* note: O_RDWR is NOT always O_RDONLY | O_WRONLY */
1239 else if (flags & GNUNET_DISK_OPEN_READ)
1241 else if (flags & GNUNET_DISK_OPEN_WRITE)
1246 GNUNET_free (expfn);
1249 if (flags & GNUNET_DISK_OPEN_FAILIFEXISTS)
1250 oflags |= (O_CREAT | O_EXCL);
1251 if (flags & GNUNET_DISK_OPEN_TRUNCATE)
1253 if (flags & GNUNET_DISK_OPEN_APPEND)
1255 if (flags & GNUNET_DISK_OPEN_CREATE)
1257 (void) GNUNET_DISK_directory_create_for_file (expfn);
1259 if (perm & GNUNET_DISK_PERM_USER_READ)
1261 if (perm & GNUNET_DISK_PERM_USER_WRITE)
1263 if (perm & GNUNET_DISK_PERM_USER_EXEC)
1265 if (perm & GNUNET_DISK_PERM_GROUP_READ)
1267 if (perm & GNUNET_DISK_PERM_GROUP_WRITE)
1269 if (perm & GNUNET_DISK_PERM_GROUP_EXEC)
1271 if (perm & GNUNET_DISK_PERM_OTHER_READ)
1273 if (perm & GNUNET_DISK_PERM_OTHER_WRITE)
1275 if (perm & GNUNET_DISK_PERM_OTHER_EXEC)
1279 fd = open (expfn, oflags | O_LARGEFILE, mode);
1282 if (0 == (flags & GNUNET_DISK_OPEN_FAILIFEXISTS))
1283 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "open", expfn);
1285 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_DEBUG, "open", expfn);
1286 GNUNET_free (expfn);
1293 if (GNUNET_DISK_OPEN_READWRITE == (flags & GNUNET_DISK_OPEN_READWRITE))
1294 access = FILE_READ_DATA | FILE_WRITE_DATA;
1295 else if (flags & GNUNET_DISK_OPEN_READ)
1296 access = FILE_READ_DATA;
1297 else if (flags & GNUNET_DISK_OPEN_WRITE)
1298 access = FILE_WRITE_DATA;
1300 if (flags & GNUNET_DISK_OPEN_FAILIFEXISTS)
1304 else if (flags & GNUNET_DISK_OPEN_CREATE)
1306 if (flags & GNUNET_DISK_OPEN_TRUNCATE)
1307 disp = CREATE_ALWAYS;
1311 else if (flags & GNUNET_DISK_OPEN_TRUNCATE)
1313 disp = TRUNCATE_EXISTING;
1317 disp = OPEN_EXISTING;
1320 /* TODO: access priviledges? */
1321 h = CreateFile (expfn, access, FILE_SHARE_DELETE | FILE_SHARE_READ
1322 | FILE_SHARE_WRITE, NULL, disp, FILE_ATTRIBUTE_NORMAL,
1324 if (h == INVALID_HANDLE_VALUE)
1326 SetErrnoFromWinError (GetLastError ());
1327 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "open", expfn);
1328 GNUNET_free (expfn);
1332 if (flags & GNUNET_DISK_OPEN_APPEND)
1333 if (SetFilePointer (h, 0, 0, FILE_END) == INVALID_SET_FILE_POINTER)
1335 SetErrnoFromWinError (GetLastError ());
1336 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "SetFilePointer",
1339 GNUNET_free (expfn);
1344 ret = GNUNET_malloc (sizeof (struct GNUNET_DISK_FileHandle));
1350 GNUNET_free (expfn);
1356 * Close an open file
1357 * @param h file handle
1358 * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
1361 GNUNET_DISK_file_close (struct GNUNET_DISK_FileHandle *h)
1366 return GNUNET_SYSERR;
1370 if (!CloseHandle (h->h))
1372 SetErrnoFromWinError (GetLastError ());
1373 GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "close");
1375 return GNUNET_SYSERR;
1378 if (close (h->fd) != 0)
1380 GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "close");
1382 return GNUNET_SYSERR;
1391 * Construct full path to a file inside of the private
1392 * directory used by GNUnet. Also creates the corresponding
1393 * directory. If the resulting name is supposed to be
1394 * a directory, end the last argument in '/' (or pass
1395 * DIR_SEPARATOR_STR as the last argument before NULL).
1397 * @param cfg configuration to use (determines HOME)
1398 * @param serviceName name of the service
1399 * @param ... is NULL-terminated list of
1400 * path components to append to the
1401 * private directory name.
1402 * @return the constructed filename
1405 GNUNET_DISK_get_home_filename (const struct GNUNET_CONFIGURATION_Handle *cfg,
1406 const char *serviceName, ...)
1412 unsigned int needed;
1415 GNUNET_CONFIGURATION_get_value_filename (cfg,
1416 serviceName, "HOME", &pfx))
1420 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1421 _("No `%s' specified for service `%s' in configuration.\n"),
1422 "HOME", serviceName);
1425 needed = strlen (pfx) + 2;
1426 if ((pfx[strlen (pfx) - 1] != '/') && (pfx[strlen (pfx) - 1] != '\\'))
1428 va_start (ap, serviceName);
1431 c = va_arg (ap, const char *);
1434 needed += strlen (c);
1435 if ((c[strlen (c) - 1] != '/') && (c[strlen (c) - 1] != '\\'))
1439 ret = GNUNET_malloc (needed);
1442 va_start (ap, serviceName);
1445 c = va_arg (ap, const char *);
1448 if ((c[strlen (c) - 1] != '/') && (c[strlen (c) - 1] != '\\'))
1449 strcat (ret, DIR_SEPARATOR_STR);
1453 if ((ret[strlen (ret) - 1] != '/') && (ret[strlen (ret) - 1] != '\\'))
1454 GNUNET_DISK_directory_create_for_file (ret);
1456 GNUNET_DISK_directory_create (ret);
1462 * Handle for a memory-mapping operation.
1464 struct GNUNET_DISK_MapHandle
1468 * Underlying OS handle.
1473 * Address where the map is in memory.
1478 * Number of bytes mapped.
1486 #define MAP_FAILED ((void *) -1)
1490 * Map a file into memory
1492 * @param h open file handle
1493 * @param m handle to the new mapping
1494 * @param access access specification, GNUNET_DISK_MAP_TYPE_xxx
1495 * @param len size of the mapping
1496 * @return pointer to the mapped memory region, NULL on failure
1499 GNUNET_DISK_file_map (const struct GNUNET_DISK_FileHandle *h,
1500 struct GNUNET_DISK_MapHandle **m,
1501 enum GNUNET_DISK_MapType access, size_t len)
1510 DWORD mapAccess, protect;
1513 if ((access & GNUNET_DISK_MAP_TYPE_READ) &&
1514 (access & GNUNET_DISK_MAP_TYPE_WRITE))
1516 protect = PAGE_READWRITE;
1517 mapAccess = FILE_MAP_ALL_ACCESS;
1519 else if (access & GNUNET_DISK_MAP_TYPE_READ)
1521 protect = PAGE_READONLY;
1522 mapAccess = FILE_MAP_READ;
1524 else if (access & GNUNET_DISK_MAP_TYPE_WRITE)
1526 protect = PAGE_READWRITE;
1527 mapAccess = FILE_MAP_WRITE;
1535 *m = GNUNET_malloc (sizeof (struct GNUNET_DISK_MapHandle));
1536 (*m)->h = CreateFileMapping (h->h, NULL, protect, 0, 0, NULL);
1537 if ((*m)->h == INVALID_HANDLE_VALUE)
1539 SetErrnoFromWinError (GetLastError ());
1544 ret = MapViewOfFile ((*m)->h, mapAccess, 0, 0, len);
1547 SetErrnoFromWinError (GetLastError ());
1548 CloseHandle ((*m)->h);
1557 if (access & GNUNET_DISK_MAP_TYPE_READ)
1559 if (access & GNUNET_DISK_MAP_TYPE_WRITE)
1561 *m = GNUNET_malloc (sizeof (struct GNUNET_DISK_MapHandle));
1562 (*m)->addr = mmap (NULL, len, prot, MAP_SHARED, h->fd, 0);
1563 GNUNET_assert (NULL != (*m)->addr);
1564 if (MAP_FAILED == (*m)->addr)
1576 * @param h mapping handle
1577 * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
1580 GNUNET_DISK_file_unmap (struct GNUNET_DISK_MapHandle *h)
1586 return GNUNET_SYSERR;
1590 ret = UnmapViewOfFile (h->h) ? GNUNET_OK : GNUNET_SYSERR;
1591 if (ret != GNUNET_OK)
1592 SetErrnoFromWinError (GetLastError ());
1593 if (!CloseHandle (h->h) && (ret == GNUNET_OK))
1595 ret = GNUNET_SYSERR;
1596 SetErrnoFromWinError (GetLastError ());
1599 ret = munmap (h->addr, h->len) != -1 ? GNUNET_OK : GNUNET_SYSERR;
1607 * Write file changes to disk
1608 * @param h handle to an open file
1609 * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
1612 GNUNET_DISK_file_sync (const struct GNUNET_DISK_FileHandle *h)
1617 return GNUNET_SYSERR;
1623 ret = FlushFileBuffers (h->h) ? GNUNET_OK : GNUNET_SYSERR;
1624 if (ret != GNUNET_OK)
1625 SetErrnoFromWinError (GetLastError ());
1627 #elif defined(FREEBSD) || defined(OPENBSD) || defined(DARWIN)
1628 return fsync (h->fd) == -1 ? GNUNET_SYSERR : GNUNET_OK;
1630 return fdatasync (h->fd) == -1 ? GNUNET_SYSERR : GNUNET_OK;
1635 * Creates an interprocess channel
1637 * @param blocking creates an asynchronous pipe if set to GNUNET_NO
1638 * @return handle to the new pipe, NULL on error
1640 struct GNUNET_DISK_PipeHandle *
1641 GNUNET_DISK_pipe (int blocking)
1643 struct GNUNET_DISK_PipeHandle *p;
1644 struct GNUNET_DISK_FileHandle *fds;
1647 GNUNET_malloc (sizeof (struct GNUNET_DISK_PipeHandle) +
1648 2 * sizeof (struct GNUNET_DISK_FileHandle));
1649 fds = (struct GNUNET_DISK_FileHandle *) &p[1];
1664 p->fd[0]->fd = fd[0];
1665 p->fd[1]->fd = fd[1];
1667 flags = fcntl (fd[0], F_GETFL);
1668 flags |= FD_CLOEXEC;
1670 flags |= O_NONBLOCK;
1671 if (0 > fcntl (fd[0], F_SETFL, flags))
1673 flags = fcntl (fd[1], F_GETFL);
1674 flags |= FD_CLOEXEC;
1676 flags |= O_NONBLOCK;
1677 if (0 > fcntl (fd[1], F_SETFL, flags))
1682 GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "fcntl");
1683 GNUNET_break (0 == close (p->fd[0]->fd));
1684 GNUNET_break (0 == close (p->fd[1]->fd));
1692 ret = CreatePipe (&p->fd[0]->h, &p->fd[1]->h, NULL, 0);
1696 SetErrnoFromWinError (GetLastError ());
1704 SetNamedPipeHandleState (p->fd[0]->h, &mode, NULL, NULL);
1705 SetNamedPipeHandleState (p->fd[1]->h, &mode, NULL, NULL);
1706 /* this always fails on Windows 95, so we don't care about error handling */
1714 * Closes an interprocess channel
1716 * @param p pipe to close
1717 * @param end which end of the pipe to close
1718 * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
1721 GNUNET_DISK_pipe_close_end (struct GNUNET_DISK_PipeHandle *p,
1722 enum GNUNET_DISK_PipeEnd end)
1724 int ret = GNUNET_OK;
1728 if (end == GNUNET_DISK_PIPE_END_READ)
1730 if (!CloseHandle (p->fd[0]->h))
1732 SetErrnoFromWinError (GetLastError ());
1733 ret = GNUNET_SYSERR;
1735 p->fd[0]->h = INVALID_HANDLE_VALUE;
1737 else if (end == GNUNET_DISK_PIPE_END_WRITE)
1739 if (!CloseHandle (p->fd[1]->h))
1741 SetErrnoFromWinError (GetLastError ());
1742 ret = GNUNET_SYSERR;
1744 p->fd[1]->h = INVALID_HANDLE_VALUE;
1749 if (end == GNUNET_DISK_PIPE_END_READ)
1751 if (0 != close (p->fd[0]->fd))
1753 ret = GNUNET_SYSERR;
1758 else if (end == GNUNET_DISK_PIPE_END_WRITE)
1760 if (0 != close (p->fd[1]->fd))
1762 ret = GNUNET_SYSERR;
1773 * Closes an interprocess channel
1775 * @param p pipe to close
1776 * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
1779 GNUNET_DISK_pipe_close (struct GNUNET_DISK_PipeHandle *p)
1781 int ret = GNUNET_OK;
1785 if (!CloseHandle (p->fd[0]->h))
1787 SetErrnoFromWinError (GetLastError ());
1788 ret = GNUNET_SYSERR;
1790 if (!CloseHandle (p->fd[1]->h))
1792 SetErrnoFromWinError (GetLastError ());
1793 ret = GNUNET_SYSERR;
1798 if (p->fd[0]->fd != -1)
1800 if (0 != close (p->fd[0]->fd))
1802 ret = GNUNET_SYSERR;
1807 if (p->fd[1]->fd != -1)
1809 if (0 != close (p->fd[1]->fd))
1811 ret = GNUNET_SYSERR;
1823 * Get the handle to a particular pipe end
1826 * @param n end to access
1827 * @return handle for the respective end
1829 const struct GNUNET_DISK_FileHandle *
1830 GNUNET_DISK_pipe_handle (const struct GNUNET_DISK_PipeHandle *p,
1831 enum GNUNET_DISK_PipeEnd n)
1835 case GNUNET_DISK_PIPE_END_READ:
1836 case GNUNET_DISK_PIPE_END_WRITE:
1846 * Retrieve OS file handle
1848 * @param fh GNUnet file descriptor
1849 * @param dst destination buffer
1850 * @param dst_len length of dst
1851 * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
1854 GNUNET_DISK_internal_file_handle_ (const struct GNUNET_DISK_FileHandle *fh,
1855 void *dst, size_t dst_len)
1858 if (dst_len < sizeof (HANDLE))
1859 return GNUNET_SYSERR;
1860 *((HANDLE *) dst) = fh->h;
1862 if (dst_len < sizeof (int))
1863 return GNUNET_SYSERR;
1864 *((int *) dst) = fh->fd;