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)
43 #define DEBUG_NPIPE GNUNET_EXTRA_LOGGING
45 #define DEBUG_PIPE GNUNET_EXTRA_LOGGING
48 * Block size for IO for copying files.
50 #define COPY_BLK_SIZE 65536
54 #if defined(LINUX) || defined(CYGWIN)
57 #if defined(SOMEBSD) || defined(DARWIN)
58 #include <sys/param.h>
59 #include <sys/mount.h>
62 #include <sys/types.h>
63 #include <sys/statvfs.h>
68 ULONG PipeSerialNumber;
70 #define _IFMT 0170000 /* type of file */
71 #define _IFLNK 0120000 /* symbolic link */
72 #define S_ISLNK(m) (((m)&_IFMT) == _IFLNK)
74 #error PORT-ME: need to port statfs (how much space is left on the drive?)
80 #if !defined(SOMEBSD) && !defined(DARWIN) && !defined(WINDOWS)
84 #include <sys/statvfs.h>
89 * Handle used to manage a pipe.
91 struct GNUNET_DISK_PipeHandle
94 * File descriptors for the pipe.
96 struct GNUNET_DISK_FileHandle *fd[2];
101 * Closure for the recursion to determine the file size
104 struct GetFileSizeData
107 * Set to the total file size.
112 * GNUNET_YES if symbolic links should be included.
114 int include_sym_links;
119 translate_unix_perms (enum GNUNET_DISK_AccessPermissions perm)
124 if (perm & GNUNET_DISK_PERM_USER_READ)
126 if (perm & GNUNET_DISK_PERM_USER_WRITE)
128 if (perm & GNUNET_DISK_PERM_USER_EXEC)
130 if (perm & GNUNET_DISK_PERM_GROUP_READ)
132 if (perm & GNUNET_DISK_PERM_GROUP_WRITE)
134 if (perm & GNUNET_DISK_PERM_GROUP_EXEC)
136 if (perm & GNUNET_DISK_PERM_OTHER_READ)
138 if (perm & GNUNET_DISK_PERM_OTHER_WRITE)
140 if (perm & GNUNET_DISK_PERM_OTHER_EXEC)
148 * Iterate over all files in the given directory and
149 * accumulate their size.
151 * @param cls closure of type "struct GetFileSizeData"
152 * @param fn current filename we are looking at
153 * @return GNUNET_SYSERR on serious errors, otherwise GNUNET_OK
156 getSizeRec (void *cls, const char *fn)
158 struct GetFileSizeData *gfsd = cls;
167 if (0 != STAT64 (fn, &buf))
169 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "stat64", fn);
170 return GNUNET_SYSERR;
173 if (0 != STAT (fn, &buf))
175 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "stat", fn);
176 return GNUNET_SYSERR;
179 if ((!S_ISLNK (buf.st_mode)) || (gfsd->include_sym_links == GNUNET_YES))
180 gfsd->total += buf.st_size;
181 if ((S_ISDIR (buf.st_mode)) && (0 == ACCESS (fn, X_OK)) &&
182 ((!S_ISLNK (buf.st_mode)) || (gfsd->include_sym_links == GNUNET_YES)))
184 if (GNUNET_SYSERR == GNUNET_DISK_directory_scan (fn, &getSizeRec, gfsd))
185 return GNUNET_SYSERR;
192 * Checks whether a handle is invalid
194 * @param h handle to check
195 * @return GNUNET_YES if invalid, GNUNET_NO if valid
198 GNUNET_DISK_handle_invalid (const struct GNUNET_DISK_FileHandle *h)
201 return ((!h) || (h->h == INVALID_HANDLE_VALUE)) ? GNUNET_YES : GNUNET_NO;
203 return ((!h) || (h->fd == -1)) ? GNUNET_YES : GNUNET_NO;
208 * Get the size of an open file.
210 * @param fh open file handle
211 * @param size where to write size of the file
212 * @return GNUNET_OK on success, GNUNET_SYSERR on error
215 GNUNET_DISK_file_handle_size (struct GNUNET_DISK_FileHandle *fh,
221 b = GetFileSizeEx (fh->h, &li);
224 SetErrnoFromWinError (GetLastError ());
225 return GNUNET_SYSERR;
227 *size = (OFF_T) li.QuadPart;
231 if (0 != FSTAT (fh->fd, &sbuf))
232 return GNUNET_SYSERR;
233 *size = sbuf.st_size;
240 * Move the read/write pointer in a file
242 * @param h handle of an open file
243 * @param offset position to move to
244 * @param whence specification to which position the offset parameter relates to
245 * @return the new position on success, GNUNET_SYSERR otherwise
248 GNUNET_DISK_file_seek (const struct GNUNET_DISK_FileHandle * h, OFF_T offset,
249 enum GNUNET_DISK_Seek whence)
254 return GNUNET_SYSERR;
258 LARGE_INTEGER li, new_pos;
261 static DWORD t[] = {[GNUNET_DISK_SEEK_SET] = FILE_BEGIN,
262 [GNUNET_DISK_SEEK_CUR] = FILE_CURRENT,[GNUNET_DISK_SEEK_END] = 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[] = {[GNUNET_DISK_SEEK_SET] = SEEK_SET,
275 [GNUNET_DISK_SEEK_CUR] = SEEK_CUR,[GNUNET_DISK_SEEK_END] = SEEK_END
278 return lseek (h->fd, offset, t[whence]);
284 * Get the size of the file (or directory) of the given file (in
287 * @param filename name of the file or directory
288 * @param size set to the size of the file (or,
289 * in the case of directories, the sum
290 * of all sizes of files in the directory)
291 * @param includeSymLinks should symbolic links be
293 * @return GNUNET_SYSERR on error, GNUNET_OK on success
296 GNUNET_DISK_file_size (const char *filename, uint64_t * size,
299 struct GetFileSizeData gfsd;
302 GNUNET_assert (size != NULL);
304 gfsd.include_sym_links = includeSymLinks;
305 ret = getSizeRec (&gfsd, filename);
312 * Obtain some unique identifiers for the given file
313 * that can be used to identify it in the local system.
314 * This function is used between GNUnet processes to
315 * quickly check if two files with the same absolute path
316 * are actually identical. The two processes represent
317 * the same peer but may communicate over the network
318 * (and the file may be on an NFS volume). This function
319 * may not be supported on all operating systems.
321 * @param filename name of the file
322 * @param dev set to the device ID
323 * @param ino set to the inode ID
324 * @return GNUNET_OK on success
327 GNUNET_DISK_file_get_identifiers (const char *filename, uint64_t * dev,
334 if ((0 == stat (filename, &sbuf)) && (0 == statvfs (filename, &fbuf)))
336 *dev = (uint64_t) fbuf.f_fsid;
337 *ino = (uint64_t) sbuf.st_ino;
344 if ((0 == stat (filename, &sbuf)) && (0 == statfs (filename, &fbuf)))
346 *dev = ((uint64_t) fbuf.f_fsid.val[0]) << 32 ||
347 ((uint64_t) fbuf.f_fsid.val[1]);
348 *ino = (uint64_t) sbuf.st_ino;
352 // FIXME NILS: test this
353 struct GNUNET_DISK_FileHandle *fh;
354 BY_HANDLE_FILE_INFORMATION info;
357 fh = GNUNET_DISK_file_open (filename, GNUNET_DISK_OPEN_READ, 0);
359 return GNUNET_SYSERR;
360 succ = GetFileInformationByHandle (fh->h, &info);
361 GNUNET_DISK_file_close (fh);
364 *dev = info.dwVolumeSerialNumber;
365 *ino = ((((uint64_t) info.nFileIndexHigh) << (sizeof (DWORD) * 8)) | info.nFileIndexLow);
369 return GNUNET_SYSERR;
372 return GNUNET_SYSERR;
377 * Create an (empty) temporary file on disk. If the given name is not
378 * an absolute path, the current 'TMPDIR' will be prepended. In any case,
379 * 6 random characters will be appended to the name to create a unique
382 * @param t component to use for the name;
383 * does NOT contain "XXXXXX" or "/tmp/".
384 * @return NULL on error, otherwise name of fresh
385 * file on disk in directory for temporary files
388 GNUNET_DISK_mktemp (const char *t)
395 if ((t[0] != '/') && (t[0] != '\\')
397 && !(isalpha ((int) t[0]) && (t[0] != '\0') && (t[1] == ':'))
401 /* FIXME: This uses system codepage on W32, not UTF-8 */
402 tmpdir = getenv ("TMPDIR");
403 tmpdir = tmpdir ? tmpdir : "/tmp";
404 GNUNET_asprintf (&tmpl, "%s/%s%s", tmpdir, t, "XXXXXX");
408 GNUNET_asprintf (&tmpl, "%s%s", t, "XXXXXX");
411 fn = (char *) GNUNET_malloc (MAX_PATH + 1);
412 if (ERROR_SUCCESS != plibc_conv_to_win_path (tmpl, fn))
422 /* FIXME: why is this not MKSTEMP()? This function is implemented in plibc.
423 * CG: really? If I put MKSTEMP here, I get a compilation error...
424 * It will assume that fn is UTF-8-encoded, if compiled with UTF-8 support.
429 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_ERROR, "mkstemp", fn);
434 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "close", fn);
440 * Get the number of blocks that are left on the partition that
441 * contains the given file (for normal users).
443 * @param part a file on the partition to check
444 * @return -1 on errors, otherwise the number of free blocks
447 GNUNET_DISK_get_blocks_available (const char *part)
452 if (0 != statvfs (part, &buf))
454 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "statfs", part);
462 wchar_t wpath[MAX_PATH + 1];
465 path = GNUNET_STRINGS_filename_expand (part);
468 /* "part" was in UTF-8, and so is "path" */
469 if (ERROR_SUCCESS != plibc_conv_to_win_pathwconv(path, wpath))
475 wcsncpy (szDrive, wpath, 3);
477 if (!GetDiskFreeSpaceW (szDrive, &dwDummy, &dwDummy, &dwBlocks, &dwDummy))
479 LOG (GNUNET_ERROR_TYPE_WARNING, _("`%s' failed for drive `%S': %u\n"),
480 "GetDiskFreeSpace", szDrive, GetLastError ());
488 if (0 != statfs (part, &s))
490 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "statfs", part);
499 * Test if "fil" is a directory.
500 * Will not print an error message if the directory
501 * does not exist. Will log errors if GNUNET_SYSERR is
502 * returned (i.e., a file exists with the same name).
504 * @param fil filename to test
505 * @return GNUNET_YES if yes, GNUNET_NO if not, GNUNET_SYSERR if it
509 GNUNET_DISK_directory_test (const char *fil)
511 struct stat filestat;
514 ret = STAT (fil, &filestat);
519 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "stat", fil);
520 return GNUNET_SYSERR;
524 if (!S_ISDIR (filestat.st_mode))
526 if (ACCESS (fil, R_OK | X_OK) < 0)
528 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "access", fil);
529 return GNUNET_SYSERR;
536 * Check that fil corresponds to a filename
537 * (of a file that exists and that is not a directory).
539 * @param fil filename to check
540 * @return GNUNET_YES if yes, GNUNET_NO if not a file, GNUNET_SYSERR if something
541 * else (will print an error message in that case, too).
544 GNUNET_DISK_file_test (const char *fil)
546 struct stat filestat;
550 rdir = GNUNET_STRINGS_filename_expand (fil);
552 return GNUNET_SYSERR;
554 ret = STAT (rdir, &filestat);
559 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "stat", rdir);
561 return GNUNET_SYSERR;
566 if (!S_ISREG (filestat.st_mode))
571 if (ACCESS (rdir, R_OK) < 0)
573 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "access", rdir);
575 return GNUNET_SYSERR;
583 * Implementation of "mkdir -p"
584 * @param dir the directory to create
585 * @returns GNUNET_OK on success, GNUNET_SYSERR on failure
588 GNUNET_DISK_directory_create (const char *dir)
595 rdir = GNUNET_STRINGS_filename_expand (dir);
597 return GNUNET_SYSERR;
601 pos = 1; /* skip heading '/' */
603 /* Local or Network path? */
604 if (strncmp (rdir, "\\\\", 2) == 0)
609 if (rdir[pos] == '\\')
619 pos = 3; /* strlen("C:\\") */
624 if ((rdir[pos] == DIR_SEPARATOR) || (pos == len))
627 ret = GNUNET_DISK_directory_test (rdir);
628 if (ret == GNUNET_SYSERR)
631 return GNUNET_SYSERR;
633 if (ret == GNUNET_NO)
636 ret = mkdir (rdir, S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH); /* 755 */
638 wchar_t wrdir[MAX_PATH + 1];
639 if (ERROR_SUCCESS == plibc_conv_to_win_pathwconv(rdir, wrdir))
640 ret = !CreateDirectoryW (wrdir, NULL);
644 if ((ret != 0) && (errno != EEXIST))
646 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_ERROR, "mkdir", rdir);
648 return GNUNET_SYSERR;
651 rdir[pos] = DIR_SEPARATOR;
661 * Create the directory structure for storing
664 * @param filename name of a file in the directory
665 * @returns GNUNET_OK on success,
666 * GNUNET_SYSERR on failure,
667 * GNUNET_NO if the directory
668 * exists but is not writeable for us
671 GNUNET_DISK_directory_create_for_file (const char *filename)
677 rdir = GNUNET_STRINGS_filename_expand (filename);
679 return GNUNET_SYSERR;
681 while ((len > 0) && (rdir[len] != DIR_SEPARATOR))
684 ret = GNUNET_DISK_directory_create (rdir);
685 if ((ret == GNUNET_OK) && (0 != ACCESS (rdir, W_OK)))
693 * Read the contents of a binary file into a buffer.
694 * @param h handle to an open file
695 * @param result the buffer to write the result to
696 * @param len the maximum number of bytes to read
697 * @return the number of bytes read on success, GNUNET_SYSERR on failure
700 GNUNET_DISK_file_read (const struct GNUNET_DISK_FileHandle * h, void *result,
706 return GNUNET_SYSERR;
712 if (h->type != GNUNET_PIPE)
714 if (!ReadFile (h->h, result, len, &bytesRead, NULL))
716 SetErrnoFromWinError (GetLastError ());
717 return GNUNET_SYSERR;
723 LOG (GNUNET_ERROR_TYPE_DEBUG, "It is a pipe trying to read\n");
725 if (!ReadFile (h->h, result, len, &bytesRead, h->oOverlapRead))
727 if (GetLastError () != ERROR_IO_PENDING)
730 LOG (GNUNET_ERROR_TYPE_DEBUG, "Error reading from pipe: %u\n", GetLastError ());
732 SetErrnoFromWinError (GetLastError ());
733 return GNUNET_SYSERR;
736 LOG (GNUNET_ERROR_TYPE_DEBUG, "Will get overlapped result\n");
738 GetOverlappedResult (h->h, h->oOverlapRead, &bytesRead, TRUE);
741 LOG (GNUNET_ERROR_TYPE_DEBUG, "Read %u bytes\n", bytesRead);
746 return read (h->fd, result, len);
752 * Read the contents of a binary file into a buffer.
753 * Guarantees not to block (returns GNUNET_SYSERR and sets errno to EAGAIN
754 * when no data can be read).
756 * @param h handle to an open file
757 * @param result the buffer to write the result to
758 * @param len the maximum number of bytes to read
759 * @return the number of bytes read on success, GNUNET_SYSERR on failure
762 GNUNET_DISK_file_read_non_blocking (const struct GNUNET_DISK_FileHandle * h,
763 void *result, size_t len)
768 return GNUNET_SYSERR;
774 if (h->type != GNUNET_PIPE)
776 if (!ReadFile (h->h, result, len, &bytesRead, NULL))
778 SetErrnoFromWinError (GetLastError ());
779 return GNUNET_SYSERR;
785 LOG (GNUNET_ERROR_TYPE_DEBUG, "It is a pipe, trying to read\n");
787 if (!ReadFile (h->h, result, len, &bytesRead, h->oOverlapRead))
789 if (GetLastError () != ERROR_IO_PENDING)
792 LOG (GNUNET_ERROR_TYPE_DEBUG, "Error reading from pipe: %u\n", GetLastError ());
794 SetErrnoFromWinError (GetLastError ());
795 return GNUNET_SYSERR;
800 LOG (GNUNET_ERROR_TYPE_DEBUG,
801 "ReadFile() queued a read, cancelling\n");
805 return GNUNET_SYSERR;
809 LOG (GNUNET_ERROR_TYPE_DEBUG, "Read %u bytes\n", bytesRead);
817 /* set to non-blocking, read, then set back */
818 flags = fcntl (h->fd, F_GETFL);
819 if (0 == (flags & O_NONBLOCK))
820 fcntl (h->fd, F_SETFL, flags | O_NONBLOCK);
821 ret = read (h->fd, result, len);
822 if (0 == (flags & O_NONBLOCK))
823 fcntl (h->fd, F_SETFL, flags);
830 * Read the contents of a binary file into a buffer.
832 * @param fn file name
833 * @param result the buffer to write the result to
834 * @param len the maximum number of bytes to read
835 * @return number of bytes read, GNUNET_SYSERR on failure
838 GNUNET_DISK_fn_read (const char *fn, void *result, size_t len)
840 struct GNUNET_DISK_FileHandle *fh;
843 fh = GNUNET_DISK_file_open (fn, GNUNET_DISK_OPEN_READ, GNUNET_DISK_PERM_NONE);
845 return GNUNET_SYSERR;
846 ret = GNUNET_DISK_file_read (fh, result, len);
847 GNUNET_assert (GNUNET_OK == GNUNET_DISK_file_close (fh));
854 * Write a buffer to a file.
855 * @param h handle to open file
856 * @param buffer the data to write
857 * @param n number of bytes to write
858 * @return number of bytes written on success, GNUNET_SYSERR on error
861 GNUNET_DISK_file_write (const struct GNUNET_DISK_FileHandle * h,
862 const void *buffer, size_t n)
867 return GNUNET_SYSERR;
873 if (h->type != GNUNET_PIPE)
875 if (!WriteFile (h->h, buffer, n, &bytesWritten, NULL))
877 SetErrnoFromWinError (GetLastError ());
878 return GNUNET_SYSERR;
884 LOG (GNUNET_ERROR_TYPE_DEBUG, "It is a pipe trying to write %u bytes\n", n);
886 if (!WriteFile (h->h, buffer, n, &bytesWritten, h->oOverlapWrite))
888 if (GetLastError () != ERROR_IO_PENDING)
890 SetErrnoFromWinError (GetLastError ());
892 LOG (GNUNET_ERROR_TYPE_DEBUG, "Error writing to pipe: %u\n",
895 return GNUNET_SYSERR;
898 LOG (GNUNET_ERROR_TYPE_DEBUG, "Will get overlapped result\n");
900 if (!GetOverlappedResult (h->h, h->oOverlapWrite, &bytesWritten, TRUE))
902 SetErrnoFromWinError (GetLastError ());
904 LOG (GNUNET_ERROR_TYPE_DEBUG,
905 "Error getting overlapped result while writing to pipe: %u\n",
908 return GNUNET_SYSERR;
914 if (!GetOverlappedResult (h->h, h->oOverlapWrite, &ovr, TRUE))
917 LOG (GNUNET_ERROR_TYPE_DEBUG,
918 "Error getting control overlapped result while writing to pipe: %u\n",
925 LOG (GNUNET_ERROR_TYPE_DEBUG,
926 "Wrote %u bytes (ovr says %u), picking the greatest\n",
931 if (bytesWritten == 0)
936 LOG (GNUNET_ERROR_TYPE_DEBUG, "Wrote %u bytes, returning -1 with EAGAIN\n", bytesWritten);
939 return GNUNET_SYSERR;
943 LOG (GNUNET_ERROR_TYPE_DEBUG, "Wrote %u bytes\n", bytesWritten);
948 return write (h->fd, buffer, n);
954 * Write a buffer to a file, blocking, if necessary.
955 * @param h handle to open file
956 * @param buffer the data to write
957 * @param n number of bytes to write
958 * @return number of bytes written on success, GNUNET_SYSERR on error
961 GNUNET_DISK_file_write_blocking (const struct GNUNET_DISK_FileHandle * h,
962 const void *buffer, size_t n)
967 return GNUNET_SYSERR;
972 /* We do a non-overlapped write, which is as blocking as it gets */
974 LOG (GNUNET_ERROR_TYPE_DEBUG, "Writing %u bytes\n", n);
976 if (!WriteFile (h->h, buffer, n, &bytesWritten, NULL))
978 SetErrnoFromWinError (GetLastError ());
980 LOG (GNUNET_ERROR_TYPE_DEBUG, "Error writing to pipe: %u\n",
983 return GNUNET_SYSERR;
985 if (bytesWritten == 0 && n > 0)
988 LOG (GNUNET_ERROR_TYPE_DEBUG, "Waiting for pipe to clean\n");
990 WaitForSingleObject (h->h, INFINITE);
991 if (!WriteFile (h->h, buffer, n, &bytesWritten, NULL))
993 SetErrnoFromWinError (GetLastError ());
995 LOG (GNUNET_ERROR_TYPE_DEBUG, "Error writing to pipe: %u\n",
998 return GNUNET_SYSERR;
1002 LOG (GNUNET_ERROR_TYPE_DEBUG, "Wrote %u bytes\n", bytesWritten);
1004 return bytesWritten;
1009 /* set to blocking, write, then set back */
1010 flags = fcntl (h->fd, F_GETFL);
1011 if (0 != (flags & O_NONBLOCK))
1012 fcntl (h->fd, F_SETFL, flags - O_NONBLOCK);
1013 ret = write (h->fd, buffer, n);
1014 if (0 == (flags & O_NONBLOCK))
1015 fcntl (h->fd, F_SETFL, flags);
1022 * Write a buffer to a file. If the file is longer than the
1023 * number of bytes that will be written, it will be truncated.
1025 * @param fn file name
1026 * @param buffer the data to write
1027 * @param n number of bytes to write
1028 * @param mode file permissions
1029 * @return number of bytes written on success, GNUNET_SYSERR on error
1032 GNUNET_DISK_fn_write (const char *fn, const void *buffer, size_t n,
1033 enum GNUNET_DISK_AccessPermissions mode)
1035 struct GNUNET_DISK_FileHandle *fh;
1038 fh = GNUNET_DISK_file_open (fn,
1039 GNUNET_DISK_OPEN_WRITE | GNUNET_DISK_OPEN_TRUNCATE
1040 | GNUNET_DISK_OPEN_CREATE, mode);
1042 return GNUNET_SYSERR;
1043 ret = GNUNET_DISK_file_write (fh, buffer, n);
1044 GNUNET_assert (GNUNET_OK == GNUNET_DISK_file_close (fh));
1050 * Scan a directory for files.
1052 * @param dirName the name of the directory
1053 * @param callback the method to call for each file,
1054 * can be NULL, in that case, we only count
1055 * @param callback_cls closure for callback
1056 * @return the number of files found, GNUNET_SYSERR on error or
1057 * ieration aborted by callback returning GNUNET_SYSERR
1060 GNUNET_DISK_directory_scan (const char *dirName,
1061 GNUNET_FileNameCallback callback,
1065 struct dirent *finfo;
1070 unsigned int name_len;
1071 unsigned int n_size;
1073 GNUNET_assert (dirName != NULL);
1074 dname = GNUNET_STRINGS_filename_expand (dirName);
1076 return GNUNET_SYSERR;
1077 while ((strlen (dname) > 0) && (dname[strlen (dname) - 1] == DIR_SEPARATOR))
1078 dname[strlen (dname) - 1] = '\0';
1079 if (0 != STAT (dname, &istat))
1081 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "stat", dname);
1082 GNUNET_free (dname);
1083 return GNUNET_SYSERR;
1085 if (!S_ISDIR (istat.st_mode))
1087 LOG (GNUNET_ERROR_TYPE_WARNING, _("Expected `%s' to be a directory!\n"),
1089 GNUNET_free (dname);
1090 return GNUNET_SYSERR;
1093 dinfo = OPENDIR (dname);
1094 if ((errno == EACCES) || (dinfo == NULL))
1096 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "opendir", dname);
1099 GNUNET_free (dname);
1100 return GNUNET_SYSERR;
1103 n_size = strlen (dname) + name_len + 2;
1104 name = GNUNET_malloc (n_size);
1105 while ((finfo = READDIR (dinfo)) != NULL)
1107 if ((0 == strcmp (finfo->d_name, ".")) ||
1108 (0 == strcmp (finfo->d_name, "..")))
1110 if (callback != NULL)
1112 if (name_len < strlen (finfo->d_name))
1115 name_len = strlen (finfo->d_name);
1116 n_size = strlen (dname) + name_len + 2;
1117 name = GNUNET_malloc (n_size);
1119 /* dname can end in "/" only if dname == "/";
1120 * if dname does not end in "/", we need to add
1121 * a "/" (otherwise, we must not!) */
1122 GNUNET_snprintf (name, n_size, "%s%s%s", dname,
1123 (strcmp (dname, DIR_SEPARATOR_STR) ==
1124 0) ? "" : DIR_SEPARATOR_STR, finfo->d_name);
1125 if (GNUNET_OK != callback (callback_cls, name))
1129 GNUNET_free (dname);
1130 return GNUNET_SYSERR;
1137 GNUNET_free (dname);
1143 * Opaque handle used for iterating over a directory.
1145 struct GNUNET_DISK_DirectoryIterator
1149 * Function to call on directory entries.
1151 GNUNET_DISK_DirectoryIteratorCallback callback;
1154 * Closure for callback.
1159 * Reference to directory.
1169 * Next filename to process.
1176 enum GNUNET_SCHEDULER_Priority priority;
1182 * Task used by the directory iterator.
1185 directory_iterator_task (void *cls,
1186 const struct GNUNET_SCHEDULER_TaskContext *tc)
1188 struct GNUNET_DISK_DirectoryIterator *iter = cls;
1191 name = iter->next_name;
1192 GNUNET_assert (name != NULL);
1193 iter->next_name = NULL;
1194 iter->callback (iter->callback_cls, iter, name, iter->dirname);
1200 * This function must be called during the DiskIteratorCallback
1201 * (exactly once) to schedule the task to process the next
1202 * filename in the directory (if there is one).
1204 * @param iter opaque handle for the iterator
1205 * @param can set to GNUNET_YES to terminate the iteration early
1206 * @return GNUNET_YES if iteration will continue,
1207 * GNUNET_NO if this was the last entry (and iteration is complete),
1208 * GNUNET_SYSERR if abort was YES
1211 GNUNET_DISK_directory_iterator_next (struct GNUNET_DISK_DirectoryIterator *iter,
1214 struct dirent *finfo;
1216 GNUNET_assert (iter->next_name == NULL);
1217 if (can == GNUNET_YES)
1219 CLOSEDIR (iter->directory);
1220 GNUNET_free (iter->dirname);
1222 return GNUNET_SYSERR;
1224 while (NULL != (finfo = READDIR (iter->directory)))
1226 if ((0 == strcmp (finfo->d_name, ".")) ||
1227 (0 == strcmp (finfo->d_name, "..")))
1229 GNUNET_asprintf (&iter->next_name, "%s%s%s", iter->dirname,
1230 DIR_SEPARATOR_STR, finfo->d_name);
1235 GNUNET_DISK_directory_iterator_next (iter, GNUNET_YES);
1238 GNUNET_SCHEDULER_add_with_priority (iter->priority, &directory_iterator_task,
1245 * Scan a directory for files using the scheduler to run a task for
1246 * each entry. The name of the directory must be expanded first (!).
1247 * If a scheduler does not need to be used, GNUNET_DISK_directory_scan
1248 * may provide a simpler API.
1250 * @param prio priority to use
1251 * @param dirName the name of the directory
1252 * @param callback the method to call for each file
1253 * @param callback_cls closure for callback
1254 * @return GNUNET_YES if directory is not empty and 'callback'
1255 * will be called later, GNUNET_NO otherwise, GNUNET_SYSERR on error.
1258 GNUNET_DISK_directory_iterator_start (enum GNUNET_SCHEDULER_Priority prio,
1259 const char *dirName,
1260 GNUNET_DISK_DirectoryIteratorCallback
1261 callback, void *callback_cls)
1263 struct GNUNET_DISK_DirectoryIterator *di;
1265 di = GNUNET_malloc (sizeof (struct GNUNET_DISK_DirectoryIterator));
1266 di->callback = callback;
1267 di->callback_cls = callback_cls;
1268 di->directory = OPENDIR (dirName);
1269 if (di->directory == NULL)
1272 callback (callback_cls, NULL, NULL, NULL);
1273 return GNUNET_SYSERR;
1275 di->dirname = GNUNET_strdup (dirName);
1276 di->priority = prio;
1277 return GNUNET_DISK_directory_iterator_next (di, GNUNET_NO);
1282 * Function that removes the given directory by calling
1283 * "GNUNET_DISK_directory_remove".
1285 * @param unused not used
1286 * @param fn directory to remove
1290 remove_helper (void *unused, const char *fn)
1292 (void) GNUNET_DISK_directory_remove (fn);
1298 * Remove all files in a directory (rm -rf). Call with
1302 * @param fileName the file to remove
1303 * @return GNUNET_OK on success, GNUNET_SYSERR on error
1306 GNUNET_DISK_directory_remove (const char *fileName)
1310 if (0 != LSTAT (fileName, &istat))
1311 return GNUNET_NO; /* file may not exist... */
1312 CHMOD (fileName, S_IWUSR | S_IRUSR | S_IXUSR);
1313 if (UNLINK (fileName) == 0)
1315 if ((errno != EISDIR) &&
1316 /* EISDIR is not sufficient in all cases, e.g.
1317 * sticky /tmp directory may result in EPERM on BSD.
1318 * So we also explicitly check "isDirectory" */
1319 (GNUNET_YES != GNUNET_DISK_directory_test (fileName)))
1321 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "rmdir", fileName);
1322 return GNUNET_SYSERR;
1324 if (GNUNET_SYSERR ==
1325 GNUNET_DISK_directory_scan (fileName, &remove_helper, NULL))
1326 return GNUNET_SYSERR;
1327 if (0 != RMDIR (fileName))
1329 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "rmdir", fileName);
1330 return GNUNET_SYSERR;
1339 * @param src file to copy
1340 * @param dst destination file name
1341 * @return GNUNET_OK on success, GNUNET_SYSERR on error
1344 GNUNET_DISK_file_copy (const char *src, const char *dst)
1350 struct GNUNET_DISK_FileHandle *in;
1351 struct GNUNET_DISK_FileHandle *out;
1353 if (GNUNET_OK != GNUNET_DISK_file_size (src, &size, GNUNET_YES))
1354 return GNUNET_SYSERR;
1356 in = GNUNET_DISK_file_open (src, GNUNET_DISK_OPEN_READ,
1357 GNUNET_DISK_PERM_NONE);
1359 return GNUNET_SYSERR;
1361 GNUNET_DISK_file_open (dst,
1362 GNUNET_DISK_OPEN_WRITE | GNUNET_DISK_OPEN_CREATE |
1363 GNUNET_DISK_OPEN_FAILIFEXISTS,
1364 GNUNET_DISK_PERM_USER_READ |
1365 GNUNET_DISK_PERM_USER_WRITE |
1366 GNUNET_DISK_PERM_GROUP_READ |
1367 GNUNET_DISK_PERM_GROUP_WRITE);
1370 GNUNET_DISK_file_close (in);
1371 return GNUNET_SYSERR;
1373 buf = GNUNET_malloc (COPY_BLK_SIZE);
1376 len = COPY_BLK_SIZE;
1377 if (len > size - pos)
1379 if (len != GNUNET_DISK_file_read (in, buf, len))
1381 if (len != GNUNET_DISK_file_write (out, buf, len))
1386 GNUNET_DISK_file_close (in);
1387 GNUNET_DISK_file_close (out);
1391 GNUNET_DISK_file_close (in);
1392 GNUNET_DISK_file_close (out);
1393 return GNUNET_SYSERR;
1398 * @brief Removes special characters as ':' from a filename.
1399 * @param fn the filename to canonicalize
1402 GNUNET_DISK_filename_canonicalize (char *fn)
1412 if (c == '/' || c == '\\' || c == ':' || c == '*' || c == '?' || c == '"' ||
1413 c == '<' || c == '>' || c == '|')
1425 * @brief Change owner of a file
1427 * @param filename name of file to change the owner of
1428 * @param user name of the new owner
1429 * @return GNUNET_OK on success, GNUNET_SYSERR on failure
1432 GNUNET_DISK_file_change_owner (const char *filename, const char *user)
1437 pws = getpwnam (user);
1440 LOG (GNUNET_ERROR_TYPE_ERROR,
1441 _("Cannot obtain information about user `%s': %s\n"), user,
1443 return GNUNET_SYSERR;
1445 if (0 != chown (filename, pws->pw_uid, pws->pw_gid))
1446 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "chown", filename);
1453 * Lock a part of a file
1454 * @param fh file handle
1455 * @param lockStart absolute position from where to lock
1456 * @param lockEnd absolute position until where to lock
1457 * @param excl GNUNET_YES for an exclusive lock
1458 * @return GNUNET_OK on success, GNUNET_SYSERR on error
1461 GNUNET_DISK_file_lock (struct GNUNET_DISK_FileHandle *fh, OFF_T lockStart,
1462 OFF_T lockEnd, int excl)
1467 return GNUNET_SYSERR;
1473 memset (&fl, 0, sizeof (struct flock));
1474 fl.l_type = excl ? F_WRLCK : F_RDLCK;
1475 fl.l_whence = SEEK_SET;
1476 fl.l_start = lockStart;
1479 return fcntl (fh->fd, F_SETLK, &fl) != 0 ? GNUNET_SYSERR : GNUNET_OK;
1482 OFF_T diff = lockEnd - lockStart;
1483 DWORD diff_low, diff_high;
1484 diff_low = (DWORD) (diff & 0xFFFFFFFF);
1485 diff_high = (DWORD) ((diff >> (sizeof (DWORD) * 8)) & 0xFFFFFFFF);
1487 memset (&o, 0, sizeof (OVERLAPPED));
1488 o.Offset = (DWORD) (lockStart & 0xFFFFFFFF);;
1489 o.OffsetHigh = (DWORD) (((lockStart & ~0xFFFFFFFF) >> (sizeof (DWORD) * 8)) & 0xFFFFFFFF);
1492 (fh->h, (excl ? LOCKFILE_EXCLUSIVE_LOCK : 0) | LOCKFILE_FAIL_IMMEDIATELY,
1493 0, diff_low, diff_high, &o))
1495 SetErrnoFromWinError (GetLastError ());
1496 return GNUNET_SYSERR;
1505 * Unlock a part of a file
1506 * @param fh file handle
1507 * @param unlockStart absolute position from where to unlock
1508 * @param unlockEnd absolute position until where to unlock
1509 * @return GNUNET_OK on success, GNUNET_SYSERR on error
1512 GNUNET_DISK_file_unlock (struct GNUNET_DISK_FileHandle *fh, OFF_T unlockStart,
1518 return GNUNET_SYSERR;
1524 memset (&fl, 0, sizeof (struct flock));
1525 fl.l_type = F_UNLCK;
1526 fl.l_whence = SEEK_SET;
1527 fl.l_start = unlockStart;
1528 fl.l_len = unlockEnd;
1530 return fcntl (fh->fd, F_SETLK, &fl) != 0 ? GNUNET_SYSERR : GNUNET_OK;
1533 OFF_T diff = unlockEnd - unlockStart;
1534 DWORD diff_low, diff_high;
1535 diff_low = (DWORD) (diff & 0xFFFFFFFF);
1536 diff_high = (DWORD) ((diff >> (sizeof (DWORD) * 8)) & 0xFFFFFFFF);
1538 memset (&o, 0, sizeof (OVERLAPPED));
1539 o.Offset = (DWORD) (unlockStart & 0xFFFFFFFF);;
1540 o.OffsetHigh = (DWORD) (((unlockStart & ~0xFFFFFFFF) >> (sizeof (DWORD) * 8)) & 0xFFFFFFFF);
1542 if (!UnlockFileEx (fh->h, 0, diff_low, diff_high, &o))
1544 SetErrnoFromWinError (GetLastError ());
1545 return GNUNET_SYSERR;
1554 * Open a file. Note that the access permissions will only be
1555 * used if a new file is created and if the underlying operating
1556 * system supports the given permissions.
1558 * @param fn file name to be opened
1559 * @param flags opening flags, a combination of GNUNET_DISK_OPEN_xxx bit flags
1560 * @param perm permissions for the newly created file, use
1561 * GNUNET_DISK_PERM_USER_NONE if a file could not be created by this
1562 * call (because of flags)
1563 * @return IO handle on success, NULL on error
1565 struct GNUNET_DISK_FileHandle *
1566 GNUNET_DISK_file_open (const char *fn, enum GNUNET_DISK_OpenFlags flags,
1567 enum GNUNET_DISK_AccessPermissions perm)
1570 struct GNUNET_DISK_FileHandle *ret;
1576 wchar_t wexpfn[MAX_PATH + 1];
1583 expfn = GNUNET_STRINGS_filename_expand (fn);
1588 if (GNUNET_DISK_OPEN_READWRITE == (flags & GNUNET_DISK_OPEN_READWRITE))
1589 oflags = O_RDWR; /* note: O_RDWR is NOT always O_RDONLY | O_WRONLY */
1590 else if (flags & GNUNET_DISK_OPEN_READ)
1592 else if (flags & GNUNET_DISK_OPEN_WRITE)
1597 GNUNET_free (expfn);
1600 if (flags & GNUNET_DISK_OPEN_FAILIFEXISTS)
1601 oflags |= (O_CREAT | O_EXCL);
1602 if (flags & GNUNET_DISK_OPEN_TRUNCATE)
1604 if (flags & GNUNET_DISK_OPEN_APPEND)
1606 if (flags & GNUNET_DISK_OPEN_CREATE)
1608 (void) GNUNET_DISK_directory_create_for_file (expfn);
1610 mode = translate_unix_perms (perm);
1613 fd = open (expfn, oflags | O_LARGEFILE, mode);
1616 if (0 == (flags & GNUNET_DISK_OPEN_FAILIFEXISTS))
1617 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "open", expfn);
1619 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_DEBUG, "open", expfn);
1620 GNUNET_free (expfn);
1627 if (GNUNET_DISK_OPEN_READWRITE == (flags & GNUNET_DISK_OPEN_READWRITE))
1628 access = FILE_READ_DATA | FILE_WRITE_DATA;
1629 else if (flags & GNUNET_DISK_OPEN_READ)
1630 access = FILE_READ_DATA;
1631 else if (flags & GNUNET_DISK_OPEN_WRITE)
1632 access = FILE_WRITE_DATA;
1634 if (flags & GNUNET_DISK_OPEN_FAILIFEXISTS)
1638 else if (flags & GNUNET_DISK_OPEN_CREATE)
1640 (void) GNUNET_DISK_directory_create_for_file (expfn);
1641 if (flags & GNUNET_DISK_OPEN_TRUNCATE)
1642 disp = CREATE_ALWAYS;
1646 else if (flags & GNUNET_DISK_OPEN_TRUNCATE)
1648 disp = TRUNCATE_EXISTING;
1652 disp = OPEN_EXISTING;
1655 if (ERROR_SUCCESS == plibc_conv_to_win_pathwconv(expfn, wexpfn))
1656 h = CreateFileW (wexpfn, access,
1657 FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE, NULL,
1658 disp, FILE_ATTRIBUTE_NORMAL, NULL);
1660 h = INVALID_HANDLE_VALUE;
1661 if (h == INVALID_HANDLE_VALUE)
1663 SetErrnoFromWinError (GetLastError ());
1664 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "open", expfn);
1665 GNUNET_free (expfn);
1669 if (flags & GNUNET_DISK_OPEN_APPEND)
1670 if (SetFilePointer (h, 0, 0, FILE_END) == INVALID_SET_FILE_POINTER)
1672 SetErrnoFromWinError (GetLastError ());
1673 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "SetFilePointer", expfn);
1675 GNUNET_free (expfn);
1680 ret = GNUNET_malloc (sizeof (struct GNUNET_DISK_FileHandle));
1683 ret->type = GNUNET_DISK_FILE;
1687 GNUNET_free (expfn);
1693 * Close an open file
1694 * @param h file handle
1695 * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
1698 GNUNET_DISK_file_close (struct GNUNET_DISK_FileHandle *h)
1703 return GNUNET_SYSERR;
1707 if (!CloseHandle (h->h))
1709 SetErrnoFromWinError (GetLastError ());
1710 LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING, "close");
1711 GNUNET_free (h->oOverlapRead);
1712 GNUNET_free (h->oOverlapWrite);
1714 return GNUNET_SYSERR;
1717 if (close (h->fd) != 0)
1719 LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING, "close");
1721 return GNUNET_SYSERR;
1730 * Construct full path to a file inside of the private
1731 * directory used by GNUnet. Also creates the corresponding
1732 * directory. If the resulting name is supposed to be
1733 * a directory, end the last argument in '/' (or pass
1734 * DIR_SEPARATOR_STR as the last argument before NULL).
1736 * @param cfg configuration to use (determines HOME)
1737 * @param serviceName name of the service
1738 * @param ... is NULL-terminated list of
1739 * path components to append to the
1740 * private directory name.
1741 * @return the constructed filename
1744 GNUNET_DISK_get_home_filename (const struct GNUNET_CONFIGURATION_Handle *cfg,
1745 const char *serviceName, ...)
1751 unsigned int needed;
1754 GNUNET_CONFIGURATION_get_value_filename (cfg, serviceName, "HOME", &pfx))
1758 LOG (GNUNET_ERROR_TYPE_WARNING,
1759 _("No `%s' specified for service `%s' in configuration.\n"), "HOME",
1763 needed = strlen (pfx) + 2;
1764 if ((pfx[strlen (pfx) - 1] != '/') && (pfx[strlen (pfx) - 1] != '\\'))
1766 va_start (ap, serviceName);
1769 c = va_arg (ap, const char *);
1773 needed += strlen (c);
1774 if ((c[strlen (c) - 1] != '/') && (c[strlen (c) - 1] != '\\'))
1778 ret = GNUNET_malloc (needed);
1781 va_start (ap, serviceName);
1784 c = va_arg (ap, const char *);
1788 if ((c[strlen (c) - 1] != '/') && (c[strlen (c) - 1] != '\\'))
1789 strcat (ret, DIR_SEPARATOR_STR);
1793 if ((ret[strlen (ret) - 1] != '/') && (ret[strlen (ret) - 1] != '\\'))
1794 (void) GNUNET_DISK_directory_create_for_file (ret);
1796 (void) GNUNET_DISK_directory_create (ret);
1802 * Handle for a memory-mapping operation.
1804 struct GNUNET_DISK_MapHandle
1807 * Address where the map is in memory.
1813 * Underlying OS handle.
1818 * Number of bytes mapped.
1826 #define MAP_FAILED ((void *) -1)
1830 * Map a file into memory
1832 * @param h open file handle
1833 * @param m handle to the new mapping
1834 * @param access access specification, GNUNET_DISK_MAP_TYPE_xxx
1835 * @param len size of the mapping
1836 * @return pointer to the mapped memory region, NULL on failure
1839 GNUNET_DISK_file_map (const struct GNUNET_DISK_FileHandle *h,
1840 struct GNUNET_DISK_MapHandle **m,
1841 enum GNUNET_DISK_MapType access, size_t len)
1850 DWORD mapAccess, protect;
1852 if ((access & GNUNET_DISK_MAP_TYPE_READ) &&
1853 (access & GNUNET_DISK_MAP_TYPE_WRITE))
1855 protect = PAGE_READWRITE;
1856 mapAccess = FILE_MAP_ALL_ACCESS;
1858 else if (access & GNUNET_DISK_MAP_TYPE_READ)
1860 protect = PAGE_READONLY;
1861 mapAccess = FILE_MAP_READ;
1863 else if (access & GNUNET_DISK_MAP_TYPE_WRITE)
1865 protect = PAGE_READWRITE;
1866 mapAccess = FILE_MAP_WRITE;
1874 *m = GNUNET_malloc (sizeof (struct GNUNET_DISK_MapHandle));
1875 (*m)->h = CreateFileMapping (h->h, NULL, protect, 0, 0, NULL);
1876 if ((*m)->h == INVALID_HANDLE_VALUE)
1878 SetErrnoFromWinError (GetLastError ());
1883 (*m)->addr = MapViewOfFile ((*m)->h, mapAccess, 0, 0, len);
1886 SetErrnoFromWinError (GetLastError ());
1887 CloseHandle ((*m)->h);
1896 if (access & GNUNET_DISK_MAP_TYPE_READ)
1898 if (access & GNUNET_DISK_MAP_TYPE_WRITE)
1900 *m = GNUNET_malloc (sizeof (struct GNUNET_DISK_MapHandle));
1901 (*m)->addr = mmap (NULL, len, prot, MAP_SHARED, h->fd, 0);
1902 GNUNET_assert (NULL != (*m)->addr);
1903 if (MAP_FAILED == (*m)->addr)
1915 * @param h mapping handle
1916 * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
1919 GNUNET_DISK_file_unmap (struct GNUNET_DISK_MapHandle *h)
1926 return GNUNET_SYSERR;
1930 ret = UnmapViewOfFile (h->addr) ? GNUNET_OK : GNUNET_SYSERR;
1931 if (ret != GNUNET_OK)
1932 SetErrnoFromWinError (GetLastError ());
1933 if (!CloseHandle (h->h) && (ret == GNUNET_OK))
1935 ret = GNUNET_SYSERR;
1936 SetErrnoFromWinError (GetLastError ());
1939 ret = munmap (h->addr, h->len) != -1 ? GNUNET_OK : GNUNET_SYSERR;
1947 * Write file changes to disk
1948 * @param h handle to an open file
1949 * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
1952 GNUNET_DISK_file_sync (const struct GNUNET_DISK_FileHandle *h)
1957 return GNUNET_SYSERR;
1963 ret = FlushFileBuffers (h->h) ? GNUNET_OK : GNUNET_SYSERR;
1964 if (ret != GNUNET_OK)
1965 SetErrnoFromWinError (GetLastError ());
1967 #elif defined(FREEBSD) || defined(OPENBSD) || defined(DARWIN)
1968 return fsync (h->fd) == -1 ? GNUNET_SYSERR : GNUNET_OK;
1970 return fdatasync (h->fd) == -1 ? GNUNET_SYSERR : GNUNET_OK;
1976 /* Copyright Bob Byrnes <byrnes <at> curl.com>
1977 http://permalink.gmane.org/gmane.os.cygwin.patches/2121
1979 /* Create a pipe, and return handles to the read and write ends,
1980 just like CreatePipe, but ensure that the write end permits
1981 FILE_READ_ATTRIBUTES access, on later versions of win32 where
1982 this is supported. This access is needed by NtQueryInformationFile,
1983 which is used to implement select and nonblocking writes.
1984 Note that the return value is either NO_ERROR or GetLastError,
1985 unlike CreatePipe, which returns a bool for success or failure. */
1987 create_selectable_pipe (PHANDLE read_pipe_ptr, PHANDLE write_pipe_ptr,
1988 LPSECURITY_ATTRIBUTES sa_ptr, DWORD psize,
1989 DWORD dwReadMode, DWORD dwWriteMode)
1991 /* Default to error. */
1992 *read_pipe_ptr = *write_pipe_ptr = INVALID_HANDLE_VALUE;
1994 HANDLE read_pipe = INVALID_HANDLE_VALUE, write_pipe = INVALID_HANDLE_VALUE;
1996 /* Ensure that there is enough pipe buffer space for atomic writes. */
1997 if (psize < PIPE_BUF)
2000 char pipename[MAX_PATH];
2002 /* Retry CreateNamedPipe as long as the pipe name is in use.
2003 * Retrying will probably never be necessary, but we want
2004 * to be as robust as possible. */
2007 static volatile LONG pipe_unique_id;
2009 snprintf (pipename, sizeof pipename, "\\\\.\\pipe\\gnunet-%d-%ld",
2010 getpid (), InterlockedIncrement ((LONG *) & pipe_unique_id));
2012 LOG (GNUNET_ERROR_TYPE_DEBUG, "CreateNamedPipe: name = %s, size = %lu\n",
2015 /* Use CreateNamedPipe instead of CreatePipe, because the latter
2016 * returns a write handle that does not permit FILE_READ_ATTRIBUTES
2017 * access, on versions of win32 earlier than WinXP SP2.
2018 * CreatePipe also stupidly creates a full duplex pipe, which is
2019 * a waste, since only a single direction is actually used.
2020 * It's important to only allow a single instance, to ensure that
2021 * the pipe was not created earlier by some other process, even if
2022 * the pid has been reused. We avoid FILE_FLAG_FIRST_PIPE_INSTANCE
2023 * because that is only available for Win2k SP2 and WinXP. */
2024 read_pipe = CreateNamedPipeA (pipename, PIPE_ACCESS_INBOUND | dwReadMode, PIPE_TYPE_BYTE | PIPE_READMODE_BYTE, 1, /* max instances */
2025 psize, /* output buffer size */
2026 psize, /* input buffer size */
2027 NMPWAIT_USE_DEFAULT_WAIT, sa_ptr);
2029 if (read_pipe != INVALID_HANDLE_VALUE)
2032 LOG (GNUNET_ERROR_TYPE_DEBUG, "pipe read handle = %p\n", read_pipe);
2037 DWORD err = GetLastError ();
2041 case ERROR_PIPE_BUSY:
2042 /* The pipe is already open with compatible parameters.
2043 * Pick a new name and retry. */
2045 LOG (GNUNET_ERROR_TYPE_DEBUG, "pipe busy, retrying\n");
2048 case ERROR_ACCESS_DENIED:
2049 /* The pipe is already open with incompatible parameters.
2050 * Pick a new name and retry. */
2052 LOG (GNUNET_ERROR_TYPE_DEBUG, "pipe access denied, retrying\n");
2055 case ERROR_CALL_NOT_IMPLEMENTED:
2056 /* We are on an older Win9x platform without named pipes.
2057 * Return an anonymous pipe as the best approximation. */
2059 LOG (GNUNET_ERROR_TYPE_DEBUG,
2060 "CreateNamedPipe not implemented, resorting to "
2061 "CreatePipe: size = %lu\n", psize);
2063 if (CreatePipe (read_pipe_ptr, write_pipe_ptr, sa_ptr, psize))
2066 LOG (GNUNET_ERROR_TYPE_DEBUG, "pipe read handle = %p\n",
2068 LOG (GNUNET_ERROR_TYPE_DEBUG, "pipe write handle = %p\n",
2073 err = GetLastError ();
2074 LOG (GNUNET_ERROR_TYPE_ERROR, "CreatePipe failed: %d\n", err);
2077 LOG (GNUNET_ERROR_TYPE_ERROR, "CreateNamedPipe failed: %d\n", err);
2083 LOG (GNUNET_ERROR_TYPE_DEBUG, "CreateFile: name = %s\n", pipename);
2086 /* Open the named pipe for writing.
2087 * Be sure to permit FILE_READ_ATTRIBUTES access. */
2088 write_pipe = CreateFileA (pipename, GENERIC_WRITE | FILE_READ_ATTRIBUTES, 0, /* share mode */
2089 sa_ptr, OPEN_EXISTING, dwWriteMode, /* flags and attributes */
2090 0); /* handle to template file */
2092 if (write_pipe == INVALID_HANDLE_VALUE)
2095 DWORD err = GetLastError ();
2098 LOG (GNUNET_ERROR_TYPE_DEBUG, "CreateFile failed: %d\n", err);
2100 CloseHandle (read_pipe);
2104 LOG (GNUNET_ERROR_TYPE_DEBUG, "pipe write handle = %p\n", write_pipe);
2107 *read_pipe_ptr = read_pipe;
2108 *write_pipe_ptr = write_pipe;
2115 * Creates an interprocess channel
2117 * @param blocking_read creates an asynchronous pipe for reading if set to GNUNET_NO
2118 * @param blocking_write creates an asynchronous pipe for writing if set to GNUNET_NO
2119 * @param inherit_read inherit the parent processes stdin (only for windows)
2120 * @param inherit_write inherit the parent processes stdout (only for windows)
2121 * @return handle to the new pipe, NULL on error
2123 struct GNUNET_DISK_PipeHandle *
2124 GNUNET_DISK_pipe (int blocking_read, int blocking_write, int inherit_read, int inherit_write)
2135 LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR, "pipe");
2139 return GNUNET_DISK_pipe_from_fd (blocking_read,
2143 struct GNUNET_DISK_PipeHandle *p;
2144 struct GNUNET_DISK_FileHandle *fds;
2149 p = GNUNET_malloc (sizeof (struct GNUNET_DISK_PipeHandle) +
2150 2 * sizeof (struct GNUNET_DISK_FileHandle));
2151 fds = (struct GNUNET_DISK_FileHandle *) &p[1];
2155 /* All pipes are overlapped. If you want them to block - just
2156 * call WriteFile() and ReadFile() with NULL overlapped pointer.
2159 create_selectable_pipe (&p->fd[0]->h, &p->fd[1]->h, NULL, 0,
2160 FILE_FLAG_OVERLAPPED,
2161 FILE_FLAG_OVERLAPPED);
2165 SetErrnoFromWinError (GetLastError ());
2168 if (!DuplicateHandle
2169 (GetCurrentProcess (), p->fd[0]->h, GetCurrentProcess (), &tmp_handle, 0,
2170 inherit_read == GNUNET_YES ? TRUE : FALSE, DUPLICATE_SAME_ACCESS))
2172 SetErrnoFromWinError (GetLastError ());
2173 CloseHandle (p->fd[0]->h);
2174 CloseHandle (p->fd[1]->h);
2178 CloseHandle (p->fd[0]->h);
2179 p->fd[0]->h = tmp_handle;
2181 if (!DuplicateHandle
2182 (GetCurrentProcess (), p->fd[1]->h, GetCurrentProcess (), &tmp_handle, 0,
2183 inherit_write == GNUNET_YES ? TRUE : FALSE, DUPLICATE_SAME_ACCESS))
2185 SetErrnoFromWinError (GetLastError ());
2186 CloseHandle (p->fd[0]->h);
2187 CloseHandle (p->fd[1]->h);
2191 CloseHandle (p->fd[1]->h);
2192 p->fd[1]->h = tmp_handle;
2194 p->fd[0]->type = GNUNET_PIPE;
2195 p->fd[1]->type = GNUNET_PIPE;
2197 p->fd[0]->oOverlapRead = GNUNET_malloc (sizeof (OVERLAPPED));
2198 p->fd[0]->oOverlapWrite = GNUNET_malloc (sizeof (OVERLAPPED));
2199 p->fd[1]->oOverlapRead = GNUNET_malloc (sizeof (OVERLAPPED));
2200 p->fd[1]->oOverlapWrite = GNUNET_malloc (sizeof (OVERLAPPED));
2202 p->fd[0]->oOverlapRead->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2203 p->fd[0]->oOverlapWrite->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2205 p->fd[1]->oOverlapRead->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2206 p->fd[1]->oOverlapWrite->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2214 * Creates a pipe object from a couple of file descriptors.
2215 * Useful for wrapping existing pipe FDs.
2217 * @param blocking_read creates an asynchronous pipe for reading if set to GNUNET_NO
2218 * @param blocking_write creates an asynchronous pipe for writing if set to GNUNET_NO
2219 * @param fd an array of two fd values. One of them may be -1 for read-only or write-only pipes
2221 * @return handle to the new pipe, NULL on error
2223 struct GNUNET_DISK_PipeHandle *
2224 GNUNET_DISK_pipe_from_fd (int blocking_read, int blocking_write, int fd[2])
2226 struct GNUNET_DISK_PipeHandle *p;
2227 struct GNUNET_DISK_FileHandle *fds;
2229 p = GNUNET_malloc (sizeof (struct GNUNET_DISK_PipeHandle) +
2230 2 * sizeof (struct GNUNET_DISK_FileHandle));
2231 fds = (struct GNUNET_DISK_FileHandle *) &p[1];
2239 p->fd[0]->fd = fd[0];
2240 p->fd[1]->fd = fd[1];
2246 flags = fcntl (fd[0], F_GETFL);
2247 flags |= O_NONBLOCK;
2248 if (0 > fcntl (fd[0], F_SETFL, flags))
2254 flags = fcntl (fd[0], F_GETFD);
2255 flags |= FD_CLOEXEC;
2256 if (0 > fcntl (fd[0], F_SETFD, flags))
2265 if (!blocking_write)
2267 flags = fcntl (fd[1], F_GETFL);
2268 flags |= O_NONBLOCK;
2269 if (0 > fcntl (fd[1], F_SETFL, flags))
2275 flags = fcntl (fd[1], F_GETFD);
2276 flags |= FD_CLOEXEC;
2277 if (0 > fcntl (fd[1], F_SETFD, flags))
2286 LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR, "fcntl");
2287 if (p->fd[0]->fd >= 0)
2288 GNUNET_break (0 == close (p->fd[0]->fd));
2289 if (p->fd[1]->fd >= 0)
2290 GNUNET_break (0 == close (p->fd[1]->fd));
2297 p->fd[0]->h = _get_osfhandle (fd[0]);
2299 p->fd[0]->h = INVALID_HANDLE_VALUE;
2301 p->fd[1]->h = _get_osfhandle (fd[1]);
2303 p->fd[1]->h = INVALID_HANDLE_VALUE;
2305 if (p->fd[0]->h != INVALID_HANDLE_VALUE)
2307 p->fd[0]->type = GNUNET_PIPE;
2308 p->fd[0]->oOverlapRead = GNUNET_malloc (sizeof (OVERLAPPED));
2309 p->fd[0]->oOverlapWrite = GNUNET_malloc (sizeof (OVERLAPPED));
2310 p->fd[0]->oOverlapRead->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2311 p->fd[0]->oOverlapWrite->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2314 if (p->fd[1]->h != INVALID_HANDLE_VALUE)
2316 p->fd[1]->type = GNUNET_PIPE;
2317 p->fd[1]->oOverlapRead = GNUNET_malloc (sizeof (OVERLAPPED));
2318 p->fd[1]->oOverlapWrite = GNUNET_malloc (sizeof (OVERLAPPED));
2319 p->fd[1]->oOverlapRead->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2320 p->fd[1]->oOverlapWrite->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2328 * Closes an interprocess channel
2330 * @param p pipe to close
2331 * @param end which end of the pipe to close
2332 * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
2335 GNUNET_DISK_pipe_close_end (struct GNUNET_DISK_PipeHandle *p,
2336 enum GNUNET_DISK_PipeEnd end)
2338 int ret = GNUNET_OK;
2342 if (end == GNUNET_DISK_PIPE_END_READ)
2344 if (p->fd[0]->h != INVALID_HANDLE_VALUE)
2346 if (!CloseHandle (p->fd[0]->h))
2348 SetErrnoFromWinError (GetLastError ());
2349 ret = GNUNET_SYSERR;
2351 GNUNET_free (p->fd[0]->oOverlapRead);
2352 GNUNET_free (p->fd[0]->oOverlapWrite);
2353 p->fd[0]->h = INVALID_HANDLE_VALUE;
2356 else if (end == GNUNET_DISK_PIPE_END_WRITE)
2358 if (p->fd[0]->h != INVALID_HANDLE_VALUE)
2360 if (!CloseHandle (p->fd[1]->h))
2362 SetErrnoFromWinError (GetLastError ());
2363 ret = GNUNET_SYSERR;
2365 GNUNET_free (p->fd[1]->oOverlapRead);
2366 GNUNET_free (p->fd[1]->oOverlapWrite);
2367 p->fd[1]->h = INVALID_HANDLE_VALUE;
2373 if (end == GNUNET_DISK_PIPE_END_READ)
2375 if (0 != close (p->fd[0]->fd))
2377 ret = GNUNET_SYSERR;
2382 else if (end == GNUNET_DISK_PIPE_END_WRITE)
2384 if (0 != close (p->fd[1]->fd))
2386 ret = GNUNET_SYSERR;
2398 * Closes an interprocess channel
2400 * @param p pipe to close
2401 * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
2404 GNUNET_DISK_pipe_close (struct GNUNET_DISK_PipeHandle *p)
2406 int ret = GNUNET_OK;
2410 if (p->fd[0]->h != INVALID_HANDLE_VALUE)
2412 if (!CloseHandle (p->fd[0]->h))
2414 SetErrnoFromWinError (GetLastError ());
2415 ret = GNUNET_SYSERR;
2417 GNUNET_free (p->fd[0]->oOverlapRead);
2418 GNUNET_free (p->fd[0]->oOverlapWrite);
2420 if (p->fd[1]->h != INVALID_HANDLE_VALUE)
2422 if (!CloseHandle (p->fd[1]->h))
2424 SetErrnoFromWinError (GetLastError ());
2425 ret = GNUNET_SYSERR;
2427 GNUNET_free (p->fd[1]->oOverlapRead);
2428 GNUNET_free (p->fd[1]->oOverlapWrite);
2433 if (p->fd[0]->fd != -1)
2435 if (0 != close (p->fd[0]->fd))
2437 ret = GNUNET_SYSERR;
2442 if (p->fd[1]->fd != -1)
2444 if (0 != close (p->fd[1]->fd))
2446 ret = GNUNET_SYSERR;
2458 * Get the handle to a particular pipe end
2461 * @param n end to access
2462 * @return handle for the respective end
2464 const struct GNUNET_DISK_FileHandle *
2465 GNUNET_DISK_pipe_handle (const struct GNUNET_DISK_PipeHandle *p,
2466 enum GNUNET_DISK_PipeEnd n)
2470 case GNUNET_DISK_PIPE_END_READ:
2471 case GNUNET_DISK_PIPE_END_WRITE:
2481 * Retrieve OS file handle
2483 * @param fh GNUnet file descriptor
2484 * @param dst destination buffer
2485 * @param dst_len length of dst
2486 * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
2489 GNUNET_DISK_internal_file_handle_ (const struct GNUNET_DISK_FileHandle *fh,
2490 void *dst, size_t dst_len)
2493 if (dst_len < sizeof (HANDLE))
2494 return GNUNET_SYSERR;
2495 *((HANDLE *) dst) = fh->h;
2497 if (dst_len < sizeof (int))
2498 return GNUNET_SYSERR;
2499 *((int *) dst) = fh->fd;