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;
104 int translate_unix_perms(enum GNUNET_DISK_AccessPermissions perm)
109 if (perm & GNUNET_DISK_PERM_USER_READ)
111 if (perm & GNUNET_DISK_PERM_USER_WRITE)
113 if (perm & GNUNET_DISK_PERM_USER_EXEC)
115 if (perm & GNUNET_DISK_PERM_GROUP_READ)
117 if (perm & GNUNET_DISK_PERM_GROUP_WRITE)
119 if (perm & GNUNET_DISK_PERM_GROUP_EXEC)
121 if (perm & GNUNET_DISK_PERM_OTHER_READ)
123 if (perm & GNUNET_DISK_PERM_OTHER_WRITE)
125 if (perm & GNUNET_DISK_PERM_OTHER_EXEC)
133 * Iterate over all files in the given directory and
134 * accumulate their size.
136 * @param cls closure of type "struct GetFileSizeData"
137 * @param fn current filename we are looking at
138 * @return GNUNET_SYSERR on serious errors, otherwise GNUNET_OK
141 getSizeRec (void *cls, const char *fn)
143 struct GetFileSizeData *gfsd = cls;
151 if (0 != STAT64 (fn, &buf))
153 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "stat64", fn);
154 return GNUNET_SYSERR;
157 if (0 != STAT (fn, &buf))
159 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "stat", fn);
160 return GNUNET_SYSERR;
163 if ((!S_ISLNK (buf.st_mode)) || (gfsd->include_sym_links == GNUNET_YES))
164 gfsd->total += buf.st_size;
165 if ((S_ISDIR (buf.st_mode)) &&
166 (0 == ACCESS (fn, X_OK)) &&
167 ((!S_ISLNK (buf.st_mode)) || (gfsd->include_sym_links == GNUNET_YES)))
169 if (GNUNET_SYSERR == GNUNET_DISK_directory_scan (fn, &getSizeRec, gfsd))
170 return GNUNET_SYSERR;
177 * Checks whether a handle is invalid
179 * @param h handle to check
180 * @return GNUNET_YES if invalid, GNUNET_NO if valid
183 GNUNET_DISK_handle_invalid (const struct GNUNET_DISK_FileHandle *h)
186 return ((!h) || (h->h == INVALID_HANDLE_VALUE)) ? GNUNET_YES : GNUNET_NO;
188 return ((!h) || (h->fd == -1)) ? GNUNET_YES : GNUNET_NO;
194 * Move the read/write pointer in a file
196 * @param h handle of an open file
197 * @param offset position to move to
198 * @param whence specification to which position the offset parameter relates to
199 * @return the new position on success, GNUNET_SYSERR otherwise
202 GNUNET_DISK_file_seek (const struct GNUNET_DISK_FileHandle * h, off_t offset,
203 enum GNUNET_DISK_Seek whence)
208 return GNUNET_SYSERR;
213 static DWORD t[] = {[GNUNET_DISK_SEEK_SET] = FILE_BEGIN,
214 [GNUNET_DISK_SEEK_CUR] = FILE_CURRENT,[GNUNET_DISK_SEEK_END] = FILE_END
217 ret = SetFilePointer (h->h, offset, NULL, t[whence]);
218 if (ret == INVALID_SET_FILE_POINTER)
220 SetErrnoFromWinError (GetLastError ());
221 return GNUNET_SYSERR;
225 static int t[] = {[GNUNET_DISK_SEEK_SET] = SEEK_SET,
226 [GNUNET_DISK_SEEK_CUR] = SEEK_CUR,[GNUNET_DISK_SEEK_END] = SEEK_END
229 return lseek (h->fd, offset, t[whence]);
235 * Get the size of the file (or directory) of the given file (in
238 * @param filename name of the file or directory
239 * @param size set to the size of the file (or,
240 * in the case of directories, the sum
241 * of all sizes of files in the directory)
242 * @param includeSymLinks should symbolic links be
244 * @return GNUNET_SYSERR on error, GNUNET_OK on success
247 GNUNET_DISK_file_size (const char *filename,
248 uint64_t * size, int includeSymLinks)
250 struct GetFileSizeData gfsd;
253 GNUNET_assert (size != NULL);
255 gfsd.include_sym_links = includeSymLinks;
256 ret = getSizeRec (&gfsd, filename);
263 * Obtain some unique identifiers for the given file
264 * that can be used to identify it in the local system.
265 * This function is used between GNUnet processes to
266 * quickly check if two files with the same absolute path
267 * are actually identical. The two processes represent
268 * the same peer but may communicate over the network
269 * (and the file may be on an NFS volume). This function
270 * may not be supported on all operating systems.
272 * @param filename name of the file
273 * @param dev set to the device ID
274 * @param ino set to the inode ID
275 * @return GNUNET_OK on success
278 GNUNET_DISK_file_get_identifiers (const char *filename,
279 uint64_t * dev, uint64_t * ino)
285 if ((0 == stat (filename, &sbuf)) && (0 == statvfs (filename, &fbuf)))
287 *dev = (uint64_t) fbuf.f_fsid;
288 *ino = (uint64_t) sbuf.st_ino;
295 if ( (0 == stat (filename, &sbuf)) &&
296 (0 == statfs (filename, &fbuf) ) )
298 *dev = ((uint64_t) fbuf.f_fsid.val[0]) << 32 || ((uint64_t) fbuf.f_fsid.val[1]);
299 *ino = (uint64_t) sbuf.st_ino;
303 // FIXME NILS: test this
304 struct GNUNET_DISK_FileHandle *fh;
305 BY_HANDLE_FILE_INFORMATION info;
308 fh = GNUNET_DISK_file_open(filename, GNUNET_DISK_OPEN_READ, 0);
310 return GNUNET_SYSERR;
311 succ = GetFileInformationByHandle(fh->h, &info);
312 GNUNET_DISK_file_close(fh);
315 *dev = info.dwVolumeSerialNumber;
316 *ino = ((info.nFileIndexHigh << sizeof(DWORD)) | info.nFileIndexLow);
320 return GNUNET_SYSERR;
323 return GNUNET_SYSERR;
328 * Create an (empty) temporary file on disk. If the given name is not
329 * an absolute path, the current 'TMPDIR' will be prepended. In any case,
330 * 6 random characters will be appended to the name to create a unique
333 * @param t component to use for the name;
334 * does NOT contain "XXXXXX" or "/tmp/".
335 * @return NULL on error, otherwise name of fresh
336 * file on disk in directory for temporary files
339 GNUNET_DISK_mktemp (const char *t)
346 if ( (t[0] != '/') &&
349 tmpdir = getenv ("TMPDIR");
350 tmpdir = tmpdir ? tmpdir : "/tmp";
351 GNUNET_asprintf (&tmpl, "%s/%s%s", tmpdir, t, "XXXXXX");
355 GNUNET_asprintf (&tmpl, "%s%s", t, "XXXXXX");
358 fn = (char *) GNUNET_malloc (MAX_PATH + 1);
359 if (ERROR_SUCCESS != plibc_conv_to_win_path (tmpl, fn))
372 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR, "mkstemp", fn);
377 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "close", fn);
383 * Get the number of blocks that are left on the partition that
384 * contains the given file (for normal users).
386 * @param part a file on the partition to check
387 * @return -1 on errors, otherwise the number of free blocks
390 GNUNET_DISK_get_blocks_available (const char *part)
395 if (0 != statvfs (part, &buf))
397 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "statfs", part);
407 path = GNUNET_STRINGS_filename_expand (part);
410 memcpy (szDrive, path, 3);
413 if (!GetDiskFreeSpace (szDrive, &dwDummy, &dwDummy, &dwBlocks, &dwDummy))
415 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
416 _("`%s' failed for drive `%s': %u\n"),
417 "GetDiskFreeSpace", szDrive, GetLastError ());
424 if (0 != statfs (part, &s))
426 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "statfs", part);
435 * Test if "fil" is a directory.
436 * Will not print an error message if the directory
437 * does not exist. Will log errors if GNUNET_SYSERR is
438 * returned (i.e., a file exists with the same name).
440 * @param fil filename to test
441 * @return GNUNET_YES if yes, GNUNET_NO if not, GNUNET_SYSERR if it
445 GNUNET_DISK_directory_test (const char *fil)
447 struct stat filestat;
450 ret = STAT (fil, &filestat);
455 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "stat", fil);
456 return GNUNET_SYSERR;
460 if (!S_ISDIR (filestat.st_mode))
462 if (ACCESS (fil, R_OK | X_OK) < 0)
464 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "access", fil);
465 return GNUNET_SYSERR;
471 * Check that fil corresponds to a filename
472 * (of a file that exists and that is not a directory).
474 * @param fil filename to check
475 * @return GNUNET_YES if yes, GNUNET_NO if not a file, GNUNET_SYSERR if something
476 * else (will print an error message in that case, too).
479 GNUNET_DISK_file_test (const char *fil)
481 struct stat filestat;
485 rdir = GNUNET_STRINGS_filename_expand (fil);
487 return GNUNET_SYSERR;
489 ret = STAT (rdir, &filestat);
494 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "stat", rdir);
496 return GNUNET_SYSERR;
501 if (!S_ISREG (filestat.st_mode))
506 if (ACCESS (rdir, R_OK) < 0)
508 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "access", rdir);
510 return GNUNET_SYSERR;
518 * Implementation of "mkdir -p"
519 * @param dir the directory to create
520 * @returns GNUNET_OK on success, GNUNET_SYSERR on failure
523 GNUNET_DISK_directory_create (const char *dir)
530 rdir = GNUNET_STRINGS_filename_expand (dir);
532 return GNUNET_SYSERR;
536 pos = 1; /* skip heading '/' */
538 /* Local or Network path? */
539 if (strncmp (rdir, "\\\\", 2) == 0)
544 if (rdir[pos] == '\\')
554 pos = 3; /* strlen("C:\\") */
559 if ((rdir[pos] == DIR_SEPARATOR) || (pos == len))
562 ret = GNUNET_DISK_directory_test (rdir);
563 if (ret == GNUNET_SYSERR)
566 return GNUNET_SYSERR;
568 if (ret == GNUNET_NO)
571 ret = mkdir (rdir, S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH); /* 755 */
575 if ((ret != 0) && (errno != EEXIST))
577 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR, "mkdir",
580 return GNUNET_SYSERR;
583 rdir[pos] = DIR_SEPARATOR;
593 * Create the directory structure for storing
596 * @param filename name of a file in the directory
597 * @returns GNUNET_OK on success,
598 * GNUNET_SYSERR on failure,
599 * GNUNET_NO if the directory
600 * exists but is not writeable for us
603 GNUNET_DISK_directory_create_for_file (const char *filename)
609 rdir = GNUNET_STRINGS_filename_expand (filename);
611 return GNUNET_SYSERR;
613 while ((len > 0) && (rdir[len] != DIR_SEPARATOR))
616 ret = GNUNET_DISK_directory_create (rdir);
617 if ((ret == GNUNET_OK) && (0 != ACCESS (rdir, W_OK)))
625 * Read the contents of a binary file into a buffer.
626 * @param h handle to an open file
627 * @param result the buffer to write the result to
628 * @param len the maximum number of bytes to read
629 * @return the number of bytes read on success, GNUNET_SYSERR on failure
632 GNUNET_DISK_file_read (const struct GNUNET_DISK_FileHandle * h, void *result,
638 return GNUNET_SYSERR;
644 if (!ReadFile (h->h, result, len, &bytesRead, NULL))
646 SetErrnoFromWinError (GetLastError ());
647 return GNUNET_SYSERR;
651 return read (h->fd, result, len);
657 * Read the contents of a binary file into a buffer.
659 * @param fn file name
660 * @param result the buffer to write the result to
661 * @param len the maximum number of bytes to read
662 * @return number of bytes read, GNUNET_SYSERR on failure
665 GNUNET_DISK_fn_read (const char *fn, void *result, size_t len)
667 struct GNUNET_DISK_FileHandle *fh;
670 fh = GNUNET_DISK_file_open (fn, GNUNET_DISK_OPEN_READ,
671 GNUNET_DISK_PERM_NONE);
673 return GNUNET_SYSERR;
674 ret = GNUNET_DISK_file_read (fh, result, len);
675 GNUNET_assert (GNUNET_OK == GNUNET_DISK_file_close (fh));
682 * Write a buffer to a file.
683 * @param h handle to open file
684 * @param buffer the data to write
685 * @param n number of bytes to write
686 * @return number of bytes written on success, GNUNET_SYSERR on error
689 GNUNET_DISK_file_write (const struct GNUNET_DISK_FileHandle * h,
690 const void *buffer, size_t n)
695 return GNUNET_SYSERR;
701 if (!WriteFile (h->h, buffer, n, &bytesWritten, NULL))
703 SetErrnoFromWinError (GetLastError ());
704 return GNUNET_SYSERR;
708 return write (h->fd, buffer, n);
713 * Write a buffer to a file. If the file is longer than the
714 * number of bytes that will be written, it will be truncated.
716 * @param fn file name
717 * @param buffer the data to write
718 * @param n number of bytes to write
719 * @param mode file permissions
720 * @return number of bytes written on success, GNUNET_SYSERR on error
723 GNUNET_DISK_fn_write (const char *fn, const void *buffer,
724 size_t n, enum GNUNET_DISK_AccessPermissions mode)
726 struct GNUNET_DISK_FileHandle *fh;
729 fh = GNUNET_DISK_file_open (fn,
730 GNUNET_DISK_OPEN_WRITE
731 | GNUNET_DISK_OPEN_TRUNCATE
732 | GNUNET_DISK_OPEN_CREATE, mode);
734 return GNUNET_SYSERR;
735 ret = GNUNET_DISK_file_write (fh, buffer, n);
736 GNUNET_assert (GNUNET_OK == GNUNET_DISK_file_close (fh));
741 * Scan a directory for files.
743 * @param dirName the name of the directory
744 * @param callback the method to call for each file,
745 * can be NULL, in that case, we only count
746 * @param callback_cls closure for callback
747 * @return the number of files found, GNUNET_SYSERR on error or
748 * ieration aborted by callback returning GNUNET_SYSERR
751 GNUNET_DISK_directory_scan (const char *dirName,
752 GNUNET_FileNameCallback callback,
756 struct dirent *finfo;
761 unsigned int name_len;
764 GNUNET_assert (dirName != NULL);
765 dname = GNUNET_STRINGS_filename_expand (dirName);
767 return GNUNET_SYSERR;
768 while ((strlen (dname) > 0) && (dname[strlen (dname) - 1] == DIR_SEPARATOR))
769 dname[strlen (dname) - 1] = '\0';
770 if (0 != STAT (dname, &istat))
772 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "stat", dname);
774 return GNUNET_SYSERR;
776 if (!S_ISDIR (istat.st_mode))
778 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
779 _("Expected `%s' to be a directory!\n"), dirName);
781 return GNUNET_SYSERR;
784 dinfo = OPENDIR (dname);
785 if ((errno == EACCES) || (dinfo == NULL))
787 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "opendir", dname);
791 return GNUNET_SYSERR;
794 n_size = strlen (dname) + name_len + 2;
795 name = GNUNET_malloc (n_size);
796 while ((finfo = readdir (dinfo)) != NULL)
798 if ((0 == strcmp (finfo->d_name, ".")) ||
799 (0 == strcmp (finfo->d_name, "..")))
801 if (callback != NULL)
803 if (name_len < strlen (finfo->d_name))
806 name_len = strlen (finfo->d_name);
807 n_size = strlen (dname) + name_len + 2;
808 name = GNUNET_malloc (n_size);
810 /* dname can end in "/" only if dname == "/";
811 if dname does not end in "/", we need to add
812 a "/" (otherwise, we must not!) */
813 GNUNET_snprintf (name,
817 (strcmp (dname, DIR_SEPARATOR_STR) ==
818 0) ? "" : DIR_SEPARATOR_STR, finfo->d_name);
819 if (GNUNET_OK != callback (callback_cls, name))
824 return GNUNET_SYSERR;
837 * Opaque handle used for iterating over a directory.
839 struct GNUNET_DISK_DirectoryIterator
843 * Function to call on directory entries.
845 GNUNET_DISK_DirectoryIteratorCallback callback;
848 * Closure for callback.
853 * Reference to directory.
863 * Next filename to process.
870 enum GNUNET_SCHEDULER_Priority priority;
876 * Task used by the directory iterator.
879 directory_iterator_task (void *cls,
880 const struct GNUNET_SCHEDULER_TaskContext *tc)
882 struct GNUNET_DISK_DirectoryIterator *iter = cls;
885 name = iter->next_name;
886 GNUNET_assert (name != NULL);
887 iter->next_name = NULL;
888 iter->callback (iter->callback_cls, iter, name, iter->dirname);
894 * This function must be called during the DiskIteratorCallback
895 * (exactly once) to schedule the task to process the next
896 * filename in the directory (if there is one).
898 * @param iter opaque handle for the iterator
899 * @param can set to GNUNET_YES to terminate the iteration early
900 * @return GNUNET_YES if iteration will continue,
901 * GNUNET_NO if this was the last entry (and iteration is complete),
902 * GNUNET_SYSERR if abort was YES
905 GNUNET_DISK_directory_iterator_next (struct GNUNET_DISK_DirectoryIterator
908 struct dirent *finfo;
910 GNUNET_assert (iter->next_name == NULL);
911 if (can == GNUNET_YES)
913 closedir (iter->directory);
914 GNUNET_free (iter->dirname);
916 return GNUNET_SYSERR;
918 while (NULL != (finfo = readdir (iter->directory)))
920 if ((0 == strcmp (finfo->d_name, ".")) ||
921 (0 == strcmp (finfo->d_name, "..")))
923 GNUNET_asprintf (&iter->next_name,
925 iter->dirname, DIR_SEPARATOR_STR, finfo->d_name);
930 GNUNET_DISK_directory_iterator_next (iter, GNUNET_YES);
933 GNUNET_SCHEDULER_add_with_priority (iter->priority,
934 &directory_iterator_task, iter);
940 * Scan a directory for files using the scheduler to run a task for
941 * each entry. The name of the directory must be expanded first (!).
942 * If a scheduler does not need to be used, GNUNET_DISK_directory_scan
943 * may provide a simpler API.
945 * @param prio priority to use
946 * @param dirName the name of the directory
947 * @param callback the method to call for each file
948 * @param callback_cls closure for callback
951 GNUNET_DISK_directory_iterator_start (enum GNUNET_SCHEDULER_Priority prio,
953 GNUNET_DISK_DirectoryIteratorCallback
954 callback, void *callback_cls)
956 struct GNUNET_DISK_DirectoryIterator *di;
958 di = GNUNET_malloc (sizeof (struct GNUNET_DISK_DirectoryIterator));
959 di->callback = callback;
960 di->callback_cls = callback_cls;
961 di->directory = OPENDIR (dirName);
962 if (di->directory == NULL)
965 callback (callback_cls, NULL, NULL, NULL);
968 di->dirname = GNUNET_strdup (dirName);
970 GNUNET_DISK_directory_iterator_next (di, GNUNET_NO);
975 * Function that removes the given directory by calling
976 * "GNUNET_DISK_directory_remove".
978 * @param unused not used
979 * @param fn directory to remove
983 remove_helper (void *unused, const char *fn)
985 (void) GNUNET_DISK_directory_remove (fn);
991 * Remove all files in a directory (rm -rf). Call with
995 * @param fileName the file to remove
996 * @return GNUNET_OK on success, GNUNET_SYSERR on error
999 GNUNET_DISK_directory_remove (const char *fileName)
1003 if (0 != LSTAT (fileName, &istat))
1004 return GNUNET_NO; /* file may not exist... */
1005 if (UNLINK (fileName) == 0)
1007 if ((errno != EISDIR) &&
1008 /* EISDIR is not sufficient in all cases, e.g.
1009 sticky /tmp directory may result in EPERM on BSD.
1010 So we also explicitly check "isDirectory" */
1011 (GNUNET_YES != GNUNET_DISK_directory_test (fileName)))
1013 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "rmdir", fileName);
1014 return GNUNET_SYSERR;
1016 if (GNUNET_SYSERR ==
1017 GNUNET_DISK_directory_scan (fileName, &remove_helper, NULL))
1018 return GNUNET_SYSERR;
1019 if (0 != RMDIR (fileName))
1021 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "rmdir", fileName);
1022 return GNUNET_SYSERR;
1031 * @param src file to copy
1032 * @param dst destination file name
1033 * @return GNUNET_OK on success, GNUNET_SYSERR on error
1036 GNUNET_DISK_file_copy (const char *src, const char *dst)
1042 struct GNUNET_DISK_FileHandle *in;
1043 struct GNUNET_DISK_FileHandle *out;
1045 if (GNUNET_OK != GNUNET_DISK_file_size (src, &size, GNUNET_YES))
1046 return GNUNET_SYSERR;
1048 in = GNUNET_DISK_file_open (src, GNUNET_DISK_OPEN_READ,
1049 GNUNET_DISK_PERM_NONE);
1051 return GNUNET_SYSERR;
1052 out = GNUNET_DISK_file_open (dst, GNUNET_DISK_OPEN_WRITE
1053 | GNUNET_DISK_OPEN_CREATE |
1054 GNUNET_DISK_OPEN_FAILIFEXISTS,
1055 GNUNET_DISK_PERM_USER_READ |
1056 GNUNET_DISK_PERM_USER_WRITE |
1057 GNUNET_DISK_PERM_GROUP_READ |
1058 GNUNET_DISK_PERM_GROUP_WRITE);
1061 GNUNET_DISK_file_close (in);
1062 return GNUNET_SYSERR;
1064 buf = GNUNET_malloc (COPY_BLK_SIZE);
1067 len = COPY_BLK_SIZE;
1068 if (len > size - pos)
1070 if (len != GNUNET_DISK_file_read (in, buf, len))
1072 if (len != GNUNET_DISK_file_write (out, buf, len))
1077 GNUNET_DISK_file_close (in);
1078 GNUNET_DISK_file_close (out);
1082 GNUNET_DISK_file_close (in);
1083 GNUNET_DISK_file_close (out);
1084 return GNUNET_SYSERR;
1089 * @brief Removes special characters as ':' from a filename.
1090 * @param fn the filename to canonicalize
1093 GNUNET_DISK_filename_canonicalize (char *fn)
1103 if (c == '/' || c == '\\' || c == ':' || c == '*' || c == '?' ||
1104 c == '"' || c == '<' || c == '>' || c == '|')
1116 * @brief Change owner of a file
1118 * @param filename name of file to change the owner of
1119 * @param user name of the new owner
1120 * @return GNUNET_OK on success, GNUNET_SYSERR on failure
1123 GNUNET_DISK_file_change_owner (const char *filename, const char *user)
1128 pws = getpwnam (user);
1131 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1132 _("Cannot obtain information about user `%s': %s\n"),
1133 user, STRERROR (errno));
1134 return GNUNET_SYSERR;
1136 if (0 != chown (filename, pws->pw_uid, pws->pw_gid))
1137 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "chown", filename);
1144 * Lock a part of a file
1145 * @param fh file handle
1146 * @param lockStart absolute position from where to lock
1147 * @param lockEnd absolute position until where to lock
1148 * @param excl GNUNET_YES for an exclusive lock
1149 * @return GNUNET_OK on success, GNUNET_SYSERR on error
1152 GNUNET_DISK_file_lock (struct GNUNET_DISK_FileHandle *fh, off_t lockStart,
1153 off_t lockEnd, int excl)
1158 return GNUNET_SYSERR;
1164 memset (&fl, 0, sizeof (struct flock));
1165 fl.l_type = excl ? F_WRLCK : F_RDLCK;
1166 fl.l_whence = SEEK_SET;
1167 fl.l_start = lockStart;
1170 return fcntl (fh->fd, F_SETLK, &fl) != 0 ? GNUNET_SYSERR : GNUNET_OK;
1174 memset (&o, 0, sizeof (OVERLAPPED));
1175 o.Offset = lockStart;
1177 if (!LockFileEx (fh->h, (excl ? LOCKFILE_EXCLUSIVE_LOCK : 0)
1178 | LOCKFILE_FAIL_IMMEDIATELY, 0, lockEnd - lockStart, 0,
1181 SetErrnoFromWinError (GetLastError ());
1182 return GNUNET_SYSERR;
1191 * Unlock a part of a file
1192 * @param fh file handle
1193 * @param unlockStart absolute position from where to unlock
1194 * @param unlockEnd absolute position until where to unlock
1195 * @return GNUNET_OK on success, GNUNET_SYSERR on error
1198 GNUNET_DISK_file_unlock (struct GNUNET_DISK_FileHandle *fh, off_t unlockStart,
1204 return GNUNET_SYSERR;
1210 memset (&fl, 0, sizeof (struct flock));
1211 fl.l_type = F_UNLCK;
1212 fl.l_whence = SEEK_SET;
1213 fl.l_start = unlockStart;
1214 fl.l_len = unlockEnd;
1216 return fcntl (fh->fd, F_SETLK, &fl) != 0 ? GNUNET_SYSERR : GNUNET_OK;
1220 memset (&o, 0, sizeof (OVERLAPPED));
1221 o.Offset = unlockStart;
1223 if (!UnlockFileEx (fh->h, 0, unlockEnd - unlockStart, 0, &o))
1225 SetErrnoFromWinError (GetLastError ());
1226 return GNUNET_SYSERR;
1235 * Open a file. Note that the access permissions will only be
1236 * used if a new file is created and if the underlying operating
1237 * system supports the given permissions.
1239 * @param fn file name to be opened
1240 * @param flags opening flags, a combination of GNUNET_DISK_OPEN_xxx bit flags
1241 * @param perm permissions for the newly created file, use
1242 * GNUNET_DISK_PERM_USER_NONE if a file could not be created by this
1243 * call (because of flags)
1244 * @return IO handle on success, NULL on error
1246 struct GNUNET_DISK_FileHandle *
1247 GNUNET_DISK_file_open (const char *fn,
1248 enum GNUNET_DISK_OpenFlags flags,
1249 enum GNUNET_DISK_AccessPermissions perm)
1252 struct GNUNET_DISK_FileHandle *ret;
1263 expfn = GNUNET_STRINGS_filename_expand (fn);
1268 if (GNUNET_DISK_OPEN_READWRITE == (flags & GNUNET_DISK_OPEN_READWRITE))
1269 oflags = O_RDWR; /* note: O_RDWR is NOT always O_RDONLY | O_WRONLY */
1270 else if (flags & GNUNET_DISK_OPEN_READ)
1272 else if (flags & GNUNET_DISK_OPEN_WRITE)
1277 GNUNET_free (expfn);
1280 if (flags & GNUNET_DISK_OPEN_FAILIFEXISTS)
1281 oflags |= (O_CREAT | O_EXCL);
1282 if (flags & GNUNET_DISK_OPEN_TRUNCATE)
1284 if (flags & GNUNET_DISK_OPEN_APPEND)
1286 if (flags & GNUNET_DISK_OPEN_CREATE)
1288 (void) GNUNET_DISK_directory_create_for_file (expfn);
1290 mode = translate_unix_perms(perm);
1293 fd = open (expfn, oflags | O_LARGEFILE, mode);
1296 if (0 == (flags & GNUNET_DISK_OPEN_FAILIFEXISTS))
1297 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "open", expfn);
1299 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_DEBUG, "open", expfn);
1300 GNUNET_free (expfn);
1307 if (GNUNET_DISK_OPEN_READWRITE == (flags & GNUNET_DISK_OPEN_READWRITE))
1308 access = FILE_READ_DATA | FILE_WRITE_DATA;
1309 else if (flags & GNUNET_DISK_OPEN_READ)
1310 access = FILE_READ_DATA;
1311 else if (flags & GNUNET_DISK_OPEN_WRITE)
1312 access = FILE_WRITE_DATA;
1314 if (flags & GNUNET_DISK_OPEN_FAILIFEXISTS)
1318 else if (flags & GNUNET_DISK_OPEN_CREATE)
1320 if (flags & GNUNET_DISK_OPEN_TRUNCATE)
1321 disp = CREATE_ALWAYS;
1325 else if (flags & GNUNET_DISK_OPEN_TRUNCATE)
1327 disp = TRUNCATE_EXISTING;
1331 disp = OPEN_EXISTING;
1334 /* TODO: access priviledges? */
1335 h = CreateFile (expfn, access, FILE_SHARE_DELETE | FILE_SHARE_READ
1336 | FILE_SHARE_WRITE, NULL, disp, FILE_ATTRIBUTE_NORMAL,
1338 if (h == INVALID_HANDLE_VALUE)
1340 SetErrnoFromWinError (GetLastError ());
1341 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "open", expfn);
1342 GNUNET_free (expfn);
1346 if (flags & GNUNET_DISK_OPEN_APPEND)
1347 if (SetFilePointer (h, 0, 0, FILE_END) == INVALID_SET_FILE_POINTER)
1349 SetErrnoFromWinError (GetLastError ());
1350 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "SetFilePointer",
1353 GNUNET_free (expfn);
1358 ret = GNUNET_malloc (sizeof (struct GNUNET_DISK_FileHandle));
1361 ret->type = GNUNET_DISK_FILE;
1365 GNUNET_free (expfn);
1371 * Close an open file
1372 * @param h file handle
1373 * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
1376 GNUNET_DISK_file_close (struct GNUNET_DISK_FileHandle *h)
1381 return GNUNET_SYSERR;
1385 if (!CloseHandle (h->h))
1387 SetErrnoFromWinError (GetLastError ());
1388 GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "close");
1390 return GNUNET_SYSERR;
1393 if (close (h->fd) != 0)
1395 GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "close");
1397 return GNUNET_SYSERR;
1406 * Construct full path to a file inside of the private
1407 * directory used by GNUnet. Also creates the corresponding
1408 * directory. If the resulting name is supposed to be
1409 * a directory, end the last argument in '/' (or pass
1410 * DIR_SEPARATOR_STR as the last argument before NULL).
1412 * @param cfg configuration to use (determines HOME)
1413 * @param serviceName name of the service
1414 * @param ... is NULL-terminated list of
1415 * path components to append to the
1416 * private directory name.
1417 * @return the constructed filename
1420 GNUNET_DISK_get_home_filename (const struct GNUNET_CONFIGURATION_Handle *cfg,
1421 const char *serviceName, ...)
1427 unsigned int needed;
1430 GNUNET_CONFIGURATION_get_value_filename (cfg,
1431 serviceName, "HOME", &pfx))
1435 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1436 _("No `%s' specified for service `%s' in configuration.\n"),
1437 "HOME", serviceName);
1440 needed = strlen (pfx) + 2;
1441 if ((pfx[strlen (pfx) - 1] != '/') && (pfx[strlen (pfx) - 1] != '\\'))
1443 va_start (ap, serviceName);
1446 c = va_arg (ap, const char *);
1449 needed += strlen (c);
1450 if ((c[strlen (c) - 1] != '/') && (c[strlen (c) - 1] != '\\'))
1454 ret = GNUNET_malloc (needed);
1457 va_start (ap, serviceName);
1460 c = va_arg (ap, const char *);
1463 if ((c[strlen (c) - 1] != '/') && (c[strlen (c) - 1] != '\\'))
1464 strcat (ret, DIR_SEPARATOR_STR);
1468 if ((ret[strlen (ret) - 1] != '/') && (ret[strlen (ret) - 1] != '\\'))
1469 GNUNET_DISK_directory_create_for_file (ret);
1471 GNUNET_DISK_directory_create (ret);
1477 * Handle for a memory-mapping operation.
1479 struct GNUNET_DISK_MapHandle
1483 * Underlying OS handle.
1488 * Address where the map is in memory.
1493 * Number of bytes mapped.
1501 #define MAP_FAILED ((void *) -1)
1505 * Map a file into memory
1507 * @param h open file handle
1508 * @param m handle to the new mapping
1509 * @param access access specification, GNUNET_DISK_MAP_TYPE_xxx
1510 * @param len size of the mapping
1511 * @return pointer to the mapped memory region, NULL on failure
1514 GNUNET_DISK_file_map (const struct GNUNET_DISK_FileHandle *h,
1515 struct GNUNET_DISK_MapHandle **m,
1516 enum GNUNET_DISK_MapType access, size_t len)
1525 DWORD mapAccess, protect;
1528 if ((access & GNUNET_DISK_MAP_TYPE_READ) &&
1529 (access & GNUNET_DISK_MAP_TYPE_WRITE))
1531 protect = PAGE_READWRITE;
1532 mapAccess = FILE_MAP_ALL_ACCESS;
1534 else if (access & GNUNET_DISK_MAP_TYPE_READ)
1536 protect = PAGE_READONLY;
1537 mapAccess = FILE_MAP_READ;
1539 else if (access & GNUNET_DISK_MAP_TYPE_WRITE)
1541 protect = PAGE_READWRITE;
1542 mapAccess = FILE_MAP_WRITE;
1550 *m = GNUNET_malloc (sizeof (struct GNUNET_DISK_MapHandle));
1551 (*m)->h = CreateFileMapping (h->h, NULL, protect, 0, 0, NULL);
1552 if ((*m)->h == INVALID_HANDLE_VALUE)
1554 SetErrnoFromWinError (GetLastError ());
1559 ret = MapViewOfFile ((*m)->h, mapAccess, 0, 0, len);
1562 SetErrnoFromWinError (GetLastError ());
1563 CloseHandle ((*m)->h);
1572 if (access & GNUNET_DISK_MAP_TYPE_READ)
1574 if (access & GNUNET_DISK_MAP_TYPE_WRITE)
1576 *m = GNUNET_malloc (sizeof (struct GNUNET_DISK_MapHandle));
1577 (*m)->addr = mmap (NULL, len, prot, MAP_SHARED, h->fd, 0);
1578 GNUNET_assert (NULL != (*m)->addr);
1579 if (MAP_FAILED == (*m)->addr)
1591 * @param h mapping handle
1592 * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
1595 GNUNET_DISK_file_unmap (struct GNUNET_DISK_MapHandle *h)
1601 return GNUNET_SYSERR;
1605 ret = UnmapViewOfFile (h->h) ? GNUNET_OK : GNUNET_SYSERR;
1606 if (ret != GNUNET_OK)
1607 SetErrnoFromWinError (GetLastError ());
1608 if (!CloseHandle (h->h) && (ret == GNUNET_OK))
1610 ret = GNUNET_SYSERR;
1611 SetErrnoFromWinError (GetLastError ());
1614 ret = munmap (h->addr, h->len) != -1 ? GNUNET_OK : GNUNET_SYSERR;
1622 * Write file changes to disk
1623 * @param h handle to an open file
1624 * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
1627 GNUNET_DISK_file_sync (const struct GNUNET_DISK_FileHandle *h)
1632 return GNUNET_SYSERR;
1638 ret = FlushFileBuffers (h->h) ? GNUNET_OK : GNUNET_SYSERR;
1639 if (ret != GNUNET_OK)
1640 SetErrnoFromWinError (GetLastError ());
1642 #elif defined(FREEBSD) || defined(OPENBSD) || defined(DARWIN)
1643 return fsync (h->fd) == -1 ? GNUNET_SYSERR : GNUNET_OK;
1645 return fdatasync (h->fd) == -1 ? GNUNET_SYSERR : GNUNET_OK;
1651 * Creates an interprocess channel
1653 * @param blocking creates an asynchronous pipe if set to GNUNET_NO
1654 * @param inherit_read inherit the parent processes stdin (only for windows)
1655 * @param inherit_write inherit the parent processes stdout (only for windows)
1657 * @return handle to the new pipe, NULL on error
1659 struct GNUNET_DISK_PipeHandle *
1660 GNUNET_DISK_pipe (int blocking, int inherit_read, int inherit_write)
1662 struct GNUNET_DISK_PipeHandle *p;
1663 struct GNUNET_DISK_FileHandle *fds;
1666 GNUNET_malloc (sizeof (struct GNUNET_DISK_PipeHandle) +
1667 2 * sizeof (struct GNUNET_DISK_FileHandle));
1668 fds = (struct GNUNET_DISK_FileHandle *) &p[1];
1681 GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "pipe");
1686 p->fd[0]->fd = fd[0];
1687 p->fd[1]->fd = fd[1];
1689 flags = fcntl (fd[0], F_GETFL);
1691 flags |= O_NONBLOCK;
1692 if (0 > fcntl (fd[0], F_SETFL, flags))
1694 flags = fcntl (fd[0], F_GETFD);
1695 flags |= FD_CLOEXEC;
1696 if (0 > fcntl (fd[0], F_SETFD, flags))
1699 flags = fcntl (fd[1], F_GETFL);
1701 flags |= O_NONBLOCK;
1702 if (0 > fcntl (fd[1], F_SETFL, flags))
1704 flags = fcntl (fd[1], F_GETFD);
1705 flags |= FD_CLOEXEC;
1706 if (0 > fcntl (fd[1], F_SETFD, flags))
1711 GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "fcntl");
1712 GNUNET_break (0 == close (p->fd[0]->fd));
1713 GNUNET_break (0 == close (p->fd[1]->fd));
1722 ret = CreatePipe (&p->fd[0]->h, &p->fd[1]->h, NULL, 0);
1726 SetErrnoFromWinError (GetLastError ());
1729 if (!DuplicateHandle (GetCurrentProcess (), p->fd[0]->h,
1730 GetCurrentProcess (), &tmp_handle, 0, inherit_read == GNUNET_YES ? TRUE : FALSE,
1731 DUPLICATE_SAME_ACCESS))
1733 SetErrnoFromWinError (GetLastError ());
1734 CloseHandle (p->fd[0]->h);
1735 CloseHandle (p->fd[1]->h);
1739 CloseHandle (p->fd[0]->h);
1740 p->fd[0]->h = tmp_handle;
1742 if (!DuplicateHandle (GetCurrentProcess (), p->fd[1]->h,
1743 GetCurrentProcess (), &tmp_handle, 0, inherit_write == GNUNET_YES ? TRUE : FALSE,
1744 DUPLICATE_SAME_ACCESS))
1746 SetErrnoFromWinError (GetLastError ());
1747 CloseHandle (p->fd[0]->h);
1748 CloseHandle (p->fd[1]->h);
1752 CloseHandle (p->fd[1]->h);
1753 p->fd[1]->h = tmp_handle;
1759 SetNamedPipeHandleState (p->fd[0]->h, &mode, NULL, NULL);
1760 SetNamedPipeHandleState (p->fd[1]->h, &mode, NULL, NULL);
1761 /* this always fails on Windows 95, so we don't care about error handling */
1763 p->fd[0]->type = GNUNET_PIPE;
1764 p->fd[1]->type = GNUNET_PIPE;
1771 * Closes an interprocess channel
1773 * @param p pipe to close
1774 * @param end which end of the pipe to close
1775 * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
1778 GNUNET_DISK_pipe_close_end (struct GNUNET_DISK_PipeHandle *p,
1779 enum GNUNET_DISK_PipeEnd end)
1781 int ret = GNUNET_OK;
1785 if (end == GNUNET_DISK_PIPE_END_READ)
1787 if (!CloseHandle (p->fd[0]->h))
1789 SetErrnoFromWinError (GetLastError ());
1790 ret = GNUNET_SYSERR;
1792 p->fd[0]->h = INVALID_HANDLE_VALUE;
1794 else if (end == GNUNET_DISK_PIPE_END_WRITE)
1796 if (!CloseHandle (p->fd[1]->h))
1798 SetErrnoFromWinError (GetLastError ());
1799 ret = GNUNET_SYSERR;
1801 p->fd[1]->h = INVALID_HANDLE_VALUE;
1806 if (end == GNUNET_DISK_PIPE_END_READ)
1808 if (0 != close (p->fd[0]->fd))
1810 ret = GNUNET_SYSERR;
1815 else if (end == GNUNET_DISK_PIPE_END_WRITE)
1817 if (0 != close (p->fd[1]->fd))
1819 ret = GNUNET_SYSERR;
1830 * Closes an interprocess channel
1832 * @param p pipe to close
1833 * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
1836 GNUNET_DISK_pipe_close (struct GNUNET_DISK_PipeHandle *p)
1838 int ret = GNUNET_OK;
1842 if (!CloseHandle (p->fd[0]->h))
1844 SetErrnoFromWinError (GetLastError ());
1845 ret = GNUNET_SYSERR;
1847 if (!CloseHandle (p->fd[1]->h))
1849 SetErrnoFromWinError (GetLastError ());
1850 ret = GNUNET_SYSERR;
1855 if (p->fd[0]->fd != -1)
1857 if (0 != close (p->fd[0]->fd))
1859 ret = GNUNET_SYSERR;
1864 if (p->fd[1]->fd != -1)
1866 if (0 != close (p->fd[1]->fd))
1868 ret = GNUNET_SYSERR;
1880 * Creates a named pipe/FIFO
1881 * @param fn name of the named pipe
1882 * @param flags open flags
1883 * @param perm access permissions
1884 * @return pipe handle on success, NULL on error
1886 struct GNUNET_DISK_FileHandle *
1887 GNUNET_DISK_npipe_open (const char *fn,
1888 enum GNUNET_DISK_OpenFlags flags,
1889 enum GNUNET_DISK_AccessPermissions perm)
1891 struct GNUNET_DISK_FileHandle *ret;
1899 if (flags & GNUNET_DISK_OPEN_READWRITE)
1900 openMode = PIPE_ACCESS_DUPLEX;
1901 else if (flags & GNUNET_DISK_OPEN_READ)
1902 openMode = PIPE_ACCESS_INBOUND;
1903 else if (flags & GNUNET_DISK_OPEN_WRITE)
1904 openMode = PIPE_ACCESS_OUTBOUND;
1906 if (flags & GNUNET_DISK_OPEN_FAILIFEXISTS)
1907 openMode |= FILE_FLAG_FIRST_PIPE_INSTANCE;
1909 GNUNET_asprintf(&name, "\\\\.\\pipe\\pipename\\%s", fn);
1910 h = CreateNamedPipe (fn, openMode | FILE_FLAG_OVERLAPPED,
1911 PIPE_TYPE_BYTE | PIPE_READMODE_BYTE, 2, 1, 1, 0, NULL);
1915 SetErrnoFromWinError(GetLastError());
1919 ret = GNUNET_malloc(sizeof(*ret));
1926 if (mkfifo(fn, translate_unix_perms(perm)) == -1)
1928 if (errno == EEXIST && flags & GNUNET_DISK_OPEN_FAILIFEXISTS)
1932 flags ~= GNUNET_DISK_OPEN_FAILIFEXISTS;
1933 return GNUNET_DISK_file_open(fn, flags, perm);
1938 * Closes a named pipe/FIFO
1939 * @param pipe named pipe
1940 * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
1943 GNUNET_DISK_npipe_close (struct GNUNET_DISK_FileHandle *pipe)
1946 return close(pipe->fd) == 0 ? GNUNET_OK : GNUNET_SYSERR;
1950 ret = CloseHandle(pipe->h);
1953 SetErrnoFromWinError(GetLastError());
1954 return GNUNET_SYSERR;
1963 * Get the handle to a particular pipe end
1966 * @param n end to access
1967 * @return handle for the respective end
1969 const struct GNUNET_DISK_FileHandle *
1970 GNUNET_DISK_pipe_handle (const struct GNUNET_DISK_PipeHandle *p,
1971 enum GNUNET_DISK_PipeEnd n)
1975 case GNUNET_DISK_PIPE_END_READ:
1976 case GNUNET_DISK_PIPE_END_WRITE:
1986 * Retrieve OS file handle
1988 * @param fh GNUnet file descriptor
1989 * @param dst destination buffer
1990 * @param dst_len length of dst
1991 * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
1994 GNUNET_DISK_internal_file_handle_ (const struct GNUNET_DISK_FileHandle *fh,
1995 void *dst, size_t dst_len)
1998 if (dst_len < sizeof (HANDLE))
1999 return GNUNET_SYSERR;
2000 *((HANDLE *) dst) = fh->h;
2002 if (dst_len < sizeof (int))
2003 return GNUNET_SYSERR;
2004 *((int *) dst) = fh->fd;