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
48 #include <sys/param.h>
49 #include <sys/mount.h>
52 #include <sys/param.h>
53 #include <sys/mount.h>
56 #include <sys/types.h>
57 #include <sys/statvfs.h>
60 #define _IFMT 0170000 /* type of file */
61 #define _IFLNK 0120000 /* symbolic link */
62 #define S_ISLNK(m) (((m)&_IFMT) == _IFLNK)
64 #error PORT-ME: need to port statfs (how much space is left on the drive?)
79 #include <sys/statvfs.h>
84 * Handle used to manage a pipe.
86 struct GNUNET_DISK_PipeHandle
89 * File descriptors for the pipe.
91 struct GNUNET_DISK_FileHandle * fd[2];
96 * Closure for the recursion to determine the file size
99 struct GetFileSizeData
102 * Set to the total file size.
107 * GNUNET_YES if symbolic links should be included.
109 int include_sym_links;
114 * Iterate over all files in the given directory and
115 * accumulate their size.
117 * @param cls closure of type "struct GetFileSizeData"
118 * @param fn current filename we are looking at
119 * @return GNUNET_SYSERR on serious errors, otherwise GNUNET_OK
122 getSizeRec (void *cls, const char *fn)
124 struct GetFileSizeData *gfsd = cls;
132 if (0 != STAT64 (fn, &buf))
134 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "stat64", fn);
135 return GNUNET_SYSERR;
138 if (0 != STAT (fn, &buf))
140 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "stat", fn);
141 return GNUNET_SYSERR;
144 if ((!S_ISLNK (buf.st_mode)) || (gfsd->include_sym_links == GNUNET_YES))
145 gfsd->total += buf.st_size;
146 if ((S_ISDIR (buf.st_mode)) &&
147 (0 == ACCESS (fn, X_OK)) &&
148 ((!S_ISLNK (buf.st_mode)) || (gfsd->include_sym_links == GNUNET_YES)))
150 if (GNUNET_SYSERR == GNUNET_DISK_directory_scan (fn, &getSizeRec, gfsd))
151 return GNUNET_SYSERR;
158 * Checks whether a handle is invalid
160 * @param h handle to check
161 * @return GNUNET_YES if invalid, GNUNET_NO if valid
164 GNUNET_DISK_handle_invalid (const struct GNUNET_DISK_FileHandle *h)
167 return ((!h) || (h->h == INVALID_HANDLE_VALUE)) ? GNUNET_YES : GNUNET_NO;
169 return ((!h) || (h->fd == -1)) ? GNUNET_YES : GNUNET_NO;
175 * Move the read/write pointer in a file
177 * @param h handle of an open file
178 * @param offset position to move to
179 * @param whence specification to which position the offset parameter relates to
180 * @return the new position on success, GNUNET_SYSERR otherwise
183 GNUNET_DISK_file_seek (const struct GNUNET_DISK_FileHandle *h, off_t offset,
184 enum GNUNET_DISK_Seek whence)
189 return GNUNET_SYSERR;
194 static DWORD t[] = { [GNUNET_DISK_SEEK_SET] = FILE_BEGIN,
195 [GNUNET_DISK_SEEK_CUR] = FILE_CURRENT, [GNUNET_DISK_SEEK_END] = FILE_END };
197 ret = SetFilePointer (h->h, offset, NULL, t[whence]);
198 if (ret == INVALID_SET_FILE_POINTER)
200 SetErrnoFromWinError (GetLastError ());
201 return GNUNET_SYSERR;
205 static int t[] = { [GNUNET_DISK_SEEK_SET] = SEEK_SET,
206 [GNUNET_DISK_SEEK_CUR] = SEEK_CUR, [GNUNET_DISK_SEEK_END] = SEEK_END };
208 return lseek (h->fd, offset, t[whence]);
214 * Get the size of the file (or directory) of the given file (in
217 * @param filename name of the file or directory
218 * @param size set to the size of the file (or,
219 * in the case of directories, the sum
220 * of all sizes of files in the directory)
221 * @param includeSymLinks should symbolic links be
223 * @return GNUNET_SYSERR on error, GNUNET_OK on success
226 GNUNET_DISK_file_size (const char *filename,
230 struct GetFileSizeData gfsd;
233 GNUNET_assert (size != NULL);
235 gfsd.include_sym_links = includeSymLinks;
236 ret = getSizeRec (&gfsd, filename);
243 * Obtain some unique identifiers for the given file
244 * that can be used to identify it in the local system.
245 * This function is used between GNUnet processes to
246 * quickly check if two files with the same absolute path
247 * are actually identical. The two processes represent
248 * the same peer but may communicate over the network
249 * (and the file may be on an NFS volume). This function
250 * may not be supported on all operating systems.
252 * @param filename name of the file
253 * @param dev set to the device ID
254 * @param ino set to the inode ID
255 * @return GNUNET_OK on success
257 int GNUNET_DISK_file_get_identifiers (const char *filename,
265 if ( (0 == stat(filename,
267 (0 == statvfs (filename,
270 *dev = (uint32_t) fbuf.f_fsid;
271 *ino = (uint64_t) sbuf.st_ino;
274 return GNUNET_SYSERR;
279 * Create an (empty) temporary file on disk.
281 * @param t component to use for the name;
282 * does NOT contain "XXXXXX" or "/tmp/".
283 * @return NULL on error, otherwise name of fresh
284 * file on disk in directory for temporary files
287 GNUNET_DISK_mktemp (const char *t)
294 tmpdir = getenv ("TMPDIR");
295 tmpdir = tmpdir ? tmpdir : "/tmp";
297 GNUNET_asprintf (&tmpl,
304 fn = (char *) GNUNET_malloc (MAX_PATH + 1);
305 plibc_conv_to_win_path (tmpl, fn);
313 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR,
320 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING,
328 * Get the number of blocks that are left on the partition that
329 * contains the given file (for normal users).
331 * @param part a file on the partition to check
332 * @return -1 on errors, otherwise the number of free blocks
335 GNUNET_DISK_get_blocks_available (const char *part)
340 if (0 != statvfs (part, &buf))
342 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "statfs", part);
352 path = GNUNET_STRINGS_filename_expand (part);
353 memcpy (szDrive, path, 3);
356 if (!GetDiskFreeSpace (szDrive, &dwDummy, &dwDummy, &dwBlocks, &dwDummy))
358 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
359 _("`%s' failed for drive `%s': %u\n"),
360 "GetDiskFreeSpace", szDrive, GetLastError ());
367 if (0 != statfs (part, &s))
369 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "statfs", part);
378 * Test if "fil" is a directory.
379 * Will not print an error message if the directory
380 * does not exist. Will log errors if GNUNET_SYSERR is
381 * returned (i.e., a file exists with the same name).
383 * @param fil filename to test
384 * @return GNUNET_YES if yes, GNUNET_NO if not, GNUNET_SYSERR if it
388 GNUNET_DISK_directory_test (const char *fil)
390 struct stat filestat;
393 ret = STAT (fil, &filestat);
398 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "stat", fil);
399 return GNUNET_SYSERR;
403 if (!S_ISDIR (filestat.st_mode))
405 if (ACCESS (fil, R_OK | X_OK) < 0)
407 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "access", fil);
408 return GNUNET_SYSERR;
414 * Check that fil corresponds to a filename
415 * (of a file that exists and that is not a directory).
417 * @param fil filename to check
418 * @return GNUNET_YES if yes, GNUNET_NO if not a file, GNUNET_SYSERR if something
419 * else (will print an error message in that case, too).
422 GNUNET_DISK_file_test (const char *fil)
424 struct stat filestat;
428 rdir = GNUNET_STRINGS_filename_expand (fil);
430 return GNUNET_SYSERR;
432 ret = STAT (rdir, &filestat);
437 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "stat", rdir);
439 return GNUNET_SYSERR;
444 if (!S_ISREG (filestat.st_mode))
449 if (ACCESS (rdir, R_OK) < 0)
451 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "access", rdir);
453 return GNUNET_SYSERR;
461 * Implementation of "mkdir -p"
462 * @param dir the directory to create
463 * @returns GNUNET_OK on success, GNUNET_SYSERR on failure
466 GNUNET_DISK_directory_create (const char *dir)
473 rdir = GNUNET_STRINGS_filename_expand (dir);
475 return GNUNET_SYSERR;
479 pos = 1; /* skip heading '/' */
481 /* Local or Network path? */
482 if (strncmp (rdir, "\\\\", 2) == 0)
487 if (rdir[pos] == '\\')
497 pos = 3; /* strlen("C:\\") */
502 if ((rdir[pos] == DIR_SEPARATOR) || (pos == len))
505 ret = GNUNET_DISK_directory_test (rdir);
506 if (ret == GNUNET_SYSERR)
509 return GNUNET_SYSERR;
511 if (ret == GNUNET_NO)
514 ret = mkdir (rdir, S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH); /* 755 */
518 if ((ret != 0) && (errno != EEXIST))
520 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR, "mkdir",
523 return GNUNET_SYSERR;
526 rdir[pos] = DIR_SEPARATOR;
536 * Create the directory structure for storing
539 * @param filename name of a file in the directory
540 * @returns GNUNET_OK on success,
541 * GNUNET_SYSERR on failure,
542 * GNUNET_NO if the directory
543 * exists but is not writeable for us
546 GNUNET_DISK_directory_create_for_file (const char *filename)
552 rdir = GNUNET_STRINGS_filename_expand (filename);
554 return GNUNET_SYSERR;
556 while ((len > 0) && (rdir[len] != DIR_SEPARATOR))
559 ret = GNUNET_DISK_directory_create (rdir);
560 if ((ret == GNUNET_OK) && (0 != ACCESS (rdir, W_OK)))
568 * Read the contents of a binary file into a buffer.
569 * @param h handle to an open file
570 * @param result the buffer to write the result to
571 * @param len the maximum number of bytes to read
572 * @return the number of bytes read on success, GNUNET_SYSERR on failure
575 GNUNET_DISK_file_read (const struct GNUNET_DISK_FileHandle *h, void *result,
581 return GNUNET_SYSERR;
587 if (!ReadFile (h->h, result, len, &bytesRead, NULL))
589 SetErrnoFromWinError (GetLastError ());
590 return GNUNET_SYSERR;
594 return read (h->fd, result, len);
600 * Read the contents of a binary file into a buffer.
602 * @param fn file name
603 * @param result the buffer to write the result to
604 * @param len the maximum number of bytes to read
605 * @return number of bytes read, GNUNET_SYSERR on failure
608 GNUNET_DISK_fn_read (const char * fn,
612 struct GNUNET_DISK_FileHandle *fh;
615 fh = GNUNET_DISK_file_open (fn, GNUNET_DISK_OPEN_READ,
616 GNUNET_DISK_PERM_NONE);
618 return GNUNET_SYSERR;
619 ret = GNUNET_DISK_file_read (fh, result, len);
620 GNUNET_assert(GNUNET_OK == GNUNET_DISK_file_close(fh));
627 * Write a buffer to a file.
628 * @param h handle to open file
629 * @param buffer the data to write
630 * @param n number of bytes to write
631 * @return number of bytes written on success, GNUNET_SYSERR on error
634 GNUNET_DISK_file_write (const struct GNUNET_DISK_FileHandle *h, const void *buffer,
640 return GNUNET_SYSERR;
646 if (!WriteFile (h->h, buffer, n, &bytesWritten, NULL))
648 SetErrnoFromWinError (GetLastError ());
649 return GNUNET_SYSERR;
653 return write (h->fd, buffer, n);
658 * Write a buffer to a file. If the file is longer than the
659 * number of bytes that will be written, it will be truncated.
661 * @param fn file name
662 * @param buffer the data to write
663 * @param n number of bytes to write
664 * @param mode file permissions
665 * @return GNUNET_OK on success, GNUNET_SYSERR on error
668 GNUNET_DISK_fn_write (const char * fn, const void *buffer,
670 enum GNUNET_DISK_AccessPermissions mode)
672 struct GNUNET_DISK_FileHandle *fh;
675 fh = GNUNET_DISK_file_open (fn,
676 GNUNET_DISK_OPEN_WRITE
677 | GNUNET_DISK_OPEN_TRUNCATE
678 | GNUNET_DISK_OPEN_CREATE, mode);
680 return GNUNET_SYSERR;
681 ret = (n == GNUNET_DISK_file_write (fh, buffer, n)) ? GNUNET_OK : GNUNET_SYSERR;
682 GNUNET_assert(GNUNET_OK == GNUNET_DISK_file_close(fh));
688 * Scan a directory for files.
690 * @param dirName the name of the directory
691 * @param callback the method to call for each file,
692 * can be NULL, in that case, we only count
693 * @param callback_cls closure for callback
694 * @return the number of files found, GNUNET_SYSERR on error or
695 * ieration aborted by callback returning GNUNET_SYSERR
698 GNUNET_DISK_directory_scan (const char *dirName,
699 GNUNET_FileNameCallback callback,
703 struct dirent *finfo;
708 unsigned int name_len;
711 GNUNET_assert (dirName != NULL);
712 dname = GNUNET_STRINGS_filename_expand (dirName);
713 while ((strlen (dname) > 0) && (dname[strlen (dname) - 1] == DIR_SEPARATOR))
714 dname[strlen (dname) - 1] = '\0';
715 if (0 != STAT (dname, &istat))
717 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "stat", dname);
719 return GNUNET_SYSERR;
721 if (!S_ISDIR (istat.st_mode))
723 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
724 _("Expected `%s' to be a directory!\n"), dirName);
726 return GNUNET_SYSERR;
729 dinfo = OPENDIR (dname);
730 if ((errno == EACCES) || (dinfo == NULL))
732 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "opendir", dname);
736 return GNUNET_SYSERR;
739 n_size = strlen (dname) + name_len + 2;
740 name = GNUNET_malloc (n_size);
741 while ((finfo = readdir (dinfo)) != NULL)
743 if ((0 == strcmp (finfo->d_name, ".")) ||
744 (0 == strcmp (finfo->d_name, "..")))
746 if (callback != NULL)
748 if (name_len < strlen (finfo->d_name))
751 name_len = strlen (finfo->d_name);
752 n_size = strlen (dname) + name_len + 2;
753 name = GNUNET_malloc (n_size);
755 /* dname can end in "/" only if dname == "/";
756 if dname does not end in "/", we need to add
757 a "/" (otherwise, we must not!) */
758 GNUNET_snprintf (name,
762 (strcmp (dname, DIR_SEPARATOR_STR) ==
763 0) ? "" : DIR_SEPARATOR_STR, finfo->d_name);
764 if (GNUNET_OK != callback (callback_cls, name))
769 return GNUNET_SYSERR;
782 * Opaque handle used for iterating over a directory.
784 struct GNUNET_DISK_DirectoryIterator
789 struct GNUNET_SCHEDULER_Handle *sched;
792 * Function to call on directory entries.
794 GNUNET_DISK_DirectoryIteratorCallback callback;
797 * Closure for callback.
802 * Reference to directory.
812 * Next filename to process.
819 enum GNUNET_SCHEDULER_Priority priority;
825 * Task used by the directory iterator.
828 directory_iterator_task (void *cls,
829 const struct GNUNET_SCHEDULER_TaskContext *tc)
831 struct GNUNET_DISK_DirectoryIterator *iter = cls;
834 name = iter->next_name;
835 GNUNET_assert (name != NULL);
836 iter->next_name = NULL;
837 iter->callback (iter->callback_cls, iter, name, iter->dirname);
843 * This function must be called during the DiskIteratorCallback
844 * (exactly once) to schedule the task to process the next
845 * filename in the directory (if there is one).
847 * @param iter opaque handle for the iterator
848 * @param can set to GNUNET_YES to terminate the iteration early
849 * @return GNUNET_YES if iteration will continue,
850 * GNUNET_NO if this was the last entry (and iteration is complete),
851 * GNUNET_SYSERR if abort was YES
854 GNUNET_DISK_directory_iterator_next (struct GNUNET_DISK_DirectoryIterator
857 struct dirent *finfo;
859 GNUNET_assert (iter->next_name == NULL);
860 if (can == GNUNET_YES)
862 closedir (iter->directory);
863 GNUNET_free (iter->dirname);
865 return GNUNET_SYSERR;
867 while (NULL != (finfo = readdir (iter->directory)))
869 if ((0 == strcmp (finfo->d_name, ".")) ||
870 (0 == strcmp (finfo->d_name, "..")))
872 GNUNET_asprintf (&iter->next_name,
874 iter->dirname, DIR_SEPARATOR_STR, finfo->d_name);
879 GNUNET_DISK_directory_iterator_next (iter, GNUNET_YES);
882 GNUNET_SCHEDULER_add_after (iter->sched,
885 GNUNET_SCHEDULER_NO_TASK,
886 &directory_iterator_task, iter);
892 * Scan a directory for files using the scheduler to run a task for
893 * each entry. The name of the directory must be expanded first (!).
894 * If a scheduler does not need to be used, GNUNET_DISK_directory_scan
895 * may provide a simpler API.
897 * @param sched scheduler to use
898 * @param prio priority to use
899 * @param dirName the name of the directory
900 * @param callback the method to call for each file
901 * @param callback_cls closure for callback
904 GNUNET_DISK_directory_iterator_start (struct GNUNET_SCHEDULER_Handle *sched,
905 enum GNUNET_SCHEDULER_Priority prio,
907 GNUNET_DISK_DirectoryIteratorCallback
908 callback, void *callback_cls)
910 struct GNUNET_DISK_DirectoryIterator *di;
912 di = GNUNET_malloc (sizeof (struct GNUNET_DISK_DirectoryIterator));
914 di->callback = callback;
915 di->callback_cls = callback_cls;
916 di->directory = OPENDIR (dirName);
917 di->dirname = GNUNET_strdup (dirName);
919 GNUNET_DISK_directory_iterator_next (di, GNUNET_NO);
924 * Function that removes the given directory by calling
925 * "GNUNET_DISK_directory_remove".
927 * @param unused not used
928 * @param fn directory to remove
932 remove_helper (void *unused, const char *fn)
934 GNUNET_DISK_directory_remove (fn);
940 * Remove all files in a directory (rm -rf). Call with
944 * @param fileName the file to remove
945 * @return GNUNET_OK on success, GNUNET_SYSERR on error
948 GNUNET_DISK_directory_remove (const char *fileName)
952 if (0 != LSTAT (fileName, &istat))
953 return GNUNET_NO; /* file may not exist... */
954 if (UNLINK (fileName) == 0)
956 if ((errno != EISDIR) &&
957 /* EISDIR is not sufficient in all cases, e.g.
958 sticky /tmp directory may result in EPERM on BSD.
959 So we also explicitly check "isDirectory" */
960 (GNUNET_YES != GNUNET_DISK_directory_test (fileName)))
962 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "rmdir", fileName);
963 return GNUNET_SYSERR;
966 GNUNET_DISK_directory_scan (fileName,
969 return GNUNET_SYSERR;
970 if (0 != RMDIR (fileName))
972 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "rmdir", fileName);
973 return GNUNET_SYSERR;
982 * @param src file to copy
983 * @param dst destination file name
984 * @return GNUNET_OK on success, GNUNET_SYSERR on error
987 GNUNET_DISK_file_copy (const char *src, const char *dst)
993 struct GNUNET_DISK_FileHandle *in;
994 struct GNUNET_DISK_FileHandle *out;
996 if (GNUNET_OK != GNUNET_DISK_file_size (src, &size, GNUNET_YES))
997 return GNUNET_SYSERR;
999 in = GNUNET_DISK_file_open (src, GNUNET_DISK_OPEN_READ,
1000 GNUNET_DISK_PERM_NONE);
1002 return GNUNET_SYSERR;
1003 out = GNUNET_DISK_file_open (dst, GNUNET_DISK_OPEN_WRITE
1004 | GNUNET_DISK_OPEN_CREATE | GNUNET_DISK_OPEN_FAILIFEXISTS,
1005 GNUNET_DISK_PERM_USER_READ | GNUNET_DISK_PERM_USER_WRITE
1006 | GNUNET_DISK_PERM_GROUP_READ | GNUNET_DISK_PERM_GROUP_WRITE);
1009 GNUNET_DISK_file_close (in);
1010 return GNUNET_SYSERR;
1012 buf = GNUNET_malloc (COPY_BLK_SIZE);
1015 len = COPY_BLK_SIZE;
1016 if (len > size - pos)
1018 if (len != GNUNET_DISK_file_read (in, buf, len))
1020 if (len != GNUNET_DISK_file_write (out, buf, len))
1025 GNUNET_DISK_file_close (in);
1026 GNUNET_DISK_file_close (out);
1030 GNUNET_DISK_file_close (in);
1031 GNUNET_DISK_file_close (out);
1032 return GNUNET_SYSERR;
1037 * @brief Removes special characters as ':' from a filename.
1038 * @param fn the filename to canonicalize
1041 GNUNET_DISK_filename_canonicalize (char *fn)
1051 if (c == '/' || c == '\\' || c == ':' || c == '*' || c == '?' ||
1052 c == '"' || c == '<' || c == '>' || c == '|')
1064 * @brief Change owner of a file
1066 * @param filename name of file to change the owner of
1067 * @param user name of the new owner
1068 * @return GNUNET_OK on success, GNUNET_SYSERR on failure
1071 GNUNET_DISK_file_change_owner (const char *filename, const char *user)
1076 pws = getpwnam (user);
1079 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1080 _("Cannot obtain information about user `%s': %s\n"),
1081 user, STRERROR (errno));
1082 return GNUNET_SYSERR;
1084 if (0 != chown (filename, pws->pw_uid, pws->pw_gid))
1085 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "chown", filename);
1092 * Lock a part of a file
1093 * @param fh file handle
1094 * @param lockStart absolute position from where to lock
1095 * @param lockEnd absolute position until where to lock
1096 * @param excl GNUNET_YES for an exclusive lock
1097 * @return GNUNET_OK on success, GNUNET_SYSERR on error
1100 GNUNET_DISK_file_lock (struct GNUNET_DISK_FileHandle *fh, off_t lockStart,
1101 off_t lockEnd, int excl)
1106 return GNUNET_SYSERR;
1112 memset (&fl, 0, sizeof(struct flock));
1113 fl.l_type = excl ? F_WRLCK : F_RDLCK;
1114 fl.l_whence = SEEK_SET;
1115 fl.l_start = lockStart;
1118 return fcntl (fh->fd, F_SETLK, &fl) != 0 ? GNUNET_SYSERR : GNUNET_OK;
1122 memset (&o, 0, sizeof(OVERLAPPED));
1123 o.Offset = lockStart;
1125 if (!LockFileEx (fh->h, (excl ? LOCKFILE_EXCLUSIVE_LOCK : 0)
1126 | LOCKFILE_FAIL_IMMEDIATELY, 0, lockEnd - lockStart, 0, &o))
1128 SetErrnoFromWinError (GetLastError ());
1129 return GNUNET_SYSERR;
1138 * Unlock a part of a file
1139 * @param fh file handle
1140 * @param unlockStart absolute position from where to unlock
1141 * @param unlockEnd absolute position until where to unlock
1142 * @return GNUNET_OK on success, GNUNET_SYSERR on error
1145 GNUNET_DISK_file_unlock (struct GNUNET_DISK_FileHandle *fh, off_t unlockStart,
1151 return GNUNET_SYSERR;
1157 memset (&fl, 0, sizeof(struct flock));
1158 fl.l_type = F_UNLCK;
1159 fl.l_whence = SEEK_SET;
1160 fl.l_start = unlockStart;
1161 fl.l_len = unlockEnd;
1163 return fcntl (fh->fd, F_SETLK, &fl) != 0 ? GNUNET_SYSERR : GNUNET_OK;
1167 memset (&o, 0, sizeof(OVERLAPPED));
1168 o.Offset = unlockStart;
1170 if (!UnlockFileEx (fh->h, 0, unlockEnd - unlockStart, 0, &o))
1172 SetErrnoFromWinError (GetLastError ());
1173 return GNUNET_SYSERR;
1182 * Open a file. Note that the access permissions will only be
1183 * used if a new file is created and if the underlying operating
1184 * system supports the given permissions.
1186 * @param fn file name to be opened
1187 * @param flags opening flags, a combination of GNUNET_DISK_OPEN_xxx bit flags
1188 * @param perm permissions for the newly created file, use
1189 * GNUNET_DISK_PERM_USER_NONE if a file could not be created by this
1190 * call (because of flags)
1191 * @return IO handle on success, NULL on error
1193 struct GNUNET_DISK_FileHandle *
1194 GNUNET_DISK_file_open (const char *fn,
1195 enum GNUNET_DISK_OpenFlags flags,
1196 enum GNUNET_DISK_AccessPermissions perm)
1199 struct GNUNET_DISK_FileHandle *ret;
1210 expfn = GNUNET_STRINGS_filename_expand (fn);
1214 if (GNUNET_DISK_OPEN_READWRITE == (flags & GNUNET_DISK_OPEN_READWRITE))
1215 oflags = O_RDWR; /* note: O_RDWR is NOT always O_RDONLY | O_WRONLY */
1216 else if (flags & GNUNET_DISK_OPEN_READ)
1218 else if (flags & GNUNET_DISK_OPEN_WRITE)
1223 GNUNET_free (expfn);
1226 if (flags & GNUNET_DISK_OPEN_FAILIFEXISTS)
1227 oflags |= (O_CREAT & O_EXCL);
1228 if (flags & GNUNET_DISK_OPEN_TRUNCATE)
1230 if (flags & GNUNET_DISK_OPEN_APPEND)
1232 if (flags & GNUNET_DISK_OPEN_CREATE)
1235 if (perm & GNUNET_DISK_PERM_USER_READ)
1237 if (perm & GNUNET_DISK_PERM_USER_WRITE)
1239 if (perm & GNUNET_DISK_PERM_USER_EXEC)
1241 if (perm & GNUNET_DISK_PERM_GROUP_READ)
1243 if (perm & GNUNET_DISK_PERM_GROUP_WRITE)
1245 if (perm & GNUNET_DISK_PERM_GROUP_EXEC)
1247 if (perm & GNUNET_DISK_PERM_OTHER_READ)
1249 if (perm & GNUNET_DISK_PERM_OTHER_WRITE)
1251 if (perm & GNUNET_DISK_PERM_OTHER_EXEC)
1255 fd = open (expfn, oflags | O_LARGEFILE, mode);
1258 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "open", expfn);
1259 GNUNET_free (expfn);
1266 if (GNUNET_DISK_OPEN_READWRITE == (flags & GNUNET_DISK_OPEN_READWRITE))
1267 access = FILE_READ_DATA | FILE_WRITE_DATA;
1268 else if (flags & GNUNET_DISK_OPEN_READ)
1269 access = FILE_READ_DATA;
1270 else if (flags & GNUNET_DISK_OPEN_WRITE)
1271 access = FILE_WRITE_DATA;
1273 if (flags & GNUNET_DISK_OPEN_FAILIFEXISTS)
1277 else if (flags & GNUNET_DISK_OPEN_CREATE)
1279 if (flags & GNUNET_DISK_OPEN_TRUNCATE)
1280 disp = CREATE_ALWAYS;
1284 else if (flags & GNUNET_DISK_OPEN_TRUNCATE)
1286 disp = TRUNCATE_EXISTING;
1293 /* TODO: access priviledges? */
1294 h = CreateFile (expfn, access, FILE_SHARE_DELETE | FILE_SHARE_READ
1295 | FILE_SHARE_WRITE, NULL, disp, FILE_ATTRIBUTE_NORMAL, NULL);
1296 if (h == INVALID_HANDLE_VALUE)
1298 SetErrnoFromWinError (GetLastError ());
1299 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "open", expfn);
1300 GNUNET_free (expfn);
1304 if (flags & GNUNET_DISK_OPEN_APPEND)
1305 if (SetFilePointer (h, 0, 0, FILE_END) == INVALID_SET_FILE_POINTER)
1307 SetErrnoFromWinError (GetLastError ());
1308 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "SetFilePointer", expfn);
1310 GNUNET_free (expfn);
1315 ret = GNUNET_malloc(sizeof(struct GNUNET_DISK_FileHandle));
1321 GNUNET_free (expfn);
1327 * Close an open file
1328 * @param h file handle
1329 * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
1332 GNUNET_DISK_file_close (struct GNUNET_DISK_FileHandle *h)
1337 return GNUNET_SYSERR;
1341 if (!CloseHandle (h->h))
1343 SetErrnoFromWinError (GetLastError ());
1344 GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "close");
1346 return GNUNET_SYSERR;
1349 if (close (h->fd) != 0)
1351 GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "close");
1353 return GNUNET_SYSERR;
1362 * Construct full path to a file inside of the private
1363 * directory used by GNUnet. Also creates the corresponding
1364 * directory. If the resulting name is supposed to be
1365 * a directory, end the last argument in '/' (or pass
1366 * DIR_SEPARATOR_STR as the last argument before NULL).
1368 * @param cfg configuration to use (determines HOME)
1369 * @param serviceName name of the service
1370 * @param ... is NULL-terminated list of
1371 * path components to append to the
1372 * private directory name.
1373 * @return the constructed filename
1376 GNUNET_DISK_get_home_filename (const struct GNUNET_CONFIGURATION_Handle *cfg,
1377 const char *serviceName, ...)
1383 unsigned int needed;
1386 GNUNET_CONFIGURATION_get_value_filename (cfg,
1387 serviceName, "HOME", &pfx))
1391 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1392 _("No `%s' specified for service `%s' in configuration.\n"),
1393 "HOME", serviceName);
1396 needed = strlen (pfx) + 2;
1397 if ((pfx[strlen (pfx) - 1] != '/') && (pfx[strlen (pfx) - 1] != '\\'))
1399 va_start (ap, serviceName);
1402 c = va_arg (ap, const char *);
1405 needed += strlen (c);
1406 if ((c[strlen (c) - 1] != '/') && (c[strlen (c) - 1] != '\\'))
1410 ret = GNUNET_malloc (needed);
1413 va_start (ap, serviceName);
1416 c = va_arg (ap, const char *);
1419 if ((c[strlen (c) - 1] != '/') && (c[strlen (c) - 1] != '\\'))
1420 strcat (ret, DIR_SEPARATOR_STR);
1424 if ((ret[strlen (ret) - 1] != '/') && (ret[strlen (ret) - 1] != '\\'))
1425 GNUNET_DISK_directory_create_for_file (ret);
1427 GNUNET_DISK_directory_create (ret);
1433 * Handle for a memory-mapping operation.
1435 struct GNUNET_DISK_MapHandle
1439 * Underlying OS handle.
1444 * Address where the map is in memory.
1449 * Number of bytes mapped.
1457 * Map a file into memory
1459 * @param h open file handle
1460 * @param m handle to the new mapping
1461 * @param access access specification, GNUNET_DISK_MAP_TYPE_xxx
1462 * @param len size of the mapping
1463 * @return pointer to the mapped memory region, NULL on failure
1466 GNUNET_DISK_file_map (const struct GNUNET_DISK_FileHandle *h, struct GNUNET_DISK_MapHandle **m,
1467 enum GNUNET_DISK_MapType access, size_t len)
1476 DWORD mapAccess, protect;
1479 if ((access & GNUNET_DISK_MAP_TYPE_READ) &&
1480 (access & GNUNET_DISK_MAP_TYPE_WRITE))
1482 protect = PAGE_READWRITE;
1483 mapAccess = FILE_MAP_ALL_ACCESS;
1485 else if (access & GNUNET_DISK_MAP_TYPE_READ)
1487 protect = PAGE_READONLY;
1488 mapAccess = FILE_MAP_READ;
1490 else if (access & GNUNET_DISK_MAP_TYPE_WRITE)
1492 protect = PAGE_READWRITE;
1493 mapAccess = FILE_MAP_WRITE;
1501 *m = GNUNET_malloc (sizeof (struct GNUNET_DISK_MapHandle));
1502 (*m)->h = CreateFileMapping (h->h, NULL, protect, 0, 0, NULL);
1503 if ((*m)->h == INVALID_HANDLE_VALUE)
1505 SetErrnoFromWinError (GetLastError ());
1510 ret = MapViewOfFile ((*m)->h, mapAccess, 0, 0, len);
1513 SetErrnoFromWinError (GetLastError ());
1514 CloseHandle ((*m)->h);
1523 if (access & GNUNET_DISK_MAP_TYPE_READ)
1525 if (access & GNUNET_DISK_MAP_TYPE_WRITE)
1527 *m = GNUNET_malloc (sizeof (struct GNUNET_DISK_MapHandle));
1528 (*m)->addr = mmap (NULL, len, prot, MAP_SHARED, h->fd, 0);
1536 * @param h mapping handle
1537 * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
1540 GNUNET_DISK_file_unmap (struct GNUNET_DISK_MapHandle *h)
1546 return GNUNET_SYSERR;
1550 ret = UnmapViewOfFile (h->h) ? GNUNET_OK : GNUNET_SYSERR;
1551 if (ret != GNUNET_OK)
1552 SetErrnoFromWinError (GetLastError ());
1553 if (!CloseHandle (h->h) && (ret == GNUNET_OK))
1555 ret = GNUNET_SYSERR;
1556 SetErrnoFromWinError (GetLastError ());
1559 ret = munmap (h->addr, h->len) != -1 ? GNUNET_OK : GNUNET_SYSERR;
1567 * Write file changes to disk
1568 * @param h handle to an open file
1569 * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
1572 GNUNET_DISK_file_sync (const struct GNUNET_DISK_FileHandle *h)
1577 return GNUNET_SYSERR;
1583 ret = FlushFileBuffers (h->h) ? GNUNET_OK : GNUNET_SYSERR;
1584 if (ret != GNUNET_OK)
1585 SetErrnoFromWinError (GetLastError ());
1587 #elif FREEBSD || OPENBSD
1588 return fsync (h->fd) == -1 ? GNUNET_SYSERR : GNUNET_OK;
1590 return fdatasync (h->fd) == -1 ? GNUNET_SYSERR : GNUNET_OK;
1595 * Creates an interprocess channel
1597 * @param blocking creates an asynchronous pipe if set to GNUNET_NO
1598 * @return handle to the new pipe, NULL on error
1600 struct GNUNET_DISK_PipeHandle *
1601 GNUNET_DISK_pipe (int blocking)
1603 struct GNUNET_DISK_PipeHandle *p;
1604 struct GNUNET_DISK_FileHandle *fds;
1606 p = GNUNET_malloc (sizeof (struct GNUNET_DISK_PipeHandle) + 2 * sizeof (struct GNUNET_DISK_FileHandle));
1607 fds = (struct GNUNET_DISK_FileHandle *) &p[1];
1624 p->fd[0]->fd = fd[0];
1625 p->fd[1]->fd = fd[1];
1628 flags = fcntl (fd[0], F_GETFL);
1629 flags |= O_NONBLOCK;
1630 ret = fcntl (fd[0], F_SETFL, flags);
1633 flags = fcntl (fd[1], F_GETFL);
1634 flags |= O_NONBLOCK;
1635 ret = fcntl (fd[1], F_SETFL, flags);
1640 GNUNET_log_strerror(GNUNET_ERROR_TYPE_ERROR, "fcntl");
1641 GNUNET_break (0 == close (p->fd[0]->fd));
1642 GNUNET_break (0 == close (p->fd[1]->fd));
1651 ret = CreatePipe (&p->fd[0]->h, &p->fd[1]->h, NULL, 0);
1655 SetErrnoFromWinError (GetLastError ());
1663 p->fd[0] = GNUNET_malloc(sizeof(struct GNUNET_DISK_FileHandle));
1664 p->fd[1] = GNUNET_malloc(sizeof(struct GNUNET_DISK_FileHandle));
1665 SetNamedPipeHandleState (p->fd[0]->h, &mode, NULL, NULL);
1666 SetNamedPipeHandleState (p->fd[1]->h, &mode, NULL, NULL);
1667 /* this always fails on Windows 95, so we don't care about error handling */
1675 * Closes an interprocess channel
1677 * @param p pipe to close
1678 * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
1681 GNUNET_DISK_pipe_close (struct GNUNET_DISK_PipeHandle *p)
1683 int ret = GNUNET_OK;
1687 if (!CloseHandle (p->fd[0]->h))
1689 SetErrnoFromWinError (GetLastError ());
1690 ret = GNUNET_SYSERR;
1692 if (!CloseHandle (p->fd[1]->h))
1694 SetErrnoFromWinError (GetLastError ());
1695 ret = GNUNET_SYSERR;
1700 if (0 != close (p->fd[0]->fd))
1702 ret = GNUNET_SYSERR;
1705 if (0 != close (p->fd[1]->fd))
1707 ret = GNUNET_SYSERR;
1718 * Get the handle to a particular pipe end
1720 * @param n end to access
1722 const struct GNUNET_DISK_FileHandle *
1723 GNUNET_DISK_pipe_handle (const struct GNUNET_DISK_PipeHandle *p,
1724 enum GNUNET_DISK_PipeEnd n)
1728 case GNUNET_DISK_PIPE_END_READ:
1729 case GNUNET_DISK_PIPE_END_WRITE:
1739 * Retrieve OS file handle
1741 * @param fh GNUnet file descriptor
1742 * @param dst destination buffer
1743 * @param dst_len length of dst
1744 * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
1747 GNUNET_DISK_internal_file_handle_ (const struct GNUNET_DISK_FileHandle *fh,
1752 if (dst_len < sizeof (HANDLE))
1753 return GNUNET_SYSERR;
1754 *((HANDLE *) dst) = fh->h;
1756 if (dst_len < sizeof(int))
1757 return GNUNET_SYSERR;
1758 *((int *) dst) = fh->fd;