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?)
74 unsigned long long total;
75 int include_sym_links;
78 struct GNUNET_DISK_PipeHandle
80 struct GNUNET_DISK_FileHandle fd[2];
84 getSizeRec (void *ptr, const char *fn)
86 GetFileSizeData *gfsd = ptr;
94 if (0 != STAT64 (fn, &buf))
96 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "stat64", fn);
100 if (0 != STAT (fn, &buf))
102 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "stat", fn);
103 return GNUNET_SYSERR;
106 if ((!S_ISLNK (buf.st_mode)) || (gfsd->include_sym_links == GNUNET_YES))
107 gfsd->total += buf.st_size;
108 if ((S_ISDIR (buf.st_mode)) &&
109 (0 == ACCESS (fn, X_OK)) &&
110 ((!S_ISLNK (buf.st_mode)) || (gfsd->include_sym_links == GNUNET_YES)))
112 if (GNUNET_SYSERR == GNUNET_DISK_directory_scan (fn, &getSizeRec, gfsd))
113 return GNUNET_SYSERR;
119 * Checks whether a handle is invalid
120 * @param h handle to check
121 * @return GNUNET_YES if invalid, GNUNET_NO if valid
124 GNUNET_DISK_handle_invalid (const struct GNUNET_DISK_FileHandle *h)
127 return !h || h->h == INVALID_HANDLE_VALUE ? GNUNET_YES : GNUNET_NO;
129 return !h || h->fd == -1 ? GNUNET_YES : GNUNET_NO;
135 * Move the read/write pointer in a file
136 * @param h handle of an open file
137 * @param offset position to move to
138 * @param whence specification to which position the offset parameter relates to
139 * @return the new position on success, GNUNET_SYSERR otherwise
142 GNUNET_DISK_file_seek (const struct GNUNET_DISK_FileHandle *h, off_t offset,
143 enum GNUNET_DISK_Seek whence)
148 return GNUNET_SYSERR;
153 static DWORD t[] = { [GNUNET_DISK_SEEK_SET] = FILE_BEGIN,
154 [GNUNET_DISK_SEEK_CUR] = FILE_CURRENT, [GNUNET_DISK_SEEK_END] = FILE_END };
156 ret = SetFilePointer (h->h, offset, NULL, t[whence]);
157 if (ret == INVALID_SET_FILE_POINTER)
159 SetErrnoFromWinError (GetLastError ());
160 return GNUNET_SYSERR;
164 static int t[] = { [GNUNET_DISK_SEEK_SET] = SEEK_SET,
165 [GNUNET_DISK_SEEK_CUR] = SEEK_CUR, [GNUNET_DISK_SEEK_END] = SEEK_END };
167 return lseek (h->fd, offset, t[whence]);
172 * Get the size of the file (or directory)
173 * of the given file (in bytes).
175 * @return GNUNET_SYSERR on error, GNUNET_OK on success
178 GNUNET_DISK_file_size (const char *filename,
179 unsigned long long *size, int includeSymLinks)
181 GetFileSizeData gfsd;
184 GNUNET_assert (size != NULL);
186 gfsd.include_sym_links = includeSymLinks;
187 ret = getSizeRec (&gfsd, filename);
194 * Create an (empty) temporary file on disk.
196 * @param template component to use for the name;
197 * does NOT contain "XXXXXX" or "/tmp/".
198 * @return NULL on error, otherwise name of fresh
199 * file on disk in directory for temporary files
202 GNUNET_DISK_mktemp (const char *template)
209 tmpdir = getenv ("TMPDIR");
210 tmpdir = tmpdir ? tmpdir : "/tmp";
212 GNUNET_asprintf (&tmpl,
219 fn = (char *) GNUNET_malloc (MAX_PATH + 1);
220 plibc_conv_to_win_path (tmpl, fn);
228 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR,
235 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING,
243 * Get the number of blocks that are left on the partition that
244 * contains the given file (for normal users).
246 * @param part a file on the partition to check
247 * @return -1 on errors, otherwise the number of free blocks
250 GNUNET_DISK_get_blocks_available (const char *part)
255 if (0 != statvfs (part, &buf))
257 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "statfs", part);
267 path = GNUNET_STRINGS_filename_expand (part);
268 memcpy (szDrive, path, 3);
271 if (!GetDiskFreeSpace (szDrive, &dwDummy, &dwDummy, &dwBlocks, &dwDummy))
273 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
274 _("`%s' failed for drive `%s': %u\n"),
275 "GetDiskFreeSpace", szDrive, GetLastError ());
282 if (0 != statfs (part, &s))
284 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "statfs", part);
292 * Test if fil is a directory.
294 * @return GNUNET_YES if yes, GNUNET_NO if not, GNUNET_SYSERR if it
298 GNUNET_DISK_directory_test (const char *fil)
300 struct stat filestat;
303 ret = STAT (fil, &filestat);
308 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "stat", fil);
309 return GNUNET_SYSERR;
313 if (!S_ISDIR (filestat.st_mode))
315 if (ACCESS (fil, R_OK | X_OK) < 0)
317 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "access", fil);
318 return GNUNET_SYSERR;
324 * Check that fil corresponds to a filename
325 * (of a file that exists and that is not a directory).
326 * @returns GNUNET_YES if yes, GNUNET_NO if not a file, GNUNET_SYSERR if something
327 * else (will print an error message in that case, too).
330 GNUNET_DISK_file_test (const char *fil)
332 struct stat filestat;
336 rdir = GNUNET_STRINGS_filename_expand (fil);
338 return GNUNET_SYSERR;
340 ret = STAT (rdir, &filestat);
345 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "stat", rdir);
347 return GNUNET_SYSERR;
352 if (!S_ISREG (filestat.st_mode))
357 if (ACCESS (rdir, R_OK) < 0)
359 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "access", rdir);
361 return GNUNET_SYSERR;
368 * Implementation of "mkdir -p"
369 * @param dir the directory to create
370 * @returns GNUNET_OK on success, GNUNET_SYSERR on failure
373 GNUNET_DISK_directory_create (const char *dir)
380 rdir = GNUNET_STRINGS_filename_expand (dir);
382 return GNUNET_SYSERR;
386 pos = 1; /* skip heading '/' */
388 /* Local or Network path? */
389 if (strncmp (rdir, "\\\\", 2) == 0)
394 if (rdir[pos] == '\\')
404 pos = 3; /* strlen("C:\\") */
409 if ((rdir[pos] == DIR_SEPARATOR) || (pos == len))
412 ret = GNUNET_DISK_directory_test (rdir);
413 if (ret == GNUNET_SYSERR)
416 return GNUNET_SYSERR;
418 if (ret == GNUNET_NO)
421 ret = mkdir (rdir, S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH); /* 755 */
425 if ((ret != 0) && (errno != EEXIST))
427 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR, "mkdir",
430 return GNUNET_SYSERR;
433 rdir[pos] = DIR_SEPARATOR;
443 * Create the directory structure for storing
446 * @param filename name of a file in the directory
447 * @returns GNUNET_OK on success,
448 * GNUNET_SYSERR on failure,
449 * GNUNET_NO if the directory
450 * exists but is not writeable for us
453 GNUNET_DISK_directory_create_for_file (const char *dir)
459 rdir = GNUNET_STRINGS_filename_expand (dir);
461 return GNUNET_SYSERR;
463 while ((len > 0) && (rdir[len] != DIR_SEPARATOR))
466 ret = GNUNET_DISK_directory_create (rdir);
467 if ((ret == GNUNET_OK) && (0 != ACCESS (rdir, W_OK)))
474 * Read the contents of a binary file into a buffer.
475 * @param h handle to an open file
476 * @param result the buffer to write the result to
477 * @param len the maximum number of bytes to read
478 * @return the number of bytes read on success, GNUNET_SYSERR on failure
481 GNUNET_DISK_file_read (const struct GNUNET_DISK_FileHandle *h, void *result,
487 return GNUNET_SYSERR;
493 if (!ReadFile (h->h, result, len, &bytesRead, NULL))
495 SetErrnoFromWinError (GetLastError ());
496 return GNUNET_SYSERR;
500 return read (h->fd, result, len);
506 * Read the contents of a binary file into a buffer.
508 * @param fn file name
509 * @param result the buffer to write the result to
510 * @param len the maximum number of bytes to read
511 * @return number of bytes read, GNUNET_SYSERR on failure
514 GNUNET_DISK_fn_read (const char * const fn,
518 struct GNUNET_DISK_FileHandle *fh;
521 fh = GNUNET_DISK_file_open (fn, GNUNET_DISK_OPEN_READ);
523 return GNUNET_SYSERR;
524 ret = GNUNET_DISK_file_read (fh, result, len);
525 GNUNET_assert(GNUNET_OK == GNUNET_DISK_file_close(fh));
532 * Write a buffer to a file.
533 * @param h handle to open file
534 * @param buffer the data to write
535 * @param n number of bytes to write
536 * @return number of bytes written on success, GNUNET_SYSERR on error
539 GNUNET_DISK_file_write (const struct GNUNET_DISK_FileHandle *h, const void *buffer,
545 return GNUNET_SYSERR;
551 if (!WriteFile (h->h, buffer, n, &bytesWritten, NULL))
553 SetErrnoFromWinError (GetLastError ());
554 return GNUNET_SYSERR;
558 return write (h->fd, buffer, n);
563 * Write a buffer to a file. If the file is longer than the
564 * number of bytes that will be written, iit will be truncated.
566 * @param fn file name
567 * @param buffer the data to write
568 * @param n number of bytes to write
569 * @return GNUNET_OK on success, GNUNET_SYSERR on error
572 GNUNET_DISK_fn_write (const char * const fn, const void *buffer,
575 struct GNUNET_DISK_FileHandle *fh;
578 fh = GNUNET_DISK_file_open (fn,
579 GNUNET_DISK_OPEN_WRITE
580 | GNUNET_DISK_OPEN_TRUNCATE
581 | GNUNET_DISK_OPEN_CREATE, mode);
583 return GNUNET_SYSERR;
584 ret = (n == GNUNET_DISK_file_write (fh, buffer, n)) ? GNUNET_OK : GNUNET_SYSERR;
585 GNUNET_assert(GNUNET_OK == GNUNET_DISK_file_close(fh));
591 * Scan a directory for files. The name of the directory
592 * must be expanded first (!).
593 * @param dirName the name of the directory
594 * @param callback the method to call for each file,
595 * can be NULL, in that case, we only count
596 * @param data argument to pass to callback
597 * @return the number of files found, GNUNET_SYSERR on error or
598 * ieration aborted by callback returning GNUNET_SYSERR
601 GNUNET_DISK_directory_scan (const char *dirName,
602 GNUNET_FileNameCallback callback, void *data)
605 struct dirent *finfo;
610 unsigned int name_len;
613 GNUNET_assert (dirName != NULL);
614 dname = GNUNET_STRINGS_filename_expand (dirName);
615 while ((strlen (dname) > 0) && (dname[strlen (dname) - 1] == DIR_SEPARATOR))
616 dname[strlen (dname) - 1] = '\0';
617 if (0 != STAT (dname, &istat))
619 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "stat", dname);
621 return GNUNET_SYSERR;
623 if (!S_ISDIR (istat.st_mode))
625 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
626 _("Expected `%s' to be a directory!\n"), dirName);
628 return GNUNET_SYSERR;
631 dinfo = OPENDIR (dname);
632 if ((errno == EACCES) || (dinfo == NULL))
634 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "opendir", dname);
638 return GNUNET_SYSERR;
641 n_size = strlen (dname) + name_len + 2;
642 name = GNUNET_malloc (n_size);
643 while ((finfo = readdir (dinfo)) != NULL)
645 if ((0 == strcmp (finfo->d_name, ".")) ||
646 (0 == strcmp (finfo->d_name, "..")))
648 if (callback != NULL)
650 if (name_len < strlen (finfo->d_name))
653 name_len = strlen (finfo->d_name);
654 n_size = strlen (dname) + name_len + 2;
655 name = GNUNET_malloc (n_size);
657 /* dname can end in "/" only if dname == "/";
658 if dname does not end in "/", we need to add
659 a "/" (otherwise, we must not!) */
660 GNUNET_snprintf (name,
664 (strcmp (dname, DIR_SEPARATOR_STR) ==
665 0) ? "" : DIR_SEPARATOR_STR, finfo->d_name);
666 if (GNUNET_OK != callback (data, name))
671 return GNUNET_SYSERR;
684 * Opaque handle used for iterating over a directory.
686 struct GNUNET_DISK_DirectoryIterator
691 struct GNUNET_SCHEDULER_Handle *sched;
694 * Function to call on directory entries.
696 GNUNET_DISK_DirectoryIteratorCallback callback;
699 * Closure for callback.
704 * Reference to directory.
714 * Next filename to process.
721 enum GNUNET_SCHEDULER_Priority priority;
727 * Task used by the directory iterator.
730 directory_iterator_task (void *cls,
731 const struct GNUNET_SCHEDULER_TaskContext *tc)
733 struct GNUNET_DISK_DirectoryIterator *iter = cls;
736 name = iter->next_name;
737 GNUNET_assert (name != NULL);
738 iter->next_name = NULL;
739 iter->callback (iter->callback_cls, iter, name, iter->dirname);
745 * This function must be called during the DiskIteratorCallback
746 * (exactly once) to schedule the task to process the next
747 * filename in the directory (if there is one).
749 * @param iter opaque handle for the iterator
750 * @param can set to GNUNET_YES to terminate the iteration early
751 * @return GNUNET_YES if iteration will continue,
752 * GNUNET_NO if this was the last entry (and iteration is complete),
753 * GNUNET_SYSERR if abort was YES
756 GNUNET_DISK_directory_iterator_next (struct GNUNET_DISK_DirectoryIterator
759 struct dirent *finfo;
761 GNUNET_assert (iter->next_name == NULL);
762 if (can == GNUNET_YES)
764 closedir (iter->directory);
765 GNUNET_free (iter->dirname);
767 return GNUNET_SYSERR;
769 while (NULL != (finfo = readdir (iter->directory)))
771 if ((0 == strcmp (finfo->d_name, ".")) ||
772 (0 == strcmp (finfo->d_name, "..")))
774 GNUNET_asprintf (&iter->next_name,
776 iter->dirname, DIR_SEPARATOR_STR, finfo->d_name);
781 GNUNET_DISK_directory_iterator_next (iter, GNUNET_YES);
784 GNUNET_SCHEDULER_add_after (iter->sched,
787 GNUNET_SCHEDULER_NO_TASK,
788 &directory_iterator_task, iter);
794 * Scan a directory for files using the scheduler to run a task for
795 * each entry. The name of the directory must be expanded first (!).
796 * If a scheduler does not need to be used, GNUNET_DISK_directory_scan
797 * may provide a simpler API.
799 * @param sched scheduler to use
800 * @param prio priority to use
801 * @param dirName the name of the directory
802 * @param callback the method to call for each file
803 * @param callback_cls closure for callback
806 GNUNET_DISK_directory_iterator_start (struct GNUNET_SCHEDULER_Handle *sched,
807 enum GNUNET_SCHEDULER_Priority prio,
809 GNUNET_DISK_DirectoryIteratorCallback
810 callback, void *callback_cls)
812 struct GNUNET_DISK_DirectoryIterator *di;
814 di = GNUNET_malloc (sizeof (struct GNUNET_DISK_DirectoryIterator));
816 di->callback = callback;
817 di->callback_cls = callback_cls;
818 di->directory = OPENDIR (dirName);
819 di->dirname = GNUNET_strdup (dirName);
821 GNUNET_DISK_directory_iterator_next (di, GNUNET_NO);
826 remove_helper (void *unused, const char *fn)
828 GNUNET_DISK_directory_remove (fn);
833 * Remove all files in a directory (rm -rf). Call with
837 * @param fileName the file to remove
838 * @return GNUNET_OK on success, GNUNET_SYSERR on error
841 GNUNET_DISK_directory_remove (const char *fileName)
845 if (0 != LSTAT (fileName, &istat))
846 return GNUNET_NO; /* file may not exist... */
847 if (UNLINK (fileName) == 0)
849 if ((errno != EISDIR) &&
850 /* EISDIR is not sufficient in all cases, e.g.
851 sticky /tmp directory may result in EPERM on BSD.
852 So we also explicitly check "isDirectory" */
853 (GNUNET_YES != GNUNET_DISK_directory_test (fileName)))
855 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "rmdir", fileName);
856 return GNUNET_SYSERR;
859 GNUNET_DISK_directory_scan (fileName, remove_helper, NULL))
860 return GNUNET_SYSERR;
861 if (0 != RMDIR (fileName))
863 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "rmdir", fileName);
864 return GNUNET_SYSERR;
869 #define COPY_BLK_SIZE 65536
873 * @return GNUNET_OK on success, GNUNET_SYSERR on error
876 GNUNET_DISK_file_copy (const char *src, const char *dst)
879 unsigned long long pos;
880 unsigned long long size;
881 unsigned long long len;
882 struct GNUNET_DISK_FileHandle *in, *out;
884 if (GNUNET_OK != GNUNET_DISK_file_size (src, &size, GNUNET_YES))
885 return GNUNET_SYSERR;
887 in = GNUNET_DISK_file_open (src, GNUNET_DISK_OPEN_READ);
889 return GNUNET_SYSERR;
890 out = GNUNET_DISK_file_open (dst, GNUNET_DISK_OPEN_WRITE
891 | GNUNET_DISK_OPEN_CREATE | GNUNET_DISK_OPEN_FAILIFEXISTS,
892 GNUNET_DISK_PERM_USER_READ | GNUNET_DISK_PERM_USER_WRITE
893 | GNUNET_DISK_PERM_GROUP_READ | GNUNET_DISK_PERM_GROUP_WRITE);
896 GNUNET_DISK_file_close (in);
897 return GNUNET_SYSERR;
899 buf = GNUNET_malloc (COPY_BLK_SIZE);
903 if (len > size - pos)
905 if (len != GNUNET_DISK_file_read (in, buf, len))
907 if (len != GNUNET_DISK_file_write (out, buf, len))
912 GNUNET_DISK_file_close (in);
913 GNUNET_DISK_file_close (out);
917 GNUNET_DISK_file_close (in);
918 GNUNET_DISK_file_close (out);
919 return GNUNET_SYSERR;
924 * @brief Removes special characters as ':' from a filename.
925 * @param fn the filename to canonicalize
928 GNUNET_DISK_filename_canonicalize (char *fn)
938 if (c == '/' || c == '\\' || c == ':' || c == '*' || c == '?' ||
939 c == '"' || c == '<' || c == '>' || c == '|')
951 * @brief Change owner of a file
954 GNUNET_DISK_file_change_owner (const char *filename, const char *user)
959 pws = getpwnam (user);
962 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
963 _("Cannot obtain information about user `%s': %s\n"),
964 user, STRERROR (errno));
965 return GNUNET_SYSERR;
967 if (0 != chown (filename, pws->pw_uid, pws->pw_gid))
968 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "chown", filename);
975 * Lock a part of a file
976 * @param fh file handle
977 * @lockStart absolute position from where to lock
978 * @lockEnd absolute position until where to lock
979 * @return GNUNET_OK on success, GNUNET_SYSERR on error
982 GNUNET_DISK_file_lock(struct GNUNET_DISK_FileHandle *fh, off_t lockStart,
988 return GNUNET_SYSERR;
994 memset(&fl, 0, sizeof(struct flock));
996 fl.l_whence = SEEK_SET;
997 fl.l_start = lockStart;
1000 return fcntl(fh->fd, F_SETLK, &fl) != 0 ? GNUNET_SYSERR : GNUNET_OK;
1002 if (!LockFile(fh->h, 0, lockStart, 0, lockEnd))
1004 SetErrnoFromWinError(GetLastError());
1005 return GNUNET_SYSERR;
1015 * @param fn file name to be opened
1016 * @param flags opening flags, a combination of GNUNET_DISK_OPEN_xxx bit flags
1017 * @param perm permissions for the newly created file
1018 * @return IO handle on success, NULL on error
1020 struct GNUNET_DISK_FileHandle *
1021 GNUNET_DISK_file_open (const char *fn, int flags, ...)
1024 struct GNUNET_DISK_FileHandle *ret;
1035 expfn = GNUNET_STRINGS_filename_expand (fn);
1040 if (GNUNET_DISK_OPEN_READWRITE == (flags & GNUNET_DISK_OPEN_READWRITE))
1041 oflags = O_RDWR; /* note: O_RDWR is NOT always O_RDONLY | O_WRONLY */
1042 else if (flags & GNUNET_DISK_OPEN_READ)
1044 else if (flags & GNUNET_DISK_OPEN_WRITE)
1049 GNUNET_free (expfn);
1052 if (flags & GNUNET_DISK_OPEN_FAILIFEXISTS)
1053 oflags |= (O_CREAT & O_EXCL);
1054 if (flags & GNUNET_DISK_OPEN_TRUNCATE)
1056 if (flags & GNUNET_DISK_OPEN_APPEND)
1058 if (flags & GNUNET_DISK_OPEN_CREATE)
1065 va_start (arg, flags);
1066 perm = va_arg (arg, int);
1069 if (perm & GNUNET_DISK_PERM_USER_READ)
1071 if (perm & GNUNET_DISK_PERM_USER_WRITE)
1073 if (perm & GNUNET_DISK_PERM_USER_EXEC)
1075 if (perm & GNUNET_DISK_PERM_GROUP_READ)
1077 if (perm & GNUNET_DISK_PERM_GROUP_WRITE)
1079 if (perm & GNUNET_DISK_PERM_GROUP_EXEC)
1081 if (perm & GNUNET_DISK_PERM_OTHER_READ)
1083 if (perm & GNUNET_DISK_PERM_OTHER_WRITE)
1085 if (perm & GNUNET_DISK_PERM_OTHER_EXEC)
1089 fd = open (expfn, oflags | O_LARGEFILE, mode);
1092 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "open", expfn);
1093 GNUNET_free (expfn);
1100 if (GNUNET_DISK_OPEN_READWRITE == (flags & GNUNET_DISK_OPEN_READWRITE))
1101 access = FILE_READ_DATA | FILE_WRITE_DATA;
1102 else if (flags & GNUNET_DISK_OPEN_READ)
1103 access = FILE_READ_DATA;
1104 else if (flags & GNUNET_DISK_OPEN_WRITE)
1105 access = FILE_WRITE_DATA;
1107 if (flags & GNUNET_DISK_OPEN_FAILIFEXISTS)
1111 else if (flags & GNUNET_DISK_OPEN_CREATE)
1113 if (flags & GNUNET_DISK_OPEN_TRUNCATE)
1114 disp = CREATE_ALWAYS;
1118 else if (flags & GNUNET_DISK_OPEN_TRUNCATE)
1120 disp = TRUNCATE_EXISTING;
1127 /* TODO: access priviledges? */
1128 h = CreateFile (expfn, access, FILE_SHARE_DELETE | FILE_SHARE_READ
1129 | FILE_SHARE_WRITE, NULL, disp, FILE_ATTRIBUTE_NORMAL, NULL);
1130 if (h == INVALID_HANDLE_VALUE)
1132 SetErrnoFromWinError (GetLastError ());
1133 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "open", expfn);
1134 GNUNET_free (expfn);
1138 if (flags & GNUNET_DISK_OPEN_APPEND)
1139 if (SetFilePointer (h, 0, 0, FILE_END) == INVALID_SET_FILE_POINTER)
1141 SetErrnoFromWinError (GetLastError ());
1142 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "SetFilePointer", expfn);
1144 GNUNET_free (expfn);
1149 ret = GNUNET_malloc(sizeof(struct GNUNET_DISK_FileHandle));
1155 GNUNET_free (expfn);
1160 * Close an open file
1161 * @param h file handle
1162 * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
1165 GNUNET_DISK_file_close (struct GNUNET_DISK_FileHandle *h)
1170 return GNUNET_SYSERR;
1174 if (!CloseHandle (h->h))
1176 SetErrnoFromWinError (GetLastError ());
1177 GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "close");
1179 return GNUNET_SYSERR;
1182 if (close (h->fd) != 0)
1184 GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "close");
1186 return GNUNET_SYSERR;
1194 * Construct full path to a file inside of the private
1195 * directory used by GNUnet. Also creates the corresponding
1196 * directory. If the resulting name is supposed to be
1197 * a directory, end the last argument in '/' (or pass
1198 * DIR_SEPARATOR_STR as the last argument before NULL).
1200 * @param cfg configuration to use (determines HOME)
1201 * @param serviceName name of the service
1202 * @param varargs is NULL-terminated list of
1203 * path components to append to the
1204 * private directory name.
1205 * @return the constructed filename
1208 GNUNET_DISK_get_home_filename (const struct GNUNET_CONFIGURATION_Handle *cfg,
1209 const char *serviceName, ...)
1215 unsigned int needed;
1218 GNUNET_CONFIGURATION_get_value_filename (cfg,
1219 serviceName, "HOME", &pfx))
1223 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1224 _("No `%s' specified for service `%s' in configuration.\n"),
1225 "HOME", serviceName);
1228 needed = strlen (pfx) + 2;
1229 if ((pfx[strlen (pfx) - 1] != '/') && (pfx[strlen (pfx) - 1] != '\\'))
1231 va_start (ap, serviceName);
1234 c = va_arg (ap, const char *);
1237 needed += strlen (c);
1238 if ((c[strlen (c) - 1] != '/') && (c[strlen (c) - 1] != '\\'))
1242 ret = GNUNET_malloc (needed);
1245 va_start (ap, serviceName);
1248 c = va_arg (ap, const char *);
1251 if ((c[strlen (c) - 1] != '/') && (c[strlen (c) - 1] != '\\'))
1252 strcat (ret, DIR_SEPARATOR_STR);
1256 if ((ret[strlen (ret) - 1] != '/') && (ret[strlen (ret) - 1] != '\\'))
1257 GNUNET_DISK_directory_create_for_file (ret);
1259 GNUNET_DISK_directory_create (ret);
1263 struct GNUNET_DISK_MapHandle
1275 * Map a file into memory
1276 * @param h open file handle
1277 * @param m handle to the new mapping
1278 * @param access access specification, GNUNET_DISK_MAP_xxx
1279 * @param len size of the mapping
1280 * @return pointer to the mapped memory region, NULL on failure
1283 GNUNET_DISK_file_map (const struct GNUNET_DISK_FileHandle *h, struct GNUNET_DISK_MapHandle **m,
1284 int access, size_t len)
1293 DWORD mapAccess, protect;
1296 if (access & GNUNET_DISK_MAP_READ && access & GNUNET_DISK_MAP_WRITE)
1298 protect = PAGE_READWRITE;
1299 mapAccess = FILE_MAP_ALL_ACCESS;
1301 else if (access & GNUNET_DISK_MAP_READ)
1303 protect = PAGE_READONLY;
1304 mapAccess = FILE_MAP_READ;
1306 else if (access & GNUNET_DISK_MAP_WRITE)
1308 protect = PAGE_READWRITE;
1309 mapAccess = FILE_MAP_WRITE;
1317 *m = GNUNET_malloc (sizeof (struct GNUNET_DISK_MapHandle));
1318 (*m)->h = CreateFileMapping (h->h, NULL, protect, 0, 0, NULL);
1319 if ((*m)->h == INVALID_HANDLE_VALUE)
1321 SetErrnoFromWinError (GetLastError ());
1326 ret = MapViewOfFile ((*m)->h, mapAccess, 0, 0, len);
1329 SetErrnoFromWinError (GetLastError ());
1330 CloseHandle ((*m)->h);
1339 if (access & GNUNET_DISK_MAP_READ)
1341 if (access & GNUNET_DISK_MAP_WRITE)
1343 *m = GNUNET_malloc (sizeof (struct GNUNET_DISK_MapHandle));
1344 (*m)->addr = mmap (NULL, len, prot, MAP_SHARED, h->fd, 0);
1352 * @param h mapping handle
1353 * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
1356 GNUNET_DISK_file_unmap (struct GNUNET_DISK_MapHandle *h)
1362 return GNUNET_SYSERR;
1366 ret = UnmapViewOfFile (h->h) ? GNUNET_OK : GNUNET_SYSERR;
1367 if (ret != GNUNET_OK)
1368 SetErrnoFromWinError (GetLastError ());
1369 if (!CloseHandle (h->h) && (ret == GNUNET_OK))
1371 ret = GNUNET_SYSERR;
1372 SetErrnoFromWinError (GetLastError ());
1375 ret = munmap (h->addr, h->len) != -1 ? GNUNET_OK : GNUNET_SYSERR;
1383 * Write file changes to disk
1384 * @param h handle to an open file
1385 * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
1388 GNUNET_DISK_file_sync (const struct GNUNET_DISK_FileHandle *h)
1393 return GNUNET_SYSERR;
1399 ret = FlushFileBuffers (h->h) ? GNUNET_OK : GNUNET_SYSERR;
1400 if (ret != GNUNET_OK)
1401 SetErrnoFromWinError (GetLastError ());
1404 return fsync (h->fd) == -1 ? GNUNET_SYSERR : GNUNET_OK;
1409 * Creates an interprocess channel
1410 * @param blocking creates an asynchronous pipe if set to GNUNET_NO
1411 * @return handle to the new pipe, NULL on error
1413 struct GNUNET_DISK_PipeHandle *
1414 GNUNET_DISK_pipe (int blocking)
1416 struct GNUNET_DISK_PipeHandle *p;
1420 p = GNUNET_malloc (sizeof (struct GNUNET_DISK_PipeHandle));
1430 p->fd[0].fd = fd[0];
1431 p->fd[1].fd = fd[1];
1435 flags = fcntl (fd[0], F_GETFL);
1436 flags |= O_NONBLOCK;
1437 ret = fcntl (fd[0], F_SETFL, flags);
1440 flags = fcntl (fd[1], F_GETFL);
1441 flags |= O_NONBLOCK;
1442 ret = fcntl (fd[1], F_SETFL, flags);
1446 GNUNET_log_strerror(GNUNET_ERROR_TYPE_ERROR, "fcntl");
1458 ret = CreatePipe (&p->fd[0].h, &p->fd[1].h, NULL, 0);
1466 SetNamedPipeHandleState (p->fd[0].h, &mode, NULL, NULL);
1467 SetNamedPipeHandleState (p->fd[1].h, &mode, NULL, NULL);
1468 /* this always fails on Windows 95, so we don't care about error handling */
1475 if (GNUNET_YES == err)
1486 * Closes an interprocess channel
1488 * @param p pipe to close
1489 * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
1492 GNUNET_DISK_pipe_close (struct GNUNET_DISK_PipeHandle *p)
1494 int ret = GNUNET_OK;
1496 if (!CloseHandle (p->fd[0].h))
1498 SetErrnoFromWinError (GetLastError ());
1499 ret = GNUNET_SYSERR;
1502 if (!CloseHandle (p->fd[1].h))
1504 SetErrnoFromWinError (GetLastError ());
1505 ret = GNUNET_SYSERR;
1510 if (0 != close (p->fd[0].fd))
1512 ret = GNUNET_SYSERR;
1518 if (0 != close (p->fd[1].fd))
1519 ret = GNUNET_SYSERR;
1529 * Get the handle to a particular pipe end
1531 * @param n number of the end
1533 const struct GNUNET_DISK_FileHandle *
1534 GNUNET_DISK_pipe_handle (const struct GNUNET_DISK_PipeHandle *p, int n)
1540 * Retrieve OS file handle
1542 * @param fh GNUnet file descriptor
1543 * @param dst destination buffer
1544 * @param dst_len length of dst
1545 * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
1548 GNUNET_internal_disk_file_handle (const struct GNUNET_DISK_FileHandle *fh,
1549 void *dst, unsigned int dst_len)
1552 if (dst_len < sizeof (HANDLE))
1553 return GNUNET_SYSERR;
1554 *((HANDLE *) dst) = fh->h;
1556 if (dst_len < sizeof(int))
1557 return GNUNET_SYSERR;
1558 *((int *) dst) = fh->fd;