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"
41 #include <sys/param.h>
42 #include <sys/mount.h>
45 #include <sys/param.h>
46 #include <sys/mount.h>
49 #include <sys/types.h>
50 #include <sys/statvfs.h>
53 #define _IFMT 0170000 /* type of file */
54 #define _IFLNK 0120000 /* symbolic link */
55 #define S_ISLNK(m) (((m)&_IFMT) == _IFLNK)
57 #error PORT-ME: need to port statfs (how much space is left on the drive?)
75 int include_sym_links;
81 getSizeRec (void *ptr, const char *fn)
83 GetFileSizeData *gfsd = ptr;
91 if (0 != STAT64 (fn, &buf))
93 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "stat64", fn);
97 if (0 != STAT (fn, &buf))
99 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "stat", fn);
100 return GNUNET_SYSERR;
103 if ((!S_ISLNK (buf.st_mode)) || (gfsd->include_sym_links == GNUNET_YES))
104 gfsd->total += buf.st_size;
105 if ((S_ISDIR (buf.st_mode)) &&
106 (0 == ACCESS (fn, X_OK)) &&
107 ((!S_ISLNK (buf.st_mode)) || (gfsd->include_sym_links == GNUNET_YES)))
109 if (GNUNET_SYSERR == GNUNET_DISK_directory_scan (fn, &getSizeRec, gfsd))
110 return GNUNET_SYSERR;
116 * Checks whether a handle is invalid
117 * @param h handle to check
118 * @return GNUNET_YES if invalid, GNUNET_NO if valid
121 GNUNET_DISK_handle_invalid (const struct GNUNET_DISK_FileHandle *h)
124 return !h || h->h == INVALID_HANDLE_VALUE ? GNUNET_YES : GNUNET_NO;
126 return !h || h->fd == -1 ? GNUNET_YES : GNUNET_NO;
132 * Move the read/write pointer in a file
133 * @param h handle of an open file
134 * @param offset position to move to
135 * @param whence specification to which position the offset parameter relates to
136 * @return the new position on success, GNUNET_SYSERR otherwise
139 GNUNET_DISK_file_seek (const struct GNUNET_DISK_FileHandle *h, off_t offset,
140 enum GNUNET_DISK_Seek whence)
145 return GNUNET_SYSERR;
150 static DWORD t[] = { [GNUNET_DISK_SEEK_SET] = FILE_BEGIN,
151 [GNUNET_DISK_SEEK_CUR] = FILE_CURRENT, [GNUNET_DISK_SEEK_END] = FILE_END };
153 ret = SetFilePointer (h->h, offset, NULL, t[whence]);
154 if (ret == INVALID_SET_FILE_POINTER)
156 SetErrnoFromWinError (GetLastError ());
157 return GNUNET_SYSERR;
161 static int t[] = { [GNUNET_DISK_SEEK_SET] = SEEK_SET,
162 [GNUNET_DISK_SEEK_CUR] = SEEK_CUR, [GNUNET_DISK_SEEK_END] = SEEK_END };
164 return lseek (h->fd, offset, t[whence]);
169 * Get the size of the file (or directory)
170 * of the given file (in bytes).
172 * @return GNUNET_SYSERR on error, GNUNET_OK on success
175 GNUNET_DISK_file_size (const char *filename,
179 GetFileSizeData gfsd;
182 GNUNET_assert (size != NULL);
184 gfsd.include_sym_links = includeSymLinks;
185 ret = getSizeRec (&gfsd, filename);
193 #include <sys/statvfs.h>
201 * @param filename name of the file
202 * @param dev set to the device ID
203 * @param ino set to the inode ID
204 * @return GNUNET_OK on success
206 int GNUNET_DISK_file_get_identifiers (const char *filename,
214 if ( (0 == stat(filename,
216 (0 == statvfs (filename,
219 *dev = (uint32_t) fbuf.f_fsid;
220 *ino = (uint64_t) sbuf.st_ino;
223 return GNUNET_SYSERR;
228 * Create an (empty) temporary file on disk.
230 * @param t component to use for the name;
231 * does NOT contain "XXXXXX" or "/tmp/".
232 * @return NULL on error, otherwise name of fresh
233 * file on disk in directory for temporary files
236 GNUNET_DISK_mktemp (const char *t)
243 tmpdir = getenv ("TMPDIR");
244 tmpdir = tmpdir ? tmpdir : "/tmp";
246 GNUNET_asprintf (&tmpl,
253 fn = (char *) GNUNET_malloc (MAX_PATH + 1);
254 plibc_conv_to_win_path (tmpl, fn);
262 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR,
269 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING,
277 * Get the number of blocks that are left on the partition that
278 * contains the given file (for normal users).
280 * @param part a file on the partition to check
281 * @return -1 on errors, otherwise the number of free blocks
284 GNUNET_DISK_get_blocks_available (const char *part)
289 if (0 != statvfs (part, &buf))
291 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "statfs", part);
301 path = GNUNET_STRINGS_filename_expand (part);
302 memcpy (szDrive, path, 3);
305 if (!GetDiskFreeSpace (szDrive, &dwDummy, &dwDummy, &dwBlocks, &dwDummy))
307 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
308 _("`%s' failed for drive `%s': %u\n"),
309 "GetDiskFreeSpace", szDrive, GetLastError ());
316 if (0 != statfs (part, &s))
318 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "statfs", part);
326 * Test if fil is a directory.
328 * @return GNUNET_YES if yes, GNUNET_NO if not, GNUNET_SYSERR if it
332 GNUNET_DISK_directory_test (const char *fil)
334 struct stat filestat;
337 ret = STAT (fil, &filestat);
342 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "stat", fil);
343 return GNUNET_SYSERR;
347 if (!S_ISDIR (filestat.st_mode))
349 if (ACCESS (fil, R_OK | X_OK) < 0)
351 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "access", fil);
352 return GNUNET_SYSERR;
358 * Check that fil corresponds to a filename
359 * (of a file that exists and that is not a directory).
360 * @returns GNUNET_YES if yes, GNUNET_NO if not a file, GNUNET_SYSERR if something
361 * else (will print an error message in that case, too).
364 GNUNET_DISK_file_test (const char *fil)
366 struct stat filestat;
370 rdir = GNUNET_STRINGS_filename_expand (fil);
372 return GNUNET_SYSERR;
374 ret = STAT (rdir, &filestat);
379 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "stat", rdir);
381 return GNUNET_SYSERR;
386 if (!S_ISREG (filestat.st_mode))
391 if (ACCESS (rdir, R_OK) < 0)
393 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "access", rdir);
395 return GNUNET_SYSERR;
402 * Implementation of "mkdir -p"
403 * @param dir the directory to create
404 * @returns GNUNET_OK on success, GNUNET_SYSERR on failure
407 GNUNET_DISK_directory_create (const char *dir)
414 rdir = GNUNET_STRINGS_filename_expand (dir);
416 return GNUNET_SYSERR;
420 pos = 1; /* skip heading '/' */
422 /* Local or Network path? */
423 if (strncmp (rdir, "\\\\", 2) == 0)
428 if (rdir[pos] == '\\')
438 pos = 3; /* strlen("C:\\") */
443 if ((rdir[pos] == DIR_SEPARATOR) || (pos == len))
446 ret = GNUNET_DISK_directory_test (rdir);
447 if (ret == GNUNET_SYSERR)
450 return GNUNET_SYSERR;
452 if (ret == GNUNET_NO)
455 ret = mkdir (rdir, S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH); /* 755 */
459 if ((ret != 0) && (errno != EEXIST))
461 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR, "mkdir",
464 return GNUNET_SYSERR;
467 rdir[pos] = DIR_SEPARATOR;
477 * Create the directory structure for storing
480 * @param dir name of a file in the directory
481 * @returns GNUNET_OK on success,
482 * GNUNET_SYSERR on failure,
483 * GNUNET_NO if the directory
484 * exists but is not writeable for us
487 GNUNET_DISK_directory_create_for_file (const char *dir)
493 rdir = GNUNET_STRINGS_filename_expand (dir);
495 return GNUNET_SYSERR;
497 while ((len > 0) && (rdir[len] != DIR_SEPARATOR))
500 ret = GNUNET_DISK_directory_create (rdir);
501 if ((ret == GNUNET_OK) && (0 != ACCESS (rdir, W_OK)))
508 * Read the contents of a binary file into a buffer.
509 * @param h handle to an open file
510 * @param result the buffer to write the result to
511 * @param len the maximum number of bytes to read
512 * @return the number of bytes read on success, GNUNET_SYSERR on failure
515 GNUNET_DISK_file_read (const struct GNUNET_DISK_FileHandle *h, void *result,
521 return GNUNET_SYSERR;
527 if (!ReadFile (h->h, result, len, &bytesRead, NULL))
529 SetErrnoFromWinError (GetLastError ());
530 return GNUNET_SYSERR;
534 return read (h->fd, result, len);
540 * Read the contents of a binary file into a buffer.
542 * @param fn file name
543 * @param result the buffer to write the result to
544 * @param len the maximum number of bytes to read
545 * @return number of bytes read, GNUNET_SYSERR on failure
548 GNUNET_DISK_fn_read (const char * const fn,
552 struct GNUNET_DISK_FileHandle *fh;
555 fh = GNUNET_DISK_file_open (fn, GNUNET_DISK_OPEN_READ);
557 return GNUNET_SYSERR;
558 ret = GNUNET_DISK_file_read (fh, result, len);
559 GNUNET_assert(GNUNET_OK == GNUNET_DISK_file_close(fh));
566 * Write a buffer to a file.
567 * @param h handle to open file
568 * @param buffer the data to write
569 * @param n number of bytes to write
570 * @return number of bytes written on success, GNUNET_SYSERR on error
573 GNUNET_DISK_file_write (const struct GNUNET_DISK_FileHandle *h, const void *buffer,
579 return GNUNET_SYSERR;
585 if (!WriteFile (h->h, buffer, n, &bytesWritten, NULL))
587 SetErrnoFromWinError (GetLastError ());
588 return GNUNET_SYSERR;
592 return write (h->fd, buffer, n);
597 * Write a buffer to a file. If the file is longer than the
598 * number of bytes that will be written, iit will be truncated.
600 * @param fn file name
601 * @param buffer the data to write
602 * @param n number of bytes to write
603 * @return GNUNET_OK on success, GNUNET_SYSERR on error
606 GNUNET_DISK_fn_write (const char * const fn, const void *buffer,
609 struct GNUNET_DISK_FileHandle *fh;
612 fh = GNUNET_DISK_file_open (fn,
613 GNUNET_DISK_OPEN_WRITE
614 | GNUNET_DISK_OPEN_TRUNCATE
615 | GNUNET_DISK_OPEN_CREATE, mode);
617 return GNUNET_SYSERR;
618 ret = (n == GNUNET_DISK_file_write (fh, buffer, n)) ? GNUNET_OK : GNUNET_SYSERR;
619 GNUNET_assert(GNUNET_OK == GNUNET_DISK_file_close(fh));
625 * Scan a directory for files. The name of the directory
626 * must be expanded first (!).
627 * @param dirName the name of the directory
628 * @param callback the method to call for each file,
629 * can be NULL, in that case, we only count
630 * @param data argument to pass to callback
631 * @return the number of files found, GNUNET_SYSERR on error or
632 * ieration aborted by callback returning GNUNET_SYSERR
635 GNUNET_DISK_directory_scan (const char *dirName,
636 GNUNET_FileNameCallback callback, void *data)
639 struct dirent *finfo;
644 unsigned int name_len;
647 GNUNET_assert (dirName != NULL);
648 dname = GNUNET_STRINGS_filename_expand (dirName);
649 while ((strlen (dname) > 0) && (dname[strlen (dname) - 1] == DIR_SEPARATOR))
650 dname[strlen (dname) - 1] = '\0';
651 if (0 != STAT (dname, &istat))
653 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "stat", dname);
655 return GNUNET_SYSERR;
657 if (!S_ISDIR (istat.st_mode))
659 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
660 _("Expected `%s' to be a directory!\n"), dirName);
662 return GNUNET_SYSERR;
665 dinfo = OPENDIR (dname);
666 if ((errno == EACCES) || (dinfo == NULL))
668 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "opendir", dname);
672 return GNUNET_SYSERR;
675 n_size = strlen (dname) + name_len + 2;
676 name = GNUNET_malloc (n_size);
677 while ((finfo = readdir (dinfo)) != NULL)
679 if ((0 == strcmp (finfo->d_name, ".")) ||
680 (0 == strcmp (finfo->d_name, "..")))
682 if (callback != NULL)
684 if (name_len < strlen (finfo->d_name))
687 name_len = strlen (finfo->d_name);
688 n_size = strlen (dname) + name_len + 2;
689 name = GNUNET_malloc (n_size);
691 /* dname can end in "/" only if dname == "/";
692 if dname does not end in "/", we need to add
693 a "/" (otherwise, we must not!) */
694 GNUNET_snprintf (name,
698 (strcmp (dname, DIR_SEPARATOR_STR) ==
699 0) ? "" : DIR_SEPARATOR_STR, finfo->d_name);
700 if (GNUNET_OK != callback (data, name))
705 return GNUNET_SYSERR;
718 * Opaque handle used for iterating over a directory.
720 struct GNUNET_DISK_DirectoryIterator
725 struct GNUNET_SCHEDULER_Handle *sched;
728 * Function to call on directory entries.
730 GNUNET_DISK_DirectoryIteratorCallback callback;
733 * Closure for callback.
738 * Reference to directory.
748 * Next filename to process.
755 enum GNUNET_SCHEDULER_Priority priority;
761 * Task used by the directory iterator.
764 directory_iterator_task (void *cls,
765 const struct GNUNET_SCHEDULER_TaskContext *tc)
767 struct GNUNET_DISK_DirectoryIterator *iter = cls;
770 name = iter->next_name;
771 GNUNET_assert (name != NULL);
772 iter->next_name = NULL;
773 iter->callback (iter->callback_cls, iter, name, iter->dirname);
779 * This function must be called during the DiskIteratorCallback
780 * (exactly once) to schedule the task to process the next
781 * filename in the directory (if there is one).
783 * @param iter opaque handle for the iterator
784 * @param can set to GNUNET_YES to terminate the iteration early
785 * @return GNUNET_YES if iteration will continue,
786 * GNUNET_NO if this was the last entry (and iteration is complete),
787 * GNUNET_SYSERR if abort was YES
790 GNUNET_DISK_directory_iterator_next (struct GNUNET_DISK_DirectoryIterator
793 struct dirent *finfo;
795 GNUNET_assert (iter->next_name == NULL);
796 if (can == GNUNET_YES)
798 closedir (iter->directory);
799 GNUNET_free (iter->dirname);
801 return GNUNET_SYSERR;
803 while (NULL != (finfo = readdir (iter->directory)))
805 if ((0 == strcmp (finfo->d_name, ".")) ||
806 (0 == strcmp (finfo->d_name, "..")))
808 GNUNET_asprintf (&iter->next_name,
810 iter->dirname, DIR_SEPARATOR_STR, finfo->d_name);
815 GNUNET_DISK_directory_iterator_next (iter, GNUNET_YES);
818 GNUNET_SCHEDULER_add_after (iter->sched,
821 GNUNET_SCHEDULER_NO_TASK,
822 &directory_iterator_task, iter);
828 * Scan a directory for files using the scheduler to run a task for
829 * each entry. The name of the directory must be expanded first (!).
830 * If a scheduler does not need to be used, GNUNET_DISK_directory_scan
831 * may provide a simpler API.
833 * @param sched scheduler to use
834 * @param prio priority to use
835 * @param dirName the name of the directory
836 * @param callback the method to call for each file
837 * @param callback_cls closure for callback
840 GNUNET_DISK_directory_iterator_start (struct GNUNET_SCHEDULER_Handle *sched,
841 enum GNUNET_SCHEDULER_Priority prio,
843 GNUNET_DISK_DirectoryIteratorCallback
844 callback, void *callback_cls)
846 struct GNUNET_DISK_DirectoryIterator *di;
848 di = GNUNET_malloc (sizeof (struct GNUNET_DISK_DirectoryIterator));
850 di->callback = callback;
851 di->callback_cls = callback_cls;
852 di->directory = OPENDIR (dirName);
853 di->dirname = GNUNET_strdup (dirName);
855 GNUNET_DISK_directory_iterator_next (di, GNUNET_NO);
860 remove_helper (void *unused, const char *fn)
862 GNUNET_DISK_directory_remove (fn);
867 * Remove all files in a directory (rm -rf). Call with
871 * @param fileName the file to remove
872 * @return GNUNET_OK on success, GNUNET_SYSERR on error
875 GNUNET_DISK_directory_remove (const char *fileName)
879 if (0 != LSTAT (fileName, &istat))
880 return GNUNET_NO; /* file may not exist... */
881 if (UNLINK (fileName) == 0)
883 if ((errno != EISDIR) &&
884 /* EISDIR is not sufficient in all cases, e.g.
885 sticky /tmp directory may result in EPERM on BSD.
886 So we also explicitly check "isDirectory" */
887 (GNUNET_YES != GNUNET_DISK_directory_test (fileName)))
889 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "rmdir", fileName);
890 return GNUNET_SYSERR;
893 GNUNET_DISK_directory_scan (fileName, remove_helper, NULL))
894 return GNUNET_SYSERR;
895 if (0 != RMDIR (fileName))
897 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "rmdir", fileName);
898 return GNUNET_SYSERR;
903 #define COPY_BLK_SIZE 65536
907 * @return GNUNET_OK on success, GNUNET_SYSERR on error
910 GNUNET_DISK_file_copy (const char *src, const char *dst)
916 struct GNUNET_DISK_FileHandle *in;
917 struct GNUNET_DISK_FileHandle *out;
919 if (GNUNET_OK != GNUNET_DISK_file_size (src, &size, GNUNET_YES))
920 return GNUNET_SYSERR;
922 in = GNUNET_DISK_file_open (src, GNUNET_DISK_OPEN_READ);
924 return GNUNET_SYSERR;
925 out = GNUNET_DISK_file_open (dst, GNUNET_DISK_OPEN_WRITE
926 | GNUNET_DISK_OPEN_CREATE | GNUNET_DISK_OPEN_FAILIFEXISTS,
927 GNUNET_DISK_PERM_USER_READ | GNUNET_DISK_PERM_USER_WRITE
928 | GNUNET_DISK_PERM_GROUP_READ | GNUNET_DISK_PERM_GROUP_WRITE);
931 GNUNET_DISK_file_close (in);
932 return GNUNET_SYSERR;
934 buf = GNUNET_malloc (COPY_BLK_SIZE);
938 if (len > size - pos)
940 if (len != GNUNET_DISK_file_read (in, buf, len))
942 if (len != GNUNET_DISK_file_write (out, buf, len))
947 GNUNET_DISK_file_close (in);
948 GNUNET_DISK_file_close (out);
952 GNUNET_DISK_file_close (in);
953 GNUNET_DISK_file_close (out);
954 return GNUNET_SYSERR;
959 * @brief Removes special characters as ':' from a filename.
960 * @param fn the filename to canonicalize
963 GNUNET_DISK_filename_canonicalize (char *fn)
973 if (c == '/' || c == '\\' || c == ':' || c == '*' || c == '?' ||
974 c == '"' || c == '<' || c == '>' || c == '|')
986 * @brief Change owner of a file
989 GNUNET_DISK_file_change_owner (const char *filename, const char *user)
994 pws = getpwnam (user);
997 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
998 _("Cannot obtain information about user `%s': %s\n"),
999 user, STRERROR (errno));
1000 return GNUNET_SYSERR;
1002 if (0 != chown (filename, pws->pw_uid, pws->pw_gid))
1003 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "chown", filename);
1010 * Lock a part of a file
1011 * @param fh file handle
1012 * @param lockStart absolute position from where to lock
1013 * @param lockEnd absolute position until where to lock
1014 * @param excl GNUNET_YES for an exclusive lock
1015 * @return GNUNET_OK on success, GNUNET_SYSERR on error
1018 GNUNET_DISK_file_lock (struct GNUNET_DISK_FileHandle *fh, off_t lockStart,
1019 off_t lockEnd, int excl)
1024 return GNUNET_SYSERR;
1030 memset (&fl, 0, sizeof(struct flock));
1031 fl.l_type = excl ? F_WRLCK : F_RDLCK;
1032 fl.l_whence = SEEK_SET;
1033 fl.l_start = lockStart;
1036 return fcntl (fh->fd, F_SETLK, &fl) != 0 ? GNUNET_SYSERR : GNUNET_OK;
1040 memset (&o, 0, sizeof(OVERLAPPED));
1041 o.Offset = lockStart;
1043 if (!LockFileEx (fh->h, (excl ? LOCKFILE_EXCLUSIVE_LOCK : 0)
1044 | LOCKFILE_FAIL_IMMEDIATELY, 0, lockEnd - lockStart, 0, &o))
1046 SetErrnoFromWinError (GetLastError ());
1047 return GNUNET_SYSERR;
1056 * Unlock a part of a file
1057 * @param fh file handle
1058 * @param unlockStart absolute position from where to unlock
1059 * @param unlockEnd absolute position until where to unlock
1060 * @return GNUNET_OK on success, GNUNET_SYSERR on error
1063 GNUNET_DISK_file_unlock (struct GNUNET_DISK_FileHandle *fh, off_t unlockStart,
1069 return GNUNET_SYSERR;
1075 memset (&fl, 0, sizeof(struct flock));
1076 fl.l_type = F_UNLCK;
1077 fl.l_whence = SEEK_SET;
1078 fl.l_start = unlockStart;
1079 fl.l_len = unlockEnd;
1081 return fcntl (fh->fd, F_SETLK, &fl) != 0 ? GNUNET_SYSERR : GNUNET_OK;
1085 memset (&o, 0, sizeof(OVERLAPPED));
1086 o.Offset = unlockStart;
1088 if (!UnlockFileEx (fh->h, 0, unlockEnd - unlockStart, 0, &o))
1090 SetErrnoFromWinError (GetLastError ());
1091 return GNUNET_SYSERR;
1101 * @param fn file name to be opened
1102 * @param flags opening flags, a combination of GNUNET_DISK_OPEN_xxx bit flags
1103 * @param ... permissions for the newly created file
1104 * @return IO handle on success, NULL on error
1106 struct GNUNET_DISK_FileHandle *
1107 GNUNET_DISK_file_open (const char *fn,
1108 enum GNUNET_DISK_OpenFlags flags,
1112 struct GNUNET_DISK_FileHandle *ret;
1123 expfn = GNUNET_STRINGS_filename_expand (fn);
1127 if (GNUNET_DISK_OPEN_READWRITE == (flags & GNUNET_DISK_OPEN_READWRITE))
1128 oflags = O_RDWR; /* note: O_RDWR is NOT always O_RDONLY | O_WRONLY */
1129 else if (flags & GNUNET_DISK_OPEN_READ)
1131 else if (flags & GNUNET_DISK_OPEN_WRITE)
1136 GNUNET_free (expfn);
1139 if (flags & GNUNET_DISK_OPEN_FAILIFEXISTS)
1140 oflags |= (O_CREAT & O_EXCL);
1141 if (flags & GNUNET_DISK_OPEN_TRUNCATE)
1143 if (flags & GNUNET_DISK_OPEN_APPEND)
1145 if (flags & GNUNET_DISK_OPEN_CREATE)
1152 va_start (arg, flags);
1153 perm = va_arg (arg, int);
1156 if (perm & GNUNET_DISK_PERM_USER_READ)
1158 if (perm & GNUNET_DISK_PERM_USER_WRITE)
1160 if (perm & GNUNET_DISK_PERM_USER_EXEC)
1162 if (perm & GNUNET_DISK_PERM_GROUP_READ)
1164 if (perm & GNUNET_DISK_PERM_GROUP_WRITE)
1166 if (perm & GNUNET_DISK_PERM_GROUP_EXEC)
1168 if (perm & GNUNET_DISK_PERM_OTHER_READ)
1170 if (perm & GNUNET_DISK_PERM_OTHER_WRITE)
1172 if (perm & GNUNET_DISK_PERM_OTHER_EXEC)
1176 fd = open (expfn, oflags | O_LARGEFILE, mode);
1179 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "open", expfn);
1180 GNUNET_free (expfn);
1187 if (GNUNET_DISK_OPEN_READWRITE == (flags & GNUNET_DISK_OPEN_READWRITE))
1188 access = FILE_READ_DATA | FILE_WRITE_DATA;
1189 else if (flags & GNUNET_DISK_OPEN_READ)
1190 access = FILE_READ_DATA;
1191 else if (flags & GNUNET_DISK_OPEN_WRITE)
1192 access = FILE_WRITE_DATA;
1194 if (flags & GNUNET_DISK_OPEN_FAILIFEXISTS)
1198 else if (flags & GNUNET_DISK_OPEN_CREATE)
1200 if (flags & GNUNET_DISK_OPEN_TRUNCATE)
1201 disp = CREATE_ALWAYS;
1205 else if (flags & GNUNET_DISK_OPEN_TRUNCATE)
1207 disp = TRUNCATE_EXISTING;
1214 /* TODO: access priviledges? */
1215 h = CreateFile (expfn, access, FILE_SHARE_DELETE | FILE_SHARE_READ
1216 | FILE_SHARE_WRITE, NULL, disp, FILE_ATTRIBUTE_NORMAL, NULL);
1217 if (h == INVALID_HANDLE_VALUE)
1219 SetErrnoFromWinError (GetLastError ());
1220 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "open", expfn);
1221 GNUNET_free (expfn);
1225 if (flags & GNUNET_DISK_OPEN_APPEND)
1226 if (SetFilePointer (h, 0, 0, FILE_END) == INVALID_SET_FILE_POINTER)
1228 SetErrnoFromWinError (GetLastError ());
1229 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "SetFilePointer", expfn);
1231 GNUNET_free (expfn);
1236 ret = GNUNET_malloc(sizeof(struct GNUNET_DISK_FileHandle));
1242 GNUNET_free (expfn);
1247 * Close an open file
1248 * @param h file handle
1249 * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
1252 GNUNET_DISK_file_close (struct GNUNET_DISK_FileHandle *h)
1257 return GNUNET_SYSERR;
1261 if (!CloseHandle (h->h))
1263 SetErrnoFromWinError (GetLastError ());
1264 GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "close");
1266 return GNUNET_SYSERR;
1269 if (close (h->fd) != 0)
1271 GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "close");
1273 return GNUNET_SYSERR;
1281 * Construct full path to a file inside of the private
1282 * directory used by GNUnet. Also creates the corresponding
1283 * directory. If the resulting name is supposed to be
1284 * a directory, end the last argument in '/' (or pass
1285 * DIR_SEPARATOR_STR as the last argument before NULL).
1287 * @param cfg configuration to use (determines HOME)
1288 * @param serviceName name of the service
1289 * @param varargs is NULL-terminated list of
1290 * path components to append to the
1291 * private directory name.
1292 * @return the constructed filename
1295 GNUNET_DISK_get_home_filename (const struct GNUNET_CONFIGURATION_Handle *cfg,
1296 const char *serviceName, ...)
1302 unsigned int needed;
1305 GNUNET_CONFIGURATION_get_value_filename (cfg,
1306 serviceName, "HOME", &pfx))
1310 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1311 _("No `%s' specified for service `%s' in configuration.\n"),
1312 "HOME", serviceName);
1315 needed = strlen (pfx) + 2;
1316 if ((pfx[strlen (pfx) - 1] != '/') && (pfx[strlen (pfx) - 1] != '\\'))
1318 va_start (ap, serviceName);
1321 c = va_arg (ap, const char *);
1324 needed += strlen (c);
1325 if ((c[strlen (c) - 1] != '/') && (c[strlen (c) - 1] != '\\'))
1329 ret = GNUNET_malloc (needed);
1332 va_start (ap, serviceName);
1335 c = va_arg (ap, const char *);
1338 if ((c[strlen (c) - 1] != '/') && (c[strlen (c) - 1] != '\\'))
1339 strcat (ret, DIR_SEPARATOR_STR);
1343 if ((ret[strlen (ret) - 1] != '/') && (ret[strlen (ret) - 1] != '\\'))
1344 GNUNET_DISK_directory_create_for_file (ret);
1346 GNUNET_DISK_directory_create (ret);
1350 struct GNUNET_DISK_MapHandle
1362 * Map a file into memory
1363 * @param h open file handle
1364 * @param m handle to the new mapping
1365 * @param access access specification, GNUNET_DISK_MAP_TYPE_xxx
1366 * @param len size of the mapping
1367 * @return pointer to the mapped memory region, NULL on failure
1370 GNUNET_DISK_file_map (const struct GNUNET_DISK_FileHandle *h, struct GNUNET_DISK_MapHandle **m,
1371 enum GNUNET_DISK_MapType access, size_t len)
1380 DWORD mapAccess, protect;
1383 if ((access & GNUNET_DISK_MAP_TYPE_READ) &&
1384 (access & GNUNET_DISK_MAP_TYPE_WRITE))
1386 protect = PAGE_READWRITE;
1387 mapAccess = FILE_MAP_ALL_ACCESS;
1389 else if (access & GNUNET_DISK_MAP_TYPE_READ)
1391 protect = PAGE_READONLY;
1392 mapAccess = FILE_MAP_READ;
1394 else if (access & GNUNET_DISK_MAP_TYPE_WRITE)
1396 protect = PAGE_READWRITE;
1397 mapAccess = FILE_MAP_WRITE;
1405 *m = GNUNET_malloc (sizeof (struct GNUNET_DISK_MapHandle));
1406 (*m)->h = CreateFileMapping (h->h, NULL, protect, 0, 0, NULL);
1407 if ((*m)->h == INVALID_HANDLE_VALUE)
1409 SetErrnoFromWinError (GetLastError ());
1414 ret = MapViewOfFile ((*m)->h, mapAccess, 0, 0, len);
1417 SetErrnoFromWinError (GetLastError ());
1418 CloseHandle ((*m)->h);
1427 if (access & GNUNET_DISK_MAP_TYPE_READ)
1429 if (access & GNUNET_DISK_MAP_TYPE_WRITE)
1431 *m = GNUNET_malloc (sizeof (struct GNUNET_DISK_MapHandle));
1432 (*m)->addr = mmap (NULL, len, prot, MAP_SHARED, h->fd, 0);
1440 * @param h mapping handle
1441 * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
1444 GNUNET_DISK_file_unmap (struct GNUNET_DISK_MapHandle *h)
1450 return GNUNET_SYSERR;
1454 ret = UnmapViewOfFile (h->h) ? GNUNET_OK : GNUNET_SYSERR;
1455 if (ret != GNUNET_OK)
1456 SetErrnoFromWinError (GetLastError ());
1457 if (!CloseHandle (h->h) && (ret == GNUNET_OK))
1459 ret = GNUNET_SYSERR;
1460 SetErrnoFromWinError (GetLastError ());
1463 ret = munmap (h->addr, h->len) != -1 ? GNUNET_OK : GNUNET_SYSERR;
1471 * Write file changes to disk
1472 * @param h handle to an open file
1473 * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
1476 GNUNET_DISK_file_sync (const struct GNUNET_DISK_FileHandle *h)
1481 return GNUNET_SYSERR;
1487 ret = FlushFileBuffers (h->h) ? GNUNET_OK : GNUNET_SYSERR;
1488 if (ret != GNUNET_OK)
1489 SetErrnoFromWinError (GetLastError ());
1491 #elif FREEBSD || OPENBSD
1492 return fsync (h->fd) == -1 ? GNUNET_SYSERR : GNUNET_OK;
1494 return fdatasync (h->fd) == -1 ? GNUNET_SYSERR : GNUNET_OK;
1499 * Creates an interprocess channel
1501 * @param blocking creates an asynchronous pipe if set to GNUNET_NO
1502 * @return handle to the new pipe, NULL on error
1504 struct GNUNET_DISK_PipeHandle *
1505 GNUNET_DISK_pipe (int blocking)
1507 struct GNUNET_DISK_PipeHandle *p;
1511 p = GNUNET_malloc (sizeof (struct GNUNET_DISK_PipeHandle));
1522 p->fd[0] = GNUNET_malloc(sizeof(struct GNUNET_DISK_FileHandle));
1523 p->fd[1] = GNUNET_malloc(sizeof(struct GNUNET_DISK_FileHandle));
1524 p->fd[0]->fd = fd[0];
1525 p->fd[1]->fd = fd[1];
1529 flags = fcntl (fd[0], F_GETFL);
1530 flags |= O_NONBLOCK;
1531 ret = fcntl (fd[0], F_SETFL, flags);
1534 flags = fcntl (fd[1], F_GETFL);
1535 flags |= O_NONBLOCK;
1536 ret = fcntl (fd[1], F_SETFL, flags);
1541 GNUNET_log_strerror(GNUNET_ERROR_TYPE_ERROR, "fcntl");
1542 GNUNET_DISK_file_close (p->fd[0]);
1543 GNUNET_DISK_file_close (p->fd[1]);
1556 ret = CreatePipe (&p->fd[0].h, &p->fd[1].h, NULL, 0);
1564 p->fd[0] = GNUNET_malloc(sizeof(struct GNUNET_DISK_FileHandle));
1565 p->fd[1] = GNUNET_malloc(sizeof(struct GNUNET_DISK_FileHandle));
1566 SetNamedPipeHandleState (p->fd[0]->h, &mode, NULL, NULL);
1567 SetNamedPipeHandleState (p->fd[1]->h, &mode, NULL, NULL);
1568 /* this always fails on Windows 95, so we don't care about error handling */
1575 if (GNUNET_YES == err)
1586 * Closes an interprocess channel
1588 * @param p pipe to close
1589 * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
1592 GNUNET_DISK_pipe_close (struct GNUNET_DISK_PipeHandle *p)
1594 int ret = GNUNET_OK;
1596 if (!CloseHandle (p->fd[0]->h))
1598 SetErrnoFromWinError (GetLastError ());
1599 ret = GNUNET_SYSERR;
1602 if (!CloseHandle (p->fd[1]->h))
1604 SetErrnoFromWinError (GetLastError ());
1605 ret = GNUNET_SYSERR;
1610 if (0 != close (p->fd[0]->fd))
1612 ret = GNUNET_SYSERR;
1618 if (0 != close (p->fd[1]->fd))
1619 ret = GNUNET_SYSERR;
1623 GNUNET_free (p->fd[0]);
1624 GNUNET_free (p->fd[1]);
1631 * Get the handle to a particular pipe end
1633 * @param n number of the end
1635 const struct GNUNET_DISK_FileHandle *
1636 GNUNET_DISK_pipe_handle (const struct GNUNET_DISK_PipeHandle *p, int n)
1642 * Retrieve OS file handle
1644 * @param fh GNUnet file descriptor
1645 * @param dst destination buffer
1646 * @param dst_len length of dst
1647 * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
1650 GNUNET_internal_disk_file_handle (const struct GNUNET_DISK_FileHandle *fh,
1651 void *dst, unsigned int dst_len)
1654 if (dst_len < sizeof (HANDLE))
1655 return GNUNET_SYSERR;
1656 *((HANDLE *) dst) = fh->h;
1658 if (dst_len < sizeof(int))
1659 return GNUNET_SYSERR;
1660 *((int *) dst) = fh->fd;