2 This file is part of GNUnet.
3 (C) 2001--2013 Christian Grothoff (and other contributing authors)
5 GNUnet is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 3, or (at your
8 option) any later version.
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with GNUnet; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
23 * @brief disk IO convenience methods
24 * @author Christian Grothoff
29 #include "gnunet_util_lib.h"
32 #define LOG(kind,...) GNUNET_log_from (kind, "util", __VA_ARGS__)
34 #define LOG_STRERROR(kind,syscall) GNUNET_log_from_strerror (kind, "util", syscall)
36 #define LOG_STRERROR_FILE(kind,syscall,filename) GNUNET_log_from_strerror_file (kind, "util", syscall, filename)
39 * Block size for IO for copying files.
41 #define COPY_BLK_SIZE 65536
43 #include <sys/types.h>
48 #include <sys/param.h>
51 #include <sys/mount.h>
53 #if HAVE_SYS_STATVFS_H
54 #include <sys/statvfs.h>
58 #define _IFMT 0170000 /* type of file */
59 #define _IFLNK 0120000 /* symbolic link */
60 #define S_ISLNK(m) (((m)&_IFMT) == _IFLNK)
65 * Handle used to manage a pipe.
67 struct GNUNET_DISK_PipeHandle
70 * File descriptors for the pipe.
71 * One or both of them could be NULL.
73 struct GNUNET_DISK_FileHandle *fd[2];
78 * Closure for the recursion to determine the file size
81 struct GetFileSizeData
84 * Set to the total file size.
89 * GNUNET_YES if symbolic links should be included.
91 int include_sym_links;
94 * GNUNET_YES if mode is file-only (return total == -1 for directories).
102 * Translate GNUnet-internal permission bitmap to UNIX file
103 * access permission bitmap.
105 * @param perm file permissions, GNUnet style
106 * @return file permissions, UNIX style
109 translate_unix_perms (enum GNUNET_DISK_AccessPermissions perm)
114 if (perm & GNUNET_DISK_PERM_USER_READ)
116 if (perm & GNUNET_DISK_PERM_USER_WRITE)
118 if (perm & GNUNET_DISK_PERM_USER_EXEC)
120 if (perm & GNUNET_DISK_PERM_GROUP_READ)
122 if (perm & GNUNET_DISK_PERM_GROUP_WRITE)
124 if (perm & GNUNET_DISK_PERM_GROUP_EXEC)
126 if (perm & GNUNET_DISK_PERM_OTHER_READ)
128 if (perm & GNUNET_DISK_PERM_OTHER_WRITE)
130 if (perm & GNUNET_DISK_PERM_OTHER_EXEC)
139 * Iterate over all files in the given directory and
140 * accumulate their size.
142 * @param cls closure of type `struct GetFileSizeData`
143 * @param fn current filename we are looking at
144 * @return #GNUNET_SYSERR on serious errors, otherwise #GNUNET_OK
147 getSizeRec (void *cls, const char *fn)
149 struct GetFileSizeData *gfsd = cls;
151 #if defined (HAVE_STAT64) && !(defined (_FILE_OFFSET_BITS) && _FILE_OFFSET_BITS == 64)
154 if (0 != STAT64 (fn, &buf))
156 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_DEBUG, "stat64", fn);
157 return GNUNET_SYSERR;
162 if (0 != STAT (fn, &buf))
164 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_DEBUG, "stat", fn);
165 return GNUNET_SYSERR;
168 if ((S_ISDIR (buf.st_mode)) && (gfsd->single_file_mode == GNUNET_YES))
171 return GNUNET_SYSERR;
173 if ((!S_ISLNK (buf.st_mode)) || (gfsd->include_sym_links == GNUNET_YES))
174 gfsd->total += buf.st_size;
175 if ((S_ISDIR (buf.st_mode)) && (0 == ACCESS (fn, X_OK)) &&
176 ((!S_ISLNK (buf.st_mode)) || (gfsd->include_sym_links == GNUNET_YES)))
178 if (GNUNET_SYSERR == GNUNET_DISK_directory_scan (fn, &getSizeRec, gfsd))
179 return GNUNET_SYSERR;
186 * Checks whether a handle is invalid
188 * @param h handle to check
189 * @return #GNUNET_YES if invalid, #GNUNET_NO if valid
192 GNUNET_DISK_handle_invalid (const struct GNUNET_DISK_FileHandle *h)
195 return ((!h) || (h->h == INVALID_HANDLE_VALUE)) ? GNUNET_YES : GNUNET_NO;
197 return ((!h) || (h->fd == -1)) ? GNUNET_YES : GNUNET_NO;
202 * Get the size of an open file.
204 * @param fh open file handle
205 * @param size where to write size of the file
206 * @return #GNUNET_OK on success, #GNUNET_SYSERR on error
209 GNUNET_DISK_file_handle_size (struct GNUNET_DISK_FileHandle *fh,
215 b = GetFileSizeEx (fh->h, &li);
218 SetErrnoFromWinError (GetLastError ());
219 return GNUNET_SYSERR;
221 *size = (off_t) li.QuadPart;
225 if (0 != FSTAT (fh->fd, &sbuf))
226 return GNUNET_SYSERR;
227 *size = sbuf.st_size;
234 * Move the read/write pointer in a file
236 * @param h handle of an open file
237 * @param offset position to move to
238 * @param whence specification to which position the offset parameter relates to
239 * @return the new position on success, #GNUNET_SYSERR otherwise
242 GNUNET_DISK_file_seek (const struct GNUNET_DISK_FileHandle * h, off_t offset,
243 enum GNUNET_DISK_Seek whence)
248 return GNUNET_SYSERR;
253 LARGE_INTEGER new_pos;
256 static DWORD t[] = { FILE_BEGIN, FILE_CURRENT, FILE_END };
257 li.QuadPart = offset;
259 b = SetFilePointerEx (h->h, li, &new_pos, t[whence]);
262 SetErrnoFromWinError (GetLastError ());
263 return GNUNET_SYSERR;
265 return (off_t) new_pos.QuadPart;
267 static int t[] = { SEEK_SET, SEEK_CUR, SEEK_END };
269 return lseek (h->fd, offset, t[whence]);
275 * Get the size of the file (or directory) of the given file (in
278 * @param filename name of the file or directory
279 * @param size set to the size of the file (or,
280 * in the case of directories, the sum
281 * of all sizes of files in the directory)
282 * @param include_symbolic_links should symbolic links be
284 * @param single_file_mode #GNUNET_YES to only get size of one file
285 * and return #GNUNET_SYSERR for directories.
286 * @return #GNUNET_SYSERR on error, #GNUNET_OK on success
289 GNUNET_DISK_file_size (const char *filename,
291 int include_symbolic_links,
292 int single_file_mode)
294 struct GetFileSizeData gfsd;
297 GNUNET_assert (size != NULL);
299 gfsd.include_sym_links = include_symbolic_links;
300 gfsd.single_file_mode = single_file_mode;
301 ret = getSizeRec (&gfsd, filename);
308 * Obtain some unique identifiers for the given file
309 * that can be used to identify it in the local system.
310 * This function is used between GNUnet processes to
311 * quickly check if two files with the same absolute path
312 * are actually identical. The two processes represent
313 * the same peer but may communicate over the network
314 * (and the file may be on an NFS volume). This function
315 * may not be supported on all operating systems.
317 * @param filename name of the file
318 * @param dev set to the device ID
319 * @param ino set to the inode ID
320 * @return #GNUNET_OK on success
323 GNUNET_DISK_file_get_identifiers (const char *filename, uint64_t * dev,
328 // FIXME NILS: test this
329 struct GNUNET_DISK_FileHandle *fh;
330 BY_HANDLE_FILE_INFORMATION info;
333 fh = GNUNET_DISK_file_open (filename, GNUNET_DISK_OPEN_READ, 0);
335 return GNUNET_SYSERR;
336 succ = GetFileInformationByHandle (fh->h, &info);
337 GNUNET_DISK_file_close (fh);
340 return GNUNET_SYSERR;
342 *dev = info.dwVolumeSerialNumber;
343 *ino = ((((uint64_t) info.nFileIndexHigh) << (sizeof (DWORD) * 8)) | info.nFileIndexLow);
350 if (0 != stat (filename, &sbuf))
352 return GNUNET_SYSERR;
354 *ino = (uint64_t) sbuf.st_ino;
363 if (0 != statvfs (filename, &fbuf))
365 return GNUNET_SYSERR;
367 *dev = (uint64_t) fbuf.f_fsid;
373 if (0 != statfs (filename, &fbuf))
375 return GNUNET_SYSERR;
377 *dev = ((uint64_t) fbuf.f_fsid.val[0]) << 32 ||
378 ((uint64_t) fbuf.f_fsid.val[1]);
383 #endif /* !WINDOWS */
389 * Create the name for a temporary file or directory from a template.
391 * @param t template (without XXXXX or "/tmp/")
392 * @return name ready for passing to 'mktemp' or 'mkdtemp', NULL on error
395 mktemp_name (const char *t)
401 if ((t[0] != '/') && (t[0] != '\\')
403 && !(isalpha ((int) t[0]) && (t[0] != '\0') && (t[1] == ':'))
407 /* FIXME: This uses system codepage on W32, not UTF-8 */
408 tmpdir = getenv ("TMPDIR");
410 tmpdir = getenv ("TMP");
412 tmpdir = getenv ("TEMP");
415 GNUNET_asprintf (&tmpl, "%s/%s%s", tmpdir, t, "XXXXXX");
419 GNUNET_asprintf (&tmpl, "%s%s", t, "XXXXXX");
422 fn = (char *) GNUNET_malloc (MAX_PATH + 1);
423 if (ERROR_SUCCESS != plibc_conv_to_win_path (tmpl, fn))
446 tfn = GNUNET_strdup (fn);
447 random_fn = _mktemp (tfn);
448 if (NULL == random_fn)
453 /* FIXME: assume fn to be UTF-8-encoded and do the right thing */
454 if (0 == CreateDirectoryA (tfn, NULL))
456 DWORD error = GetLastError ();
458 if (ERROR_ALREADY_EXISTS == error)
470 * Update POSIX permissions mask of a file on disk. If both argumets
471 * are #GNUNET_NO, the file is made world-read-write-executable (777).
473 * @param fn name of the file to update
474 * @param require_uid_match #GNUNET_YES means 700
475 * @param require_gid_match #GNUNET_YES means 770 unless @a require_uid_match is set
478 GNUNET_DISK_fix_permissions (const char *fn,
479 int require_uid_match,
480 int require_gid_match)
484 if (GNUNET_YES == require_uid_match)
485 mode = S_IRUSR | S_IWUSR | S_IXUSR;
486 else if (GNUNET_YES == require_gid_match)
487 mode = S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IWGRP | S_IXGRP;
489 mode = S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IWGRP | S_IXGRP | S_IROTH | S_IWOTH | S_IXOTH;
490 if (0 != chmod (fn, mode))
491 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING,
499 * Create an (empty) temporary directory on disk. If the given name is not
500 * an absolute path, the current 'TMPDIR' will be prepended. In any case,
501 * 6 random characters will be appended to the name to create a unique
504 * @param t component to use for the name;
505 * does NOT contain "XXXXXX" or "/tmp/".
506 * @return NULL on error, otherwise name of fresh
507 * file on disk in directory for temporary files
510 GNUNET_DISK_mkdtemp (const char *t)
514 fn = mktemp_name (t);
515 if (fn != mkdtemp (fn))
517 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_ERROR, "mkstemp", fn);
526 * Move a file out of the way (create a backup) by
527 * renaming it to "orig.NUM~" where NUM is the smallest
528 * number that is not used yet.
530 * @param fil name of the file to back up
533 GNUNET_DISK_file_backup (const char *fil)
539 slen = strlen (fil) + 20;
540 target = GNUNET_malloc (slen);
544 GNUNET_snprintf (target, slen,
548 } while (0 == access (target, F_OK));
549 if (0 != rename (fil, target))
550 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR,
553 GNUNET_free (target);
558 * Create an (empty) temporary file on disk. If the given name is not
559 * an absolute path, the current 'TMPDIR' will be prepended. In any case,
560 * 6 random characters will be appended to the name to create a unique
563 * @param t component to use for the name;
564 * does NOT contain "XXXXXX" or "/tmp/".
565 * @return NULL on error, otherwise name of fresh
566 * file on disk in directory for temporary files
569 GNUNET_DISK_mktemp (const char *t)
574 fn = mktemp_name (t);
575 if (-1 == (fd = mkstemp (fn)))
577 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_ERROR, "mkstemp", fn);
582 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "close", fn);
588 * Test if @a fil is a directory and listable. Optionally, also check if the
589 * directory is readable. Will not print an error message if the directory does
590 * not exist. Will log errors if #GNUNET_SYSERR is returned (i.e., a file exists
591 * with the same name).
593 * @param fil filename to test
594 * @param is_readable GNUNET_YES to additionally check if @a fil is readable;
595 * #GNUNET_NO to disable this check
596 * @return #GNUNET_YES if yes, #GNUNET_NO if not; #GNUNET_SYSERR if it
597 * does not exist or stat'ed
600 GNUNET_DISK_directory_test (const char *fil, int is_readable)
602 struct stat filestat;
605 ret = STAT (fil, &filestat);
609 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "stat", fil);
610 return GNUNET_SYSERR;
612 if (!S_ISDIR (filestat.st_mode))
614 LOG (GNUNET_ERROR_TYPE_DEBUG,
615 "A file already exits with the same name %s\n", fil);
618 if (GNUNET_YES == is_readable)
619 ret = ACCESS (fil, R_OK | X_OK);
621 ret = ACCESS (fil, X_OK);
624 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "access", fil);
632 * Check that fil corresponds to a filename
633 * (of a file that exists and that is not a directory).
635 * @param fil filename to check
636 * @return #GNUNET_YES if yes, #GNUNET_NO if not a file, #GNUNET_SYSERR if something
637 * else (will print an error message in that case, too).
640 GNUNET_DISK_file_test (const char *fil)
642 struct stat filestat;
646 rdir = GNUNET_STRINGS_filename_expand (fil);
648 return GNUNET_SYSERR;
650 ret = STAT (rdir, &filestat);
655 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "stat", rdir);
657 return GNUNET_SYSERR;
662 if (!S_ISREG (filestat.st_mode))
667 if (ACCESS (rdir, F_OK) < 0)
669 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "access", rdir);
671 return GNUNET_SYSERR;
679 * Implementation of "mkdir -p"
681 * @param dir the directory to create
682 * @return #GNUNET_OK on success, #GNUNET_SYSERR on failure
685 GNUNET_DISK_directory_create (const char *dir)
693 rdir = GNUNET_STRINGS_filename_expand (dir);
695 return GNUNET_SYSERR;
699 pos = 1; /* skip heading '/' */
701 /* Local or Network path? */
702 if (strncmp (rdir, "\\\\", 2) == 0)
707 if (rdir[pos] == '\\')
717 pos = 3; /* strlen("C:\\") */
720 /* Check which low level directories already exist */
722 rdir[len] = DIR_SEPARATOR;
725 if (DIR_SEPARATOR == rdir[pos2])
728 ret = GNUNET_DISK_directory_test (rdir, GNUNET_NO);
729 if (GNUNET_NO == ret)
732 return GNUNET_SYSERR;
734 rdir[pos2] = DIR_SEPARATOR;
735 if (GNUNET_YES == ret)
746 /* Start creating directories */
749 if ((rdir[pos] == DIR_SEPARATOR) || (pos == len))
752 ret = GNUNET_DISK_directory_test (rdir, GNUNET_NO);
753 if (GNUNET_NO == ret)
756 return GNUNET_SYSERR;
758 if (GNUNET_SYSERR == ret)
761 ret = mkdir (rdir, S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH); /* 755 */
763 wchar_t wrdir[MAX_PATH + 1];
764 if (ERROR_SUCCESS == plibc_conv_to_win_pathwconv(rdir, wrdir))
765 ret = !CreateDirectoryW (wrdir, NULL);
769 if ((ret != 0) && (errno != EEXIST))
771 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_ERROR, "mkdir", rdir);
773 return GNUNET_SYSERR;
776 rdir[pos] = DIR_SEPARATOR;
786 * Create the directory structure for storing
789 * @param filename name of a file in the directory
790 * @returns #GNUNET_OK on success,
791 * #GNUNET_SYSERR on failure,
792 * #GNUNET_NO if the directory
793 * exists but is not writeable for us
796 GNUNET_DISK_directory_create_for_file (const char *filename)
802 rdir = GNUNET_STRINGS_filename_expand (filename);
804 return GNUNET_SYSERR;
806 while ((len > 0) && (rdir[len] != DIR_SEPARATOR))
809 ret = GNUNET_DISK_directory_create (rdir);
810 if ((ret == GNUNET_OK) && (0 != ACCESS (rdir, W_OK)))
818 * Read the contents of a binary file into a buffer.
820 * @param h handle to an open file
821 * @param result the buffer to write the result to
822 * @param len the maximum number of bytes to read
823 * @return the number of bytes read on success, #GNUNET_SYSERR on failure
826 GNUNET_DISK_file_read (const struct GNUNET_DISK_FileHandle *h,
833 return GNUNET_SYSERR;
839 if (h->type != GNUNET_DISK_HANLDE_TYPE_PIPE)
841 if (!ReadFile (h->h, result, len, &bytes_read, NULL))
843 SetErrnoFromWinError (GetLastError ());
844 return GNUNET_SYSERR;
849 if (!ReadFile (h->h, result, len, &bytes_read, h->oOverlapRead))
851 if (GetLastError () != ERROR_IO_PENDING)
853 LOG (GNUNET_ERROR_TYPE_DEBUG, "Error reading from pipe: %u\n", GetLastError ());
854 SetErrnoFromWinError (GetLastError ());
855 return GNUNET_SYSERR;
857 LOG (GNUNET_ERROR_TYPE_DEBUG, "Will get overlapped result\n");
858 GetOverlappedResult (h->h, h->oOverlapRead, &bytes_read, TRUE);
860 LOG (GNUNET_ERROR_TYPE_DEBUG, "Read %u bytes from pipe\n", bytes_read);
864 return read (h->fd, result, len);
870 * Read the contents of a binary file into a buffer.
871 * Guarantees not to block (returns GNUNET_SYSERR and sets errno to EAGAIN
872 * when no data can be read).
874 * @param h handle to an open file
875 * @param result the buffer to write the result to
876 * @param len the maximum number of bytes to read
877 * @return the number of bytes read on success, #GNUNET_SYSERR on failure
880 GNUNET_DISK_file_read_non_blocking (const struct GNUNET_DISK_FileHandle *h,
887 return GNUNET_SYSERR;
893 if (h->type != GNUNET_DISK_HANLDE_TYPE_PIPE)
895 if (!ReadFile (h->h, result, len, &bytes_read, NULL))
897 SetErrnoFromWinError (GetLastError ());
898 return GNUNET_SYSERR;
903 if (!ReadFile (h->h, result, len, &bytes_read, h->oOverlapRead))
905 if (GetLastError () != ERROR_IO_PENDING)
907 LOG (GNUNET_ERROR_TYPE_DEBUG, "Error reading from pipe: %u\n", GetLastError ());
908 SetErrnoFromWinError (GetLastError ());
909 return GNUNET_SYSERR;
913 LOG (GNUNET_ERROR_TYPE_DEBUG,
914 "ReadFile() queued a read, cancelling\n");
917 return GNUNET_SYSERR;
920 LOG (GNUNET_ERROR_TYPE_DEBUG, "Read %u bytes\n", bytes_read);
927 /* set to non-blocking, read, then set back */
928 flags = fcntl (h->fd, F_GETFL);
929 if (0 == (flags & O_NONBLOCK))
930 (void) fcntl (h->fd, F_SETFL, flags | O_NONBLOCK);
931 ret = read (h->fd, result, len);
932 if (0 == (flags & O_NONBLOCK))
935 (void) fcntl (h->fd, F_SETFL, flags);
944 * Read the contents of a binary file into a buffer.
946 * @param fn file name
947 * @param result the buffer to write the result to
948 * @param len the maximum number of bytes to read
949 * @return number of bytes read, #GNUNET_SYSERR on failure
952 GNUNET_DISK_fn_read (const char *fn,
956 struct GNUNET_DISK_FileHandle *fh;
959 fh = GNUNET_DISK_file_open (fn, GNUNET_DISK_OPEN_READ, GNUNET_DISK_PERM_NONE);
961 return GNUNET_SYSERR;
962 ret = GNUNET_DISK_file_read (fh, result, len);
963 GNUNET_assert (GNUNET_OK == GNUNET_DISK_file_close (fh));
970 * Write a buffer to a file.
972 * @param h handle to open file
973 * @param buffer the data to write
974 * @param n number of bytes to write
975 * @return number of bytes written on success, #GNUNET_SYSERR on error
978 GNUNET_DISK_file_write (const struct GNUNET_DISK_FileHandle * h,
979 const void *buffer, size_t n)
984 return GNUNET_SYSERR;
990 if (h->type != GNUNET_DISK_HANLDE_TYPE_PIPE)
992 if (!WriteFile (h->h, buffer, n, &bytes_written, NULL))
994 SetErrnoFromWinError (GetLastError ());
995 return GNUNET_SYSERR;
1000 LOG (GNUNET_ERROR_TYPE_DEBUG, "It is a pipe trying to write %u bytes\n", n);
1001 if (!WriteFile (h->h, buffer, n, &bytes_written, h->oOverlapWrite))
1003 if (GetLastError () != ERROR_IO_PENDING)
1005 SetErrnoFromWinError (GetLastError ());
1006 LOG (GNUNET_ERROR_TYPE_DEBUG, "Error writing to pipe: %u\n",
1008 return GNUNET_SYSERR;
1010 LOG (GNUNET_ERROR_TYPE_DEBUG, "Will get overlapped result\n");
1011 if (!GetOverlappedResult (h->h, h->oOverlapWrite, &bytes_written, TRUE))
1013 SetErrnoFromWinError (GetLastError ());
1014 LOG (GNUNET_ERROR_TYPE_DEBUG,
1015 "Error getting overlapped result while writing to pipe: %u\n",
1017 return GNUNET_SYSERR;
1023 if (!GetOverlappedResult (h->h, h->oOverlapWrite, &ovr, TRUE))
1025 LOG (GNUNET_ERROR_TYPE_DEBUG,
1026 "Error getting control overlapped result while writing to pipe: %u\n",
1031 LOG (GNUNET_ERROR_TYPE_DEBUG,
1032 "Wrote %u bytes (ovr says %u), picking the greatest\n",
1033 bytes_written, ovr);
1036 if (bytes_written == 0)
1040 LOG (GNUNET_ERROR_TYPE_DEBUG, "Wrote %u bytes, returning -1 with EAGAIN\n", bytes_written);
1042 return GNUNET_SYSERR;
1045 LOG (GNUNET_ERROR_TYPE_DEBUG, "Wrote %u bytes\n", bytes_written);
1047 return bytes_written;
1049 return write (h->fd, buffer, n);
1055 * Write a buffer to a file, blocking, if necessary.
1057 * @param h handle to open file
1058 * @param buffer the data to write
1059 * @param n number of bytes to write
1060 * @return number of bytes written on success, #GNUNET_SYSERR on error
1063 GNUNET_DISK_file_write_blocking (const struct GNUNET_DISK_FileHandle * h,
1070 return GNUNET_SYSERR;
1074 DWORD bytes_written;
1075 /* We do a non-overlapped write, which is as blocking as it gets */
1076 LOG (GNUNET_ERROR_TYPE_DEBUG, "Writing %u bytes\n", n);
1077 if (!WriteFile (h->h, buffer, n, &bytes_written, NULL))
1079 SetErrnoFromWinError (GetLastError ());
1080 LOG (GNUNET_ERROR_TYPE_DEBUG, "Error writing to pipe: %u\n",
1082 return GNUNET_SYSERR;
1084 if (bytes_written == 0 && n > 0)
1086 LOG (GNUNET_ERROR_TYPE_DEBUG, "Waiting for pipe to clean\n");
1087 WaitForSingleObject (h->h, INFINITE);
1088 if (!WriteFile (h->h, buffer, n, &bytes_written, NULL))
1090 SetErrnoFromWinError (GetLastError ());
1091 LOG (GNUNET_ERROR_TYPE_DEBUG, "Error writing to pipe: %u\n",
1093 return GNUNET_SYSERR;
1096 LOG (GNUNET_ERROR_TYPE_DEBUG,
1099 return bytes_written;
1104 /* set to blocking, write, then set back */
1105 flags = fcntl (h->fd, F_GETFL);
1106 if (0 != (flags & O_NONBLOCK))
1107 (void) fcntl (h->fd, F_SETFL, flags - O_NONBLOCK);
1108 ret = write (h->fd, buffer, n);
1109 if (0 == (flags & O_NONBLOCK))
1110 (void) fcntl (h->fd, F_SETFL, flags);
1117 * Write a buffer to a file. If the file is longer than the
1118 * number of bytes that will be written, it will be truncated.
1120 * @param fn file name
1121 * @param buffer the data to write
1122 * @param n number of bytes to write
1123 * @param mode file permissions
1124 * @return number of bytes written on success, #GNUNET_SYSERR on error
1127 GNUNET_DISK_fn_write (const char *fn, const void *buffer, size_t n,
1128 enum GNUNET_DISK_AccessPermissions mode)
1130 struct GNUNET_DISK_FileHandle *fh;
1133 fh = GNUNET_DISK_file_open (fn,
1134 GNUNET_DISK_OPEN_WRITE | GNUNET_DISK_OPEN_TRUNCATE
1135 | GNUNET_DISK_OPEN_CREATE, mode);
1137 return GNUNET_SYSERR;
1138 ret = GNUNET_DISK_file_write (fh, buffer, n);
1139 GNUNET_assert (GNUNET_OK == GNUNET_DISK_file_close (fh));
1145 * Scan a directory for files.
1147 * @param dir_name the name of the directory
1148 * @param callback the method to call for each file,
1149 * can be NULL, in that case, we only count
1150 * @param callback_cls closure for @a callback
1151 * @return the number of files found, #GNUNET_SYSERR on error or
1152 * ieration aborted by callback returning #GNUNET_SYSERR
1155 GNUNET_DISK_directory_scan (const char *dir_name,
1156 GNUNET_FileNameCallback callback,
1160 struct dirent *finfo;
1166 unsigned int name_len;
1167 unsigned int n_size;
1169 GNUNET_assert (dir_name != NULL);
1170 dname = GNUNET_STRINGS_filename_expand (dir_name);
1172 return GNUNET_SYSERR;
1173 while ((strlen (dname) > 0) && (dname[strlen (dname) - 1] == DIR_SEPARATOR))
1174 dname[strlen (dname) - 1] = '\0';
1175 if (0 != STAT (dname, &istat))
1177 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "stat", dname);
1178 GNUNET_free (dname);
1179 return GNUNET_SYSERR;
1181 if (!S_ISDIR (istat.st_mode))
1183 LOG (GNUNET_ERROR_TYPE_WARNING,
1184 _("Expected `%s' to be a directory!\n"),
1186 GNUNET_free (dname);
1187 return GNUNET_SYSERR;
1190 dinfo = OPENDIR (dname);
1191 if ((errno == EACCES) || (dinfo == NULL))
1193 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "opendir", dname);
1196 GNUNET_free (dname);
1197 return GNUNET_SYSERR;
1200 n_size = strlen (dname) + name_len + 2;
1201 name = GNUNET_malloc (n_size);
1202 while ((finfo = READDIR (dinfo)) != NULL)
1204 if ((0 == strcmp (finfo->d_name, ".")) ||
1205 (0 == strcmp (finfo->d_name, "..")))
1207 if (callback != NULL)
1209 if (name_len < strlen (finfo->d_name))
1212 name_len = strlen (finfo->d_name);
1213 n_size = strlen (dname) + name_len + 2;
1214 name = GNUNET_malloc (n_size);
1216 /* dname can end in "/" only if dname == "/";
1217 * if dname does not end in "/", we need to add
1218 * a "/" (otherwise, we must not!) */
1219 GNUNET_snprintf (name, n_size, "%s%s%s", dname,
1220 (strcmp (dname, DIR_SEPARATOR_STR) ==
1221 0) ? "" : DIR_SEPARATOR_STR, finfo->d_name);
1222 ret = callback (callback_cls, name);
1223 if (GNUNET_OK != ret)
1227 GNUNET_free (dname);
1228 if (GNUNET_NO == ret)
1230 return GNUNET_SYSERR;
1237 GNUNET_free (dname);
1243 * Opaque handle used for iterating over a directory.
1245 struct GNUNET_DISK_DirectoryIterator
1249 * Function to call on directory entries.
1251 GNUNET_DISK_DirectoryIteratorCallback callback;
1254 * Closure for callback.
1259 * Reference to directory.
1269 * Next filename to process.
1276 enum GNUNET_SCHEDULER_Priority priority;
1282 * Task used by the directory iterator.
1285 directory_iterator_task (void *cls,
1286 const struct GNUNET_SCHEDULER_TaskContext *tc)
1288 struct GNUNET_DISK_DirectoryIterator *iter = cls;
1291 name = iter->next_name;
1292 GNUNET_assert (name != NULL);
1293 iter->next_name = NULL;
1294 iter->callback (iter->callback_cls, iter, name, iter->dirname);
1300 * This function must be called during the DiskIteratorCallback
1301 * (exactly once) to schedule the task to process the next
1302 * filename in the directory (if there is one).
1304 * @param iter opaque handle for the iterator
1305 * @param can set to GNUNET_YES to terminate the iteration early
1306 * @return GNUNET_YES if iteration will continue,
1307 * GNUNET_NO if this was the last entry (and iteration is complete),
1308 * GNUNET_SYSERR if abort was YES
1311 GNUNET_DISK_directory_iterator_next (struct GNUNET_DISK_DirectoryIterator *iter,
1314 struct dirent *finfo;
1316 GNUNET_assert (iter->next_name == NULL);
1317 if (can == GNUNET_YES)
1319 CLOSEDIR (iter->directory);
1320 GNUNET_free (iter->dirname);
1322 return GNUNET_SYSERR;
1324 while (NULL != (finfo = READDIR (iter->directory)))
1326 if ((0 == strcmp (finfo->d_name, ".")) ||
1327 (0 == strcmp (finfo->d_name, "..")))
1329 GNUNET_asprintf (&iter->next_name, "%s%s%s", iter->dirname,
1330 DIR_SEPARATOR_STR, finfo->d_name);
1335 GNUNET_DISK_directory_iterator_next (iter, GNUNET_YES);
1338 GNUNET_SCHEDULER_add_with_priority (iter->priority, &directory_iterator_task,
1345 * Scan a directory for files using the scheduler to run a task for
1346 * each entry. The name of the directory must be expanded first (!).
1347 * If a scheduler does not need to be used, GNUNET_DISK_directory_scan
1348 * may provide a simpler API.
1350 * @param prio priority to use
1351 * @param dir_name the name of the directory
1352 * @param callback the method to call for each file
1353 * @param callback_cls closure for callback
1354 * @return GNUNET_YES if directory is not empty and 'callback'
1355 * will be called later, GNUNET_NO otherwise, GNUNET_SYSERR on error.
1358 GNUNET_DISK_directory_iterator_start (enum GNUNET_SCHEDULER_Priority prio,
1359 const char *dir_name,
1360 GNUNET_DISK_DirectoryIteratorCallback
1361 callback, void *callback_cls)
1363 struct GNUNET_DISK_DirectoryIterator *di;
1365 di = GNUNET_new (struct GNUNET_DISK_DirectoryIterator);
1366 di->callback = callback;
1367 di->callback_cls = callback_cls;
1368 di->directory = OPENDIR (dir_name);
1369 if (di->directory == NULL)
1372 callback (callback_cls, NULL, NULL, NULL);
1373 return GNUNET_SYSERR;
1375 di->dirname = GNUNET_strdup (dir_name);
1376 di->priority = prio;
1377 return GNUNET_DISK_directory_iterator_next (di, GNUNET_NO);
1382 * Function that removes the given directory by calling
1383 * "GNUNET_DISK_directory_remove".
1385 * @param unused not used
1386 * @param fn directory to remove
1387 * @return #GNUNET_OK
1390 remove_helper (void *unused, const char *fn)
1392 (void) GNUNET_DISK_directory_remove (fn);
1398 * Remove all files in a directory (rm -rf). Call with
1401 * @param filename the file to remove
1402 * @return #GNUNET_OK on success, #GNUNET_SYSERR on error
1405 GNUNET_DISK_directory_remove (const char *filename)
1409 if (NULL == filename)
1412 return GNUNET_SYSERR;
1414 if (0 != LSTAT (filename, &istat))
1415 return GNUNET_NO; /* file may not exist... */
1416 (void) CHMOD (filename, S_IWUSR | S_IRUSR | S_IXUSR);
1417 if (UNLINK (filename) == 0)
1419 if ((errno != EISDIR) &&
1420 /* EISDIR is not sufficient in all cases, e.g.
1421 * sticky /tmp directory may result in EPERM on BSD.
1422 * So we also explicitly check "isDirectory" */
1423 (GNUNET_YES != GNUNET_DISK_directory_test (filename, GNUNET_YES)))
1425 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "rmdir", filename);
1426 return GNUNET_SYSERR;
1428 if (GNUNET_SYSERR ==
1429 GNUNET_DISK_directory_scan (filename, &remove_helper, NULL))
1430 return GNUNET_SYSERR;
1431 if (0 != RMDIR (filename))
1433 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "rmdir", filename);
1434 return GNUNET_SYSERR;
1443 * @param src file to copy
1444 * @param dst destination file name
1445 * @return #GNUNET_OK on success, #GNUNET_SYSERR on error
1448 GNUNET_DISK_file_copy (const char *src,
1455 struct GNUNET_DISK_FileHandle *in;
1456 struct GNUNET_DISK_FileHandle *out;
1458 if (GNUNET_OK != GNUNET_DISK_file_size (src, &size, GNUNET_YES, GNUNET_YES))
1459 return GNUNET_SYSERR;
1461 in = GNUNET_DISK_file_open (src, GNUNET_DISK_OPEN_READ,
1462 GNUNET_DISK_PERM_NONE);
1464 return GNUNET_SYSERR;
1466 GNUNET_DISK_file_open (dst,
1467 GNUNET_DISK_OPEN_WRITE | GNUNET_DISK_OPEN_CREATE |
1468 GNUNET_DISK_OPEN_FAILIFEXISTS,
1469 GNUNET_DISK_PERM_USER_READ |
1470 GNUNET_DISK_PERM_USER_WRITE |
1471 GNUNET_DISK_PERM_GROUP_READ |
1472 GNUNET_DISK_PERM_GROUP_WRITE);
1475 GNUNET_DISK_file_close (in);
1476 return GNUNET_SYSERR;
1478 buf = GNUNET_malloc (COPY_BLK_SIZE);
1481 len = COPY_BLK_SIZE;
1482 if (len > size - pos)
1484 if (len != GNUNET_DISK_file_read (in, buf, len))
1486 if (len != GNUNET_DISK_file_write (out, buf, len))
1491 GNUNET_DISK_file_close (in);
1492 GNUNET_DISK_file_close (out);
1496 GNUNET_DISK_file_close (in);
1497 GNUNET_DISK_file_close (out);
1498 return GNUNET_SYSERR;
1503 * @brief Removes special characters as ':' from a filename.
1504 * @param fn the filename to canonicalize
1507 GNUNET_DISK_filename_canonicalize (char *fn)
1517 if (c == '/' || c == '\\' || c == ':' || c == '*' || c == '?' || c == '"' ||
1518 c == '<' || c == '>' || c == '|')
1530 * @brief Change owner of a file
1532 * @param filename name of file to change the owner of
1533 * @param user name of the new owner
1534 * @return GNUNET_OK on success, GNUNET_SYSERR on failure
1537 GNUNET_DISK_file_change_owner (const char *filename, const char *user)
1542 pws = getpwnam (user);
1545 LOG (GNUNET_ERROR_TYPE_ERROR,
1546 _("Cannot obtain information about user `%s': %s\n"), user,
1548 return GNUNET_SYSERR;
1550 if (0 != chown (filename, pws->pw_uid, pws->pw_gid))
1551 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "chown", filename);
1558 * Lock a part of a file
1559 * @param fh file handle
1560 * @param lock_start absolute position from where to lock
1561 * @param lock_end absolute position until where to lock
1562 * @param excl GNUNET_YES for an exclusive lock
1563 * @return GNUNET_OK on success, GNUNET_SYSERR on error
1566 GNUNET_DISK_file_lock (struct GNUNET_DISK_FileHandle *fh, off_t lock_start,
1567 off_t lock_end, int excl)
1572 return GNUNET_SYSERR;
1578 memset (&fl, 0, sizeof (struct flock));
1579 fl.l_type = excl ? F_WRLCK : F_RDLCK;
1580 fl.l_whence = SEEK_SET;
1581 fl.l_start = lock_start;
1582 fl.l_len = lock_end;
1584 return fcntl (fh->fd, F_SETLK, &fl) != 0 ? GNUNET_SYSERR : GNUNET_OK;
1587 off_t diff = lock_end - lock_start;
1588 DWORD diff_low, diff_high;
1589 diff_low = (DWORD) (diff & 0xFFFFFFFF);
1590 diff_high = (DWORD) ((diff >> (sizeof (DWORD) * 8)) & 0xFFFFFFFF);
1592 memset (&o, 0, sizeof (OVERLAPPED));
1593 o.Offset = (DWORD) (lock_start & 0xFFFFFFFF);;
1594 o.OffsetHigh = (DWORD) (((lock_start & ~0xFFFFFFFF) >> (sizeof (DWORD) * 8)) & 0xFFFFFFFF);
1597 (fh->h, (excl ? LOCKFILE_EXCLUSIVE_LOCK : 0) | LOCKFILE_FAIL_IMMEDIATELY,
1598 0, diff_low, diff_high, &o))
1600 SetErrnoFromWinError (GetLastError ());
1601 return GNUNET_SYSERR;
1610 * Unlock a part of a file
1611 * @param fh file handle
1612 * @param unlock_start absolute position from where to unlock
1613 * @param unlock_end absolute position until where to unlock
1614 * @return #GNUNET_OK on success, #GNUNET_SYSERR on error
1617 GNUNET_DISK_file_unlock (struct GNUNET_DISK_FileHandle *fh, off_t unlock_start,
1623 return GNUNET_SYSERR;
1629 memset (&fl, 0, sizeof (struct flock));
1630 fl.l_type = F_UNLCK;
1631 fl.l_whence = SEEK_SET;
1632 fl.l_start = unlock_start;
1633 fl.l_len = unlock_end;
1635 return fcntl (fh->fd, F_SETLK, &fl) != 0 ? GNUNET_SYSERR : GNUNET_OK;
1638 off_t diff = unlock_end - unlock_start;
1639 DWORD diff_low, diff_high;
1640 diff_low = (DWORD) (diff & 0xFFFFFFFF);
1641 diff_high = (DWORD) ((diff >> (sizeof (DWORD) * 8)) & 0xFFFFFFFF);
1643 memset (&o, 0, sizeof (OVERLAPPED));
1644 o.Offset = (DWORD) (unlock_start & 0xFFFFFFFF);;
1645 o.OffsetHigh = (DWORD) (((unlock_start & ~0xFFFFFFFF) >> (sizeof (DWORD) * 8)) & 0xFFFFFFFF);
1647 if (!UnlockFileEx (fh->h, 0, diff_low, diff_high, &o))
1649 SetErrnoFromWinError (GetLastError ());
1650 return GNUNET_SYSERR;
1659 * Open a file. Note that the access permissions will only be
1660 * used if a new file is created and if the underlying operating
1661 * system supports the given permissions.
1663 * @param fn file name to be opened
1664 * @param flags opening flags, a combination of GNUNET_DISK_OPEN_xxx bit flags
1665 * @param perm permissions for the newly created file, use
1666 * #GNUNET_DISK_PERM_NONE if a file could not be created by this
1667 * call (because of flags)
1668 * @return IO handle on success, NULL on error
1670 struct GNUNET_DISK_FileHandle *
1671 GNUNET_DISK_file_open (const char *fn,
1672 enum GNUNET_DISK_OpenFlags flags,
1673 enum GNUNET_DISK_AccessPermissions perm)
1676 struct GNUNET_DISK_FileHandle *ret;
1682 wchar_t wexpfn[MAX_PATH + 1];
1689 expfn = GNUNET_STRINGS_filename_expand (fn);
1694 if (GNUNET_DISK_OPEN_READWRITE == (flags & GNUNET_DISK_OPEN_READWRITE))
1695 oflags = O_RDWR; /* note: O_RDWR is NOT always O_RDONLY | O_WRONLY */
1696 else if (flags & GNUNET_DISK_OPEN_READ)
1698 else if (flags & GNUNET_DISK_OPEN_WRITE)
1703 GNUNET_free (expfn);
1706 if (flags & GNUNET_DISK_OPEN_FAILIFEXISTS)
1707 oflags |= (O_CREAT | O_EXCL);
1708 if (flags & GNUNET_DISK_OPEN_TRUNCATE)
1710 if (flags & GNUNET_DISK_OPEN_APPEND)
1712 if (flags & GNUNET_DISK_OPEN_CREATE)
1714 (void) GNUNET_DISK_directory_create_for_file (expfn);
1716 mode = translate_unix_perms (perm);
1719 fd = open (expfn, oflags
1723 | O_LARGEFILE, mode);
1726 if (0 == (flags & GNUNET_DISK_OPEN_FAILIFEXISTS))
1727 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "open", expfn);
1729 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_DEBUG, "open", expfn);
1730 GNUNET_free (expfn);
1737 if (GNUNET_DISK_OPEN_READWRITE == (flags & GNUNET_DISK_OPEN_READWRITE))
1738 access = FILE_READ_DATA | FILE_WRITE_DATA;
1739 else if (flags & GNUNET_DISK_OPEN_READ)
1740 access = FILE_READ_DATA;
1741 else if (flags & GNUNET_DISK_OPEN_WRITE)
1742 access = FILE_WRITE_DATA;
1744 if (flags & GNUNET_DISK_OPEN_FAILIFEXISTS)
1748 else if (flags & GNUNET_DISK_OPEN_CREATE)
1750 (void) GNUNET_DISK_directory_create_for_file (expfn);
1751 if (flags & GNUNET_DISK_OPEN_TRUNCATE)
1752 disp = CREATE_ALWAYS;
1756 else if (flags & GNUNET_DISK_OPEN_TRUNCATE)
1758 disp = TRUNCATE_EXISTING;
1762 disp = OPEN_EXISTING;
1765 if (ERROR_SUCCESS == plibc_conv_to_win_pathwconv(expfn, wexpfn))
1766 h = CreateFileW (wexpfn, access,
1767 FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE, NULL,
1768 disp, FILE_ATTRIBUTE_NORMAL, NULL);
1770 h = INVALID_HANDLE_VALUE;
1771 if (h == INVALID_HANDLE_VALUE)
1774 SetErrnoFromWinError (GetLastError ());
1776 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_INFO, "open", expfn);
1777 GNUNET_free (expfn);
1782 if (flags & GNUNET_DISK_OPEN_APPEND)
1783 if (SetFilePointer (h, 0, 0, FILE_END) == INVALID_SET_FILE_POINTER)
1785 SetErrnoFromWinError (GetLastError ());
1786 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "SetFilePointer", expfn);
1788 GNUNET_free (expfn);
1793 ret = GNUNET_new (struct GNUNET_DISK_FileHandle);
1796 ret->type = GNUNET_DISK_HANLDE_TYPE_FILE;
1800 GNUNET_free (expfn);
1806 * Close an open file
1807 * @param h file handle
1808 * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
1811 GNUNET_DISK_file_close (struct GNUNET_DISK_FileHandle *h)
1817 return GNUNET_SYSERR;
1823 if (!CloseHandle (h->h))
1825 SetErrnoFromWinError (GetLastError ());
1826 LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING, "close");
1827 ret = GNUNET_SYSERR;
1829 if (h->oOverlapRead)
1831 if (!CloseHandle (h->oOverlapRead->hEvent))
1833 SetErrnoFromWinError (GetLastError ());
1834 LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING, "close");
1835 ret = GNUNET_SYSERR;
1837 GNUNET_free (h->oOverlapRead);
1839 if (h->oOverlapWrite)
1841 if (!CloseHandle (h->oOverlapWrite->hEvent))
1843 SetErrnoFromWinError (GetLastError ());
1844 LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING, "close");
1845 ret = GNUNET_SYSERR;
1847 GNUNET_free (h->oOverlapWrite);
1850 if (close (h->fd) != 0)
1852 LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING, "close");
1853 ret = GNUNET_SYSERR;
1862 * Get a GNUnet file handle from a W32 handle.
1864 * @param handle native handle
1865 * @return GNUnet file handle corresponding to the W32 handle
1867 struct GNUNET_DISK_FileHandle *
1868 GNUNET_DISK_get_handle_from_w32_handle (HANDLE osfh)
1870 struct GNUNET_DISK_FileHandle *fh;
1873 enum GNUNET_FILE_Type ftype;
1875 dwret = GetFileType (osfh);
1878 case FILE_TYPE_DISK:
1879 ftype = GNUNET_DISK_HANLDE_TYPE_FILE;
1881 case FILE_TYPE_PIPE:
1882 ftype = GNUNET_DISK_HANLDE_TYPE_PIPE;
1888 fh = GNUNET_new (struct GNUNET_DISK_FileHandle);
1892 if (ftype == GNUNET_DISK_HANLDE_TYPE_PIPE)
1895 * Note that we can't make it overlapped if it isn't already.
1896 * (ReOpenFile() is only available in 2003/Vista).
1897 * The process that opened this file in the first place (usually a parent
1898 * process, if this is stdin/stdout/stderr) must make it overlapped,
1899 * otherwise we're screwed, as selecting on non-overlapped handle
1902 fh->oOverlapRead = GNUNET_new (OVERLAPPED);
1903 fh->oOverlapWrite = GNUNET_new (OVERLAPPED);
1904 fh->oOverlapRead->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
1905 fh->oOverlapWrite->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
1913 * Get a handle from a native integer FD.
1915 * @param fno native integer file descriptor
1916 * @return file handle corresponding to the descriptor, NULL on error
1918 struct GNUNET_DISK_FileHandle *
1919 GNUNET_DISK_get_handle_from_int_fd (int fno)
1921 struct GNUNET_DISK_FileHandle *fh;
1923 if ( (((off_t) -1) == lseek (fno, 0, SEEK_CUR)) &&
1925 return NULL; /* invalid FD */
1928 fh = GNUNET_new (struct GNUNET_DISK_FileHandle);
1934 osfh = _get_osfhandle (fno);
1935 if (INVALID_HANDLE_VALUE == (HANDLE) osfh)
1938 fh = GNUNET_DISK_get_handle_from_w32_handle ((HANDLE) osfh);
1946 * Get a handle from a native streaming FD.
1948 * @param fd native streaming file descriptor
1949 * @return file handle corresponding to the descriptor
1951 struct GNUNET_DISK_FileHandle *
1952 GNUNET_DISK_get_handle_from_native (FILE *fd)
1960 return GNUNET_DISK_get_handle_from_int_fd (fno);
1965 * Handle for a memory-mapping operation.
1967 struct GNUNET_DISK_MapHandle
1970 * Address where the map is in memory.
1976 * Underlying OS handle.
1981 * Number of bytes mapped.
1989 #define MAP_FAILED ((void *) -1)
1993 * Map a file into memory
1995 * @param h open file handle
1996 * @param m handle to the new mapping
1997 * @param access access specification, GNUNET_DISK_MAP_TYPE_xxx
1998 * @param len size of the mapping
1999 * @return pointer to the mapped memory region, NULL on failure
2002 GNUNET_DISK_file_map (const struct GNUNET_DISK_FileHandle *h,
2003 struct GNUNET_DISK_MapHandle **m,
2004 enum GNUNET_DISK_MapType access, size_t len)
2013 DWORD mapAccess, protect;
2015 if ((access & GNUNET_DISK_MAP_TYPE_READ) &&
2016 (access & GNUNET_DISK_MAP_TYPE_WRITE))
2018 protect = PAGE_READWRITE;
2019 mapAccess = FILE_MAP_ALL_ACCESS;
2021 else if (access & GNUNET_DISK_MAP_TYPE_READ)
2023 protect = PAGE_READONLY;
2024 mapAccess = FILE_MAP_READ;
2026 else if (access & GNUNET_DISK_MAP_TYPE_WRITE)
2028 protect = PAGE_READWRITE;
2029 mapAccess = FILE_MAP_WRITE;
2037 *m = GNUNET_new (struct GNUNET_DISK_MapHandle);
2038 (*m)->h = CreateFileMapping (h->h, NULL, protect, 0, 0, NULL);
2039 if ((*m)->h == INVALID_HANDLE_VALUE)
2041 SetErrnoFromWinError (GetLastError ());
2046 (*m)->addr = MapViewOfFile ((*m)->h, mapAccess, 0, 0, len);
2049 SetErrnoFromWinError (GetLastError ());
2050 CloseHandle ((*m)->h);
2059 if (access & GNUNET_DISK_MAP_TYPE_READ)
2061 if (access & GNUNET_DISK_MAP_TYPE_WRITE)
2063 *m = GNUNET_new (struct GNUNET_DISK_MapHandle);
2064 (*m)->addr = mmap (NULL, len, prot, MAP_SHARED, h->fd, 0);
2065 GNUNET_assert (NULL != (*m)->addr);
2066 if (MAP_FAILED == (*m)->addr)
2078 * @param h mapping handle
2079 * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
2082 GNUNET_DISK_file_unmap (struct GNUNET_DISK_MapHandle *h)
2089 return GNUNET_SYSERR;
2093 ret = UnmapViewOfFile (h->addr) ? GNUNET_OK : GNUNET_SYSERR;
2094 if (ret != GNUNET_OK)
2095 SetErrnoFromWinError (GetLastError ());
2096 if (!CloseHandle (h->h) && (ret == GNUNET_OK))
2098 ret = GNUNET_SYSERR;
2099 SetErrnoFromWinError (GetLastError ());
2102 ret = munmap (h->addr, h->len) != -1 ? GNUNET_OK : GNUNET_SYSERR;
2110 * Write file changes to disk
2111 * @param h handle to an open file
2112 * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
2115 GNUNET_DISK_file_sync (const struct GNUNET_DISK_FileHandle *h)
2120 return GNUNET_SYSERR;
2126 ret = FlushFileBuffers (h->h) ? GNUNET_OK : GNUNET_SYSERR;
2127 if (ret != GNUNET_OK)
2128 SetErrnoFromWinError (GetLastError ());
2130 #elif defined(FREEBSD) || defined(OPENBSD) || defined(DARWIN)
2131 return fsync (h->fd) == -1 ? GNUNET_SYSERR : GNUNET_OK;
2133 return fdatasync (h->fd) == -1 ? GNUNET_SYSERR : GNUNET_OK;
2140 #define PIPE_BUF 512
2142 /* Copyright Bob Byrnes <byrnes <at> curl.com>
2143 http://permalink.gmane.org/gmane.os.cygwin.patches/2121
2145 /* Create a pipe, and return handles to the read and write ends,
2146 just like CreatePipe, but ensure that the write end permits
2147 FILE_READ_ATTRIBUTES access, on later versions of win32 where
2148 this is supported. This access is needed by NtQueryInformationFile,
2149 which is used to implement select and nonblocking writes.
2150 Note that the return value is either NO_ERROR or GetLastError,
2151 unlike CreatePipe, which returns a bool for success or failure. */
2153 create_selectable_pipe (PHANDLE read_pipe_ptr, PHANDLE write_pipe_ptr,
2154 LPSECURITY_ATTRIBUTES sa_ptr, DWORD psize,
2155 DWORD dwReadMode, DWORD dwWriteMode)
2157 /* Default to error. */
2158 *read_pipe_ptr = *write_pipe_ptr = INVALID_HANDLE_VALUE;
2163 /* Ensure that there is enough pipe buffer space for atomic writes. */
2164 if (psize < PIPE_BUF)
2167 char pipename[MAX_PATH];
2169 /* Retry CreateNamedPipe as long as the pipe name is in use.
2170 * Retrying will probably never be necessary, but we want
2171 * to be as robust as possible. */
2174 static volatile LONG pipe_unique_id;
2176 snprintf (pipename, sizeof pipename, "\\\\.\\pipe\\gnunet-%d-%ld",
2177 getpid (), InterlockedIncrement ((LONG *) & pipe_unique_id));
2178 LOG (GNUNET_ERROR_TYPE_DEBUG, "CreateNamedPipe: name = %s, size = %lu\n",
2180 /* Use CreateNamedPipe instead of CreatePipe, because the latter
2181 * returns a write handle that does not permit FILE_READ_ATTRIBUTES
2182 * access, on versions of win32 earlier than WinXP SP2.
2183 * CreatePipe also stupidly creates a full duplex pipe, which is
2184 * a waste, since only a single direction is actually used.
2185 * It's important to only allow a single instance, to ensure that
2186 * the pipe was not created earlier by some other process, even if
2187 * the pid has been reused. */
2188 read_pipe = CreateNamedPipeA (pipename, PIPE_ACCESS_INBOUND | FILE_FLAG_FIRST_PIPE_INSTANCE | dwReadMode, PIPE_TYPE_BYTE | PIPE_READMODE_BYTE, 1, /* max instances */
2189 psize, /* output buffer size */
2190 psize, /* input buffer size */
2191 NMPWAIT_USE_DEFAULT_WAIT, sa_ptr);
2193 if (read_pipe != INVALID_HANDLE_VALUE)
2195 LOG (GNUNET_ERROR_TYPE_DEBUG, "pipe read handle = %p\n", read_pipe);
2199 DWORD err = GetLastError ();
2203 case ERROR_PIPE_BUSY:
2204 /* The pipe is already open with compatible parameters.
2205 * Pick a new name and retry. */
2206 LOG (GNUNET_ERROR_TYPE_DEBUG, "pipe busy, retrying\n");
2208 case ERROR_ACCESS_DENIED:
2209 /* The pipe is already open with incompatible parameters.
2210 * Pick a new name and retry. */
2211 LOG (GNUNET_ERROR_TYPE_DEBUG, "pipe access denied, retrying\n");
2213 case ERROR_CALL_NOT_IMPLEMENTED:
2214 /* We are on an older Win9x platform without named pipes.
2215 * Return an anonymous pipe as the best approximation. */
2216 LOG (GNUNET_ERROR_TYPE_DEBUG,
2217 "CreateNamedPipe not implemented, resorting to "
2218 "CreatePipe: size = %lu\n", psize);
2219 if (CreatePipe (read_pipe_ptr, write_pipe_ptr, sa_ptr, psize))
2221 LOG (GNUNET_ERROR_TYPE_DEBUG, "pipe read handle = %p, write handle = %p\n",
2226 err = GetLastError ();
2227 LOG (GNUNET_ERROR_TYPE_ERROR, "CreatePipe failed: %d\n", err);
2230 LOG (GNUNET_ERROR_TYPE_ERROR, "CreateNamedPipe failed: %d\n", err);
2235 LOG (GNUNET_ERROR_TYPE_DEBUG, "CreateFile: name = %s\n", pipename);
2237 /* Open the named pipe for writing.
2238 * Be sure to permit FILE_READ_ATTRIBUTES access. */
2239 write_pipe = CreateFileA (pipename, GENERIC_WRITE | FILE_READ_ATTRIBUTES, 0, /* share mode */
2240 sa_ptr, OPEN_EXISTING, dwWriteMode, /* flags and attributes */
2241 0); /* handle to template file */
2243 if (write_pipe == INVALID_HANDLE_VALUE)
2246 DWORD err = GetLastError ();
2248 LOG (GNUNET_ERROR_TYPE_DEBUG, "CreateFile failed: %d\n", err);
2249 CloseHandle (read_pipe);
2252 LOG (GNUNET_ERROR_TYPE_DEBUG, "pipe write handle = %p\n", write_pipe);
2254 *read_pipe_ptr = read_pipe;
2255 *write_pipe_ptr = write_pipe;
2262 * Creates an interprocess channel
2264 * @param blocking_read creates an asynchronous pipe for reading if set to GNUNET_NO
2265 * @param blocking_write creates an asynchronous pipe for writing if set to GNUNET_NO
2266 * @param inherit_read inherit the parent processes stdin (only for windows)
2267 * @param inherit_write inherit the parent processes stdout (only for windows)
2268 * @return handle to the new pipe, NULL on error
2270 struct GNUNET_DISK_PipeHandle *
2271 GNUNET_DISK_pipe (int blocking_read, int blocking_write, int inherit_read, int inherit_write)
2282 LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR, "pipe");
2286 return GNUNET_DISK_pipe_from_fd (blocking_read,
2290 struct GNUNET_DISK_PipeHandle *p;
2295 p = GNUNET_new (struct GNUNET_DISK_PipeHandle);
2296 p->fd[0] = GNUNET_new (struct GNUNET_DISK_FileHandle);
2297 p->fd[1] = GNUNET_new (struct GNUNET_DISK_FileHandle);
2299 /* All pipes are overlapped. If you want them to block - just
2300 * call WriteFile() and ReadFile() with NULL overlapped pointer.
2301 * NOTE: calling with NULL overlapped pointer works only
2302 * for pipes, and doesn't seem to be a documented feature.
2303 * It will NOT work for files, because overlapped files need
2304 * to read offsets from the overlapped structure, regardless.
2305 * Pipes are not seekable, and need no offsets, which is
2306 * probably why it works for them.
2309 create_selectable_pipe (&p->fd[0]->h, &p->fd[1]->h, NULL, 0,
2310 FILE_FLAG_OVERLAPPED,
2311 FILE_FLAG_OVERLAPPED);
2314 SetErrnoFromWinError (GetLastError ());
2316 GNUNET_free (p->fd[0]);
2317 GNUNET_free (p->fd[1]);
2322 if (!DuplicateHandle
2323 (GetCurrentProcess (), p->fd[0]->h, GetCurrentProcess (), &tmp_handle, 0,
2324 inherit_read == GNUNET_YES ? TRUE : FALSE, DUPLICATE_SAME_ACCESS))
2326 SetErrnoFromWinError (GetLastError ());
2328 CloseHandle (p->fd[0]->h);
2329 CloseHandle (p->fd[1]->h);
2330 GNUNET_free (p->fd[0]);
2331 GNUNET_free (p->fd[1]);
2336 CloseHandle (p->fd[0]->h);
2337 p->fd[0]->h = tmp_handle;
2339 if (!DuplicateHandle
2340 (GetCurrentProcess (), p->fd[1]->h, GetCurrentProcess (), &tmp_handle, 0,
2341 inherit_write == GNUNET_YES ? TRUE : FALSE, DUPLICATE_SAME_ACCESS))
2343 SetErrnoFromWinError (GetLastError ());
2345 CloseHandle (p->fd[0]->h);
2346 CloseHandle (p->fd[1]->h);
2347 GNUNET_free (p->fd[0]);
2348 GNUNET_free (p->fd[1]);
2353 CloseHandle (p->fd[1]->h);
2354 p->fd[1]->h = tmp_handle;
2356 p->fd[0]->type = GNUNET_DISK_HANLDE_TYPE_PIPE;
2357 p->fd[1]->type = GNUNET_DISK_HANLDE_TYPE_PIPE;
2359 p->fd[0]->oOverlapRead = GNUNET_new (OVERLAPPED);
2360 p->fd[0]->oOverlapWrite = GNUNET_new (OVERLAPPED);
2361 p->fd[1]->oOverlapRead = GNUNET_new (OVERLAPPED);
2362 p->fd[1]->oOverlapWrite = GNUNET_new (OVERLAPPED);
2364 p->fd[0]->oOverlapRead->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2365 p->fd[0]->oOverlapWrite->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2367 p->fd[1]->oOverlapRead->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2368 p->fd[1]->oOverlapWrite->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2376 * Creates a pipe object from a couple of file descriptors.
2377 * Useful for wrapping existing pipe FDs.
2379 * @param blocking_read creates an asynchronous pipe for reading if set to GNUNET_NO
2380 * @param blocking_write creates an asynchronous pipe for writing if set to GNUNET_NO
2381 * @param fd an array of two fd values. One of them may be -1 for read-only or write-only pipes
2383 * @return handle to the new pipe, NULL on error
2385 struct GNUNET_DISK_PipeHandle *
2386 GNUNET_DISK_pipe_from_fd (int blocking_read, int blocking_write, int fd[2])
2388 struct GNUNET_DISK_PipeHandle *p;
2390 p = GNUNET_new (struct GNUNET_DISK_PipeHandle);
2395 int eno = 0; /* make gcc happy */
2400 p->fd[0] = GNUNET_new (struct GNUNET_DISK_FileHandle);
2401 p->fd[0]->fd = fd[0];
2404 flags = fcntl (fd[0], F_GETFL);
2405 flags |= O_NONBLOCK;
2406 if (0 > fcntl (fd[0], F_SETFL, flags))
2412 flags = fcntl (fd[0], F_GETFD);
2413 flags |= FD_CLOEXEC;
2414 if (0 > fcntl (fd[0], F_SETFD, flags))
2423 p->fd[1] = GNUNET_new (struct GNUNET_DISK_FileHandle);
2424 p->fd[1]->fd = fd[1];
2425 if (!blocking_write)
2427 flags = fcntl (fd[1], F_GETFL);
2428 flags |= O_NONBLOCK;
2429 if (0 > fcntl (fd[1], F_SETFL, flags))
2435 flags = fcntl (fd[1], F_GETFD);
2436 flags |= FD_CLOEXEC;
2437 if (0 > fcntl (fd[1], F_SETFD, flags))
2446 LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR, "fcntl");
2447 if (p->fd[0]->fd >= 0)
2448 GNUNET_break (0 == close (p->fd[0]->fd));
2449 if (p->fd[1]->fd >= 0)
2450 GNUNET_break (0 == close (p->fd[1]->fd));
2451 GNUNET_free_non_null (p->fd[0]);
2452 GNUNET_free_non_null (p->fd[1]);
2460 p->fd[0] = GNUNET_new (struct GNUNET_DISK_FileHandle);
2461 p->fd[0]->h = (HANDLE) _get_osfhandle (fd[0]);
2462 if (p->fd[0]->h != INVALID_HANDLE_VALUE)
2464 p->fd[0]->type = GNUNET_DISK_HANLDE_TYPE_PIPE;
2465 p->fd[0]->oOverlapRead = GNUNET_new (OVERLAPPED);
2466 p->fd[0]->oOverlapWrite = GNUNET_new (OVERLAPPED);
2467 p->fd[0]->oOverlapRead->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2468 p->fd[0]->oOverlapWrite->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2472 GNUNET_free (p->fd[0]);
2478 p->fd[1] = GNUNET_new (struct GNUNET_DISK_FileHandle);
2479 p->fd[1]->h = (HANDLE) _get_osfhandle (fd[1]);
2480 if (p->fd[1]->h != INVALID_HANDLE_VALUE)
2482 p->fd[1]->type = GNUNET_DISK_HANLDE_TYPE_PIPE;
2483 p->fd[1]->oOverlapRead = GNUNET_new (OVERLAPPED);
2484 p->fd[1]->oOverlapWrite = GNUNET_new (OVERLAPPED);
2485 p->fd[1]->oOverlapRead->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2486 p->fd[1]->oOverlapWrite->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2490 GNUNET_free (p->fd[1]);
2501 * Closes an interprocess channel
2503 * @param p pipe to close
2504 * @param end which end of the pipe to close
2505 * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
2508 GNUNET_DISK_pipe_close_end (struct GNUNET_DISK_PipeHandle *p,
2509 enum GNUNET_DISK_PipeEnd end)
2511 int ret = GNUNET_OK;
2513 if (end == GNUNET_DISK_PIPE_END_READ)
2517 ret = GNUNET_DISK_file_close (p->fd[0]);
2521 else if (end == GNUNET_DISK_PIPE_END_WRITE)
2525 ret = GNUNET_DISK_file_close (p->fd[1]);
2534 * Detaches one of the ends from the pipe.
2535 * Detached end is a fully-functional FileHandle, it will
2536 * not be affected by anything you do with the pipe afterwards.
2537 * Each end of a pipe can only be detched from it once (i.e.
2538 * it is not duplicated).
2540 * @param p pipe to detach an end from
2541 * @param end which end of the pipe to detach
2542 * @return Detached end on success, NULL on failure
2543 * (or if that end is not present or is closed).
2545 struct GNUNET_DISK_FileHandle *
2546 GNUNET_DISK_pipe_detach_end (struct GNUNET_DISK_PipeHandle *p,
2547 enum GNUNET_DISK_PipeEnd end)
2549 struct GNUNET_DISK_FileHandle *ret = NULL;
2551 if (end == GNUNET_DISK_PIPE_END_READ)
2559 else if (end == GNUNET_DISK_PIPE_END_WRITE)
2573 * Closes an interprocess channel
2575 * @param p pipe to close
2576 * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
2579 GNUNET_DISK_pipe_close (struct GNUNET_DISK_PipeHandle *p)
2581 int ret = GNUNET_OK;
2584 int write_end_close;
2585 int read_end_close_errno;
2586 int write_end_close_errno;
2588 read_end_close = GNUNET_DISK_pipe_close_end (p, GNUNET_DISK_PIPE_END_READ);
2589 read_end_close_errno = errno;
2590 write_end_close = GNUNET_DISK_pipe_close_end (p, GNUNET_DISK_PIPE_END_WRITE);
2591 write_end_close_errno = errno;
2594 if (GNUNET_OK != read_end_close)
2596 errno = read_end_close_errno;
2597 ret = read_end_close;
2599 else if (GNUNET_OK != write_end_close)
2601 errno = write_end_close_errno;
2602 ret = write_end_close;
2610 * Get the handle to a particular pipe end
2613 * @param n end to access
2614 * @return handle for the respective end
2616 const struct GNUNET_DISK_FileHandle *
2617 GNUNET_DISK_pipe_handle (const struct GNUNET_DISK_PipeHandle *p,
2618 enum GNUNET_DISK_PipeEnd n)
2622 case GNUNET_DISK_PIPE_END_READ:
2623 case GNUNET_DISK_PIPE_END_WRITE:
2633 * Retrieve OS file handle
2635 * @param fh GNUnet file descriptor
2636 * @param dst destination buffer
2637 * @param dst_len length of dst
2638 * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
2641 GNUNET_DISK_internal_file_handle_ (const struct GNUNET_DISK_FileHandle *fh,
2642 void *dst, size_t dst_len)
2645 if (dst_len < sizeof (HANDLE))
2646 return GNUNET_SYSERR;
2647 *((HANDLE *) dst) = fh->h;
2649 if (dst_len < sizeof (int))
2650 return GNUNET_SYSERR;
2651 *((int *) dst) = fh->fd;