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"
40 #include <sys/param.h>
41 #include <sys/mount.h>
44 #include <sys/param.h>
45 #include <sys/mount.h>
48 #include <sys/types.h>
49 #include <sys/statvfs.h>
52 #define _IFMT 0170000 /* type of file */
53 #define _IFLNK 0120000 /* symbolic link */
54 #define S_ISLNK(m) (((m)&_IFMT) == _IFLNK)
56 #error PORT-ME: need to port statfs (how much space is left on the drive?)
73 unsigned long long total;
74 int include_sym_links;
77 struct GNUNET_DISK_FileHandle
87 getSizeRec (void *ptr, const char *fn)
89 GetFileSizeData *gfsd = ptr;
97 if (0 != STAT64 (fn, &buf))
99 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "stat64", fn);
100 return GNUNET_SYSERR;
103 if (0 != STAT (fn, &buf))
105 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "stat", fn);
106 return GNUNET_SYSERR;
109 if ((!S_ISLNK (buf.st_mode)) || (gfsd->include_sym_links == GNUNET_YES))
110 gfsd->total += buf.st_size;
111 if ((S_ISDIR (buf.st_mode)) &&
112 (0 == ACCESS (fn, X_OK)) &&
113 ((!S_ISLNK (buf.st_mode)) || (gfsd->include_sym_links == GNUNET_YES)))
115 if (GNUNET_SYSERR == GNUNET_DISK_directory_scan (fn, &getSizeRec, gfsd))
116 return GNUNET_SYSERR;
122 * Checks whether a handle is invalid
123 * @param h handle to check
124 * @return GNUNET_YES if invalid, GNUNET_NO if valid
127 GNUNET_DISK_handle_invalid (const struct GNUNET_DISK_FileHandle *h)
130 return !h || h->h == INVALID_HANDLE_VALUE ? GNUNET_YES : GNUNET_NO;
132 return !h || h->fd == -1 ? GNUNET_YES : GNUNET_NO;
138 * Move the read/write pointer in a file
139 * @param h handle of an open file
140 * @param offset position to move to
141 * @param whence specification to which position the offset parameter relates to
142 * @return the new position on success, GNUNET_SYSERR otherwise
145 GNUNET_DISK_file_seek (const struct GNUNET_DISK_FileHandle *h, off_t offset,
146 enum GNUNET_DISK_Seek whence)
151 return GNUNET_SYSERR;
156 static DWORD t[] = { [GNUNET_SEEK_SET] = FILE_BEGIN,
157 [GNUNET_SEEK_CUR] = FILE_CURRENT, [GNUNET_SEEK_END] = FILE_END };
159 ret = SetFilePointer (h->h, offset, NULL, t[whence]);
160 if (ret == INVALID_SET_FILE_POINTER)
162 SetErrnoFromWinError (GetLastError ());
163 return GNUNET_SYSERR;
167 static int t[] = { [GNUNET_SEEK_SET] = SEEK_SET,
168 [GNUNET_SEEK_CUR] = SEEK_CUR, [GNUNET_SEEK_END] = SEEK_END };
170 return lseek (h->fd, offset, t[whence]);
175 * Get the size of the file (or directory)
176 * of the given file (in bytes).
178 * @return GNUNET_SYSERR on error, GNUNET_OK on success
181 GNUNET_DISK_file_size (const char *filename,
182 unsigned long long *size, int includeSymLinks)
184 GetFileSizeData gfsd;
187 GNUNET_assert (size != NULL);
189 gfsd.include_sym_links = includeSymLinks;
190 ret = getSizeRec (&gfsd, filename);
197 * Get the number of blocks that are left on the partition that
198 * contains the given file (for normal users).
200 * @param part a file on the partition to check
201 * @return -1 on errors, otherwise the number of free blocks
204 GNUNET_DISK_get_blocks_available (const char *part)
209 if (0 != statvfs (part, &buf))
211 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "statfs", part);
220 memcpy (szDrive, part, 3);
222 if (!GetDiskFreeSpace (szDrive, &dwDummy, &dwDummy, &dwBlocks, &dwDummy))
224 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
225 _("`%s' failed for drive `%s': %u\n"),
226 "GetDiskFreeSpace", szDrive, GetLastError ());
233 if (0 != statfs (part, &s))
235 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "statfs", part);
243 * Test if fil is a directory.
245 * @return GNUNET_YES if yes, GNUNET_NO if not, GNUNET_SYSERR if it
249 GNUNET_DISK_directory_test (const char *fil)
251 struct stat filestat;
254 ret = STAT (fil, &filestat);
259 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "stat", fil);
260 return GNUNET_SYSERR;
264 if (!S_ISDIR (filestat.st_mode))
266 if (ACCESS (fil, R_OK | X_OK) < 0)
268 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "access", fil);
269 return GNUNET_SYSERR;
275 * Check that fil corresponds to a filename
276 * (of a file that exists and that is not a directory).
277 * @returns GNUNET_YES if yes, GNUNET_NO if not a file, GNUNET_SYSERR if something
278 * else (will print an error message in that case, too).
281 GNUNET_DISK_file_test (const char *fil)
283 struct stat filestat;
287 rdir = GNUNET_STRINGS_filename_expand (fil);
289 return GNUNET_SYSERR;
291 ret = STAT (rdir, &filestat);
296 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "stat", rdir);
298 return GNUNET_SYSERR;
303 if (!S_ISREG (filestat.st_mode))
308 if (ACCESS (rdir, R_OK) < 0)
310 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "access", rdir);
312 return GNUNET_SYSERR;
319 * Implementation of "mkdir -p"
320 * @param dir the directory to create
321 * @returns GNUNET_OK on success, GNUNET_SYSERR on failure
324 GNUNET_DISK_directory_create (const char *dir)
331 rdir = GNUNET_STRINGS_filename_expand (dir);
333 return GNUNET_SYSERR;
337 pos = 1; /* skip heading '/' */
339 /* Local or Network path? */
340 if (strncmp (rdir, "\\\\", 2) == 0)
345 if (rdir[pos] == '\\')
355 pos = 3; /* strlen("C:\\") */
360 if ((rdir[pos] == DIR_SEPARATOR) || (pos == len))
363 ret = GNUNET_DISK_directory_test (rdir);
364 if (ret == GNUNET_SYSERR)
367 return GNUNET_SYSERR;
369 if (ret == GNUNET_NO)
372 ret = mkdir (rdir, S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH); /* 755 */
376 if ((ret != 0) && (errno != EEXIST))
378 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR, "mkdir",
381 return GNUNET_SYSERR;
384 rdir[pos] = DIR_SEPARATOR;
394 * Create the directory structure for storing
397 * @param filename name of a file in the directory
398 * @returns GNUNET_OK on success,
399 * GNUNET_SYSERR on failure,
400 * GNUNET_NO if the directory
401 * exists but is not writeable for us
404 GNUNET_DISK_directory_create_for_file (const char *dir)
410 rdir = GNUNET_STRINGS_filename_expand (dir);
412 return GNUNET_SYSERR;
414 while ((len > 0) && (rdir[len] != DIR_SEPARATOR))
417 ret = GNUNET_DISK_directory_create (rdir);
418 if ((ret == GNUNET_OK) && (0 != ACCESS (rdir, W_OK)))
425 * Read the contents of a binary file into a buffer.
426 * @param h handle to an open file
427 * @param result the buffer to write the result to
428 * @param len the maximum number of bytes to read
429 * @return the number of bytes read on success, GNUNET_SYSERR on failure
432 GNUNET_DISK_file_read (const struct GNUNET_DISK_FileHandle *h, void *result,
438 return GNUNET_SYSERR;
444 if (!ReadFile (h->h, result, len, &bytesRead, NULL))
446 SetErrnoFromWinError (GetLastError ());
447 return GNUNET_SYSERR;
451 return read (h->fd, result, len);
457 * Read the contents of a binary file into a buffer.
459 * @param fn file name
460 * @param result the buffer to write the result to
461 * @param len the maximum number of bytes to read
462 * @return number of bytes read, GNUNET_SYSERR on failure
465 GNUNET_DISK_fn_read (const char * const fn,
469 struct GNUNET_DISK_FileHandle *fh;
472 fh = GNUNET_DISK_file_open (fn, GNUNET_DISK_OPEN_READ);
474 return GNUNET_SYSERR;
475 ret = GNUNET_DISK_file_read (fh, result, len);
476 GNUNET_assert(GNUNET_OK == GNUNET_DISK_file_close(fh));
483 * Write a buffer to a file.
484 * @param h handle to open file
485 * @param buffer the data to write
486 * @param n number of bytes to write
487 * @return number of bytes written on success, GNUNET_SYSERR on error
490 GNUNET_DISK_file_write (const struct GNUNET_DISK_FileHandle *h, const void *buffer,
496 return GNUNET_SYSERR;
502 if (!WriteFile (h->h, buffer, n, &bytesWritten, NULL))
504 SetErrnoFromWinError (GetLastError ());
505 return GNUNET_SYSERR;
509 return write (h->fd, buffer, n);
514 * Write a buffer to a file. If the file is longer than the
515 * number of bytes that will be written, iit will be truncated.
517 * @param fn file name
518 * @param buffer the data to write
519 * @param n number of bytes to write
520 * @return GNUNET_OK on success, GNUNET_SYSERR on error
523 GNUNET_DISK_fn_write (const char * const fn, const void *buffer,
526 struct GNUNET_DISK_FileHandle *fh;
529 fh = GNUNET_DISK_file_open (fn,
530 GNUNET_DISK_OPEN_WRITE
531 | GNUNET_DISK_OPEN_TRUNCATE
532 | GNUNET_DISK_OPEN_CREATE, mode);
534 return GNUNET_SYSERR;
535 ret = (n == GNUNET_DISK_file_write (fh, buffer, n)) ? GNUNET_OK : GNUNET_SYSERR;
536 GNUNET_assert(GNUNET_OK == GNUNET_DISK_file_close(fh));
542 * Scan a directory for files. The name of the directory
543 * must be expanded first (!).
544 * @param dirName the name of the directory
545 * @param callback the method to call for each file,
546 * can be NULL, in that case, we only count
547 * @param data argument to pass to callback
548 * @return the number of files found, GNUNET_SYSERR on error or
549 * ieration aborted by callback returning GNUNET_SYSERR
552 GNUNET_DISK_directory_scan (const char *dirName,
553 GNUNET_FileNameCallback callback, void *data)
556 struct dirent *finfo;
561 unsigned int name_len;
564 GNUNET_assert (dirName != NULL);
565 dname = GNUNET_STRINGS_filename_expand (dirName);
566 while ((strlen (dname) > 0) && (dname[strlen (dname) - 1] == DIR_SEPARATOR))
567 dname[strlen (dname) - 1] = '\0';
568 if (0 != STAT (dname, &istat))
570 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "stat", dname);
572 return GNUNET_SYSERR;
574 if (!S_ISDIR (istat.st_mode))
576 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
577 _("Expected `%s' to be a directory!\n"), dirName);
579 return GNUNET_SYSERR;
582 dinfo = OPENDIR (dname);
583 if ((errno == EACCES) || (dinfo == NULL))
585 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "opendir", dname);
589 return GNUNET_SYSERR;
592 n_size = strlen (dname) + name_len + 2;
593 name = GNUNET_malloc (n_size);
594 while ((finfo = readdir (dinfo)) != NULL)
596 if ((0 == strcmp (finfo->d_name, ".")) ||
597 (0 == strcmp (finfo->d_name, "..")))
599 if (callback != NULL)
601 if (name_len < strlen (finfo->d_name))
604 name_len = strlen (finfo->d_name);
605 n_size = strlen (dname) + name_len + 2;
606 name = GNUNET_malloc (n_size);
608 /* dname can end in "/" only if dname == "/";
609 if dname does not end in "/", we need to add
610 a "/" (otherwise, we must not!) */
611 GNUNET_snprintf (name,
615 (strcmp (dname, DIR_SEPARATOR_STR) ==
616 0) ? "" : DIR_SEPARATOR_STR, finfo->d_name);
617 if (GNUNET_OK != callback (data, name))
622 return GNUNET_SYSERR;
635 * Opaque handle used for iterating over a directory.
637 struct GNUNET_DISK_DirectoryIterator
642 struct GNUNET_SCHEDULER_Handle *sched;
645 * Function to call on directory entries.
647 GNUNET_DISK_DirectoryIteratorCallback callback;
650 * Closure for callback.
655 * Reference to directory.
665 * Next filename to process.
672 enum GNUNET_SCHEDULER_Priority priority;
678 * Task used by the directory iterator.
681 directory_iterator_task (void *cls,
682 const struct GNUNET_SCHEDULER_TaskContext *tc)
684 struct GNUNET_DISK_DirectoryIterator *iter = cls;
687 name = iter->next_name;
688 GNUNET_assert (name != NULL);
689 iter->next_name = NULL;
690 iter->callback (iter->callback_cls, iter, name, iter->dirname);
696 * This function must be called during the DiskIteratorCallback
697 * (exactly once) to schedule the task to process the next
698 * filename in the directory (if there is one).
700 * @param iter opaque handle for the iterator
701 * @param can set to GNUNET_YES to terminate the iteration early
702 * @return GNUNET_YES if iteration will continue,
703 * GNUNET_NO if this was the last entry (and iteration is complete),
704 * GNUNET_SYSERR if abort was YES
707 GNUNET_DISK_directory_iterator_next (struct GNUNET_DISK_DirectoryIterator
710 struct dirent *finfo;
712 GNUNET_assert (iter->next_name == NULL);
713 if (can == GNUNET_YES)
715 closedir (iter->directory);
716 GNUNET_free (iter->dirname);
718 return GNUNET_SYSERR;
720 while (NULL != (finfo = readdir (iter->directory)))
722 if ((0 == strcmp (finfo->d_name, ".")) ||
723 (0 == strcmp (finfo->d_name, "..")))
725 GNUNET_asprintf (&iter->next_name,
727 iter->dirname, DIR_SEPARATOR_STR, finfo->d_name);
732 GNUNET_DISK_directory_iterator_next (iter, GNUNET_YES);
735 GNUNET_SCHEDULER_add_after (iter->sched,
738 GNUNET_SCHEDULER_NO_TASK,
739 &directory_iterator_task, iter);
745 * Scan a directory for files using the scheduler to run a task for
746 * each entry. The name of the directory must be expanded first (!).
747 * If a scheduler does not need to be used, GNUNET_DISK_directory_scan
748 * may provide a simpler API.
750 * @param sched scheduler to use
751 * @param prio priority to use
752 * @param dirName the name of the directory
753 * @param callback the method to call for each file
754 * @param callback_cls closure for callback
757 GNUNET_DISK_directory_iterator_start (struct GNUNET_SCHEDULER_Handle *sched,
758 enum GNUNET_SCHEDULER_Priority prio,
760 GNUNET_DISK_DirectoryIteratorCallback
761 callback, void *callback_cls)
763 struct GNUNET_DISK_DirectoryIterator *di;
765 di = GNUNET_malloc (sizeof (struct GNUNET_DISK_DirectoryIterator));
767 di->callback = callback;
768 di->callback_cls = callback_cls;
769 di->directory = OPENDIR (dirName);
770 di->dirname = GNUNET_strdup (dirName);
772 GNUNET_DISK_directory_iterator_next (di, GNUNET_NO);
777 remove_helper (void *unused, const char *fn)
779 GNUNET_DISK_directory_remove (fn);
784 * Remove all files in a directory (rm -rf). Call with
788 * @param fileName the file to remove
789 * @return GNUNET_OK on success, GNUNET_SYSERR on error
792 GNUNET_DISK_directory_remove (const char *fileName)
796 if (0 != LSTAT (fileName, &istat))
797 return GNUNET_NO; /* file may not exist... */
798 if (UNLINK (fileName) == 0)
800 if ((errno != EISDIR) &&
801 /* EISDIR is not sufficient in all cases, e.g.
802 sticky /tmp directory may result in EPERM on BSD.
803 So we also explicitly check "isDirectory" */
804 (GNUNET_YES != GNUNET_DISK_directory_test (fileName)))
806 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "rmdir", fileName);
807 return GNUNET_SYSERR;
810 GNUNET_DISK_directory_scan (fileName, remove_helper, NULL))
811 return GNUNET_SYSERR;
812 if (0 != RMDIR (fileName))
814 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "rmdir", fileName);
815 return GNUNET_SYSERR;
820 #define COPY_BLK_SIZE 65536
824 * @return GNUNET_OK on success, GNUNET_SYSERR on error
827 GNUNET_DISK_file_copy (const char *src, const char *dst)
830 unsigned long long pos;
831 unsigned long long size;
832 unsigned long long len;
833 struct GNUNET_DISK_FileHandle *in, *out;
835 if (GNUNET_OK != GNUNET_DISK_file_size (src, &size, GNUNET_YES))
836 return GNUNET_SYSERR;
838 in = GNUNET_DISK_file_open (src, GNUNET_DISK_OPEN_READ);
840 return GNUNET_SYSERR;
841 out = GNUNET_DISK_file_open (dst, GNUNET_DISK_OPEN_WRITE
842 | GNUNET_DISK_OPEN_CREATE | GNUNET_DISK_OPEN_FAILIFEXISTS,
843 GNUNET_DISK_PERM_USER_READ | GNUNET_DISK_PERM_USER_WRITE
844 | GNUNET_DISK_PERM_GROUP_READ | GNUNET_DISK_PERM_GROUP_WRITE);
847 GNUNET_DISK_file_close (in);
848 return GNUNET_SYSERR;
850 buf = GNUNET_malloc (COPY_BLK_SIZE);
854 if (len > size - pos)
856 if (len != GNUNET_DISK_file_read (in, buf, len))
858 if (len != GNUNET_DISK_file_write (out, buf, len))
863 GNUNET_DISK_file_close (in);
864 GNUNET_DISK_file_close (out);
868 GNUNET_DISK_file_close (in);
869 GNUNET_DISK_file_close (out);
870 return GNUNET_SYSERR;
875 * @brief Removes special characters as ':' from a filename.
876 * @param fn the filename to canonicalize
879 GNUNET_DISK_filename_canonicalize (char *fn)
889 if (c == '/' || c == '\\' || c == ':' || c == '*' || c == '?' ||
890 c == '"' || c == '<' || c == '>' || c == '|')
902 * @brief Change owner of a file
905 GNUNET_DISK_file_change_owner (const char *filename, const char *user)
910 pws = getpwnam (user);
913 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
914 _("Cannot obtain information about user `%s': %s\n"),
915 user, STRERROR (errno));
916 return GNUNET_SYSERR;
918 if (0 != chown (filename, pws->pw_uid, pws->pw_gid))
919 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "chown", filename);
926 * Lock a part of a file
927 * @param fh file handle
928 * @lockStart absolute position from where to lock
929 * @lockEnd absolute position until where to lock
930 * @return GNUNET_OK on success, GNUNET_SYSERR on error
933 GNUNET_DISK_file_lock(struct GNUNET_DISK_FileHandle *fh, off_t lockStart,
939 return GNUNET_SYSERR;
945 memset(&fl, 0, sizeof(struct flock));
947 fl.l_whence = SEEK_SET;
948 fl.l_start = lockStart;
951 return fcntl(fh->fd, F_SETLK, &fl) != 0 ? GNUNET_SYSERR : GNUNET_OK;
953 if (!LockFile(fh->h, 0, lockStart, 0, lockEnd))
955 SetErrnoFromWinError(GetLastError());
956 return GNUNET_SYSERR;
966 * @param fn file name to be opened
967 * @param flags opening flags, a combination of GNUNET_DISK_OPEN_xxx bit flags
968 * @param perm permissions for the newly created file
969 * @return IO handle on success, NULL on error
971 struct GNUNET_DISK_FileHandle *
972 GNUNET_DISK_file_open (const char *fn, int flags, ...)
975 struct GNUNET_DISK_FileHandle *ret;
986 expfn = GNUNET_STRINGS_filename_expand (fn);
991 if (GNUNET_DISK_OPEN_READWRITE == (flags & GNUNET_DISK_OPEN_READWRITE))
992 oflags = O_RDWR; /* note: O_RDWR is NOT always O_RDONLY | O_WRONLY */
993 else if (flags & GNUNET_DISK_OPEN_READ)
995 else if (flags & GNUNET_DISK_OPEN_WRITE)
1002 if (flags & GNUNET_DISK_OPEN_FAILIFEXISTS)
1003 oflags |= (O_CREAT & O_EXCL);
1004 if (flags & GNUNET_DISK_OPEN_TRUNCATE)
1006 if (flags & GNUNET_DISK_OPEN_APPEND)
1008 if (flags & GNUNET_DISK_OPEN_CREATE)
1015 va_start (arg, flags);
1016 perm = va_arg (arg, int);
1019 if (perm & GNUNET_DISK_PERM_USER_READ)
1021 if (perm & GNUNET_DISK_PERM_USER_WRITE)
1023 if (perm & GNUNET_DISK_PERM_USER_EXEC)
1025 if (perm & GNUNET_DISK_PERM_GROUP_READ)
1027 if (perm & GNUNET_DISK_PERM_GROUP_WRITE)
1029 if (perm & GNUNET_DISK_PERM_GROUP_EXEC)
1031 if (perm & GNUNET_DISK_PERM_OTHER_READ)
1033 if (perm & GNUNET_DISK_PERM_OTHER_WRITE)
1035 if (perm & GNUNET_DISK_PERM_OTHER_EXEC)
1039 fd = open (expfn, oflags | O_LARGEFILE, mode);
1042 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "open", expfn);
1043 GNUNET_free (expfn);
1050 if (flags & GNUNET_DISK_OPEN_READ)
1051 access = FILE_READ_DATA;
1052 if (flags & GNUNET_DISK_OPEN_WRITE)
1053 access = FILE_WRITE_DATA;
1055 if (flags & GNUNET_DISK_OPEN_FAILIFEXISTS)
1057 if (flags & GNUNET_DISK_OPEN_TRUNCATE)
1058 disp = TRUNCATE_EXISTING;
1059 if (flags & GNUNET_DISK_OPEN_CREATE)
1060 disp |= OPEN_ALWAYS;
1062 /* TODO: access priviledges? */
1063 h = CreateFile (expfn, access, FILE_SHARE_DELETE | FILE_SHARE_READ
1064 | FILE_SHARE_WRITE, NULL, disp, FILE_ATTRIBUTE_NORMAL, NULL);
1065 if (h == INVALID_HANDLE_VALUE)
1067 SetErrnoFromWinError (GetLastError ());
1068 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "open", expfn);
1069 GNUNET_free (expfn);
1073 if (flags & GNUNET_DISK_OPEN_APPEND)
1074 if (SetFilePointer (h, 0, 0, FILE_END) == INVALID_SET_FILE_POINTER)
1076 SetErrnoFromWinError (GetLastError ());
1077 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "SetFilePointer", expfn);
1079 GNUNET_free (expfn);
1084 ret = GNUNET_malloc(sizeof(struct GNUNET_DISK_FileHandle));
1090 GNUNET_free (expfn);
1095 * Close an open file
1096 * @param h file handle
1097 * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
1100 GNUNET_DISK_file_close (struct GNUNET_DISK_FileHandle *h)
1105 return GNUNET_SYSERR;
1109 if (!CloseHandle (h->h))
1111 SetErrnoFromWinError (GetLastError ());
1112 GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "close");
1114 return GNUNET_SYSERR;
1117 if (close (h->fd) != 0)
1119 GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "close");
1121 return GNUNET_SYSERR;
1129 * Construct full path to a file inside of the private
1130 * directory used by GNUnet. Also creates the corresponding
1131 * directory. If the resulting name is supposed to be
1132 * a directory, end the last argument in '/' (or pass
1133 * DIR_SEPARATOR_STR as the last argument before NULL).
1135 * @param cfg configuration to use (determines HOME)
1136 * @param serviceName name of the service
1137 * @param varargs is NULL-terminated list of
1138 * path components to append to the
1139 * private directory name.
1140 * @return the constructed filename
1143 GNUNET_DISK_get_home_filename (const struct GNUNET_CONFIGURATION_Handle *cfg,
1144 const char *serviceName, ...)
1150 unsigned int needed;
1153 GNUNET_CONFIGURATION_get_value_filename (cfg,
1154 serviceName, "HOME", &pfx))
1158 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1159 _("No `%s' specified for service `%s' in configuration.\n"),
1160 "HOME", serviceName);
1163 needed = strlen (pfx) + 2;
1164 if ((pfx[strlen (pfx) - 1] != '/') && (pfx[strlen (pfx) - 1] != '\\'))
1166 va_start (ap, serviceName);
1169 c = va_arg (ap, const char *);
1172 needed += strlen (c);
1173 if ((c[strlen (c) - 1] != '/') && (c[strlen (c) - 1] != '\\'))
1177 ret = GNUNET_malloc (needed);
1180 va_start (ap, serviceName);
1183 c = va_arg (ap, const char *);
1186 if ((c[strlen (c) - 1] != '/') && (c[strlen (c) - 1] != '\\'))
1187 strcat (ret, DIR_SEPARATOR_STR);
1191 if ((ret[strlen (ret) - 1] != '/') && (ret[strlen (ret) - 1] != '\\'))
1192 GNUNET_DISK_directory_create_for_file (ret);
1194 GNUNET_DISK_directory_create (ret);
1198 struct GNUNET_DISK_MapHandle
1210 * Map a file into memory
1211 * @param h open file handle
1212 * @param m handle to the new mapping
1213 * @param access access specification, GNUNET_DISK_MAP_xxx
1214 * @param len size of the mapping
1215 * @return pointer to the mapped memory region, NULL on failure
1218 GNUNET_DISK_file_map (const struct GNUNET_DISK_FileHandle *h, struct GNUNET_DISK_MapHandle **m,
1219 int access, size_t len)
1228 DWORD mapAccess, protect;
1231 if (access & GNUNET_DISK_MAP_READ && access & GNUNET_DISK_MAP_WRITE)
1233 protect = PAGE_READWRITE;
1234 mapAccess = FILE_MAP_ALL_ACCESS;
1236 else if (access & GNUNET_DISK_MAP_READ)
1238 protect = PAGE_READONLY;
1239 mapAccess = FILE_MAP_READ;
1241 else if (access & GNUNET_DISK_MAP_WRITE)
1243 protect = PAGE_READWRITE;
1244 mapAccess = FILE_MAP_WRITE;
1252 *m = GNUNET_malloc (sizeof (struct GNUNET_DISK_MapHandle));
1253 (*m)->h = CreateFileMapping (h->h, NULL, protect, 0, 0, NULL);
1254 if ((*m)->h == INVALID_HANDLE_VALUE)
1256 SetErrnoFromWinError (GetLastError ());
1261 ret = MapViewOfFile ((*m)->h, mapAccess, 0, 0, len);
1264 SetErrnoFromWinError (GetLastError ());
1265 CloseHandle ((*m)->h);
1274 if (access & GNUNET_DISK_MAP_READ)
1276 if (access & GNUNET_DISK_MAP_WRITE)
1278 *m = GNUNET_malloc (sizeof (struct GNUNET_DISK_MapHandle));
1279 (*m)->addr = mmap (NULL, len, prot, MAP_SHARED, h->fd, 0);
1287 * @param h mapping handle
1288 * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
1291 GNUNET_DISK_file_unmap (struct GNUNET_DISK_MapHandle *h)
1297 return GNUNET_SYSERR;
1301 ret = UnmapViewOfFile (h->h) ? GNUNET_OK : GNUNET_SYSERR;
1302 if (ret != GNUNET_OK)
1303 SetErrnoFromWinError (GetLastError ());
1304 if (!CloseHandle (h->h) && (ret == GNUNET_OK))
1306 ret = GNUNET_SYSERR;
1307 SetErrnoFromWinError (GetLastError ());
1310 ret = munmap (h->addr, h->len) != -1 ? GNUNET_OK : GNUNET_SYSERR;
1318 * Write file changes to disk
1319 * @param h handle to an open file
1320 * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
1323 GNUNET_DISK_file_sync (const struct GNUNET_DISK_FileHandle *h)
1328 return GNUNET_SYSERR;
1334 ret = FlushFileBuffers (h->h) ? GNUNET_OK : GNUNET_SYSERR;
1335 if (ret != GNUNET_OK)
1336 SetErrnoFromWinError (GetLastError ());
1339 return fsync (h->fd) == -1 ? GNUNET_SYSERR : GNUNET_OK;