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_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, "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);
886 return read (h->fd, result, len);
892 * Read the contents of a binary file into a buffer.
893 * Guarantees not to block (returns GNUNET_SYSERR and sets errno to EAGAIN
894 * when no data can be read).
896 * @param h handle to an open file
897 * @param result the buffer to write the result to
898 * @param len the maximum number of bytes to read
899 * @return the number of bytes read on success, #GNUNET_SYSERR on failure
902 GNUNET_DISK_file_read_non_blocking (const struct GNUNET_DISK_FileHandle *h,
909 return GNUNET_SYSERR;
915 if (h->type == GNUNET_DISK_HANLDE_TYPE_FILE)
917 if (!ReadFile (h->h, result, len, &bytes_read, NULL))
919 SetErrnoFromWinError (GetLastError ());
920 return GNUNET_SYSERR;
923 else if (h->type == GNUNET_DISK_HANLDE_TYPE_PIPE)
925 if (!ReadFile (h->h, result, len, &bytes_read, h->oOverlapRead))
927 if (GetLastError () != ERROR_IO_PENDING)
929 LOG (GNUNET_ERROR_TYPE_DEBUG, "Error reading from pipe: %u\n", GetLastError ());
930 SetErrnoFromWinError (GetLastError ());
931 return GNUNET_SYSERR;
935 LOG (GNUNET_ERROR_TYPE_DEBUG,
936 "ReadFile() queued a read, cancelling\n");
939 return GNUNET_SYSERR;
942 LOG (GNUNET_ERROR_TYPE_DEBUG,
955 /* set to non-blocking, read, then set back */
956 flags = fcntl (h->fd, F_GETFL);
957 if (0 == (flags & O_NONBLOCK))
958 (void) fcntl (h->fd, F_SETFL, flags | O_NONBLOCK);
959 ret = read (h->fd, result, len);
960 if (0 == (flags & O_NONBLOCK))
963 (void) fcntl (h->fd, F_SETFL, flags);
972 * Read the contents of a binary file into a buffer.
974 * @param fn file name
975 * @param result the buffer to write the result to
976 * @param len the maximum number of bytes to read
977 * @return number of bytes read, #GNUNET_SYSERR on failure
980 GNUNET_DISK_fn_read (const char *fn,
984 struct GNUNET_DISK_FileHandle *fh;
987 fh = GNUNET_DISK_file_open (fn,
988 GNUNET_DISK_OPEN_READ,
989 GNUNET_DISK_PERM_NONE);
991 return GNUNET_SYSERR;
992 ret = GNUNET_DISK_file_read (fh, result, len);
993 GNUNET_assert (GNUNET_OK == GNUNET_DISK_file_close (fh));
1000 * Write a buffer to a file.
1002 * @param h handle to open file
1003 * @param buffer the data to write
1004 * @param n number of bytes to write
1005 * @return number of bytes written on success, #GNUNET_SYSERR on error
1008 GNUNET_DISK_file_write (const struct GNUNET_DISK_FileHandle * h,
1009 const void *buffer, size_t n)
1014 return GNUNET_SYSERR;
1018 DWORD bytes_written;
1020 if (h->type == GNUNET_DISK_HANLDE_TYPE_FILE)
1022 if (!WriteFile (h->h, buffer, n, &bytes_written, NULL))
1024 SetErrnoFromWinError (GetLastError ());
1025 return GNUNET_SYSERR;
1028 else if (h->type == GNUNET_DISK_HANLDE_TYPE_PIPE)
1030 LOG (GNUNET_ERROR_TYPE_DEBUG, "It is a pipe trying to write %u bytes\n", n);
1031 if (!WriteFile (h->h, buffer, n, &bytes_written, h->oOverlapWrite))
1033 if (GetLastError () != ERROR_IO_PENDING)
1035 SetErrnoFromWinError (GetLastError ());
1036 LOG (GNUNET_ERROR_TYPE_DEBUG, "Error writing to pipe: %u\n",
1038 return GNUNET_SYSERR;
1040 LOG (GNUNET_ERROR_TYPE_DEBUG, "Will get overlapped result\n");
1041 if (!GetOverlappedResult (h->h, h->oOverlapWrite, &bytes_written, TRUE))
1043 SetErrnoFromWinError (GetLastError ());
1044 LOG (GNUNET_ERROR_TYPE_DEBUG,
1045 "Error getting overlapped result while writing to pipe: %u\n",
1047 return GNUNET_SYSERR;
1053 if (!GetOverlappedResult (h->h, h->oOverlapWrite, &ovr, TRUE))
1055 LOG (GNUNET_ERROR_TYPE_DEBUG,
1056 "Error getting control overlapped result while writing to pipe: %u\n",
1061 LOG (GNUNET_ERROR_TYPE_DEBUG,
1062 "Wrote %u bytes (ovr says %u), picking the greatest\n",
1063 bytes_written, ovr);
1066 if (bytes_written == 0)
1070 LOG (GNUNET_ERROR_TYPE_DEBUG, "Wrote %u bytes, returning -1 with EAGAIN\n", bytes_written);
1072 return GNUNET_SYSERR;
1075 LOG (GNUNET_ERROR_TYPE_DEBUG, "Wrote %u bytes\n", bytes_written);
1081 return bytes_written;
1083 return write (h->fd, buffer, n);
1089 * Write a buffer to a file, blocking, if necessary.
1091 * @param h handle to open file
1092 * @param buffer the data to write
1093 * @param n number of bytes to write
1094 * @return number of bytes written on success, #GNUNET_SYSERR on error
1097 GNUNET_DISK_file_write_blocking (const struct GNUNET_DISK_FileHandle * h,
1104 return GNUNET_SYSERR;
1108 DWORD bytes_written;
1109 /* We do a non-overlapped write, which is as blocking as it gets */
1110 LOG (GNUNET_ERROR_TYPE_DEBUG, "Writing %u bytes\n", n);
1111 if (!WriteFile (h->h, buffer, n, &bytes_written, NULL))
1113 SetErrnoFromWinError (GetLastError ());
1114 LOG (GNUNET_ERROR_TYPE_DEBUG, "Error writing to pipe: %u\n",
1116 return GNUNET_SYSERR;
1118 if (bytes_written == 0 && n > 0)
1120 LOG (GNUNET_ERROR_TYPE_DEBUG, "Waiting for pipe to clean\n");
1121 WaitForSingleObject (h->h, INFINITE);
1122 if (!WriteFile (h->h, buffer, n, &bytes_written, NULL))
1124 SetErrnoFromWinError (GetLastError ());
1125 LOG (GNUNET_ERROR_TYPE_DEBUG, "Error writing to pipe: %u\n",
1127 return GNUNET_SYSERR;
1130 LOG (GNUNET_ERROR_TYPE_DEBUG,
1133 return bytes_written;
1138 /* set to blocking, write, then set back */
1139 flags = fcntl (h->fd, F_GETFL);
1140 if (0 != (flags & O_NONBLOCK))
1141 (void) fcntl (h->fd, F_SETFL, flags - O_NONBLOCK);
1142 ret = write (h->fd, buffer, n);
1143 if (0 == (flags & O_NONBLOCK))
1144 (void) fcntl (h->fd, F_SETFL, flags);
1151 * Write a buffer to a file. If the file is longer than the
1152 * number of bytes that will be written, it will be truncated.
1154 * @param fn file name
1155 * @param buffer the data to write
1156 * @param n number of bytes to write
1157 * @param mode file permissions
1158 * @return number of bytes written on success, #GNUNET_SYSERR on error
1161 GNUNET_DISK_fn_write (const char *fn, const void *buffer, size_t n,
1162 enum GNUNET_DISK_AccessPermissions mode)
1164 struct GNUNET_DISK_FileHandle *fh;
1167 fh = GNUNET_DISK_file_open (fn,
1168 GNUNET_DISK_OPEN_WRITE | GNUNET_DISK_OPEN_TRUNCATE
1169 | GNUNET_DISK_OPEN_CREATE, mode);
1171 return GNUNET_SYSERR;
1172 ret = GNUNET_DISK_file_write (fh, buffer, n);
1173 GNUNET_assert (GNUNET_OK == GNUNET_DISK_file_close (fh));
1179 * Scan a directory for files.
1181 * @param dir_name the name of the directory
1182 * @param callback the method to call for each file,
1183 * can be NULL, in that case, we only count
1184 * @param callback_cls closure for @a callback
1185 * @return the number of files found, #GNUNET_SYSERR on error or
1186 * ieration aborted by callback returning #GNUNET_SYSERR
1189 GNUNET_DISK_directory_scan (const char *dir_name,
1190 GNUNET_FileNameCallback callback,
1194 struct dirent *finfo;
1200 unsigned int name_len;
1201 unsigned int n_size;
1203 GNUNET_assert (NULL != dir_name);
1204 dname = GNUNET_STRINGS_filename_expand (dir_name);
1206 return GNUNET_SYSERR;
1207 while ((strlen (dname) > 0) && (dname[strlen (dname) - 1] == DIR_SEPARATOR))
1208 dname[strlen (dname) - 1] = '\0';
1209 if (0 != STAT (dname, &istat))
1211 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "stat", dname);
1212 GNUNET_free (dname);
1213 return GNUNET_SYSERR;
1215 if (! S_ISDIR (istat.st_mode))
1217 LOG (GNUNET_ERROR_TYPE_WARNING,
1218 _("Expected `%s' to be a directory!\n"),
1220 GNUNET_free (dname);
1221 return GNUNET_SYSERR;
1224 dinfo = OPENDIR (dname);
1225 if ((errno == EACCES) || (NULL == dinfo))
1227 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "opendir", dname);
1230 GNUNET_free (dname);
1231 return GNUNET_SYSERR;
1234 n_size = strlen (dname) + name_len + 2;
1235 name = GNUNET_malloc (n_size);
1236 while (NULL != (finfo = READDIR (dinfo)))
1238 if ((0 == strcmp (finfo->d_name, ".")) ||
1239 (0 == strcmp (finfo->d_name, "..")))
1241 if (NULL != callback)
1243 if (name_len < strlen (finfo->d_name))
1246 name_len = strlen (finfo->d_name);
1247 n_size = strlen (dname) + name_len + 2;
1248 name = GNUNET_malloc (n_size);
1250 /* dname can end in "/" only if dname == "/";
1251 * if dname does not end in "/", we need to add
1252 * a "/" (otherwise, we must not!) */
1253 GNUNET_snprintf (name, n_size, "%s%s%s", dname,
1254 (strcmp (dname, DIR_SEPARATOR_STR) ==
1255 0) ? "" : DIR_SEPARATOR_STR, finfo->d_name);
1256 ret = callback (callback_cls, name);
1257 if (GNUNET_OK != ret)
1261 GNUNET_free (dname);
1262 if (GNUNET_NO == ret)
1264 return GNUNET_SYSERR;
1271 GNUNET_free (dname);
1277 * Opaque handle used for iterating over a directory.
1279 struct GNUNET_DISK_DirectoryIterator
1283 * Function to call on directory entries.
1285 GNUNET_DISK_DirectoryIteratorCallback callback;
1288 * Closure for callback.
1293 * Reference to directory.
1303 * Next filename to process.
1310 enum GNUNET_SCHEDULER_Priority priority;
1316 * Task used by the directory iterator.
1319 directory_iterator_task (void *cls,
1320 const struct GNUNET_SCHEDULER_TaskContext *tc)
1322 struct GNUNET_DISK_DirectoryIterator *iter = cls;
1325 name = iter->next_name;
1326 GNUNET_assert (name != NULL);
1327 iter->next_name = NULL;
1328 iter->callback (iter->callback_cls, iter, name, iter->dirname);
1334 * This function must be called during the DiskIteratorCallback
1335 * (exactly once) to schedule the task to process the next
1336 * filename in the directory (if there is one).
1338 * @param iter opaque handle for the iterator
1339 * @param can set to GNUNET_YES to terminate the iteration early
1340 * @return GNUNET_YES if iteration will continue,
1341 * GNUNET_NO if this was the last entry (and iteration is complete),
1342 * GNUNET_SYSERR if abort was YES
1345 GNUNET_DISK_directory_iterator_next (struct GNUNET_DISK_DirectoryIterator *iter,
1348 struct dirent *finfo;
1350 GNUNET_assert (iter->next_name == NULL);
1351 if (can == GNUNET_YES)
1353 CLOSEDIR (iter->directory);
1354 GNUNET_free (iter->dirname);
1356 return GNUNET_SYSERR;
1358 while (NULL != (finfo = READDIR (iter->directory)))
1360 if ((0 == strcmp (finfo->d_name, ".")) ||
1361 (0 == strcmp (finfo->d_name, "..")))
1363 GNUNET_asprintf (&iter->next_name, "%s%s%s", iter->dirname,
1364 DIR_SEPARATOR_STR, finfo->d_name);
1369 GNUNET_DISK_directory_iterator_next (iter, GNUNET_YES);
1372 GNUNET_SCHEDULER_add_with_priority (iter->priority, &directory_iterator_task,
1379 * Scan a directory for files using the scheduler to run a task for
1380 * each entry. The name of the directory must be expanded first (!).
1381 * If a scheduler does not need to be used, GNUNET_DISK_directory_scan
1382 * may provide a simpler API.
1384 * @param prio priority to use
1385 * @param dir_name the name of the directory
1386 * @param callback the method to call for each file
1387 * @param callback_cls closure for callback
1388 * @return GNUNET_YES if directory is not empty and 'callback'
1389 * will be called later, GNUNET_NO otherwise, GNUNET_SYSERR on error.
1392 GNUNET_DISK_directory_iterator_start (enum GNUNET_SCHEDULER_Priority prio,
1393 const char *dir_name,
1394 GNUNET_DISK_DirectoryIteratorCallback
1395 callback, void *callback_cls)
1397 struct GNUNET_DISK_DirectoryIterator *di;
1399 di = GNUNET_new (struct GNUNET_DISK_DirectoryIterator);
1400 di->callback = callback;
1401 di->callback_cls = callback_cls;
1402 di->directory = OPENDIR (dir_name);
1403 if (di->directory == NULL)
1406 callback (callback_cls, NULL, NULL, NULL);
1407 return GNUNET_SYSERR;
1409 di->dirname = GNUNET_strdup (dir_name);
1410 di->priority = prio;
1411 return GNUNET_DISK_directory_iterator_next (di, GNUNET_NO);
1416 * Function that removes the given directory by calling
1417 * "GNUNET_DISK_directory_remove".
1419 * @param unused not used
1420 * @param fn directory to remove
1421 * @return #GNUNET_OK
1424 remove_helper (void *unused, const char *fn)
1426 (void) GNUNET_DISK_directory_remove (fn);
1432 * Remove all files in a directory (rm -rf). Call with
1435 * @param filename the file to remove
1436 * @return #GNUNET_OK on success, #GNUNET_SYSERR on error
1439 GNUNET_DISK_directory_remove (const char *filename)
1443 if (NULL == filename)
1446 return GNUNET_SYSERR;
1448 if (0 != LSTAT (filename, &istat))
1449 return GNUNET_NO; /* file may not exist... */
1450 (void) CHMOD (filename, S_IWUSR | S_IRUSR | S_IXUSR);
1451 if (UNLINK (filename) == 0)
1453 if ((errno != EISDIR) &&
1454 /* EISDIR is not sufficient in all cases, e.g.
1455 * sticky /tmp directory may result in EPERM on BSD.
1456 * So we also explicitly check "isDirectory" */
1457 (GNUNET_YES != GNUNET_DISK_directory_test (filename, GNUNET_YES)))
1459 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "rmdir", filename);
1460 return GNUNET_SYSERR;
1462 if (GNUNET_SYSERR ==
1463 GNUNET_DISK_directory_scan (filename, &remove_helper, NULL))
1464 return GNUNET_SYSERR;
1465 if (0 != RMDIR (filename))
1467 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "rmdir", filename);
1468 return GNUNET_SYSERR;
1477 * @param src file to copy
1478 * @param dst destination file name
1479 * @return #GNUNET_OK on success, #GNUNET_SYSERR on error
1482 GNUNET_DISK_file_copy (const char *src,
1489 struct GNUNET_DISK_FileHandle *in;
1490 struct GNUNET_DISK_FileHandle *out;
1492 if (GNUNET_OK != GNUNET_DISK_file_size (src, &size, GNUNET_YES, GNUNET_YES))
1493 return GNUNET_SYSERR;
1495 in = GNUNET_DISK_file_open (src, GNUNET_DISK_OPEN_READ,
1496 GNUNET_DISK_PERM_NONE);
1498 return GNUNET_SYSERR;
1500 GNUNET_DISK_file_open (dst,
1501 GNUNET_DISK_OPEN_WRITE | GNUNET_DISK_OPEN_CREATE |
1502 GNUNET_DISK_OPEN_FAILIFEXISTS,
1503 GNUNET_DISK_PERM_USER_READ |
1504 GNUNET_DISK_PERM_USER_WRITE |
1505 GNUNET_DISK_PERM_GROUP_READ |
1506 GNUNET_DISK_PERM_GROUP_WRITE);
1509 GNUNET_DISK_file_close (in);
1510 return GNUNET_SYSERR;
1512 buf = GNUNET_malloc (COPY_BLK_SIZE);
1515 len = COPY_BLK_SIZE;
1516 if (len > size - pos)
1518 if (len != GNUNET_DISK_file_read (in, buf, len))
1520 if (len != GNUNET_DISK_file_write (out, buf, len))
1525 GNUNET_DISK_file_close (in);
1526 GNUNET_DISK_file_close (out);
1530 GNUNET_DISK_file_close (in);
1531 GNUNET_DISK_file_close (out);
1532 return GNUNET_SYSERR;
1537 * @brief Removes special characters as ':' from a filename.
1538 * @param fn the filename to canonicalize
1541 GNUNET_DISK_filename_canonicalize (char *fn)
1551 if (c == '/' || c == '\\' || c == ':' || c == '*' || c == '?' || c == '"' ||
1552 c == '<' || c == '>' || c == '|')
1564 * @brief Change owner of a file
1566 * @param filename name of file to change the owner of
1567 * @param user name of the new owner
1568 * @return GNUNET_OK on success, GNUNET_SYSERR on failure
1571 GNUNET_DISK_file_change_owner (const char *filename, const char *user)
1576 pws = getpwnam (user);
1579 LOG (GNUNET_ERROR_TYPE_ERROR,
1580 _("Cannot obtain information about user `%s': %s\n"), user,
1582 return GNUNET_SYSERR;
1584 if (0 != chown (filename, pws->pw_uid, pws->pw_gid))
1585 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "chown", filename);
1592 * Lock a part of a file
1593 * @param fh file handle
1594 * @param lock_start absolute position from where to lock
1595 * @param lock_end absolute position until where to lock
1596 * @param excl GNUNET_YES for an exclusive lock
1597 * @return GNUNET_OK on success, GNUNET_SYSERR on error
1600 GNUNET_DISK_file_lock (struct GNUNET_DISK_FileHandle *fh, off_t lock_start,
1601 off_t lock_end, int excl)
1606 return GNUNET_SYSERR;
1612 memset (&fl, 0, sizeof (struct flock));
1613 fl.l_type = excl ? F_WRLCK : F_RDLCK;
1614 fl.l_whence = SEEK_SET;
1615 fl.l_start = lock_start;
1616 fl.l_len = lock_end;
1618 return fcntl (fh->fd, F_SETLK, &fl) != 0 ? GNUNET_SYSERR : GNUNET_OK;
1621 off_t diff = lock_end - lock_start;
1622 DWORD diff_low, diff_high;
1623 diff_low = (DWORD) (diff & 0xFFFFFFFF);
1624 diff_high = (DWORD) ((diff >> (sizeof (DWORD) * 8)) & 0xFFFFFFFF);
1626 memset (&o, 0, sizeof (OVERLAPPED));
1627 o.Offset = (DWORD) (lock_start & 0xFFFFFFFF);;
1628 o.OffsetHigh = (DWORD) (((lock_start & ~0xFFFFFFFF) >> (sizeof (DWORD) * 8)) & 0xFFFFFFFF);
1631 (fh->h, (excl ? LOCKFILE_EXCLUSIVE_LOCK : 0) | LOCKFILE_FAIL_IMMEDIATELY,
1632 0, diff_low, diff_high, &o))
1634 SetErrnoFromWinError (GetLastError ());
1635 return GNUNET_SYSERR;
1644 * Unlock a part of a file
1645 * @param fh file handle
1646 * @param unlock_start absolute position from where to unlock
1647 * @param unlock_end absolute position until where to unlock
1648 * @return #GNUNET_OK on success, #GNUNET_SYSERR on error
1651 GNUNET_DISK_file_unlock (struct GNUNET_DISK_FileHandle *fh, off_t unlock_start,
1657 return GNUNET_SYSERR;
1663 memset (&fl, 0, sizeof (struct flock));
1664 fl.l_type = F_UNLCK;
1665 fl.l_whence = SEEK_SET;
1666 fl.l_start = unlock_start;
1667 fl.l_len = unlock_end;
1669 return fcntl (fh->fd, F_SETLK, &fl) != 0 ? GNUNET_SYSERR : GNUNET_OK;
1672 off_t diff = unlock_end - unlock_start;
1673 DWORD diff_low, diff_high;
1674 diff_low = (DWORD) (diff & 0xFFFFFFFF);
1675 diff_high = (DWORD) ((diff >> (sizeof (DWORD) * 8)) & 0xFFFFFFFF);
1677 memset (&o, 0, sizeof (OVERLAPPED));
1678 o.Offset = (DWORD) (unlock_start & 0xFFFFFFFF);;
1679 o.OffsetHigh = (DWORD) (((unlock_start & ~0xFFFFFFFF) >> (sizeof (DWORD) * 8)) & 0xFFFFFFFF);
1681 if (!UnlockFileEx (fh->h, 0, diff_low, diff_high, &o))
1683 SetErrnoFromWinError (GetLastError ());
1684 return GNUNET_SYSERR;
1693 * Open a file. Note that the access permissions will only be
1694 * used if a new file is created and if the underlying operating
1695 * system supports the given permissions.
1697 * @param fn file name to be opened
1698 * @param flags opening flags, a combination of GNUNET_DISK_OPEN_xxx bit flags
1699 * @param perm permissions for the newly created file, use
1700 * #GNUNET_DISK_PERM_NONE if a file could not be created by this
1701 * call (because of flags)
1702 * @return IO handle on success, NULL on error
1704 struct GNUNET_DISK_FileHandle *
1705 GNUNET_DISK_file_open (const char *fn,
1706 enum GNUNET_DISK_OpenFlags flags,
1707 enum GNUNET_DISK_AccessPermissions perm)
1710 struct GNUNET_DISK_FileHandle *ret;
1716 wchar_t wexpfn[MAX_PATH + 1];
1723 expfn = GNUNET_STRINGS_filename_expand (fn);
1728 if (GNUNET_DISK_OPEN_READWRITE == (flags & GNUNET_DISK_OPEN_READWRITE))
1729 oflags = O_RDWR; /* note: O_RDWR is NOT always O_RDONLY | O_WRONLY */
1730 else if (flags & GNUNET_DISK_OPEN_READ)
1732 else if (flags & GNUNET_DISK_OPEN_WRITE)
1737 GNUNET_free (expfn);
1740 if (flags & GNUNET_DISK_OPEN_FAILIFEXISTS)
1741 oflags |= (O_CREAT | O_EXCL);
1742 if (flags & GNUNET_DISK_OPEN_TRUNCATE)
1744 if (flags & GNUNET_DISK_OPEN_APPEND)
1746 if (flags & GNUNET_DISK_OPEN_CREATE)
1748 (void) GNUNET_DISK_directory_create_for_file (expfn);
1750 mode = translate_unix_perms (perm);
1753 fd = open (expfn, oflags
1757 | O_LARGEFILE, mode);
1760 if (0 == (flags & GNUNET_DISK_OPEN_FAILIFEXISTS))
1761 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "open", expfn);
1763 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_DEBUG, "open", expfn);
1764 GNUNET_free (expfn);
1771 if (GNUNET_DISK_OPEN_READWRITE == (flags & GNUNET_DISK_OPEN_READWRITE))
1772 access = FILE_READ_DATA | FILE_WRITE_DATA;
1773 else if (flags & GNUNET_DISK_OPEN_READ)
1774 access = FILE_READ_DATA;
1775 else if (flags & GNUNET_DISK_OPEN_WRITE)
1776 access = FILE_WRITE_DATA;
1778 if (flags & GNUNET_DISK_OPEN_FAILIFEXISTS)
1782 else if (flags & GNUNET_DISK_OPEN_CREATE)
1784 (void) GNUNET_DISK_directory_create_for_file (expfn);
1785 if (flags & GNUNET_DISK_OPEN_TRUNCATE)
1786 disp = CREATE_ALWAYS;
1790 else if (flags & GNUNET_DISK_OPEN_TRUNCATE)
1792 disp = TRUNCATE_EXISTING;
1796 disp = OPEN_EXISTING;
1799 if (ERROR_SUCCESS == plibc_conv_to_win_pathwconv(expfn, wexpfn))
1800 h = CreateFileW (wexpfn, access,
1801 FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE, NULL,
1802 disp, FILE_ATTRIBUTE_NORMAL, NULL);
1804 h = INVALID_HANDLE_VALUE;
1805 if (h == INVALID_HANDLE_VALUE)
1808 SetErrnoFromWinError (GetLastError ());
1810 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_INFO, "open", expfn);
1811 GNUNET_free (expfn);
1816 if (flags & GNUNET_DISK_OPEN_APPEND)
1817 if (SetFilePointer (h, 0, 0, FILE_END) == INVALID_SET_FILE_POINTER)
1819 SetErrnoFromWinError (GetLastError ());
1820 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "SetFilePointer", expfn);
1822 GNUNET_free (expfn);
1827 ret = GNUNET_new (struct GNUNET_DISK_FileHandle);
1830 ret->type = GNUNET_DISK_HANLDE_TYPE_FILE;
1834 GNUNET_free (expfn);
1840 * Close an open file
1841 * @param h file handle
1842 * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
1845 GNUNET_DISK_file_close (struct GNUNET_DISK_FileHandle *h)
1851 return GNUNET_SYSERR;
1857 if (!CloseHandle (h->h))
1859 SetErrnoFromWinError (GetLastError ());
1860 LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING, "close");
1861 ret = GNUNET_SYSERR;
1863 if (h->oOverlapRead)
1865 if (!CloseHandle (h->oOverlapRead->hEvent))
1867 SetErrnoFromWinError (GetLastError ());
1868 LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING, "close");
1869 ret = GNUNET_SYSERR;
1871 GNUNET_free (h->oOverlapRead);
1873 if (h->oOverlapWrite)
1875 if (!CloseHandle (h->oOverlapWrite->hEvent))
1877 SetErrnoFromWinError (GetLastError ());
1878 LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING, "close");
1879 ret = GNUNET_SYSERR;
1881 GNUNET_free (h->oOverlapWrite);
1884 if (close (h->fd) != 0)
1886 LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING, "close");
1887 ret = GNUNET_SYSERR;
1896 * Get a GNUnet file handle from a W32 handle.
1898 * @param handle native handle
1899 * @return GNUnet file handle corresponding to the W32 handle
1901 struct GNUNET_DISK_FileHandle *
1902 GNUNET_DISK_get_handle_from_w32_handle (HANDLE osfh)
1904 struct GNUNET_DISK_FileHandle *fh;
1907 enum GNUNET_FILE_Type ftype;
1909 dwret = GetFileType (osfh);
1912 case FILE_TYPE_DISK:
1913 ftype = GNUNET_DISK_HANLDE_TYPE_FILE;
1915 case FILE_TYPE_PIPE:
1916 ftype = GNUNET_DISK_HANLDE_TYPE_PIPE;
1918 case FILE_TYPE_UNKNOWN:
1919 if (GetLastError () == NO_ERROR || GetLastError () == ERROR_INVALID_HANDLE)
1921 if (0 != ResetEvent (osfh))
1922 ftype = GNUNET_DISK_HANLDE_TYPE_EVENT;
1931 fh = GNUNET_new (struct GNUNET_DISK_FileHandle);
1935 if (ftype == GNUNET_DISK_HANLDE_TYPE_PIPE)
1938 * Note that we can't make it overlapped if it isn't already.
1939 * (ReOpenFile() is only available in 2003/Vista).
1940 * The process that opened this file in the first place (usually a parent
1941 * process, if this is stdin/stdout/stderr) must make it overlapped,
1942 * otherwise we're screwed, as selecting on non-overlapped handle
1945 fh->oOverlapRead = GNUNET_new (OVERLAPPED);
1946 fh->oOverlapWrite = GNUNET_new (OVERLAPPED);
1947 fh->oOverlapRead->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
1948 fh->oOverlapWrite->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
1956 * Get a handle from a native integer FD.
1958 * @param fno native integer file descriptor
1959 * @return file handle corresponding to the descriptor, NULL on error
1961 struct GNUNET_DISK_FileHandle *
1962 GNUNET_DISK_get_handle_from_int_fd (int fno)
1964 struct GNUNET_DISK_FileHandle *fh;
1966 if ( (((off_t) -1) == lseek (fno, 0, SEEK_CUR)) &&
1968 return NULL; /* invalid FD */
1971 fh = GNUNET_new (struct GNUNET_DISK_FileHandle);
1977 osfh = _get_osfhandle (fno);
1978 if (INVALID_HANDLE_VALUE == (HANDLE) osfh)
1981 fh = GNUNET_DISK_get_handle_from_w32_handle ((HANDLE) osfh);
1989 * Get a handle from a native streaming FD.
1991 * @param fd native streaming file descriptor
1992 * @return file handle corresponding to the descriptor
1994 struct GNUNET_DISK_FileHandle *
1995 GNUNET_DISK_get_handle_from_native (FILE *fd)
2003 return GNUNET_DISK_get_handle_from_int_fd (fno);
2008 * Handle for a memory-mapping operation.
2010 struct GNUNET_DISK_MapHandle
2013 * Address where the map is in memory.
2019 * Underlying OS handle.
2024 * Number of bytes mapped.
2032 #define MAP_FAILED ((void *) -1)
2036 * Map a file into memory
2038 * @param h open file handle
2039 * @param m handle to the new mapping
2040 * @param access access specification, GNUNET_DISK_MAP_TYPE_xxx
2041 * @param len size of the mapping
2042 * @return pointer to the mapped memory region, NULL on failure
2045 GNUNET_DISK_file_map (const struct GNUNET_DISK_FileHandle *h,
2046 struct GNUNET_DISK_MapHandle **m,
2047 enum GNUNET_DISK_MapType access, size_t len)
2056 DWORD mapAccess, protect;
2058 if ((access & GNUNET_DISK_MAP_TYPE_READ) &&
2059 (access & GNUNET_DISK_MAP_TYPE_WRITE))
2061 protect = PAGE_READWRITE;
2062 mapAccess = FILE_MAP_ALL_ACCESS;
2064 else if (access & GNUNET_DISK_MAP_TYPE_READ)
2066 protect = PAGE_READONLY;
2067 mapAccess = FILE_MAP_READ;
2069 else if (access & GNUNET_DISK_MAP_TYPE_WRITE)
2071 protect = PAGE_READWRITE;
2072 mapAccess = FILE_MAP_WRITE;
2080 *m = GNUNET_new (struct GNUNET_DISK_MapHandle);
2081 (*m)->h = CreateFileMapping (h->h, NULL, protect, 0, 0, NULL);
2082 if ((*m)->h == INVALID_HANDLE_VALUE)
2084 SetErrnoFromWinError (GetLastError ());
2089 (*m)->addr = MapViewOfFile ((*m)->h, mapAccess, 0, 0, len);
2092 SetErrnoFromWinError (GetLastError ());
2093 CloseHandle ((*m)->h);
2102 if (access & GNUNET_DISK_MAP_TYPE_READ)
2104 if (access & GNUNET_DISK_MAP_TYPE_WRITE)
2106 *m = GNUNET_new (struct GNUNET_DISK_MapHandle);
2107 (*m)->addr = mmap (NULL, len, prot, MAP_SHARED, h->fd, 0);
2108 GNUNET_assert (NULL != (*m)->addr);
2109 if (MAP_FAILED == (*m)->addr)
2121 * @param h mapping handle
2122 * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
2125 GNUNET_DISK_file_unmap (struct GNUNET_DISK_MapHandle *h)
2132 return GNUNET_SYSERR;
2136 ret = UnmapViewOfFile (h->addr) ? GNUNET_OK : GNUNET_SYSERR;
2137 if (ret != GNUNET_OK)
2138 SetErrnoFromWinError (GetLastError ());
2139 if (!CloseHandle (h->h) && (ret == GNUNET_OK))
2141 ret = GNUNET_SYSERR;
2142 SetErrnoFromWinError (GetLastError ());
2145 ret = munmap (h->addr, h->len) != -1 ? GNUNET_OK : GNUNET_SYSERR;
2153 * Write file changes to disk
2154 * @param h handle to an open file
2155 * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
2158 GNUNET_DISK_file_sync (const struct GNUNET_DISK_FileHandle *h)
2163 return GNUNET_SYSERR;
2169 ret = FlushFileBuffers (h->h) ? GNUNET_OK : GNUNET_SYSERR;
2170 if (ret != GNUNET_OK)
2171 SetErrnoFromWinError (GetLastError ());
2173 #elif defined(FREEBSD) || defined(OPENBSD) || defined(DARWIN)
2174 return fsync (h->fd) == -1 ? GNUNET_SYSERR : GNUNET_OK;
2176 return fdatasync (h->fd) == -1 ? GNUNET_SYSERR : GNUNET_OK;
2183 #define PIPE_BUF 512
2185 /* Copyright Bob Byrnes <byrnes <at> curl.com>
2186 http://permalink.gmane.org/gmane.os.cygwin.patches/2121
2188 /* Create a pipe, and return handles to the read and write ends,
2189 just like CreatePipe, but ensure that the write end permits
2190 FILE_READ_ATTRIBUTES access, on later versions of win32 where
2191 this is supported. This access is needed by NtQueryInformationFile,
2192 which is used to implement select and nonblocking writes.
2193 Note that the return value is either NO_ERROR or GetLastError,
2194 unlike CreatePipe, which returns a bool for success or failure. */
2196 create_selectable_pipe (PHANDLE read_pipe_ptr, PHANDLE write_pipe_ptr,
2197 LPSECURITY_ATTRIBUTES sa_ptr, DWORD psize,
2198 DWORD dwReadMode, DWORD dwWriteMode)
2200 /* Default to error. */
2201 *read_pipe_ptr = *write_pipe_ptr = INVALID_HANDLE_VALUE;
2206 /* Ensure that there is enough pipe buffer space for atomic writes. */
2207 if (psize < PIPE_BUF)
2210 char pipename[MAX_PATH];
2212 /* Retry CreateNamedPipe as long as the pipe name is in use.
2213 * Retrying will probably never be necessary, but we want
2214 * to be as robust as possible. */
2217 static volatile LONG pipe_unique_id;
2219 snprintf (pipename, sizeof pipename, "\\\\.\\pipe\\gnunet-%d-%ld",
2220 getpid (), InterlockedIncrement ((LONG *) & pipe_unique_id));
2221 LOG (GNUNET_ERROR_TYPE_DEBUG, "CreateNamedPipe: name = %s, size = %lu\n",
2223 /* Use CreateNamedPipe instead of CreatePipe, because the latter
2224 * returns a write handle that does not permit FILE_READ_ATTRIBUTES
2225 * access, on versions of win32 earlier than WinXP SP2.
2226 * CreatePipe also stupidly creates a full duplex pipe, which is
2227 * a waste, since only a single direction is actually used.
2228 * It's important to only allow a single instance, to ensure that
2229 * the pipe was not created earlier by some other process, even if
2230 * the pid has been reused. */
2231 read_pipe = CreateNamedPipeA (pipename, PIPE_ACCESS_INBOUND | FILE_FLAG_FIRST_PIPE_INSTANCE | dwReadMode, PIPE_TYPE_BYTE | PIPE_READMODE_BYTE, 1, /* max instances */
2232 psize, /* output buffer size */
2233 psize, /* input buffer size */
2234 NMPWAIT_USE_DEFAULT_WAIT, sa_ptr);
2236 if (read_pipe != INVALID_HANDLE_VALUE)
2238 LOG (GNUNET_ERROR_TYPE_DEBUG, "pipe read handle = %p\n", read_pipe);
2242 DWORD err = GetLastError ();
2246 case ERROR_PIPE_BUSY:
2247 /* The pipe is already open with compatible parameters.
2248 * Pick a new name and retry. */
2249 LOG (GNUNET_ERROR_TYPE_DEBUG, "pipe busy, retrying\n");
2251 case ERROR_ACCESS_DENIED:
2252 /* The pipe is already open with incompatible parameters.
2253 * Pick a new name and retry. */
2254 LOG (GNUNET_ERROR_TYPE_DEBUG, "pipe access denied, retrying\n");
2256 case ERROR_CALL_NOT_IMPLEMENTED:
2257 /* We are on an older Win9x platform without named pipes.
2258 * Return an anonymous pipe as the best approximation. */
2259 LOG (GNUNET_ERROR_TYPE_DEBUG,
2260 "CreateNamedPipe not implemented, resorting to "
2261 "CreatePipe: size = %lu\n", psize);
2262 if (CreatePipe (read_pipe_ptr, write_pipe_ptr, sa_ptr, psize))
2264 LOG (GNUNET_ERROR_TYPE_DEBUG, "pipe read handle = %p, write handle = %p\n",
2269 err = GetLastError ();
2270 LOG (GNUNET_ERROR_TYPE_ERROR, "CreatePipe failed: %d\n", err);
2273 LOG (GNUNET_ERROR_TYPE_ERROR, "CreateNamedPipe failed: %d\n", err);
2278 LOG (GNUNET_ERROR_TYPE_DEBUG, "CreateFile: name = %s\n", pipename);
2280 /* Open the named pipe for writing.
2281 * Be sure to permit FILE_READ_ATTRIBUTES access. */
2282 write_pipe = CreateFileA (pipename, GENERIC_WRITE | FILE_READ_ATTRIBUTES, 0, /* share mode */
2283 sa_ptr, OPEN_EXISTING, dwWriteMode, /* flags and attributes */
2284 0); /* handle to template file */
2286 if (write_pipe == INVALID_HANDLE_VALUE)
2289 DWORD err = GetLastError ();
2291 LOG (GNUNET_ERROR_TYPE_DEBUG, "CreateFile failed: %d\n", err);
2292 CloseHandle (read_pipe);
2295 LOG (GNUNET_ERROR_TYPE_DEBUG, "pipe write handle = %p\n", write_pipe);
2297 *read_pipe_ptr = read_pipe;
2298 *write_pipe_ptr = write_pipe;
2305 * Creates an interprocess channel
2307 * @param blocking_read creates an asynchronous pipe for reading if set to GNUNET_NO
2308 * @param blocking_write creates an asynchronous pipe for writing if set to GNUNET_NO
2309 * @param inherit_read inherit the parent processes stdin (only for windows)
2310 * @param inherit_write inherit the parent processes stdout (only for windows)
2311 * @return handle to the new pipe, NULL on error
2313 struct GNUNET_DISK_PipeHandle *
2314 GNUNET_DISK_pipe (int blocking_read, int blocking_write, int inherit_read, int inherit_write)
2325 LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR, "pipe");
2329 return GNUNET_DISK_pipe_from_fd (blocking_read,
2333 struct GNUNET_DISK_PipeHandle *p;
2338 p = GNUNET_new (struct GNUNET_DISK_PipeHandle);
2339 p->fd[0] = GNUNET_new (struct GNUNET_DISK_FileHandle);
2340 p->fd[1] = GNUNET_new (struct GNUNET_DISK_FileHandle);
2342 /* All pipes are overlapped. If you want them to block - just
2343 * call WriteFile() and ReadFile() with NULL overlapped pointer.
2344 * NOTE: calling with NULL overlapped pointer works only
2345 * for pipes, and doesn't seem to be a documented feature.
2346 * It will NOT work for files, because overlapped files need
2347 * to read offsets from the overlapped structure, regardless.
2348 * Pipes are not seekable, and need no offsets, which is
2349 * probably why it works for them.
2352 create_selectable_pipe (&p->fd[0]->h, &p->fd[1]->h, NULL, 0,
2353 FILE_FLAG_OVERLAPPED,
2354 FILE_FLAG_OVERLAPPED);
2357 SetErrnoFromWinError (GetLastError ());
2359 GNUNET_free (p->fd[0]);
2360 GNUNET_free (p->fd[1]);
2365 if (!DuplicateHandle
2366 (GetCurrentProcess (), p->fd[0]->h, GetCurrentProcess (), &tmp_handle, 0,
2367 inherit_read == GNUNET_YES ? TRUE : FALSE, DUPLICATE_SAME_ACCESS))
2369 SetErrnoFromWinError (GetLastError ());
2371 CloseHandle (p->fd[0]->h);
2372 CloseHandle (p->fd[1]->h);
2373 GNUNET_free (p->fd[0]);
2374 GNUNET_free (p->fd[1]);
2379 CloseHandle (p->fd[0]->h);
2380 p->fd[0]->h = tmp_handle;
2382 if (!DuplicateHandle
2383 (GetCurrentProcess (), p->fd[1]->h, GetCurrentProcess (), &tmp_handle, 0,
2384 inherit_write == GNUNET_YES ? TRUE : FALSE, DUPLICATE_SAME_ACCESS))
2386 SetErrnoFromWinError (GetLastError ());
2388 CloseHandle (p->fd[0]->h);
2389 CloseHandle (p->fd[1]->h);
2390 GNUNET_free (p->fd[0]);
2391 GNUNET_free (p->fd[1]);
2396 CloseHandle (p->fd[1]->h);
2397 p->fd[1]->h = tmp_handle;
2399 p->fd[0]->type = GNUNET_DISK_HANLDE_TYPE_PIPE;
2400 p->fd[1]->type = GNUNET_DISK_HANLDE_TYPE_PIPE;
2402 p->fd[0]->oOverlapRead = GNUNET_new (OVERLAPPED);
2403 p->fd[0]->oOverlapWrite = GNUNET_new (OVERLAPPED);
2404 p->fd[1]->oOverlapRead = GNUNET_new (OVERLAPPED);
2405 p->fd[1]->oOverlapWrite = GNUNET_new (OVERLAPPED);
2407 p->fd[0]->oOverlapRead->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2408 p->fd[0]->oOverlapWrite->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2410 p->fd[1]->oOverlapRead->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2411 p->fd[1]->oOverlapWrite->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2419 * Creates a pipe object from a couple of file descriptors.
2420 * Useful for wrapping existing pipe FDs.
2422 * @param blocking_read creates an asynchronous pipe for reading if set to GNUNET_NO
2423 * @param blocking_write creates an asynchronous pipe for writing if set to GNUNET_NO
2424 * @param fd an array of two fd values. One of them may be -1 for read-only or write-only pipes
2426 * @return handle to the new pipe, NULL on error
2428 struct GNUNET_DISK_PipeHandle *
2429 GNUNET_DISK_pipe_from_fd (int blocking_read, int blocking_write, int fd[2])
2431 struct GNUNET_DISK_PipeHandle *p;
2433 p = GNUNET_new (struct GNUNET_DISK_PipeHandle);
2438 int eno = 0; /* make gcc happy */
2443 p->fd[0] = GNUNET_new (struct GNUNET_DISK_FileHandle);
2444 p->fd[0]->fd = fd[0];
2447 flags = fcntl (fd[0], F_GETFL);
2448 flags |= O_NONBLOCK;
2449 if (0 > fcntl (fd[0], F_SETFL, flags))
2455 flags = fcntl (fd[0], F_GETFD);
2456 flags |= FD_CLOEXEC;
2457 if (0 > fcntl (fd[0], F_SETFD, flags))
2466 p->fd[1] = GNUNET_new (struct GNUNET_DISK_FileHandle);
2467 p->fd[1]->fd = fd[1];
2468 if (!blocking_write)
2470 flags = fcntl (fd[1], F_GETFL);
2471 flags |= O_NONBLOCK;
2472 if (0 > fcntl (fd[1], F_SETFL, flags))
2478 flags = fcntl (fd[1], F_GETFD);
2479 flags |= FD_CLOEXEC;
2480 if (0 > fcntl (fd[1], F_SETFD, flags))
2489 LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR, "fcntl");
2490 if (p->fd[0]->fd >= 0)
2491 GNUNET_break (0 == close (p->fd[0]->fd));
2492 if (p->fd[1]->fd >= 0)
2493 GNUNET_break (0 == close (p->fd[1]->fd));
2494 GNUNET_free_non_null (p->fd[0]);
2495 GNUNET_free_non_null (p->fd[1]);
2503 p->fd[0] = GNUNET_new (struct GNUNET_DISK_FileHandle);
2504 p->fd[0]->h = (HANDLE) _get_osfhandle (fd[0]);
2505 if (p->fd[0]->h != INVALID_HANDLE_VALUE)
2507 p->fd[0]->type = GNUNET_DISK_HANLDE_TYPE_PIPE;
2508 p->fd[0]->oOverlapRead = GNUNET_new (OVERLAPPED);
2509 p->fd[0]->oOverlapWrite = GNUNET_new (OVERLAPPED);
2510 p->fd[0]->oOverlapRead->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2511 p->fd[0]->oOverlapWrite->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2515 GNUNET_free (p->fd[0]);
2521 p->fd[1] = GNUNET_new (struct GNUNET_DISK_FileHandle);
2522 p->fd[1]->h = (HANDLE) _get_osfhandle (fd[1]);
2523 if (p->fd[1]->h != INVALID_HANDLE_VALUE)
2525 p->fd[1]->type = GNUNET_DISK_HANLDE_TYPE_PIPE;
2526 p->fd[1]->oOverlapRead = GNUNET_new (OVERLAPPED);
2527 p->fd[1]->oOverlapWrite = GNUNET_new (OVERLAPPED);
2528 p->fd[1]->oOverlapRead->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2529 p->fd[1]->oOverlapWrite->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2533 GNUNET_free (p->fd[1]);
2544 * Closes an interprocess channel
2546 * @param p pipe to close
2547 * @param end which end of the pipe to close
2548 * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
2551 GNUNET_DISK_pipe_close_end (struct GNUNET_DISK_PipeHandle *p,
2552 enum GNUNET_DISK_PipeEnd end)
2554 int ret = GNUNET_OK;
2556 if (end == GNUNET_DISK_PIPE_END_READ)
2560 ret = GNUNET_DISK_file_close (p->fd[0]);
2564 else if (end == GNUNET_DISK_PIPE_END_WRITE)
2568 ret = GNUNET_DISK_file_close (p->fd[1]);
2577 * Detaches one of the ends from the pipe.
2578 * Detached end is a fully-functional FileHandle, it will
2579 * not be affected by anything you do with the pipe afterwards.
2580 * Each end of a pipe can only be detched from it once (i.e.
2581 * it is not duplicated).
2583 * @param p pipe to detach an end from
2584 * @param end which end of the pipe to detach
2585 * @return Detached end on success, NULL on failure
2586 * (or if that end is not present or is closed).
2588 struct GNUNET_DISK_FileHandle *
2589 GNUNET_DISK_pipe_detach_end (struct GNUNET_DISK_PipeHandle *p,
2590 enum GNUNET_DISK_PipeEnd end)
2592 struct GNUNET_DISK_FileHandle *ret = NULL;
2594 if (end == GNUNET_DISK_PIPE_END_READ)
2602 else if (end == GNUNET_DISK_PIPE_END_WRITE)
2616 * Closes an interprocess channel
2618 * @param p pipe to close
2619 * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
2622 GNUNET_DISK_pipe_close (struct GNUNET_DISK_PipeHandle *p)
2624 int ret = GNUNET_OK;
2627 int write_end_close;
2628 int read_end_close_errno;
2629 int write_end_close_errno;
2631 read_end_close = GNUNET_DISK_pipe_close_end (p, GNUNET_DISK_PIPE_END_READ);
2632 read_end_close_errno = errno;
2633 write_end_close = GNUNET_DISK_pipe_close_end (p, GNUNET_DISK_PIPE_END_WRITE);
2634 write_end_close_errno = errno;
2637 if (GNUNET_OK != read_end_close)
2639 errno = read_end_close_errno;
2640 ret = read_end_close;
2642 else if (GNUNET_OK != write_end_close)
2644 errno = write_end_close_errno;
2645 ret = write_end_close;
2653 * Get the handle to a particular pipe end
2656 * @param n end to access
2657 * @return handle for the respective end
2659 const struct GNUNET_DISK_FileHandle *
2660 GNUNET_DISK_pipe_handle (const struct GNUNET_DISK_PipeHandle *p,
2661 enum GNUNET_DISK_PipeEnd n)
2665 case GNUNET_DISK_PIPE_END_READ:
2666 case GNUNET_DISK_PIPE_END_WRITE:
2676 * Retrieve OS file handle
2678 * @param fh GNUnet file descriptor
2679 * @param dst destination buffer
2680 * @param dst_len length of dst
2681 * @return #GNUNET_OK on success, #GNUNET_SYSERR otherwise
2684 GNUNET_DISK_internal_file_handle_ (const struct GNUNET_DISK_FileHandle *fh,
2685 void *dst, size_t dst_len)
2688 return GNUNET_SYSERR;
2690 if (dst_len < sizeof (HANDLE))
2691 return GNUNET_SYSERR;
2692 *((HANDLE *) dst) = fh->h;
2694 if (dst_len < sizeof (int))
2695 return GNUNET_SYSERR;
2696 *((int *) dst) = fh->fd;