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;
117 * GNUNET_YES if mode is file-only (return total == -1 for directories).
119 int single_file_mode;
124 translate_unix_perms (enum GNUNET_DISK_AccessPermissions perm)
129 if (perm & GNUNET_DISK_PERM_USER_READ)
131 if (perm & GNUNET_DISK_PERM_USER_WRITE)
133 if (perm & GNUNET_DISK_PERM_USER_EXEC)
135 if (perm & GNUNET_DISK_PERM_GROUP_READ)
137 if (perm & GNUNET_DISK_PERM_GROUP_WRITE)
139 if (perm & GNUNET_DISK_PERM_GROUP_EXEC)
141 if (perm & GNUNET_DISK_PERM_OTHER_READ)
143 if (perm & GNUNET_DISK_PERM_OTHER_WRITE)
145 if (perm & GNUNET_DISK_PERM_OTHER_EXEC)
153 * Iterate over all files in the given directory and
154 * accumulate their size.
156 * @param cls closure of type "struct GetFileSizeData"
157 * @param fn current filename we are looking at
158 * @return GNUNET_SYSERR on serious errors, otherwise GNUNET_OK
161 getSizeRec (void *cls, const char *fn)
163 struct GetFileSizeData *gfsd = cls;
172 if (0 != STAT64 (fn, &buf))
174 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_DEBUG, "stat64", fn);
175 return GNUNET_SYSERR;
178 if (0 != STAT (fn, &buf))
180 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_DEBUG, "stat", fn);
181 return GNUNET_SYSERR;
184 if ((S_ISDIR (buf.st_mode)) && (gfsd->single_file_mode == GNUNET_YES))
187 return GNUNET_SYSERR;
189 if ((!S_ISLNK (buf.st_mode)) || (gfsd->include_sym_links == GNUNET_YES))
190 gfsd->total += buf.st_size;
191 if ((S_ISDIR (buf.st_mode)) && (0 == ACCESS (fn, X_OK)) &&
192 ((!S_ISLNK (buf.st_mode)) || (gfsd->include_sym_links == GNUNET_YES)))
194 if (GNUNET_SYSERR == GNUNET_DISK_directory_scan (fn, &getSizeRec, gfsd))
195 return GNUNET_SYSERR;
202 * Checks whether a handle is invalid
204 * @param h handle to check
205 * @return GNUNET_YES if invalid, GNUNET_NO if valid
208 GNUNET_DISK_handle_invalid (const struct GNUNET_DISK_FileHandle *h)
211 return ((!h) || (h->h == INVALID_HANDLE_VALUE)) ? GNUNET_YES : GNUNET_NO;
213 return ((!h) || (h->fd == -1)) ? GNUNET_YES : GNUNET_NO;
218 * Get the size of an open file.
220 * @param fh open file handle
221 * @param size where to write size of the file
222 * @return GNUNET_OK on success, GNUNET_SYSERR on error
225 GNUNET_DISK_file_handle_size (struct GNUNET_DISK_FileHandle *fh,
231 b = GetFileSizeEx (fh->h, &li);
234 SetErrnoFromWinError (GetLastError ());
235 return GNUNET_SYSERR;
237 *size = (OFF_T) li.QuadPart;
241 if (0 != FSTAT (fh->fd, &sbuf))
242 return GNUNET_SYSERR;
243 *size = sbuf.st_size;
250 * Move the read/write pointer in a file
252 * @param h handle of an open file
253 * @param offset position to move to
254 * @param whence specification to which position the offset parameter relates to
255 * @return the new position on success, GNUNET_SYSERR otherwise
258 GNUNET_DISK_file_seek (const struct GNUNET_DISK_FileHandle * h, OFF_T offset,
259 enum GNUNET_DISK_Seek whence)
264 return GNUNET_SYSERR;
268 LARGE_INTEGER li, new_pos;
271 static DWORD t[] = {[GNUNET_DISK_SEEK_SET] = FILE_BEGIN,
272 [GNUNET_DISK_SEEK_CUR] = FILE_CURRENT,[GNUNET_DISK_SEEK_END] = FILE_END
274 li.QuadPart = offset;
276 b = SetFilePointerEx (h->h, li, &new_pos, t[whence]);
279 SetErrnoFromWinError (GetLastError ());
280 return GNUNET_SYSERR;
282 return (OFF_T) new_pos.QuadPart;
284 static int t[] = {[GNUNET_DISK_SEEK_SET] = SEEK_SET,
285 [GNUNET_DISK_SEEK_CUR] = SEEK_CUR,[GNUNET_DISK_SEEK_END] = SEEK_END
288 return lseek (h->fd, offset, t[whence]);
294 * Get the size of the file (or directory) of the given file (in
297 * @param filename name of the file or directory
298 * @param size set to the size of the file (or,
299 * in the case of directories, the sum
300 * of all sizes of files in the directory)
301 * @param includeSymLinks should symbolic links be
303 * @param singleFileMode GNUNET_YES to only get size of one file
304 * and return GNUNET_SYSERR for directories.
305 * @return GNUNET_SYSERR on error, GNUNET_OK on success
308 GNUNET_DISK_file_size (const char *filename, uint64_t * size,
309 int includeSymLinks, int singleFileMode)
311 struct GetFileSizeData gfsd;
314 GNUNET_assert (size != NULL);
316 gfsd.include_sym_links = includeSymLinks;
317 gfsd.single_file_mode = singleFileMode;
318 ret = getSizeRec (&gfsd, filename);
325 * Obtain some unique identifiers for the given file
326 * that can be used to identify it in the local system.
327 * This function is used between GNUnet processes to
328 * quickly check if two files with the same absolute path
329 * are actually identical. The two processes represent
330 * the same peer but may communicate over the network
331 * (and the file may be on an NFS volume). This function
332 * may not be supported on all operating systems.
334 * @param filename name of the file
335 * @param dev set to the device ID
336 * @param ino set to the inode ID
337 * @return GNUNET_OK on success
340 GNUNET_DISK_file_get_identifiers (const char *filename, uint64_t * dev,
347 if ((0 == stat (filename, &sbuf)) && (0 == statvfs (filename, &fbuf)))
349 *dev = (uint64_t) fbuf.f_fsid;
350 *ino = (uint64_t) sbuf.st_ino;
357 if ((0 == stat (filename, &sbuf)) && (0 == statfs (filename, &fbuf)))
359 *dev = ((uint64_t) fbuf.f_fsid.val[0]) << 32 ||
360 ((uint64_t) fbuf.f_fsid.val[1]);
361 *ino = (uint64_t) sbuf.st_ino;
365 // FIXME NILS: test this
366 struct GNUNET_DISK_FileHandle *fh;
367 BY_HANDLE_FILE_INFORMATION info;
370 fh = GNUNET_DISK_file_open (filename, GNUNET_DISK_OPEN_READ, 0);
372 return GNUNET_SYSERR;
373 succ = GetFileInformationByHandle (fh->h, &info);
374 GNUNET_DISK_file_close (fh);
377 *dev = info.dwVolumeSerialNumber;
378 *ino = ((((uint64_t) info.nFileIndexHigh) << (sizeof (DWORD) * 8)) | info.nFileIndexLow);
382 return GNUNET_SYSERR;
385 return GNUNET_SYSERR;
390 * Create an (empty) temporary file on disk. If the given name is not
391 * an absolute path, the current 'TMPDIR' will be prepended. In any case,
392 * 6 random characters will be appended to the name to create a unique
395 * @param t component to use for the name;
396 * does NOT contain "XXXXXX" or "/tmp/".
397 * @return NULL on error, otherwise name of fresh
398 * file on disk in directory for temporary files
401 GNUNET_DISK_mktemp (const char *t)
408 if ((t[0] != '/') && (t[0] != '\\')
410 && !(isalpha ((int) t[0]) && (t[0] != '\0') && (t[1] == ':'))
414 /* FIXME: This uses system codepage on W32, not UTF-8 */
415 tmpdir = getenv ("TMPDIR");
416 tmpdir = tmpdir ? tmpdir : "/tmp";
417 GNUNET_asprintf (&tmpl, "%s/%s%s", tmpdir, t, "XXXXXX");
421 GNUNET_asprintf (&tmpl, "%s%s", t, "XXXXXX");
424 fn = (char *) GNUNET_malloc (MAX_PATH + 1);
425 if (ERROR_SUCCESS != plibc_conv_to_win_path (tmpl, fn))
435 /* FIXME: why is this not MKSTEMP()? This function is implemented in plibc.
436 * CG: really? If I put MKSTEMP here, I get a compilation error...
437 * It will assume that fn is UTF-8-encoded, if compiled with UTF-8 support.
442 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_ERROR, "mkstemp", fn);
447 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "close", fn);
453 * Get the number of blocks that are left on the partition that
454 * contains the given file (for normal users).
456 * @param part a file on the partition to check
457 * @return -1 on errors, otherwise the number of free blocks
460 GNUNET_DISK_get_blocks_available (const char *part)
465 if (0 != statvfs (part, &buf))
467 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "statfs", part);
475 wchar_t wpath[MAX_PATH + 1];
478 path = GNUNET_STRINGS_filename_expand (part);
481 /* "part" was in UTF-8, and so is "path" */
482 if (ERROR_SUCCESS != plibc_conv_to_win_pathwconv(path, wpath))
488 wcsncpy (szDrive, wpath, 3);
490 if (!GetDiskFreeSpaceW (szDrive, &dwDummy, &dwDummy, &dwBlocks, &dwDummy))
492 LOG (GNUNET_ERROR_TYPE_WARNING, _("`%s' failed for drive `%S': %u\n"),
493 "GetDiskFreeSpace", szDrive, GetLastError ());
501 if (0 != statfs (part, &s))
503 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "statfs", part);
512 * Test if "fil" is a directory.
513 * Will not print an error message if the directory
514 * does not exist. Will log errors if GNUNET_SYSERR is
515 * returned (i.e., a file exists with the same name).
517 * @param fil filename to test
518 * @return GNUNET_YES if yes, GNUNET_NO if not, GNUNET_SYSERR if it
522 GNUNET_DISK_directory_test (const char *fil)
524 struct stat filestat;
527 ret = STAT (fil, &filestat);
532 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "stat", fil);
533 return GNUNET_SYSERR;
537 if (!S_ISDIR (filestat.st_mode))
539 if (ACCESS (fil, R_OK | X_OK) < 0)
541 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "access", fil);
542 return GNUNET_SYSERR;
549 * Check that fil corresponds to a filename
550 * (of a file that exists and that is not a directory).
552 * @param fil filename to check
553 * @return GNUNET_YES if yes, GNUNET_NO if not a file, GNUNET_SYSERR if something
554 * else (will print an error message in that case, too).
557 GNUNET_DISK_file_test (const char *fil)
559 struct stat filestat;
563 rdir = GNUNET_STRINGS_filename_expand (fil);
565 return GNUNET_SYSERR;
567 ret = STAT (rdir, &filestat);
572 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "stat", rdir);
574 return GNUNET_SYSERR;
579 if (!S_ISREG (filestat.st_mode))
584 if (ACCESS (rdir, R_OK) < 0)
586 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "access", rdir);
588 return GNUNET_SYSERR;
596 * Implementation of "mkdir -p"
597 * @param dir the directory to create
598 * @returns GNUNET_OK on success, GNUNET_SYSERR on failure
601 GNUNET_DISK_directory_create (const char *dir)
608 rdir = GNUNET_STRINGS_filename_expand (dir);
610 return GNUNET_SYSERR;
614 pos = 1; /* skip heading '/' */
616 /* Local or Network path? */
617 if (strncmp (rdir, "\\\\", 2) == 0)
622 if (rdir[pos] == '\\')
632 pos = 3; /* strlen("C:\\") */
637 if ((rdir[pos] == DIR_SEPARATOR) || (pos == len))
640 ret = GNUNET_DISK_directory_test (rdir);
641 if (ret == GNUNET_SYSERR)
644 return GNUNET_SYSERR;
646 if (ret == GNUNET_NO)
649 ret = mkdir (rdir, S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH); /* 755 */
651 wchar_t wrdir[MAX_PATH + 1];
652 if (ERROR_SUCCESS == plibc_conv_to_win_pathwconv(rdir, wrdir))
653 ret = !CreateDirectoryW (wrdir, NULL);
657 if ((ret != 0) && (errno != EEXIST))
659 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_ERROR, "mkdir", rdir);
661 return GNUNET_SYSERR;
664 rdir[pos] = DIR_SEPARATOR;
674 * Create the directory structure for storing
677 * @param filename name of a file in the directory
678 * @returns GNUNET_OK on success,
679 * GNUNET_SYSERR on failure,
680 * GNUNET_NO if the directory
681 * exists but is not writeable for us
684 GNUNET_DISK_directory_create_for_file (const char *filename)
690 rdir = GNUNET_STRINGS_filename_expand (filename);
692 return GNUNET_SYSERR;
694 while ((len > 0) && (rdir[len] != DIR_SEPARATOR))
697 ret = GNUNET_DISK_directory_create (rdir);
698 if ((ret == GNUNET_OK) && (0 != ACCESS (rdir, W_OK)))
706 * Read the contents of a binary file into a buffer.
707 * @param h handle to an open file
708 * @param result the buffer to write the result to
709 * @param len the maximum number of bytes to read
710 * @return the number of bytes read on success, GNUNET_SYSERR on failure
713 GNUNET_DISK_file_read (const struct GNUNET_DISK_FileHandle * h, void *result,
719 return GNUNET_SYSERR;
725 if (h->type != GNUNET_PIPE)
727 if (!ReadFile (h->h, result, len, &bytesRead, NULL))
729 SetErrnoFromWinError (GetLastError ());
730 return GNUNET_SYSERR;
736 LOG (GNUNET_ERROR_TYPE_DEBUG, "It is a pipe trying to read\n");
738 if (!ReadFile (h->h, result, len, &bytesRead, h->oOverlapRead))
740 if (GetLastError () != ERROR_IO_PENDING)
743 LOG (GNUNET_ERROR_TYPE_DEBUG, "Error reading from pipe: %u\n", GetLastError ());
745 SetErrnoFromWinError (GetLastError ());
746 return GNUNET_SYSERR;
749 LOG (GNUNET_ERROR_TYPE_DEBUG, "Will get overlapped result\n");
751 GetOverlappedResult (h->h, h->oOverlapRead, &bytesRead, TRUE);
754 LOG (GNUNET_ERROR_TYPE_DEBUG, "Read %u bytes\n", bytesRead);
759 return read (h->fd, result, len);
765 * Read the contents of a binary file into a buffer.
766 * Guarantees not to block (returns GNUNET_SYSERR and sets errno to EAGAIN
767 * when no data can be read).
769 * @param h handle to an open file
770 * @param result the buffer to write the result to
771 * @param len the maximum number of bytes to read
772 * @return the number of bytes read on success, GNUNET_SYSERR on failure
775 GNUNET_DISK_file_read_non_blocking (const struct GNUNET_DISK_FileHandle * h,
776 void *result, size_t len)
781 return GNUNET_SYSERR;
787 if (h->type != GNUNET_PIPE)
789 if (!ReadFile (h->h, result, len, &bytesRead, NULL))
791 SetErrnoFromWinError (GetLastError ());
792 return GNUNET_SYSERR;
798 LOG (GNUNET_ERROR_TYPE_DEBUG, "It is a pipe, trying to read\n");
800 if (!ReadFile (h->h, result, len, &bytesRead, h->oOverlapRead))
802 if (GetLastError () != ERROR_IO_PENDING)
805 LOG (GNUNET_ERROR_TYPE_DEBUG, "Error reading from pipe: %u\n", GetLastError ());
807 SetErrnoFromWinError (GetLastError ());
808 return GNUNET_SYSERR;
813 LOG (GNUNET_ERROR_TYPE_DEBUG,
814 "ReadFile() queued a read, cancelling\n");
818 return GNUNET_SYSERR;
822 LOG (GNUNET_ERROR_TYPE_DEBUG, "Read %u bytes\n", bytesRead);
830 /* set to non-blocking, read, then set back */
831 flags = fcntl (h->fd, F_GETFL);
832 if (0 == (flags & O_NONBLOCK))
833 fcntl (h->fd, F_SETFL, flags | O_NONBLOCK);
834 ret = read (h->fd, result, len);
835 if (0 == (flags & O_NONBLOCK))
836 fcntl (h->fd, F_SETFL, flags);
843 * Read the contents of a binary file into a buffer.
845 * @param fn file name
846 * @param result the buffer to write the result to
847 * @param len the maximum number of bytes to read
848 * @return number of bytes read, GNUNET_SYSERR on failure
851 GNUNET_DISK_fn_read (const char *fn, void *result, size_t len)
853 struct GNUNET_DISK_FileHandle *fh;
856 fh = GNUNET_DISK_file_open (fn, GNUNET_DISK_OPEN_READ, GNUNET_DISK_PERM_NONE);
858 return GNUNET_SYSERR;
859 ret = GNUNET_DISK_file_read (fh, result, len);
860 GNUNET_assert (GNUNET_OK == GNUNET_DISK_file_close (fh));
867 * Write a buffer to a file.
868 * @param h handle to open file
869 * @param buffer the data to write
870 * @param n number of bytes to write
871 * @return number of bytes written on success, GNUNET_SYSERR on error
874 GNUNET_DISK_file_write (const struct GNUNET_DISK_FileHandle * h,
875 const void *buffer, size_t n)
880 return GNUNET_SYSERR;
886 if (h->type != GNUNET_PIPE)
888 if (!WriteFile (h->h, buffer, n, &bytesWritten, NULL))
890 SetErrnoFromWinError (GetLastError ());
891 return GNUNET_SYSERR;
897 LOG (GNUNET_ERROR_TYPE_DEBUG, "It is a pipe trying to write %u bytes\n", n);
899 if (!WriteFile (h->h, buffer, n, &bytesWritten, h->oOverlapWrite))
901 if (GetLastError () != ERROR_IO_PENDING)
903 SetErrnoFromWinError (GetLastError ());
905 LOG (GNUNET_ERROR_TYPE_DEBUG, "Error writing to pipe: %u\n",
908 return GNUNET_SYSERR;
911 LOG (GNUNET_ERROR_TYPE_DEBUG, "Will get overlapped result\n");
913 if (!GetOverlappedResult (h->h, h->oOverlapWrite, &bytesWritten, TRUE))
915 SetErrnoFromWinError (GetLastError ());
917 LOG (GNUNET_ERROR_TYPE_DEBUG,
918 "Error getting overlapped result while writing to pipe: %u\n",
921 return GNUNET_SYSERR;
927 if (!GetOverlappedResult (h->h, h->oOverlapWrite, &ovr, TRUE))
930 LOG (GNUNET_ERROR_TYPE_DEBUG,
931 "Error getting control overlapped result while writing to pipe: %u\n",
938 LOG (GNUNET_ERROR_TYPE_DEBUG,
939 "Wrote %u bytes (ovr says %u), picking the greatest\n",
944 if (bytesWritten == 0)
949 LOG (GNUNET_ERROR_TYPE_DEBUG, "Wrote %u bytes, returning -1 with EAGAIN\n", bytesWritten);
952 return GNUNET_SYSERR;
956 LOG (GNUNET_ERROR_TYPE_DEBUG, "Wrote %u bytes\n", bytesWritten);
961 return write (h->fd, buffer, n);
967 * Write a buffer to a file, blocking, if necessary.
968 * @param h handle to open file
969 * @param buffer the data to write
970 * @param n number of bytes to write
971 * @return number of bytes written on success, GNUNET_SYSERR on error
974 GNUNET_DISK_file_write_blocking (const struct GNUNET_DISK_FileHandle * h,
975 const void *buffer, size_t n)
980 return GNUNET_SYSERR;
985 /* We do a non-overlapped write, which is as blocking as it gets */
987 LOG (GNUNET_ERROR_TYPE_DEBUG, "Writing %u bytes\n", n);
989 if (!WriteFile (h->h, buffer, n, &bytesWritten, NULL))
991 SetErrnoFromWinError (GetLastError ());
993 LOG (GNUNET_ERROR_TYPE_DEBUG, "Error writing to pipe: %u\n",
996 return GNUNET_SYSERR;
998 if (bytesWritten == 0 && n > 0)
1001 LOG (GNUNET_ERROR_TYPE_DEBUG, "Waiting for pipe to clean\n");
1003 WaitForSingleObject (h->h, INFINITE);
1004 if (!WriteFile (h->h, buffer, n, &bytesWritten, NULL))
1006 SetErrnoFromWinError (GetLastError ());
1008 LOG (GNUNET_ERROR_TYPE_DEBUG, "Error writing to pipe: %u\n",
1011 return GNUNET_SYSERR;
1015 LOG (GNUNET_ERROR_TYPE_DEBUG, "Wrote %u bytes\n", bytesWritten);
1017 return bytesWritten;
1022 /* set to blocking, write, then set back */
1023 flags = fcntl (h->fd, F_GETFL);
1024 if (0 != (flags & O_NONBLOCK))
1025 fcntl (h->fd, F_SETFL, flags - O_NONBLOCK);
1026 ret = write (h->fd, buffer, n);
1027 if (0 == (flags & O_NONBLOCK))
1028 fcntl (h->fd, F_SETFL, flags);
1035 * Write a buffer to a file. If the file is longer than the
1036 * number of bytes that will be written, it will be truncated.
1038 * @param fn file name
1039 * @param buffer the data to write
1040 * @param n number of bytes to write
1041 * @param mode file permissions
1042 * @return number of bytes written on success, GNUNET_SYSERR on error
1045 GNUNET_DISK_fn_write (const char *fn, const void *buffer, size_t n,
1046 enum GNUNET_DISK_AccessPermissions mode)
1048 struct GNUNET_DISK_FileHandle *fh;
1051 fh = GNUNET_DISK_file_open (fn,
1052 GNUNET_DISK_OPEN_WRITE | GNUNET_DISK_OPEN_TRUNCATE
1053 | GNUNET_DISK_OPEN_CREATE, mode);
1055 return GNUNET_SYSERR;
1056 ret = GNUNET_DISK_file_write (fh, buffer, n);
1057 GNUNET_assert (GNUNET_OK == GNUNET_DISK_file_close (fh));
1063 * Scan a directory for files.
1065 * @param dirName the name of the directory
1066 * @param callback the method to call for each file,
1067 * can be NULL, in that case, we only count
1068 * @param callback_cls closure for callback
1069 * @return the number of files found, GNUNET_SYSERR on error or
1070 * ieration aborted by callback returning GNUNET_SYSERR
1073 GNUNET_DISK_directory_scan (const char *dirName,
1074 GNUNET_FileNameCallback callback,
1078 struct dirent *finfo;
1083 unsigned int name_len;
1084 unsigned int n_size;
1086 GNUNET_assert (dirName != NULL);
1087 dname = GNUNET_STRINGS_filename_expand (dirName);
1089 return GNUNET_SYSERR;
1090 while ((strlen (dname) > 0) && (dname[strlen (dname) - 1] == DIR_SEPARATOR))
1091 dname[strlen (dname) - 1] = '\0';
1092 if (0 != STAT (dname, &istat))
1094 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "stat", dname);
1095 GNUNET_free (dname);
1096 return GNUNET_SYSERR;
1098 if (!S_ISDIR (istat.st_mode))
1100 LOG (GNUNET_ERROR_TYPE_WARNING, _("Expected `%s' to be a directory!\n"),
1102 GNUNET_free (dname);
1103 return GNUNET_SYSERR;
1106 dinfo = OPENDIR (dname);
1107 if ((errno == EACCES) || (dinfo == NULL))
1109 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "opendir", dname);
1112 GNUNET_free (dname);
1113 return GNUNET_SYSERR;
1116 n_size = strlen (dname) + name_len + 2;
1117 name = GNUNET_malloc (n_size);
1118 while ((finfo = READDIR (dinfo)) != NULL)
1120 if ((0 == strcmp (finfo->d_name, ".")) ||
1121 (0 == strcmp (finfo->d_name, "..")))
1123 if (callback != NULL)
1125 if (name_len < strlen (finfo->d_name))
1128 name_len = strlen (finfo->d_name);
1129 n_size = strlen (dname) + name_len + 2;
1130 name = GNUNET_malloc (n_size);
1132 /* dname can end in "/" only if dname == "/";
1133 * if dname does not end in "/", we need to add
1134 * a "/" (otherwise, we must not!) */
1135 GNUNET_snprintf (name, n_size, "%s%s%s", dname,
1136 (strcmp (dname, DIR_SEPARATOR_STR) ==
1137 0) ? "" : DIR_SEPARATOR_STR, finfo->d_name);
1138 if (GNUNET_OK != callback (callback_cls, name))
1142 GNUNET_free (dname);
1143 return GNUNET_SYSERR;
1150 GNUNET_free (dname);
1156 * Opaque handle used for iterating over a directory.
1158 struct GNUNET_DISK_DirectoryIterator
1162 * Function to call on directory entries.
1164 GNUNET_DISK_DirectoryIteratorCallback callback;
1167 * Closure for callback.
1172 * Reference to directory.
1182 * Next filename to process.
1189 enum GNUNET_SCHEDULER_Priority priority;
1195 * Task used by the directory iterator.
1198 directory_iterator_task (void *cls,
1199 const struct GNUNET_SCHEDULER_TaskContext *tc)
1201 struct GNUNET_DISK_DirectoryIterator *iter = cls;
1204 name = iter->next_name;
1205 GNUNET_assert (name != NULL);
1206 iter->next_name = NULL;
1207 iter->callback (iter->callback_cls, iter, name, iter->dirname);
1213 * This function must be called during the DiskIteratorCallback
1214 * (exactly once) to schedule the task to process the next
1215 * filename in the directory (if there is one).
1217 * @param iter opaque handle for the iterator
1218 * @param can set to GNUNET_YES to terminate the iteration early
1219 * @return GNUNET_YES if iteration will continue,
1220 * GNUNET_NO if this was the last entry (and iteration is complete),
1221 * GNUNET_SYSERR if abort was YES
1224 GNUNET_DISK_directory_iterator_next (struct GNUNET_DISK_DirectoryIterator *iter,
1227 struct dirent *finfo;
1229 GNUNET_assert (iter->next_name == NULL);
1230 if (can == GNUNET_YES)
1232 CLOSEDIR (iter->directory);
1233 GNUNET_free (iter->dirname);
1235 return GNUNET_SYSERR;
1237 while (NULL != (finfo = READDIR (iter->directory)))
1239 if ((0 == strcmp (finfo->d_name, ".")) ||
1240 (0 == strcmp (finfo->d_name, "..")))
1242 GNUNET_asprintf (&iter->next_name, "%s%s%s", iter->dirname,
1243 DIR_SEPARATOR_STR, finfo->d_name);
1248 GNUNET_DISK_directory_iterator_next (iter, GNUNET_YES);
1251 GNUNET_SCHEDULER_add_with_priority (iter->priority, &directory_iterator_task,
1258 * Scan a directory for files using the scheduler to run a task for
1259 * each entry. The name of the directory must be expanded first (!).
1260 * If a scheduler does not need to be used, GNUNET_DISK_directory_scan
1261 * may provide a simpler API.
1263 * @param prio priority to use
1264 * @param dirName the name of the directory
1265 * @param callback the method to call for each file
1266 * @param callback_cls closure for callback
1267 * @return GNUNET_YES if directory is not empty and 'callback'
1268 * will be called later, GNUNET_NO otherwise, GNUNET_SYSERR on error.
1271 GNUNET_DISK_directory_iterator_start (enum GNUNET_SCHEDULER_Priority prio,
1272 const char *dirName,
1273 GNUNET_DISK_DirectoryIteratorCallback
1274 callback, void *callback_cls)
1276 struct GNUNET_DISK_DirectoryIterator *di;
1278 di = GNUNET_malloc (sizeof (struct GNUNET_DISK_DirectoryIterator));
1279 di->callback = callback;
1280 di->callback_cls = callback_cls;
1281 di->directory = OPENDIR (dirName);
1282 if (di->directory == NULL)
1285 callback (callback_cls, NULL, NULL, NULL);
1286 return GNUNET_SYSERR;
1288 di->dirname = GNUNET_strdup (dirName);
1289 di->priority = prio;
1290 return GNUNET_DISK_directory_iterator_next (di, GNUNET_NO);
1295 * Function that removes the given directory by calling
1296 * "GNUNET_DISK_directory_remove".
1298 * @param unused not used
1299 * @param fn directory to remove
1303 remove_helper (void *unused, const char *fn)
1305 (void) GNUNET_DISK_directory_remove (fn);
1311 * Remove all files in a directory (rm -rf). Call with
1315 * @param fileName the file to remove
1316 * @return GNUNET_OK on success, GNUNET_SYSERR on error
1319 GNUNET_DISK_directory_remove (const char *fileName)
1323 if (0 != LSTAT (fileName, &istat))
1324 return GNUNET_NO; /* file may not exist... */
1325 CHMOD (fileName, S_IWUSR | S_IRUSR | S_IXUSR);
1326 if (UNLINK (fileName) == 0)
1328 if ((errno != EISDIR) &&
1329 /* EISDIR is not sufficient in all cases, e.g.
1330 * sticky /tmp directory may result in EPERM on BSD.
1331 * So we also explicitly check "isDirectory" */
1332 (GNUNET_YES != GNUNET_DISK_directory_test (fileName)))
1334 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "rmdir", fileName);
1335 return GNUNET_SYSERR;
1337 if (GNUNET_SYSERR ==
1338 GNUNET_DISK_directory_scan (fileName, &remove_helper, NULL))
1339 return GNUNET_SYSERR;
1340 if (0 != RMDIR (fileName))
1342 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "rmdir", fileName);
1343 return GNUNET_SYSERR;
1352 * @param src file to copy
1353 * @param dst destination file name
1354 * @return GNUNET_OK on success, GNUNET_SYSERR on error
1357 GNUNET_DISK_file_copy (const char *src, const char *dst)
1363 struct GNUNET_DISK_FileHandle *in;
1364 struct GNUNET_DISK_FileHandle *out;
1366 if (GNUNET_OK != GNUNET_DISK_file_size (src, &size, GNUNET_YES, GNUNET_YES))
1367 return GNUNET_SYSERR;
1369 in = GNUNET_DISK_file_open (src, GNUNET_DISK_OPEN_READ,
1370 GNUNET_DISK_PERM_NONE);
1372 return GNUNET_SYSERR;
1374 GNUNET_DISK_file_open (dst,
1375 GNUNET_DISK_OPEN_WRITE | GNUNET_DISK_OPEN_CREATE |
1376 GNUNET_DISK_OPEN_FAILIFEXISTS,
1377 GNUNET_DISK_PERM_USER_READ |
1378 GNUNET_DISK_PERM_USER_WRITE |
1379 GNUNET_DISK_PERM_GROUP_READ |
1380 GNUNET_DISK_PERM_GROUP_WRITE);
1383 GNUNET_DISK_file_close (in);
1384 return GNUNET_SYSERR;
1386 buf = GNUNET_malloc (COPY_BLK_SIZE);
1389 len = COPY_BLK_SIZE;
1390 if (len > size - pos)
1392 if (len != GNUNET_DISK_file_read (in, buf, len))
1394 if (len != GNUNET_DISK_file_write (out, buf, len))
1399 GNUNET_DISK_file_close (in);
1400 GNUNET_DISK_file_close (out);
1404 GNUNET_DISK_file_close (in);
1405 GNUNET_DISK_file_close (out);
1406 return GNUNET_SYSERR;
1411 * @brief Removes special characters as ':' from a filename.
1412 * @param fn the filename to canonicalize
1415 GNUNET_DISK_filename_canonicalize (char *fn)
1425 if (c == '/' || c == '\\' || c == ':' || c == '*' || c == '?' || c == '"' ||
1426 c == '<' || c == '>' || c == '|')
1438 * @brief Change owner of a file
1440 * @param filename name of file to change the owner of
1441 * @param user name of the new owner
1442 * @return GNUNET_OK on success, GNUNET_SYSERR on failure
1445 GNUNET_DISK_file_change_owner (const char *filename, const char *user)
1450 pws = getpwnam (user);
1453 LOG (GNUNET_ERROR_TYPE_ERROR,
1454 _("Cannot obtain information about user `%s': %s\n"), user,
1456 return GNUNET_SYSERR;
1458 if (0 != chown (filename, pws->pw_uid, pws->pw_gid))
1459 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "chown", filename);
1466 * Lock a part of a file
1467 * @param fh file handle
1468 * @param lockStart absolute position from where to lock
1469 * @param lockEnd absolute position until where to lock
1470 * @param excl GNUNET_YES for an exclusive lock
1471 * @return GNUNET_OK on success, GNUNET_SYSERR on error
1474 GNUNET_DISK_file_lock (struct GNUNET_DISK_FileHandle *fh, OFF_T lockStart,
1475 OFF_T lockEnd, int excl)
1480 return GNUNET_SYSERR;
1486 memset (&fl, 0, sizeof (struct flock));
1487 fl.l_type = excl ? F_WRLCK : F_RDLCK;
1488 fl.l_whence = SEEK_SET;
1489 fl.l_start = lockStart;
1492 return fcntl (fh->fd, F_SETLK, &fl) != 0 ? GNUNET_SYSERR : GNUNET_OK;
1495 OFF_T diff = lockEnd - lockStart;
1496 DWORD diff_low, diff_high;
1497 diff_low = (DWORD) (diff & 0xFFFFFFFF);
1498 diff_high = (DWORD) ((diff >> (sizeof (DWORD) * 8)) & 0xFFFFFFFF);
1500 memset (&o, 0, sizeof (OVERLAPPED));
1501 o.Offset = (DWORD) (lockStart & 0xFFFFFFFF);;
1502 o.OffsetHigh = (DWORD) (((lockStart & ~0xFFFFFFFF) >> (sizeof (DWORD) * 8)) & 0xFFFFFFFF);
1505 (fh->h, (excl ? LOCKFILE_EXCLUSIVE_LOCK : 0) | LOCKFILE_FAIL_IMMEDIATELY,
1506 0, diff_low, diff_high, &o))
1508 SetErrnoFromWinError (GetLastError ());
1509 return GNUNET_SYSERR;
1518 * Unlock a part of a file
1519 * @param fh file handle
1520 * @param unlockStart absolute position from where to unlock
1521 * @param unlockEnd absolute position until where to unlock
1522 * @return GNUNET_OK on success, GNUNET_SYSERR on error
1525 GNUNET_DISK_file_unlock (struct GNUNET_DISK_FileHandle *fh, OFF_T unlockStart,
1531 return GNUNET_SYSERR;
1537 memset (&fl, 0, sizeof (struct flock));
1538 fl.l_type = F_UNLCK;
1539 fl.l_whence = SEEK_SET;
1540 fl.l_start = unlockStart;
1541 fl.l_len = unlockEnd;
1543 return fcntl (fh->fd, F_SETLK, &fl) != 0 ? GNUNET_SYSERR : GNUNET_OK;
1546 OFF_T diff = unlockEnd - unlockStart;
1547 DWORD diff_low, diff_high;
1548 diff_low = (DWORD) (diff & 0xFFFFFFFF);
1549 diff_high = (DWORD) ((diff >> (sizeof (DWORD) * 8)) & 0xFFFFFFFF);
1551 memset (&o, 0, sizeof (OVERLAPPED));
1552 o.Offset = (DWORD) (unlockStart & 0xFFFFFFFF);;
1553 o.OffsetHigh = (DWORD) (((unlockStart & ~0xFFFFFFFF) >> (sizeof (DWORD) * 8)) & 0xFFFFFFFF);
1555 if (!UnlockFileEx (fh->h, 0, diff_low, diff_high, &o))
1557 SetErrnoFromWinError (GetLastError ());
1558 return GNUNET_SYSERR;
1567 * Open a file. Note that the access permissions will only be
1568 * used if a new file is created and if the underlying operating
1569 * system supports the given permissions.
1571 * @param fn file name to be opened
1572 * @param flags opening flags, a combination of GNUNET_DISK_OPEN_xxx bit flags
1573 * @param perm permissions for the newly created file, use
1574 * GNUNET_DISK_PERM_USER_NONE if a file could not be created by this
1575 * call (because of flags)
1576 * @return IO handle on success, NULL on error
1578 struct GNUNET_DISK_FileHandle *
1579 GNUNET_DISK_file_open (const char *fn, enum GNUNET_DISK_OpenFlags flags,
1580 enum GNUNET_DISK_AccessPermissions perm)
1583 struct GNUNET_DISK_FileHandle *ret;
1589 wchar_t wexpfn[MAX_PATH + 1];
1596 expfn = GNUNET_STRINGS_filename_expand (fn);
1601 if (GNUNET_DISK_OPEN_READWRITE == (flags & GNUNET_DISK_OPEN_READWRITE))
1602 oflags = O_RDWR; /* note: O_RDWR is NOT always O_RDONLY | O_WRONLY */
1603 else if (flags & GNUNET_DISK_OPEN_READ)
1605 else if (flags & GNUNET_DISK_OPEN_WRITE)
1610 GNUNET_free (expfn);
1613 if (flags & GNUNET_DISK_OPEN_FAILIFEXISTS)
1614 oflags |= (O_CREAT | O_EXCL);
1615 if (flags & GNUNET_DISK_OPEN_TRUNCATE)
1617 if (flags & GNUNET_DISK_OPEN_APPEND)
1619 if (flags & GNUNET_DISK_OPEN_CREATE)
1621 (void) GNUNET_DISK_directory_create_for_file (expfn);
1623 mode = translate_unix_perms (perm);
1626 fd = open (expfn, oflags | O_LARGEFILE, mode);
1629 if (0 == (flags & GNUNET_DISK_OPEN_FAILIFEXISTS))
1630 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "open", expfn);
1632 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_DEBUG, "open", expfn);
1633 GNUNET_free (expfn);
1640 if (GNUNET_DISK_OPEN_READWRITE == (flags & GNUNET_DISK_OPEN_READWRITE))
1641 access = FILE_READ_DATA | FILE_WRITE_DATA;
1642 else if (flags & GNUNET_DISK_OPEN_READ)
1643 access = FILE_READ_DATA;
1644 else if (flags & GNUNET_DISK_OPEN_WRITE)
1645 access = FILE_WRITE_DATA;
1647 if (flags & GNUNET_DISK_OPEN_FAILIFEXISTS)
1651 else if (flags & GNUNET_DISK_OPEN_CREATE)
1653 (void) GNUNET_DISK_directory_create_for_file (expfn);
1654 if (flags & GNUNET_DISK_OPEN_TRUNCATE)
1655 disp = CREATE_ALWAYS;
1659 else if (flags & GNUNET_DISK_OPEN_TRUNCATE)
1661 disp = TRUNCATE_EXISTING;
1665 disp = OPEN_EXISTING;
1668 if (ERROR_SUCCESS == plibc_conv_to_win_pathwconv(expfn, wexpfn))
1669 h = CreateFileW (wexpfn, access,
1670 FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE, NULL,
1671 disp, FILE_ATTRIBUTE_NORMAL, NULL);
1673 h = INVALID_HANDLE_VALUE;
1674 if (h == INVALID_HANDLE_VALUE)
1676 SetErrnoFromWinError (GetLastError ());
1677 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "open", expfn);
1678 GNUNET_free (expfn);
1682 if (flags & GNUNET_DISK_OPEN_APPEND)
1683 if (SetFilePointer (h, 0, 0, FILE_END) == INVALID_SET_FILE_POINTER)
1685 SetErrnoFromWinError (GetLastError ());
1686 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "SetFilePointer", expfn);
1688 GNUNET_free (expfn);
1693 ret = GNUNET_malloc (sizeof (struct GNUNET_DISK_FileHandle));
1696 ret->type = GNUNET_DISK_FILE;
1700 GNUNET_free (expfn);
1706 * Close an open file
1707 * @param h file handle
1708 * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
1711 GNUNET_DISK_file_close (struct GNUNET_DISK_FileHandle *h)
1716 return GNUNET_SYSERR;
1720 if (!CloseHandle (h->h))
1722 SetErrnoFromWinError (GetLastError ());
1723 LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING, "close");
1724 GNUNET_free (h->oOverlapRead);
1725 GNUNET_free (h->oOverlapWrite);
1727 return GNUNET_SYSERR;
1730 if (close (h->fd) != 0)
1732 LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING, "close");
1734 return GNUNET_SYSERR;
1743 * Construct full path to a file inside of the private
1744 * directory used by GNUnet. Also creates the corresponding
1745 * directory. If the resulting name is supposed to be
1746 * a directory, end the last argument in '/' (or pass
1747 * DIR_SEPARATOR_STR as the last argument before NULL).
1749 * @param cfg configuration to use (determines HOME)
1750 * @param serviceName name of the service
1751 * @param ... is NULL-terminated list of
1752 * path components to append to the
1753 * private directory name.
1754 * @return the constructed filename
1757 GNUNET_DISK_get_home_filename (const struct GNUNET_CONFIGURATION_Handle *cfg,
1758 const char *serviceName, ...)
1764 unsigned int needed;
1767 GNUNET_CONFIGURATION_get_value_filename (cfg, serviceName, "HOME", &pfx))
1771 LOG (GNUNET_ERROR_TYPE_WARNING,
1772 _("No `%s' specified for service `%s' in configuration.\n"), "HOME",
1776 needed = strlen (pfx) + 2;
1777 if ((pfx[strlen (pfx) - 1] != '/') && (pfx[strlen (pfx) - 1] != '\\'))
1779 va_start (ap, serviceName);
1782 c = va_arg (ap, const char *);
1786 needed += strlen (c);
1787 if ((c[strlen (c) - 1] != '/') && (c[strlen (c) - 1] != '\\'))
1791 ret = GNUNET_malloc (needed);
1794 va_start (ap, serviceName);
1797 c = va_arg (ap, const char *);
1801 if ((c[strlen (c) - 1] != '/') && (c[strlen (c) - 1] != '\\'))
1802 strcat (ret, DIR_SEPARATOR_STR);
1806 if ((ret[strlen (ret) - 1] != '/') && (ret[strlen (ret) - 1] != '\\'))
1807 (void) GNUNET_DISK_directory_create_for_file (ret);
1809 (void) GNUNET_DISK_directory_create (ret);
1815 * Handle for a memory-mapping operation.
1817 struct GNUNET_DISK_MapHandle
1820 * Address where the map is in memory.
1826 * Underlying OS handle.
1831 * Number of bytes mapped.
1839 #define MAP_FAILED ((void *) -1)
1843 * Map a file into memory
1845 * @param h open file handle
1846 * @param m handle to the new mapping
1847 * @param access access specification, GNUNET_DISK_MAP_TYPE_xxx
1848 * @param len size of the mapping
1849 * @return pointer to the mapped memory region, NULL on failure
1852 GNUNET_DISK_file_map (const struct GNUNET_DISK_FileHandle *h,
1853 struct GNUNET_DISK_MapHandle **m,
1854 enum GNUNET_DISK_MapType access, size_t len)
1863 DWORD mapAccess, protect;
1865 if ((access & GNUNET_DISK_MAP_TYPE_READ) &&
1866 (access & GNUNET_DISK_MAP_TYPE_WRITE))
1868 protect = PAGE_READWRITE;
1869 mapAccess = FILE_MAP_ALL_ACCESS;
1871 else if (access & GNUNET_DISK_MAP_TYPE_READ)
1873 protect = PAGE_READONLY;
1874 mapAccess = FILE_MAP_READ;
1876 else if (access & GNUNET_DISK_MAP_TYPE_WRITE)
1878 protect = PAGE_READWRITE;
1879 mapAccess = FILE_MAP_WRITE;
1887 *m = GNUNET_malloc (sizeof (struct GNUNET_DISK_MapHandle));
1888 (*m)->h = CreateFileMapping (h->h, NULL, protect, 0, 0, NULL);
1889 if ((*m)->h == INVALID_HANDLE_VALUE)
1891 SetErrnoFromWinError (GetLastError ());
1896 (*m)->addr = MapViewOfFile ((*m)->h, mapAccess, 0, 0, len);
1899 SetErrnoFromWinError (GetLastError ());
1900 CloseHandle ((*m)->h);
1909 if (access & GNUNET_DISK_MAP_TYPE_READ)
1911 if (access & GNUNET_DISK_MAP_TYPE_WRITE)
1913 *m = GNUNET_malloc (sizeof (struct GNUNET_DISK_MapHandle));
1914 (*m)->addr = mmap (NULL, len, prot, MAP_SHARED, h->fd, 0);
1915 GNUNET_assert (NULL != (*m)->addr);
1916 if (MAP_FAILED == (*m)->addr)
1928 * @param h mapping handle
1929 * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
1932 GNUNET_DISK_file_unmap (struct GNUNET_DISK_MapHandle *h)
1939 return GNUNET_SYSERR;
1943 ret = UnmapViewOfFile (h->addr) ? GNUNET_OK : GNUNET_SYSERR;
1944 if (ret != GNUNET_OK)
1945 SetErrnoFromWinError (GetLastError ());
1946 if (!CloseHandle (h->h) && (ret == GNUNET_OK))
1948 ret = GNUNET_SYSERR;
1949 SetErrnoFromWinError (GetLastError ());
1952 ret = munmap (h->addr, h->len) != -1 ? GNUNET_OK : GNUNET_SYSERR;
1960 * Write file changes to disk
1961 * @param h handle to an open file
1962 * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
1965 GNUNET_DISK_file_sync (const struct GNUNET_DISK_FileHandle *h)
1970 return GNUNET_SYSERR;
1976 ret = FlushFileBuffers (h->h) ? GNUNET_OK : GNUNET_SYSERR;
1977 if (ret != GNUNET_OK)
1978 SetErrnoFromWinError (GetLastError ());
1980 #elif defined(FREEBSD) || defined(OPENBSD) || defined(DARWIN)
1981 return fsync (h->fd) == -1 ? GNUNET_SYSERR : GNUNET_OK;
1983 return fdatasync (h->fd) == -1 ? GNUNET_SYSERR : GNUNET_OK;
1989 /* Copyright Bob Byrnes <byrnes <at> curl.com>
1990 http://permalink.gmane.org/gmane.os.cygwin.patches/2121
1992 /* Create a pipe, and return handles to the read and write ends,
1993 just like CreatePipe, but ensure that the write end permits
1994 FILE_READ_ATTRIBUTES access, on later versions of win32 where
1995 this is supported. This access is needed by NtQueryInformationFile,
1996 which is used to implement select and nonblocking writes.
1997 Note that the return value is either NO_ERROR or GetLastError,
1998 unlike CreatePipe, which returns a bool for success or failure. */
2000 create_selectable_pipe (PHANDLE read_pipe_ptr, PHANDLE write_pipe_ptr,
2001 LPSECURITY_ATTRIBUTES sa_ptr, DWORD psize,
2002 DWORD dwReadMode, DWORD dwWriteMode)
2004 /* Default to error. */
2005 *read_pipe_ptr = *write_pipe_ptr = INVALID_HANDLE_VALUE;
2007 HANDLE read_pipe = INVALID_HANDLE_VALUE, write_pipe = INVALID_HANDLE_VALUE;
2009 /* Ensure that there is enough pipe buffer space for atomic writes. */
2010 if (psize < PIPE_BUF)
2013 char pipename[MAX_PATH];
2015 /* Retry CreateNamedPipe as long as the pipe name is in use.
2016 * Retrying will probably never be necessary, but we want
2017 * to be as robust as possible. */
2020 static volatile LONG pipe_unique_id;
2022 snprintf (pipename, sizeof pipename, "\\\\.\\pipe\\gnunet-%d-%ld",
2023 getpid (), InterlockedIncrement ((LONG *) & pipe_unique_id));
2025 LOG (GNUNET_ERROR_TYPE_DEBUG, "CreateNamedPipe: name = %s, size = %lu\n",
2028 /* Use CreateNamedPipe instead of CreatePipe, because the latter
2029 * returns a write handle that does not permit FILE_READ_ATTRIBUTES
2030 * access, on versions of win32 earlier than WinXP SP2.
2031 * CreatePipe also stupidly creates a full duplex pipe, which is
2032 * a waste, since only a single direction is actually used.
2033 * It's important to only allow a single instance, to ensure that
2034 * the pipe was not created earlier by some other process, even if
2035 * the pid has been reused. We avoid FILE_FLAG_FIRST_PIPE_INSTANCE
2036 * because that is only available for Win2k SP2 and WinXP. */
2037 read_pipe = CreateNamedPipeA (pipename, PIPE_ACCESS_INBOUND | dwReadMode, PIPE_TYPE_BYTE | PIPE_READMODE_BYTE, 1, /* max instances */
2038 psize, /* output buffer size */
2039 psize, /* input buffer size */
2040 NMPWAIT_USE_DEFAULT_WAIT, sa_ptr);
2042 if (read_pipe != INVALID_HANDLE_VALUE)
2045 LOG (GNUNET_ERROR_TYPE_DEBUG, "pipe read handle = %p\n", read_pipe);
2050 DWORD err = GetLastError ();
2054 case ERROR_PIPE_BUSY:
2055 /* The pipe is already open with compatible parameters.
2056 * Pick a new name and retry. */
2058 LOG (GNUNET_ERROR_TYPE_DEBUG, "pipe busy, retrying\n");
2061 case ERROR_ACCESS_DENIED:
2062 /* The pipe is already open with incompatible parameters.
2063 * Pick a new name and retry. */
2065 LOG (GNUNET_ERROR_TYPE_DEBUG, "pipe access denied, retrying\n");
2068 case ERROR_CALL_NOT_IMPLEMENTED:
2069 /* We are on an older Win9x platform without named pipes.
2070 * Return an anonymous pipe as the best approximation. */
2072 LOG (GNUNET_ERROR_TYPE_DEBUG,
2073 "CreateNamedPipe not implemented, resorting to "
2074 "CreatePipe: size = %lu\n", psize);
2076 if (CreatePipe (read_pipe_ptr, write_pipe_ptr, sa_ptr, psize))
2079 LOG (GNUNET_ERROR_TYPE_DEBUG, "pipe read handle = %p\n",
2081 LOG (GNUNET_ERROR_TYPE_DEBUG, "pipe write handle = %p\n",
2086 err = GetLastError ();
2087 LOG (GNUNET_ERROR_TYPE_ERROR, "CreatePipe failed: %d\n", err);
2090 LOG (GNUNET_ERROR_TYPE_ERROR, "CreateNamedPipe failed: %d\n", err);
2096 LOG (GNUNET_ERROR_TYPE_DEBUG, "CreateFile: name = %s\n", pipename);
2099 /* Open the named pipe for writing.
2100 * Be sure to permit FILE_READ_ATTRIBUTES access. */
2101 write_pipe = CreateFileA (pipename, GENERIC_WRITE | FILE_READ_ATTRIBUTES, 0, /* share mode */
2102 sa_ptr, OPEN_EXISTING, dwWriteMode, /* flags and attributes */
2103 0); /* handle to template file */
2105 if (write_pipe == INVALID_HANDLE_VALUE)
2108 DWORD err = GetLastError ();
2111 LOG (GNUNET_ERROR_TYPE_DEBUG, "CreateFile failed: %d\n", err);
2113 CloseHandle (read_pipe);
2117 LOG (GNUNET_ERROR_TYPE_DEBUG, "pipe write handle = %p\n", write_pipe);
2120 *read_pipe_ptr = read_pipe;
2121 *write_pipe_ptr = write_pipe;
2128 * Creates an interprocess channel
2130 * @param blocking_read creates an asynchronous pipe for reading if set to GNUNET_NO
2131 * @param blocking_write creates an asynchronous pipe for writing if set to GNUNET_NO
2132 * @param inherit_read inherit the parent processes stdin (only for windows)
2133 * @param inherit_write inherit the parent processes stdout (only for windows)
2134 * @return handle to the new pipe, NULL on error
2136 struct GNUNET_DISK_PipeHandle *
2137 GNUNET_DISK_pipe (int blocking_read, int blocking_write, int inherit_read, int inherit_write)
2148 LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR, "pipe");
2152 return GNUNET_DISK_pipe_from_fd (blocking_read,
2156 struct GNUNET_DISK_PipeHandle *p;
2157 struct GNUNET_DISK_FileHandle *fds;
2162 p = GNUNET_malloc (sizeof (struct GNUNET_DISK_PipeHandle) +
2163 2 * sizeof (struct GNUNET_DISK_FileHandle));
2164 fds = (struct GNUNET_DISK_FileHandle *) &p[1];
2168 /* All pipes are overlapped. If you want them to block - just
2169 * call WriteFile() and ReadFile() with NULL overlapped pointer.
2172 create_selectable_pipe (&p->fd[0]->h, &p->fd[1]->h, NULL, 0,
2173 FILE_FLAG_OVERLAPPED,
2174 FILE_FLAG_OVERLAPPED);
2178 SetErrnoFromWinError (GetLastError ());
2181 if (!DuplicateHandle
2182 (GetCurrentProcess (), p->fd[0]->h, GetCurrentProcess (), &tmp_handle, 0,
2183 inherit_read == 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[0]->h);
2192 p->fd[0]->h = tmp_handle;
2194 if (!DuplicateHandle
2195 (GetCurrentProcess (), p->fd[1]->h, GetCurrentProcess (), &tmp_handle, 0,
2196 inherit_write == GNUNET_YES ? TRUE : FALSE, DUPLICATE_SAME_ACCESS))
2198 SetErrnoFromWinError (GetLastError ());
2199 CloseHandle (p->fd[0]->h);
2200 CloseHandle (p->fd[1]->h);
2204 CloseHandle (p->fd[1]->h);
2205 p->fd[1]->h = tmp_handle;
2207 p->fd[0]->type = GNUNET_PIPE;
2208 p->fd[1]->type = GNUNET_PIPE;
2210 p->fd[0]->oOverlapRead = GNUNET_malloc (sizeof (OVERLAPPED));
2211 p->fd[0]->oOverlapWrite = GNUNET_malloc (sizeof (OVERLAPPED));
2212 p->fd[1]->oOverlapRead = GNUNET_malloc (sizeof (OVERLAPPED));
2213 p->fd[1]->oOverlapWrite = GNUNET_malloc (sizeof (OVERLAPPED));
2215 p->fd[0]->oOverlapRead->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2216 p->fd[0]->oOverlapWrite->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2218 p->fd[1]->oOverlapRead->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2219 p->fd[1]->oOverlapWrite->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2227 * Creates a pipe object from a couple of file descriptors.
2228 * Useful for wrapping existing pipe FDs.
2230 * @param blocking_read creates an asynchronous pipe for reading if set to GNUNET_NO
2231 * @param blocking_write creates an asynchronous pipe for writing if set to GNUNET_NO
2232 * @param fd an array of two fd values. One of them may be -1 for read-only or write-only pipes
2234 * @return handle to the new pipe, NULL on error
2236 struct GNUNET_DISK_PipeHandle *
2237 GNUNET_DISK_pipe_from_fd (int blocking_read, int blocking_write, int fd[2])
2239 struct GNUNET_DISK_PipeHandle *p;
2240 struct GNUNET_DISK_FileHandle *fds;
2242 p = GNUNET_malloc (sizeof (struct GNUNET_DISK_PipeHandle) +
2243 2 * sizeof (struct GNUNET_DISK_FileHandle));
2244 fds = (struct GNUNET_DISK_FileHandle *) &p[1];
2250 int eno = 0; /* make gcc happy */
2252 p->fd[0]->fd = fd[0];
2253 p->fd[1]->fd = fd[1];
2259 flags = fcntl (fd[0], F_GETFL);
2260 flags |= O_NONBLOCK;
2261 if (0 > fcntl (fd[0], F_SETFL, flags))
2267 flags = fcntl (fd[0], F_GETFD);
2268 flags |= FD_CLOEXEC;
2269 if (0 > fcntl (fd[0], F_SETFD, flags))
2278 if (!blocking_write)
2280 flags = fcntl (fd[1], F_GETFL);
2281 flags |= O_NONBLOCK;
2282 if (0 > fcntl (fd[1], F_SETFL, flags))
2288 flags = fcntl (fd[1], F_GETFD);
2289 flags |= FD_CLOEXEC;
2290 if (0 > fcntl (fd[1], F_SETFD, flags))
2299 LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR, "fcntl");
2300 if (p->fd[0]->fd >= 0)
2301 GNUNET_break (0 == close (p->fd[0]->fd));
2302 if (p->fd[1]->fd >= 0)
2303 GNUNET_break (0 == close (p->fd[1]->fd));
2310 p->fd[0]->h = _get_osfhandle (fd[0]);
2312 p->fd[0]->h = INVALID_HANDLE_VALUE;
2314 p->fd[1]->h = _get_osfhandle (fd[1]);
2316 p->fd[1]->h = INVALID_HANDLE_VALUE;
2318 if (p->fd[0]->h != INVALID_HANDLE_VALUE)
2320 p->fd[0]->type = GNUNET_PIPE;
2321 p->fd[0]->oOverlapRead = GNUNET_malloc (sizeof (OVERLAPPED));
2322 p->fd[0]->oOverlapWrite = GNUNET_malloc (sizeof (OVERLAPPED));
2323 p->fd[0]->oOverlapRead->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2324 p->fd[0]->oOverlapWrite->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2327 if (p->fd[1]->h != INVALID_HANDLE_VALUE)
2329 p->fd[1]->type = GNUNET_PIPE;
2330 p->fd[1]->oOverlapRead = GNUNET_malloc (sizeof (OVERLAPPED));
2331 p->fd[1]->oOverlapWrite = GNUNET_malloc (sizeof (OVERLAPPED));
2332 p->fd[1]->oOverlapRead->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2333 p->fd[1]->oOverlapWrite->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2341 * Closes an interprocess channel
2343 * @param p pipe to close
2344 * @param end which end of the pipe to close
2345 * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
2348 GNUNET_DISK_pipe_close_end (struct GNUNET_DISK_PipeHandle *p,
2349 enum GNUNET_DISK_PipeEnd end)
2351 int ret = GNUNET_OK;
2355 if (end == GNUNET_DISK_PIPE_END_READ)
2357 if (p->fd[0]->h != INVALID_HANDLE_VALUE)
2359 if (!CloseHandle (p->fd[0]->h))
2361 SetErrnoFromWinError (GetLastError ());
2362 ret = GNUNET_SYSERR;
2364 GNUNET_free (p->fd[0]->oOverlapRead);
2365 GNUNET_free (p->fd[0]->oOverlapWrite);
2366 p->fd[0]->h = INVALID_HANDLE_VALUE;
2369 else if (end == GNUNET_DISK_PIPE_END_WRITE)
2371 if (p->fd[0]->h != INVALID_HANDLE_VALUE)
2373 if (!CloseHandle (p->fd[1]->h))
2375 SetErrnoFromWinError (GetLastError ());
2376 ret = GNUNET_SYSERR;
2378 GNUNET_free (p->fd[1]->oOverlapRead);
2379 GNUNET_free (p->fd[1]->oOverlapWrite);
2380 p->fd[1]->h = INVALID_HANDLE_VALUE;
2386 if (end == GNUNET_DISK_PIPE_END_READ)
2388 if (0 != close (p->fd[0]->fd))
2390 ret = GNUNET_SYSERR;
2395 else if (end == GNUNET_DISK_PIPE_END_WRITE)
2397 if (0 != close (p->fd[1]->fd))
2399 ret = GNUNET_SYSERR;
2411 * Closes an interprocess channel
2413 * @param p pipe to close
2414 * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
2417 GNUNET_DISK_pipe_close (struct GNUNET_DISK_PipeHandle *p)
2419 int ret = GNUNET_OK;
2423 if (p->fd[0]->h != INVALID_HANDLE_VALUE)
2425 if (!CloseHandle (p->fd[0]->h))
2427 SetErrnoFromWinError (GetLastError ());
2428 ret = GNUNET_SYSERR;
2430 GNUNET_free (p->fd[0]->oOverlapRead);
2431 GNUNET_free (p->fd[0]->oOverlapWrite);
2433 if (p->fd[1]->h != INVALID_HANDLE_VALUE)
2435 if (!CloseHandle (p->fd[1]->h))
2437 SetErrnoFromWinError (GetLastError ());
2438 ret = GNUNET_SYSERR;
2440 GNUNET_free (p->fd[1]->oOverlapRead);
2441 GNUNET_free (p->fd[1]->oOverlapWrite);
2446 if (p->fd[0]->fd != -1)
2448 if (0 != close (p->fd[0]->fd))
2450 ret = GNUNET_SYSERR;
2455 if (p->fd[1]->fd != -1)
2457 if (0 != close (p->fd[1]->fd))
2459 ret = GNUNET_SYSERR;
2471 * Get the handle to a particular pipe end
2474 * @param n end to access
2475 * @return handle for the respective end
2477 const struct GNUNET_DISK_FileHandle *
2478 GNUNET_DISK_pipe_handle (const struct GNUNET_DISK_PipeHandle *p,
2479 enum GNUNET_DISK_PipeEnd n)
2483 case GNUNET_DISK_PIPE_END_READ:
2484 case GNUNET_DISK_PIPE_END_WRITE:
2494 * Retrieve OS file handle
2496 * @param fh GNUnet file descriptor
2497 * @param dst destination buffer
2498 * @param dst_len length of dst
2499 * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
2502 GNUNET_DISK_internal_file_handle_ (const struct GNUNET_DISK_FileHandle *fh,
2503 void *dst, size_t dst_len)
2506 if (dst_len < sizeof (HANDLE))
2507 return GNUNET_SYSERR;
2508 *((HANDLE *) dst) = fh->h;
2510 if (dst_len < sizeof (int))
2511 return GNUNET_SYSERR;
2512 *((int *) dst) = fh->fd;