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"
657 * @param dir the directory to create
658 * @returns GNUNET_OK on success, GNUNET_SYSERR on failure
661 GNUNET_DISK_directory_create (const char *dir)
669 rdir = GNUNET_STRINGS_filename_expand (dir);
671 return GNUNET_SYSERR;
675 pos = 1; /* skip heading '/' */
677 /* Local or Network path? */
678 if (strncmp (rdir, "\\\\", 2) == 0)
683 if (rdir[pos] == '\\')
693 pos = 3; /* strlen("C:\\") */
696 /* Check which low level directories already exist */
698 rdir[len] = DIR_SEPARATOR;
701 if (DIR_SEPARATOR == rdir[pos2])
704 ret = GNUNET_DISK_directory_test (rdir, GNUNET_NO);
705 if (GNUNET_NO == ret)
708 return GNUNET_SYSERR;
710 rdir[pos2] = DIR_SEPARATOR;
711 if (GNUNET_YES == ret)
722 /* Start creating directories */
725 if ((rdir[pos] == DIR_SEPARATOR) || (pos == len))
728 ret = GNUNET_DISK_directory_test (rdir, GNUNET_NO);
729 if (GNUNET_NO == ret)
732 return GNUNET_SYSERR;
734 if (GNUNET_SYSERR == ret)
737 ret = mkdir (rdir, S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH); /* 755 */
739 wchar_t wrdir[MAX_PATH + 1];
740 if (ERROR_SUCCESS == plibc_conv_to_win_pathwconv(rdir, wrdir))
741 ret = !CreateDirectoryW (wrdir, NULL);
745 if ((ret != 0) && (errno != EEXIST))
747 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_ERROR, "mkdir", rdir);
749 return GNUNET_SYSERR;
752 rdir[pos] = DIR_SEPARATOR;
762 * Create the directory structure for storing
765 * @param filename name of a file in the directory
766 * @returns GNUNET_OK on success,
767 * GNUNET_SYSERR on failure,
768 * GNUNET_NO if the directory
769 * exists but is not writeable for us
772 GNUNET_DISK_directory_create_for_file (const char *filename)
778 rdir = GNUNET_STRINGS_filename_expand (filename);
780 return GNUNET_SYSERR;
782 while ((len > 0) && (rdir[len] != DIR_SEPARATOR))
785 ret = GNUNET_DISK_directory_create (rdir);
786 if ((ret == GNUNET_OK) && (0 != ACCESS (rdir, W_OK)))
794 * Read the contents of a binary file into a buffer.
795 * @param h handle to an open file
796 * @param result the buffer to write the result to
797 * @param len the maximum number of bytes to read
798 * @return the number of bytes read on success, GNUNET_SYSERR on failure
801 GNUNET_DISK_file_read (const struct GNUNET_DISK_FileHandle * h, void *result,
807 return GNUNET_SYSERR;
813 if (h->type != GNUNET_DISK_HANLDE_TYPE_PIPE)
815 if (!ReadFile (h->h, result, len, &bytesRead, NULL))
817 SetErrnoFromWinError (GetLastError ());
818 return GNUNET_SYSERR;
823 if (!ReadFile (h->h, result, len, &bytesRead, h->oOverlapRead))
825 if (GetLastError () != ERROR_IO_PENDING)
827 LOG (GNUNET_ERROR_TYPE_DEBUG, "Error reading from pipe: %u\n", GetLastError ());
828 SetErrnoFromWinError (GetLastError ());
829 return GNUNET_SYSERR;
831 LOG (GNUNET_ERROR_TYPE_DEBUG, "Will get overlapped result\n");
832 GetOverlappedResult (h->h, h->oOverlapRead, &bytesRead, TRUE);
834 LOG (GNUNET_ERROR_TYPE_DEBUG, "Read %u bytes from pipe\n", bytesRead);
838 return read (h->fd, result, len);
844 * Read the contents of a binary file into a buffer.
845 * Guarantees not to block (returns GNUNET_SYSERR and sets errno to EAGAIN
846 * when no data can be read).
848 * @param h handle to an open file
849 * @param result the buffer to write the result to
850 * @param len the maximum number of bytes to read
851 * @return the number of bytes read on success, GNUNET_SYSERR on failure
854 GNUNET_DISK_file_read_non_blocking (const struct GNUNET_DISK_FileHandle * h,
861 return GNUNET_SYSERR;
867 if (h->type != GNUNET_DISK_HANLDE_TYPE_PIPE)
869 if (!ReadFile (h->h, result, len, &bytesRead, NULL))
871 SetErrnoFromWinError (GetLastError ());
872 return GNUNET_SYSERR;
877 if (!ReadFile (h->h, result, len, &bytesRead, h->oOverlapRead))
879 if (GetLastError () != ERROR_IO_PENDING)
881 LOG (GNUNET_ERROR_TYPE_DEBUG, "Error reading from pipe: %u\n", GetLastError ());
882 SetErrnoFromWinError (GetLastError ());
883 return GNUNET_SYSERR;
887 LOG (GNUNET_ERROR_TYPE_DEBUG,
888 "ReadFile() queued a read, cancelling\n");
891 return GNUNET_SYSERR;
894 LOG (GNUNET_ERROR_TYPE_DEBUG, "Read %u bytes\n", bytesRead);
901 /* set to non-blocking, read, then set back */
902 flags = fcntl (h->fd, F_GETFL);
903 if (0 == (flags & O_NONBLOCK))
904 (void) fcntl (h->fd, F_SETFL, flags | O_NONBLOCK);
905 ret = read (h->fd, result, len);
906 if (0 == (flags & O_NONBLOCK))
909 (void) fcntl (h->fd, F_SETFL, flags);
918 * Read the contents of a binary file into a buffer.
920 * @param fn file name
921 * @param result the buffer to write the result to
922 * @param len the maximum number of bytes to read
923 * @return number of bytes read, GNUNET_SYSERR on failure
926 GNUNET_DISK_fn_read (const char *fn, void *result, size_t len)
928 struct GNUNET_DISK_FileHandle *fh;
931 fh = GNUNET_DISK_file_open (fn, GNUNET_DISK_OPEN_READ, GNUNET_DISK_PERM_NONE);
933 return GNUNET_SYSERR;
934 ret = GNUNET_DISK_file_read (fh, result, len);
935 GNUNET_assert (GNUNET_OK == GNUNET_DISK_file_close (fh));
942 * Write a buffer to a file.
943 * @param h handle to open file
944 * @param buffer the data to write
945 * @param n number of bytes to write
946 * @return number of bytes written on success, GNUNET_SYSERR on error
949 GNUNET_DISK_file_write (const struct GNUNET_DISK_FileHandle * h,
950 const void *buffer, size_t n)
955 return GNUNET_SYSERR;
961 if (h->type != GNUNET_DISK_HANLDE_TYPE_PIPE)
963 if (!WriteFile (h->h, buffer, n, &bytesWritten, NULL))
965 SetErrnoFromWinError (GetLastError ());
966 return GNUNET_SYSERR;
971 LOG (GNUNET_ERROR_TYPE_DEBUG, "It is a pipe trying to write %u bytes\n", n);
972 if (!WriteFile (h->h, buffer, n, &bytesWritten, h->oOverlapWrite))
974 if (GetLastError () != ERROR_IO_PENDING)
976 SetErrnoFromWinError (GetLastError ());
977 LOG (GNUNET_ERROR_TYPE_DEBUG, "Error writing to pipe: %u\n",
979 return GNUNET_SYSERR;
981 LOG (GNUNET_ERROR_TYPE_DEBUG, "Will get overlapped result\n");
982 if (!GetOverlappedResult (h->h, h->oOverlapWrite, &bytesWritten, TRUE))
984 SetErrnoFromWinError (GetLastError ());
985 LOG (GNUNET_ERROR_TYPE_DEBUG,
986 "Error getting overlapped result while writing to pipe: %u\n",
988 return GNUNET_SYSERR;
994 if (!GetOverlappedResult (h->h, h->oOverlapWrite, &ovr, TRUE))
996 LOG (GNUNET_ERROR_TYPE_DEBUG,
997 "Error getting control overlapped result while writing to pipe: %u\n",
1002 LOG (GNUNET_ERROR_TYPE_DEBUG,
1003 "Wrote %u bytes (ovr says %u), picking the greatest\n",
1007 if (bytesWritten == 0)
1011 LOG (GNUNET_ERROR_TYPE_DEBUG, "Wrote %u bytes, returning -1 with EAGAIN\n", bytesWritten);
1013 return GNUNET_SYSERR;
1016 LOG (GNUNET_ERROR_TYPE_DEBUG, "Wrote %u bytes\n", bytesWritten);
1018 return bytesWritten;
1020 return write (h->fd, buffer, n);
1026 * Write a buffer to a file, blocking, if necessary.
1027 * @param h handle to open file
1028 * @param buffer the data to write
1029 * @param n number of bytes to write
1030 * @return number of bytes written on success, GNUNET_SYSERR on error
1033 GNUNET_DISK_file_write_blocking (const struct GNUNET_DISK_FileHandle * h,
1034 const void *buffer, size_t n)
1039 return GNUNET_SYSERR;
1044 /* We do a non-overlapped write, which is as blocking as it gets */
1045 LOG (GNUNET_ERROR_TYPE_DEBUG, "Writing %u bytes\n", n);
1046 if (!WriteFile (h->h, buffer, n, &bytesWritten, NULL))
1048 SetErrnoFromWinError (GetLastError ());
1049 LOG (GNUNET_ERROR_TYPE_DEBUG, "Error writing to pipe: %u\n",
1051 return GNUNET_SYSERR;
1053 if (bytesWritten == 0 && n > 0)
1055 LOG (GNUNET_ERROR_TYPE_DEBUG, "Waiting for pipe to clean\n");
1056 WaitForSingleObject (h->h, INFINITE);
1057 if (!WriteFile (h->h, buffer, n, &bytesWritten, NULL))
1059 SetErrnoFromWinError (GetLastError ());
1060 LOG (GNUNET_ERROR_TYPE_DEBUG, "Error writing to pipe: %u\n",
1062 return GNUNET_SYSERR;
1065 LOG (GNUNET_ERROR_TYPE_DEBUG, "Wrote %u bytes\n", bytesWritten);
1066 return bytesWritten;
1071 /* set to blocking, write, then set back */
1072 flags = fcntl (h->fd, F_GETFL);
1073 if (0 != (flags & O_NONBLOCK))
1074 (void) fcntl (h->fd, F_SETFL, flags - O_NONBLOCK);
1075 ret = write (h->fd, buffer, n);
1076 if (0 == (flags & O_NONBLOCK))
1077 (void) fcntl (h->fd, F_SETFL, flags);
1084 * Write a buffer to a file. If the file is longer than the
1085 * number of bytes that will be written, it will be truncated.
1087 * @param fn file name
1088 * @param buffer the data to write
1089 * @param n number of bytes to write
1090 * @param mode file permissions
1091 * @return number of bytes written on success, GNUNET_SYSERR on error
1094 GNUNET_DISK_fn_write (const char *fn, const void *buffer, size_t n,
1095 enum GNUNET_DISK_AccessPermissions mode)
1097 struct GNUNET_DISK_FileHandle *fh;
1100 fh = GNUNET_DISK_file_open (fn,
1101 GNUNET_DISK_OPEN_WRITE | GNUNET_DISK_OPEN_TRUNCATE
1102 | GNUNET_DISK_OPEN_CREATE, mode);
1104 return GNUNET_SYSERR;
1105 ret = GNUNET_DISK_file_write (fh, buffer, n);
1106 GNUNET_assert (GNUNET_OK == GNUNET_DISK_file_close (fh));
1112 * Scan a directory for files.
1114 * @param dirName the name of the directory
1115 * @param callback the method to call for each file,
1116 * can be NULL, in that case, we only count
1117 * @param callback_cls closure for callback
1118 * @return the number of files found, GNUNET_SYSERR on error or
1119 * ieration aborted by callback returning GNUNET_SYSERR
1122 GNUNET_DISK_directory_scan (const char *dirName,
1123 GNUNET_FileNameCallback callback,
1127 struct dirent *finfo;
1133 unsigned int name_len;
1134 unsigned int n_size;
1136 GNUNET_assert (dirName != NULL);
1137 dname = GNUNET_STRINGS_filename_expand (dirName);
1139 return GNUNET_SYSERR;
1140 while ((strlen (dname) > 0) && (dname[strlen (dname) - 1] == DIR_SEPARATOR))
1141 dname[strlen (dname) - 1] = '\0';
1142 if (0 != STAT (dname, &istat))
1144 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "stat", dname);
1145 GNUNET_free (dname);
1146 return GNUNET_SYSERR;
1148 if (!S_ISDIR (istat.st_mode))
1150 LOG (GNUNET_ERROR_TYPE_WARNING, _("Expected `%s' to be a directory!\n"),
1152 GNUNET_free (dname);
1153 return GNUNET_SYSERR;
1156 dinfo = OPENDIR (dname);
1157 if ((errno == EACCES) || (dinfo == NULL))
1159 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "opendir", dname);
1162 GNUNET_free (dname);
1163 return GNUNET_SYSERR;
1166 n_size = strlen (dname) + name_len + 2;
1167 name = GNUNET_malloc (n_size);
1168 while ((finfo = READDIR (dinfo)) != NULL)
1170 if ((0 == strcmp (finfo->d_name, ".")) ||
1171 (0 == strcmp (finfo->d_name, "..")))
1173 if (callback != NULL)
1175 if (name_len < strlen (finfo->d_name))
1178 name_len = strlen (finfo->d_name);
1179 n_size = strlen (dname) + name_len + 2;
1180 name = GNUNET_malloc (n_size);
1182 /* dname can end in "/" only if dname == "/";
1183 * if dname does not end in "/", we need to add
1184 * a "/" (otherwise, we must not!) */
1185 GNUNET_snprintf (name, n_size, "%s%s%s", dname,
1186 (strcmp (dname, DIR_SEPARATOR_STR) ==
1187 0) ? "" : DIR_SEPARATOR_STR, finfo->d_name);
1188 ret = callback (callback_cls, name);
1189 if (GNUNET_OK != ret)
1193 GNUNET_free (dname);
1194 if (GNUNET_NO == ret)
1196 return GNUNET_SYSERR;
1203 GNUNET_free (dname);
1209 * Opaque handle used for iterating over a directory.
1211 struct GNUNET_DISK_DirectoryIterator
1215 * Function to call on directory entries.
1217 GNUNET_DISK_DirectoryIteratorCallback callback;
1220 * Closure for callback.
1225 * Reference to directory.
1235 * Next filename to process.
1242 enum GNUNET_SCHEDULER_Priority priority;
1248 * Task used by the directory iterator.
1251 directory_iterator_task (void *cls,
1252 const struct GNUNET_SCHEDULER_TaskContext *tc)
1254 struct GNUNET_DISK_DirectoryIterator *iter = cls;
1257 name = iter->next_name;
1258 GNUNET_assert (name != NULL);
1259 iter->next_name = NULL;
1260 iter->callback (iter->callback_cls, iter, name, iter->dirname);
1266 * This function must be called during the DiskIteratorCallback
1267 * (exactly once) to schedule the task to process the next
1268 * filename in the directory (if there is one).
1270 * @param iter opaque handle for the iterator
1271 * @param can set to GNUNET_YES to terminate the iteration early
1272 * @return GNUNET_YES if iteration will continue,
1273 * GNUNET_NO if this was the last entry (and iteration is complete),
1274 * GNUNET_SYSERR if abort was YES
1277 GNUNET_DISK_directory_iterator_next (struct GNUNET_DISK_DirectoryIterator *iter,
1280 struct dirent *finfo;
1282 GNUNET_assert (iter->next_name == NULL);
1283 if (can == GNUNET_YES)
1285 CLOSEDIR (iter->directory);
1286 GNUNET_free (iter->dirname);
1288 return GNUNET_SYSERR;
1290 while (NULL != (finfo = READDIR (iter->directory)))
1292 if ((0 == strcmp (finfo->d_name, ".")) ||
1293 (0 == strcmp (finfo->d_name, "..")))
1295 GNUNET_asprintf (&iter->next_name, "%s%s%s", iter->dirname,
1296 DIR_SEPARATOR_STR, finfo->d_name);
1301 GNUNET_DISK_directory_iterator_next (iter, GNUNET_YES);
1304 GNUNET_SCHEDULER_add_with_priority (iter->priority, &directory_iterator_task,
1311 * Scan a directory for files using the scheduler to run a task for
1312 * each entry. The name of the directory must be expanded first (!).
1313 * If a scheduler does not need to be used, GNUNET_DISK_directory_scan
1314 * may provide a simpler API.
1316 * @param prio priority to use
1317 * @param dirName the name of the directory
1318 * @param callback the method to call for each file
1319 * @param callback_cls closure for callback
1320 * @return GNUNET_YES if directory is not empty and 'callback'
1321 * will be called later, GNUNET_NO otherwise, GNUNET_SYSERR on error.
1324 GNUNET_DISK_directory_iterator_start (enum GNUNET_SCHEDULER_Priority prio,
1325 const char *dirName,
1326 GNUNET_DISK_DirectoryIteratorCallback
1327 callback, void *callback_cls)
1329 struct GNUNET_DISK_DirectoryIterator *di;
1331 di = GNUNET_malloc (sizeof (struct GNUNET_DISK_DirectoryIterator));
1332 di->callback = callback;
1333 di->callback_cls = callback_cls;
1334 di->directory = OPENDIR (dirName);
1335 if (di->directory == NULL)
1338 callback (callback_cls, NULL, NULL, NULL);
1339 return GNUNET_SYSERR;
1341 di->dirname = GNUNET_strdup (dirName);
1342 di->priority = prio;
1343 return GNUNET_DISK_directory_iterator_next (di, GNUNET_NO);
1348 * Function that removes the given directory by calling
1349 * "GNUNET_DISK_directory_remove".
1351 * @param unused not used
1352 * @param fn directory to remove
1356 remove_helper (void *unused, const char *fn)
1358 (void) GNUNET_DISK_directory_remove (fn);
1364 * Remove all files in a directory (rm -rf). Call with
1368 * @param filename the file to remove
1369 * @return GNUNET_OK on success, GNUNET_SYSERR on error
1372 GNUNET_DISK_directory_remove (const char *filename)
1376 if (0 != LSTAT (filename, &istat))
1377 return GNUNET_NO; /* file may not exist... */
1378 (void) CHMOD (filename, S_IWUSR | S_IRUSR | S_IXUSR);
1379 if (UNLINK (filename) == 0)
1381 if ((errno != EISDIR) &&
1382 /* EISDIR is not sufficient in all cases, e.g.
1383 * sticky /tmp directory may result in EPERM on BSD.
1384 * So we also explicitly check "isDirectory" */
1385 (GNUNET_YES != GNUNET_DISK_directory_test (filename, GNUNET_YES)))
1387 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "rmdir", filename);
1388 return GNUNET_SYSERR;
1390 if (GNUNET_SYSERR ==
1391 GNUNET_DISK_directory_scan (filename, &remove_helper, NULL))
1392 return GNUNET_SYSERR;
1393 if (0 != RMDIR (filename))
1395 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "rmdir", filename);
1396 return GNUNET_SYSERR;
1405 * @param src file to copy
1406 * @param dst destination file name
1407 * @return GNUNET_OK on success, GNUNET_SYSERR on error
1410 GNUNET_DISK_file_copy (const char *src, const char *dst)
1416 struct GNUNET_DISK_FileHandle *in;
1417 struct GNUNET_DISK_FileHandle *out;
1419 if (GNUNET_OK != GNUNET_DISK_file_size (src, &size, GNUNET_YES, GNUNET_YES))
1420 return GNUNET_SYSERR;
1422 in = GNUNET_DISK_file_open (src, GNUNET_DISK_OPEN_READ,
1423 GNUNET_DISK_PERM_NONE);
1425 return GNUNET_SYSERR;
1427 GNUNET_DISK_file_open (dst,
1428 GNUNET_DISK_OPEN_WRITE | GNUNET_DISK_OPEN_CREATE |
1429 GNUNET_DISK_OPEN_FAILIFEXISTS,
1430 GNUNET_DISK_PERM_USER_READ |
1431 GNUNET_DISK_PERM_USER_WRITE |
1432 GNUNET_DISK_PERM_GROUP_READ |
1433 GNUNET_DISK_PERM_GROUP_WRITE);
1436 GNUNET_DISK_file_close (in);
1437 return GNUNET_SYSERR;
1439 buf = GNUNET_malloc (COPY_BLK_SIZE);
1442 len = COPY_BLK_SIZE;
1443 if (len > size - pos)
1445 if (len != GNUNET_DISK_file_read (in, buf, len))
1447 if (len != GNUNET_DISK_file_write (out, buf, len))
1452 GNUNET_DISK_file_close (in);
1453 GNUNET_DISK_file_close (out);
1457 GNUNET_DISK_file_close (in);
1458 GNUNET_DISK_file_close (out);
1459 return GNUNET_SYSERR;
1464 * @brief Removes special characters as ':' from a filename.
1465 * @param fn the filename to canonicalize
1468 GNUNET_DISK_filename_canonicalize (char *fn)
1478 if (c == '/' || c == '\\' || c == ':' || c == '*' || c == '?' || c == '"' ||
1479 c == '<' || c == '>' || c == '|')
1491 * @brief Change owner of a file
1493 * @param filename name of file to change the owner of
1494 * @param user name of the new owner
1495 * @return GNUNET_OK on success, GNUNET_SYSERR on failure
1498 GNUNET_DISK_file_change_owner (const char *filename, const char *user)
1503 pws = getpwnam (user);
1506 LOG (GNUNET_ERROR_TYPE_ERROR,
1507 _("Cannot obtain information about user `%s': %s\n"), user,
1509 return GNUNET_SYSERR;
1511 if (0 != chown (filename, pws->pw_uid, pws->pw_gid))
1512 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "chown", filename);
1519 * Lock a part of a file
1520 * @param fh file handle
1521 * @param lockStart absolute position from where to lock
1522 * @param lockEnd absolute position until where to lock
1523 * @param excl GNUNET_YES for an exclusive lock
1524 * @return GNUNET_OK on success, GNUNET_SYSERR on error
1527 GNUNET_DISK_file_lock (struct GNUNET_DISK_FileHandle *fh, OFF_T lockStart,
1528 OFF_T lockEnd, int excl)
1533 return GNUNET_SYSERR;
1539 memset (&fl, 0, sizeof (struct flock));
1540 fl.l_type = excl ? F_WRLCK : F_RDLCK;
1541 fl.l_whence = SEEK_SET;
1542 fl.l_start = lockStart;
1545 return fcntl (fh->fd, F_SETLK, &fl) != 0 ? GNUNET_SYSERR : GNUNET_OK;
1548 OFF_T diff = lockEnd - lockStart;
1549 DWORD diff_low, diff_high;
1550 diff_low = (DWORD) (diff & 0xFFFFFFFF);
1551 diff_high = (DWORD) ((diff >> (sizeof (DWORD) * 8)) & 0xFFFFFFFF);
1553 memset (&o, 0, sizeof (OVERLAPPED));
1554 o.Offset = (DWORD) (lockStart & 0xFFFFFFFF);;
1555 o.OffsetHigh = (DWORD) (((lockStart & ~0xFFFFFFFF) >> (sizeof (DWORD) * 8)) & 0xFFFFFFFF);
1558 (fh->h, (excl ? LOCKFILE_EXCLUSIVE_LOCK : 0) | LOCKFILE_FAIL_IMMEDIATELY,
1559 0, diff_low, diff_high, &o))
1561 SetErrnoFromWinError (GetLastError ());
1562 return GNUNET_SYSERR;
1571 * Unlock a part of a file
1572 * @param fh file handle
1573 * @param unlockStart absolute position from where to unlock
1574 * @param unlockEnd absolute position until where to unlock
1575 * @return GNUNET_OK on success, GNUNET_SYSERR on error
1578 GNUNET_DISK_file_unlock (struct GNUNET_DISK_FileHandle *fh, OFF_T unlockStart,
1584 return GNUNET_SYSERR;
1590 memset (&fl, 0, sizeof (struct flock));
1591 fl.l_type = F_UNLCK;
1592 fl.l_whence = SEEK_SET;
1593 fl.l_start = unlockStart;
1594 fl.l_len = unlockEnd;
1596 return fcntl (fh->fd, F_SETLK, &fl) != 0 ? GNUNET_SYSERR : GNUNET_OK;
1599 OFF_T diff = unlockEnd - unlockStart;
1600 DWORD diff_low, diff_high;
1601 diff_low = (DWORD) (diff & 0xFFFFFFFF);
1602 diff_high = (DWORD) ((diff >> (sizeof (DWORD) * 8)) & 0xFFFFFFFF);
1604 memset (&o, 0, sizeof (OVERLAPPED));
1605 o.Offset = (DWORD) (unlockStart & 0xFFFFFFFF);;
1606 o.OffsetHigh = (DWORD) (((unlockStart & ~0xFFFFFFFF) >> (sizeof (DWORD) * 8)) & 0xFFFFFFFF);
1608 if (!UnlockFileEx (fh->h, 0, diff_low, diff_high, &o))
1610 SetErrnoFromWinError (GetLastError ());
1611 return GNUNET_SYSERR;
1620 * Open a file. Note that the access permissions will only be
1621 * used if a new file is created and if the underlying operating
1622 * system supports the given permissions.
1624 * @param fn file name to be opened
1625 * @param flags opening flags, a combination of GNUNET_DISK_OPEN_xxx bit flags
1626 * @param perm permissions for the newly created file, use
1627 * GNUNET_DISK_PERM_USER_NONE if a file could not be created by this
1628 * call (because of flags)
1629 * @return IO handle on success, NULL on error
1631 struct GNUNET_DISK_FileHandle *
1632 GNUNET_DISK_file_open (const char *fn, enum GNUNET_DISK_OpenFlags flags,
1633 enum GNUNET_DISK_AccessPermissions perm)
1636 struct GNUNET_DISK_FileHandle *ret;
1642 wchar_t wexpfn[MAX_PATH + 1];
1649 expfn = GNUNET_STRINGS_filename_expand (fn);
1654 if (GNUNET_DISK_OPEN_READWRITE == (flags & GNUNET_DISK_OPEN_READWRITE))
1655 oflags = O_RDWR; /* note: O_RDWR is NOT always O_RDONLY | O_WRONLY */
1656 else if (flags & GNUNET_DISK_OPEN_READ)
1658 else if (flags & GNUNET_DISK_OPEN_WRITE)
1663 GNUNET_free (expfn);
1666 if (flags & GNUNET_DISK_OPEN_FAILIFEXISTS)
1667 oflags |= (O_CREAT | O_EXCL);
1668 if (flags & GNUNET_DISK_OPEN_TRUNCATE)
1670 if (flags & GNUNET_DISK_OPEN_APPEND)
1672 if (flags & GNUNET_DISK_OPEN_CREATE)
1674 (void) GNUNET_DISK_directory_create_for_file (expfn);
1676 mode = translate_unix_perms (perm);
1679 fd = open (expfn, oflags
1683 | O_LARGEFILE, mode);
1686 if (0 == (flags & GNUNET_DISK_OPEN_FAILIFEXISTS))
1687 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "open", expfn);
1689 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_DEBUG, "open", expfn);
1690 GNUNET_free (expfn);
1697 if (GNUNET_DISK_OPEN_READWRITE == (flags & GNUNET_DISK_OPEN_READWRITE))
1698 access = FILE_READ_DATA | FILE_WRITE_DATA;
1699 else if (flags & GNUNET_DISK_OPEN_READ)
1700 access = FILE_READ_DATA;
1701 else if (flags & GNUNET_DISK_OPEN_WRITE)
1702 access = FILE_WRITE_DATA;
1704 if (flags & GNUNET_DISK_OPEN_FAILIFEXISTS)
1708 else if (flags & GNUNET_DISK_OPEN_CREATE)
1710 (void) GNUNET_DISK_directory_create_for_file (expfn);
1711 if (flags & GNUNET_DISK_OPEN_TRUNCATE)
1712 disp = CREATE_ALWAYS;
1716 else if (flags & GNUNET_DISK_OPEN_TRUNCATE)
1718 disp = TRUNCATE_EXISTING;
1722 disp = OPEN_EXISTING;
1725 if (ERROR_SUCCESS == plibc_conv_to_win_pathwconv(expfn, wexpfn))
1726 h = CreateFileW (wexpfn, access,
1727 FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE, NULL,
1728 disp, FILE_ATTRIBUTE_NORMAL, NULL);
1730 h = INVALID_HANDLE_VALUE;
1731 if (h == INVALID_HANDLE_VALUE)
1734 SetErrnoFromWinError (GetLastError ());
1736 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_INFO, "open", expfn);
1737 GNUNET_free (expfn);
1742 if (flags & GNUNET_DISK_OPEN_APPEND)
1743 if (SetFilePointer (h, 0, 0, FILE_END) == INVALID_SET_FILE_POINTER)
1745 SetErrnoFromWinError (GetLastError ());
1746 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "SetFilePointer", expfn);
1748 GNUNET_free (expfn);
1753 ret = GNUNET_malloc (sizeof (struct GNUNET_DISK_FileHandle));
1756 ret->type = GNUNET_DISK_HANLDE_TYPE_FILE;
1760 GNUNET_free (expfn);
1766 * Close an open file
1767 * @param h file handle
1768 * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
1771 GNUNET_DISK_file_close (struct GNUNET_DISK_FileHandle *h)
1777 return GNUNET_SYSERR;
1783 if (!CloseHandle (h->h))
1785 SetErrnoFromWinError (GetLastError ());
1786 LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING, "close");
1787 ret = GNUNET_SYSERR;
1789 if (h->oOverlapRead)
1791 if (!CloseHandle (h->oOverlapRead->hEvent))
1793 SetErrnoFromWinError (GetLastError ());
1794 LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING, "close");
1795 ret = GNUNET_SYSERR;
1797 GNUNET_free (h->oOverlapRead);
1799 if (h->oOverlapWrite)
1801 if (!CloseHandle (h->oOverlapWrite->hEvent))
1803 SetErrnoFromWinError (GetLastError ());
1804 LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING, "close");
1805 ret = GNUNET_SYSERR;
1807 GNUNET_free (h->oOverlapWrite);
1810 if (close (h->fd) != 0)
1812 LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING, "close");
1813 ret = GNUNET_SYSERR;
1822 * Get a GNUnet file handle from a W32 handle.
1824 * @param handle native handle
1825 * @return GNUnet file handle corresponding to the W32 handle
1827 struct GNUNET_DISK_FileHandle *
1828 GNUNET_DISK_get_handle_from_w32_handle (HANDLE osfh)
1830 struct GNUNET_DISK_FileHandle *fh;
1833 enum GNUNET_FILE_Type ftype;
1835 dwret = GetFileType (osfh);
1838 case FILE_TYPE_DISK:
1839 ftype = GNUNET_DISK_HANLDE_TYPE_FILE;
1841 case FILE_TYPE_PIPE:
1842 ftype = GNUNET_DISK_HANLDE_TYPE_PIPE;
1848 fh = GNUNET_malloc (sizeof (struct GNUNET_DISK_FileHandle));
1852 if (ftype == GNUNET_DISK_HANLDE_TYPE_PIPE)
1855 * Note that we can't make it overlapped if it isn't already.
1856 * (ReOpenFile() is only available in 2003/Vista).
1857 * The process that opened this file in the first place (usually a parent
1858 * process, if this is stdin/stdout/stderr) must make it overlapped,
1859 * otherwise we're screwed, as selecting on non-overlapped handle
1862 fh->oOverlapRead = GNUNET_malloc (sizeof (OVERLAPPED));
1863 fh->oOverlapWrite = GNUNET_malloc (sizeof (OVERLAPPED));
1864 fh->oOverlapRead->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
1865 fh->oOverlapWrite->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
1873 * Get a handle from a native integer FD.
1875 * @param fno native integer file descriptor
1876 * @return file handle corresponding to the descriptor, NULL on error
1878 struct GNUNET_DISK_FileHandle *
1879 GNUNET_DISK_get_handle_from_int_fd (int fno)
1881 struct GNUNET_DISK_FileHandle *fh;
1883 if ( (((off_t) -1) == lseek (fno, 0, SEEK_CUR)) &&
1885 return NULL; /* invalid FD */
1888 fh = GNUNET_malloc (sizeof (struct GNUNET_DISK_FileHandle));
1894 osfh = _get_osfhandle (fno);
1895 if (INVALID_HANDLE_VALUE == (HANDLE) osfh)
1898 fh = GNUNET_DISK_get_handle_from_w32_handle ((HANDLE) osfh);
1906 * Get a handle from a native streaming FD.
1908 * @param fd native streaming file descriptor
1909 * @return file handle corresponding to the descriptor
1911 struct GNUNET_DISK_FileHandle *
1912 GNUNET_DISK_get_handle_from_native (FILE *fd)
1920 return GNUNET_DISK_get_handle_from_int_fd (fno);
1925 * Construct full path to a file inside of the private
1926 * directory used by GNUnet. Also creates the corresponding
1927 * directory. If the resulting name is supposed to be
1928 * a directory, end the last argument in '/' (or pass
1929 * DIR_SEPARATOR_STR as the last argument before NULL).
1931 * @param cfg configuration to use (determines HOME)
1932 * @param serviceName name of the service
1933 * @param ... is NULL-terminated list of
1934 * path components to append to the
1935 * private directory name.
1936 * @return the constructed filename
1939 GNUNET_DISK_get_home_filename (const struct GNUNET_CONFIGURATION_Handle *cfg,
1940 const char *serviceName, ...)
1946 unsigned int needed;
1949 GNUNET_CONFIGURATION_get_value_filename (cfg, serviceName, "HOME", &pfx))
1953 LOG (GNUNET_ERROR_TYPE_WARNING,
1954 _("No `%s' specified for service `%s' in configuration.\n"), "HOME",
1958 needed = strlen (pfx) + 2;
1959 if ((pfx[strlen (pfx) - 1] != '/') && (pfx[strlen (pfx) - 1] != '\\'))
1961 va_start (ap, serviceName);
1964 c = va_arg (ap, const char *);
1968 needed += strlen (c);
1969 if ((c[strlen (c) - 1] != '/') && (c[strlen (c) - 1] != '\\'))
1973 ret = GNUNET_malloc (needed);
1976 va_start (ap, serviceName);
1979 c = va_arg (ap, const char *);
1983 if ((c[strlen (c) - 1] != '/') && (c[strlen (c) - 1] != '\\'))
1984 strcat (ret, DIR_SEPARATOR_STR);
1988 if ((ret[strlen (ret) - 1] != '/') && (ret[strlen (ret) - 1] != '\\'))
1989 (void) GNUNET_DISK_directory_create_for_file (ret);
1991 (void) GNUNET_DISK_directory_create (ret);
1997 * Handle for a memory-mapping operation.
1999 struct GNUNET_DISK_MapHandle
2002 * Address where the map is in memory.
2008 * Underlying OS handle.
2013 * Number of bytes mapped.
2021 #define MAP_FAILED ((void *) -1)
2025 * Map a file into memory
2027 * @param h open file handle
2028 * @param m handle to the new mapping
2029 * @param access access specification, GNUNET_DISK_MAP_TYPE_xxx
2030 * @param len size of the mapping
2031 * @return pointer to the mapped memory region, NULL on failure
2034 GNUNET_DISK_file_map (const struct GNUNET_DISK_FileHandle *h,
2035 struct GNUNET_DISK_MapHandle **m,
2036 enum GNUNET_DISK_MapType access, size_t len)
2045 DWORD mapAccess, protect;
2047 if ((access & GNUNET_DISK_MAP_TYPE_READ) &&
2048 (access & GNUNET_DISK_MAP_TYPE_WRITE))
2050 protect = PAGE_READWRITE;
2051 mapAccess = FILE_MAP_ALL_ACCESS;
2053 else if (access & GNUNET_DISK_MAP_TYPE_READ)
2055 protect = PAGE_READONLY;
2056 mapAccess = FILE_MAP_READ;
2058 else if (access & GNUNET_DISK_MAP_TYPE_WRITE)
2060 protect = PAGE_READWRITE;
2061 mapAccess = FILE_MAP_WRITE;
2069 *m = GNUNET_malloc (sizeof (struct GNUNET_DISK_MapHandle));
2070 (*m)->h = CreateFileMapping (h->h, NULL, protect, 0, 0, NULL);
2071 if ((*m)->h == INVALID_HANDLE_VALUE)
2073 SetErrnoFromWinError (GetLastError ());
2078 (*m)->addr = MapViewOfFile ((*m)->h, mapAccess, 0, 0, len);
2081 SetErrnoFromWinError (GetLastError ());
2082 CloseHandle ((*m)->h);
2091 if (access & GNUNET_DISK_MAP_TYPE_READ)
2093 if (access & GNUNET_DISK_MAP_TYPE_WRITE)
2095 *m = GNUNET_malloc (sizeof (struct GNUNET_DISK_MapHandle));
2096 (*m)->addr = mmap (NULL, len, prot, MAP_SHARED, h->fd, 0);
2097 GNUNET_assert (NULL != (*m)->addr);
2098 if (MAP_FAILED == (*m)->addr)
2110 * @param h mapping handle
2111 * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
2114 GNUNET_DISK_file_unmap (struct GNUNET_DISK_MapHandle *h)
2121 return GNUNET_SYSERR;
2125 ret = UnmapViewOfFile (h->addr) ? GNUNET_OK : GNUNET_SYSERR;
2126 if (ret != GNUNET_OK)
2127 SetErrnoFromWinError (GetLastError ());
2128 if (!CloseHandle (h->h) && (ret == GNUNET_OK))
2130 ret = GNUNET_SYSERR;
2131 SetErrnoFromWinError (GetLastError ());
2134 ret = munmap (h->addr, h->len) != -1 ? GNUNET_OK : GNUNET_SYSERR;
2142 * Write file changes to disk
2143 * @param h handle to an open file
2144 * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
2147 GNUNET_DISK_file_sync (const struct GNUNET_DISK_FileHandle *h)
2152 return GNUNET_SYSERR;
2158 ret = FlushFileBuffers (h->h) ? GNUNET_OK : GNUNET_SYSERR;
2159 if (ret != GNUNET_OK)
2160 SetErrnoFromWinError (GetLastError ());
2162 #elif defined(FREEBSD) || defined(OPENBSD) || defined(DARWIN)
2163 return fsync (h->fd) == -1 ? GNUNET_SYSERR : GNUNET_OK;
2165 return fdatasync (h->fd) == -1 ? GNUNET_SYSERR : GNUNET_OK;
2172 #define PIPE_BUF 512
2174 /* Copyright Bob Byrnes <byrnes <at> curl.com>
2175 http://permalink.gmane.org/gmane.os.cygwin.patches/2121
2177 /* Create a pipe, and return handles to the read and write ends,
2178 just like CreatePipe, but ensure that the write end permits
2179 FILE_READ_ATTRIBUTES access, on later versions of win32 where
2180 this is supported. This access is needed by NtQueryInformationFile,
2181 which is used to implement select and nonblocking writes.
2182 Note that the return value is either NO_ERROR or GetLastError,
2183 unlike CreatePipe, which returns a bool for success or failure. */
2185 create_selectable_pipe (PHANDLE read_pipe_ptr, PHANDLE write_pipe_ptr,
2186 LPSECURITY_ATTRIBUTES sa_ptr, DWORD psize,
2187 DWORD dwReadMode, DWORD dwWriteMode)
2189 /* Default to error. */
2190 *read_pipe_ptr = *write_pipe_ptr = INVALID_HANDLE_VALUE;
2195 /* Ensure that there is enough pipe buffer space for atomic writes. */
2196 if (psize < PIPE_BUF)
2199 char pipename[MAX_PATH];
2201 /* Retry CreateNamedPipe as long as the pipe name is in use.
2202 * Retrying will probably never be necessary, but we want
2203 * to be as robust as possible. */
2206 static volatile LONG pipe_unique_id;
2208 snprintf (pipename, sizeof pipename, "\\\\.\\pipe\\gnunet-%d-%ld",
2209 getpid (), InterlockedIncrement ((LONG *) & pipe_unique_id));
2210 LOG (GNUNET_ERROR_TYPE_DEBUG, "CreateNamedPipe: name = %s, size = %lu\n",
2212 /* Use CreateNamedPipe instead of CreatePipe, because the latter
2213 * returns a write handle that does not permit FILE_READ_ATTRIBUTES
2214 * access, on versions of win32 earlier than WinXP SP2.
2215 * CreatePipe also stupidly creates a full duplex pipe, which is
2216 * a waste, since only a single direction is actually used.
2217 * It's important to only allow a single instance, to ensure that
2218 * the pipe was not created earlier by some other process, even if
2219 * the pid has been reused. */
2220 read_pipe = CreateNamedPipeA (pipename, PIPE_ACCESS_INBOUND | FILE_FLAG_FIRST_PIPE_INSTANCE | dwReadMode, PIPE_TYPE_BYTE | PIPE_READMODE_BYTE, 1, /* max instances */
2221 psize, /* output buffer size */
2222 psize, /* input buffer size */
2223 NMPWAIT_USE_DEFAULT_WAIT, sa_ptr);
2225 if (read_pipe != INVALID_HANDLE_VALUE)
2227 LOG (GNUNET_ERROR_TYPE_DEBUG, "pipe read handle = %p\n", read_pipe);
2231 DWORD err = GetLastError ();
2235 case ERROR_PIPE_BUSY:
2236 /* The pipe is already open with compatible parameters.
2237 * Pick a new name and retry. */
2238 LOG (GNUNET_ERROR_TYPE_DEBUG, "pipe busy, retrying\n");
2240 case ERROR_ACCESS_DENIED:
2241 /* The pipe is already open with incompatible parameters.
2242 * Pick a new name and retry. */
2243 LOG (GNUNET_ERROR_TYPE_DEBUG, "pipe access denied, retrying\n");
2245 case ERROR_CALL_NOT_IMPLEMENTED:
2246 /* We are on an older Win9x platform without named pipes.
2247 * Return an anonymous pipe as the best approximation. */
2248 LOG (GNUNET_ERROR_TYPE_DEBUG,
2249 "CreateNamedPipe not implemented, resorting to "
2250 "CreatePipe: size = %lu\n", psize);
2251 if (CreatePipe (read_pipe_ptr, write_pipe_ptr, sa_ptr, psize))
2253 LOG (GNUNET_ERROR_TYPE_DEBUG, "pipe read handle = %p, write handle = %p\n",
2258 err = GetLastError ();
2259 LOG (GNUNET_ERROR_TYPE_ERROR, "CreatePipe failed: %d\n", err);
2262 LOG (GNUNET_ERROR_TYPE_ERROR, "CreateNamedPipe failed: %d\n", err);
2267 LOG (GNUNET_ERROR_TYPE_DEBUG, "CreateFile: name = %s\n", pipename);
2269 /* Open the named pipe for writing.
2270 * Be sure to permit FILE_READ_ATTRIBUTES access. */
2271 write_pipe = CreateFileA (pipename, GENERIC_WRITE | FILE_READ_ATTRIBUTES, 0, /* share mode */
2272 sa_ptr, OPEN_EXISTING, dwWriteMode, /* flags and attributes */
2273 0); /* handle to template file */
2275 if (write_pipe == INVALID_HANDLE_VALUE)
2278 DWORD err = GetLastError ();
2280 LOG (GNUNET_ERROR_TYPE_DEBUG, "CreateFile failed: %d\n", err);
2281 CloseHandle (read_pipe);
2284 LOG (GNUNET_ERROR_TYPE_DEBUG, "pipe write handle = %p\n", write_pipe);
2286 *read_pipe_ptr = read_pipe;
2287 *write_pipe_ptr = write_pipe;
2294 * Creates an interprocess channel
2296 * @param blocking_read creates an asynchronous pipe for reading if set to GNUNET_NO
2297 * @param blocking_write creates an asynchronous pipe for writing if set to GNUNET_NO
2298 * @param inherit_read inherit the parent processes stdin (only for windows)
2299 * @param inherit_write inherit the parent processes stdout (only for windows)
2300 * @return handle to the new pipe, NULL on error
2302 struct GNUNET_DISK_PipeHandle *
2303 GNUNET_DISK_pipe (int blocking_read, int blocking_write, int inherit_read, int inherit_write)
2314 LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR, "pipe");
2318 return GNUNET_DISK_pipe_from_fd (blocking_read,
2322 struct GNUNET_DISK_PipeHandle *p;
2327 p = GNUNET_malloc (sizeof (struct GNUNET_DISK_PipeHandle));
2328 p->fd[0] = GNUNET_malloc (sizeof (struct GNUNET_DISK_FileHandle));
2329 p->fd[1] = GNUNET_malloc (sizeof (struct GNUNET_DISK_FileHandle));
2331 /* All pipes are overlapped. If you want them to block - just
2332 * call WriteFile() and ReadFile() with NULL overlapped pointer.
2333 * NOTE: calling with NULL overlapped pointer works only
2334 * for pipes, and doesn't seem to be a documented feature.
2335 * It will NOT work for files, because overlapped files need
2336 * to read offsets from the overlapped structure, regardless.
2337 * Pipes are not seekable, and need no offsets, which is
2338 * probably why it works for them.
2341 create_selectable_pipe (&p->fd[0]->h, &p->fd[1]->h, NULL, 0,
2342 FILE_FLAG_OVERLAPPED,
2343 FILE_FLAG_OVERLAPPED);
2346 SetErrnoFromWinError (GetLastError ());
2348 GNUNET_free (p->fd[0]);
2349 GNUNET_free (p->fd[1]);
2354 if (!DuplicateHandle
2355 (GetCurrentProcess (), p->fd[0]->h, GetCurrentProcess (), &tmp_handle, 0,
2356 inherit_read == GNUNET_YES ? TRUE : FALSE, DUPLICATE_SAME_ACCESS))
2358 SetErrnoFromWinError (GetLastError ());
2360 CloseHandle (p->fd[0]->h);
2361 CloseHandle (p->fd[1]->h);
2362 GNUNET_free (p->fd[0]);
2363 GNUNET_free (p->fd[1]);
2368 CloseHandle (p->fd[0]->h);
2369 p->fd[0]->h = tmp_handle;
2371 if (!DuplicateHandle
2372 (GetCurrentProcess (), p->fd[1]->h, GetCurrentProcess (), &tmp_handle, 0,
2373 inherit_write == GNUNET_YES ? TRUE : FALSE, DUPLICATE_SAME_ACCESS))
2375 SetErrnoFromWinError (GetLastError ());
2377 CloseHandle (p->fd[0]->h);
2378 CloseHandle (p->fd[1]->h);
2379 GNUNET_free (p->fd[0]);
2380 GNUNET_free (p->fd[1]);
2385 CloseHandle (p->fd[1]->h);
2386 p->fd[1]->h = tmp_handle;
2388 p->fd[0]->type = GNUNET_DISK_HANLDE_TYPE_PIPE;
2389 p->fd[1]->type = GNUNET_DISK_HANLDE_TYPE_PIPE;
2391 p->fd[0]->oOverlapRead = GNUNET_malloc (sizeof (OVERLAPPED));
2392 p->fd[0]->oOverlapWrite = GNUNET_malloc (sizeof (OVERLAPPED));
2393 p->fd[1]->oOverlapRead = GNUNET_malloc (sizeof (OVERLAPPED));
2394 p->fd[1]->oOverlapWrite = GNUNET_malloc (sizeof (OVERLAPPED));
2396 p->fd[0]->oOverlapRead->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2397 p->fd[0]->oOverlapWrite->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2399 p->fd[1]->oOverlapRead->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2400 p->fd[1]->oOverlapWrite->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2408 * Creates a pipe object from a couple of file descriptors.
2409 * Useful for wrapping existing pipe FDs.
2411 * @param blocking_read creates an asynchronous pipe for reading if set to GNUNET_NO
2412 * @param blocking_write creates an asynchronous pipe for writing if set to GNUNET_NO
2413 * @param fd an array of two fd values. One of them may be -1 for read-only or write-only pipes
2415 * @return handle to the new pipe, NULL on error
2417 struct GNUNET_DISK_PipeHandle *
2418 GNUNET_DISK_pipe_from_fd (int blocking_read, int blocking_write, int fd[2])
2420 struct GNUNET_DISK_PipeHandle *p;
2422 p = GNUNET_malloc (sizeof (struct GNUNET_DISK_PipeHandle));
2427 int eno = 0; /* make gcc happy */
2432 p->fd[0] = GNUNET_malloc (sizeof (struct GNUNET_DISK_FileHandle));
2433 p->fd[0]->fd = fd[0];
2436 flags = fcntl (fd[0], F_GETFL);
2437 flags |= O_NONBLOCK;
2438 if (0 > fcntl (fd[0], F_SETFL, flags))
2444 flags = fcntl (fd[0], F_GETFD);
2445 flags |= FD_CLOEXEC;
2446 if (0 > fcntl (fd[0], F_SETFD, flags))
2455 p->fd[1] = GNUNET_malloc (sizeof (struct GNUNET_DISK_FileHandle));
2456 p->fd[1]->fd = fd[1];
2457 if (!blocking_write)
2459 flags = fcntl (fd[1], F_GETFL);
2460 flags |= O_NONBLOCK;
2461 if (0 > fcntl (fd[1], F_SETFL, flags))
2467 flags = fcntl (fd[1], F_GETFD);
2468 flags |= FD_CLOEXEC;
2469 if (0 > fcntl (fd[1], F_SETFD, flags))
2478 LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR, "fcntl");
2479 if (p->fd[0]->fd >= 0)
2480 GNUNET_break (0 == close (p->fd[0]->fd));
2481 if (p->fd[1]->fd >= 0)
2482 GNUNET_break (0 == close (p->fd[1]->fd));
2483 GNUNET_free_non_null (p->fd[0]);
2484 GNUNET_free_non_null (p->fd[1]);
2492 p->fd[0] = GNUNET_malloc (sizeof (struct GNUNET_DISK_FileHandle));
2493 p->fd[0]->h = (HANDLE) _get_osfhandle (fd[0]);
2494 if (p->fd[0]->h != INVALID_HANDLE_VALUE)
2496 p->fd[0]->type = GNUNET_DISK_HANLDE_TYPE_PIPE;
2497 p->fd[0]->oOverlapRead = GNUNET_malloc (sizeof (OVERLAPPED));
2498 p->fd[0]->oOverlapWrite = GNUNET_malloc (sizeof (OVERLAPPED));
2499 p->fd[0]->oOverlapRead->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2500 p->fd[0]->oOverlapWrite->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2504 GNUNET_free (p->fd[0]);
2510 p->fd[1] = GNUNET_malloc (sizeof (struct GNUNET_DISK_FileHandle));
2511 p->fd[1]->h = (HANDLE) _get_osfhandle (fd[1]);
2512 if (p->fd[1]->h != INVALID_HANDLE_VALUE)
2514 p->fd[1]->type = GNUNET_DISK_HANLDE_TYPE_PIPE;
2515 p->fd[1]->oOverlapRead = GNUNET_malloc (sizeof (OVERLAPPED));
2516 p->fd[1]->oOverlapWrite = GNUNET_malloc (sizeof (OVERLAPPED));
2517 p->fd[1]->oOverlapRead->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2518 p->fd[1]->oOverlapWrite->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2522 GNUNET_free (p->fd[1]);
2533 * Closes an interprocess channel
2535 * @param p pipe to close
2536 * @param end which end of the pipe to close
2537 * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
2540 GNUNET_DISK_pipe_close_end (struct GNUNET_DISK_PipeHandle *p,
2541 enum GNUNET_DISK_PipeEnd end)
2543 int ret = GNUNET_OK;
2545 if (end == GNUNET_DISK_PIPE_END_READ)
2549 ret = GNUNET_DISK_file_close (p->fd[0]);
2553 else if (end == GNUNET_DISK_PIPE_END_WRITE)
2557 ret = GNUNET_DISK_file_close (p->fd[1]);
2566 * Detaches one of the ends from the pipe.
2567 * Detached end is a fully-functional FileHandle, it will
2568 * not be affected by anything you do with the pipe afterwards.
2569 * Each end of a pipe can only be detched from it once (i.e.
2570 * it is not duplicated).
2572 * @param p pipe to detach an end from
2573 * @param end which end of the pipe to detach
2574 * @return Detached end on success, NULL on failure
2575 * (or if that end is not present or is closed).
2577 struct GNUNET_DISK_FileHandle *
2578 GNUNET_DISK_pipe_detach_end (struct GNUNET_DISK_PipeHandle *p,
2579 enum GNUNET_DISK_PipeEnd end)
2581 struct GNUNET_DISK_FileHandle *ret = NULL;
2583 if (end == GNUNET_DISK_PIPE_END_READ)
2591 else if (end == GNUNET_DISK_PIPE_END_WRITE)
2605 * Closes an interprocess channel
2607 * @param p pipe to close
2608 * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
2611 GNUNET_DISK_pipe_close (struct GNUNET_DISK_PipeHandle *p)
2613 int ret = GNUNET_OK;
2616 int write_end_close;
2617 int read_end_close_errno;
2618 int write_end_close_errno;
2620 read_end_close = GNUNET_DISK_pipe_close_end (p, GNUNET_DISK_PIPE_END_READ);
2621 read_end_close_errno = errno;
2622 write_end_close = GNUNET_DISK_pipe_close_end (p, GNUNET_DISK_PIPE_END_WRITE);
2623 write_end_close_errno = errno;
2626 if (GNUNET_OK != read_end_close)
2628 errno = read_end_close_errno;
2629 ret = read_end_close;
2631 else if (GNUNET_OK != write_end_close)
2633 errno = write_end_close_errno;
2634 ret = write_end_close;
2642 * Get the handle to a particular pipe end
2645 * @param n end to access
2646 * @return handle for the respective end
2648 const struct GNUNET_DISK_FileHandle *
2649 GNUNET_DISK_pipe_handle (const struct GNUNET_DISK_PipeHandle *p,
2650 enum GNUNET_DISK_PipeEnd n)
2654 case GNUNET_DISK_PIPE_END_READ:
2655 case GNUNET_DISK_PIPE_END_WRITE:
2665 * Retrieve OS file handle
2667 * @param fh GNUnet file descriptor
2668 * @param dst destination buffer
2669 * @param dst_len length of dst
2670 * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
2673 GNUNET_DISK_internal_file_handle_ (const struct GNUNET_DISK_FileHandle *fh,
2674 void *dst, size_t dst_len)
2677 if (dst_len < sizeof (HANDLE))
2678 return GNUNET_SYSERR;
2679 *((HANDLE *) dst) = fh->h;
2681 if (dst_len < sizeof (int))
2682 return GNUNET_SYSERR;
2683 *((int *) dst) = fh->fd;