2 This file is part of GNUnet.
3 Copyright (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_FILE)
859 if (!ReadFile (h->h, result, len, &bytes_read, NULL))
861 SetErrnoFromWinError (GetLastError ());
862 return GNUNET_SYSERR;
865 else if (h->type == GNUNET_DISK_HANLDE_TYPE_PIPE)
867 if (!ReadFile (h->h, result, len, &bytes_read, h->oOverlapRead))
869 if (GetLastError () != ERROR_IO_PENDING)
871 LOG (GNUNET_ERROR_TYPE_DEBUG,
872 "Error reading from pipe: %u\n",
874 SetErrnoFromWinError (GetLastError ());
875 return GNUNET_SYSERR;
877 LOG (GNUNET_ERROR_TYPE_DEBUG, "Will get overlapped result\n");
878 GetOverlappedResult (h->h, h->oOverlapRead, &bytes_read, TRUE);
880 LOG (GNUNET_ERROR_TYPE_DEBUG, "Read %u bytes from pipe\n", bytes_read);
888 return read (h->fd, result, len);
894 * Read the contents of a binary file into a buffer.
895 * Guarantees not to block (returns GNUNET_SYSERR and sets errno to EAGAIN
896 * when no data can be read).
898 * @param h handle to an open file
899 * @param result the buffer to write the result to
900 * @param len the maximum number of bytes to read
901 * @return the number of bytes read on success, #GNUNET_SYSERR on failure
904 GNUNET_DISK_file_read_non_blocking (const struct GNUNET_DISK_FileHandle *h,
911 return GNUNET_SYSERR;
917 if (h->type == GNUNET_DISK_HANLDE_TYPE_FILE)
919 if (!ReadFile (h->h, result, len, &bytes_read, NULL))
921 SetErrnoFromWinError (GetLastError ());
922 return GNUNET_SYSERR;
925 else if (h->type == GNUNET_DISK_HANLDE_TYPE_PIPE)
927 if (!ReadFile (h->h, result, len, &bytes_read, h->oOverlapRead))
929 if (GetLastError () != ERROR_IO_PENDING)
931 LOG (GNUNET_ERROR_TYPE_DEBUG, "Error reading from pipe: %u\n", GetLastError ());
932 SetErrnoFromWinError (GetLastError ());
933 return GNUNET_SYSERR;
937 LOG (GNUNET_ERROR_TYPE_DEBUG,
938 "ReadFile() queued a read, cancelling\n");
941 return GNUNET_SYSERR;
944 LOG (GNUNET_ERROR_TYPE_DEBUG,
957 /* set to non-blocking, read, then set back */
958 flags = fcntl (h->fd, F_GETFL);
959 if (0 == (flags & O_NONBLOCK))
960 (void) fcntl (h->fd, F_SETFL, flags | O_NONBLOCK);
961 ret = read (h->fd, result, len);
962 if (0 == (flags & O_NONBLOCK))
965 (void) fcntl (h->fd, F_SETFL, flags);
974 * Read the contents of a binary file into a buffer.
976 * @param fn file name
977 * @param result the buffer to write the result to
978 * @param len the maximum number of bytes to read
979 * @return number of bytes read, #GNUNET_SYSERR on failure
982 GNUNET_DISK_fn_read (const char *fn,
986 struct GNUNET_DISK_FileHandle *fh;
990 fh = GNUNET_DISK_file_open (fn,
991 GNUNET_DISK_OPEN_READ,
992 GNUNET_DISK_PERM_NONE);
994 return GNUNET_SYSERR;
995 ret = GNUNET_DISK_file_read (fh, result, len);
997 GNUNET_assert (GNUNET_OK == GNUNET_DISK_file_close (fh));
1004 * Write a buffer to a file.
1006 * @param h handle to open file
1007 * @param buffer the data to write
1008 * @param n number of bytes to write
1009 * @return number of bytes written on success, #GNUNET_SYSERR on error
1012 GNUNET_DISK_file_write (const struct GNUNET_DISK_FileHandle * h,
1013 const void *buffer, size_t n)
1018 return GNUNET_SYSERR;
1022 DWORD bytes_written;
1024 if (h->type == GNUNET_DISK_HANLDE_TYPE_FILE)
1026 if (!WriteFile (h->h, buffer, n, &bytes_written, NULL))
1028 SetErrnoFromWinError (GetLastError ());
1029 return GNUNET_SYSERR;
1032 else if (h->type == GNUNET_DISK_HANLDE_TYPE_PIPE)
1034 LOG (GNUNET_ERROR_TYPE_DEBUG, "It is a pipe trying to write %u bytes\n", n);
1035 if (!WriteFile (h->h, buffer, n, &bytes_written, h->oOverlapWrite))
1037 if (GetLastError () != ERROR_IO_PENDING)
1039 SetErrnoFromWinError (GetLastError ());
1040 LOG (GNUNET_ERROR_TYPE_DEBUG, "Error writing to pipe: %u\n",
1042 return GNUNET_SYSERR;
1044 LOG (GNUNET_ERROR_TYPE_DEBUG, "Will get overlapped result\n");
1045 if (!GetOverlappedResult (h->h, h->oOverlapWrite, &bytes_written, TRUE))
1047 SetErrnoFromWinError (GetLastError ());
1048 LOG (GNUNET_ERROR_TYPE_DEBUG,
1049 "Error getting overlapped result while writing to pipe: %u\n",
1051 return GNUNET_SYSERR;
1057 if (!GetOverlappedResult (h->h, h->oOverlapWrite, &ovr, TRUE))
1059 LOG (GNUNET_ERROR_TYPE_DEBUG,
1060 "Error getting control overlapped result while writing to pipe: %u\n",
1065 LOG (GNUNET_ERROR_TYPE_DEBUG,
1066 "Wrote %u bytes (ovr says %u), picking the greatest\n",
1067 bytes_written, ovr);
1070 if (bytes_written == 0)
1074 LOG (GNUNET_ERROR_TYPE_DEBUG, "Wrote %u bytes, returning -1 with EAGAIN\n", bytes_written);
1076 return GNUNET_SYSERR;
1079 LOG (GNUNET_ERROR_TYPE_DEBUG, "Wrote %u bytes\n", bytes_written);
1085 return bytes_written;
1087 return write (h->fd, buffer, n);
1093 * Write a buffer to a file, blocking, if necessary.
1095 * @param h handle to open file
1096 * @param buffer the data to write
1097 * @param n number of bytes to write
1098 * @return number of bytes written on success, #GNUNET_SYSERR on error
1101 GNUNET_DISK_file_write_blocking (const struct GNUNET_DISK_FileHandle * h,
1108 return GNUNET_SYSERR;
1112 DWORD bytes_written;
1113 /* We do a non-overlapped write, which is as blocking as it gets */
1114 LOG (GNUNET_ERROR_TYPE_DEBUG, "Writing %u bytes\n", n);
1115 if (!WriteFile (h->h, buffer, n, &bytes_written, NULL))
1117 SetErrnoFromWinError (GetLastError ());
1118 LOG (GNUNET_ERROR_TYPE_DEBUG, "Error writing to pipe: %u\n",
1120 return GNUNET_SYSERR;
1122 if (bytes_written == 0 && n > 0)
1124 LOG (GNUNET_ERROR_TYPE_DEBUG, "Waiting for pipe to clean\n");
1125 WaitForSingleObject (h->h, INFINITE);
1126 if (!WriteFile (h->h, buffer, n, &bytes_written, NULL))
1128 SetErrnoFromWinError (GetLastError ());
1129 LOG (GNUNET_ERROR_TYPE_DEBUG, "Error writing to pipe: %u\n",
1131 return GNUNET_SYSERR;
1134 LOG (GNUNET_ERROR_TYPE_DEBUG,
1137 return bytes_written;
1142 /* set to blocking, write, then set back */
1143 flags = fcntl (h->fd, F_GETFL);
1144 if (0 != (flags & O_NONBLOCK))
1145 (void) fcntl (h->fd, F_SETFL, flags - O_NONBLOCK);
1146 ret = write (h->fd, buffer, n);
1147 if (0 == (flags & O_NONBLOCK))
1148 (void) fcntl (h->fd, F_SETFL, flags);
1155 * Write a buffer to a file. If the file is longer than the
1156 * number of bytes that will be written, it will be truncated.
1158 * @param fn file name
1159 * @param buffer the data to write
1160 * @param n number of bytes to write
1161 * @param mode file permissions
1162 * @return number of bytes written on success, #GNUNET_SYSERR on error
1165 GNUNET_DISK_fn_write (const char *fn, const void *buffer, size_t n,
1166 enum GNUNET_DISK_AccessPermissions mode)
1168 struct GNUNET_DISK_FileHandle *fh;
1171 fh = GNUNET_DISK_file_open (fn,
1172 GNUNET_DISK_OPEN_WRITE | GNUNET_DISK_OPEN_TRUNCATE
1173 | GNUNET_DISK_OPEN_CREATE, mode);
1175 return GNUNET_SYSERR;
1176 ret = GNUNET_DISK_file_write (fh, buffer, n);
1177 GNUNET_assert (GNUNET_OK == GNUNET_DISK_file_close (fh));
1183 * Scan a directory for files.
1185 * @param dir_name the name of the directory
1186 * @param callback the method to call for each file,
1187 * can be NULL, in that case, we only count
1188 * @param callback_cls closure for @a callback
1189 * @return the number of files found, #GNUNET_SYSERR on error or
1190 * ieration aborted by callback returning #GNUNET_SYSERR
1193 GNUNET_DISK_directory_scan (const char *dir_name,
1194 GNUNET_FileNameCallback callback,
1198 struct dirent *finfo;
1204 unsigned int name_len;
1205 unsigned int n_size;
1207 GNUNET_assert (NULL != dir_name);
1208 dname = GNUNET_STRINGS_filename_expand (dir_name);
1210 return GNUNET_SYSERR;
1211 while ((strlen (dname) > 0) && (dname[strlen (dname) - 1] == DIR_SEPARATOR))
1212 dname[strlen (dname) - 1] = '\0';
1213 if (0 != STAT (dname, &istat))
1215 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "stat", dname);
1216 GNUNET_free (dname);
1217 return GNUNET_SYSERR;
1219 if (! S_ISDIR (istat.st_mode))
1221 LOG (GNUNET_ERROR_TYPE_WARNING,
1222 _("Expected `%s' to be a directory!\n"),
1224 GNUNET_free (dname);
1225 return GNUNET_SYSERR;
1228 dinfo = OPENDIR (dname);
1229 if ((errno == EACCES) || (NULL == dinfo))
1231 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "opendir", dname);
1234 GNUNET_free (dname);
1235 return GNUNET_SYSERR;
1238 n_size = strlen (dname) + name_len + 2;
1239 name = GNUNET_malloc (n_size);
1240 while (NULL != (finfo = READDIR (dinfo)))
1242 if ((0 == strcmp (finfo->d_name, ".")) ||
1243 (0 == strcmp (finfo->d_name, "..")))
1245 if (NULL != callback)
1247 if (name_len < strlen (finfo->d_name))
1250 name_len = strlen (finfo->d_name);
1251 n_size = strlen (dname) + name_len + 2;
1252 name = GNUNET_malloc (n_size);
1254 /* dname can end in "/" only if dname == "/";
1255 * if dname does not end in "/", we need to add
1256 * a "/" (otherwise, we must not!) */
1257 GNUNET_snprintf (name, n_size, "%s%s%s", dname,
1258 (strcmp (dname, DIR_SEPARATOR_STR) ==
1259 0) ? "" : DIR_SEPARATOR_STR, finfo->d_name);
1260 ret = callback (callback_cls, name);
1261 if (GNUNET_OK != ret)
1265 GNUNET_free (dname);
1266 if (GNUNET_NO == ret)
1268 return GNUNET_SYSERR;
1275 GNUNET_free (dname);
1281 * Opaque handle used for iterating over a directory.
1283 struct GNUNET_DISK_DirectoryIterator
1287 * Function to call on directory entries.
1289 GNUNET_DISK_DirectoryIteratorCallback callback;
1292 * Closure for callback.
1297 * Reference to directory.
1307 * Next filename to process.
1314 enum GNUNET_SCHEDULER_Priority priority;
1320 * Task used by the directory iterator.
1323 directory_iterator_task (void *cls,
1324 const struct GNUNET_SCHEDULER_TaskContext *tc)
1326 struct GNUNET_DISK_DirectoryIterator *iter = cls;
1329 name = iter->next_name;
1330 GNUNET_assert (name != NULL);
1331 iter->next_name = NULL;
1332 iter->callback (iter->callback_cls, iter, name, iter->dirname);
1338 * This function must be called during the DiskIteratorCallback
1339 * (exactly once) to schedule the task to process the next
1340 * filename in the directory (if there is one).
1342 * @param iter opaque handle for the iterator
1343 * @param can set to GNUNET_YES to terminate the iteration early
1344 * @return GNUNET_YES if iteration will continue,
1345 * GNUNET_NO if this was the last entry (and iteration is complete),
1346 * GNUNET_SYSERR if abort was YES
1349 GNUNET_DISK_directory_iterator_next (struct GNUNET_DISK_DirectoryIterator *iter,
1352 struct dirent *finfo;
1354 GNUNET_assert (iter->next_name == NULL);
1355 if (can == GNUNET_YES)
1357 CLOSEDIR (iter->directory);
1358 GNUNET_free (iter->dirname);
1360 return GNUNET_SYSERR;
1362 while (NULL != (finfo = READDIR (iter->directory)))
1364 if ((0 == strcmp (finfo->d_name, ".")) ||
1365 (0 == strcmp (finfo->d_name, "..")))
1367 GNUNET_asprintf (&iter->next_name, "%s%s%s", iter->dirname,
1368 DIR_SEPARATOR_STR, finfo->d_name);
1373 GNUNET_DISK_directory_iterator_next (iter, GNUNET_YES);
1376 GNUNET_SCHEDULER_add_with_priority (iter->priority, &directory_iterator_task,
1383 * Scan a directory for files using the scheduler to run a task for
1384 * each entry. The name of the directory must be expanded first (!).
1385 * If a scheduler does not need to be used, GNUNET_DISK_directory_scan
1386 * may provide a simpler API.
1388 * @param prio priority to use
1389 * @param dir_name the name of the directory
1390 * @param callback the method to call for each file
1391 * @param callback_cls closure for callback
1392 * @return GNUNET_YES if directory is not empty and 'callback'
1393 * will be called later, GNUNET_NO otherwise, GNUNET_SYSERR on error.
1396 GNUNET_DISK_directory_iterator_start (enum GNUNET_SCHEDULER_Priority prio,
1397 const char *dir_name,
1398 GNUNET_DISK_DirectoryIteratorCallback
1399 callback, void *callback_cls)
1401 struct GNUNET_DISK_DirectoryIterator *di;
1403 di = GNUNET_new (struct GNUNET_DISK_DirectoryIterator);
1404 di->callback = callback;
1405 di->callback_cls = callback_cls;
1406 di->directory = OPENDIR (dir_name);
1407 if (di->directory == NULL)
1410 callback (callback_cls, NULL, NULL, NULL);
1411 return GNUNET_SYSERR;
1413 di->dirname = GNUNET_strdup (dir_name);
1414 di->priority = prio;
1415 return GNUNET_DISK_directory_iterator_next (di, GNUNET_NO);
1420 * Function that removes the given directory by calling
1421 * "GNUNET_DISK_directory_remove".
1423 * @param unused not used
1424 * @param fn directory to remove
1425 * @return #GNUNET_OK
1428 remove_helper (void *unused, const char *fn)
1430 (void) GNUNET_DISK_directory_remove (fn);
1436 * Remove all files in a directory (rm -rf). Call with
1439 * @param filename the file to remove
1440 * @return #GNUNET_OK on success, #GNUNET_SYSERR on error
1443 GNUNET_DISK_directory_remove (const char *filename)
1447 if (NULL == filename)
1450 return GNUNET_SYSERR;
1452 if (0 != LSTAT (filename, &istat))
1453 return GNUNET_NO; /* file may not exist... */
1454 (void) CHMOD (filename, S_IWUSR | S_IRUSR | S_IXUSR);
1455 if (UNLINK (filename) == 0)
1457 if ((errno != EISDIR) &&
1458 /* EISDIR is not sufficient in all cases, e.g.
1459 * sticky /tmp directory may result in EPERM on BSD.
1460 * So we also explicitly check "isDirectory" */
1461 (GNUNET_YES != GNUNET_DISK_directory_test (filename, GNUNET_YES)))
1463 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "rmdir", filename);
1464 return GNUNET_SYSERR;
1466 if (GNUNET_SYSERR ==
1467 GNUNET_DISK_directory_scan (filename, &remove_helper, NULL))
1468 return GNUNET_SYSERR;
1469 if (0 != RMDIR (filename))
1471 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "rmdir", filename);
1472 return GNUNET_SYSERR;
1481 * @param src file to copy
1482 * @param dst destination file name
1483 * @return #GNUNET_OK on success, #GNUNET_SYSERR on error
1486 GNUNET_DISK_file_copy (const char *src,
1493 struct GNUNET_DISK_FileHandle *in;
1494 struct GNUNET_DISK_FileHandle *out;
1496 if (GNUNET_OK != GNUNET_DISK_file_size (src, &size, GNUNET_YES, GNUNET_YES))
1497 return GNUNET_SYSERR;
1499 in = GNUNET_DISK_file_open (src, GNUNET_DISK_OPEN_READ,
1500 GNUNET_DISK_PERM_NONE);
1502 return GNUNET_SYSERR;
1504 GNUNET_DISK_file_open (dst,
1505 GNUNET_DISK_OPEN_WRITE | GNUNET_DISK_OPEN_CREATE |
1506 GNUNET_DISK_OPEN_FAILIFEXISTS,
1507 GNUNET_DISK_PERM_USER_READ |
1508 GNUNET_DISK_PERM_USER_WRITE |
1509 GNUNET_DISK_PERM_GROUP_READ |
1510 GNUNET_DISK_PERM_GROUP_WRITE);
1513 GNUNET_DISK_file_close (in);
1514 return GNUNET_SYSERR;
1516 buf = GNUNET_malloc (COPY_BLK_SIZE);
1519 len = COPY_BLK_SIZE;
1520 if (len > size - pos)
1522 if (len != GNUNET_DISK_file_read (in, buf, len))
1524 if (len != GNUNET_DISK_file_write (out, buf, len))
1529 GNUNET_DISK_file_close (in);
1530 GNUNET_DISK_file_close (out);
1534 GNUNET_DISK_file_close (in);
1535 GNUNET_DISK_file_close (out);
1536 return GNUNET_SYSERR;
1541 * @brief Removes special characters as ':' from a filename.
1542 * @param fn the filename to canonicalize
1545 GNUNET_DISK_filename_canonicalize (char *fn)
1555 if (c == '/' || c == '\\' || c == ':' || c == '*' || c == '?' || c == '"' ||
1556 c == '<' || c == '>' || c == '|')
1568 * @brief Change owner of a file
1570 * @param filename name of file to change the owner of
1571 * @param user name of the new owner
1572 * @return GNUNET_OK on success, GNUNET_SYSERR on failure
1575 GNUNET_DISK_file_change_owner (const char *filename, const char *user)
1580 pws = getpwnam (user);
1583 LOG (GNUNET_ERROR_TYPE_ERROR,
1584 _("Cannot obtain information about user `%s': %s\n"), user,
1586 return GNUNET_SYSERR;
1588 if (0 != chown (filename, pws->pw_uid, pws->pw_gid))
1589 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "chown", filename);
1596 * Lock a part of a file
1597 * @param fh file handle
1598 * @param lock_start absolute position from where to lock
1599 * @param lock_end absolute position until where to lock
1600 * @param excl GNUNET_YES for an exclusive lock
1601 * @return GNUNET_OK on success, GNUNET_SYSERR on error
1604 GNUNET_DISK_file_lock (struct GNUNET_DISK_FileHandle *fh, off_t lock_start,
1605 off_t lock_end, int excl)
1610 return GNUNET_SYSERR;
1616 memset (&fl, 0, sizeof (struct flock));
1617 fl.l_type = excl ? F_WRLCK : F_RDLCK;
1618 fl.l_whence = SEEK_SET;
1619 fl.l_start = lock_start;
1620 fl.l_len = lock_end;
1622 return fcntl (fh->fd, F_SETLK, &fl) != 0 ? GNUNET_SYSERR : GNUNET_OK;
1625 off_t diff = lock_end - lock_start;
1626 DWORD diff_low, diff_high;
1627 diff_low = (DWORD) (diff & 0xFFFFFFFF);
1628 diff_high = (DWORD) ((diff >> (sizeof (DWORD) * 8)) & 0xFFFFFFFF);
1630 memset (&o, 0, sizeof (OVERLAPPED));
1631 o.Offset = (DWORD) (lock_start & 0xFFFFFFFF);;
1632 o.OffsetHigh = (DWORD) (((lock_start & ~0xFFFFFFFF) >> (sizeof (DWORD) * 8)) & 0xFFFFFFFF);
1635 (fh->h, (excl ? LOCKFILE_EXCLUSIVE_LOCK : 0) | LOCKFILE_FAIL_IMMEDIATELY,
1636 0, diff_low, diff_high, &o))
1638 SetErrnoFromWinError (GetLastError ());
1639 return GNUNET_SYSERR;
1648 * Unlock a part of a file
1649 * @param fh file handle
1650 * @param unlock_start absolute position from where to unlock
1651 * @param unlock_end absolute position until where to unlock
1652 * @return #GNUNET_OK on success, #GNUNET_SYSERR on error
1655 GNUNET_DISK_file_unlock (struct GNUNET_DISK_FileHandle *fh, off_t unlock_start,
1661 return GNUNET_SYSERR;
1667 memset (&fl, 0, sizeof (struct flock));
1668 fl.l_type = F_UNLCK;
1669 fl.l_whence = SEEK_SET;
1670 fl.l_start = unlock_start;
1671 fl.l_len = unlock_end;
1673 return fcntl (fh->fd, F_SETLK, &fl) != 0 ? GNUNET_SYSERR : GNUNET_OK;
1676 off_t diff = unlock_end - unlock_start;
1677 DWORD diff_low, diff_high;
1678 diff_low = (DWORD) (diff & 0xFFFFFFFF);
1679 diff_high = (DWORD) ((diff >> (sizeof (DWORD) * 8)) & 0xFFFFFFFF);
1681 memset (&o, 0, sizeof (OVERLAPPED));
1682 o.Offset = (DWORD) (unlock_start & 0xFFFFFFFF);;
1683 o.OffsetHigh = (DWORD) (((unlock_start & ~0xFFFFFFFF) >> (sizeof (DWORD) * 8)) & 0xFFFFFFFF);
1685 if (!UnlockFileEx (fh->h, 0, diff_low, diff_high, &o))
1687 SetErrnoFromWinError (GetLastError ());
1688 return GNUNET_SYSERR;
1697 * Open a file. Note that the access permissions will only be
1698 * used if a new file is created and if the underlying operating
1699 * system supports the given permissions.
1701 * @param fn file name to be opened
1702 * @param flags opening flags, a combination of GNUNET_DISK_OPEN_xxx bit flags
1703 * @param perm permissions for the newly created file, use
1704 * #GNUNET_DISK_PERM_NONE if a file could not be created by this
1705 * call (because of flags)
1706 * @return IO handle on success, NULL on error
1708 struct GNUNET_DISK_FileHandle *
1709 GNUNET_DISK_file_open (const char *fn,
1710 enum GNUNET_DISK_OpenFlags flags,
1711 enum GNUNET_DISK_AccessPermissions perm)
1714 struct GNUNET_DISK_FileHandle *ret;
1720 wchar_t wexpfn[MAX_PATH + 1];
1727 expfn = GNUNET_STRINGS_filename_expand (fn);
1732 if (GNUNET_DISK_OPEN_READWRITE == (flags & GNUNET_DISK_OPEN_READWRITE))
1733 oflags = O_RDWR; /* note: O_RDWR is NOT always O_RDONLY | O_WRONLY */
1734 else if (flags & GNUNET_DISK_OPEN_READ)
1736 else if (flags & GNUNET_DISK_OPEN_WRITE)
1741 GNUNET_free (expfn);
1744 if (flags & GNUNET_DISK_OPEN_FAILIFEXISTS)
1745 oflags |= (O_CREAT | O_EXCL);
1746 if (flags & GNUNET_DISK_OPEN_TRUNCATE)
1748 if (flags & GNUNET_DISK_OPEN_APPEND)
1750 if (flags & GNUNET_DISK_OPEN_CREATE)
1752 (void) GNUNET_DISK_directory_create_for_file (expfn);
1754 mode = translate_unix_perms (perm);
1757 fd = open (expfn, oflags
1761 | O_LARGEFILE, mode);
1764 if (0 == (flags & GNUNET_DISK_OPEN_FAILIFEXISTS))
1765 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "open", expfn);
1767 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_DEBUG, "open", expfn);
1768 GNUNET_free (expfn);
1775 if (GNUNET_DISK_OPEN_READWRITE == (flags & GNUNET_DISK_OPEN_READWRITE))
1776 access = FILE_READ_DATA | FILE_WRITE_DATA;
1777 else if (flags & GNUNET_DISK_OPEN_READ)
1778 access = FILE_READ_DATA;
1779 else if (flags & GNUNET_DISK_OPEN_WRITE)
1780 access = FILE_WRITE_DATA;
1782 if (flags & GNUNET_DISK_OPEN_FAILIFEXISTS)
1786 else if (flags & GNUNET_DISK_OPEN_CREATE)
1788 (void) GNUNET_DISK_directory_create_for_file (expfn);
1789 if (flags & GNUNET_DISK_OPEN_TRUNCATE)
1790 disp = CREATE_ALWAYS;
1794 else if (flags & GNUNET_DISK_OPEN_TRUNCATE)
1796 disp = TRUNCATE_EXISTING;
1800 disp = OPEN_EXISTING;
1803 if (ERROR_SUCCESS == plibc_conv_to_win_pathwconv(expfn, wexpfn))
1804 h = CreateFileW (wexpfn, access,
1805 FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE, NULL,
1806 disp, FILE_ATTRIBUTE_NORMAL, NULL);
1808 h = INVALID_HANDLE_VALUE;
1809 if (h == INVALID_HANDLE_VALUE)
1812 SetErrnoFromWinError (GetLastError ());
1814 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_INFO, "open", expfn);
1815 GNUNET_free (expfn);
1820 if (flags & GNUNET_DISK_OPEN_APPEND)
1821 if (SetFilePointer (h, 0, 0, FILE_END) == INVALID_SET_FILE_POINTER)
1823 SetErrnoFromWinError (GetLastError ());
1824 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "SetFilePointer", expfn);
1826 GNUNET_free (expfn);
1831 ret = GNUNET_new (struct GNUNET_DISK_FileHandle);
1834 ret->type = GNUNET_DISK_HANLDE_TYPE_FILE;
1838 GNUNET_free (expfn);
1844 * Close an open file
1845 * @param h file handle
1846 * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
1849 GNUNET_DISK_file_close (struct GNUNET_DISK_FileHandle *h)
1855 return GNUNET_SYSERR;
1861 if (!CloseHandle (h->h))
1863 SetErrnoFromWinError (GetLastError ());
1864 LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING, "close");
1865 ret = GNUNET_SYSERR;
1867 if (h->oOverlapRead)
1869 if (!CloseHandle (h->oOverlapRead->hEvent))
1871 SetErrnoFromWinError (GetLastError ());
1872 LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING, "close");
1873 ret = GNUNET_SYSERR;
1875 GNUNET_free (h->oOverlapRead);
1877 if (h->oOverlapWrite)
1879 if (!CloseHandle (h->oOverlapWrite->hEvent))
1881 SetErrnoFromWinError (GetLastError ());
1882 LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING, "close");
1883 ret = GNUNET_SYSERR;
1885 GNUNET_free (h->oOverlapWrite);
1888 if (close (h->fd) != 0)
1890 LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING, "close");
1891 ret = GNUNET_SYSERR;
1900 * Get a GNUnet file handle from a W32 handle.
1902 * @param handle native handle
1903 * @return GNUnet file handle corresponding to the W32 handle
1905 struct GNUNET_DISK_FileHandle *
1906 GNUNET_DISK_get_handle_from_w32_handle (HANDLE osfh)
1908 struct GNUNET_DISK_FileHandle *fh;
1911 enum GNUNET_FILE_Type ftype;
1913 dwret = GetFileType (osfh);
1916 case FILE_TYPE_DISK:
1917 ftype = GNUNET_DISK_HANLDE_TYPE_FILE;
1919 case FILE_TYPE_PIPE:
1920 ftype = GNUNET_DISK_HANLDE_TYPE_PIPE;
1922 case FILE_TYPE_UNKNOWN:
1923 if (GetLastError () == NO_ERROR || GetLastError () == ERROR_INVALID_HANDLE)
1925 if (0 != ResetEvent (osfh))
1926 ftype = GNUNET_DISK_HANLDE_TYPE_EVENT;
1935 fh = GNUNET_new (struct GNUNET_DISK_FileHandle);
1939 if (ftype == GNUNET_DISK_HANLDE_TYPE_PIPE)
1942 * Note that we can't make it overlapped if it isn't already.
1943 * (ReOpenFile() is only available in 2003/Vista).
1944 * The process that opened this file in the first place (usually a parent
1945 * process, if this is stdin/stdout/stderr) must make it overlapped,
1946 * otherwise we're screwed, as selecting on non-overlapped handle
1949 fh->oOverlapRead = GNUNET_new (OVERLAPPED);
1950 fh->oOverlapWrite = GNUNET_new (OVERLAPPED);
1951 fh->oOverlapRead->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
1952 fh->oOverlapWrite->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
1960 * Get a handle from a native integer FD.
1962 * @param fno native integer file descriptor
1963 * @return file handle corresponding to the descriptor, NULL on error
1965 struct GNUNET_DISK_FileHandle *
1966 GNUNET_DISK_get_handle_from_int_fd (int fno)
1968 struct GNUNET_DISK_FileHandle *fh;
1970 if ( (((off_t) -1) == lseek (fno, 0, SEEK_CUR)) &&
1972 return NULL; /* invalid FD */
1975 fh = GNUNET_new (struct GNUNET_DISK_FileHandle);
1981 osfh = _get_osfhandle (fno);
1982 if (INVALID_HANDLE_VALUE == (HANDLE) osfh)
1985 fh = GNUNET_DISK_get_handle_from_w32_handle ((HANDLE) osfh);
1993 * Get a handle from a native streaming FD.
1995 * @param fd native streaming file descriptor
1996 * @return file handle corresponding to the descriptor
1998 struct GNUNET_DISK_FileHandle *
1999 GNUNET_DISK_get_handle_from_native (FILE *fd)
2007 return GNUNET_DISK_get_handle_from_int_fd (fno);
2012 * Handle for a memory-mapping operation.
2014 struct GNUNET_DISK_MapHandle
2017 * Address where the map is in memory.
2023 * Underlying OS handle.
2028 * Number of bytes mapped.
2036 #define MAP_FAILED ((void *) -1)
2040 * Map a file into memory
2042 * @param h open file handle
2043 * @param m handle to the new mapping
2044 * @param access access specification, GNUNET_DISK_MAP_TYPE_xxx
2045 * @param len size of the mapping
2046 * @return pointer to the mapped memory region, NULL on failure
2049 GNUNET_DISK_file_map (const struct GNUNET_DISK_FileHandle *h,
2050 struct GNUNET_DISK_MapHandle **m,
2051 enum GNUNET_DISK_MapType access, size_t len)
2060 DWORD mapAccess, protect;
2062 if ((access & GNUNET_DISK_MAP_TYPE_READ) &&
2063 (access & GNUNET_DISK_MAP_TYPE_WRITE))
2065 protect = PAGE_READWRITE;
2066 mapAccess = FILE_MAP_ALL_ACCESS;
2068 else if (access & GNUNET_DISK_MAP_TYPE_READ)
2070 protect = PAGE_READONLY;
2071 mapAccess = FILE_MAP_READ;
2073 else if (access & GNUNET_DISK_MAP_TYPE_WRITE)
2075 protect = PAGE_READWRITE;
2076 mapAccess = FILE_MAP_WRITE;
2084 *m = GNUNET_new (struct GNUNET_DISK_MapHandle);
2085 (*m)->h = CreateFileMapping (h->h, NULL, protect, 0, 0, NULL);
2086 if ((*m)->h == INVALID_HANDLE_VALUE)
2088 SetErrnoFromWinError (GetLastError ());
2093 (*m)->addr = MapViewOfFile ((*m)->h, mapAccess, 0, 0, len);
2096 SetErrnoFromWinError (GetLastError ());
2097 CloseHandle ((*m)->h);
2106 if (access & GNUNET_DISK_MAP_TYPE_READ)
2108 if (access & GNUNET_DISK_MAP_TYPE_WRITE)
2110 *m = GNUNET_new (struct GNUNET_DISK_MapHandle);
2111 (*m)->addr = mmap (NULL, len, prot, MAP_SHARED, h->fd, 0);
2112 GNUNET_assert (NULL != (*m)->addr);
2113 if (MAP_FAILED == (*m)->addr)
2125 * @param h mapping handle
2126 * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
2129 GNUNET_DISK_file_unmap (struct GNUNET_DISK_MapHandle *h)
2136 return GNUNET_SYSERR;
2140 ret = UnmapViewOfFile (h->addr) ? GNUNET_OK : GNUNET_SYSERR;
2141 if (ret != GNUNET_OK)
2142 SetErrnoFromWinError (GetLastError ());
2143 if (!CloseHandle (h->h) && (ret == GNUNET_OK))
2145 ret = GNUNET_SYSERR;
2146 SetErrnoFromWinError (GetLastError ());
2149 ret = munmap (h->addr, h->len) != -1 ? GNUNET_OK : GNUNET_SYSERR;
2157 * Write file changes to disk
2158 * @param h handle to an open file
2159 * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
2162 GNUNET_DISK_file_sync (const struct GNUNET_DISK_FileHandle *h)
2167 return GNUNET_SYSERR;
2173 ret = FlushFileBuffers (h->h) ? GNUNET_OK : GNUNET_SYSERR;
2174 if (ret != GNUNET_OK)
2175 SetErrnoFromWinError (GetLastError ());
2177 #elif defined(FREEBSD) || defined(OPENBSD) || defined(DARWIN)
2178 return fsync (h->fd) == -1 ? GNUNET_SYSERR : GNUNET_OK;
2180 return fdatasync (h->fd) == -1 ? GNUNET_SYSERR : GNUNET_OK;
2187 #define PIPE_BUF 512
2189 /* Copyright Bob Byrnes <byrnes <at> curl.com>
2190 http://permalink.gmane.org/gmane.os.cygwin.patches/2121
2192 /* Create a pipe, and return handles to the read and write ends,
2193 just like CreatePipe, but ensure that the write end permits
2194 FILE_READ_ATTRIBUTES access, on later versions of win32 where
2195 this is supported. This access is needed by NtQueryInformationFile,
2196 which is used to implement select and nonblocking writes.
2197 Note that the return value is either NO_ERROR or GetLastError,
2198 unlike CreatePipe, which returns a bool for success or failure. */
2200 create_selectable_pipe (PHANDLE read_pipe_ptr, PHANDLE write_pipe_ptr,
2201 LPSECURITY_ATTRIBUTES sa_ptr, DWORD psize,
2202 DWORD dwReadMode, DWORD dwWriteMode)
2204 /* Default to error. */
2205 *read_pipe_ptr = *write_pipe_ptr = INVALID_HANDLE_VALUE;
2210 /* Ensure that there is enough pipe buffer space for atomic writes. */
2211 if (psize < PIPE_BUF)
2214 char pipename[MAX_PATH];
2216 /* Retry CreateNamedPipe as long as the pipe name is in use.
2217 * Retrying will probably never be necessary, but we want
2218 * to be as robust as possible. */
2221 static volatile LONG pipe_unique_id;
2223 snprintf (pipename, sizeof pipename, "\\\\.\\pipe\\gnunet-%d-%ld",
2224 getpid (), InterlockedIncrement ((LONG *) & pipe_unique_id));
2225 LOG (GNUNET_ERROR_TYPE_DEBUG, "CreateNamedPipe: name = %s, size = %lu\n",
2227 /* Use CreateNamedPipe instead of CreatePipe, because the latter
2228 * returns a write handle that does not permit FILE_READ_ATTRIBUTES
2229 * access, on versions of win32 earlier than WinXP SP2.
2230 * CreatePipe also stupidly creates a full duplex pipe, which is
2231 * a waste, since only a single direction is actually used.
2232 * It's important to only allow a single instance, to ensure that
2233 * the pipe was not created earlier by some other process, even if
2234 * the pid has been reused. */
2235 read_pipe = CreateNamedPipeA (pipename, PIPE_ACCESS_INBOUND | FILE_FLAG_FIRST_PIPE_INSTANCE | dwReadMode, PIPE_TYPE_BYTE | PIPE_READMODE_BYTE, 1, /* max instances */
2236 psize, /* output buffer size */
2237 psize, /* input buffer size */
2238 NMPWAIT_USE_DEFAULT_WAIT, sa_ptr);
2240 if (read_pipe != INVALID_HANDLE_VALUE)
2242 LOG (GNUNET_ERROR_TYPE_DEBUG, "pipe read handle = %p\n", read_pipe);
2246 DWORD err = GetLastError ();
2250 case ERROR_PIPE_BUSY:
2251 /* The pipe is already open with compatible parameters.
2252 * Pick a new name and retry. */
2253 LOG (GNUNET_ERROR_TYPE_DEBUG, "pipe busy, retrying\n");
2255 case ERROR_ACCESS_DENIED:
2256 /* The pipe is already open with incompatible parameters.
2257 * Pick a new name and retry. */
2258 LOG (GNUNET_ERROR_TYPE_DEBUG, "pipe access denied, retrying\n");
2260 case ERROR_CALL_NOT_IMPLEMENTED:
2261 /* We are on an older Win9x platform without named pipes.
2262 * Return an anonymous pipe as the best approximation. */
2263 LOG (GNUNET_ERROR_TYPE_DEBUG,
2264 "CreateNamedPipe not implemented, resorting to "
2265 "CreatePipe: size = %lu\n", psize);
2266 if (CreatePipe (read_pipe_ptr, write_pipe_ptr, sa_ptr, psize))
2268 LOG (GNUNET_ERROR_TYPE_DEBUG, "pipe read handle = %p, write handle = %p\n",
2273 err = GetLastError ();
2274 LOG (GNUNET_ERROR_TYPE_ERROR, "CreatePipe failed: %d\n", err);
2277 LOG (GNUNET_ERROR_TYPE_ERROR, "CreateNamedPipe failed: %d\n", err);
2282 LOG (GNUNET_ERROR_TYPE_DEBUG, "CreateFile: name = %s\n", pipename);
2284 /* Open the named pipe for writing.
2285 * Be sure to permit FILE_READ_ATTRIBUTES access. */
2286 write_pipe = CreateFileA (pipename, GENERIC_WRITE | FILE_READ_ATTRIBUTES, 0, /* share mode */
2287 sa_ptr, OPEN_EXISTING, dwWriteMode, /* flags and attributes */
2288 0); /* handle to template file */
2290 if (write_pipe == INVALID_HANDLE_VALUE)
2293 DWORD err = GetLastError ();
2295 LOG (GNUNET_ERROR_TYPE_DEBUG, "CreateFile failed: %d\n", err);
2296 CloseHandle (read_pipe);
2299 LOG (GNUNET_ERROR_TYPE_DEBUG, "pipe write handle = %p\n", write_pipe);
2301 *read_pipe_ptr = read_pipe;
2302 *write_pipe_ptr = write_pipe;
2309 * Creates an interprocess channel
2311 * @param blocking_read creates an asynchronous pipe for reading if set to GNUNET_NO
2312 * @param blocking_write creates an asynchronous pipe for writing if set to GNUNET_NO
2313 * @param inherit_read inherit the parent processes stdin (only for windows)
2314 * @param inherit_write inherit the parent processes stdout (only for windows)
2315 * @return handle to the new pipe, NULL on error
2317 struct GNUNET_DISK_PipeHandle *
2318 GNUNET_DISK_pipe (int blocking_read, int blocking_write, int inherit_read, int inherit_write)
2329 LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR, "pipe");
2333 return GNUNET_DISK_pipe_from_fd (blocking_read,
2337 struct GNUNET_DISK_PipeHandle *p;
2342 p = GNUNET_new (struct GNUNET_DISK_PipeHandle);
2343 p->fd[0] = GNUNET_new (struct GNUNET_DISK_FileHandle);
2344 p->fd[1] = GNUNET_new (struct GNUNET_DISK_FileHandle);
2346 /* All pipes are overlapped. If you want them to block - just
2347 * call WriteFile() and ReadFile() with NULL overlapped pointer.
2348 * NOTE: calling with NULL overlapped pointer works only
2349 * for pipes, and doesn't seem to be a documented feature.
2350 * It will NOT work for files, because overlapped files need
2351 * to read offsets from the overlapped structure, regardless.
2352 * Pipes are not seekable, and need no offsets, which is
2353 * probably why it works for them.
2356 create_selectable_pipe (&p->fd[0]->h, &p->fd[1]->h, NULL, 0,
2357 FILE_FLAG_OVERLAPPED,
2358 FILE_FLAG_OVERLAPPED);
2361 SetErrnoFromWinError (GetLastError ());
2363 GNUNET_free (p->fd[0]);
2364 GNUNET_free (p->fd[1]);
2369 if (!DuplicateHandle
2370 (GetCurrentProcess (), p->fd[0]->h, GetCurrentProcess (), &tmp_handle, 0,
2371 inherit_read == GNUNET_YES ? TRUE : FALSE, DUPLICATE_SAME_ACCESS))
2373 SetErrnoFromWinError (GetLastError ());
2375 CloseHandle (p->fd[0]->h);
2376 CloseHandle (p->fd[1]->h);
2377 GNUNET_free (p->fd[0]);
2378 GNUNET_free (p->fd[1]);
2383 CloseHandle (p->fd[0]->h);
2384 p->fd[0]->h = tmp_handle;
2386 if (!DuplicateHandle
2387 (GetCurrentProcess (), p->fd[1]->h, GetCurrentProcess (), &tmp_handle, 0,
2388 inherit_write == GNUNET_YES ? TRUE : FALSE, DUPLICATE_SAME_ACCESS))
2390 SetErrnoFromWinError (GetLastError ());
2392 CloseHandle (p->fd[0]->h);
2393 CloseHandle (p->fd[1]->h);
2394 GNUNET_free (p->fd[0]);
2395 GNUNET_free (p->fd[1]);
2400 CloseHandle (p->fd[1]->h);
2401 p->fd[1]->h = tmp_handle;
2403 p->fd[0]->type = GNUNET_DISK_HANLDE_TYPE_PIPE;
2404 p->fd[1]->type = GNUNET_DISK_HANLDE_TYPE_PIPE;
2406 p->fd[0]->oOverlapRead = GNUNET_new (OVERLAPPED);
2407 p->fd[0]->oOverlapWrite = GNUNET_new (OVERLAPPED);
2408 p->fd[1]->oOverlapRead = GNUNET_new (OVERLAPPED);
2409 p->fd[1]->oOverlapWrite = GNUNET_new (OVERLAPPED);
2411 p->fd[0]->oOverlapRead->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2412 p->fd[0]->oOverlapWrite->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2414 p->fd[1]->oOverlapRead->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2415 p->fd[1]->oOverlapWrite->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2423 * Creates a pipe object from a couple of file descriptors.
2424 * Useful for wrapping existing pipe FDs.
2426 * @param blocking_read creates an asynchronous pipe for reading if set to GNUNET_NO
2427 * @param blocking_write creates an asynchronous pipe for writing if set to GNUNET_NO
2428 * @param fd an array of two fd values. One of them may be -1 for read-only or write-only pipes
2430 * @return handle to the new pipe, NULL on error
2432 struct GNUNET_DISK_PipeHandle *
2433 GNUNET_DISK_pipe_from_fd (int blocking_read, int blocking_write, int fd[2])
2435 struct GNUNET_DISK_PipeHandle *p;
2437 p = GNUNET_new (struct GNUNET_DISK_PipeHandle);
2442 int eno = 0; /* make gcc happy */
2447 p->fd[0] = GNUNET_new (struct GNUNET_DISK_FileHandle);
2448 p->fd[0]->fd = fd[0];
2451 flags = fcntl (fd[0], F_GETFL);
2452 flags |= O_NONBLOCK;
2453 if (0 > fcntl (fd[0], F_SETFL, flags))
2459 flags = fcntl (fd[0], F_GETFD);
2460 flags |= FD_CLOEXEC;
2461 if (0 > fcntl (fd[0], F_SETFD, flags))
2470 p->fd[1] = GNUNET_new (struct GNUNET_DISK_FileHandle);
2471 p->fd[1]->fd = fd[1];
2472 if (!blocking_write)
2474 flags = fcntl (fd[1], F_GETFL);
2475 flags |= O_NONBLOCK;
2476 if (0 > fcntl (fd[1], F_SETFL, flags))
2482 flags = fcntl (fd[1], F_GETFD);
2483 flags |= FD_CLOEXEC;
2484 if (0 > fcntl (fd[1], F_SETFD, flags))
2493 LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR, "fcntl");
2494 if (p->fd[0]->fd >= 0)
2495 GNUNET_break (0 == close (p->fd[0]->fd));
2496 if (p->fd[1]->fd >= 0)
2497 GNUNET_break (0 == close (p->fd[1]->fd));
2498 GNUNET_free_non_null (p->fd[0]);
2499 GNUNET_free_non_null (p->fd[1]);
2507 p->fd[0] = GNUNET_new (struct GNUNET_DISK_FileHandle);
2508 p->fd[0]->h = (HANDLE) _get_osfhandle (fd[0]);
2509 if (p->fd[0]->h != INVALID_HANDLE_VALUE)
2511 p->fd[0]->type = GNUNET_DISK_HANLDE_TYPE_PIPE;
2512 p->fd[0]->oOverlapRead = GNUNET_new (OVERLAPPED);
2513 p->fd[0]->oOverlapWrite = GNUNET_new (OVERLAPPED);
2514 p->fd[0]->oOverlapRead->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2515 p->fd[0]->oOverlapWrite->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2519 GNUNET_free (p->fd[0]);
2525 p->fd[1] = GNUNET_new (struct GNUNET_DISK_FileHandle);
2526 p->fd[1]->h = (HANDLE) _get_osfhandle (fd[1]);
2527 if (p->fd[1]->h != INVALID_HANDLE_VALUE)
2529 p->fd[1]->type = GNUNET_DISK_HANLDE_TYPE_PIPE;
2530 p->fd[1]->oOverlapRead = GNUNET_new (OVERLAPPED);
2531 p->fd[1]->oOverlapWrite = GNUNET_new (OVERLAPPED);
2532 p->fd[1]->oOverlapRead->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2533 p->fd[1]->oOverlapWrite->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2537 GNUNET_free (p->fd[1]);
2548 * Closes an interprocess channel
2550 * @param p pipe to close
2551 * @param end which end of the pipe to close
2552 * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
2555 GNUNET_DISK_pipe_close_end (struct GNUNET_DISK_PipeHandle *p,
2556 enum GNUNET_DISK_PipeEnd end)
2558 int ret = GNUNET_OK;
2560 if (end == GNUNET_DISK_PIPE_END_READ)
2564 ret = GNUNET_DISK_file_close (p->fd[0]);
2568 else if (end == GNUNET_DISK_PIPE_END_WRITE)
2572 ret = GNUNET_DISK_file_close (p->fd[1]);
2581 * Detaches one of the ends from the pipe.
2582 * Detached end is a fully-functional FileHandle, it will
2583 * not be affected by anything you do with the pipe afterwards.
2584 * Each end of a pipe can only be detched from it once (i.e.
2585 * it is not duplicated).
2587 * @param p pipe to detach an end from
2588 * @param end which end of the pipe to detach
2589 * @return Detached end on success, NULL on failure
2590 * (or if that end is not present or is closed).
2592 struct GNUNET_DISK_FileHandle *
2593 GNUNET_DISK_pipe_detach_end (struct GNUNET_DISK_PipeHandle *p,
2594 enum GNUNET_DISK_PipeEnd end)
2596 struct GNUNET_DISK_FileHandle *ret = NULL;
2598 if (end == GNUNET_DISK_PIPE_END_READ)
2606 else if (end == GNUNET_DISK_PIPE_END_WRITE)
2620 * Closes an interprocess channel
2622 * @param p pipe to close
2623 * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
2626 GNUNET_DISK_pipe_close (struct GNUNET_DISK_PipeHandle *p)
2628 int ret = GNUNET_OK;
2631 int write_end_close;
2632 int read_end_close_errno;
2633 int write_end_close_errno;
2635 read_end_close = GNUNET_DISK_pipe_close_end (p, GNUNET_DISK_PIPE_END_READ);
2636 read_end_close_errno = errno;
2637 write_end_close = GNUNET_DISK_pipe_close_end (p, GNUNET_DISK_PIPE_END_WRITE);
2638 write_end_close_errno = errno;
2641 if (GNUNET_OK != read_end_close)
2643 errno = read_end_close_errno;
2644 ret = read_end_close;
2646 else if (GNUNET_OK != write_end_close)
2648 errno = write_end_close_errno;
2649 ret = write_end_close;
2657 * Get the handle to a particular pipe end
2660 * @param n end to access
2661 * @return handle for the respective end
2663 const struct GNUNET_DISK_FileHandle *
2664 GNUNET_DISK_pipe_handle (const struct GNUNET_DISK_PipeHandle *p,
2665 enum GNUNET_DISK_PipeEnd n)
2669 case GNUNET_DISK_PIPE_END_READ:
2670 case GNUNET_DISK_PIPE_END_WRITE:
2680 * Retrieve OS file handle
2682 * @param fh GNUnet file descriptor
2683 * @param dst destination buffer
2684 * @param dst_len length of dst
2685 * @return #GNUNET_OK on success, #GNUNET_SYSERR otherwise
2688 GNUNET_DISK_internal_file_handle_ (const struct GNUNET_DISK_FileHandle *fh,
2689 void *dst, size_t dst_len)
2692 return GNUNET_SYSERR;
2694 if (dst_len < sizeof (HANDLE))
2695 return GNUNET_SYSERR;
2696 *((HANDLE *) dst) = fh->h;
2698 if (dst_len < sizeof (int))
2699 return GNUNET_SYSERR;
2700 *((int *) dst) = fh->fd;