2 This file is part of GNUnet.
3 (C) 2001--2012 Christian Grothoff (and other contributing authors)
5 GNUnet is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 2, or (at your
8 option) any later version.
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with GNUnet; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
23 * @brief disk IO convenience methods
24 * @author Christian Grothoff
29 #include "gnunet_common.h"
30 #include "gnunet_directories.h"
31 #include "gnunet_disk_lib.h"
32 #include "gnunet_scheduler_lib.h"
33 #include "gnunet_strings_lib.h"
34 #include "gnunet_crypto_lib.h"
37 #define LOG(kind,...) GNUNET_log_from (kind, "util", __VA_ARGS__)
39 #define LOG_STRERROR(kind,syscall) GNUNET_log_from_strerror (kind, "util", syscall)
41 #define LOG_STRERROR_FILE(kind,syscall,filename) GNUNET_log_from_strerror_file (kind, "util", syscall, filename)
44 * Block size for IO for copying files.
46 #define COPY_BLK_SIZE 65536
50 #if defined(LINUX) || defined(CYGWIN)
53 #if defined(SOMEBSD) || defined(DARWIN)
54 #include <sys/param.h>
55 #include <sys/mount.h>
58 #include <sys/types.h>
59 #include <sys/statvfs.h>
64 ULONG PipeSerialNumber;
66 #define _IFMT 0170000 /* type of file */
67 #define _IFLNK 0120000 /* symbolic link */
68 #define S_ISLNK(m) (((m)&_IFMT) == _IFLNK)
70 #error PORT-ME: need to port statfs (how much space is left on the drive?)
76 #if !defined(SOMEBSD) && !defined(DARWIN) && !defined(WINDOWS)
80 #include <sys/statvfs.h>
85 * Handle used to manage a pipe.
87 struct GNUNET_DISK_PipeHandle
90 * File descriptors for the pipe.
92 struct GNUNET_DISK_FileHandle *fd[2];
97 * Closure for the recursion to determine the file size
100 struct GetFileSizeData
103 * Set to the total file size.
108 * GNUNET_YES if symbolic links should be included.
110 int include_sym_links;
113 * GNUNET_YES if mode is file-only (return total == -1 for directories).
115 int single_file_mode;
120 translate_unix_perms (enum GNUNET_DISK_AccessPermissions perm)
125 if (perm & GNUNET_DISK_PERM_USER_READ)
127 if (perm & GNUNET_DISK_PERM_USER_WRITE)
129 if (perm & GNUNET_DISK_PERM_USER_EXEC)
131 if (perm & GNUNET_DISK_PERM_GROUP_READ)
133 if (perm & GNUNET_DISK_PERM_GROUP_WRITE)
135 if (perm & GNUNET_DISK_PERM_GROUP_EXEC)
137 if (perm & GNUNET_DISK_PERM_OTHER_READ)
139 if (perm & GNUNET_DISK_PERM_OTHER_WRITE)
141 if (perm & GNUNET_DISK_PERM_OTHER_EXEC)
149 * Iterate over all files in the given directory and
150 * accumulate their size.
152 * @param cls closure of type "struct GetFileSizeData"
153 * @param fn current filename we are looking at
154 * @return GNUNET_SYSERR on serious errors, otherwise GNUNET_OK
157 getSizeRec (void *cls, const char *fn)
159 struct GetFileSizeData *gfsd = cls;
168 if (0 != STAT64 (fn, &buf))
170 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_DEBUG, "stat64", fn);
171 return GNUNET_SYSERR;
174 if (0 != STAT (fn, &buf))
176 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_DEBUG, "stat", fn);
177 return GNUNET_SYSERR;
180 if ((S_ISDIR (buf.st_mode)) && (gfsd->single_file_mode == GNUNET_YES))
183 return GNUNET_SYSERR;
185 if ((!S_ISLNK (buf.st_mode)) || (gfsd->include_sym_links == GNUNET_YES))
186 gfsd->total += buf.st_size;
187 if ((S_ISDIR (buf.st_mode)) && (0 == ACCESS (fn, X_OK)) &&
188 ((!S_ISLNK (buf.st_mode)) || (gfsd->include_sym_links == GNUNET_YES)))
190 if (GNUNET_SYSERR == GNUNET_DISK_directory_scan (fn, &getSizeRec, gfsd))
191 return GNUNET_SYSERR;
198 * Checks whether a handle is invalid
200 * @param h handle to check
201 * @return GNUNET_YES if invalid, GNUNET_NO if valid
204 GNUNET_DISK_handle_invalid (const struct GNUNET_DISK_FileHandle *h)
207 return ((!h) || (h->h == INVALID_HANDLE_VALUE)) ? GNUNET_YES : GNUNET_NO;
209 return ((!h) || (h->fd == -1)) ? GNUNET_YES : GNUNET_NO;
214 * Get the size of an open file.
216 * @param fh open file handle
217 * @param size where to write size of the file
218 * @return GNUNET_OK on success, GNUNET_SYSERR on error
221 GNUNET_DISK_file_handle_size (struct GNUNET_DISK_FileHandle *fh,
227 b = GetFileSizeEx (fh->h, &li);
230 SetErrnoFromWinError (GetLastError ());
231 return GNUNET_SYSERR;
233 *size = (OFF_T) li.QuadPart;
237 if (0 != FSTAT (fh->fd, &sbuf))
238 return GNUNET_SYSERR;
239 *size = sbuf.st_size;
246 * Move the read/write pointer in a file
248 * @param h handle of an open file
249 * @param offset position to move to
250 * @param whence specification to which position the offset parameter relates to
251 * @return the new position on success, GNUNET_SYSERR otherwise
254 GNUNET_DISK_file_seek (const struct GNUNET_DISK_FileHandle * h, OFF_T offset,
255 enum GNUNET_DISK_Seek whence)
260 return GNUNET_SYSERR;
264 LARGE_INTEGER li, new_pos;
267 static DWORD t[] = {[GNUNET_DISK_SEEK_SET] = FILE_BEGIN,
268 [GNUNET_DISK_SEEK_CUR] = FILE_CURRENT,[GNUNET_DISK_SEEK_END] = FILE_END
270 li.QuadPart = offset;
272 b = SetFilePointerEx (h->h, li, &new_pos, t[whence]);
275 SetErrnoFromWinError (GetLastError ());
276 return GNUNET_SYSERR;
278 return (OFF_T) new_pos.QuadPart;
280 static int t[] = {[GNUNET_DISK_SEEK_SET] = SEEK_SET,
281 [GNUNET_DISK_SEEK_CUR] = SEEK_CUR,[GNUNET_DISK_SEEK_END] = SEEK_END
284 return lseek (h->fd, offset, t[whence]);
290 * Get the size of the file (or directory) of the given file (in
293 * @param filename name of the file or directory
294 * @param size set to the size of the file (or,
295 * in the case of directories, the sum
296 * of all sizes of files in the directory)
297 * @param includeSymLinks should symbolic links be
299 * @param singleFileMode GNUNET_YES to only get size of one file
300 * and return GNUNET_SYSERR for directories.
301 * @return GNUNET_SYSERR on error, GNUNET_OK on success
304 GNUNET_DISK_file_size (const char *filename, uint64_t * size,
305 int includeSymLinks, int singleFileMode)
307 struct GetFileSizeData gfsd;
310 GNUNET_assert (size != NULL);
312 gfsd.include_sym_links = includeSymLinks;
313 gfsd.single_file_mode = singleFileMode;
314 ret = getSizeRec (&gfsd, filename);
321 * Obtain some unique identifiers for the given file
322 * that can be used to identify it in the local system.
323 * This function is used between GNUnet processes to
324 * quickly check if two files with the same absolute path
325 * are actually identical. The two processes represent
326 * the same peer but may communicate over the network
327 * (and the file may be on an NFS volume). This function
328 * may not be supported on all operating systems.
330 * @param filename name of the file
331 * @param dev set to the device ID
332 * @param ino set to the inode ID
333 * @return GNUNET_OK on success
336 GNUNET_DISK_file_get_identifiers (const char *filename, uint64_t * dev,
343 if ((0 == stat (filename, &sbuf)) && (0 == statvfs (filename, &fbuf)))
345 *dev = (uint64_t) fbuf.f_fsid;
346 *ino = (uint64_t) sbuf.st_ino;
353 if ((0 == stat (filename, &sbuf)) && (0 == statfs (filename, &fbuf)))
355 *dev = ((uint64_t) fbuf.f_fsid.val[0]) << 32 ||
356 ((uint64_t) fbuf.f_fsid.val[1]);
357 *ino = (uint64_t) sbuf.st_ino;
361 // FIXME NILS: test this
362 struct GNUNET_DISK_FileHandle *fh;
363 BY_HANDLE_FILE_INFORMATION info;
366 fh = GNUNET_DISK_file_open (filename, GNUNET_DISK_OPEN_READ, 0);
368 return GNUNET_SYSERR;
369 succ = GetFileInformationByHandle (fh->h, &info);
370 GNUNET_DISK_file_close (fh);
373 *dev = info.dwVolumeSerialNumber;
374 *ino = ((((uint64_t) info.nFileIndexHigh) << (sizeof (DWORD) * 8)) | info.nFileIndexLow);
378 return GNUNET_SYSERR;
381 return GNUNET_SYSERR;
386 * Create an (empty) temporary file on disk. If the given name is not
387 * an absolute path, the current 'TMPDIR' will be prepended. In any case,
388 * 6 random characters will be appended to the name to create a unique
391 * @param t component to use for the name;
392 * does NOT contain "XXXXXX" or "/tmp/".
393 * @return NULL on error, otherwise name of fresh
394 * file on disk in directory for temporary files
397 GNUNET_DISK_mktemp (const char *t)
404 if ((t[0] != '/') && (t[0] != '\\')
406 && !(isalpha ((int) t[0]) && (t[0] != '\0') && (t[1] == ':'))
410 /* FIXME: This uses system codepage on W32, not UTF-8 */
411 tmpdir = getenv ("TMPDIR");
412 tmpdir = tmpdir ? tmpdir : "/tmp";
413 GNUNET_asprintf (&tmpl, "%s/%s%s", tmpdir, t, "XXXXXX");
417 GNUNET_asprintf (&tmpl, "%s%s", t, "XXXXXX");
420 fn = (char *) GNUNET_malloc (MAX_PATH + 1);
421 if (ERROR_SUCCESS != plibc_conv_to_win_path (tmpl, fn))
431 /* FIXME: why is this not MKSTEMP()? This function is implemented in plibc.
432 * CG: really? If I put MKSTEMP here, I get a compilation error...
433 * It will assume that fn is UTF-8-encoded, if compiled with UTF-8 support.
438 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_ERROR, "mkstemp", fn);
443 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "close", fn);
449 * Get the number of blocks that are left on the partition that
450 * contains the given file (for normal users).
452 * @param part a file on the partition to check
453 * @return -1 on errors, otherwise the number of free blocks
456 GNUNET_DISK_get_blocks_available (const char *part)
461 if (0 != statvfs (part, &buf))
463 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "statfs", part);
471 wchar_t wpath[MAX_PATH + 1];
474 path = GNUNET_STRINGS_filename_expand (part);
477 /* "part" was in UTF-8, and so is "path" */
478 if (ERROR_SUCCESS != plibc_conv_to_win_pathwconv(path, wpath))
484 wcsncpy (szDrive, wpath, 3);
486 if (!GetDiskFreeSpaceW (szDrive, &dwDummy, &dwDummy, &dwBlocks, &dwDummy))
488 LOG (GNUNET_ERROR_TYPE_WARNING, _("`%s' failed for drive `%S': %u\n"),
489 "GetDiskFreeSpace", szDrive, GetLastError ());
497 if (0 != statfs (part, &s))
499 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "statfs", part);
508 * Test if "fil" is a directory.
509 * Will not print an error message if the directory
510 * does not exist. Will log errors if GNUNET_SYSERR is
511 * returned (i.e., a file exists with the same name).
513 * @param fil filename to test
514 * @return GNUNET_YES if yes, GNUNET_NO if not, GNUNET_SYSERR if it
518 GNUNET_DISK_directory_test (const char *fil)
520 struct stat filestat;
523 ret = STAT (fil, &filestat);
528 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "stat", fil);
529 return GNUNET_SYSERR;
533 if (!S_ISDIR (filestat.st_mode))
535 if (ACCESS (fil, R_OK | X_OK) < 0)
537 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "access", fil);
538 return GNUNET_SYSERR;
545 * Check that fil corresponds to a filename
546 * (of a file that exists and that is not a directory).
548 * @param fil filename to check
549 * @return GNUNET_YES if yes, GNUNET_NO if not a file, GNUNET_SYSERR if something
550 * else (will print an error message in that case, too).
553 GNUNET_DISK_file_test (const char *fil)
555 struct stat filestat;
559 rdir = GNUNET_STRINGS_filename_expand (fil);
561 return GNUNET_SYSERR;
563 ret = STAT (rdir, &filestat);
568 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "stat", rdir);
570 return GNUNET_SYSERR;
575 if (!S_ISREG (filestat.st_mode))
580 if (ACCESS (rdir, R_OK) < 0)
582 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "access", rdir);
584 return GNUNET_SYSERR;
592 * Implementation of "mkdir -p"
593 * @param dir the directory to create
594 * @returns GNUNET_OK on success, GNUNET_SYSERR on failure
597 GNUNET_DISK_directory_create (const char *dir)
604 rdir = GNUNET_STRINGS_filename_expand (dir);
606 return GNUNET_SYSERR;
610 pos = 1; /* skip heading '/' */
612 /* Local or Network path? */
613 if (strncmp (rdir, "\\\\", 2) == 0)
618 if (rdir[pos] == '\\')
628 pos = 3; /* strlen("C:\\") */
633 if ((rdir[pos] == DIR_SEPARATOR) || (pos == len))
636 ret = GNUNET_DISK_directory_test (rdir);
637 if (ret == GNUNET_SYSERR)
640 return GNUNET_SYSERR;
642 if (ret == GNUNET_NO)
645 ret = mkdir (rdir, S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH); /* 755 */
647 wchar_t wrdir[MAX_PATH + 1];
648 if (ERROR_SUCCESS == plibc_conv_to_win_pathwconv(rdir, wrdir))
649 ret = !CreateDirectoryW (wrdir, NULL);
653 if ((ret != 0) && (errno != EEXIST))
655 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_ERROR, "mkdir", rdir);
657 return GNUNET_SYSERR;
660 rdir[pos] = DIR_SEPARATOR;
670 * Create the directory structure for storing
673 * @param filename name of a file in the directory
674 * @returns GNUNET_OK on success,
675 * GNUNET_SYSERR on failure,
676 * GNUNET_NO if the directory
677 * exists but is not writeable for us
680 GNUNET_DISK_directory_create_for_file (const char *filename)
686 rdir = GNUNET_STRINGS_filename_expand (filename);
688 return GNUNET_SYSERR;
690 while ((len > 0) && (rdir[len] != DIR_SEPARATOR))
693 ret = GNUNET_DISK_directory_create (rdir);
694 if ((ret == GNUNET_OK) && (0 != ACCESS (rdir, W_OK)))
702 * Read the contents of a binary file into a buffer.
703 * @param h handle to an open file
704 * @param result the buffer to write the result to
705 * @param len the maximum number of bytes to read
706 * @return the number of bytes read on success, GNUNET_SYSERR on failure
709 GNUNET_DISK_file_read (const struct GNUNET_DISK_FileHandle * h, void *result,
715 return GNUNET_SYSERR;
721 if (h->type != GNUNET_PIPE)
723 if (!ReadFile (h->h, result, len, &bytesRead, NULL))
725 SetErrnoFromWinError (GetLastError ());
726 return GNUNET_SYSERR;
731 if (!ReadFile (h->h, result, len, &bytesRead, h->oOverlapRead))
733 if (GetLastError () != ERROR_IO_PENDING)
735 LOG (GNUNET_ERROR_TYPE_DEBUG, "Error reading from pipe: %u\n", GetLastError ());
736 SetErrnoFromWinError (GetLastError ());
737 return GNUNET_SYSERR;
739 LOG (GNUNET_ERROR_TYPE_DEBUG, "Will get overlapped result\n");
740 GetOverlappedResult (h->h, h->oOverlapRead, &bytesRead, TRUE);
742 LOG (GNUNET_ERROR_TYPE_DEBUG, "Read %u bytes from pipe\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;
784 if (!ReadFile (h->h, result, len, &bytesRead, h->oOverlapRead))
786 if (GetLastError () != ERROR_IO_PENDING)
788 LOG (GNUNET_ERROR_TYPE_DEBUG, "Error reading from pipe: %u\n", GetLastError ());
789 SetErrnoFromWinError (GetLastError ());
790 return GNUNET_SYSERR;
794 LOG (GNUNET_ERROR_TYPE_DEBUG,
795 "ReadFile() queued a read, cancelling\n");
798 return GNUNET_SYSERR;
801 LOG (GNUNET_ERROR_TYPE_DEBUG, "Read %u bytes\n", bytesRead);
808 /* set to non-blocking, read, then set back */
809 flags = fcntl (h->fd, F_GETFL);
810 if (0 == (flags & O_NONBLOCK))
811 fcntl (h->fd, F_SETFL, flags | O_NONBLOCK);
812 ret = read (h->fd, result, len);
813 if (0 == (flags & O_NONBLOCK))
814 fcntl (h->fd, F_SETFL, flags);
821 * Read the contents of a binary file into a buffer.
823 * @param fn file name
824 * @param result the buffer to write the result to
825 * @param len the maximum number of bytes to read
826 * @return number of bytes read, GNUNET_SYSERR on failure
829 GNUNET_DISK_fn_read (const char *fn, void *result, size_t len)
831 struct GNUNET_DISK_FileHandle *fh;
834 fh = GNUNET_DISK_file_open (fn, GNUNET_DISK_OPEN_READ, GNUNET_DISK_PERM_NONE);
836 return GNUNET_SYSERR;
837 ret = GNUNET_DISK_file_read (fh, result, len);
838 GNUNET_assert (GNUNET_OK == GNUNET_DISK_file_close (fh));
845 * Write a buffer to a file.
846 * @param h handle to open file
847 * @param buffer the data to write
848 * @param n number of bytes to write
849 * @return number of bytes written on success, GNUNET_SYSERR on error
852 GNUNET_DISK_file_write (const struct GNUNET_DISK_FileHandle * h,
853 const void *buffer, size_t n)
858 return GNUNET_SYSERR;
864 if (h->type != GNUNET_PIPE)
866 if (!WriteFile (h->h, buffer, n, &bytesWritten, NULL))
868 SetErrnoFromWinError (GetLastError ());
869 return GNUNET_SYSERR;
874 LOG (GNUNET_ERROR_TYPE_DEBUG, "It is a pipe trying to write %u bytes\n", n);
875 if (!WriteFile (h->h, buffer, n, &bytesWritten, h->oOverlapWrite))
877 if (GetLastError () != ERROR_IO_PENDING)
879 SetErrnoFromWinError (GetLastError ());
880 LOG (GNUNET_ERROR_TYPE_DEBUG, "Error writing to pipe: %u\n",
882 return GNUNET_SYSERR;
884 LOG (GNUNET_ERROR_TYPE_DEBUG, "Will get overlapped result\n");
885 if (!GetOverlappedResult (h->h, h->oOverlapWrite, &bytesWritten, TRUE))
887 SetErrnoFromWinError (GetLastError ());
888 LOG (GNUNET_ERROR_TYPE_DEBUG,
889 "Error getting overlapped result while writing to pipe: %u\n",
891 return GNUNET_SYSERR;
897 if (!GetOverlappedResult (h->h, h->oOverlapWrite, &ovr, TRUE))
899 LOG (GNUNET_ERROR_TYPE_DEBUG,
900 "Error getting control overlapped result while writing to pipe: %u\n",
905 LOG (GNUNET_ERROR_TYPE_DEBUG,
906 "Wrote %u bytes (ovr says %u), picking the greatest\n",
910 if (bytesWritten == 0)
914 LOG (GNUNET_ERROR_TYPE_DEBUG, "Wrote %u bytes, returning -1 with EAGAIN\n", bytesWritten);
916 return GNUNET_SYSERR;
919 LOG (GNUNET_ERROR_TYPE_DEBUG, "Wrote %u bytes\n", bytesWritten);
923 return write (h->fd, buffer, n);
929 * Write a buffer to a file, blocking, if necessary.
930 * @param h handle to open file
931 * @param buffer the data to write
932 * @param n number of bytes to write
933 * @return number of bytes written on success, GNUNET_SYSERR on error
936 GNUNET_DISK_file_write_blocking (const struct GNUNET_DISK_FileHandle * h,
937 const void *buffer, size_t n)
942 return GNUNET_SYSERR;
947 /* We do a non-overlapped write, which is as blocking as it gets */
948 LOG (GNUNET_ERROR_TYPE_DEBUG, "Writing %u bytes\n", n);
949 if (!WriteFile (h->h, buffer, n, &bytesWritten, NULL))
951 SetErrnoFromWinError (GetLastError ());
952 LOG (GNUNET_ERROR_TYPE_DEBUG, "Error writing to pipe: %u\n",
954 return GNUNET_SYSERR;
956 if (bytesWritten == 0 && n > 0)
958 LOG (GNUNET_ERROR_TYPE_DEBUG, "Waiting for pipe to clean\n");
959 WaitForSingleObject (h->h, INFINITE);
960 if (!WriteFile (h->h, buffer, n, &bytesWritten, NULL))
962 SetErrnoFromWinError (GetLastError ());
963 LOG (GNUNET_ERROR_TYPE_DEBUG, "Error writing to pipe: %u\n",
965 return GNUNET_SYSERR;
968 LOG (GNUNET_ERROR_TYPE_DEBUG, "Wrote %u bytes\n", bytesWritten);
974 /* set to blocking, write, then set back */
975 flags = fcntl (h->fd, F_GETFL);
976 if (0 != (flags & O_NONBLOCK))
977 fcntl (h->fd, F_SETFL, flags - O_NONBLOCK);
978 ret = write (h->fd, buffer, n);
979 if (0 == (flags & O_NONBLOCK))
980 fcntl (h->fd, F_SETFL, flags);
987 * Write a buffer to a file. If the file is longer than the
988 * number of bytes that will be written, it will be truncated.
990 * @param fn file name
991 * @param buffer the data to write
992 * @param n number of bytes to write
993 * @param mode file permissions
994 * @return number of bytes written on success, GNUNET_SYSERR on error
997 GNUNET_DISK_fn_write (const char *fn, const void *buffer, size_t n,
998 enum GNUNET_DISK_AccessPermissions mode)
1000 struct GNUNET_DISK_FileHandle *fh;
1003 fh = GNUNET_DISK_file_open (fn,
1004 GNUNET_DISK_OPEN_WRITE | GNUNET_DISK_OPEN_TRUNCATE
1005 | GNUNET_DISK_OPEN_CREATE, mode);
1007 return GNUNET_SYSERR;
1008 ret = GNUNET_DISK_file_write (fh, buffer, n);
1009 GNUNET_assert (GNUNET_OK == GNUNET_DISK_file_close (fh));
1015 * Scan a directory for files.
1017 * @param dirName the name of the directory
1018 * @param callback the method to call for each file,
1019 * can be NULL, in that case, we only count
1020 * @param callback_cls closure for callback
1021 * @return the number of files found, GNUNET_SYSERR on error or
1022 * ieration aborted by callback returning GNUNET_SYSERR
1025 GNUNET_DISK_directory_scan (const char *dirName,
1026 GNUNET_FileNameCallback callback,
1030 struct dirent *finfo;
1035 unsigned int name_len;
1036 unsigned int n_size;
1038 GNUNET_assert (dirName != NULL);
1039 dname = GNUNET_STRINGS_filename_expand (dirName);
1041 return GNUNET_SYSERR;
1042 while ((strlen (dname) > 0) && (dname[strlen (dname) - 1] == DIR_SEPARATOR))
1043 dname[strlen (dname) - 1] = '\0';
1044 if (0 != STAT (dname, &istat))
1046 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "stat", dname);
1047 GNUNET_free (dname);
1048 return GNUNET_SYSERR;
1050 if (!S_ISDIR (istat.st_mode))
1052 LOG (GNUNET_ERROR_TYPE_WARNING, _("Expected `%s' to be a directory!\n"),
1054 GNUNET_free (dname);
1055 return GNUNET_SYSERR;
1058 dinfo = OPENDIR (dname);
1059 if ((errno == EACCES) || (dinfo == NULL))
1061 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "opendir", dname);
1064 GNUNET_free (dname);
1065 return GNUNET_SYSERR;
1068 n_size = strlen (dname) + name_len + 2;
1069 name = GNUNET_malloc (n_size);
1070 while ((finfo = READDIR (dinfo)) != NULL)
1072 if ((0 == strcmp (finfo->d_name, ".")) ||
1073 (0 == strcmp (finfo->d_name, "..")))
1075 if (callback != NULL)
1077 if (name_len < strlen (finfo->d_name))
1080 name_len = strlen (finfo->d_name);
1081 n_size = strlen (dname) + name_len + 2;
1082 name = GNUNET_malloc (n_size);
1084 /* dname can end in "/" only if dname == "/";
1085 * if dname does not end in "/", we need to add
1086 * a "/" (otherwise, we must not!) */
1087 GNUNET_snprintf (name, n_size, "%s%s%s", dname,
1088 (strcmp (dname, DIR_SEPARATOR_STR) ==
1089 0) ? "" : DIR_SEPARATOR_STR, finfo->d_name);
1090 if (GNUNET_OK != callback (callback_cls, name))
1094 GNUNET_free (dname);
1095 return GNUNET_SYSERR;
1102 GNUNET_free (dname);
1108 * Opaque handle used for iterating over a directory.
1110 struct GNUNET_DISK_DirectoryIterator
1114 * Function to call on directory entries.
1116 GNUNET_DISK_DirectoryIteratorCallback callback;
1119 * Closure for callback.
1124 * Reference to directory.
1134 * Next filename to process.
1141 enum GNUNET_SCHEDULER_Priority priority;
1147 * Task used by the directory iterator.
1150 directory_iterator_task (void *cls,
1151 const struct GNUNET_SCHEDULER_TaskContext *tc)
1153 struct GNUNET_DISK_DirectoryIterator *iter = cls;
1156 name = iter->next_name;
1157 GNUNET_assert (name != NULL);
1158 iter->next_name = NULL;
1159 iter->callback (iter->callback_cls, iter, name, iter->dirname);
1165 * This function must be called during the DiskIteratorCallback
1166 * (exactly once) to schedule the task to process the next
1167 * filename in the directory (if there is one).
1169 * @param iter opaque handle for the iterator
1170 * @param can set to GNUNET_YES to terminate the iteration early
1171 * @return GNUNET_YES if iteration will continue,
1172 * GNUNET_NO if this was the last entry (and iteration is complete),
1173 * GNUNET_SYSERR if abort was YES
1176 GNUNET_DISK_directory_iterator_next (struct GNUNET_DISK_DirectoryIterator *iter,
1179 struct dirent *finfo;
1181 GNUNET_assert (iter->next_name == NULL);
1182 if (can == GNUNET_YES)
1184 CLOSEDIR (iter->directory);
1185 GNUNET_free (iter->dirname);
1187 return GNUNET_SYSERR;
1189 while (NULL != (finfo = READDIR (iter->directory)))
1191 if ((0 == strcmp (finfo->d_name, ".")) ||
1192 (0 == strcmp (finfo->d_name, "..")))
1194 GNUNET_asprintf (&iter->next_name, "%s%s%s", iter->dirname,
1195 DIR_SEPARATOR_STR, finfo->d_name);
1200 GNUNET_DISK_directory_iterator_next (iter, GNUNET_YES);
1203 GNUNET_SCHEDULER_add_with_priority (iter->priority, &directory_iterator_task,
1210 * Scan a directory for files using the scheduler to run a task for
1211 * each entry. The name of the directory must be expanded first (!).
1212 * If a scheduler does not need to be used, GNUNET_DISK_directory_scan
1213 * may provide a simpler API.
1215 * @param prio priority to use
1216 * @param dirName the name of the directory
1217 * @param callback the method to call for each file
1218 * @param callback_cls closure for callback
1219 * @return GNUNET_YES if directory is not empty and 'callback'
1220 * will be called later, GNUNET_NO otherwise, GNUNET_SYSERR on error.
1223 GNUNET_DISK_directory_iterator_start (enum GNUNET_SCHEDULER_Priority prio,
1224 const char *dirName,
1225 GNUNET_DISK_DirectoryIteratorCallback
1226 callback, void *callback_cls)
1228 struct GNUNET_DISK_DirectoryIterator *di;
1230 di = GNUNET_malloc (sizeof (struct GNUNET_DISK_DirectoryIterator));
1231 di->callback = callback;
1232 di->callback_cls = callback_cls;
1233 di->directory = OPENDIR (dirName);
1234 if (di->directory == NULL)
1237 callback (callback_cls, NULL, NULL, NULL);
1238 return GNUNET_SYSERR;
1240 di->dirname = GNUNET_strdup (dirName);
1241 di->priority = prio;
1242 return GNUNET_DISK_directory_iterator_next (di, GNUNET_NO);
1247 * Function that removes the given directory by calling
1248 * "GNUNET_DISK_directory_remove".
1250 * @param unused not used
1251 * @param fn directory to remove
1255 remove_helper (void *unused, const char *fn)
1257 (void) GNUNET_DISK_directory_remove (fn);
1263 * Remove all files in a directory (rm -rf). Call with
1267 * @param fileName the file to remove
1268 * @return GNUNET_OK on success, GNUNET_SYSERR on error
1271 GNUNET_DISK_directory_remove (const char *fileName)
1275 if (0 != LSTAT (fileName, &istat))
1276 return GNUNET_NO; /* file may not exist... */
1277 CHMOD (fileName, S_IWUSR | S_IRUSR | S_IXUSR);
1278 if (UNLINK (fileName) == 0)
1280 if ((errno != EISDIR) &&
1281 /* EISDIR is not sufficient in all cases, e.g.
1282 * sticky /tmp directory may result in EPERM on BSD.
1283 * So we also explicitly check "isDirectory" */
1284 (GNUNET_YES != GNUNET_DISK_directory_test (fileName)))
1286 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "rmdir", fileName);
1287 return GNUNET_SYSERR;
1289 if (GNUNET_SYSERR ==
1290 GNUNET_DISK_directory_scan (fileName, &remove_helper, NULL))
1291 return GNUNET_SYSERR;
1292 if (0 != RMDIR (fileName))
1294 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "rmdir", fileName);
1295 return GNUNET_SYSERR;
1304 * @param src file to copy
1305 * @param dst destination file name
1306 * @return GNUNET_OK on success, GNUNET_SYSERR on error
1309 GNUNET_DISK_file_copy (const char *src, const char *dst)
1315 struct GNUNET_DISK_FileHandle *in;
1316 struct GNUNET_DISK_FileHandle *out;
1318 if (GNUNET_OK != GNUNET_DISK_file_size (src, &size, GNUNET_YES, GNUNET_YES))
1319 return GNUNET_SYSERR;
1321 in = GNUNET_DISK_file_open (src, GNUNET_DISK_OPEN_READ,
1322 GNUNET_DISK_PERM_NONE);
1324 return GNUNET_SYSERR;
1326 GNUNET_DISK_file_open (dst,
1327 GNUNET_DISK_OPEN_WRITE | GNUNET_DISK_OPEN_CREATE |
1328 GNUNET_DISK_OPEN_FAILIFEXISTS,
1329 GNUNET_DISK_PERM_USER_READ |
1330 GNUNET_DISK_PERM_USER_WRITE |
1331 GNUNET_DISK_PERM_GROUP_READ |
1332 GNUNET_DISK_PERM_GROUP_WRITE);
1335 GNUNET_DISK_file_close (in);
1336 return GNUNET_SYSERR;
1338 buf = GNUNET_malloc (COPY_BLK_SIZE);
1341 len = COPY_BLK_SIZE;
1342 if (len > size - pos)
1344 if (len != GNUNET_DISK_file_read (in, buf, len))
1346 if (len != GNUNET_DISK_file_write (out, buf, len))
1351 GNUNET_DISK_file_close (in);
1352 GNUNET_DISK_file_close (out);
1356 GNUNET_DISK_file_close (in);
1357 GNUNET_DISK_file_close (out);
1358 return GNUNET_SYSERR;
1363 * @brief Removes special characters as ':' from a filename.
1364 * @param fn the filename to canonicalize
1367 GNUNET_DISK_filename_canonicalize (char *fn)
1377 if (c == '/' || c == '\\' || c == ':' || c == '*' || c == '?' || c == '"' ||
1378 c == '<' || c == '>' || c == '|')
1390 * @brief Change owner of a file
1392 * @param filename name of file to change the owner of
1393 * @param user name of the new owner
1394 * @return GNUNET_OK on success, GNUNET_SYSERR on failure
1397 GNUNET_DISK_file_change_owner (const char *filename, const char *user)
1402 pws = getpwnam (user);
1405 LOG (GNUNET_ERROR_TYPE_ERROR,
1406 _("Cannot obtain information about user `%s': %s\n"), user,
1408 return GNUNET_SYSERR;
1410 if (0 != chown (filename, pws->pw_uid, pws->pw_gid))
1411 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "chown", filename);
1418 * Lock a part of a file
1419 * @param fh file handle
1420 * @param lockStart absolute position from where to lock
1421 * @param lockEnd absolute position until where to lock
1422 * @param excl GNUNET_YES for an exclusive lock
1423 * @return GNUNET_OK on success, GNUNET_SYSERR on error
1426 GNUNET_DISK_file_lock (struct GNUNET_DISK_FileHandle *fh, OFF_T lockStart,
1427 OFF_T lockEnd, int excl)
1432 return GNUNET_SYSERR;
1438 memset (&fl, 0, sizeof (struct flock));
1439 fl.l_type = excl ? F_WRLCK : F_RDLCK;
1440 fl.l_whence = SEEK_SET;
1441 fl.l_start = lockStart;
1444 return fcntl (fh->fd, F_SETLK, &fl) != 0 ? GNUNET_SYSERR : GNUNET_OK;
1447 OFF_T diff = lockEnd - lockStart;
1448 DWORD diff_low, diff_high;
1449 diff_low = (DWORD) (diff & 0xFFFFFFFF);
1450 diff_high = (DWORD) ((diff >> (sizeof (DWORD) * 8)) & 0xFFFFFFFF);
1452 memset (&o, 0, sizeof (OVERLAPPED));
1453 o.Offset = (DWORD) (lockStart & 0xFFFFFFFF);;
1454 o.OffsetHigh = (DWORD) (((lockStart & ~0xFFFFFFFF) >> (sizeof (DWORD) * 8)) & 0xFFFFFFFF);
1457 (fh->h, (excl ? LOCKFILE_EXCLUSIVE_LOCK : 0) | LOCKFILE_FAIL_IMMEDIATELY,
1458 0, diff_low, diff_high, &o))
1460 SetErrnoFromWinError (GetLastError ());
1461 return GNUNET_SYSERR;
1470 * Unlock a part of a file
1471 * @param fh file handle
1472 * @param unlockStart absolute position from where to unlock
1473 * @param unlockEnd absolute position until where to unlock
1474 * @return GNUNET_OK on success, GNUNET_SYSERR on error
1477 GNUNET_DISK_file_unlock (struct GNUNET_DISK_FileHandle *fh, OFF_T unlockStart,
1483 return GNUNET_SYSERR;
1489 memset (&fl, 0, sizeof (struct flock));
1490 fl.l_type = F_UNLCK;
1491 fl.l_whence = SEEK_SET;
1492 fl.l_start = unlockStart;
1493 fl.l_len = unlockEnd;
1495 return fcntl (fh->fd, F_SETLK, &fl) != 0 ? GNUNET_SYSERR : GNUNET_OK;
1498 OFF_T diff = unlockEnd - unlockStart;
1499 DWORD diff_low, diff_high;
1500 diff_low = (DWORD) (diff & 0xFFFFFFFF);
1501 diff_high = (DWORD) ((diff >> (sizeof (DWORD) * 8)) & 0xFFFFFFFF);
1503 memset (&o, 0, sizeof (OVERLAPPED));
1504 o.Offset = (DWORD) (unlockStart & 0xFFFFFFFF);;
1505 o.OffsetHigh = (DWORD) (((unlockStart & ~0xFFFFFFFF) >> (sizeof (DWORD) * 8)) & 0xFFFFFFFF);
1507 if (!UnlockFileEx (fh->h, 0, diff_low, diff_high, &o))
1509 SetErrnoFromWinError (GetLastError ());
1510 return GNUNET_SYSERR;
1519 * Open a file. Note that the access permissions will only be
1520 * used if a new file is created and if the underlying operating
1521 * system supports the given permissions.
1523 * @param fn file name to be opened
1524 * @param flags opening flags, a combination of GNUNET_DISK_OPEN_xxx bit flags
1525 * @param perm permissions for the newly created file, use
1526 * GNUNET_DISK_PERM_USER_NONE if a file could not be created by this
1527 * call (because of flags)
1528 * @return IO handle on success, NULL on error
1530 struct GNUNET_DISK_FileHandle *
1531 GNUNET_DISK_file_open (const char *fn, enum GNUNET_DISK_OpenFlags flags,
1532 enum GNUNET_DISK_AccessPermissions perm)
1535 struct GNUNET_DISK_FileHandle *ret;
1541 wchar_t wexpfn[MAX_PATH + 1];
1548 expfn = GNUNET_STRINGS_filename_expand (fn);
1553 if (GNUNET_DISK_OPEN_READWRITE == (flags & GNUNET_DISK_OPEN_READWRITE))
1554 oflags = O_RDWR; /* note: O_RDWR is NOT always O_RDONLY | O_WRONLY */
1555 else if (flags & GNUNET_DISK_OPEN_READ)
1557 else if (flags & GNUNET_DISK_OPEN_WRITE)
1562 GNUNET_free (expfn);
1565 if (flags & GNUNET_DISK_OPEN_FAILIFEXISTS)
1566 oflags |= (O_CREAT | O_EXCL);
1567 if (flags & GNUNET_DISK_OPEN_TRUNCATE)
1569 if (flags & GNUNET_DISK_OPEN_APPEND)
1571 if (flags & GNUNET_DISK_OPEN_CREATE)
1573 (void) GNUNET_DISK_directory_create_for_file (expfn);
1575 mode = translate_unix_perms (perm);
1578 fd = open (expfn, oflags | O_LARGEFILE, mode);
1581 if (0 == (flags & GNUNET_DISK_OPEN_FAILIFEXISTS))
1582 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "open", expfn);
1584 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_DEBUG, "open", expfn);
1585 GNUNET_free (expfn);
1592 if (GNUNET_DISK_OPEN_READWRITE == (flags & GNUNET_DISK_OPEN_READWRITE))
1593 access = FILE_READ_DATA | FILE_WRITE_DATA;
1594 else if (flags & GNUNET_DISK_OPEN_READ)
1595 access = FILE_READ_DATA;
1596 else if (flags & GNUNET_DISK_OPEN_WRITE)
1597 access = FILE_WRITE_DATA;
1599 if (flags & GNUNET_DISK_OPEN_FAILIFEXISTS)
1603 else if (flags & GNUNET_DISK_OPEN_CREATE)
1605 (void) GNUNET_DISK_directory_create_for_file (expfn);
1606 if (flags & GNUNET_DISK_OPEN_TRUNCATE)
1607 disp = CREATE_ALWAYS;
1611 else if (flags & GNUNET_DISK_OPEN_TRUNCATE)
1613 disp = TRUNCATE_EXISTING;
1617 disp = OPEN_EXISTING;
1620 if (ERROR_SUCCESS == plibc_conv_to_win_pathwconv(expfn, wexpfn))
1621 h = CreateFileW (wexpfn, access,
1622 FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE, NULL,
1623 disp, FILE_ATTRIBUTE_NORMAL, NULL);
1625 h = INVALID_HANDLE_VALUE;
1626 if (h == INVALID_HANDLE_VALUE)
1628 SetErrnoFromWinError (GetLastError ());
1629 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "open", expfn);
1630 GNUNET_free (expfn);
1634 if (flags & GNUNET_DISK_OPEN_APPEND)
1635 if (SetFilePointer (h, 0, 0, FILE_END) == INVALID_SET_FILE_POINTER)
1637 SetErrnoFromWinError (GetLastError ());
1638 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "SetFilePointer", expfn);
1640 GNUNET_free (expfn);
1645 ret = GNUNET_malloc (sizeof (struct GNUNET_DISK_FileHandle));
1648 ret->type = GNUNET_DISK_FILE;
1652 GNUNET_free (expfn);
1658 * Close an open file
1659 * @param h file handle
1660 * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
1663 GNUNET_DISK_file_close (struct GNUNET_DISK_FileHandle *h)
1668 return GNUNET_SYSERR;
1672 if (!CloseHandle (h->h))
1674 SetErrnoFromWinError (GetLastError ());
1675 LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING, "close");
1676 GNUNET_free (h->oOverlapRead);
1677 GNUNET_free (h->oOverlapWrite);
1679 return GNUNET_SYSERR;
1682 if (close (h->fd) != 0)
1684 LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING, "close");
1686 return GNUNET_SYSERR;
1695 * Construct full path to a file inside of the private
1696 * directory used by GNUnet. Also creates the corresponding
1697 * directory. If the resulting name is supposed to be
1698 * a directory, end the last argument in '/' (or pass
1699 * DIR_SEPARATOR_STR as the last argument before NULL).
1701 * @param cfg configuration to use (determines HOME)
1702 * @param serviceName name of the service
1703 * @param ... is NULL-terminated list of
1704 * path components to append to the
1705 * private directory name.
1706 * @return the constructed filename
1709 GNUNET_DISK_get_home_filename (const struct GNUNET_CONFIGURATION_Handle *cfg,
1710 const char *serviceName, ...)
1716 unsigned int needed;
1719 GNUNET_CONFIGURATION_get_value_filename (cfg, serviceName, "HOME", &pfx))
1723 LOG (GNUNET_ERROR_TYPE_WARNING,
1724 _("No `%s' specified for service `%s' in configuration.\n"), "HOME",
1728 needed = strlen (pfx) + 2;
1729 if ((pfx[strlen (pfx) - 1] != '/') && (pfx[strlen (pfx) - 1] != '\\'))
1731 va_start (ap, serviceName);
1734 c = va_arg (ap, const char *);
1738 needed += strlen (c);
1739 if ((c[strlen (c) - 1] != '/') && (c[strlen (c) - 1] != '\\'))
1743 ret = GNUNET_malloc (needed);
1746 va_start (ap, serviceName);
1749 c = va_arg (ap, const char *);
1753 if ((c[strlen (c) - 1] != '/') && (c[strlen (c) - 1] != '\\'))
1754 strcat (ret, DIR_SEPARATOR_STR);
1758 if ((ret[strlen (ret) - 1] != '/') && (ret[strlen (ret) - 1] != '\\'))
1759 (void) GNUNET_DISK_directory_create_for_file (ret);
1761 (void) GNUNET_DISK_directory_create (ret);
1767 * Handle for a memory-mapping operation.
1769 struct GNUNET_DISK_MapHandle
1772 * Address where the map is in memory.
1778 * Underlying OS handle.
1783 * Number of bytes mapped.
1791 #define MAP_FAILED ((void *) -1)
1795 * Map a file into memory
1797 * @param h open file handle
1798 * @param m handle to the new mapping
1799 * @param access access specification, GNUNET_DISK_MAP_TYPE_xxx
1800 * @param len size of the mapping
1801 * @return pointer to the mapped memory region, NULL on failure
1804 GNUNET_DISK_file_map (const struct GNUNET_DISK_FileHandle *h,
1805 struct GNUNET_DISK_MapHandle **m,
1806 enum GNUNET_DISK_MapType access, size_t len)
1815 DWORD mapAccess, protect;
1817 if ((access & GNUNET_DISK_MAP_TYPE_READ) &&
1818 (access & GNUNET_DISK_MAP_TYPE_WRITE))
1820 protect = PAGE_READWRITE;
1821 mapAccess = FILE_MAP_ALL_ACCESS;
1823 else if (access & GNUNET_DISK_MAP_TYPE_READ)
1825 protect = PAGE_READONLY;
1826 mapAccess = FILE_MAP_READ;
1828 else if (access & GNUNET_DISK_MAP_TYPE_WRITE)
1830 protect = PAGE_READWRITE;
1831 mapAccess = FILE_MAP_WRITE;
1839 *m = GNUNET_malloc (sizeof (struct GNUNET_DISK_MapHandle));
1840 (*m)->h = CreateFileMapping (h->h, NULL, protect, 0, 0, NULL);
1841 if ((*m)->h == INVALID_HANDLE_VALUE)
1843 SetErrnoFromWinError (GetLastError ());
1848 (*m)->addr = MapViewOfFile ((*m)->h, mapAccess, 0, 0, len);
1851 SetErrnoFromWinError (GetLastError ());
1852 CloseHandle ((*m)->h);
1861 if (access & GNUNET_DISK_MAP_TYPE_READ)
1863 if (access & GNUNET_DISK_MAP_TYPE_WRITE)
1865 *m = GNUNET_malloc (sizeof (struct GNUNET_DISK_MapHandle));
1866 (*m)->addr = mmap (NULL, len, prot, MAP_SHARED, h->fd, 0);
1867 GNUNET_assert (NULL != (*m)->addr);
1868 if (MAP_FAILED == (*m)->addr)
1880 * @param h mapping handle
1881 * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
1884 GNUNET_DISK_file_unmap (struct GNUNET_DISK_MapHandle *h)
1891 return GNUNET_SYSERR;
1895 ret = UnmapViewOfFile (h->addr) ? GNUNET_OK : GNUNET_SYSERR;
1896 if (ret != GNUNET_OK)
1897 SetErrnoFromWinError (GetLastError ());
1898 if (!CloseHandle (h->h) && (ret == GNUNET_OK))
1900 ret = GNUNET_SYSERR;
1901 SetErrnoFromWinError (GetLastError ());
1904 ret = munmap (h->addr, h->len) != -1 ? GNUNET_OK : GNUNET_SYSERR;
1912 * Write file changes to disk
1913 * @param h handle to an open file
1914 * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
1917 GNUNET_DISK_file_sync (const struct GNUNET_DISK_FileHandle *h)
1922 return GNUNET_SYSERR;
1928 ret = FlushFileBuffers (h->h) ? GNUNET_OK : GNUNET_SYSERR;
1929 if (ret != GNUNET_OK)
1930 SetErrnoFromWinError (GetLastError ());
1932 #elif defined(FREEBSD) || defined(OPENBSD) || defined(DARWIN)
1933 return fsync (h->fd) == -1 ? GNUNET_SYSERR : GNUNET_OK;
1935 return fdatasync (h->fd) == -1 ? GNUNET_SYSERR : GNUNET_OK;
1941 /* Copyright Bob Byrnes <byrnes <at> curl.com>
1942 http://permalink.gmane.org/gmane.os.cygwin.patches/2121
1944 /* Create a pipe, and return handles to the read and write ends,
1945 just like CreatePipe, but ensure that the write end permits
1946 FILE_READ_ATTRIBUTES access, on later versions of win32 where
1947 this is supported. This access is needed by NtQueryInformationFile,
1948 which is used to implement select and nonblocking writes.
1949 Note that the return value is either NO_ERROR or GetLastError,
1950 unlike CreatePipe, which returns a bool for success or failure. */
1952 create_selectable_pipe (PHANDLE read_pipe_ptr, PHANDLE write_pipe_ptr,
1953 LPSECURITY_ATTRIBUTES sa_ptr, DWORD psize,
1954 DWORD dwReadMode, DWORD dwWriteMode)
1956 /* Default to error. */
1957 *read_pipe_ptr = *write_pipe_ptr = INVALID_HANDLE_VALUE;
1959 HANDLE read_pipe = INVALID_HANDLE_VALUE, write_pipe = INVALID_HANDLE_VALUE;
1961 /* Ensure that there is enough pipe buffer space for atomic writes. */
1962 if (psize < PIPE_BUF)
1965 char pipename[MAX_PATH];
1967 /* Retry CreateNamedPipe as long as the pipe name is in use.
1968 * Retrying will probably never be necessary, but we want
1969 * to be as robust as possible. */
1972 static volatile LONG pipe_unique_id;
1974 snprintf (pipename, sizeof pipename, "\\\\.\\pipe\\gnunet-%d-%ld",
1975 getpid (), InterlockedIncrement ((LONG *) & pipe_unique_id));
1976 LOG (GNUNET_ERROR_TYPE_DEBUG, "CreateNamedPipe: name = %s, size = %lu\n",
1978 /* Use CreateNamedPipe instead of CreatePipe, because the latter
1979 * returns a write handle that does not permit FILE_READ_ATTRIBUTES
1980 * access, on versions of win32 earlier than WinXP SP2.
1981 * CreatePipe also stupidly creates a full duplex pipe, which is
1982 * a waste, since only a single direction is actually used.
1983 * It's important to only allow a single instance, to ensure that
1984 * the pipe was not created earlier by some other process, even if
1985 * the pid has been reused. We avoid FILE_FLAG_FIRST_PIPE_INSTANCE
1986 * because that is only available for Win2k SP2 and WinXP. */
1987 read_pipe = CreateNamedPipeA (pipename, PIPE_ACCESS_INBOUND | dwReadMode, PIPE_TYPE_BYTE | PIPE_READMODE_BYTE, 1, /* max instances */
1988 psize, /* output buffer size */
1989 psize, /* input buffer size */
1990 NMPWAIT_USE_DEFAULT_WAIT, sa_ptr);
1992 if (read_pipe != INVALID_HANDLE_VALUE)
1994 LOG (GNUNET_ERROR_TYPE_DEBUG, "pipe read handle = %p\n", read_pipe);
1998 DWORD err = GetLastError ();
2002 case ERROR_PIPE_BUSY:
2003 /* The pipe is already open with compatible parameters.
2004 * Pick a new name and retry. */
2005 LOG (GNUNET_ERROR_TYPE_DEBUG, "pipe busy, retrying\n");
2007 case ERROR_ACCESS_DENIED:
2008 /* The pipe is already open with incompatible parameters.
2009 * Pick a new name and retry. */
2010 LOG (GNUNET_ERROR_TYPE_DEBUG, "pipe access denied, retrying\n");
2012 case ERROR_CALL_NOT_IMPLEMENTED:
2013 /* We are on an older Win9x platform without named pipes.
2014 * Return an anonymous pipe as the best approximation. */
2015 LOG (GNUNET_ERROR_TYPE_DEBUG,
2016 "CreateNamedPipe not implemented, resorting to "
2017 "CreatePipe: size = %lu\n", psize);
2018 if (CreatePipe (read_pipe_ptr, write_pipe_ptr, sa_ptr, psize))
2020 LOG (GNUNET_ERROR_TYPE_DEBUG, "pipe read handle = %p, write handle = %p\n",
2025 err = GetLastError ();
2026 LOG (GNUNET_ERROR_TYPE_ERROR, "CreatePipe failed: %d\n", err);
2029 LOG (GNUNET_ERROR_TYPE_ERROR, "CreateNamedPipe failed: %d\n", err);
2034 LOG (GNUNET_ERROR_TYPE_DEBUG, "CreateFile: name = %s\n", pipename);
2036 /* Open the named pipe for writing.
2037 * Be sure to permit FILE_READ_ATTRIBUTES access. */
2038 write_pipe = CreateFileA (pipename, GENERIC_WRITE | FILE_READ_ATTRIBUTES, 0, /* share mode */
2039 sa_ptr, OPEN_EXISTING, dwWriteMode, /* flags and attributes */
2040 0); /* handle to template file */
2042 if (write_pipe == INVALID_HANDLE_VALUE)
2045 DWORD err = GetLastError ();
2047 LOG (GNUNET_ERROR_TYPE_DEBUG, "CreateFile failed: %d\n", err);
2048 CloseHandle (read_pipe);
2051 LOG (GNUNET_ERROR_TYPE_DEBUG, "pipe write handle = %p\n", write_pipe);
2053 *read_pipe_ptr = read_pipe;
2054 *write_pipe_ptr = write_pipe;
2061 * Creates an interprocess channel
2063 * @param blocking_read creates an asynchronous pipe for reading if set to GNUNET_NO
2064 * @param blocking_write creates an asynchronous pipe for writing if set to GNUNET_NO
2065 * @param inherit_read inherit the parent processes stdin (only for windows)
2066 * @param inherit_write inherit the parent processes stdout (only for windows)
2067 * @return handle to the new pipe, NULL on error
2069 struct GNUNET_DISK_PipeHandle *
2070 GNUNET_DISK_pipe (int blocking_read, int blocking_write, int inherit_read, int inherit_write)
2081 LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR, "pipe");
2085 return GNUNET_DISK_pipe_from_fd (blocking_read,
2089 struct GNUNET_DISK_PipeHandle *p;
2090 struct GNUNET_DISK_FileHandle *fds;
2095 p = GNUNET_malloc (sizeof (struct GNUNET_DISK_PipeHandle) +
2096 2 * sizeof (struct GNUNET_DISK_FileHandle));
2097 fds = (struct GNUNET_DISK_FileHandle *) &p[1];
2101 /* All pipes are overlapped. If you want them to block - just
2102 * call WriteFile() and ReadFile() with NULL overlapped pointer.
2105 create_selectable_pipe (&p->fd[0]->h, &p->fd[1]->h, NULL, 0,
2106 FILE_FLAG_OVERLAPPED,
2107 FILE_FLAG_OVERLAPPED);
2111 SetErrnoFromWinError (GetLastError ());
2114 if (!DuplicateHandle
2115 (GetCurrentProcess (), p->fd[0]->h, GetCurrentProcess (), &tmp_handle, 0,
2116 inherit_read == GNUNET_YES ? TRUE : FALSE, DUPLICATE_SAME_ACCESS))
2118 SetErrnoFromWinError (GetLastError ());
2119 CloseHandle (p->fd[0]->h);
2120 CloseHandle (p->fd[1]->h);
2124 CloseHandle (p->fd[0]->h);
2125 p->fd[0]->h = tmp_handle;
2127 if (!DuplicateHandle
2128 (GetCurrentProcess (), p->fd[1]->h, GetCurrentProcess (), &tmp_handle, 0,
2129 inherit_write == GNUNET_YES ? TRUE : FALSE, DUPLICATE_SAME_ACCESS))
2131 SetErrnoFromWinError (GetLastError ());
2132 CloseHandle (p->fd[0]->h);
2133 CloseHandle (p->fd[1]->h);
2137 CloseHandle (p->fd[1]->h);
2138 p->fd[1]->h = tmp_handle;
2140 p->fd[0]->type = GNUNET_PIPE;
2141 p->fd[1]->type = GNUNET_PIPE;
2143 p->fd[0]->oOverlapRead = GNUNET_malloc (sizeof (OVERLAPPED));
2144 p->fd[0]->oOverlapWrite = GNUNET_malloc (sizeof (OVERLAPPED));
2145 p->fd[1]->oOverlapRead = GNUNET_malloc (sizeof (OVERLAPPED));
2146 p->fd[1]->oOverlapWrite = GNUNET_malloc (sizeof (OVERLAPPED));
2148 p->fd[0]->oOverlapRead->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2149 p->fd[0]->oOverlapWrite->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2151 p->fd[1]->oOverlapRead->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2152 p->fd[1]->oOverlapWrite->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2160 * Creates a pipe object from a couple of file descriptors.
2161 * Useful for wrapping existing pipe FDs.
2163 * @param blocking_read creates an asynchronous pipe for reading if set to GNUNET_NO
2164 * @param blocking_write creates an asynchronous pipe for writing if set to GNUNET_NO
2165 * @param fd an array of two fd values. One of them may be -1 for read-only or write-only pipes
2167 * @return handle to the new pipe, NULL on error
2169 struct GNUNET_DISK_PipeHandle *
2170 GNUNET_DISK_pipe_from_fd (int blocking_read, int blocking_write, int fd[2])
2172 struct GNUNET_DISK_PipeHandle *p;
2173 struct GNUNET_DISK_FileHandle *fds;
2175 p = GNUNET_malloc (sizeof (struct GNUNET_DISK_PipeHandle) +
2176 2 * sizeof (struct GNUNET_DISK_FileHandle));
2177 fds = (struct GNUNET_DISK_FileHandle *) &p[1];
2183 int eno = 0; /* make gcc happy */
2185 p->fd[0]->fd = fd[0];
2186 p->fd[1]->fd = fd[1];
2192 flags = fcntl (fd[0], F_GETFL);
2193 flags |= O_NONBLOCK;
2194 if (0 > fcntl (fd[0], F_SETFL, flags))
2200 flags = fcntl (fd[0], F_GETFD);
2201 flags |= FD_CLOEXEC;
2202 if (0 > fcntl (fd[0], F_SETFD, flags))
2211 if (!blocking_write)
2213 flags = fcntl (fd[1], F_GETFL);
2214 flags |= O_NONBLOCK;
2215 if (0 > fcntl (fd[1], F_SETFL, flags))
2221 flags = fcntl (fd[1], F_GETFD);
2222 flags |= FD_CLOEXEC;
2223 if (0 > fcntl (fd[1], F_SETFD, flags))
2232 LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR, "fcntl");
2233 if (p->fd[0]->fd >= 0)
2234 GNUNET_break (0 == close (p->fd[0]->fd));
2235 if (p->fd[1]->fd >= 0)
2236 GNUNET_break (0 == close (p->fd[1]->fd));
2243 p->fd[0]->h = _get_osfhandle (fd[0]);
2245 p->fd[0]->h = INVALID_HANDLE_VALUE;
2247 p->fd[1]->h = _get_osfhandle (fd[1]);
2249 p->fd[1]->h = INVALID_HANDLE_VALUE;
2251 if (p->fd[0]->h != INVALID_HANDLE_VALUE)
2253 p->fd[0]->type = GNUNET_PIPE;
2254 p->fd[0]->oOverlapRead = GNUNET_malloc (sizeof (OVERLAPPED));
2255 p->fd[0]->oOverlapWrite = GNUNET_malloc (sizeof (OVERLAPPED));
2256 p->fd[0]->oOverlapRead->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2257 p->fd[0]->oOverlapWrite->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2260 if (p->fd[1]->h != INVALID_HANDLE_VALUE)
2262 p->fd[1]->type = GNUNET_PIPE;
2263 p->fd[1]->oOverlapRead = GNUNET_malloc (sizeof (OVERLAPPED));
2264 p->fd[1]->oOverlapWrite = GNUNET_malloc (sizeof (OVERLAPPED));
2265 p->fd[1]->oOverlapRead->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2266 p->fd[1]->oOverlapWrite->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2274 * Closes an interprocess channel
2276 * @param p pipe to close
2277 * @param end which end of the pipe to close
2278 * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
2281 GNUNET_DISK_pipe_close_end (struct GNUNET_DISK_PipeHandle *p,
2282 enum GNUNET_DISK_PipeEnd end)
2284 int ret = GNUNET_OK;
2288 if (end == GNUNET_DISK_PIPE_END_READ)
2290 if (p->fd[0]->h != INVALID_HANDLE_VALUE)
2292 if (!CloseHandle (p->fd[0]->h))
2294 SetErrnoFromWinError (GetLastError ());
2295 ret = GNUNET_SYSERR;
2297 GNUNET_free (p->fd[0]->oOverlapRead);
2298 GNUNET_free (p->fd[0]->oOverlapWrite);
2299 p->fd[0]->h = INVALID_HANDLE_VALUE;
2302 else if (end == GNUNET_DISK_PIPE_END_WRITE)
2304 if (p->fd[0]->h != INVALID_HANDLE_VALUE)
2306 if (!CloseHandle (p->fd[1]->h))
2308 SetErrnoFromWinError (GetLastError ());
2309 ret = GNUNET_SYSERR;
2311 GNUNET_free (p->fd[1]->oOverlapRead);
2312 GNUNET_free (p->fd[1]->oOverlapWrite);
2313 p->fd[1]->h = INVALID_HANDLE_VALUE;
2319 if (end == GNUNET_DISK_PIPE_END_READ)
2321 if (0 != close (p->fd[0]->fd))
2323 ret = GNUNET_SYSERR;
2328 else if (end == GNUNET_DISK_PIPE_END_WRITE)
2330 if (0 != close (p->fd[1]->fd))
2332 ret = GNUNET_SYSERR;
2344 * Closes an interprocess channel
2346 * @param p pipe to close
2347 * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
2350 GNUNET_DISK_pipe_close (struct GNUNET_DISK_PipeHandle *p)
2352 int ret = GNUNET_OK;
2356 if (p->fd[0]->h != INVALID_HANDLE_VALUE)
2358 if (!CloseHandle (p->fd[0]->h))
2360 SetErrnoFromWinError (GetLastError ());
2361 ret = GNUNET_SYSERR;
2363 GNUNET_free (p->fd[0]->oOverlapRead);
2364 GNUNET_free (p->fd[0]->oOverlapWrite);
2366 if (p->fd[1]->h != INVALID_HANDLE_VALUE)
2368 if (!CloseHandle (p->fd[1]->h))
2370 SetErrnoFromWinError (GetLastError ());
2371 ret = GNUNET_SYSERR;
2373 GNUNET_free (p->fd[1]->oOverlapRead);
2374 GNUNET_free (p->fd[1]->oOverlapWrite);
2379 if (p->fd[0]->fd != -1)
2381 if (0 != close (p->fd[0]->fd))
2383 ret = GNUNET_SYSERR;
2388 if (p->fd[1]->fd != -1)
2390 if (0 != close (p->fd[1]->fd))
2392 ret = GNUNET_SYSERR;
2404 * Get the handle to a particular pipe end
2407 * @param n end to access
2408 * @return handle for the respective end
2410 const struct GNUNET_DISK_FileHandle *
2411 GNUNET_DISK_pipe_handle (const struct GNUNET_DISK_PipeHandle *p,
2412 enum GNUNET_DISK_PipeEnd n)
2416 case GNUNET_DISK_PIPE_END_READ:
2417 case GNUNET_DISK_PIPE_END_WRITE:
2427 * Retrieve OS file handle
2429 * @param fh GNUnet file descriptor
2430 * @param dst destination buffer
2431 * @param dst_len length of dst
2432 * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
2435 GNUNET_DISK_internal_file_handle_ (const struct GNUNET_DISK_FileHandle *fh,
2436 void *dst, size_t dst_len)
2439 if (dst_len < sizeof (HANDLE))
2440 return GNUNET_SYSERR;
2441 *((HANDLE *) dst) = fh->h;
2443 if (dst_len < sizeof (int))
2444 return GNUNET_SYSERR;
2445 *((int *) dst) = fh->fd;