2 This file is part of GNUnet.
3 (C) 2001--2013 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 3, 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"
34 #include "gnunet_crypto_lib.h"
37 #define LOG(kind,...) GNUNET_log_from (kind, "util", __VA_ARGS__)
39 #define LOG_STRERROR(kind,syscall) GNUNET_log_from_strerror (kind, "util", syscall)
41 #define LOG_STRERROR_FILE(kind,syscall,filename) GNUNET_log_from_strerror_file (kind, "util", syscall, filename)
44 * Block size for IO for copying files.
46 #define COPY_BLK_SIZE 65536
48 #include <sys/types.h>
53 #include <sys/param.h>
56 #include <sys/mount.h>
58 #if HAVE_SYS_STATVFS_H
59 #include <sys/statvfs.h>
63 #define _IFMT 0170000 /* type of file */
64 #define _IFLNK 0120000 /* symbolic link */
65 #define S_ISLNK(m) (((m)&_IFMT) == _IFLNK)
70 * Handle used to manage a pipe.
72 struct GNUNET_DISK_PipeHandle
75 * File descriptors for the pipe.
76 * One or both of them could be NULL.
78 struct GNUNET_DISK_FileHandle *fd[2];
83 * Closure for the recursion to determine the file size
86 struct GetFileSizeData
89 * Set to the total file size.
94 * GNUNET_YES if symbolic links should be included.
96 int include_sym_links;
99 * GNUNET_YES if mode is file-only (return total == -1 for directories).
101 int single_file_mode;
107 * Translate GNUnet-internal permission bitmap to UNIX file
108 * access permission bitmap.
110 * @param perm file permissions, GNUnet style
111 * @return file permissions, UNIX style
114 translate_unix_perms (enum GNUNET_DISK_AccessPermissions perm)
119 if (perm & GNUNET_DISK_PERM_USER_READ)
121 if (perm & GNUNET_DISK_PERM_USER_WRITE)
123 if (perm & GNUNET_DISK_PERM_USER_EXEC)
125 if (perm & GNUNET_DISK_PERM_GROUP_READ)
127 if (perm & GNUNET_DISK_PERM_GROUP_WRITE)
129 if (perm & GNUNET_DISK_PERM_GROUP_EXEC)
131 if (perm & GNUNET_DISK_PERM_OTHER_READ)
133 if (perm & GNUNET_DISK_PERM_OTHER_WRITE)
135 if (perm & GNUNET_DISK_PERM_OTHER_EXEC)
144 * Iterate over all files in the given directory and
145 * accumulate their size.
147 * @param cls closure of type "struct GetFileSizeData"
148 * @param fn current filename we are looking at
149 * @return GNUNET_SYSERR on serious errors, otherwise GNUNET_OK
152 getSizeRec (void *cls, const char *fn)
154 struct GetFileSizeData *gfsd = cls;
163 if (0 != STAT64 (fn, &buf))
165 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_DEBUG, "stat64", fn);
166 return GNUNET_SYSERR;
169 if (0 != STAT (fn, &buf))
171 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_DEBUG, "stat", fn);
172 return GNUNET_SYSERR;
175 if ((S_ISDIR (buf.st_mode)) && (gfsd->single_file_mode == GNUNET_YES))
178 return GNUNET_SYSERR;
180 if ((!S_ISLNK (buf.st_mode)) || (gfsd->include_sym_links == GNUNET_YES))
181 gfsd->total += buf.st_size;
182 if ((S_ISDIR (buf.st_mode)) && (0 == ACCESS (fn, X_OK)) &&
183 ((!S_ISLNK (buf.st_mode)) || (gfsd->include_sym_links == GNUNET_YES)))
185 if (GNUNET_SYSERR == GNUNET_DISK_directory_scan (fn, &getSizeRec, gfsd))
186 return GNUNET_SYSERR;
193 * Checks whether a handle is invalid
195 * @param h handle to check
196 * @return GNUNET_YES if invalid, GNUNET_NO if valid
199 GNUNET_DISK_handle_invalid (const struct GNUNET_DISK_FileHandle *h)
202 return ((!h) || (h->h == INVALID_HANDLE_VALUE)) ? GNUNET_YES : GNUNET_NO;
204 return ((!h) || (h->fd == -1)) ? GNUNET_YES : GNUNET_NO;
209 * Get the size of an open file.
211 * @param fh open file handle
212 * @param size where to write size of the file
213 * @return GNUNET_OK on success, GNUNET_SYSERR on error
216 GNUNET_DISK_file_handle_size (struct GNUNET_DISK_FileHandle *fh,
222 b = GetFileSizeEx (fh->h, &li);
225 SetErrnoFromWinError (GetLastError ());
226 return GNUNET_SYSERR;
228 *size = (OFF_T) li.QuadPart;
232 if (0 != FSTAT (fh->fd, &sbuf))
233 return GNUNET_SYSERR;
234 *size = sbuf.st_size;
241 * Move the read/write pointer in a file
243 * @param h handle of an open file
244 * @param offset position to move to
245 * @param whence specification to which position the offset parameter relates to
246 * @return the new position on success, GNUNET_SYSERR otherwise
249 GNUNET_DISK_file_seek (const struct GNUNET_DISK_FileHandle * h, OFF_T offset,
250 enum GNUNET_DISK_Seek whence)
255 return GNUNET_SYSERR;
260 LARGE_INTEGER new_pos;
263 static DWORD t[] = { FILE_BEGIN, FILE_CURRENT, FILE_END };
264 li.QuadPart = offset;
266 b = SetFilePointerEx (h->h, li, &new_pos, t[whence]);
269 SetErrnoFromWinError (GetLastError ());
270 return GNUNET_SYSERR;
272 return (OFF_T) new_pos.QuadPart;
274 static int t[] = { SEEK_SET, SEEK_CUR, SEEK_END };
276 return lseek (h->fd, offset, t[whence]);
282 * Get the size of the file (or directory) of the given file (in
285 * @param filename name of the file or directory
286 * @param size set to the size of the file (or,
287 * in the case of directories, the sum
288 * of all sizes of files in the directory)
289 * @param includeSymLinks should symbolic links be
291 * @param singleFileMode GNUNET_YES to only get size of one file
292 * and return GNUNET_SYSERR for directories.
293 * @return GNUNET_SYSERR on error, GNUNET_OK on success
296 GNUNET_DISK_file_size (const char *filename, uint64_t * size,
297 int includeSymLinks, int singleFileMode)
299 struct GetFileSizeData gfsd;
302 GNUNET_assert (size != NULL);
304 gfsd.include_sym_links = includeSymLinks;
305 gfsd.single_file_mode = singleFileMode;
306 ret = getSizeRec (&gfsd, filename);
313 * Obtain some unique identifiers for the given file
314 * that can be used to identify it in the local system.
315 * This function is used between GNUnet processes to
316 * quickly check if two files with the same absolute path
317 * are actually identical. The two processes represent
318 * the same peer but may communicate over the network
319 * (and the file may be on an NFS volume). This function
320 * may not be supported on all operating systems.
322 * @param filename name of the file
323 * @param dev set to the device ID
324 * @param ino set to the inode ID
325 * @return GNUNET_OK on success
328 GNUNET_DISK_file_get_identifiers (const char *filename, uint64_t * dev,
333 // FIXME NILS: test this
334 struct GNUNET_DISK_FileHandle *fh;
335 BY_HANDLE_FILE_INFORMATION info;
338 fh = GNUNET_DISK_file_open (filename, GNUNET_DISK_OPEN_READ, 0);
340 return GNUNET_SYSERR;
341 succ = GetFileInformationByHandle (fh->h, &info);
342 GNUNET_DISK_file_close (fh);
345 return GNUNET_SYSERR;
347 *dev = info.dwVolumeSerialNumber;
348 *ino = ((((uint64_t) info.nFileIndexHigh) << (sizeof (DWORD) * 8)) | info.nFileIndexLow);
355 if (0 != stat (filename, &sbuf))
357 return GNUNET_SYSERR;
359 *ino = (uint64_t) sbuf.st_ino;
368 if (0 != statvfs (filename, &fbuf))
370 return GNUNET_SYSERR;
372 *dev = (uint64_t) fbuf.f_fsid;
378 if (0 != statfs (filename, &fbuf))
380 return GNUNET_SYSERR;
382 *dev = ((uint64_t) fbuf.f_fsid.val[0]) << 32 ||
383 ((uint64_t) fbuf.f_fsid.val[1]);
388 #endif /* !WINDOWS */
394 * Create the name for a temporary file or directory from a template.
396 * @param t template (without XXXXX or "/tmp/")
397 * @return name ready for passing to 'mktemp' or 'mkdtemp', NULL on error
400 mktemp_name (const char *t)
406 if ((t[0] != '/') && (t[0] != '\\')
408 && !(isalpha ((int) t[0]) && (t[0] != '\0') && (t[1] == ':'))
412 /* FIXME: This uses system codepage on W32, not UTF-8 */
413 tmpdir = getenv ("TMPDIR");
415 tmpdir = getenv ("TMP");
417 tmpdir = getenv ("TEMP");
420 GNUNET_asprintf (&tmpl, "%s/%s%s", tmpdir, t, "XXXXXX");
424 GNUNET_asprintf (&tmpl, "%s%s", t, "XXXXXX");
427 fn = (char *) GNUNET_malloc (MAX_PATH + 1);
428 if (ERROR_SUCCESS != plibc_conv_to_win_path (tmpl, fn))
451 tfn = GNUNET_strdup (fn);
452 random_fn = _mktemp (tfn);
453 if (NULL == random_fn)
458 /* FIXME: assume fn to be UTF-8-encoded and do the right thing */
459 if (0 == CreateDirectoryA (tfn, NULL))
461 DWORD error = GetLastError ();
463 if (ERROR_ALREADY_EXISTS == error)
475 * Create an (empty) temporary directory on disk. If the given name is not
476 * an absolute path, the current 'TMPDIR' will be prepended. In any case,
477 * 6 random characters will be appended to the name to create a unique
480 * @param t component to use for the name;
481 * does NOT contain "XXXXXX" or "/tmp/".
482 * @return NULL on error, otherwise name of fresh
483 * file on disk in directory for temporary files
486 GNUNET_DISK_mkdtemp (const char *t)
490 fn = mktemp_name (t);
491 if (fn != mkdtemp (fn))
493 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_ERROR, "mkstemp", fn);
502 * Move a file out of the way (create a backup) by
503 * renaming it to "orig.NUM~" where NUM is the smallest
504 * number that is not used yet.
506 * @param fil name of the file to back up
509 GNUNET_DISK_file_backup (const char *fil)
515 slen = strlen (fil) + 20;
516 target = GNUNET_malloc (slen);
520 GNUNET_snprintf (target, slen,
524 } while (0 == access (target, F_OK));
525 if (0 != rename (fil, target))
526 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR,
529 GNUNET_free (target);
534 * Create an (empty) temporary file on disk. If the given name is not
535 * an absolute path, the current 'TMPDIR' will be prepended. In any case,
536 * 6 random characters will be appended to the name to create a unique
539 * @param t component to use for the name;
540 * does NOT contain "XXXXXX" or "/tmp/".
541 * @return NULL on error, otherwise name of fresh
542 * file on disk in directory for temporary files
545 GNUNET_DISK_mktemp (const char *t)
550 fn = mktemp_name (t);
551 if (-1 == (fd = mkstemp (fn)))
553 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_ERROR, "mkstemp", fn);
558 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "close", fn);
564 * Test if "fil" is a directory and listable. Optionally, also check if the
565 * directory is readable. Will not print an error message if the directory does
566 * not exist. Will log errors if GNUNET_SYSERR is returned (i.e., a file exists
567 * with the same name).
569 * @param fil filename to test
570 * @param is_readable GNUNET_YES to additionally check if "fil" is readable;
571 * GNUNET_NO to disable this check
572 * @return GNUNET_YES if yes, GNUNET_NO if not; GNUNET_SYSERR if it
573 * does not exist or stat'ed
576 GNUNET_DISK_directory_test (const char *fil, int is_readable)
578 struct stat filestat;
581 ret = STAT (fil, &filestat);
585 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "stat", fil);
586 return GNUNET_SYSERR;
588 if (!S_ISDIR (filestat.st_mode))
590 LOG (GNUNET_ERROR_TYPE_DEBUG,
591 "A file already exits with the same name %s\n", fil);
594 if (GNUNET_YES == is_readable)
595 ret = ACCESS (fil, R_OK | X_OK);
597 ret = ACCESS (fil, X_OK);
600 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "access", fil);
608 * Check that fil corresponds to a filename
609 * (of a file that exists and that is not a directory).
611 * @param fil filename to check
612 * @return GNUNET_YES if yes, GNUNET_NO if not a file, GNUNET_SYSERR if something
613 * else (will print an error message in that case, too).
616 GNUNET_DISK_file_test (const char *fil)
618 struct stat filestat;
622 rdir = GNUNET_STRINGS_filename_expand (fil);
624 return GNUNET_SYSERR;
626 ret = STAT (rdir, &filestat);
631 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "stat", rdir);
633 return GNUNET_SYSERR;
638 if (!S_ISREG (filestat.st_mode))
643 if (ACCESS (rdir, F_OK) < 0)
645 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "access", rdir);
647 return GNUNET_SYSERR;
655 * Implementation of "mkdir -p"
656 * @param dir the directory to create
657 * @returns GNUNET_OK on success, GNUNET_SYSERR on failure
660 GNUNET_DISK_directory_create (const char *dir)
668 rdir = GNUNET_STRINGS_filename_expand (dir);
670 return GNUNET_SYSERR;
674 pos = 1; /* skip heading '/' */
676 /* Local or Network path? */
677 if (strncmp (rdir, "\\\\", 2) == 0)
682 if (rdir[pos] == '\\')
692 pos = 3; /* strlen("C:\\") */
695 /* Check which low level directories already exist */
697 rdir[len] = DIR_SEPARATOR;
700 if (DIR_SEPARATOR == rdir[pos2])
703 ret = GNUNET_DISK_directory_test (rdir, GNUNET_NO);
704 if (GNUNET_NO == ret)
707 return GNUNET_SYSERR;
709 rdir[pos2] = DIR_SEPARATOR;
710 if (GNUNET_YES == ret)
721 /* Start creating directories */
724 if ((rdir[pos] == DIR_SEPARATOR) || (pos == len))
727 ret = GNUNET_DISK_directory_test (rdir, GNUNET_NO);
728 if (GNUNET_NO == ret)
731 return GNUNET_SYSERR;
733 if (GNUNET_SYSERR == ret)
736 ret = mkdir (rdir, S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH); /* 755 */
738 wchar_t wrdir[MAX_PATH + 1];
739 if (ERROR_SUCCESS == plibc_conv_to_win_pathwconv(rdir, wrdir))
740 ret = !CreateDirectoryW (wrdir, NULL);
744 if ((ret != 0) && (errno != EEXIST))
746 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_ERROR, "mkdir", rdir);
748 return GNUNET_SYSERR;
751 rdir[pos] = DIR_SEPARATOR;
761 * Create the directory structure for storing
764 * @param filename name of a file in the directory
765 * @returns GNUNET_OK on success,
766 * GNUNET_SYSERR on failure,
767 * GNUNET_NO if the directory
768 * exists but is not writeable for us
771 GNUNET_DISK_directory_create_for_file (const char *filename)
777 rdir = GNUNET_STRINGS_filename_expand (filename);
779 return GNUNET_SYSERR;
781 while ((len > 0) && (rdir[len] != DIR_SEPARATOR))
784 ret = GNUNET_DISK_directory_create (rdir);
785 if ((ret == GNUNET_OK) && (0 != ACCESS (rdir, W_OK)))
793 * Read the contents of a binary file into a buffer.
794 * @param h handle to an open file
795 * @param result the buffer to write the result to
796 * @param len the maximum number of bytes to read
797 * @return the number of bytes read on success, GNUNET_SYSERR on failure
800 GNUNET_DISK_file_read (const struct GNUNET_DISK_FileHandle * h, void *result,
806 return GNUNET_SYSERR;
812 if (h->type != GNUNET_DISK_HANLDE_TYPE_PIPE)
814 if (!ReadFile (h->h, result, len, &bytesRead, NULL))
816 SetErrnoFromWinError (GetLastError ());
817 return GNUNET_SYSERR;
822 if (!ReadFile (h->h, result, len, &bytesRead, h->oOverlapRead))
824 if (GetLastError () != ERROR_IO_PENDING)
826 LOG (GNUNET_ERROR_TYPE_DEBUG, "Error reading from pipe: %u\n", GetLastError ());
827 SetErrnoFromWinError (GetLastError ());
828 return GNUNET_SYSERR;
830 LOG (GNUNET_ERROR_TYPE_DEBUG, "Will get overlapped result\n");
831 GetOverlappedResult (h->h, h->oOverlapRead, &bytesRead, TRUE);
833 LOG (GNUNET_ERROR_TYPE_DEBUG, "Read %u bytes from pipe\n", bytesRead);
837 return read (h->fd, result, len);
843 * Read the contents of a binary file into a buffer.
844 * Guarantees not to block (returns GNUNET_SYSERR and sets errno to EAGAIN
845 * when no data can be read).
847 * @param h handle to an open file
848 * @param result the buffer to write the result to
849 * @param len the maximum number of bytes to read
850 * @return the number of bytes read on success, GNUNET_SYSERR on failure
853 GNUNET_DISK_file_read_non_blocking (const struct GNUNET_DISK_FileHandle * h,
860 return GNUNET_SYSERR;
866 if (h->type != GNUNET_DISK_HANLDE_TYPE_PIPE)
868 if (!ReadFile (h->h, result, len, &bytesRead, NULL))
870 SetErrnoFromWinError (GetLastError ());
871 return GNUNET_SYSERR;
876 if (!ReadFile (h->h, result, len, &bytesRead, h->oOverlapRead))
878 if (GetLastError () != ERROR_IO_PENDING)
880 LOG (GNUNET_ERROR_TYPE_DEBUG, "Error reading from pipe: %u\n", GetLastError ());
881 SetErrnoFromWinError (GetLastError ());
882 return GNUNET_SYSERR;
886 LOG (GNUNET_ERROR_TYPE_DEBUG,
887 "ReadFile() queued a read, cancelling\n");
890 return GNUNET_SYSERR;
893 LOG (GNUNET_ERROR_TYPE_DEBUG, "Read %u bytes\n", bytesRead);
900 /* set to non-blocking, read, then set back */
901 flags = fcntl (h->fd, F_GETFL);
902 if (0 == (flags & O_NONBLOCK))
903 (void) fcntl (h->fd, F_SETFL, flags | O_NONBLOCK);
904 ret = read (h->fd, result, len);
905 if (0 == (flags & O_NONBLOCK))
908 (void) fcntl (h->fd, F_SETFL, flags);
917 * Read the contents of a binary file into a buffer.
919 * @param fn file name
920 * @param result the buffer to write the result to
921 * @param len the maximum number of bytes to read
922 * @return number of bytes read, GNUNET_SYSERR on failure
925 GNUNET_DISK_fn_read (const char *fn, void *result, size_t len)
927 struct GNUNET_DISK_FileHandle *fh;
930 fh = GNUNET_DISK_file_open (fn, GNUNET_DISK_OPEN_READ, GNUNET_DISK_PERM_NONE);
932 return GNUNET_SYSERR;
933 ret = GNUNET_DISK_file_read (fh, result, len);
934 GNUNET_assert (GNUNET_OK == GNUNET_DISK_file_close (fh));
941 * Write a buffer to a file.
942 * @param h handle to open file
943 * @param buffer the data to write
944 * @param n number of bytes to write
945 * @return number of bytes written on success, GNUNET_SYSERR on error
948 GNUNET_DISK_file_write (const struct GNUNET_DISK_FileHandle * h,
949 const void *buffer, size_t n)
954 return GNUNET_SYSERR;
960 if (h->type != GNUNET_DISK_HANLDE_TYPE_PIPE)
962 if (!WriteFile (h->h, buffer, n, &bytesWritten, NULL))
964 SetErrnoFromWinError (GetLastError ());
965 return GNUNET_SYSERR;
970 LOG (GNUNET_ERROR_TYPE_DEBUG, "It is a pipe trying to write %u bytes\n", n);
971 if (!WriteFile (h->h, buffer, n, &bytesWritten, h->oOverlapWrite))
973 if (GetLastError () != ERROR_IO_PENDING)
975 SetErrnoFromWinError (GetLastError ());
976 LOG (GNUNET_ERROR_TYPE_DEBUG, "Error writing to pipe: %u\n",
978 return GNUNET_SYSERR;
980 LOG (GNUNET_ERROR_TYPE_DEBUG, "Will get overlapped result\n");
981 if (!GetOverlappedResult (h->h, h->oOverlapWrite, &bytesWritten, TRUE))
983 SetErrnoFromWinError (GetLastError ());
984 LOG (GNUNET_ERROR_TYPE_DEBUG,
985 "Error getting overlapped result while writing to pipe: %u\n",
987 return GNUNET_SYSERR;
993 if (!GetOverlappedResult (h->h, h->oOverlapWrite, &ovr, TRUE))
995 LOG (GNUNET_ERROR_TYPE_DEBUG,
996 "Error getting control overlapped result while writing to pipe: %u\n",
1001 LOG (GNUNET_ERROR_TYPE_DEBUG,
1002 "Wrote %u bytes (ovr says %u), picking the greatest\n",
1006 if (bytesWritten == 0)
1010 LOG (GNUNET_ERROR_TYPE_DEBUG, "Wrote %u bytes, returning -1 with EAGAIN\n", bytesWritten);
1012 return GNUNET_SYSERR;
1015 LOG (GNUNET_ERROR_TYPE_DEBUG, "Wrote %u bytes\n", bytesWritten);
1017 return bytesWritten;
1019 return write (h->fd, buffer, n);
1025 * Write a buffer to a file, blocking, if necessary.
1026 * @param h handle to open file
1027 * @param buffer the data to write
1028 * @param n number of bytes to write
1029 * @return number of bytes written on success, GNUNET_SYSERR on error
1032 GNUNET_DISK_file_write_blocking (const struct GNUNET_DISK_FileHandle * h,
1033 const void *buffer, size_t n)
1038 return GNUNET_SYSERR;
1043 /* We do a non-overlapped write, which is as blocking as it gets */
1044 LOG (GNUNET_ERROR_TYPE_DEBUG, "Writing %u bytes\n", n);
1045 if (!WriteFile (h->h, buffer, n, &bytesWritten, NULL))
1047 SetErrnoFromWinError (GetLastError ());
1048 LOG (GNUNET_ERROR_TYPE_DEBUG, "Error writing to pipe: %u\n",
1050 return GNUNET_SYSERR;
1052 if (bytesWritten == 0 && n > 0)
1054 LOG (GNUNET_ERROR_TYPE_DEBUG, "Waiting for pipe to clean\n");
1055 WaitForSingleObject (h->h, INFINITE);
1056 if (!WriteFile (h->h, buffer, n, &bytesWritten, NULL))
1058 SetErrnoFromWinError (GetLastError ());
1059 LOG (GNUNET_ERROR_TYPE_DEBUG, "Error writing to pipe: %u\n",
1061 return GNUNET_SYSERR;
1064 LOG (GNUNET_ERROR_TYPE_DEBUG, "Wrote %u bytes\n", bytesWritten);
1065 return bytesWritten;
1070 /* set to blocking, write, then set back */
1071 flags = fcntl (h->fd, F_GETFL);
1072 if (0 != (flags & O_NONBLOCK))
1073 (void) fcntl (h->fd, F_SETFL, flags - O_NONBLOCK);
1074 ret = write (h->fd, buffer, n);
1075 if (0 == (flags & O_NONBLOCK))
1076 (void) fcntl (h->fd, F_SETFL, flags);
1083 * Write a buffer to a file. If the file is longer than the
1084 * number of bytes that will be written, it will be truncated.
1086 * @param fn file name
1087 * @param buffer the data to write
1088 * @param n number of bytes to write
1089 * @param mode file permissions
1090 * @return number of bytes written on success, GNUNET_SYSERR on error
1093 GNUNET_DISK_fn_write (const char *fn, const void *buffer, size_t n,
1094 enum GNUNET_DISK_AccessPermissions mode)
1096 struct GNUNET_DISK_FileHandle *fh;
1099 fh = GNUNET_DISK_file_open (fn,
1100 GNUNET_DISK_OPEN_WRITE | GNUNET_DISK_OPEN_TRUNCATE
1101 | GNUNET_DISK_OPEN_CREATE, mode);
1103 return GNUNET_SYSERR;
1104 ret = GNUNET_DISK_file_write (fh, buffer, n);
1105 GNUNET_assert (GNUNET_OK == GNUNET_DISK_file_close (fh));
1111 * Scan a directory for files.
1113 * @param dirName the name of the directory
1114 * @param callback the method to call for each file,
1115 * can be NULL, in that case, we only count
1116 * @param callback_cls closure for callback
1117 * @return the number of files found, GNUNET_SYSERR on error or
1118 * ieration aborted by callback returning GNUNET_SYSERR
1121 GNUNET_DISK_directory_scan (const char *dirName,
1122 GNUNET_FileNameCallback callback,
1126 struct dirent *finfo;
1132 unsigned int name_len;
1133 unsigned int n_size;
1135 GNUNET_assert (dirName != NULL);
1136 dname = GNUNET_STRINGS_filename_expand (dirName);
1138 return GNUNET_SYSERR;
1139 while ((strlen (dname) > 0) && (dname[strlen (dname) - 1] == DIR_SEPARATOR))
1140 dname[strlen (dname) - 1] = '\0';
1141 if (0 != STAT (dname, &istat))
1143 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "stat", dname);
1144 GNUNET_free (dname);
1145 return GNUNET_SYSERR;
1147 if (!S_ISDIR (istat.st_mode))
1149 LOG (GNUNET_ERROR_TYPE_WARNING, _("Expected `%s' to be a directory!\n"),
1151 GNUNET_free (dname);
1152 return GNUNET_SYSERR;
1155 dinfo = OPENDIR (dname);
1156 if ((errno == EACCES) || (dinfo == NULL))
1158 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "opendir", dname);
1161 GNUNET_free (dname);
1162 return GNUNET_SYSERR;
1165 n_size = strlen (dname) + name_len + 2;
1166 name = GNUNET_malloc (n_size);
1167 while ((finfo = READDIR (dinfo)) != NULL)
1169 if ((0 == strcmp (finfo->d_name, ".")) ||
1170 (0 == strcmp (finfo->d_name, "..")))
1172 if (callback != NULL)
1174 if (name_len < strlen (finfo->d_name))
1177 name_len = strlen (finfo->d_name);
1178 n_size = strlen (dname) + name_len + 2;
1179 name = GNUNET_malloc (n_size);
1181 /* dname can end in "/" only if dname == "/";
1182 * if dname does not end in "/", we need to add
1183 * a "/" (otherwise, we must not!) */
1184 GNUNET_snprintf (name, n_size, "%s%s%s", dname,
1185 (strcmp (dname, DIR_SEPARATOR_STR) ==
1186 0) ? "" : DIR_SEPARATOR_STR, finfo->d_name);
1187 ret = callback (callback_cls, name);
1188 if (GNUNET_OK != ret)
1192 GNUNET_free (dname);
1193 if (GNUNET_NO == ret)
1195 return GNUNET_SYSERR;
1202 GNUNET_free (dname);
1208 * Opaque handle used for iterating over a directory.
1210 struct GNUNET_DISK_DirectoryIterator
1214 * Function to call on directory entries.
1216 GNUNET_DISK_DirectoryIteratorCallback callback;
1219 * Closure for callback.
1224 * Reference to directory.
1234 * Next filename to process.
1241 enum GNUNET_SCHEDULER_Priority priority;
1247 * Task used by the directory iterator.
1250 directory_iterator_task (void *cls,
1251 const struct GNUNET_SCHEDULER_TaskContext *tc)
1253 struct GNUNET_DISK_DirectoryIterator *iter = cls;
1256 name = iter->next_name;
1257 GNUNET_assert (name != NULL);
1258 iter->next_name = NULL;
1259 iter->callback (iter->callback_cls, iter, name, iter->dirname);
1265 * This function must be called during the DiskIteratorCallback
1266 * (exactly once) to schedule the task to process the next
1267 * filename in the directory (if there is one).
1269 * @param iter opaque handle for the iterator
1270 * @param can set to GNUNET_YES to terminate the iteration early
1271 * @return GNUNET_YES if iteration will continue,
1272 * GNUNET_NO if this was the last entry (and iteration is complete),
1273 * GNUNET_SYSERR if abort was YES
1276 GNUNET_DISK_directory_iterator_next (struct GNUNET_DISK_DirectoryIterator *iter,
1279 struct dirent *finfo;
1281 GNUNET_assert (iter->next_name == NULL);
1282 if (can == GNUNET_YES)
1284 CLOSEDIR (iter->directory);
1285 GNUNET_free (iter->dirname);
1287 return GNUNET_SYSERR;
1289 while (NULL != (finfo = READDIR (iter->directory)))
1291 if ((0 == strcmp (finfo->d_name, ".")) ||
1292 (0 == strcmp (finfo->d_name, "..")))
1294 GNUNET_asprintf (&iter->next_name, "%s%s%s", iter->dirname,
1295 DIR_SEPARATOR_STR, finfo->d_name);
1300 GNUNET_DISK_directory_iterator_next (iter, GNUNET_YES);
1303 GNUNET_SCHEDULER_add_with_priority (iter->priority, &directory_iterator_task,
1310 * Scan a directory for files using the scheduler to run a task for
1311 * each entry. The name of the directory must be expanded first (!).
1312 * If a scheduler does not need to be used, GNUNET_DISK_directory_scan
1313 * may provide a simpler API.
1315 * @param prio priority to use
1316 * @param dirName the name of the directory
1317 * @param callback the method to call for each file
1318 * @param callback_cls closure for callback
1319 * @return GNUNET_YES if directory is not empty and 'callback'
1320 * will be called later, GNUNET_NO otherwise, GNUNET_SYSERR on error.
1323 GNUNET_DISK_directory_iterator_start (enum GNUNET_SCHEDULER_Priority prio,
1324 const char *dirName,
1325 GNUNET_DISK_DirectoryIteratorCallback
1326 callback, void *callback_cls)
1328 struct GNUNET_DISK_DirectoryIterator *di;
1330 di = GNUNET_malloc (sizeof (struct GNUNET_DISK_DirectoryIterator));
1331 di->callback = callback;
1332 di->callback_cls = callback_cls;
1333 di->directory = OPENDIR (dirName);
1334 if (di->directory == NULL)
1337 callback (callback_cls, NULL, NULL, NULL);
1338 return GNUNET_SYSERR;
1340 di->dirname = GNUNET_strdup (dirName);
1341 di->priority = prio;
1342 return GNUNET_DISK_directory_iterator_next (di, GNUNET_NO);
1347 * Function that removes the given directory by calling
1348 * "GNUNET_DISK_directory_remove".
1350 * @param unused not used
1351 * @param fn directory to remove
1355 remove_helper (void *unused, const char *fn)
1357 (void) GNUNET_DISK_directory_remove (fn);
1363 * Remove all files in a directory (rm -rf). Call with
1367 * @param filename the file to remove
1368 * @return GNUNET_OK on success, GNUNET_SYSERR on error
1371 GNUNET_DISK_directory_remove (const char *filename)
1375 if (0 != LSTAT (filename, &istat))
1376 return GNUNET_NO; /* file may not exist... */
1377 (void) CHMOD (filename, S_IWUSR | S_IRUSR | S_IXUSR);
1378 if (UNLINK (filename) == 0)
1380 if ((errno != EISDIR) &&
1381 /* EISDIR is not sufficient in all cases, e.g.
1382 * sticky /tmp directory may result in EPERM on BSD.
1383 * So we also explicitly check "isDirectory" */
1384 (GNUNET_YES != GNUNET_DISK_directory_test (filename, GNUNET_YES)))
1386 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "rmdir", filename);
1387 return GNUNET_SYSERR;
1389 if (GNUNET_SYSERR ==
1390 GNUNET_DISK_directory_scan (filename, &remove_helper, NULL))
1391 return GNUNET_SYSERR;
1392 if (0 != RMDIR (filename))
1394 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "rmdir", filename);
1395 return GNUNET_SYSERR;
1404 * @param src file to copy
1405 * @param dst destination file name
1406 * @return GNUNET_OK on success, GNUNET_SYSERR on error
1409 GNUNET_DISK_file_copy (const char *src, const char *dst)
1415 struct GNUNET_DISK_FileHandle *in;
1416 struct GNUNET_DISK_FileHandle *out;
1418 if (GNUNET_OK != GNUNET_DISK_file_size (src, &size, GNUNET_YES, GNUNET_YES))
1419 return GNUNET_SYSERR;
1421 in = GNUNET_DISK_file_open (src, GNUNET_DISK_OPEN_READ,
1422 GNUNET_DISK_PERM_NONE);
1424 return GNUNET_SYSERR;
1426 GNUNET_DISK_file_open (dst,
1427 GNUNET_DISK_OPEN_WRITE | GNUNET_DISK_OPEN_CREATE |
1428 GNUNET_DISK_OPEN_FAILIFEXISTS,
1429 GNUNET_DISK_PERM_USER_READ |
1430 GNUNET_DISK_PERM_USER_WRITE |
1431 GNUNET_DISK_PERM_GROUP_READ |
1432 GNUNET_DISK_PERM_GROUP_WRITE);
1435 GNUNET_DISK_file_close (in);
1436 return GNUNET_SYSERR;
1438 buf = GNUNET_malloc (COPY_BLK_SIZE);
1441 len = COPY_BLK_SIZE;
1442 if (len > size - pos)
1444 if (len != GNUNET_DISK_file_read (in, buf, len))
1446 if (len != GNUNET_DISK_file_write (out, buf, len))
1451 GNUNET_DISK_file_close (in);
1452 GNUNET_DISK_file_close (out);
1456 GNUNET_DISK_file_close (in);
1457 GNUNET_DISK_file_close (out);
1458 return GNUNET_SYSERR;
1463 * @brief Removes special characters as ':' from a filename.
1464 * @param fn the filename to canonicalize
1467 GNUNET_DISK_filename_canonicalize (char *fn)
1477 if (c == '/' || c == '\\' || c == ':' || c == '*' || c == '?' || c == '"' ||
1478 c == '<' || c == '>' || c == '|')
1490 * @brief Change owner of a file
1492 * @param filename name of file to change the owner of
1493 * @param user name of the new owner
1494 * @return GNUNET_OK on success, GNUNET_SYSERR on failure
1497 GNUNET_DISK_file_change_owner (const char *filename, const char *user)
1502 pws = getpwnam (user);
1505 LOG (GNUNET_ERROR_TYPE_ERROR,
1506 _("Cannot obtain information about user `%s': %s\n"), user,
1508 return GNUNET_SYSERR;
1510 if (0 != chown (filename, pws->pw_uid, pws->pw_gid))
1511 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "chown", filename);
1518 * Lock a part of a file
1519 * @param fh file handle
1520 * @param lockStart absolute position from where to lock
1521 * @param lockEnd absolute position until where to lock
1522 * @param excl GNUNET_YES for an exclusive lock
1523 * @return GNUNET_OK on success, GNUNET_SYSERR on error
1526 GNUNET_DISK_file_lock (struct GNUNET_DISK_FileHandle *fh, OFF_T lockStart,
1527 OFF_T lockEnd, int excl)
1532 return GNUNET_SYSERR;
1538 memset (&fl, 0, sizeof (struct flock));
1539 fl.l_type = excl ? F_WRLCK : F_RDLCK;
1540 fl.l_whence = SEEK_SET;
1541 fl.l_start = lockStart;
1544 return fcntl (fh->fd, F_SETLK, &fl) != 0 ? GNUNET_SYSERR : GNUNET_OK;
1547 OFF_T diff = lockEnd - lockStart;
1548 DWORD diff_low, diff_high;
1549 diff_low = (DWORD) (diff & 0xFFFFFFFF);
1550 diff_high = (DWORD) ((diff >> (sizeof (DWORD) * 8)) & 0xFFFFFFFF);
1552 memset (&o, 0, sizeof (OVERLAPPED));
1553 o.Offset = (DWORD) (lockStart & 0xFFFFFFFF);;
1554 o.OffsetHigh = (DWORD) (((lockStart & ~0xFFFFFFFF) >> (sizeof (DWORD) * 8)) & 0xFFFFFFFF);
1557 (fh->h, (excl ? LOCKFILE_EXCLUSIVE_LOCK : 0) | LOCKFILE_FAIL_IMMEDIATELY,
1558 0, diff_low, diff_high, &o))
1560 SetErrnoFromWinError (GetLastError ());
1561 return GNUNET_SYSERR;
1570 * Unlock a part of a file
1571 * @param fh file handle
1572 * @param unlockStart absolute position from where to unlock
1573 * @param unlockEnd absolute position until where to unlock
1574 * @return GNUNET_OK on success, GNUNET_SYSERR on error
1577 GNUNET_DISK_file_unlock (struct GNUNET_DISK_FileHandle *fh, OFF_T unlockStart,
1583 return GNUNET_SYSERR;
1589 memset (&fl, 0, sizeof (struct flock));
1590 fl.l_type = F_UNLCK;
1591 fl.l_whence = SEEK_SET;
1592 fl.l_start = unlockStart;
1593 fl.l_len = unlockEnd;
1595 return fcntl (fh->fd, F_SETLK, &fl) != 0 ? GNUNET_SYSERR : GNUNET_OK;
1598 OFF_T diff = unlockEnd - unlockStart;
1599 DWORD diff_low, diff_high;
1600 diff_low = (DWORD) (diff & 0xFFFFFFFF);
1601 diff_high = (DWORD) ((diff >> (sizeof (DWORD) * 8)) & 0xFFFFFFFF);
1603 memset (&o, 0, sizeof (OVERLAPPED));
1604 o.Offset = (DWORD) (unlockStart & 0xFFFFFFFF);;
1605 o.OffsetHigh = (DWORD) (((unlockStart & ~0xFFFFFFFF) >> (sizeof (DWORD) * 8)) & 0xFFFFFFFF);
1607 if (!UnlockFileEx (fh->h, 0, diff_low, diff_high, &o))
1609 SetErrnoFromWinError (GetLastError ());
1610 return GNUNET_SYSERR;
1619 * Open a file. Note that the access permissions will only be
1620 * used if a new file is created and if the underlying operating
1621 * system supports the given permissions.
1623 * @param fn file name to be opened
1624 * @param flags opening flags, a combination of GNUNET_DISK_OPEN_xxx bit flags
1625 * @param perm permissions for the newly created file, use
1626 * GNUNET_DISK_PERM_USER_NONE if a file could not be created by this
1627 * call (because of flags)
1628 * @return IO handle on success, NULL on error
1630 struct GNUNET_DISK_FileHandle *
1631 GNUNET_DISK_file_open (const char *fn, enum GNUNET_DISK_OpenFlags flags,
1632 enum GNUNET_DISK_AccessPermissions perm)
1635 struct GNUNET_DISK_FileHandle *ret;
1641 wchar_t wexpfn[MAX_PATH + 1];
1648 expfn = GNUNET_STRINGS_filename_expand (fn);
1653 if (GNUNET_DISK_OPEN_READWRITE == (flags & GNUNET_DISK_OPEN_READWRITE))
1654 oflags = O_RDWR; /* note: O_RDWR is NOT always O_RDONLY | O_WRONLY */
1655 else if (flags & GNUNET_DISK_OPEN_READ)
1657 else if (flags & GNUNET_DISK_OPEN_WRITE)
1662 GNUNET_free (expfn);
1665 if (flags & GNUNET_DISK_OPEN_FAILIFEXISTS)
1666 oflags |= (O_CREAT | O_EXCL);
1667 if (flags & GNUNET_DISK_OPEN_TRUNCATE)
1669 if (flags & GNUNET_DISK_OPEN_APPEND)
1671 if (flags & GNUNET_DISK_OPEN_CREATE)
1673 (void) GNUNET_DISK_directory_create_for_file (expfn);
1675 mode = translate_unix_perms (perm);
1678 fd = open (expfn, oflags
1682 | O_LARGEFILE, mode);
1685 if (0 == (flags & GNUNET_DISK_OPEN_FAILIFEXISTS))
1686 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "open", expfn);
1688 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_DEBUG, "open", expfn);
1689 GNUNET_free (expfn);
1696 if (GNUNET_DISK_OPEN_READWRITE == (flags & GNUNET_DISK_OPEN_READWRITE))
1697 access = FILE_READ_DATA | FILE_WRITE_DATA;
1698 else if (flags & GNUNET_DISK_OPEN_READ)
1699 access = FILE_READ_DATA;
1700 else if (flags & GNUNET_DISK_OPEN_WRITE)
1701 access = FILE_WRITE_DATA;
1703 if (flags & GNUNET_DISK_OPEN_FAILIFEXISTS)
1707 else if (flags & GNUNET_DISK_OPEN_CREATE)
1709 (void) GNUNET_DISK_directory_create_for_file (expfn);
1710 if (flags & GNUNET_DISK_OPEN_TRUNCATE)
1711 disp = CREATE_ALWAYS;
1715 else if (flags & GNUNET_DISK_OPEN_TRUNCATE)
1717 disp = TRUNCATE_EXISTING;
1721 disp = OPEN_EXISTING;
1724 if (ERROR_SUCCESS == plibc_conv_to_win_pathwconv(expfn, wexpfn))
1725 h = CreateFileW (wexpfn, access,
1726 FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE, NULL,
1727 disp, FILE_ATTRIBUTE_NORMAL, NULL);
1729 h = INVALID_HANDLE_VALUE;
1730 if (h == INVALID_HANDLE_VALUE)
1733 SetErrnoFromWinError (GetLastError ());
1735 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_INFO, "open", expfn);
1736 GNUNET_free (expfn);
1741 if (flags & GNUNET_DISK_OPEN_APPEND)
1742 if (SetFilePointer (h, 0, 0, FILE_END) == INVALID_SET_FILE_POINTER)
1744 SetErrnoFromWinError (GetLastError ());
1745 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "SetFilePointer", expfn);
1747 GNUNET_free (expfn);
1752 ret = GNUNET_malloc (sizeof (struct GNUNET_DISK_FileHandle));
1755 ret->type = GNUNET_DISK_HANLDE_TYPE_FILE;
1759 GNUNET_free (expfn);
1765 * Close an open file
1766 * @param h file handle
1767 * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
1770 GNUNET_DISK_file_close (struct GNUNET_DISK_FileHandle *h)
1776 return GNUNET_SYSERR;
1782 if (!CloseHandle (h->h))
1784 SetErrnoFromWinError (GetLastError ());
1785 LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING, "close");
1786 ret = GNUNET_SYSERR;
1788 if (h->oOverlapRead)
1790 if (!CloseHandle (h->oOverlapRead->hEvent))
1792 SetErrnoFromWinError (GetLastError ());
1793 LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING, "close");
1794 ret = GNUNET_SYSERR;
1796 GNUNET_free (h->oOverlapRead);
1798 if (h->oOverlapWrite)
1800 if (!CloseHandle (h->oOverlapWrite->hEvent))
1802 SetErrnoFromWinError (GetLastError ());
1803 LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING, "close");
1804 ret = GNUNET_SYSERR;
1806 GNUNET_free (h->oOverlapWrite);
1809 if (close (h->fd) != 0)
1811 LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING, "close");
1812 ret = GNUNET_SYSERR;
1821 * Get a GNUnet file handle from a W32 handle.
1823 * @param handle native handle
1824 * @return GNUnet file handle corresponding to the W32 handle
1826 struct GNUNET_DISK_FileHandle *
1827 GNUNET_DISK_get_handle_from_w32_handle (HANDLE osfh)
1829 struct GNUNET_DISK_FileHandle *fh;
1832 enum GNUNET_FILE_Type ftype;
1834 dwret = GetFileType (osfh);
1837 case FILE_TYPE_DISK:
1838 ftype = GNUNET_DISK_HANLDE_TYPE_FILE;
1840 case FILE_TYPE_PIPE:
1841 ftype = GNUNET_DISK_HANLDE_TYPE_PIPE;
1847 fh = GNUNET_malloc (sizeof (struct GNUNET_DISK_FileHandle));
1851 if (ftype == GNUNET_DISK_HANLDE_TYPE_PIPE)
1854 * Note that we can't make it overlapped if it isn't already.
1855 * (ReOpenFile() is only available in 2003/Vista).
1856 * The process that opened this file in the first place (usually a parent
1857 * process, if this is stdin/stdout/stderr) must make it overlapped,
1858 * otherwise we're screwed, as selecting on non-overlapped handle
1861 fh->oOverlapRead = GNUNET_malloc (sizeof (OVERLAPPED));
1862 fh->oOverlapWrite = GNUNET_malloc (sizeof (OVERLAPPED));
1863 fh->oOverlapRead->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
1864 fh->oOverlapWrite->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
1872 * Get a handle from a native integer FD.
1874 * @param fno native integer file descriptor
1875 * @return file handle corresponding to the descriptor, NULL on error
1877 struct GNUNET_DISK_FileHandle *
1878 GNUNET_DISK_get_handle_from_int_fd (int fno)
1880 struct GNUNET_DISK_FileHandle *fh;
1882 if ( (((off_t) -1) == lseek (fno, 0, SEEK_CUR)) &&
1884 return NULL; /* invalid FD */
1887 fh = GNUNET_malloc (sizeof (struct GNUNET_DISK_FileHandle));
1893 osfh = _get_osfhandle (fno);
1894 if (INVALID_HANDLE_VALUE == (HANDLE) osfh)
1897 fh = GNUNET_DISK_get_handle_from_w32_handle ((HANDLE) osfh);
1905 * Get a handle from a native streaming FD.
1907 * @param fd native streaming file descriptor
1908 * @return file handle corresponding to the descriptor
1910 struct GNUNET_DISK_FileHandle *
1911 GNUNET_DISK_get_handle_from_native (FILE *fd)
1919 return GNUNET_DISK_get_handle_from_int_fd (fno);
1924 * Construct full path to a file inside of the private
1925 * directory used by GNUnet. Also creates the corresponding
1926 * directory. If the resulting name is supposed to be
1927 * a directory, end the last argument in '/' (or pass
1928 * DIR_SEPARATOR_STR as the last argument before NULL).
1930 * @param cfg configuration to use (determines HOME)
1931 * @param serviceName name of the service
1932 * @param ... is NULL-terminated list of
1933 * path components to append to the
1934 * private directory name.
1935 * @return the constructed filename
1938 GNUNET_DISK_get_home_filename (const struct GNUNET_CONFIGURATION_Handle *cfg,
1939 const char *serviceName, ...)
1945 unsigned int needed;
1948 GNUNET_CONFIGURATION_get_value_filename (cfg, serviceName, "HOME", &pfx))
1952 LOG (GNUNET_ERROR_TYPE_WARNING,
1953 _("No `%s' specified for service `%s' in configuration.\n"), "HOME",
1957 needed = strlen (pfx) + 2;
1958 if ((pfx[strlen (pfx) - 1] != '/') && (pfx[strlen (pfx) - 1] != '\\'))
1960 va_start (ap, serviceName);
1963 c = va_arg (ap, const char *);
1967 needed += strlen (c);
1968 if ((c[strlen (c) - 1] != '/') && (c[strlen (c) - 1] != '\\'))
1972 ret = GNUNET_malloc (needed);
1975 va_start (ap, serviceName);
1978 c = va_arg (ap, const char *);
1982 if ((c[strlen (c) - 1] != '/') && (c[strlen (c) - 1] != '\\'))
1983 strcat (ret, DIR_SEPARATOR_STR);
1987 if ((ret[strlen (ret) - 1] != '/') && (ret[strlen (ret) - 1] != '\\'))
1988 (void) GNUNET_DISK_directory_create_for_file (ret);
1990 (void) GNUNET_DISK_directory_create (ret);
1996 * Handle for a memory-mapping operation.
1998 struct GNUNET_DISK_MapHandle
2001 * Address where the map is in memory.
2007 * Underlying OS handle.
2012 * Number of bytes mapped.
2020 #define MAP_FAILED ((void *) -1)
2024 * Map a file into memory
2026 * @param h open file handle
2027 * @param m handle to the new mapping
2028 * @param access access specification, GNUNET_DISK_MAP_TYPE_xxx
2029 * @param len size of the mapping
2030 * @return pointer to the mapped memory region, NULL on failure
2033 GNUNET_DISK_file_map (const struct GNUNET_DISK_FileHandle *h,
2034 struct GNUNET_DISK_MapHandle **m,
2035 enum GNUNET_DISK_MapType access, size_t len)
2044 DWORD mapAccess, protect;
2046 if ((access & GNUNET_DISK_MAP_TYPE_READ) &&
2047 (access & GNUNET_DISK_MAP_TYPE_WRITE))
2049 protect = PAGE_READWRITE;
2050 mapAccess = FILE_MAP_ALL_ACCESS;
2052 else if (access & GNUNET_DISK_MAP_TYPE_READ)
2054 protect = PAGE_READONLY;
2055 mapAccess = FILE_MAP_READ;
2057 else if (access & GNUNET_DISK_MAP_TYPE_WRITE)
2059 protect = PAGE_READWRITE;
2060 mapAccess = FILE_MAP_WRITE;
2068 *m = GNUNET_malloc (sizeof (struct GNUNET_DISK_MapHandle));
2069 (*m)->h = CreateFileMapping (h->h, NULL, protect, 0, 0, NULL);
2070 if ((*m)->h == INVALID_HANDLE_VALUE)
2072 SetErrnoFromWinError (GetLastError ());
2077 (*m)->addr = MapViewOfFile ((*m)->h, mapAccess, 0, 0, len);
2080 SetErrnoFromWinError (GetLastError ());
2081 CloseHandle ((*m)->h);
2090 if (access & GNUNET_DISK_MAP_TYPE_READ)
2092 if (access & GNUNET_DISK_MAP_TYPE_WRITE)
2094 *m = GNUNET_malloc (sizeof (struct GNUNET_DISK_MapHandle));
2095 (*m)->addr = mmap (NULL, len, prot, MAP_SHARED, h->fd, 0);
2096 GNUNET_assert (NULL != (*m)->addr);
2097 if (MAP_FAILED == (*m)->addr)
2109 * @param h mapping handle
2110 * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
2113 GNUNET_DISK_file_unmap (struct GNUNET_DISK_MapHandle *h)
2120 return GNUNET_SYSERR;
2124 ret = UnmapViewOfFile (h->addr) ? GNUNET_OK : GNUNET_SYSERR;
2125 if (ret != GNUNET_OK)
2126 SetErrnoFromWinError (GetLastError ());
2127 if (!CloseHandle (h->h) && (ret == GNUNET_OK))
2129 ret = GNUNET_SYSERR;
2130 SetErrnoFromWinError (GetLastError ());
2133 ret = munmap (h->addr, h->len) != -1 ? GNUNET_OK : GNUNET_SYSERR;
2141 * Write file changes to disk
2142 * @param h handle to an open file
2143 * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
2146 GNUNET_DISK_file_sync (const struct GNUNET_DISK_FileHandle *h)
2151 return GNUNET_SYSERR;
2157 ret = FlushFileBuffers (h->h) ? GNUNET_OK : GNUNET_SYSERR;
2158 if (ret != GNUNET_OK)
2159 SetErrnoFromWinError (GetLastError ());
2161 #elif defined(FREEBSD) || defined(OPENBSD) || defined(DARWIN)
2162 return fsync (h->fd) == -1 ? GNUNET_SYSERR : GNUNET_OK;
2164 return fdatasync (h->fd) == -1 ? GNUNET_SYSERR : GNUNET_OK;
2171 #define PIPE_BUF 512
2173 /* Copyright Bob Byrnes <byrnes <at> curl.com>
2174 http://permalink.gmane.org/gmane.os.cygwin.patches/2121
2176 /* Create a pipe, and return handles to the read and write ends,
2177 just like CreatePipe, but ensure that the write end permits
2178 FILE_READ_ATTRIBUTES access, on later versions of win32 where
2179 this is supported. This access is needed by NtQueryInformationFile,
2180 which is used to implement select and nonblocking writes.
2181 Note that the return value is either NO_ERROR or GetLastError,
2182 unlike CreatePipe, which returns a bool for success or failure. */
2184 create_selectable_pipe (PHANDLE read_pipe_ptr, PHANDLE write_pipe_ptr,
2185 LPSECURITY_ATTRIBUTES sa_ptr, DWORD psize,
2186 DWORD dwReadMode, DWORD dwWriteMode)
2188 /* Default to error. */
2189 *read_pipe_ptr = *write_pipe_ptr = INVALID_HANDLE_VALUE;
2194 /* Ensure that there is enough pipe buffer space for atomic writes. */
2195 if (psize < PIPE_BUF)
2198 char pipename[MAX_PATH];
2200 /* Retry CreateNamedPipe as long as the pipe name is in use.
2201 * Retrying will probably never be necessary, but we want
2202 * to be as robust as possible. */
2205 static volatile LONG pipe_unique_id;
2207 snprintf (pipename, sizeof pipename, "\\\\.\\pipe\\gnunet-%d-%ld",
2208 getpid (), InterlockedIncrement ((LONG *) & pipe_unique_id));
2209 LOG (GNUNET_ERROR_TYPE_DEBUG, "CreateNamedPipe: name = %s, size = %lu\n",
2211 /* Use CreateNamedPipe instead of CreatePipe, because the latter
2212 * returns a write handle that does not permit FILE_READ_ATTRIBUTES
2213 * access, on versions of win32 earlier than WinXP SP2.
2214 * CreatePipe also stupidly creates a full duplex pipe, which is
2215 * a waste, since only a single direction is actually used.
2216 * It's important to only allow a single instance, to ensure that
2217 * the pipe was not created earlier by some other process, even if
2218 * the pid has been reused. */
2219 read_pipe = CreateNamedPipeA (pipename, PIPE_ACCESS_INBOUND | FILE_FLAG_FIRST_PIPE_INSTANCE | dwReadMode, PIPE_TYPE_BYTE | PIPE_READMODE_BYTE, 1, /* max instances */
2220 psize, /* output buffer size */
2221 psize, /* input buffer size */
2222 NMPWAIT_USE_DEFAULT_WAIT, sa_ptr);
2224 if (read_pipe != INVALID_HANDLE_VALUE)
2226 LOG (GNUNET_ERROR_TYPE_DEBUG, "pipe read handle = %p\n", read_pipe);
2230 DWORD err = GetLastError ();
2234 case ERROR_PIPE_BUSY:
2235 /* The pipe is already open with compatible parameters.
2236 * Pick a new name and retry. */
2237 LOG (GNUNET_ERROR_TYPE_DEBUG, "pipe busy, retrying\n");
2239 case ERROR_ACCESS_DENIED:
2240 /* The pipe is already open with incompatible parameters.
2241 * Pick a new name and retry. */
2242 LOG (GNUNET_ERROR_TYPE_DEBUG, "pipe access denied, retrying\n");
2244 case ERROR_CALL_NOT_IMPLEMENTED:
2245 /* We are on an older Win9x platform without named pipes.
2246 * Return an anonymous pipe as the best approximation. */
2247 LOG (GNUNET_ERROR_TYPE_DEBUG,
2248 "CreateNamedPipe not implemented, resorting to "
2249 "CreatePipe: size = %lu\n", psize);
2250 if (CreatePipe (read_pipe_ptr, write_pipe_ptr, sa_ptr, psize))
2252 LOG (GNUNET_ERROR_TYPE_DEBUG, "pipe read handle = %p, write handle = %p\n",
2257 err = GetLastError ();
2258 LOG (GNUNET_ERROR_TYPE_ERROR, "CreatePipe failed: %d\n", err);
2261 LOG (GNUNET_ERROR_TYPE_ERROR, "CreateNamedPipe failed: %d\n", err);
2266 LOG (GNUNET_ERROR_TYPE_DEBUG, "CreateFile: name = %s\n", pipename);
2268 /* Open the named pipe for writing.
2269 * Be sure to permit FILE_READ_ATTRIBUTES access. */
2270 write_pipe = CreateFileA (pipename, GENERIC_WRITE | FILE_READ_ATTRIBUTES, 0, /* share mode */
2271 sa_ptr, OPEN_EXISTING, dwWriteMode, /* flags and attributes */
2272 0); /* handle to template file */
2274 if (write_pipe == INVALID_HANDLE_VALUE)
2277 DWORD err = GetLastError ();
2279 LOG (GNUNET_ERROR_TYPE_DEBUG, "CreateFile failed: %d\n", err);
2280 CloseHandle (read_pipe);
2283 LOG (GNUNET_ERROR_TYPE_DEBUG, "pipe write handle = %p\n", write_pipe);
2285 *read_pipe_ptr = read_pipe;
2286 *write_pipe_ptr = write_pipe;
2293 * Creates an interprocess channel
2295 * @param blocking_read creates an asynchronous pipe for reading if set to GNUNET_NO
2296 * @param blocking_write creates an asynchronous pipe for writing if set to GNUNET_NO
2297 * @param inherit_read inherit the parent processes stdin (only for windows)
2298 * @param inherit_write inherit the parent processes stdout (only for windows)
2299 * @return handle to the new pipe, NULL on error
2301 struct GNUNET_DISK_PipeHandle *
2302 GNUNET_DISK_pipe (int blocking_read, int blocking_write, int inherit_read, int inherit_write)
2313 LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR, "pipe");
2317 return GNUNET_DISK_pipe_from_fd (blocking_read,
2321 struct GNUNET_DISK_PipeHandle *p;
2326 p = GNUNET_malloc (sizeof (struct GNUNET_DISK_PipeHandle));
2327 p->fd[0] = GNUNET_malloc (sizeof (struct GNUNET_DISK_FileHandle));
2328 p->fd[1] = GNUNET_malloc (sizeof (struct GNUNET_DISK_FileHandle));
2330 /* All pipes are overlapped. If you want them to block - just
2331 * call WriteFile() and ReadFile() with NULL overlapped pointer.
2332 * NOTE: calling with NULL overlapped pointer works only
2333 * for pipes, and doesn't seem to be a documented feature.
2334 * It will NOT work for files, because overlapped files need
2335 * to read offsets from the overlapped structure, regardless.
2336 * Pipes are not seekable, and need no offsets, which is
2337 * probably why it works for them.
2340 create_selectable_pipe (&p->fd[0]->h, &p->fd[1]->h, NULL, 0,
2341 FILE_FLAG_OVERLAPPED,
2342 FILE_FLAG_OVERLAPPED);
2345 SetErrnoFromWinError (GetLastError ());
2347 GNUNET_free (p->fd[0]);
2348 GNUNET_free (p->fd[1]);
2353 if (!DuplicateHandle
2354 (GetCurrentProcess (), p->fd[0]->h, GetCurrentProcess (), &tmp_handle, 0,
2355 inherit_read == GNUNET_YES ? TRUE : FALSE, DUPLICATE_SAME_ACCESS))
2357 SetErrnoFromWinError (GetLastError ());
2359 CloseHandle (p->fd[0]->h);
2360 CloseHandle (p->fd[1]->h);
2361 GNUNET_free (p->fd[0]);
2362 GNUNET_free (p->fd[1]);
2367 CloseHandle (p->fd[0]->h);
2368 p->fd[0]->h = tmp_handle;
2370 if (!DuplicateHandle
2371 (GetCurrentProcess (), p->fd[1]->h, GetCurrentProcess (), &tmp_handle, 0,
2372 inherit_write == GNUNET_YES ? TRUE : FALSE, DUPLICATE_SAME_ACCESS))
2374 SetErrnoFromWinError (GetLastError ());
2376 CloseHandle (p->fd[0]->h);
2377 CloseHandle (p->fd[1]->h);
2378 GNUNET_free (p->fd[0]);
2379 GNUNET_free (p->fd[1]);
2384 CloseHandle (p->fd[1]->h);
2385 p->fd[1]->h = tmp_handle;
2387 p->fd[0]->type = GNUNET_DISK_HANLDE_TYPE_PIPE;
2388 p->fd[1]->type = GNUNET_DISK_HANLDE_TYPE_PIPE;
2390 p->fd[0]->oOverlapRead = GNUNET_malloc (sizeof (OVERLAPPED));
2391 p->fd[0]->oOverlapWrite = GNUNET_malloc (sizeof (OVERLAPPED));
2392 p->fd[1]->oOverlapRead = GNUNET_malloc (sizeof (OVERLAPPED));
2393 p->fd[1]->oOverlapWrite = GNUNET_malloc (sizeof (OVERLAPPED));
2395 p->fd[0]->oOverlapRead->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2396 p->fd[0]->oOverlapWrite->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2398 p->fd[1]->oOverlapRead->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2399 p->fd[1]->oOverlapWrite->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2407 * Creates a pipe object from a couple of file descriptors.
2408 * Useful for wrapping existing pipe FDs.
2410 * @param blocking_read creates an asynchronous pipe for reading if set to GNUNET_NO
2411 * @param blocking_write creates an asynchronous pipe for writing if set to GNUNET_NO
2412 * @param fd an array of two fd values. One of them may be -1 for read-only or write-only pipes
2414 * @return handle to the new pipe, NULL on error
2416 struct GNUNET_DISK_PipeHandle *
2417 GNUNET_DISK_pipe_from_fd (int blocking_read, int blocking_write, int fd[2])
2419 struct GNUNET_DISK_PipeHandle *p;
2421 p = GNUNET_malloc (sizeof (struct GNUNET_DISK_PipeHandle));
2426 int eno = 0; /* make gcc happy */
2431 p->fd[0] = GNUNET_malloc (sizeof (struct GNUNET_DISK_FileHandle));
2432 p->fd[0]->fd = fd[0];
2435 flags = fcntl (fd[0], F_GETFL);
2436 flags |= O_NONBLOCK;
2437 if (0 > fcntl (fd[0], F_SETFL, flags))
2443 flags = fcntl (fd[0], F_GETFD);
2444 flags |= FD_CLOEXEC;
2445 if (0 > fcntl (fd[0], F_SETFD, flags))
2454 p->fd[1] = GNUNET_malloc (sizeof (struct GNUNET_DISK_FileHandle));
2455 p->fd[1]->fd = fd[1];
2456 if (!blocking_write)
2458 flags = fcntl (fd[1], F_GETFL);
2459 flags |= O_NONBLOCK;
2460 if (0 > fcntl (fd[1], F_SETFL, flags))
2466 flags = fcntl (fd[1], F_GETFD);
2467 flags |= FD_CLOEXEC;
2468 if (0 > fcntl (fd[1], F_SETFD, flags))
2477 LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR, "fcntl");
2478 if (p->fd[0]->fd >= 0)
2479 GNUNET_break (0 == close (p->fd[0]->fd));
2480 if (p->fd[1]->fd >= 0)
2481 GNUNET_break (0 == close (p->fd[1]->fd));
2482 GNUNET_free_non_null (p->fd[0]);
2483 GNUNET_free_non_null (p->fd[1]);
2491 p->fd[0] = GNUNET_malloc (sizeof (struct GNUNET_DISK_FileHandle));
2492 p->fd[0]->h = (HANDLE) _get_osfhandle (fd[0]);
2493 if (p->fd[0]->h != INVALID_HANDLE_VALUE)
2495 p->fd[0]->type = GNUNET_DISK_HANLDE_TYPE_PIPE;
2496 p->fd[0]->oOverlapRead = GNUNET_malloc (sizeof (OVERLAPPED));
2497 p->fd[0]->oOverlapWrite = GNUNET_malloc (sizeof (OVERLAPPED));
2498 p->fd[0]->oOverlapRead->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2499 p->fd[0]->oOverlapWrite->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2503 GNUNET_free (p->fd[0]);
2509 p->fd[1] = GNUNET_malloc (sizeof (struct GNUNET_DISK_FileHandle));
2510 p->fd[1]->h = (HANDLE) _get_osfhandle (fd[1]);
2511 if (p->fd[1]->h != INVALID_HANDLE_VALUE)
2513 p->fd[1]->type = GNUNET_DISK_HANLDE_TYPE_PIPE;
2514 p->fd[1]->oOverlapRead = GNUNET_malloc (sizeof (OVERLAPPED));
2515 p->fd[1]->oOverlapWrite = GNUNET_malloc (sizeof (OVERLAPPED));
2516 p->fd[1]->oOverlapRead->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2517 p->fd[1]->oOverlapWrite->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2521 GNUNET_free (p->fd[1]);
2532 * Closes an interprocess channel
2534 * @param p pipe to close
2535 * @param end which end of the pipe to close
2536 * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
2539 GNUNET_DISK_pipe_close_end (struct GNUNET_DISK_PipeHandle *p,
2540 enum GNUNET_DISK_PipeEnd end)
2542 int ret = GNUNET_OK;
2544 if (end == GNUNET_DISK_PIPE_END_READ)
2548 ret = GNUNET_DISK_file_close (p->fd[0]);
2552 else if (end == GNUNET_DISK_PIPE_END_WRITE)
2556 ret = GNUNET_DISK_file_close (p->fd[1]);
2565 * Detaches one of the ends from the pipe.
2566 * Detached end is a fully-functional FileHandle, it will
2567 * not be affected by anything you do with the pipe afterwards.
2568 * Each end of a pipe can only be detched from it once (i.e.
2569 * it is not duplicated).
2571 * @param p pipe to detach an end from
2572 * @param end which end of the pipe to detach
2573 * @return Detached end on success, NULL on failure
2574 * (or if that end is not present or is closed).
2576 struct GNUNET_DISK_FileHandle *
2577 GNUNET_DISK_pipe_detach_end (struct GNUNET_DISK_PipeHandle *p,
2578 enum GNUNET_DISK_PipeEnd end)
2580 struct GNUNET_DISK_FileHandle *ret = NULL;
2582 if (end == GNUNET_DISK_PIPE_END_READ)
2590 else if (end == GNUNET_DISK_PIPE_END_WRITE)
2604 * Closes an interprocess channel
2606 * @param p pipe to close
2607 * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
2610 GNUNET_DISK_pipe_close (struct GNUNET_DISK_PipeHandle *p)
2612 int ret = GNUNET_OK;
2615 int write_end_close;
2616 int read_end_close_errno;
2617 int write_end_close_errno;
2619 read_end_close = GNUNET_DISK_pipe_close_end (p, GNUNET_DISK_PIPE_END_READ);
2620 read_end_close_errno = errno;
2621 write_end_close = GNUNET_DISK_pipe_close_end (p, GNUNET_DISK_PIPE_END_WRITE);
2622 write_end_close_errno = errno;
2625 if (GNUNET_OK != read_end_close)
2627 errno = read_end_close_errno;
2628 ret = read_end_close;
2630 else if (GNUNET_OK != write_end_close)
2632 errno = write_end_close_errno;
2633 ret = write_end_close;
2641 * Get the handle to a particular pipe end
2644 * @param n end to access
2645 * @return handle for the respective end
2647 const struct GNUNET_DISK_FileHandle *
2648 GNUNET_DISK_pipe_handle (const struct GNUNET_DISK_PipeHandle *p,
2649 enum GNUNET_DISK_PipeEnd n)
2653 case GNUNET_DISK_PIPE_END_READ:
2654 case GNUNET_DISK_PIPE_END_WRITE:
2664 * Retrieve OS file handle
2666 * @param fh GNUnet file descriptor
2667 * @param dst destination buffer
2668 * @param dst_len length of dst
2669 * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
2672 GNUNET_DISK_internal_file_handle_ (const struct GNUNET_DISK_FileHandle *fh,
2673 void *dst, size_t dst_len)
2676 if (dst_len < sizeof (HANDLE))
2677 return GNUNET_SYSERR;
2678 *((HANDLE *) dst) = fh->h;
2680 if (dst_len < sizeof (int))
2681 return GNUNET_SYSERR;
2682 *((int *) dst) = fh->fd;