2 This file is part of GNUnet.
3 (C) 2001--2012 Christian Grothoff (and other contributing authors)
5 GNUnet is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 2, or (at your
8 option) any later version.
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with GNUnet; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
23 * @brief disk IO convenience methods
24 * @author Christian Grothoff
29 #include "gnunet_common.h"
30 #include "gnunet_directories.h"
31 #include "gnunet_disk_lib.h"
32 #include "gnunet_scheduler_lib.h"
33 #include "gnunet_strings_lib.h"
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
50 #if defined(LINUX) || defined(CYGWIN) || defined(GNU)
53 #if defined(SOMEBSD) || defined(DARWIN)
54 #include <sys/param.h>
55 #include <sys/mount.h>
58 #include <sys/types.h>
59 #include <sys/statvfs.h>
64 ULONG PipeSerialNumber;
66 #define _IFMT 0170000 /* type of file */
67 #define _IFLNK 0120000 /* symbolic link */
68 #define S_ISLNK(m) (((m)&_IFMT) == _IFLNK)
70 #error PORT-ME: need to port statfs (how much space is left on the drive?)
76 #if !defined(SOMEBSD) && !defined(DARWIN) && !defined(WINDOWS)
80 #include <sys/statvfs.h>
85 * Handle used to manage a pipe.
87 struct GNUNET_DISK_PipeHandle
90 * File descriptors for the pipe.
91 * One or both of them could be NULL.
93 struct GNUNET_DISK_FileHandle *fd[2];
98 * Closure for the recursion to determine the file size
101 struct GetFileSizeData
104 * Set to the total file size.
109 * GNUNET_YES if symbolic links should be included.
111 int include_sym_links;
114 * GNUNET_YES if mode is file-only (return total == -1 for directories).
116 int single_file_mode;
122 * Translate GNUnet-internal permission bitmap to UNIX file
123 * access permission bitmap.
125 * @param perm file permissions, GNUnet style
126 * @return file permissions, UNIX style
129 translate_unix_perms (enum GNUNET_DISK_AccessPermissions perm)
134 if (perm & GNUNET_DISK_PERM_USER_READ)
136 if (perm & GNUNET_DISK_PERM_USER_WRITE)
138 if (perm & GNUNET_DISK_PERM_USER_EXEC)
140 if (perm & GNUNET_DISK_PERM_GROUP_READ)
142 if (perm & GNUNET_DISK_PERM_GROUP_WRITE)
144 if (perm & GNUNET_DISK_PERM_GROUP_EXEC)
146 if (perm & GNUNET_DISK_PERM_OTHER_READ)
148 if (perm & GNUNET_DISK_PERM_OTHER_WRITE)
150 if (perm & GNUNET_DISK_PERM_OTHER_EXEC)
159 * Iterate over all files in the given directory and
160 * accumulate their size.
162 * @param cls closure of type "struct GetFileSizeData"
163 * @param fn current filename we are looking at
164 * @return GNUNET_SYSERR on serious errors, otherwise GNUNET_OK
167 getSizeRec (void *cls, const char *fn)
169 struct GetFileSizeData *gfsd = cls;
178 if (0 != STAT64 (fn, &buf))
180 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_DEBUG, "stat64", fn);
181 return GNUNET_SYSERR;
184 if (0 != STAT (fn, &buf))
186 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_DEBUG, "stat", fn);
187 return GNUNET_SYSERR;
190 if ((S_ISDIR (buf.st_mode)) && (gfsd->single_file_mode == GNUNET_YES))
193 return GNUNET_SYSERR;
195 if ((!S_ISLNK (buf.st_mode)) || (gfsd->include_sym_links == GNUNET_YES))
196 gfsd->total += buf.st_size;
197 if ((S_ISDIR (buf.st_mode)) && (0 == ACCESS (fn, X_OK)) &&
198 ((!S_ISLNK (buf.st_mode)) || (gfsd->include_sym_links == GNUNET_YES)))
200 if (GNUNET_SYSERR == GNUNET_DISK_directory_scan (fn, &getSizeRec, gfsd))
201 return GNUNET_SYSERR;
208 * Checks whether a handle is invalid
210 * @param h handle to check
211 * @return GNUNET_YES if invalid, GNUNET_NO if valid
214 GNUNET_DISK_handle_invalid (const struct GNUNET_DISK_FileHandle *h)
217 return ((!h) || (h->h == INVALID_HANDLE_VALUE)) ? GNUNET_YES : GNUNET_NO;
219 return ((!h) || (h->fd == -1)) ? GNUNET_YES : GNUNET_NO;
224 * Get the size of an open file.
226 * @param fh open file handle
227 * @param size where to write size of the file
228 * @return GNUNET_OK on success, GNUNET_SYSERR on error
231 GNUNET_DISK_file_handle_size (struct GNUNET_DISK_FileHandle *fh,
237 b = GetFileSizeEx (fh->h, &li);
240 SetErrnoFromWinError (GetLastError ());
241 return GNUNET_SYSERR;
243 *size = (OFF_T) li.QuadPart;
247 if (0 != FSTAT (fh->fd, &sbuf))
248 return GNUNET_SYSERR;
249 *size = sbuf.st_size;
256 * Move the read/write pointer in a file
258 * @param h handle of an open file
259 * @param offset position to move to
260 * @param whence specification to which position the offset parameter relates to
261 * @return the new position on success, GNUNET_SYSERR otherwise
264 GNUNET_DISK_file_seek (const struct GNUNET_DISK_FileHandle * h, OFF_T offset,
265 enum GNUNET_DISK_Seek whence)
270 return GNUNET_SYSERR;
275 LARGE_INTEGER new_pos;
278 static DWORD t[] = { FILE_BEGIN, FILE_CURRENT, FILE_END };
279 li.QuadPart = offset;
281 b = SetFilePointerEx (h->h, li, &new_pos, t[whence]);
284 SetErrnoFromWinError (GetLastError ());
285 return GNUNET_SYSERR;
287 return (OFF_T) new_pos.QuadPart;
289 static int t[] = { SEEK_SET, SEEK_CUR, SEEK_END };
291 return lseek (h->fd, offset, t[whence]);
297 * Get the size of the file (or directory) of the given file (in
300 * @param filename name of the file or directory
301 * @param size set to the size of the file (or,
302 * in the case of directories, the sum
303 * of all sizes of files in the directory)
304 * @param includeSymLinks should symbolic links be
306 * @param singleFileMode GNUNET_YES to only get size of one file
307 * and return GNUNET_SYSERR for directories.
308 * @return GNUNET_SYSERR on error, GNUNET_OK on success
311 GNUNET_DISK_file_size (const char *filename, uint64_t * size,
312 int includeSymLinks, int singleFileMode)
314 struct GetFileSizeData gfsd;
317 GNUNET_assert (size != NULL);
319 gfsd.include_sym_links = includeSymLinks;
320 gfsd.single_file_mode = singleFileMode;
321 ret = getSizeRec (&gfsd, filename);
328 * Obtain some unique identifiers for the given file
329 * that can be used to identify it in the local system.
330 * This function is used between GNUnet processes to
331 * quickly check if two files with the same absolute path
332 * are actually identical. The two processes represent
333 * the same peer but may communicate over the network
334 * (and the file may be on an NFS volume). This function
335 * may not be supported on all operating systems.
337 * @param filename name of the file
338 * @param dev set to the device ID
339 * @param ino set to the inode ID
340 * @return GNUNET_OK on success
343 GNUNET_DISK_file_get_identifiers (const char *filename, uint64_t * dev,
350 if ((0 == stat (filename, &sbuf)) && (0 == statvfs (filename, &fbuf)))
352 *dev = (uint64_t) fbuf.f_fsid;
353 *ino = (uint64_t) sbuf.st_ino;
360 if ((0 == stat (filename, &sbuf)) && (0 == statfs (filename, &fbuf)))
362 *dev = ((uint64_t) fbuf.f_fsid.val[0]) << 32 ||
363 ((uint64_t) fbuf.f_fsid.val[1]);
364 *ino = (uint64_t) sbuf.st_ino;
368 // FIXME NILS: test this
369 struct GNUNET_DISK_FileHandle *fh;
370 BY_HANDLE_FILE_INFORMATION info;
373 fh = GNUNET_DISK_file_open (filename, GNUNET_DISK_OPEN_READ, 0);
375 return GNUNET_SYSERR;
376 succ = GetFileInformationByHandle (fh->h, &info);
377 GNUNET_DISK_file_close (fh);
380 *dev = info.dwVolumeSerialNumber;
381 *ino = ((((uint64_t) info.nFileIndexHigh) << (sizeof (DWORD) * 8)) | info.nFileIndexLow);
385 return GNUNET_SYSERR;
388 return GNUNET_SYSERR;
393 * Create the name for a temporary file or directory from a template.
395 * @param t template (without XXXXX or "/tmp/")
396 * @return name ready for passing to 'mktemp' or 'mkdtemp', NULL on error
399 mktemp_name (const char *t)
405 if ((t[0] != '/') && (t[0] != '\\')
407 && !(isalpha ((int) t[0]) && (t[0] != '\0') && (t[1] == ':'))
411 /* FIXME: This uses system codepage on W32, not UTF-8 */
412 tmpdir = getenv ("TMPDIR");
414 tmpdir = getenv ("TMP");
416 tmpdir = getenv ("TEMP");
419 GNUNET_asprintf (&tmpl, "%s/%s%s", tmpdir, t, "XXXXXX");
423 GNUNET_asprintf (&tmpl, "%s%s", t, "XXXXXX");
426 fn = (char *) GNUNET_malloc (MAX_PATH + 1);
427 if (ERROR_SUCCESS != plibc_conv_to_win_path (tmpl, fn))
450 tfn = GNUNET_strdup (fn);
451 random_fn = _mktemp (tfn);
452 if (NULL == random_fn)
457 /* FIXME: assume fn to be UTF-8-encoded and do the right thing */
458 if (0 == CreateDirectoryA (tfn, NULL))
460 DWORD error = GetLastError ();
462 if (ERROR_ALREADY_EXISTS == error)
474 * Create an (empty) temporary directory on disk. If the given name is not
475 * an absolute path, the current 'TMPDIR' will be prepended. In any case,
476 * 6 random characters will be appended to the name to create a unique
479 * @param t component to use for the name;
480 * does NOT contain "XXXXXX" or "/tmp/".
481 * @return NULL on error, otherwise name of fresh
482 * file on disk in directory for temporary files
485 GNUNET_DISK_mkdtemp (const char *t)
489 fn = mktemp_name (t);
490 if (fn != mkdtemp (fn))
492 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_ERROR, "mkstemp", fn);
501 * Move a file out of the way (create a backup) by
502 * renaming it to "orig.NUM~" where NUM is the smallest
503 * number that is not used yet.
505 * @param fil name of the file to back up
508 GNUNET_DISK_file_backup (const char *fil)
514 slen = strlen (fil) + 20;
515 target = GNUNET_malloc (slen);
519 GNUNET_snprintf (target, slen,
523 } while (0 == access (target, F_OK));
524 if (0 != rename (fil, target))
525 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR,
528 GNUNET_free (target);
533 * Create an (empty) temporary file on disk. If the given name is not
534 * an absolute path, the current 'TMPDIR' will be prepended. In any case,
535 * 6 random characters will be appended to the name to create a unique
538 * @param t component to use for the name;
539 * does NOT contain "XXXXXX" or "/tmp/".
540 * @return NULL on error, otherwise name of fresh
541 * file on disk in directory for temporary files
544 GNUNET_DISK_mktemp (const char *t)
549 fn = mktemp_name (t);
550 if (-1 == (fd = mkstemp (fn)))
552 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_ERROR, "mkstemp", fn);
557 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "close", fn);
563 * Get the number of blocks that are left on the partition that
564 * contains the given file (for normal users).
566 * @param part a file on the partition to check
567 * @return -1 on errors, otherwise the number of free blocks
570 GNUNET_DISK_get_blocks_available (const char *part)
575 if (0 != statvfs (part, &buf))
577 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "statfs", part);
585 wchar_t wpath[MAX_PATH + 1];
588 path = GNUNET_STRINGS_filename_expand (part);
591 /* "part" was in UTF-8, and so is "path" */
592 if (ERROR_SUCCESS != plibc_conv_to_win_pathwconv(path, wpath))
598 wcsncpy (szDrive, wpath, 3);
600 if (!GetDiskFreeSpaceW (szDrive, &dwDummy, &dwDummy, &dwBlocks, &dwDummy))
602 LOG (GNUNET_ERROR_TYPE_WARNING, _("`%s' failed for drive `%S': %u\n"),
603 "GetDiskFreeSpace", szDrive, GetLastError ());
611 if (0 != statfs (part, &s))
613 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "statfs", part);
622 * Test if "fil" is a directory and listable. Optionally, also check if the
623 * directory is readable. Will not print an error message if the directory does
624 * not exist. Will log errors if GNUNET_SYSERR is returned (i.e., a file exists
625 * with the same name).
627 * @param fil filename to test
628 * @param is_readable GNUNET_YES to additionally check if "fil" is readable;
629 * GNUNET_NO to disable this check
630 * @return GNUNET_YES if yes, GNUNET_NO if not; GNUNET_SYSERR if it
631 * does not exist or stat'ed
634 GNUNET_DISK_directory_test (const char *fil, int is_readable)
636 struct stat filestat;
639 ret = STAT (fil, &filestat);
643 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "stat", fil);
644 return GNUNET_SYSERR;
646 if (!S_ISDIR (filestat.st_mode))
648 LOG (GNUNET_ERROR_TYPE_WARNING,
649 "A file already exits with the same name %s\n", fil);
652 if (GNUNET_YES == is_readable)
653 ret = ACCESS (fil, R_OK | X_OK);
655 ret = ACCESS (fil, X_OK);
658 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "access", fil);
666 * Check that fil corresponds to a filename
667 * (of a file that exists and that is not a directory).
669 * @param fil filename to check
670 * @return GNUNET_YES if yes, GNUNET_NO if not a file, GNUNET_SYSERR if something
671 * else (will print an error message in that case, too).
674 GNUNET_DISK_file_test (const char *fil)
676 struct stat filestat;
680 rdir = GNUNET_STRINGS_filename_expand (fil);
682 return GNUNET_SYSERR;
684 ret = STAT (rdir, &filestat);
689 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "stat", rdir);
691 return GNUNET_SYSERR;
696 if (!S_ISREG (filestat.st_mode))
701 if (ACCESS (rdir, F_OK) < 0)
703 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "access", rdir);
705 return GNUNET_SYSERR;
713 * Implementation of "mkdir -p"
714 * @param dir the directory to create
715 * @returns GNUNET_OK on success, GNUNET_SYSERR on failure
718 GNUNET_DISK_directory_create (const char *dir)
726 rdir = GNUNET_STRINGS_filename_expand (dir);
728 return GNUNET_SYSERR;
732 pos = 1; /* skip heading '/' */
734 /* Local or Network path? */
735 if (strncmp (rdir, "\\\\", 2) == 0)
740 if (rdir[pos] == '\\')
750 pos = 3; /* strlen("C:\\") */
753 /* Check which low level directories already exist */
755 rdir[len] = DIR_SEPARATOR;
758 if (DIR_SEPARATOR == rdir[pos2])
761 ret = GNUNET_DISK_directory_test (rdir, GNUNET_NO);
762 if (GNUNET_NO == ret)
765 return GNUNET_SYSERR;
767 rdir[pos2] = DIR_SEPARATOR;
768 if (GNUNET_YES == ret)
779 /* Start creating directories */
782 if ((rdir[pos] == DIR_SEPARATOR) || (pos == len))
785 ret = GNUNET_DISK_directory_test (rdir, GNUNET_NO);
786 if (GNUNET_NO == ret)
789 return GNUNET_SYSERR;
791 if (GNUNET_SYSERR == ret)
794 ret = mkdir (rdir, S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH); /* 755 */
796 wchar_t wrdir[MAX_PATH + 1];
797 if (ERROR_SUCCESS == plibc_conv_to_win_pathwconv(rdir, wrdir))
798 ret = !CreateDirectoryW (wrdir, NULL);
802 if ((ret != 0) && (errno != EEXIST))
804 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_ERROR, "mkdir", rdir);
806 return GNUNET_SYSERR;
809 rdir[pos] = DIR_SEPARATOR;
819 * Create the directory structure for storing
822 * @param filename name of a file in the directory
823 * @returns GNUNET_OK on success,
824 * GNUNET_SYSERR on failure,
825 * GNUNET_NO if the directory
826 * exists but is not writeable for us
829 GNUNET_DISK_directory_create_for_file (const char *filename)
835 rdir = GNUNET_STRINGS_filename_expand (filename);
837 return GNUNET_SYSERR;
839 while ((len > 0) && (rdir[len] != DIR_SEPARATOR))
842 ret = GNUNET_DISK_directory_create (rdir);
843 if ((ret == GNUNET_OK) && (0 != ACCESS (rdir, W_OK)))
851 * Read the contents of a binary file into a buffer.
852 * @param h handle to an open file
853 * @param result the buffer to write the result to
854 * @param len the maximum number of bytes to read
855 * @return the number of bytes read on success, GNUNET_SYSERR on failure
858 GNUNET_DISK_file_read (const struct GNUNET_DISK_FileHandle * h, void *result,
864 return GNUNET_SYSERR;
870 if (h->type != GNUNET_DISK_HANLDE_TYPE_PIPE)
872 if (!ReadFile (h->h, result, len, &bytesRead, NULL))
874 SetErrnoFromWinError (GetLastError ());
875 return GNUNET_SYSERR;
880 if (!ReadFile (h->h, result, len, &bytesRead, h->oOverlapRead))
882 if (GetLastError () != ERROR_IO_PENDING)
884 LOG (GNUNET_ERROR_TYPE_DEBUG, "Error reading from pipe: %u\n", GetLastError ());
885 SetErrnoFromWinError (GetLastError ());
886 return GNUNET_SYSERR;
888 LOG (GNUNET_ERROR_TYPE_DEBUG, "Will get overlapped result\n");
889 GetOverlappedResult (h->h, h->oOverlapRead, &bytesRead, TRUE);
891 LOG (GNUNET_ERROR_TYPE_DEBUG, "Read %u bytes from pipe\n", bytesRead);
895 return read (h->fd, result, len);
901 * Read the contents of a binary file into a buffer.
902 * Guarantees not to block (returns GNUNET_SYSERR and sets errno to EAGAIN
903 * when no data can be read).
905 * @param h handle to an open file
906 * @param result the buffer to write the result to
907 * @param len the maximum number of bytes to read
908 * @return the number of bytes read on success, GNUNET_SYSERR on failure
911 GNUNET_DISK_file_read_non_blocking (const struct GNUNET_DISK_FileHandle * h,
918 return GNUNET_SYSERR;
924 if (h->type != GNUNET_DISK_HANLDE_TYPE_PIPE)
926 if (!ReadFile (h->h, result, len, &bytesRead, NULL))
928 SetErrnoFromWinError (GetLastError ());
929 return GNUNET_SYSERR;
934 if (!ReadFile (h->h, result, len, &bytesRead, h->oOverlapRead))
936 if (GetLastError () != ERROR_IO_PENDING)
938 LOG (GNUNET_ERROR_TYPE_DEBUG, "Error reading from pipe: %u\n", GetLastError ());
939 SetErrnoFromWinError (GetLastError ());
940 return GNUNET_SYSERR;
944 LOG (GNUNET_ERROR_TYPE_DEBUG,
945 "ReadFile() queued a read, cancelling\n");
948 return GNUNET_SYSERR;
951 LOG (GNUNET_ERROR_TYPE_DEBUG, "Read %u bytes\n", bytesRead);
958 /* set to non-blocking, read, then set back */
959 flags = fcntl (h->fd, F_GETFL);
960 if (0 == (flags & O_NONBLOCK))
961 (void) fcntl (h->fd, F_SETFL, flags | O_NONBLOCK);
962 ret = read (h->fd, result, len);
963 if (0 == (flags & O_NONBLOCK))
966 (void) fcntl (h->fd, F_SETFL, flags);
975 * Read the contents of a binary file into a buffer.
977 * @param fn file name
978 * @param result the buffer to write the result to
979 * @param len the maximum number of bytes to read
980 * @return number of bytes read, GNUNET_SYSERR on failure
983 GNUNET_DISK_fn_read (const char *fn, void *result, size_t len)
985 struct GNUNET_DISK_FileHandle *fh;
988 fh = GNUNET_DISK_file_open (fn, GNUNET_DISK_OPEN_READ, GNUNET_DISK_PERM_NONE);
990 return GNUNET_SYSERR;
991 ret = GNUNET_DISK_file_read (fh, result, len);
992 GNUNET_assert (GNUNET_OK == GNUNET_DISK_file_close (fh));
999 * Write a buffer to a file.
1000 * @param h handle to open file
1001 * @param buffer the data to write
1002 * @param n number of bytes to write
1003 * @return number of bytes written on success, GNUNET_SYSERR on error
1006 GNUNET_DISK_file_write (const struct GNUNET_DISK_FileHandle * h,
1007 const void *buffer, size_t n)
1012 return GNUNET_SYSERR;
1018 if (h->type != GNUNET_DISK_HANLDE_TYPE_PIPE)
1020 if (!WriteFile (h->h, buffer, n, &bytesWritten, NULL))
1022 SetErrnoFromWinError (GetLastError ());
1023 return GNUNET_SYSERR;
1028 LOG (GNUNET_ERROR_TYPE_DEBUG, "It is a pipe trying to write %u bytes\n", n);
1029 if (!WriteFile (h->h, buffer, n, &bytesWritten, h->oOverlapWrite))
1031 if (GetLastError () != ERROR_IO_PENDING)
1033 SetErrnoFromWinError (GetLastError ());
1034 LOG (GNUNET_ERROR_TYPE_DEBUG, "Error writing to pipe: %u\n",
1036 return GNUNET_SYSERR;
1038 LOG (GNUNET_ERROR_TYPE_DEBUG, "Will get overlapped result\n");
1039 if (!GetOverlappedResult (h->h, h->oOverlapWrite, &bytesWritten, TRUE))
1041 SetErrnoFromWinError (GetLastError ());
1042 LOG (GNUNET_ERROR_TYPE_DEBUG,
1043 "Error getting overlapped result while writing to pipe: %u\n",
1045 return GNUNET_SYSERR;
1051 if (!GetOverlappedResult (h->h, h->oOverlapWrite, &ovr, TRUE))
1053 LOG (GNUNET_ERROR_TYPE_DEBUG,
1054 "Error getting control overlapped result while writing to pipe: %u\n",
1059 LOG (GNUNET_ERROR_TYPE_DEBUG,
1060 "Wrote %u bytes (ovr says %u), picking the greatest\n",
1064 if (bytesWritten == 0)
1068 LOG (GNUNET_ERROR_TYPE_DEBUG, "Wrote %u bytes, returning -1 with EAGAIN\n", bytesWritten);
1070 return GNUNET_SYSERR;
1073 LOG (GNUNET_ERROR_TYPE_DEBUG, "Wrote %u bytes\n", bytesWritten);
1075 return bytesWritten;
1077 return write (h->fd, buffer, n);
1083 * Write a buffer to a file, blocking, if necessary.
1084 * @param h handle to open file
1085 * @param buffer the data to write
1086 * @param n number of bytes to write
1087 * @return number of bytes written on success, GNUNET_SYSERR on error
1090 GNUNET_DISK_file_write_blocking (const struct GNUNET_DISK_FileHandle * h,
1091 const void *buffer, size_t n)
1096 return GNUNET_SYSERR;
1101 /* We do a non-overlapped write, which is as blocking as it gets */
1102 LOG (GNUNET_ERROR_TYPE_DEBUG, "Writing %u bytes\n", n);
1103 if (!WriteFile (h->h, buffer, n, &bytesWritten, NULL))
1105 SetErrnoFromWinError (GetLastError ());
1106 LOG (GNUNET_ERROR_TYPE_DEBUG, "Error writing to pipe: %u\n",
1108 return GNUNET_SYSERR;
1110 if (bytesWritten == 0 && n > 0)
1112 LOG (GNUNET_ERROR_TYPE_DEBUG, "Waiting for pipe to clean\n");
1113 WaitForSingleObject (h->h, INFINITE);
1114 if (!WriteFile (h->h, buffer, n, &bytesWritten, NULL))
1116 SetErrnoFromWinError (GetLastError ());
1117 LOG (GNUNET_ERROR_TYPE_DEBUG, "Error writing to pipe: %u\n",
1119 return GNUNET_SYSERR;
1122 LOG (GNUNET_ERROR_TYPE_DEBUG, "Wrote %u bytes\n", bytesWritten);
1123 return bytesWritten;
1128 /* set to blocking, write, then set back */
1129 flags = fcntl (h->fd, F_GETFL);
1130 if (0 != (flags & O_NONBLOCK))
1131 (void) fcntl (h->fd, F_SETFL, flags - O_NONBLOCK);
1132 ret = write (h->fd, buffer, n);
1133 if (0 == (flags & O_NONBLOCK))
1134 (void) fcntl (h->fd, F_SETFL, flags);
1141 * Write a buffer to a file. If the file is longer than the
1142 * number of bytes that will be written, it will be truncated.
1144 * @param fn file name
1145 * @param buffer the data to write
1146 * @param n number of bytes to write
1147 * @param mode file permissions
1148 * @return number of bytes written on success, GNUNET_SYSERR on error
1151 GNUNET_DISK_fn_write (const char *fn, const void *buffer, size_t n,
1152 enum GNUNET_DISK_AccessPermissions mode)
1154 struct GNUNET_DISK_FileHandle *fh;
1157 fh = GNUNET_DISK_file_open (fn,
1158 GNUNET_DISK_OPEN_WRITE | GNUNET_DISK_OPEN_TRUNCATE
1159 | GNUNET_DISK_OPEN_CREATE, mode);
1161 return GNUNET_SYSERR;
1162 ret = GNUNET_DISK_file_write (fh, buffer, n);
1163 GNUNET_assert (GNUNET_OK == GNUNET_DISK_file_close (fh));
1169 * Scan a directory for files.
1171 * @param dirName the name of the directory
1172 * @param callback the method to call for each file,
1173 * can be NULL, in that case, we only count
1174 * @param callback_cls closure for callback
1175 * @return the number of files found, GNUNET_SYSERR on error or
1176 * ieration aborted by callback returning GNUNET_SYSERR
1179 GNUNET_DISK_directory_scan (const char *dirName,
1180 GNUNET_FileNameCallback callback,
1184 struct dirent *finfo;
1190 unsigned int name_len;
1191 unsigned int n_size;
1193 GNUNET_assert (dirName != NULL);
1194 dname = GNUNET_STRINGS_filename_expand (dirName);
1196 return GNUNET_SYSERR;
1197 while ((strlen (dname) > 0) && (dname[strlen (dname) - 1] == DIR_SEPARATOR))
1198 dname[strlen (dname) - 1] = '\0';
1199 if (0 != STAT (dname, &istat))
1201 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "stat", dname);
1202 GNUNET_free (dname);
1203 return GNUNET_SYSERR;
1205 if (!S_ISDIR (istat.st_mode))
1207 LOG (GNUNET_ERROR_TYPE_WARNING, _("Expected `%s' to be a directory!\n"),
1209 GNUNET_free (dname);
1210 return GNUNET_SYSERR;
1213 dinfo = OPENDIR (dname);
1214 if ((errno == EACCES) || (dinfo == NULL))
1216 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "opendir", dname);
1219 GNUNET_free (dname);
1220 return GNUNET_SYSERR;
1223 n_size = strlen (dname) + name_len + 2;
1224 name = GNUNET_malloc (n_size);
1225 while ((finfo = READDIR (dinfo)) != NULL)
1227 if ((0 == strcmp (finfo->d_name, ".")) ||
1228 (0 == strcmp (finfo->d_name, "..")))
1230 if (callback != NULL)
1232 if (name_len < strlen (finfo->d_name))
1235 name_len = strlen (finfo->d_name);
1236 n_size = strlen (dname) + name_len + 2;
1237 name = GNUNET_malloc (n_size);
1239 /* dname can end in "/" only if dname == "/";
1240 * if dname does not end in "/", we need to add
1241 * a "/" (otherwise, we must not!) */
1242 GNUNET_snprintf (name, n_size, "%s%s%s", dname,
1243 (strcmp (dname, DIR_SEPARATOR_STR) ==
1244 0) ? "" : DIR_SEPARATOR_STR, finfo->d_name);
1245 ret = callback (callback_cls, name);
1246 if (GNUNET_OK != ret)
1250 GNUNET_free (dname);
1251 if (GNUNET_NO == ret)
1253 return GNUNET_SYSERR;
1260 GNUNET_free (dname);
1266 * Opaque handle used for iterating over a directory.
1268 struct GNUNET_DISK_DirectoryIterator
1272 * Function to call on directory entries.
1274 GNUNET_DISK_DirectoryIteratorCallback callback;
1277 * Closure for callback.
1282 * Reference to directory.
1292 * Next filename to process.
1299 enum GNUNET_SCHEDULER_Priority priority;
1305 * Task used by the directory iterator.
1308 directory_iterator_task (void *cls,
1309 const struct GNUNET_SCHEDULER_TaskContext *tc)
1311 struct GNUNET_DISK_DirectoryIterator *iter = cls;
1314 name = iter->next_name;
1315 GNUNET_assert (name != NULL);
1316 iter->next_name = NULL;
1317 iter->callback (iter->callback_cls, iter, name, iter->dirname);
1323 * This function must be called during the DiskIteratorCallback
1324 * (exactly once) to schedule the task to process the next
1325 * filename in the directory (if there is one).
1327 * @param iter opaque handle for the iterator
1328 * @param can set to GNUNET_YES to terminate the iteration early
1329 * @return GNUNET_YES if iteration will continue,
1330 * GNUNET_NO if this was the last entry (and iteration is complete),
1331 * GNUNET_SYSERR if abort was YES
1334 GNUNET_DISK_directory_iterator_next (struct GNUNET_DISK_DirectoryIterator *iter,
1337 struct dirent *finfo;
1339 GNUNET_assert (iter->next_name == NULL);
1340 if (can == GNUNET_YES)
1342 CLOSEDIR (iter->directory);
1343 GNUNET_free (iter->dirname);
1345 return GNUNET_SYSERR;
1347 while (NULL != (finfo = READDIR (iter->directory)))
1349 if ((0 == strcmp (finfo->d_name, ".")) ||
1350 (0 == strcmp (finfo->d_name, "..")))
1352 GNUNET_asprintf (&iter->next_name, "%s%s%s", iter->dirname,
1353 DIR_SEPARATOR_STR, finfo->d_name);
1358 GNUNET_DISK_directory_iterator_next (iter, GNUNET_YES);
1361 GNUNET_SCHEDULER_add_with_priority (iter->priority, &directory_iterator_task,
1368 * Scan a directory for files using the scheduler to run a task for
1369 * each entry. The name of the directory must be expanded first (!).
1370 * If a scheduler does not need to be used, GNUNET_DISK_directory_scan
1371 * may provide a simpler API.
1373 * @param prio priority to use
1374 * @param dirName the name of the directory
1375 * @param callback the method to call for each file
1376 * @param callback_cls closure for callback
1377 * @return GNUNET_YES if directory is not empty and 'callback'
1378 * will be called later, GNUNET_NO otherwise, GNUNET_SYSERR on error.
1381 GNUNET_DISK_directory_iterator_start (enum GNUNET_SCHEDULER_Priority prio,
1382 const char *dirName,
1383 GNUNET_DISK_DirectoryIteratorCallback
1384 callback, void *callback_cls)
1386 struct GNUNET_DISK_DirectoryIterator *di;
1388 di = GNUNET_malloc (sizeof (struct GNUNET_DISK_DirectoryIterator));
1389 di->callback = callback;
1390 di->callback_cls = callback_cls;
1391 di->directory = OPENDIR (dirName);
1392 if (di->directory == NULL)
1395 callback (callback_cls, NULL, NULL, NULL);
1396 return GNUNET_SYSERR;
1398 di->dirname = GNUNET_strdup (dirName);
1399 di->priority = prio;
1400 return GNUNET_DISK_directory_iterator_next (di, GNUNET_NO);
1405 * Function that removes the given directory by calling
1406 * "GNUNET_DISK_directory_remove".
1408 * @param unused not used
1409 * @param fn directory to remove
1413 remove_helper (void *unused, const char *fn)
1415 (void) GNUNET_DISK_directory_remove (fn);
1421 * Remove all files in a directory (rm -rf). Call with
1425 * @param filename the file to remove
1426 * @return GNUNET_OK on success, GNUNET_SYSERR on error
1429 GNUNET_DISK_directory_remove (const char *filename)
1433 if (0 != LSTAT (filename, &istat))
1434 return GNUNET_NO; /* file may not exist... */
1435 (void) CHMOD (filename, S_IWUSR | S_IRUSR | S_IXUSR);
1436 if (UNLINK (filename) == 0)
1438 if ((errno != EISDIR) &&
1439 /* EISDIR is not sufficient in all cases, e.g.
1440 * sticky /tmp directory may result in EPERM on BSD.
1441 * So we also explicitly check "isDirectory" */
1442 (GNUNET_YES != GNUNET_DISK_directory_test (filename, GNUNET_YES)))
1444 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "rmdir", filename);
1445 return GNUNET_SYSERR;
1447 if (GNUNET_SYSERR ==
1448 GNUNET_DISK_directory_scan (filename, &remove_helper, NULL))
1449 return GNUNET_SYSERR;
1450 if (0 != RMDIR (filename))
1452 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "rmdir", filename);
1453 return GNUNET_SYSERR;
1462 * @param src file to copy
1463 * @param dst destination file name
1464 * @return GNUNET_OK on success, GNUNET_SYSERR on error
1467 GNUNET_DISK_file_copy (const char *src, const char *dst)
1473 struct GNUNET_DISK_FileHandle *in;
1474 struct GNUNET_DISK_FileHandle *out;
1476 if (GNUNET_OK != GNUNET_DISK_file_size (src, &size, GNUNET_YES, GNUNET_YES))
1477 return GNUNET_SYSERR;
1479 in = GNUNET_DISK_file_open (src, GNUNET_DISK_OPEN_READ,
1480 GNUNET_DISK_PERM_NONE);
1482 return GNUNET_SYSERR;
1484 GNUNET_DISK_file_open (dst,
1485 GNUNET_DISK_OPEN_WRITE | GNUNET_DISK_OPEN_CREATE |
1486 GNUNET_DISK_OPEN_FAILIFEXISTS,
1487 GNUNET_DISK_PERM_USER_READ |
1488 GNUNET_DISK_PERM_USER_WRITE |
1489 GNUNET_DISK_PERM_GROUP_READ |
1490 GNUNET_DISK_PERM_GROUP_WRITE);
1493 GNUNET_DISK_file_close (in);
1494 return GNUNET_SYSERR;
1496 buf = GNUNET_malloc (COPY_BLK_SIZE);
1499 len = COPY_BLK_SIZE;
1500 if (len > size - pos)
1502 if (len != GNUNET_DISK_file_read (in, buf, len))
1504 if (len != GNUNET_DISK_file_write (out, buf, len))
1509 GNUNET_DISK_file_close (in);
1510 GNUNET_DISK_file_close (out);
1514 GNUNET_DISK_file_close (in);
1515 GNUNET_DISK_file_close (out);
1516 return GNUNET_SYSERR;
1521 * @brief Removes special characters as ':' from a filename.
1522 * @param fn the filename to canonicalize
1525 GNUNET_DISK_filename_canonicalize (char *fn)
1535 if (c == '/' || c == '\\' || c == ':' || c == '*' || c == '?' || c == '"' ||
1536 c == '<' || c == '>' || c == '|')
1548 * @brief Change owner of a file
1550 * @param filename name of file to change the owner of
1551 * @param user name of the new owner
1552 * @return GNUNET_OK on success, GNUNET_SYSERR on failure
1555 GNUNET_DISK_file_change_owner (const char *filename, const char *user)
1560 pws = getpwnam (user);
1563 LOG (GNUNET_ERROR_TYPE_ERROR,
1564 _("Cannot obtain information about user `%s': %s\n"), user,
1566 return GNUNET_SYSERR;
1568 if (0 != chown (filename, pws->pw_uid, pws->pw_gid))
1569 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "chown", filename);
1576 * Lock a part of a file
1577 * @param fh file handle
1578 * @param lockStart absolute position from where to lock
1579 * @param lockEnd absolute position until where to lock
1580 * @param excl GNUNET_YES for an exclusive lock
1581 * @return GNUNET_OK on success, GNUNET_SYSERR on error
1584 GNUNET_DISK_file_lock (struct GNUNET_DISK_FileHandle *fh, OFF_T lockStart,
1585 OFF_T lockEnd, int excl)
1590 return GNUNET_SYSERR;
1596 memset (&fl, 0, sizeof (struct flock));
1597 fl.l_type = excl ? F_WRLCK : F_RDLCK;
1598 fl.l_whence = SEEK_SET;
1599 fl.l_start = lockStart;
1602 return fcntl (fh->fd, F_SETLK, &fl) != 0 ? GNUNET_SYSERR : GNUNET_OK;
1605 OFF_T diff = lockEnd - lockStart;
1606 DWORD diff_low, diff_high;
1607 diff_low = (DWORD) (diff & 0xFFFFFFFF);
1608 diff_high = (DWORD) ((diff >> (sizeof (DWORD) * 8)) & 0xFFFFFFFF);
1610 memset (&o, 0, sizeof (OVERLAPPED));
1611 o.Offset = (DWORD) (lockStart & 0xFFFFFFFF);;
1612 o.OffsetHigh = (DWORD) (((lockStart & ~0xFFFFFFFF) >> (sizeof (DWORD) * 8)) & 0xFFFFFFFF);
1615 (fh->h, (excl ? LOCKFILE_EXCLUSIVE_LOCK : 0) | LOCKFILE_FAIL_IMMEDIATELY,
1616 0, diff_low, diff_high, &o))
1618 SetErrnoFromWinError (GetLastError ());
1619 return GNUNET_SYSERR;
1628 * Unlock a part of a file
1629 * @param fh file handle
1630 * @param unlockStart absolute position from where to unlock
1631 * @param unlockEnd absolute position until where to unlock
1632 * @return GNUNET_OK on success, GNUNET_SYSERR on error
1635 GNUNET_DISK_file_unlock (struct GNUNET_DISK_FileHandle *fh, OFF_T unlockStart,
1641 return GNUNET_SYSERR;
1647 memset (&fl, 0, sizeof (struct flock));
1648 fl.l_type = F_UNLCK;
1649 fl.l_whence = SEEK_SET;
1650 fl.l_start = unlockStart;
1651 fl.l_len = unlockEnd;
1653 return fcntl (fh->fd, F_SETLK, &fl) != 0 ? GNUNET_SYSERR : GNUNET_OK;
1656 OFF_T diff = unlockEnd - unlockStart;
1657 DWORD diff_low, diff_high;
1658 diff_low = (DWORD) (diff & 0xFFFFFFFF);
1659 diff_high = (DWORD) ((diff >> (sizeof (DWORD) * 8)) & 0xFFFFFFFF);
1661 memset (&o, 0, sizeof (OVERLAPPED));
1662 o.Offset = (DWORD) (unlockStart & 0xFFFFFFFF);;
1663 o.OffsetHigh = (DWORD) (((unlockStart & ~0xFFFFFFFF) >> (sizeof (DWORD) * 8)) & 0xFFFFFFFF);
1665 if (!UnlockFileEx (fh->h, 0, diff_low, diff_high, &o))
1667 SetErrnoFromWinError (GetLastError ());
1668 return GNUNET_SYSERR;
1677 * Open a file. Note that the access permissions will only be
1678 * used if a new file is created and if the underlying operating
1679 * system supports the given permissions.
1681 * @param fn file name to be opened
1682 * @param flags opening flags, a combination of GNUNET_DISK_OPEN_xxx bit flags
1683 * @param perm permissions for the newly created file, use
1684 * GNUNET_DISK_PERM_USER_NONE if a file could not be created by this
1685 * call (because of flags)
1686 * @return IO handle on success, NULL on error
1688 struct GNUNET_DISK_FileHandle *
1689 GNUNET_DISK_file_open (const char *fn, enum GNUNET_DISK_OpenFlags flags,
1690 enum GNUNET_DISK_AccessPermissions perm)
1693 struct GNUNET_DISK_FileHandle *ret;
1699 wchar_t wexpfn[MAX_PATH + 1];
1706 expfn = GNUNET_STRINGS_filename_expand (fn);
1711 if (GNUNET_DISK_OPEN_READWRITE == (flags & GNUNET_DISK_OPEN_READWRITE))
1712 oflags = O_RDWR; /* note: O_RDWR is NOT always O_RDONLY | O_WRONLY */
1713 else if (flags & GNUNET_DISK_OPEN_READ)
1715 else if (flags & GNUNET_DISK_OPEN_WRITE)
1720 GNUNET_free (expfn);
1723 if (flags & GNUNET_DISK_OPEN_FAILIFEXISTS)
1724 oflags |= (O_CREAT | O_EXCL);
1725 if (flags & GNUNET_DISK_OPEN_TRUNCATE)
1727 if (flags & GNUNET_DISK_OPEN_APPEND)
1729 if (flags & GNUNET_DISK_OPEN_CREATE)
1731 (void) GNUNET_DISK_directory_create_for_file (expfn);
1733 mode = translate_unix_perms (perm);
1736 fd = open (expfn, oflags
1740 | O_LARGEFILE, mode);
1743 if (0 == (flags & GNUNET_DISK_OPEN_FAILIFEXISTS))
1744 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "open", expfn);
1746 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_DEBUG, "open", expfn);
1747 GNUNET_free (expfn);
1754 if (GNUNET_DISK_OPEN_READWRITE == (flags & GNUNET_DISK_OPEN_READWRITE))
1755 access = FILE_READ_DATA | FILE_WRITE_DATA;
1756 else if (flags & GNUNET_DISK_OPEN_READ)
1757 access = FILE_READ_DATA;
1758 else if (flags & GNUNET_DISK_OPEN_WRITE)
1759 access = FILE_WRITE_DATA;
1761 if (flags & GNUNET_DISK_OPEN_FAILIFEXISTS)
1765 else if (flags & GNUNET_DISK_OPEN_CREATE)
1767 (void) GNUNET_DISK_directory_create_for_file (expfn);
1768 if (flags & GNUNET_DISK_OPEN_TRUNCATE)
1769 disp = CREATE_ALWAYS;
1773 else if (flags & GNUNET_DISK_OPEN_TRUNCATE)
1775 disp = TRUNCATE_EXISTING;
1779 disp = OPEN_EXISTING;
1782 if (ERROR_SUCCESS == plibc_conv_to_win_pathwconv(expfn, wexpfn))
1783 h = CreateFileW (wexpfn, access,
1784 FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE, NULL,
1785 disp, FILE_ATTRIBUTE_NORMAL, NULL);
1787 h = INVALID_HANDLE_VALUE;
1788 if (h == INVALID_HANDLE_VALUE)
1791 SetErrnoFromWinError (GetLastError ());
1793 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_INFO, "open", expfn);
1794 GNUNET_free (expfn);
1799 if (flags & GNUNET_DISK_OPEN_APPEND)
1800 if (SetFilePointer (h, 0, 0, FILE_END) == INVALID_SET_FILE_POINTER)
1802 SetErrnoFromWinError (GetLastError ());
1803 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "SetFilePointer", expfn);
1805 GNUNET_free (expfn);
1810 ret = GNUNET_malloc (sizeof (struct GNUNET_DISK_FileHandle));
1813 ret->type = GNUNET_DISK_HANLDE_TYPE_FILE;
1817 GNUNET_free (expfn);
1823 * Close an open file
1824 * @param h file handle
1825 * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
1828 GNUNET_DISK_file_close (struct GNUNET_DISK_FileHandle *h)
1834 return GNUNET_SYSERR;
1840 if (!CloseHandle (h->h))
1842 SetErrnoFromWinError (GetLastError ());
1843 LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING, "close");
1844 ret = GNUNET_SYSERR;
1846 if (h->oOverlapRead)
1848 if (!CloseHandle (h->oOverlapRead->hEvent))
1850 SetErrnoFromWinError (GetLastError ());
1851 LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING, "close");
1852 ret = GNUNET_SYSERR;
1854 GNUNET_free (h->oOverlapRead);
1856 if (h->oOverlapWrite)
1858 if (!CloseHandle (h->oOverlapWrite->hEvent))
1860 SetErrnoFromWinError (GetLastError ());
1861 LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING, "close");
1862 ret = GNUNET_SYSERR;
1864 GNUNET_free (h->oOverlapWrite);
1867 if (close (h->fd) != 0)
1869 LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING, "close");
1870 ret = GNUNET_SYSERR;
1879 * Get a GNUnet file handle from a W32 handle.
1881 * @param handle native handle
1882 * @return GNUnet file handle corresponding to the W32 handle
1884 struct GNUNET_DISK_FileHandle *
1885 GNUNET_DISK_get_handle_from_w32_handle (HANDLE osfh)
1887 struct GNUNET_DISK_FileHandle *fh;
1890 enum GNUNET_FILE_Type ftype;
1892 dwret = GetFileType (osfh);
1895 case FILE_TYPE_DISK:
1896 ftype = GNUNET_DISK_HANLDE_TYPE_FILE;
1898 case FILE_TYPE_PIPE:
1899 ftype = GNUNET_DISK_HANLDE_TYPE_PIPE;
1905 fh = GNUNET_malloc (sizeof (struct GNUNET_DISK_FileHandle));
1909 if (ftype == GNUNET_DISK_HANLDE_TYPE_PIPE)
1912 * Note that we can't make it overlapped if it isn't already.
1913 * (ReOpenFile() is only available in 2003/Vista).
1914 * The process that opened this file in the first place (usually a parent
1915 * process, if this is stdin/stdout/stderr) must make it overlapped,
1916 * otherwise we're screwed, as selecting on non-overlapped handle
1919 fh->oOverlapRead = GNUNET_malloc (sizeof (OVERLAPPED));
1920 fh->oOverlapWrite = GNUNET_malloc (sizeof (OVERLAPPED));
1921 fh->oOverlapRead->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
1922 fh->oOverlapWrite->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
1930 * Get a handle from a native integer FD.
1932 * @param fno native integer file descriptor
1933 * @return file handle corresponding to the descriptor, NULL on error
1935 struct GNUNET_DISK_FileHandle *
1936 GNUNET_DISK_get_handle_from_int_fd (int fno)
1938 struct GNUNET_DISK_FileHandle *fh;
1940 if ( (((off_t) -1) == lseek (fno, 0, SEEK_CUR)) &&
1942 return NULL; /* invalid FD */
1945 fh = GNUNET_malloc (sizeof (struct GNUNET_DISK_FileHandle));
1951 osfh = _get_osfhandle (fno);
1952 if (INVALID_HANDLE_VALUE == (HANDLE) osfh)
1955 fh = GNUNET_DISK_get_handle_from_w32_handle ((HANDLE) osfh);
1963 * Get a handle from a native streaming FD.
1965 * @param fd native streaming file descriptor
1966 * @return file handle corresponding to the descriptor
1968 struct GNUNET_DISK_FileHandle *
1969 GNUNET_DISK_get_handle_from_native (FILE *fd)
1977 return GNUNET_DISK_get_handle_from_int_fd (fno);
1982 * Construct full path to a file inside of the private
1983 * directory used by GNUnet. Also creates the corresponding
1984 * directory. If the resulting name is supposed to be
1985 * a directory, end the last argument in '/' (or pass
1986 * DIR_SEPARATOR_STR as the last argument before NULL).
1988 * @param cfg configuration to use (determines HOME)
1989 * @param serviceName name of the service
1990 * @param ... is NULL-terminated list of
1991 * path components to append to the
1992 * private directory name.
1993 * @return the constructed filename
1996 GNUNET_DISK_get_home_filename (const struct GNUNET_CONFIGURATION_Handle *cfg,
1997 const char *serviceName, ...)
2003 unsigned int needed;
2006 GNUNET_CONFIGURATION_get_value_filename (cfg, serviceName, "HOME", &pfx))
2010 LOG (GNUNET_ERROR_TYPE_WARNING,
2011 _("No `%s' specified for service `%s' in configuration.\n"), "HOME",
2015 needed = strlen (pfx) + 2;
2016 if ((pfx[strlen (pfx) - 1] != '/') && (pfx[strlen (pfx) - 1] != '\\'))
2018 va_start (ap, serviceName);
2021 c = va_arg (ap, const char *);
2025 needed += strlen (c);
2026 if ((c[strlen (c) - 1] != '/') && (c[strlen (c) - 1] != '\\'))
2030 ret = GNUNET_malloc (needed);
2033 va_start (ap, serviceName);
2036 c = va_arg (ap, const char *);
2040 if ((c[strlen (c) - 1] != '/') && (c[strlen (c) - 1] != '\\'))
2041 strcat (ret, DIR_SEPARATOR_STR);
2045 if ((ret[strlen (ret) - 1] != '/') && (ret[strlen (ret) - 1] != '\\'))
2046 (void) GNUNET_DISK_directory_create_for_file (ret);
2048 (void) GNUNET_DISK_directory_create (ret);
2054 * Handle for a memory-mapping operation.
2056 struct GNUNET_DISK_MapHandle
2059 * Address where the map is in memory.
2065 * Underlying OS handle.
2070 * Number of bytes mapped.
2078 #define MAP_FAILED ((void *) -1)
2082 * Map a file into memory
2084 * @param h open file handle
2085 * @param m handle to the new mapping
2086 * @param access access specification, GNUNET_DISK_MAP_TYPE_xxx
2087 * @param len size of the mapping
2088 * @return pointer to the mapped memory region, NULL on failure
2091 GNUNET_DISK_file_map (const struct GNUNET_DISK_FileHandle *h,
2092 struct GNUNET_DISK_MapHandle **m,
2093 enum GNUNET_DISK_MapType access, size_t len)
2102 DWORD mapAccess, protect;
2104 if ((access & GNUNET_DISK_MAP_TYPE_READ) &&
2105 (access & GNUNET_DISK_MAP_TYPE_WRITE))
2107 protect = PAGE_READWRITE;
2108 mapAccess = FILE_MAP_ALL_ACCESS;
2110 else if (access & GNUNET_DISK_MAP_TYPE_READ)
2112 protect = PAGE_READONLY;
2113 mapAccess = FILE_MAP_READ;
2115 else if (access & GNUNET_DISK_MAP_TYPE_WRITE)
2117 protect = PAGE_READWRITE;
2118 mapAccess = FILE_MAP_WRITE;
2126 *m = GNUNET_malloc (sizeof (struct GNUNET_DISK_MapHandle));
2127 (*m)->h = CreateFileMapping (h->h, NULL, protect, 0, 0, NULL);
2128 if ((*m)->h == INVALID_HANDLE_VALUE)
2130 SetErrnoFromWinError (GetLastError ());
2135 (*m)->addr = MapViewOfFile ((*m)->h, mapAccess, 0, 0, len);
2138 SetErrnoFromWinError (GetLastError ());
2139 CloseHandle ((*m)->h);
2148 if (access & GNUNET_DISK_MAP_TYPE_READ)
2150 if (access & GNUNET_DISK_MAP_TYPE_WRITE)
2152 *m = GNUNET_malloc (sizeof (struct GNUNET_DISK_MapHandle));
2153 (*m)->addr = mmap (NULL, len, prot, MAP_SHARED, h->fd, 0);
2154 GNUNET_assert (NULL != (*m)->addr);
2155 if (MAP_FAILED == (*m)->addr)
2167 * @param h mapping handle
2168 * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
2171 GNUNET_DISK_file_unmap (struct GNUNET_DISK_MapHandle *h)
2178 return GNUNET_SYSERR;
2182 ret = UnmapViewOfFile (h->addr) ? GNUNET_OK : GNUNET_SYSERR;
2183 if (ret != GNUNET_OK)
2184 SetErrnoFromWinError (GetLastError ());
2185 if (!CloseHandle (h->h) && (ret == GNUNET_OK))
2187 ret = GNUNET_SYSERR;
2188 SetErrnoFromWinError (GetLastError ());
2191 ret = munmap (h->addr, h->len) != -1 ? GNUNET_OK : GNUNET_SYSERR;
2199 * Write file changes to disk
2200 * @param h handle to an open file
2201 * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
2204 GNUNET_DISK_file_sync (const struct GNUNET_DISK_FileHandle *h)
2209 return GNUNET_SYSERR;
2215 ret = FlushFileBuffers (h->h) ? GNUNET_OK : GNUNET_SYSERR;
2216 if (ret != GNUNET_OK)
2217 SetErrnoFromWinError (GetLastError ());
2219 #elif defined(FREEBSD) || defined(OPENBSD) || defined(DARWIN)
2220 return fsync (h->fd) == -1 ? GNUNET_SYSERR : GNUNET_OK;
2222 return fdatasync (h->fd) == -1 ? GNUNET_SYSERR : GNUNET_OK;
2228 /* Copyright Bob Byrnes <byrnes <at> curl.com>
2229 http://permalink.gmane.org/gmane.os.cygwin.patches/2121
2231 /* Create a pipe, and return handles to the read and write ends,
2232 just like CreatePipe, but ensure that the write end permits
2233 FILE_READ_ATTRIBUTES access, on later versions of win32 where
2234 this is supported. This access is needed by NtQueryInformationFile,
2235 which is used to implement select and nonblocking writes.
2236 Note that the return value is either NO_ERROR or GetLastError,
2237 unlike CreatePipe, which returns a bool for success or failure. */
2239 create_selectable_pipe (PHANDLE read_pipe_ptr, PHANDLE write_pipe_ptr,
2240 LPSECURITY_ATTRIBUTES sa_ptr, DWORD psize,
2241 DWORD dwReadMode, DWORD dwWriteMode)
2243 /* Default to error. */
2244 *read_pipe_ptr = *write_pipe_ptr = INVALID_HANDLE_VALUE;
2249 /* Ensure that there is enough pipe buffer space for atomic writes. */
2250 if (psize < PIPE_BUF)
2253 char pipename[MAX_PATH];
2255 /* Retry CreateNamedPipe as long as the pipe name is in use.
2256 * Retrying will probably never be necessary, but we want
2257 * to be as robust as possible. */
2260 static volatile LONG pipe_unique_id;
2262 snprintf (pipename, sizeof pipename, "\\\\.\\pipe\\gnunet-%d-%ld",
2263 getpid (), InterlockedIncrement ((LONG *) & pipe_unique_id));
2264 LOG (GNUNET_ERROR_TYPE_DEBUG, "CreateNamedPipe: name = %s, size = %lu\n",
2266 /* Use CreateNamedPipe instead of CreatePipe, because the latter
2267 * returns a write handle that does not permit FILE_READ_ATTRIBUTES
2268 * access, on versions of win32 earlier than WinXP SP2.
2269 * CreatePipe also stupidly creates a full duplex pipe, which is
2270 * a waste, since only a single direction is actually used.
2271 * It's important to only allow a single instance, to ensure that
2272 * the pipe was not created earlier by some other process, even if
2273 * the pid has been reused. */
2274 read_pipe = CreateNamedPipeA (pipename, PIPE_ACCESS_INBOUND | FILE_FLAG_FIRST_PIPE_INSTANCE | dwReadMode, PIPE_TYPE_BYTE | PIPE_READMODE_BYTE, 1, /* max instances */
2275 psize, /* output buffer size */
2276 psize, /* input buffer size */
2277 NMPWAIT_USE_DEFAULT_WAIT, sa_ptr);
2279 if (read_pipe != INVALID_HANDLE_VALUE)
2281 LOG (GNUNET_ERROR_TYPE_DEBUG, "pipe read handle = %p\n", read_pipe);
2285 DWORD err = GetLastError ();
2289 case ERROR_PIPE_BUSY:
2290 /* The pipe is already open with compatible parameters.
2291 * Pick a new name and retry. */
2292 LOG (GNUNET_ERROR_TYPE_DEBUG, "pipe busy, retrying\n");
2294 case ERROR_ACCESS_DENIED:
2295 /* The pipe is already open with incompatible parameters.
2296 * Pick a new name and retry. */
2297 LOG (GNUNET_ERROR_TYPE_DEBUG, "pipe access denied, retrying\n");
2299 case ERROR_CALL_NOT_IMPLEMENTED:
2300 /* We are on an older Win9x platform without named pipes.
2301 * Return an anonymous pipe as the best approximation. */
2302 LOG (GNUNET_ERROR_TYPE_DEBUG,
2303 "CreateNamedPipe not implemented, resorting to "
2304 "CreatePipe: size = %lu\n", psize);
2305 if (CreatePipe (read_pipe_ptr, write_pipe_ptr, sa_ptr, psize))
2307 LOG (GNUNET_ERROR_TYPE_DEBUG, "pipe read handle = %p, write handle = %p\n",
2312 err = GetLastError ();
2313 LOG (GNUNET_ERROR_TYPE_ERROR, "CreatePipe failed: %d\n", err);
2316 LOG (GNUNET_ERROR_TYPE_ERROR, "CreateNamedPipe failed: %d\n", err);
2321 LOG (GNUNET_ERROR_TYPE_DEBUG, "CreateFile: name = %s\n", pipename);
2323 /* Open the named pipe for writing.
2324 * Be sure to permit FILE_READ_ATTRIBUTES access. */
2325 write_pipe = CreateFileA (pipename, GENERIC_WRITE | FILE_READ_ATTRIBUTES, 0, /* share mode */
2326 sa_ptr, OPEN_EXISTING, dwWriteMode, /* flags and attributes */
2327 0); /* handle to template file */
2329 if (write_pipe == INVALID_HANDLE_VALUE)
2332 DWORD err = GetLastError ();
2334 LOG (GNUNET_ERROR_TYPE_DEBUG, "CreateFile failed: %d\n", err);
2335 CloseHandle (read_pipe);
2338 LOG (GNUNET_ERROR_TYPE_DEBUG, "pipe write handle = %p\n", write_pipe);
2340 *read_pipe_ptr = read_pipe;
2341 *write_pipe_ptr = write_pipe;
2348 * Creates an interprocess channel
2350 * @param blocking_read creates an asynchronous pipe for reading if set to GNUNET_NO
2351 * @param blocking_write creates an asynchronous pipe for writing if set to GNUNET_NO
2352 * @param inherit_read inherit the parent processes stdin (only for windows)
2353 * @param inherit_write inherit the parent processes stdout (only for windows)
2354 * @return handle to the new pipe, NULL on error
2356 struct GNUNET_DISK_PipeHandle *
2357 GNUNET_DISK_pipe (int blocking_read, int blocking_write, int inherit_read, int inherit_write)
2368 LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR, "pipe");
2372 return GNUNET_DISK_pipe_from_fd (blocking_read,
2376 struct GNUNET_DISK_PipeHandle *p;
2381 p = GNUNET_malloc (sizeof (struct GNUNET_DISK_PipeHandle));
2382 p->fd[0] = GNUNET_malloc (sizeof (struct GNUNET_DISK_FileHandle));
2383 p->fd[1] = GNUNET_malloc (sizeof (struct GNUNET_DISK_FileHandle));
2385 /* All pipes are overlapped. If you want them to block - just
2386 * call WriteFile() and ReadFile() with NULL overlapped pointer.
2387 * NOTE: calling with NULL overlapped pointer works only
2388 * for pipes, and doesn't seem to be a documented feature.
2389 * It will NOT work for files, because overlapped files need
2390 * to read offsets from the overlapped structure, regardless.
2391 * Pipes are not seekable, and need no offsets, which is
2392 * probably why it works for them.
2395 create_selectable_pipe (&p->fd[0]->h, &p->fd[1]->h, NULL, 0,
2396 FILE_FLAG_OVERLAPPED,
2397 FILE_FLAG_OVERLAPPED);
2400 SetErrnoFromWinError (GetLastError ());
2402 GNUNET_free (p->fd[0]);
2403 GNUNET_free (p->fd[1]);
2408 if (!DuplicateHandle
2409 (GetCurrentProcess (), p->fd[0]->h, GetCurrentProcess (), &tmp_handle, 0,
2410 inherit_read == GNUNET_YES ? TRUE : FALSE, DUPLICATE_SAME_ACCESS))
2412 SetErrnoFromWinError (GetLastError ());
2414 CloseHandle (p->fd[0]->h);
2415 CloseHandle (p->fd[1]->h);
2416 GNUNET_free (p->fd[0]);
2417 GNUNET_free (p->fd[1]);
2422 CloseHandle (p->fd[0]->h);
2423 p->fd[0]->h = tmp_handle;
2425 if (!DuplicateHandle
2426 (GetCurrentProcess (), p->fd[1]->h, GetCurrentProcess (), &tmp_handle, 0,
2427 inherit_write == GNUNET_YES ? TRUE : FALSE, DUPLICATE_SAME_ACCESS))
2429 SetErrnoFromWinError (GetLastError ());
2431 CloseHandle (p->fd[0]->h);
2432 CloseHandle (p->fd[1]->h);
2433 GNUNET_free (p->fd[0]);
2434 GNUNET_free (p->fd[1]);
2439 CloseHandle (p->fd[1]->h);
2440 p->fd[1]->h = tmp_handle;
2442 p->fd[0]->type = GNUNET_DISK_HANLDE_TYPE_PIPE;
2443 p->fd[1]->type = GNUNET_DISK_HANLDE_TYPE_PIPE;
2445 p->fd[0]->oOverlapRead = GNUNET_malloc (sizeof (OVERLAPPED));
2446 p->fd[0]->oOverlapWrite = GNUNET_malloc (sizeof (OVERLAPPED));
2447 p->fd[1]->oOverlapRead = GNUNET_malloc (sizeof (OVERLAPPED));
2448 p->fd[1]->oOverlapWrite = GNUNET_malloc (sizeof (OVERLAPPED));
2450 p->fd[0]->oOverlapRead->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2451 p->fd[0]->oOverlapWrite->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2453 p->fd[1]->oOverlapRead->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2454 p->fd[1]->oOverlapWrite->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2462 * Creates a pipe object from a couple of file descriptors.
2463 * Useful for wrapping existing pipe FDs.
2465 * @param blocking_read creates an asynchronous pipe for reading if set to GNUNET_NO
2466 * @param blocking_write creates an asynchronous pipe for writing if set to GNUNET_NO
2467 * @param fd an array of two fd values. One of them may be -1 for read-only or write-only pipes
2469 * @return handle to the new pipe, NULL on error
2471 struct GNUNET_DISK_PipeHandle *
2472 GNUNET_DISK_pipe_from_fd (int blocking_read, int blocking_write, int fd[2])
2474 struct GNUNET_DISK_PipeHandle *p;
2476 p = GNUNET_malloc (sizeof (struct GNUNET_DISK_PipeHandle));
2481 int eno = 0; /* make gcc happy */
2486 p->fd[0] = GNUNET_malloc (sizeof (struct GNUNET_DISK_FileHandle));
2487 p->fd[0]->fd = fd[0];
2490 flags = fcntl (fd[0], F_GETFL);
2491 flags |= O_NONBLOCK;
2492 if (0 > fcntl (fd[0], F_SETFL, flags))
2498 flags = fcntl (fd[0], F_GETFD);
2499 flags |= FD_CLOEXEC;
2500 if (0 > fcntl (fd[0], F_SETFD, flags))
2509 p->fd[1] = GNUNET_malloc (sizeof (struct GNUNET_DISK_FileHandle));
2510 p->fd[1]->fd = fd[1];
2511 if (!blocking_write)
2513 flags = fcntl (fd[1], F_GETFL);
2514 flags |= O_NONBLOCK;
2515 if (0 > fcntl (fd[1], F_SETFL, flags))
2521 flags = fcntl (fd[1], F_GETFD);
2522 flags |= FD_CLOEXEC;
2523 if (0 > fcntl (fd[1], F_SETFD, flags))
2532 LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR, "fcntl");
2533 if (p->fd[0]->fd >= 0)
2534 GNUNET_break (0 == close (p->fd[0]->fd));
2535 if (p->fd[1]->fd >= 0)
2536 GNUNET_break (0 == close (p->fd[1]->fd));
2537 GNUNET_free_non_null (p->fd[0]);
2538 GNUNET_free_non_null (p->fd[1]);
2546 p->fd[0] = GNUNET_malloc (sizeof (struct GNUNET_DISK_FileHandle));
2547 p->fd[0]->h = (HANDLE) _get_osfhandle (fd[0]);
2548 if (p->fd[0]->h != INVALID_HANDLE_VALUE)
2550 p->fd[0]->type = GNUNET_DISK_HANLDE_TYPE_PIPE;
2551 p->fd[0]->oOverlapRead = GNUNET_malloc (sizeof (OVERLAPPED));
2552 p->fd[0]->oOverlapWrite = GNUNET_malloc (sizeof (OVERLAPPED));
2553 p->fd[0]->oOverlapRead->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2554 p->fd[0]->oOverlapWrite->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2558 GNUNET_free (p->fd[0]);
2564 p->fd[1] = GNUNET_malloc (sizeof (struct GNUNET_DISK_FileHandle));
2565 p->fd[1]->h = (HANDLE) _get_osfhandle (fd[1]);
2566 if (p->fd[1]->h != INVALID_HANDLE_VALUE)
2568 p->fd[1]->type = GNUNET_DISK_HANLDE_TYPE_PIPE;
2569 p->fd[1]->oOverlapRead = GNUNET_malloc (sizeof (OVERLAPPED));
2570 p->fd[1]->oOverlapWrite = GNUNET_malloc (sizeof (OVERLAPPED));
2571 p->fd[1]->oOverlapRead->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2572 p->fd[1]->oOverlapWrite->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2576 GNUNET_free (p->fd[1]);
2587 * Closes an interprocess channel
2589 * @param p pipe to close
2590 * @param end which end of the pipe to close
2591 * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
2594 GNUNET_DISK_pipe_close_end (struct GNUNET_DISK_PipeHandle *p,
2595 enum GNUNET_DISK_PipeEnd end)
2597 int ret = GNUNET_OK;
2599 if (end == GNUNET_DISK_PIPE_END_READ)
2603 ret = GNUNET_DISK_file_close (p->fd[0]);
2607 else if (end == GNUNET_DISK_PIPE_END_WRITE)
2611 ret = GNUNET_DISK_file_close (p->fd[1]);
2620 * Detaches one of the ends from the pipe.
2621 * Detached end is a fully-functional FileHandle, it will
2622 * not be affected by anything you do with the pipe afterwards.
2623 * Each end of a pipe can only be detched from it once (i.e.
2624 * it is not duplicated).
2626 * @param p pipe to detach an end from
2627 * @param end which end of the pipe to detach
2628 * @return Detached end on success, NULL on failure
2629 * (or if that end is not present or is closed).
2631 struct GNUNET_DISK_FileHandle *
2632 GNUNET_DISK_pipe_detach_end (struct GNUNET_DISK_PipeHandle *p,
2633 enum GNUNET_DISK_PipeEnd end)
2635 struct GNUNET_DISK_FileHandle *ret = NULL;
2637 if (end == GNUNET_DISK_PIPE_END_READ)
2645 else if (end == GNUNET_DISK_PIPE_END_WRITE)
2659 * Closes an interprocess channel
2661 * @param p pipe to close
2662 * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
2665 GNUNET_DISK_pipe_close (struct GNUNET_DISK_PipeHandle *p)
2667 int ret = GNUNET_OK;
2670 int write_end_close;
2671 int read_end_close_errno;
2672 int write_end_close_errno;
2674 read_end_close = GNUNET_DISK_pipe_close_end (p, GNUNET_DISK_PIPE_END_READ);
2675 read_end_close_errno = errno;
2676 write_end_close = GNUNET_DISK_pipe_close_end (p, GNUNET_DISK_PIPE_END_WRITE);
2677 write_end_close_errno = errno;
2680 if (GNUNET_OK != read_end_close)
2682 errno = read_end_close_errno;
2683 ret = read_end_close;
2685 else if (GNUNET_OK != write_end_close)
2687 errno = write_end_close_errno;
2688 ret = write_end_close;
2696 * Get the handle to a particular pipe end
2699 * @param n end to access
2700 * @return handle for the respective end
2702 const struct GNUNET_DISK_FileHandle *
2703 GNUNET_DISK_pipe_handle (const struct GNUNET_DISK_PipeHandle *p,
2704 enum GNUNET_DISK_PipeEnd n)
2708 case GNUNET_DISK_PIPE_END_READ:
2709 case GNUNET_DISK_PIPE_END_WRITE:
2719 * Retrieve OS file handle
2721 * @param fh GNUnet file descriptor
2722 * @param dst destination buffer
2723 * @param dst_len length of dst
2724 * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
2727 GNUNET_DISK_internal_file_handle_ (const struct GNUNET_DISK_FileHandle *fh,
2728 void *dst, size_t dst_len)
2731 if (dst_len < sizeof (HANDLE))
2732 return GNUNET_SYSERR;
2733 *((HANDLE *) dst) = fh->h;
2735 if (dst_len < sizeof (int))
2736 return GNUNET_SYSERR;
2737 *((int *) dst) = fh->fd;