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)
469 * Update POSIX permissions mask of a file on disk. If both argumets
470 * are #GNUNET_NO, the file is made world-read-write-executable (777).
471 * Does nothing on W32.
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)
488 * Update POSIX permissions mask of a file on disk. If both argumets
489 * are #GNUNET_NO, the file is made world-read-write-executable (777).
491 * @param fn name of the file to update
492 * @param require_uid_match #GNUNET_YES means 700
493 * @param require_gid_match #GNUNET_YES means 770 unless @a require_uid_match is set
496 GNUNET_DISK_fix_permissions (const char *fn,
497 int require_uid_match,
498 int require_gid_match)
502 if (GNUNET_YES == require_uid_match)
503 mode = S_IRUSR | S_IWUSR | S_IXUSR;
504 else if (GNUNET_YES == require_gid_match)
505 mode = S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IWGRP | S_IXGRP;
507 mode = S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IWGRP | S_IXGRP | S_IROTH | S_IWOTH | S_IXOTH;
508 if (0 != chmod (fn, mode))
509 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING,
517 * Create an (empty) temporary directory on disk. If the given name is not
518 * an absolute path, the current 'TMPDIR' will be prepended. In any case,
519 * 6 random characters will be appended to the name to create a unique
522 * @param t component to use for the name;
523 * does NOT contain "XXXXXX" or "/tmp/".
524 * @return NULL on error, otherwise name of fresh
525 * file on disk in directory for temporary files
528 GNUNET_DISK_mkdtemp (const char *t)
532 fn = mktemp_name (t);
533 if (fn != mkdtemp (fn))
535 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_ERROR, "mkstemp", fn);
544 * Move a file out of the way (create a backup) by
545 * renaming it to "orig.NUM~" where NUM is the smallest
546 * number that is not used yet.
548 * @param fil name of the file to back up
551 GNUNET_DISK_file_backup (const char *fil)
557 slen = strlen (fil) + 20;
558 target = GNUNET_malloc (slen);
562 GNUNET_snprintf (target, slen,
566 } while (0 == access (target, F_OK));
567 if (0 != rename (fil, target))
568 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR,
571 GNUNET_free (target);
576 * Create an (empty) temporary file on disk. If the given name is not
577 * an absolute path, the current 'TMPDIR' will be prepended. In any case,
578 * 6 random characters will be appended to the name to create a unique
581 * @param t component to use for the name;
582 * does NOT contain "XXXXXX" or "/tmp/".
583 * @return NULL on error, otherwise name of fresh
584 * file on disk in directory for temporary files
587 GNUNET_DISK_mktemp (const char *t)
592 fn = mktemp_name (t);
593 if (-1 == (fd = mkstemp (fn)))
595 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_ERROR, "mkstemp", fn);
600 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "close", fn);
606 * Test if @a fil is a directory and listable. Optionally, also check if the
607 * directory is readable. Will not print an error message if the directory does
608 * not exist. Will log errors if #GNUNET_SYSERR is returned (i.e., a file exists
609 * with the same name).
611 * @param fil filename to test
612 * @param is_readable GNUNET_YES to additionally check if @a fil is readable;
613 * #GNUNET_NO to disable this check
614 * @return #GNUNET_YES if yes, #GNUNET_NO if not; #GNUNET_SYSERR if it
615 * does not exist or stat'ed
618 GNUNET_DISK_directory_test (const char *fil, int is_readable)
620 struct stat filestat;
623 ret = STAT (fil, &filestat);
627 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "stat", fil);
628 return GNUNET_SYSERR;
630 if (!S_ISDIR (filestat.st_mode))
632 LOG (GNUNET_ERROR_TYPE_DEBUG,
633 "A file already exits with the same name %s\n", fil);
636 if (GNUNET_YES == is_readable)
637 ret = ACCESS (fil, R_OK | X_OK);
639 ret = ACCESS (fil, X_OK);
642 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "access", fil);
650 * Check that fil corresponds to a filename
651 * (of a file that exists and that is not a directory).
653 * @param fil filename to check
654 * @return #GNUNET_YES if yes, #GNUNET_NO if not a file, #GNUNET_SYSERR if something
655 * else (will print an error message in that case, too).
658 GNUNET_DISK_file_test (const char *fil)
660 struct stat filestat;
664 rdir = GNUNET_STRINGS_filename_expand (fil);
666 return GNUNET_SYSERR;
668 ret = STAT (rdir, &filestat);
673 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "stat", rdir);
675 return GNUNET_SYSERR;
680 if (!S_ISREG (filestat.st_mode))
685 if (ACCESS (rdir, F_OK) < 0)
687 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "access", rdir);
689 return GNUNET_SYSERR;
697 * Implementation of "mkdir -p"
699 * @param dir the directory to create
700 * @return #GNUNET_OK on success, #GNUNET_SYSERR on failure
703 GNUNET_DISK_directory_create (const char *dir)
711 rdir = GNUNET_STRINGS_filename_expand (dir);
713 return GNUNET_SYSERR;
717 pos = 1; /* skip heading '/' */
719 /* Local or Network path? */
720 if (strncmp (rdir, "\\\\", 2) == 0)
725 if (rdir[pos] == '\\')
735 pos = 3; /* strlen("C:\\") */
738 /* Check which low level directories already exist */
740 rdir[len] = DIR_SEPARATOR;
743 if (DIR_SEPARATOR == rdir[pos2])
746 ret = GNUNET_DISK_directory_test (rdir, GNUNET_NO);
747 if (GNUNET_NO == ret)
750 return GNUNET_SYSERR;
752 rdir[pos2] = DIR_SEPARATOR;
753 if (GNUNET_YES == ret)
764 /* Start creating directories */
767 if ((rdir[pos] == DIR_SEPARATOR) || (pos == len))
770 ret = GNUNET_DISK_directory_test (rdir, GNUNET_NO);
771 if (GNUNET_NO == ret)
774 return GNUNET_SYSERR;
776 if (GNUNET_SYSERR == ret)
779 ret = mkdir (rdir, S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH); /* 755 */
781 wchar_t wrdir[MAX_PATH + 1];
782 if (ERROR_SUCCESS == plibc_conv_to_win_pathwconv(rdir, wrdir))
783 ret = !CreateDirectoryW (wrdir, NULL);
787 if ((ret != 0) && (errno != EEXIST))
789 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_ERROR, "mkdir", rdir);
791 return GNUNET_SYSERR;
794 rdir[pos] = DIR_SEPARATOR;
804 * Create the directory structure for storing
807 * @param filename name of a file in the directory
808 * @returns #GNUNET_OK on success,
809 * #GNUNET_SYSERR on failure,
810 * #GNUNET_NO if the directory
811 * exists but is not writeable for us
814 GNUNET_DISK_directory_create_for_file (const char *filename)
820 rdir = GNUNET_STRINGS_filename_expand (filename);
822 return GNUNET_SYSERR;
824 while ((len > 0) && (rdir[len] != DIR_SEPARATOR))
827 ret = GNUNET_DISK_directory_create (rdir);
828 if ((ret == GNUNET_OK) && (0 != ACCESS (rdir, W_OK)))
836 * Read the contents of a binary file into a buffer.
838 * @param h handle to an open file
839 * @param result the buffer to write the result to
840 * @param len the maximum number of bytes to read
841 * @return the number of bytes read on success, #GNUNET_SYSERR on failure
844 GNUNET_DISK_file_read (const struct GNUNET_DISK_FileHandle *h,
851 return GNUNET_SYSERR;
857 if (h->type != GNUNET_DISK_HANLDE_TYPE_PIPE)
859 if (!ReadFile (h->h, result, len, &bytes_read, NULL))
861 SetErrnoFromWinError (GetLastError ());
862 return GNUNET_SYSERR;
867 if (!ReadFile (h->h, result, len, &bytes_read, h->oOverlapRead))
869 if (GetLastError () != ERROR_IO_PENDING)
871 LOG (GNUNET_ERROR_TYPE_DEBUG, "Error reading from pipe: %u\n", GetLastError ());
872 SetErrnoFromWinError (GetLastError ());
873 return GNUNET_SYSERR;
875 LOG (GNUNET_ERROR_TYPE_DEBUG, "Will get overlapped result\n");
876 GetOverlappedResult (h->h, h->oOverlapRead, &bytes_read, TRUE);
878 LOG (GNUNET_ERROR_TYPE_DEBUG, "Read %u bytes from pipe\n", bytes_read);
882 return read (h->fd, result, len);
888 * Read the contents of a binary file into a buffer.
889 * Guarantees not to block (returns GNUNET_SYSERR and sets errno to EAGAIN
890 * when no data can be read).
892 * @param h handle to an open file
893 * @param result the buffer to write the result to
894 * @param len the maximum number of bytes to read
895 * @return the number of bytes read on success, #GNUNET_SYSERR on failure
898 GNUNET_DISK_file_read_non_blocking (const struct GNUNET_DISK_FileHandle *h,
905 return GNUNET_SYSERR;
911 if (h->type != GNUNET_DISK_HANLDE_TYPE_PIPE)
913 if (!ReadFile (h->h, result, len, &bytes_read, NULL))
915 SetErrnoFromWinError (GetLastError ());
916 return GNUNET_SYSERR;
921 if (!ReadFile (h->h, result, len, &bytes_read, h->oOverlapRead))
923 if (GetLastError () != ERROR_IO_PENDING)
925 LOG (GNUNET_ERROR_TYPE_DEBUG, "Error reading from pipe: %u\n", GetLastError ());
926 SetErrnoFromWinError (GetLastError ());
927 return GNUNET_SYSERR;
931 LOG (GNUNET_ERROR_TYPE_DEBUG,
932 "ReadFile() queued a read, cancelling\n");
935 return GNUNET_SYSERR;
938 LOG (GNUNET_ERROR_TYPE_DEBUG, "Read %u bytes\n", bytes_read);
945 /* set to non-blocking, read, then set back */
946 flags = fcntl (h->fd, F_GETFL);
947 if (0 == (flags & O_NONBLOCK))
948 (void) fcntl (h->fd, F_SETFL, flags | O_NONBLOCK);
949 ret = read (h->fd, result, len);
950 if (0 == (flags & O_NONBLOCK))
953 (void) fcntl (h->fd, F_SETFL, flags);
962 * Read the contents of a binary file into a buffer.
964 * @param fn file name
965 * @param result the buffer to write the result to
966 * @param len the maximum number of bytes to read
967 * @return number of bytes read, #GNUNET_SYSERR on failure
970 GNUNET_DISK_fn_read (const char *fn,
974 struct GNUNET_DISK_FileHandle *fh;
977 fh = GNUNET_DISK_file_open (fn, GNUNET_DISK_OPEN_READ, GNUNET_DISK_PERM_NONE);
979 return GNUNET_SYSERR;
980 ret = GNUNET_DISK_file_read (fh, result, len);
981 GNUNET_assert (GNUNET_OK == GNUNET_DISK_file_close (fh));
988 * Write a buffer to a file.
990 * @param h handle to open file
991 * @param buffer the data to write
992 * @param n number of bytes to write
993 * @return number of bytes written on success, #GNUNET_SYSERR on error
996 GNUNET_DISK_file_write (const struct GNUNET_DISK_FileHandle * h,
997 const void *buffer, size_t n)
1002 return GNUNET_SYSERR;
1006 DWORD bytes_written;
1008 if (h->type != GNUNET_DISK_HANLDE_TYPE_PIPE)
1010 if (!WriteFile (h->h, buffer, n, &bytes_written, NULL))
1012 SetErrnoFromWinError (GetLastError ());
1013 return GNUNET_SYSERR;
1018 LOG (GNUNET_ERROR_TYPE_DEBUG, "It is a pipe trying to write %u bytes\n", n);
1019 if (!WriteFile (h->h, buffer, n, &bytes_written, h->oOverlapWrite))
1021 if (GetLastError () != ERROR_IO_PENDING)
1023 SetErrnoFromWinError (GetLastError ());
1024 LOG (GNUNET_ERROR_TYPE_DEBUG, "Error writing to pipe: %u\n",
1026 return GNUNET_SYSERR;
1028 LOG (GNUNET_ERROR_TYPE_DEBUG, "Will get overlapped result\n");
1029 if (!GetOverlappedResult (h->h, h->oOverlapWrite, &bytes_written, TRUE))
1031 SetErrnoFromWinError (GetLastError ());
1032 LOG (GNUNET_ERROR_TYPE_DEBUG,
1033 "Error getting overlapped result while writing to pipe: %u\n",
1035 return GNUNET_SYSERR;
1041 if (!GetOverlappedResult (h->h, h->oOverlapWrite, &ovr, TRUE))
1043 LOG (GNUNET_ERROR_TYPE_DEBUG,
1044 "Error getting control overlapped result while writing to pipe: %u\n",
1049 LOG (GNUNET_ERROR_TYPE_DEBUG,
1050 "Wrote %u bytes (ovr says %u), picking the greatest\n",
1051 bytes_written, ovr);
1054 if (bytes_written == 0)
1058 LOG (GNUNET_ERROR_TYPE_DEBUG, "Wrote %u bytes, returning -1 with EAGAIN\n", bytes_written);
1060 return GNUNET_SYSERR;
1063 LOG (GNUNET_ERROR_TYPE_DEBUG, "Wrote %u bytes\n", bytes_written);
1065 return bytes_written;
1067 return write (h->fd, buffer, n);
1073 * Write a buffer to a file, blocking, if necessary.
1075 * @param h handle to open file
1076 * @param buffer the data to write
1077 * @param n number of bytes to write
1078 * @return number of bytes written on success, #GNUNET_SYSERR on error
1081 GNUNET_DISK_file_write_blocking (const struct GNUNET_DISK_FileHandle * h,
1088 return GNUNET_SYSERR;
1092 DWORD bytes_written;
1093 /* We do a non-overlapped write, which is as blocking as it gets */
1094 LOG (GNUNET_ERROR_TYPE_DEBUG, "Writing %u bytes\n", n);
1095 if (!WriteFile (h->h, buffer, n, &bytes_written, NULL))
1097 SetErrnoFromWinError (GetLastError ());
1098 LOG (GNUNET_ERROR_TYPE_DEBUG, "Error writing to pipe: %u\n",
1100 return GNUNET_SYSERR;
1102 if (bytes_written == 0 && n > 0)
1104 LOG (GNUNET_ERROR_TYPE_DEBUG, "Waiting for pipe to clean\n");
1105 WaitForSingleObject (h->h, INFINITE);
1106 if (!WriteFile (h->h, buffer, n, &bytes_written, NULL))
1108 SetErrnoFromWinError (GetLastError ());
1109 LOG (GNUNET_ERROR_TYPE_DEBUG, "Error writing to pipe: %u\n",
1111 return GNUNET_SYSERR;
1114 LOG (GNUNET_ERROR_TYPE_DEBUG,
1117 return bytes_written;
1122 /* set to blocking, write, then set back */
1123 flags = fcntl (h->fd, F_GETFL);
1124 if (0 != (flags & O_NONBLOCK))
1125 (void) fcntl (h->fd, F_SETFL, flags - O_NONBLOCK);
1126 ret = write (h->fd, buffer, n);
1127 if (0 == (flags & O_NONBLOCK))
1128 (void) fcntl (h->fd, F_SETFL, flags);
1135 * Write a buffer to a file. If the file is longer than the
1136 * number of bytes that will be written, it will be truncated.
1138 * @param fn file name
1139 * @param buffer the data to write
1140 * @param n number of bytes to write
1141 * @param mode file permissions
1142 * @return number of bytes written on success, #GNUNET_SYSERR on error
1145 GNUNET_DISK_fn_write (const char *fn, const void *buffer, size_t n,
1146 enum GNUNET_DISK_AccessPermissions mode)
1148 struct GNUNET_DISK_FileHandle *fh;
1151 fh = GNUNET_DISK_file_open (fn,
1152 GNUNET_DISK_OPEN_WRITE | GNUNET_DISK_OPEN_TRUNCATE
1153 | GNUNET_DISK_OPEN_CREATE, mode);
1155 return GNUNET_SYSERR;
1156 ret = GNUNET_DISK_file_write (fh, buffer, n);
1157 GNUNET_assert (GNUNET_OK == GNUNET_DISK_file_close (fh));
1163 * Scan a directory for files.
1165 * @param dir_name the name of the directory
1166 * @param callback the method to call for each file,
1167 * can be NULL, in that case, we only count
1168 * @param callback_cls closure for @a callback
1169 * @return the number of files found, #GNUNET_SYSERR on error or
1170 * ieration aborted by callback returning #GNUNET_SYSERR
1173 GNUNET_DISK_directory_scan (const char *dir_name,
1174 GNUNET_FileNameCallback callback,
1178 struct dirent *finfo;
1184 unsigned int name_len;
1185 unsigned int n_size;
1187 GNUNET_assert (dir_name != NULL);
1188 dname = GNUNET_STRINGS_filename_expand (dir_name);
1190 return GNUNET_SYSERR;
1191 while ((strlen (dname) > 0) && (dname[strlen (dname) - 1] == DIR_SEPARATOR))
1192 dname[strlen (dname) - 1] = '\0';
1193 if (0 != STAT (dname, &istat))
1195 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "stat", dname);
1196 GNUNET_free (dname);
1197 return GNUNET_SYSERR;
1199 if (!S_ISDIR (istat.st_mode))
1201 LOG (GNUNET_ERROR_TYPE_WARNING,
1202 _("Expected `%s' to be a directory!\n"),
1204 GNUNET_free (dname);
1205 return GNUNET_SYSERR;
1208 dinfo = OPENDIR (dname);
1209 if ((errno == EACCES) || (dinfo == NULL))
1211 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "opendir", dname);
1214 GNUNET_free (dname);
1215 return GNUNET_SYSERR;
1218 n_size = strlen (dname) + name_len + 2;
1219 name = GNUNET_malloc (n_size);
1220 while ((finfo = READDIR (dinfo)) != NULL)
1222 if ((0 == strcmp (finfo->d_name, ".")) ||
1223 (0 == strcmp (finfo->d_name, "..")))
1225 if (callback != NULL)
1227 if (name_len < strlen (finfo->d_name))
1230 name_len = strlen (finfo->d_name);
1231 n_size = strlen (dname) + name_len + 2;
1232 name = GNUNET_malloc (n_size);
1234 /* dname can end in "/" only if dname == "/";
1235 * if dname does not end in "/", we need to add
1236 * a "/" (otherwise, we must not!) */
1237 GNUNET_snprintf (name, n_size, "%s%s%s", dname,
1238 (strcmp (dname, DIR_SEPARATOR_STR) ==
1239 0) ? "" : DIR_SEPARATOR_STR, finfo->d_name);
1240 ret = callback (callback_cls, name);
1241 if (GNUNET_OK != ret)
1245 GNUNET_free (dname);
1246 if (GNUNET_NO == ret)
1248 return GNUNET_SYSERR;
1255 GNUNET_free (dname);
1261 * Opaque handle used for iterating over a directory.
1263 struct GNUNET_DISK_DirectoryIterator
1267 * Function to call on directory entries.
1269 GNUNET_DISK_DirectoryIteratorCallback callback;
1272 * Closure for callback.
1277 * Reference to directory.
1287 * Next filename to process.
1294 enum GNUNET_SCHEDULER_Priority priority;
1300 * Task used by the directory iterator.
1303 directory_iterator_task (void *cls,
1304 const struct GNUNET_SCHEDULER_TaskContext *tc)
1306 struct GNUNET_DISK_DirectoryIterator *iter = cls;
1309 name = iter->next_name;
1310 GNUNET_assert (name != NULL);
1311 iter->next_name = NULL;
1312 iter->callback (iter->callback_cls, iter, name, iter->dirname);
1318 * This function must be called during the DiskIteratorCallback
1319 * (exactly once) to schedule the task to process the next
1320 * filename in the directory (if there is one).
1322 * @param iter opaque handle for the iterator
1323 * @param can set to GNUNET_YES to terminate the iteration early
1324 * @return GNUNET_YES if iteration will continue,
1325 * GNUNET_NO if this was the last entry (and iteration is complete),
1326 * GNUNET_SYSERR if abort was YES
1329 GNUNET_DISK_directory_iterator_next (struct GNUNET_DISK_DirectoryIterator *iter,
1332 struct dirent *finfo;
1334 GNUNET_assert (iter->next_name == NULL);
1335 if (can == GNUNET_YES)
1337 CLOSEDIR (iter->directory);
1338 GNUNET_free (iter->dirname);
1340 return GNUNET_SYSERR;
1342 while (NULL != (finfo = READDIR (iter->directory)))
1344 if ((0 == strcmp (finfo->d_name, ".")) ||
1345 (0 == strcmp (finfo->d_name, "..")))
1347 GNUNET_asprintf (&iter->next_name, "%s%s%s", iter->dirname,
1348 DIR_SEPARATOR_STR, finfo->d_name);
1353 GNUNET_DISK_directory_iterator_next (iter, GNUNET_YES);
1356 GNUNET_SCHEDULER_add_with_priority (iter->priority, &directory_iterator_task,
1363 * Scan a directory for files using the scheduler to run a task for
1364 * each entry. The name of the directory must be expanded first (!).
1365 * If a scheduler does not need to be used, GNUNET_DISK_directory_scan
1366 * may provide a simpler API.
1368 * @param prio priority to use
1369 * @param dir_name the name of the directory
1370 * @param callback the method to call for each file
1371 * @param callback_cls closure for callback
1372 * @return GNUNET_YES if directory is not empty and 'callback'
1373 * will be called later, GNUNET_NO otherwise, GNUNET_SYSERR on error.
1376 GNUNET_DISK_directory_iterator_start (enum GNUNET_SCHEDULER_Priority prio,
1377 const char *dir_name,
1378 GNUNET_DISK_DirectoryIteratorCallback
1379 callback, void *callback_cls)
1381 struct GNUNET_DISK_DirectoryIterator *di;
1383 di = GNUNET_new (struct GNUNET_DISK_DirectoryIterator);
1384 di->callback = callback;
1385 di->callback_cls = callback_cls;
1386 di->directory = OPENDIR (dir_name);
1387 if (di->directory == NULL)
1390 callback (callback_cls, NULL, NULL, NULL);
1391 return GNUNET_SYSERR;
1393 di->dirname = GNUNET_strdup (dir_name);
1394 di->priority = prio;
1395 return GNUNET_DISK_directory_iterator_next (di, GNUNET_NO);
1400 * Function that removes the given directory by calling
1401 * "GNUNET_DISK_directory_remove".
1403 * @param unused not used
1404 * @param fn directory to remove
1405 * @return #GNUNET_OK
1408 remove_helper (void *unused, const char *fn)
1410 (void) GNUNET_DISK_directory_remove (fn);
1416 * Remove all files in a directory (rm -rf). Call with
1419 * @param filename the file to remove
1420 * @return #GNUNET_OK on success, #GNUNET_SYSERR on error
1423 GNUNET_DISK_directory_remove (const char *filename)
1427 if (NULL == filename)
1430 return GNUNET_SYSERR;
1432 if (0 != LSTAT (filename, &istat))
1433 return GNUNET_NO; /* file may not exist... */
1434 (void) CHMOD (filename, S_IWUSR | S_IRUSR | S_IXUSR);
1435 if (UNLINK (filename) == 0)
1437 if ((errno != EISDIR) &&
1438 /* EISDIR is not sufficient in all cases, e.g.
1439 * sticky /tmp directory may result in EPERM on BSD.
1440 * So we also explicitly check "isDirectory" */
1441 (GNUNET_YES != GNUNET_DISK_directory_test (filename, GNUNET_YES)))
1443 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "rmdir", filename);
1444 return GNUNET_SYSERR;
1446 if (GNUNET_SYSERR ==
1447 GNUNET_DISK_directory_scan (filename, &remove_helper, NULL))
1448 return GNUNET_SYSERR;
1449 if (0 != RMDIR (filename))
1451 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "rmdir", filename);
1452 return GNUNET_SYSERR;
1461 * @param src file to copy
1462 * @param dst destination file name
1463 * @return #GNUNET_OK on success, #GNUNET_SYSERR on error
1466 GNUNET_DISK_file_copy (const char *src,
1473 struct GNUNET_DISK_FileHandle *in;
1474 struct GNUNET_DISK_FileHandle *out;
1476 if (GNUNET_OK != GNUNET_DISK_file_size (src, &size, GNUNET_YES, GNUNET_YES))
1477 return GNUNET_SYSERR;
1479 in = GNUNET_DISK_file_open (src, GNUNET_DISK_OPEN_READ,
1480 GNUNET_DISK_PERM_NONE);
1482 return GNUNET_SYSERR;
1484 GNUNET_DISK_file_open (dst,
1485 GNUNET_DISK_OPEN_WRITE | GNUNET_DISK_OPEN_CREATE |
1486 GNUNET_DISK_OPEN_FAILIFEXISTS,
1487 GNUNET_DISK_PERM_USER_READ |
1488 GNUNET_DISK_PERM_USER_WRITE |
1489 GNUNET_DISK_PERM_GROUP_READ |
1490 GNUNET_DISK_PERM_GROUP_WRITE);
1493 GNUNET_DISK_file_close (in);
1494 return GNUNET_SYSERR;
1496 buf = GNUNET_malloc (COPY_BLK_SIZE);
1499 len = COPY_BLK_SIZE;
1500 if (len > size - pos)
1502 if (len != GNUNET_DISK_file_read (in, buf, len))
1504 if (len != GNUNET_DISK_file_write (out, buf, len))
1509 GNUNET_DISK_file_close (in);
1510 GNUNET_DISK_file_close (out);
1514 GNUNET_DISK_file_close (in);
1515 GNUNET_DISK_file_close (out);
1516 return GNUNET_SYSERR;
1521 * @brief Removes special characters as ':' from a filename.
1522 * @param fn the filename to canonicalize
1525 GNUNET_DISK_filename_canonicalize (char *fn)
1535 if (c == '/' || c == '\\' || c == ':' || c == '*' || c == '?' || c == '"' ||
1536 c == '<' || c == '>' || c == '|')
1548 * @brief Change owner of a file
1550 * @param filename name of file to change the owner of
1551 * @param user name of the new owner
1552 * @return GNUNET_OK on success, GNUNET_SYSERR on failure
1555 GNUNET_DISK_file_change_owner (const char *filename, const char *user)
1560 pws = getpwnam (user);
1563 LOG (GNUNET_ERROR_TYPE_ERROR,
1564 _("Cannot obtain information about user `%s': %s\n"), user,
1566 return GNUNET_SYSERR;
1568 if (0 != chown (filename, pws->pw_uid, pws->pw_gid))
1569 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "chown", filename);
1576 * Lock a part of a file
1577 * @param fh file handle
1578 * @param lock_start absolute position from where to lock
1579 * @param lock_end absolute position until where to lock
1580 * @param excl GNUNET_YES for an exclusive lock
1581 * @return GNUNET_OK on success, GNUNET_SYSERR on error
1584 GNUNET_DISK_file_lock (struct GNUNET_DISK_FileHandle *fh, off_t lock_start,
1585 off_t lock_end, int excl)
1590 return GNUNET_SYSERR;
1596 memset (&fl, 0, sizeof (struct flock));
1597 fl.l_type = excl ? F_WRLCK : F_RDLCK;
1598 fl.l_whence = SEEK_SET;
1599 fl.l_start = lock_start;
1600 fl.l_len = lock_end;
1602 return fcntl (fh->fd, F_SETLK, &fl) != 0 ? GNUNET_SYSERR : GNUNET_OK;
1605 off_t diff = lock_end - lock_start;
1606 DWORD diff_low, diff_high;
1607 diff_low = (DWORD) (diff & 0xFFFFFFFF);
1608 diff_high = (DWORD) ((diff >> (sizeof (DWORD) * 8)) & 0xFFFFFFFF);
1610 memset (&o, 0, sizeof (OVERLAPPED));
1611 o.Offset = (DWORD) (lock_start & 0xFFFFFFFF);;
1612 o.OffsetHigh = (DWORD) (((lock_start & ~0xFFFFFFFF) >> (sizeof (DWORD) * 8)) & 0xFFFFFFFF);
1615 (fh->h, (excl ? LOCKFILE_EXCLUSIVE_LOCK : 0) | LOCKFILE_FAIL_IMMEDIATELY,
1616 0, diff_low, diff_high, &o))
1618 SetErrnoFromWinError (GetLastError ());
1619 return GNUNET_SYSERR;
1628 * Unlock a part of a file
1629 * @param fh file handle
1630 * @param unlock_start absolute position from where to unlock
1631 * @param unlock_end absolute position until where to unlock
1632 * @return #GNUNET_OK on success, #GNUNET_SYSERR on error
1635 GNUNET_DISK_file_unlock (struct GNUNET_DISK_FileHandle *fh, off_t unlock_start,
1641 return GNUNET_SYSERR;
1647 memset (&fl, 0, sizeof (struct flock));
1648 fl.l_type = F_UNLCK;
1649 fl.l_whence = SEEK_SET;
1650 fl.l_start = unlock_start;
1651 fl.l_len = unlock_end;
1653 return fcntl (fh->fd, F_SETLK, &fl) != 0 ? GNUNET_SYSERR : GNUNET_OK;
1656 off_t diff = unlock_end - unlock_start;
1657 DWORD diff_low, diff_high;
1658 diff_low = (DWORD) (diff & 0xFFFFFFFF);
1659 diff_high = (DWORD) ((diff >> (sizeof (DWORD) * 8)) & 0xFFFFFFFF);
1661 memset (&o, 0, sizeof (OVERLAPPED));
1662 o.Offset = (DWORD) (unlock_start & 0xFFFFFFFF);;
1663 o.OffsetHigh = (DWORD) (((unlock_start & ~0xFFFFFFFF) >> (sizeof (DWORD) * 8)) & 0xFFFFFFFF);
1665 if (!UnlockFileEx (fh->h, 0, diff_low, diff_high, &o))
1667 SetErrnoFromWinError (GetLastError ());
1668 return GNUNET_SYSERR;
1677 * Open a file. Note that the access permissions will only be
1678 * used if a new file is created and if the underlying operating
1679 * system supports the given permissions.
1681 * @param fn file name to be opened
1682 * @param flags opening flags, a combination of GNUNET_DISK_OPEN_xxx bit flags
1683 * @param perm permissions for the newly created file, use
1684 * #GNUNET_DISK_PERM_NONE if a file could not be created by this
1685 * call (because of flags)
1686 * @return IO handle on success, NULL on error
1688 struct GNUNET_DISK_FileHandle *
1689 GNUNET_DISK_file_open (const char *fn,
1690 enum GNUNET_DISK_OpenFlags flags,
1691 enum GNUNET_DISK_AccessPermissions perm)
1694 struct GNUNET_DISK_FileHandle *ret;
1700 wchar_t wexpfn[MAX_PATH + 1];
1707 expfn = GNUNET_STRINGS_filename_expand (fn);
1712 if (GNUNET_DISK_OPEN_READWRITE == (flags & GNUNET_DISK_OPEN_READWRITE))
1713 oflags = O_RDWR; /* note: O_RDWR is NOT always O_RDONLY | O_WRONLY */
1714 else if (flags & GNUNET_DISK_OPEN_READ)
1716 else if (flags & GNUNET_DISK_OPEN_WRITE)
1721 GNUNET_free (expfn);
1724 if (flags & GNUNET_DISK_OPEN_FAILIFEXISTS)
1725 oflags |= (O_CREAT | O_EXCL);
1726 if (flags & GNUNET_DISK_OPEN_TRUNCATE)
1728 if (flags & GNUNET_DISK_OPEN_APPEND)
1730 if (flags & GNUNET_DISK_OPEN_CREATE)
1732 (void) GNUNET_DISK_directory_create_for_file (expfn);
1734 mode = translate_unix_perms (perm);
1737 fd = open (expfn, oflags
1741 | O_LARGEFILE, mode);
1744 if (0 == (flags & GNUNET_DISK_OPEN_FAILIFEXISTS))
1745 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "open", expfn);
1747 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_DEBUG, "open", expfn);
1748 GNUNET_free (expfn);
1755 if (GNUNET_DISK_OPEN_READWRITE == (flags & GNUNET_DISK_OPEN_READWRITE))
1756 access = FILE_READ_DATA | FILE_WRITE_DATA;
1757 else if (flags & GNUNET_DISK_OPEN_READ)
1758 access = FILE_READ_DATA;
1759 else if (flags & GNUNET_DISK_OPEN_WRITE)
1760 access = FILE_WRITE_DATA;
1762 if (flags & GNUNET_DISK_OPEN_FAILIFEXISTS)
1766 else if (flags & GNUNET_DISK_OPEN_CREATE)
1768 (void) GNUNET_DISK_directory_create_for_file (expfn);
1769 if (flags & GNUNET_DISK_OPEN_TRUNCATE)
1770 disp = CREATE_ALWAYS;
1774 else if (flags & GNUNET_DISK_OPEN_TRUNCATE)
1776 disp = TRUNCATE_EXISTING;
1780 disp = OPEN_EXISTING;
1783 if (ERROR_SUCCESS == plibc_conv_to_win_pathwconv(expfn, wexpfn))
1784 h = CreateFileW (wexpfn, access,
1785 FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE, NULL,
1786 disp, FILE_ATTRIBUTE_NORMAL, NULL);
1788 h = INVALID_HANDLE_VALUE;
1789 if (h == INVALID_HANDLE_VALUE)
1792 SetErrnoFromWinError (GetLastError ());
1794 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_INFO, "open", expfn);
1795 GNUNET_free (expfn);
1800 if (flags & GNUNET_DISK_OPEN_APPEND)
1801 if (SetFilePointer (h, 0, 0, FILE_END) == INVALID_SET_FILE_POINTER)
1803 SetErrnoFromWinError (GetLastError ());
1804 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "SetFilePointer", expfn);
1806 GNUNET_free (expfn);
1811 ret = GNUNET_new (struct GNUNET_DISK_FileHandle);
1814 ret->type = GNUNET_DISK_HANLDE_TYPE_FILE;
1818 GNUNET_free (expfn);
1824 * Close an open file
1825 * @param h file handle
1826 * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
1829 GNUNET_DISK_file_close (struct GNUNET_DISK_FileHandle *h)
1835 return GNUNET_SYSERR;
1841 if (!CloseHandle (h->h))
1843 SetErrnoFromWinError (GetLastError ());
1844 LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING, "close");
1845 ret = GNUNET_SYSERR;
1847 if (h->oOverlapRead)
1849 if (!CloseHandle (h->oOverlapRead->hEvent))
1851 SetErrnoFromWinError (GetLastError ());
1852 LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING, "close");
1853 ret = GNUNET_SYSERR;
1855 GNUNET_free (h->oOverlapRead);
1857 if (h->oOverlapWrite)
1859 if (!CloseHandle (h->oOverlapWrite->hEvent))
1861 SetErrnoFromWinError (GetLastError ());
1862 LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING, "close");
1863 ret = GNUNET_SYSERR;
1865 GNUNET_free (h->oOverlapWrite);
1868 if (close (h->fd) != 0)
1870 LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING, "close");
1871 ret = GNUNET_SYSERR;
1880 * Get a GNUnet file handle from a W32 handle.
1882 * @param handle native handle
1883 * @return GNUnet file handle corresponding to the W32 handle
1885 struct GNUNET_DISK_FileHandle *
1886 GNUNET_DISK_get_handle_from_w32_handle (HANDLE osfh)
1888 struct GNUNET_DISK_FileHandle *fh;
1891 enum GNUNET_FILE_Type ftype;
1893 dwret = GetFileType (osfh);
1896 case FILE_TYPE_DISK:
1897 ftype = GNUNET_DISK_HANLDE_TYPE_FILE;
1899 case FILE_TYPE_PIPE:
1900 ftype = GNUNET_DISK_HANLDE_TYPE_PIPE;
1906 fh = GNUNET_new (struct GNUNET_DISK_FileHandle);
1910 if (ftype == GNUNET_DISK_HANLDE_TYPE_PIPE)
1913 * Note that we can't make it overlapped if it isn't already.
1914 * (ReOpenFile() is only available in 2003/Vista).
1915 * The process that opened this file in the first place (usually a parent
1916 * process, if this is stdin/stdout/stderr) must make it overlapped,
1917 * otherwise we're screwed, as selecting on non-overlapped handle
1920 fh->oOverlapRead = GNUNET_new (OVERLAPPED);
1921 fh->oOverlapWrite = GNUNET_new (OVERLAPPED);
1922 fh->oOverlapRead->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
1923 fh->oOverlapWrite->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
1931 * Get a handle from a native integer FD.
1933 * @param fno native integer file descriptor
1934 * @return file handle corresponding to the descriptor, NULL on error
1936 struct GNUNET_DISK_FileHandle *
1937 GNUNET_DISK_get_handle_from_int_fd (int fno)
1939 struct GNUNET_DISK_FileHandle *fh;
1941 if ( (((off_t) -1) == lseek (fno, 0, SEEK_CUR)) &&
1943 return NULL; /* invalid FD */
1946 fh = GNUNET_new (struct GNUNET_DISK_FileHandle);
1952 osfh = _get_osfhandle (fno);
1953 if (INVALID_HANDLE_VALUE == (HANDLE) osfh)
1956 fh = GNUNET_DISK_get_handle_from_w32_handle ((HANDLE) osfh);
1964 * Get a handle from a native streaming FD.
1966 * @param fd native streaming file descriptor
1967 * @return file handle corresponding to the descriptor
1969 struct GNUNET_DISK_FileHandle *
1970 GNUNET_DISK_get_handle_from_native (FILE *fd)
1978 return GNUNET_DISK_get_handle_from_int_fd (fno);
1983 * Handle for a memory-mapping operation.
1985 struct GNUNET_DISK_MapHandle
1988 * Address where the map is in memory.
1994 * Underlying OS handle.
1999 * Number of bytes mapped.
2007 #define MAP_FAILED ((void *) -1)
2011 * Map a file into memory
2013 * @param h open file handle
2014 * @param m handle to the new mapping
2015 * @param access access specification, GNUNET_DISK_MAP_TYPE_xxx
2016 * @param len size of the mapping
2017 * @return pointer to the mapped memory region, NULL on failure
2020 GNUNET_DISK_file_map (const struct GNUNET_DISK_FileHandle *h,
2021 struct GNUNET_DISK_MapHandle **m,
2022 enum GNUNET_DISK_MapType access, size_t len)
2031 DWORD mapAccess, protect;
2033 if ((access & GNUNET_DISK_MAP_TYPE_READ) &&
2034 (access & GNUNET_DISK_MAP_TYPE_WRITE))
2036 protect = PAGE_READWRITE;
2037 mapAccess = FILE_MAP_ALL_ACCESS;
2039 else if (access & GNUNET_DISK_MAP_TYPE_READ)
2041 protect = PAGE_READONLY;
2042 mapAccess = FILE_MAP_READ;
2044 else if (access & GNUNET_DISK_MAP_TYPE_WRITE)
2046 protect = PAGE_READWRITE;
2047 mapAccess = FILE_MAP_WRITE;
2055 *m = GNUNET_new (struct GNUNET_DISK_MapHandle);
2056 (*m)->h = CreateFileMapping (h->h, NULL, protect, 0, 0, NULL);
2057 if ((*m)->h == INVALID_HANDLE_VALUE)
2059 SetErrnoFromWinError (GetLastError ());
2064 (*m)->addr = MapViewOfFile ((*m)->h, mapAccess, 0, 0, len);
2067 SetErrnoFromWinError (GetLastError ());
2068 CloseHandle ((*m)->h);
2077 if (access & GNUNET_DISK_MAP_TYPE_READ)
2079 if (access & GNUNET_DISK_MAP_TYPE_WRITE)
2081 *m = GNUNET_new (struct GNUNET_DISK_MapHandle);
2082 (*m)->addr = mmap (NULL, len, prot, MAP_SHARED, h->fd, 0);
2083 GNUNET_assert (NULL != (*m)->addr);
2084 if (MAP_FAILED == (*m)->addr)
2096 * @param h mapping handle
2097 * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
2100 GNUNET_DISK_file_unmap (struct GNUNET_DISK_MapHandle *h)
2107 return GNUNET_SYSERR;
2111 ret = UnmapViewOfFile (h->addr) ? GNUNET_OK : GNUNET_SYSERR;
2112 if (ret != GNUNET_OK)
2113 SetErrnoFromWinError (GetLastError ());
2114 if (!CloseHandle (h->h) && (ret == GNUNET_OK))
2116 ret = GNUNET_SYSERR;
2117 SetErrnoFromWinError (GetLastError ());
2120 ret = munmap (h->addr, h->len) != -1 ? GNUNET_OK : GNUNET_SYSERR;
2128 * Write file changes to disk
2129 * @param h handle to an open file
2130 * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
2133 GNUNET_DISK_file_sync (const struct GNUNET_DISK_FileHandle *h)
2138 return GNUNET_SYSERR;
2144 ret = FlushFileBuffers (h->h) ? GNUNET_OK : GNUNET_SYSERR;
2145 if (ret != GNUNET_OK)
2146 SetErrnoFromWinError (GetLastError ());
2148 #elif defined(FREEBSD) || defined(OPENBSD) || defined(DARWIN)
2149 return fsync (h->fd) == -1 ? GNUNET_SYSERR : GNUNET_OK;
2151 return fdatasync (h->fd) == -1 ? GNUNET_SYSERR : GNUNET_OK;
2158 #define PIPE_BUF 512
2160 /* Copyright Bob Byrnes <byrnes <at> curl.com>
2161 http://permalink.gmane.org/gmane.os.cygwin.patches/2121
2163 /* Create a pipe, and return handles to the read and write ends,
2164 just like CreatePipe, but ensure that the write end permits
2165 FILE_READ_ATTRIBUTES access, on later versions of win32 where
2166 this is supported. This access is needed by NtQueryInformationFile,
2167 which is used to implement select and nonblocking writes.
2168 Note that the return value is either NO_ERROR or GetLastError,
2169 unlike CreatePipe, which returns a bool for success or failure. */
2171 create_selectable_pipe (PHANDLE read_pipe_ptr, PHANDLE write_pipe_ptr,
2172 LPSECURITY_ATTRIBUTES sa_ptr, DWORD psize,
2173 DWORD dwReadMode, DWORD dwWriteMode)
2175 /* Default to error. */
2176 *read_pipe_ptr = *write_pipe_ptr = INVALID_HANDLE_VALUE;
2181 /* Ensure that there is enough pipe buffer space for atomic writes. */
2182 if (psize < PIPE_BUF)
2185 char pipename[MAX_PATH];
2187 /* Retry CreateNamedPipe as long as the pipe name is in use.
2188 * Retrying will probably never be necessary, but we want
2189 * to be as robust as possible. */
2192 static volatile LONG pipe_unique_id;
2194 snprintf (pipename, sizeof pipename, "\\\\.\\pipe\\gnunet-%d-%ld",
2195 getpid (), InterlockedIncrement ((LONG *) & pipe_unique_id));
2196 LOG (GNUNET_ERROR_TYPE_DEBUG, "CreateNamedPipe: name = %s, size = %lu\n",
2198 /* Use CreateNamedPipe instead of CreatePipe, because the latter
2199 * returns a write handle that does not permit FILE_READ_ATTRIBUTES
2200 * access, on versions of win32 earlier than WinXP SP2.
2201 * CreatePipe also stupidly creates a full duplex pipe, which is
2202 * a waste, since only a single direction is actually used.
2203 * It's important to only allow a single instance, to ensure that
2204 * the pipe was not created earlier by some other process, even if
2205 * the pid has been reused. */
2206 read_pipe = CreateNamedPipeA (pipename, PIPE_ACCESS_INBOUND | FILE_FLAG_FIRST_PIPE_INSTANCE | dwReadMode, PIPE_TYPE_BYTE | PIPE_READMODE_BYTE, 1, /* max instances */
2207 psize, /* output buffer size */
2208 psize, /* input buffer size */
2209 NMPWAIT_USE_DEFAULT_WAIT, sa_ptr);
2211 if (read_pipe != INVALID_HANDLE_VALUE)
2213 LOG (GNUNET_ERROR_TYPE_DEBUG, "pipe read handle = %p\n", read_pipe);
2217 DWORD err = GetLastError ();
2221 case ERROR_PIPE_BUSY:
2222 /* The pipe is already open with compatible parameters.
2223 * Pick a new name and retry. */
2224 LOG (GNUNET_ERROR_TYPE_DEBUG, "pipe busy, retrying\n");
2226 case ERROR_ACCESS_DENIED:
2227 /* The pipe is already open with incompatible parameters.
2228 * Pick a new name and retry. */
2229 LOG (GNUNET_ERROR_TYPE_DEBUG, "pipe access denied, retrying\n");
2231 case ERROR_CALL_NOT_IMPLEMENTED:
2232 /* We are on an older Win9x platform without named pipes.
2233 * Return an anonymous pipe as the best approximation. */
2234 LOG (GNUNET_ERROR_TYPE_DEBUG,
2235 "CreateNamedPipe not implemented, resorting to "
2236 "CreatePipe: size = %lu\n", psize);
2237 if (CreatePipe (read_pipe_ptr, write_pipe_ptr, sa_ptr, psize))
2239 LOG (GNUNET_ERROR_TYPE_DEBUG, "pipe read handle = %p, write handle = %p\n",
2244 err = GetLastError ();
2245 LOG (GNUNET_ERROR_TYPE_ERROR, "CreatePipe failed: %d\n", err);
2248 LOG (GNUNET_ERROR_TYPE_ERROR, "CreateNamedPipe failed: %d\n", err);
2253 LOG (GNUNET_ERROR_TYPE_DEBUG, "CreateFile: name = %s\n", pipename);
2255 /* Open the named pipe for writing.
2256 * Be sure to permit FILE_READ_ATTRIBUTES access. */
2257 write_pipe = CreateFileA (pipename, GENERIC_WRITE | FILE_READ_ATTRIBUTES, 0, /* share mode */
2258 sa_ptr, OPEN_EXISTING, dwWriteMode, /* flags and attributes */
2259 0); /* handle to template file */
2261 if (write_pipe == INVALID_HANDLE_VALUE)
2264 DWORD err = GetLastError ();
2266 LOG (GNUNET_ERROR_TYPE_DEBUG, "CreateFile failed: %d\n", err);
2267 CloseHandle (read_pipe);
2270 LOG (GNUNET_ERROR_TYPE_DEBUG, "pipe write handle = %p\n", write_pipe);
2272 *read_pipe_ptr = read_pipe;
2273 *write_pipe_ptr = write_pipe;
2280 * Creates an interprocess channel
2282 * @param blocking_read creates an asynchronous pipe for reading if set to GNUNET_NO
2283 * @param blocking_write creates an asynchronous pipe for writing if set to GNUNET_NO
2284 * @param inherit_read inherit the parent processes stdin (only for windows)
2285 * @param inherit_write inherit the parent processes stdout (only for windows)
2286 * @return handle to the new pipe, NULL on error
2288 struct GNUNET_DISK_PipeHandle *
2289 GNUNET_DISK_pipe (int blocking_read, int blocking_write, int inherit_read, int inherit_write)
2300 LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR, "pipe");
2304 return GNUNET_DISK_pipe_from_fd (blocking_read,
2308 struct GNUNET_DISK_PipeHandle *p;
2313 p = GNUNET_new (struct GNUNET_DISK_PipeHandle);
2314 p->fd[0] = GNUNET_new (struct GNUNET_DISK_FileHandle);
2315 p->fd[1] = GNUNET_new (struct GNUNET_DISK_FileHandle);
2317 /* All pipes are overlapped. If you want them to block - just
2318 * call WriteFile() and ReadFile() with NULL overlapped pointer.
2319 * NOTE: calling with NULL overlapped pointer works only
2320 * for pipes, and doesn't seem to be a documented feature.
2321 * It will NOT work for files, because overlapped files need
2322 * to read offsets from the overlapped structure, regardless.
2323 * Pipes are not seekable, and need no offsets, which is
2324 * probably why it works for them.
2327 create_selectable_pipe (&p->fd[0]->h, &p->fd[1]->h, NULL, 0,
2328 FILE_FLAG_OVERLAPPED,
2329 FILE_FLAG_OVERLAPPED);
2332 SetErrnoFromWinError (GetLastError ());
2334 GNUNET_free (p->fd[0]);
2335 GNUNET_free (p->fd[1]);
2340 if (!DuplicateHandle
2341 (GetCurrentProcess (), p->fd[0]->h, GetCurrentProcess (), &tmp_handle, 0,
2342 inherit_read == GNUNET_YES ? TRUE : FALSE, DUPLICATE_SAME_ACCESS))
2344 SetErrnoFromWinError (GetLastError ());
2346 CloseHandle (p->fd[0]->h);
2347 CloseHandle (p->fd[1]->h);
2348 GNUNET_free (p->fd[0]);
2349 GNUNET_free (p->fd[1]);
2354 CloseHandle (p->fd[0]->h);
2355 p->fd[0]->h = tmp_handle;
2357 if (!DuplicateHandle
2358 (GetCurrentProcess (), p->fd[1]->h, GetCurrentProcess (), &tmp_handle, 0,
2359 inherit_write == GNUNET_YES ? TRUE : FALSE, DUPLICATE_SAME_ACCESS))
2361 SetErrnoFromWinError (GetLastError ());
2363 CloseHandle (p->fd[0]->h);
2364 CloseHandle (p->fd[1]->h);
2365 GNUNET_free (p->fd[0]);
2366 GNUNET_free (p->fd[1]);
2371 CloseHandle (p->fd[1]->h);
2372 p->fd[1]->h = tmp_handle;
2374 p->fd[0]->type = GNUNET_DISK_HANLDE_TYPE_PIPE;
2375 p->fd[1]->type = GNUNET_DISK_HANLDE_TYPE_PIPE;
2377 p->fd[0]->oOverlapRead = GNUNET_new (OVERLAPPED);
2378 p->fd[0]->oOverlapWrite = GNUNET_new (OVERLAPPED);
2379 p->fd[1]->oOverlapRead = GNUNET_new (OVERLAPPED);
2380 p->fd[1]->oOverlapWrite = GNUNET_new (OVERLAPPED);
2382 p->fd[0]->oOverlapRead->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2383 p->fd[0]->oOverlapWrite->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2385 p->fd[1]->oOverlapRead->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2386 p->fd[1]->oOverlapWrite->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2394 * Creates a pipe object from a couple of file descriptors.
2395 * Useful for wrapping existing pipe FDs.
2397 * @param blocking_read creates an asynchronous pipe for reading if set to GNUNET_NO
2398 * @param blocking_write creates an asynchronous pipe for writing if set to GNUNET_NO
2399 * @param fd an array of two fd values. One of them may be -1 for read-only or write-only pipes
2401 * @return handle to the new pipe, NULL on error
2403 struct GNUNET_DISK_PipeHandle *
2404 GNUNET_DISK_pipe_from_fd (int blocking_read, int blocking_write, int fd[2])
2406 struct GNUNET_DISK_PipeHandle *p;
2408 p = GNUNET_new (struct GNUNET_DISK_PipeHandle);
2413 int eno = 0; /* make gcc happy */
2418 p->fd[0] = GNUNET_new (struct GNUNET_DISK_FileHandle);
2419 p->fd[0]->fd = fd[0];
2422 flags = fcntl (fd[0], F_GETFL);
2423 flags |= O_NONBLOCK;
2424 if (0 > fcntl (fd[0], F_SETFL, flags))
2430 flags = fcntl (fd[0], F_GETFD);
2431 flags |= FD_CLOEXEC;
2432 if (0 > fcntl (fd[0], F_SETFD, flags))
2441 p->fd[1] = GNUNET_new (struct GNUNET_DISK_FileHandle);
2442 p->fd[1]->fd = fd[1];
2443 if (!blocking_write)
2445 flags = fcntl (fd[1], F_GETFL);
2446 flags |= O_NONBLOCK;
2447 if (0 > fcntl (fd[1], F_SETFL, flags))
2453 flags = fcntl (fd[1], F_GETFD);
2454 flags |= FD_CLOEXEC;
2455 if (0 > fcntl (fd[1], F_SETFD, flags))
2464 LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR, "fcntl");
2465 if (p->fd[0]->fd >= 0)
2466 GNUNET_break (0 == close (p->fd[0]->fd));
2467 if (p->fd[1]->fd >= 0)
2468 GNUNET_break (0 == close (p->fd[1]->fd));
2469 GNUNET_free_non_null (p->fd[0]);
2470 GNUNET_free_non_null (p->fd[1]);
2478 p->fd[0] = GNUNET_new (struct GNUNET_DISK_FileHandle);
2479 p->fd[0]->h = (HANDLE) _get_osfhandle (fd[0]);
2480 if (p->fd[0]->h != INVALID_HANDLE_VALUE)
2482 p->fd[0]->type = GNUNET_DISK_HANLDE_TYPE_PIPE;
2483 p->fd[0]->oOverlapRead = GNUNET_new (OVERLAPPED);
2484 p->fd[0]->oOverlapWrite = GNUNET_new (OVERLAPPED);
2485 p->fd[0]->oOverlapRead->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2486 p->fd[0]->oOverlapWrite->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2490 GNUNET_free (p->fd[0]);
2496 p->fd[1] = GNUNET_new (struct GNUNET_DISK_FileHandle);
2497 p->fd[1]->h = (HANDLE) _get_osfhandle (fd[1]);
2498 if (p->fd[1]->h != INVALID_HANDLE_VALUE)
2500 p->fd[1]->type = GNUNET_DISK_HANLDE_TYPE_PIPE;
2501 p->fd[1]->oOverlapRead = GNUNET_new (OVERLAPPED);
2502 p->fd[1]->oOverlapWrite = GNUNET_new (OVERLAPPED);
2503 p->fd[1]->oOverlapRead->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2504 p->fd[1]->oOverlapWrite->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2508 GNUNET_free (p->fd[1]);
2519 * Closes an interprocess channel
2521 * @param p pipe to close
2522 * @param end which end of the pipe to close
2523 * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
2526 GNUNET_DISK_pipe_close_end (struct GNUNET_DISK_PipeHandle *p,
2527 enum GNUNET_DISK_PipeEnd end)
2529 int ret = GNUNET_OK;
2531 if (end == GNUNET_DISK_PIPE_END_READ)
2535 ret = GNUNET_DISK_file_close (p->fd[0]);
2539 else if (end == GNUNET_DISK_PIPE_END_WRITE)
2543 ret = GNUNET_DISK_file_close (p->fd[1]);
2552 * Detaches one of the ends from the pipe.
2553 * Detached end is a fully-functional FileHandle, it will
2554 * not be affected by anything you do with the pipe afterwards.
2555 * Each end of a pipe can only be detched from it once (i.e.
2556 * it is not duplicated).
2558 * @param p pipe to detach an end from
2559 * @param end which end of the pipe to detach
2560 * @return Detached end on success, NULL on failure
2561 * (or if that end is not present or is closed).
2563 struct GNUNET_DISK_FileHandle *
2564 GNUNET_DISK_pipe_detach_end (struct GNUNET_DISK_PipeHandle *p,
2565 enum GNUNET_DISK_PipeEnd end)
2567 struct GNUNET_DISK_FileHandle *ret = NULL;
2569 if (end == GNUNET_DISK_PIPE_END_READ)
2577 else if (end == GNUNET_DISK_PIPE_END_WRITE)
2591 * Closes an interprocess channel
2593 * @param p pipe to close
2594 * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
2597 GNUNET_DISK_pipe_close (struct GNUNET_DISK_PipeHandle *p)
2599 int ret = GNUNET_OK;
2602 int write_end_close;
2603 int read_end_close_errno;
2604 int write_end_close_errno;
2606 read_end_close = GNUNET_DISK_pipe_close_end (p, GNUNET_DISK_PIPE_END_READ);
2607 read_end_close_errno = errno;
2608 write_end_close = GNUNET_DISK_pipe_close_end (p, GNUNET_DISK_PIPE_END_WRITE);
2609 write_end_close_errno = errno;
2612 if (GNUNET_OK != read_end_close)
2614 errno = read_end_close_errno;
2615 ret = read_end_close;
2617 else if (GNUNET_OK != write_end_close)
2619 errno = write_end_close_errno;
2620 ret = write_end_close;
2628 * Get the handle to a particular pipe end
2631 * @param n end to access
2632 * @return handle for the respective end
2634 const struct GNUNET_DISK_FileHandle *
2635 GNUNET_DISK_pipe_handle (const struct GNUNET_DISK_PipeHandle *p,
2636 enum GNUNET_DISK_PipeEnd n)
2640 case GNUNET_DISK_PIPE_END_READ:
2641 case GNUNET_DISK_PIPE_END_WRITE:
2651 * Retrieve OS file handle
2653 * @param fh GNUnet file descriptor
2654 * @param dst destination buffer
2655 * @param dst_len length of dst
2656 * @return #GNUNET_OK on success, #GNUNET_SYSERR otherwise
2659 GNUNET_DISK_internal_file_handle_ (const struct GNUNET_DISK_FileHandle *fh,
2660 void *dst, size_t dst_len)
2663 return GNUNET_SYSERR;
2665 if (dst_len < sizeof (HANDLE))
2666 return GNUNET_SYSERR;
2667 *((HANDLE *) dst) = fh->h;
2669 if (dst_len < sizeof (int))
2670 return GNUNET_SYSERR;
2671 *((int *) dst) = fh->fd;