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, "Read %u bytes\n", bytes_read);
953 /* set to non-blocking, read, then set back */
954 flags = fcntl (h->fd, F_GETFL);
955 if (0 == (flags & O_NONBLOCK))
956 (void) fcntl (h->fd, F_SETFL, flags | O_NONBLOCK);
957 ret = read (h->fd, result, len);
958 if (0 == (flags & O_NONBLOCK))
961 (void) fcntl (h->fd, F_SETFL, flags);
970 * Read the contents of a binary file into a buffer.
972 * @param fn file name
973 * @param result the buffer to write the result to
974 * @param len the maximum number of bytes to read
975 * @return number of bytes read, #GNUNET_SYSERR on failure
978 GNUNET_DISK_fn_read (const char *fn,
982 struct GNUNET_DISK_FileHandle *fh;
985 fh = GNUNET_DISK_file_open (fn, GNUNET_DISK_OPEN_READ, GNUNET_DISK_PERM_NONE);
987 return GNUNET_SYSERR;
988 ret = GNUNET_DISK_file_read (fh, result, len);
989 GNUNET_assert (GNUNET_OK == GNUNET_DISK_file_close (fh));
996 * Write a buffer to a file.
998 * @param h handle to open file
999 * @param buffer the data to write
1000 * @param n number of bytes to write
1001 * @return number of bytes written on success, #GNUNET_SYSERR on error
1004 GNUNET_DISK_file_write (const struct GNUNET_DISK_FileHandle * h,
1005 const void *buffer, size_t n)
1010 return GNUNET_SYSERR;
1014 DWORD bytes_written;
1016 if (h->type == GNUNET_DISK_HANLDE_TYPE_FILE)
1018 if (!WriteFile (h->h, buffer, n, &bytes_written, NULL))
1020 SetErrnoFromWinError (GetLastError ());
1021 return GNUNET_SYSERR;
1024 else if (h->type == GNUNET_DISK_HANLDE_TYPE_PIPE)
1026 LOG (GNUNET_ERROR_TYPE_DEBUG, "It is a pipe trying to write %u bytes\n", n);
1027 if (!WriteFile (h->h, buffer, n, &bytes_written, h->oOverlapWrite))
1029 if (GetLastError () != ERROR_IO_PENDING)
1031 SetErrnoFromWinError (GetLastError ());
1032 LOG (GNUNET_ERROR_TYPE_DEBUG, "Error writing to pipe: %u\n",
1034 return GNUNET_SYSERR;
1036 LOG (GNUNET_ERROR_TYPE_DEBUG, "Will get overlapped result\n");
1037 if (!GetOverlappedResult (h->h, h->oOverlapWrite, &bytes_written, TRUE))
1039 SetErrnoFromWinError (GetLastError ());
1040 LOG (GNUNET_ERROR_TYPE_DEBUG,
1041 "Error getting overlapped result while writing to pipe: %u\n",
1043 return GNUNET_SYSERR;
1049 if (!GetOverlappedResult (h->h, h->oOverlapWrite, &ovr, TRUE))
1051 LOG (GNUNET_ERROR_TYPE_DEBUG,
1052 "Error getting control overlapped result while writing to pipe: %u\n",
1057 LOG (GNUNET_ERROR_TYPE_DEBUG,
1058 "Wrote %u bytes (ovr says %u), picking the greatest\n",
1059 bytes_written, ovr);
1062 if (bytes_written == 0)
1066 LOG (GNUNET_ERROR_TYPE_DEBUG, "Wrote %u bytes, returning -1 with EAGAIN\n", bytes_written);
1068 return GNUNET_SYSERR;
1071 LOG (GNUNET_ERROR_TYPE_DEBUG, "Wrote %u bytes\n", bytes_written);
1077 return bytes_written;
1079 return write (h->fd, buffer, n);
1085 * Write a buffer to a file, blocking, if necessary.
1087 * @param h handle to open file
1088 * @param buffer the data to write
1089 * @param n number of bytes to write
1090 * @return number of bytes written on success, #GNUNET_SYSERR on error
1093 GNUNET_DISK_file_write_blocking (const struct GNUNET_DISK_FileHandle * h,
1100 return GNUNET_SYSERR;
1104 DWORD bytes_written;
1105 /* We do a non-overlapped write, which is as blocking as it gets */
1106 LOG (GNUNET_ERROR_TYPE_DEBUG, "Writing %u bytes\n", n);
1107 if (!WriteFile (h->h, buffer, n, &bytes_written, NULL))
1109 SetErrnoFromWinError (GetLastError ());
1110 LOG (GNUNET_ERROR_TYPE_DEBUG, "Error writing to pipe: %u\n",
1112 return GNUNET_SYSERR;
1114 if (bytes_written == 0 && n > 0)
1116 LOG (GNUNET_ERROR_TYPE_DEBUG, "Waiting for pipe to clean\n");
1117 WaitForSingleObject (h->h, INFINITE);
1118 if (!WriteFile (h->h, buffer, n, &bytes_written, NULL))
1120 SetErrnoFromWinError (GetLastError ());
1121 LOG (GNUNET_ERROR_TYPE_DEBUG, "Error writing to pipe: %u\n",
1123 return GNUNET_SYSERR;
1126 LOG (GNUNET_ERROR_TYPE_DEBUG,
1129 return bytes_written;
1134 /* set to blocking, write, then set back */
1135 flags = fcntl (h->fd, F_GETFL);
1136 if (0 != (flags & O_NONBLOCK))
1137 (void) fcntl (h->fd, F_SETFL, flags - O_NONBLOCK);
1138 ret = write (h->fd, buffer, n);
1139 if (0 == (flags & O_NONBLOCK))
1140 (void) fcntl (h->fd, F_SETFL, flags);
1147 * Write a buffer to a file. If the file is longer than the
1148 * number of bytes that will be written, it will be truncated.
1150 * @param fn file name
1151 * @param buffer the data to write
1152 * @param n number of bytes to write
1153 * @param mode file permissions
1154 * @return number of bytes written on success, #GNUNET_SYSERR on error
1157 GNUNET_DISK_fn_write (const char *fn, const void *buffer, size_t n,
1158 enum GNUNET_DISK_AccessPermissions mode)
1160 struct GNUNET_DISK_FileHandle *fh;
1163 fh = GNUNET_DISK_file_open (fn,
1164 GNUNET_DISK_OPEN_WRITE | GNUNET_DISK_OPEN_TRUNCATE
1165 | GNUNET_DISK_OPEN_CREATE, mode);
1167 return GNUNET_SYSERR;
1168 ret = GNUNET_DISK_file_write (fh, buffer, n);
1169 GNUNET_assert (GNUNET_OK == GNUNET_DISK_file_close (fh));
1175 * Scan a directory for files.
1177 * @param dir_name the name of the directory
1178 * @param callback the method to call for each file,
1179 * can be NULL, in that case, we only count
1180 * @param callback_cls closure for @a callback
1181 * @return the number of files found, #GNUNET_SYSERR on error or
1182 * ieration aborted by callback returning #GNUNET_SYSERR
1185 GNUNET_DISK_directory_scan (const char *dir_name,
1186 GNUNET_FileNameCallback callback,
1190 struct dirent *finfo;
1196 unsigned int name_len;
1197 unsigned int n_size;
1199 GNUNET_assert (dir_name != NULL);
1200 dname = GNUNET_STRINGS_filename_expand (dir_name);
1202 return GNUNET_SYSERR;
1203 while ((strlen (dname) > 0) && (dname[strlen (dname) - 1] == DIR_SEPARATOR))
1204 dname[strlen (dname) - 1] = '\0';
1205 if (0 != STAT (dname, &istat))
1207 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "stat", dname);
1208 GNUNET_free (dname);
1209 return GNUNET_SYSERR;
1211 if (!S_ISDIR (istat.st_mode))
1213 LOG (GNUNET_ERROR_TYPE_WARNING,
1214 _("Expected `%s' to be a directory!\n"),
1216 GNUNET_free (dname);
1217 return GNUNET_SYSERR;
1220 dinfo = OPENDIR (dname);
1221 if ((errno == EACCES) || (dinfo == NULL))
1223 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "opendir", dname);
1226 GNUNET_free (dname);
1227 return GNUNET_SYSERR;
1230 n_size = strlen (dname) + name_len + 2;
1231 name = GNUNET_malloc (n_size);
1232 while ((finfo = READDIR (dinfo)) != NULL)
1234 if ((0 == strcmp (finfo->d_name, ".")) ||
1235 (0 == strcmp (finfo->d_name, "..")))
1237 if (callback != NULL)
1239 if (name_len < strlen (finfo->d_name))
1242 name_len = strlen (finfo->d_name);
1243 n_size = strlen (dname) + name_len + 2;
1244 name = GNUNET_malloc (n_size);
1246 /* dname can end in "/" only if dname == "/";
1247 * if dname does not end in "/", we need to add
1248 * a "/" (otherwise, we must not!) */
1249 GNUNET_snprintf (name, n_size, "%s%s%s", dname,
1250 (strcmp (dname, DIR_SEPARATOR_STR) ==
1251 0) ? "" : DIR_SEPARATOR_STR, finfo->d_name);
1252 ret = callback (callback_cls, name);
1253 if (GNUNET_OK != ret)
1257 GNUNET_free (dname);
1258 if (GNUNET_NO == ret)
1260 return GNUNET_SYSERR;
1267 GNUNET_free (dname);
1273 * Opaque handle used for iterating over a directory.
1275 struct GNUNET_DISK_DirectoryIterator
1279 * Function to call on directory entries.
1281 GNUNET_DISK_DirectoryIteratorCallback callback;
1284 * Closure for callback.
1289 * Reference to directory.
1299 * Next filename to process.
1306 enum GNUNET_SCHEDULER_Priority priority;
1312 * Task used by the directory iterator.
1315 directory_iterator_task (void *cls,
1316 const struct GNUNET_SCHEDULER_TaskContext *tc)
1318 struct GNUNET_DISK_DirectoryIterator *iter = cls;
1321 name = iter->next_name;
1322 GNUNET_assert (name != NULL);
1323 iter->next_name = NULL;
1324 iter->callback (iter->callback_cls, iter, name, iter->dirname);
1330 * This function must be called during the DiskIteratorCallback
1331 * (exactly once) to schedule the task to process the next
1332 * filename in the directory (if there is one).
1334 * @param iter opaque handle for the iterator
1335 * @param can set to GNUNET_YES to terminate the iteration early
1336 * @return GNUNET_YES if iteration will continue,
1337 * GNUNET_NO if this was the last entry (and iteration is complete),
1338 * GNUNET_SYSERR if abort was YES
1341 GNUNET_DISK_directory_iterator_next (struct GNUNET_DISK_DirectoryIterator *iter,
1344 struct dirent *finfo;
1346 GNUNET_assert (iter->next_name == NULL);
1347 if (can == GNUNET_YES)
1349 CLOSEDIR (iter->directory);
1350 GNUNET_free (iter->dirname);
1352 return GNUNET_SYSERR;
1354 while (NULL != (finfo = READDIR (iter->directory)))
1356 if ((0 == strcmp (finfo->d_name, ".")) ||
1357 (0 == strcmp (finfo->d_name, "..")))
1359 GNUNET_asprintf (&iter->next_name, "%s%s%s", iter->dirname,
1360 DIR_SEPARATOR_STR, finfo->d_name);
1365 GNUNET_DISK_directory_iterator_next (iter, GNUNET_YES);
1368 GNUNET_SCHEDULER_add_with_priority (iter->priority, &directory_iterator_task,
1375 * Scan a directory for files using the scheduler to run a task for
1376 * each entry. The name of the directory must be expanded first (!).
1377 * If a scheduler does not need to be used, GNUNET_DISK_directory_scan
1378 * may provide a simpler API.
1380 * @param prio priority to use
1381 * @param dir_name the name of the directory
1382 * @param callback the method to call for each file
1383 * @param callback_cls closure for callback
1384 * @return GNUNET_YES if directory is not empty and 'callback'
1385 * will be called later, GNUNET_NO otherwise, GNUNET_SYSERR on error.
1388 GNUNET_DISK_directory_iterator_start (enum GNUNET_SCHEDULER_Priority prio,
1389 const char *dir_name,
1390 GNUNET_DISK_DirectoryIteratorCallback
1391 callback, void *callback_cls)
1393 struct GNUNET_DISK_DirectoryIterator *di;
1395 di = GNUNET_new (struct GNUNET_DISK_DirectoryIterator);
1396 di->callback = callback;
1397 di->callback_cls = callback_cls;
1398 di->directory = OPENDIR (dir_name);
1399 if (di->directory == NULL)
1402 callback (callback_cls, NULL, NULL, NULL);
1403 return GNUNET_SYSERR;
1405 di->dirname = GNUNET_strdup (dir_name);
1406 di->priority = prio;
1407 return GNUNET_DISK_directory_iterator_next (di, GNUNET_NO);
1412 * Function that removes the given directory by calling
1413 * "GNUNET_DISK_directory_remove".
1415 * @param unused not used
1416 * @param fn directory to remove
1417 * @return #GNUNET_OK
1420 remove_helper (void *unused, const char *fn)
1422 (void) GNUNET_DISK_directory_remove (fn);
1428 * Remove all files in a directory (rm -rf). Call with
1431 * @param filename the file to remove
1432 * @return #GNUNET_OK on success, #GNUNET_SYSERR on error
1435 GNUNET_DISK_directory_remove (const char *filename)
1439 if (NULL == filename)
1442 return GNUNET_SYSERR;
1444 if (0 != LSTAT (filename, &istat))
1445 return GNUNET_NO; /* file may not exist... */
1446 (void) CHMOD (filename, S_IWUSR | S_IRUSR | S_IXUSR);
1447 if (UNLINK (filename) == 0)
1449 if ((errno != EISDIR) &&
1450 /* EISDIR is not sufficient in all cases, e.g.
1451 * sticky /tmp directory may result in EPERM on BSD.
1452 * So we also explicitly check "isDirectory" */
1453 (GNUNET_YES != GNUNET_DISK_directory_test (filename, GNUNET_YES)))
1455 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "rmdir", filename);
1456 return GNUNET_SYSERR;
1458 if (GNUNET_SYSERR ==
1459 GNUNET_DISK_directory_scan (filename, &remove_helper, NULL))
1460 return GNUNET_SYSERR;
1461 if (0 != RMDIR (filename))
1463 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "rmdir", filename);
1464 return GNUNET_SYSERR;
1473 * @param src file to copy
1474 * @param dst destination file name
1475 * @return #GNUNET_OK on success, #GNUNET_SYSERR on error
1478 GNUNET_DISK_file_copy (const char *src,
1485 struct GNUNET_DISK_FileHandle *in;
1486 struct GNUNET_DISK_FileHandle *out;
1488 if (GNUNET_OK != GNUNET_DISK_file_size (src, &size, GNUNET_YES, GNUNET_YES))
1489 return GNUNET_SYSERR;
1491 in = GNUNET_DISK_file_open (src, GNUNET_DISK_OPEN_READ,
1492 GNUNET_DISK_PERM_NONE);
1494 return GNUNET_SYSERR;
1496 GNUNET_DISK_file_open (dst,
1497 GNUNET_DISK_OPEN_WRITE | GNUNET_DISK_OPEN_CREATE |
1498 GNUNET_DISK_OPEN_FAILIFEXISTS,
1499 GNUNET_DISK_PERM_USER_READ |
1500 GNUNET_DISK_PERM_USER_WRITE |
1501 GNUNET_DISK_PERM_GROUP_READ |
1502 GNUNET_DISK_PERM_GROUP_WRITE);
1505 GNUNET_DISK_file_close (in);
1506 return GNUNET_SYSERR;
1508 buf = GNUNET_malloc (COPY_BLK_SIZE);
1511 len = COPY_BLK_SIZE;
1512 if (len > size - pos)
1514 if (len != GNUNET_DISK_file_read (in, buf, len))
1516 if (len != GNUNET_DISK_file_write (out, buf, len))
1521 GNUNET_DISK_file_close (in);
1522 GNUNET_DISK_file_close (out);
1526 GNUNET_DISK_file_close (in);
1527 GNUNET_DISK_file_close (out);
1528 return GNUNET_SYSERR;
1533 * @brief Removes special characters as ':' from a filename.
1534 * @param fn the filename to canonicalize
1537 GNUNET_DISK_filename_canonicalize (char *fn)
1547 if (c == '/' || c == '\\' || c == ':' || c == '*' || c == '?' || c == '"' ||
1548 c == '<' || c == '>' || c == '|')
1560 * @brief Change owner of a file
1562 * @param filename name of file to change the owner of
1563 * @param user name of the new owner
1564 * @return GNUNET_OK on success, GNUNET_SYSERR on failure
1567 GNUNET_DISK_file_change_owner (const char *filename, const char *user)
1572 pws = getpwnam (user);
1575 LOG (GNUNET_ERROR_TYPE_ERROR,
1576 _("Cannot obtain information about user `%s': %s\n"), user,
1578 return GNUNET_SYSERR;
1580 if (0 != chown (filename, pws->pw_uid, pws->pw_gid))
1581 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "chown", filename);
1588 * Lock a part of a file
1589 * @param fh file handle
1590 * @param lock_start absolute position from where to lock
1591 * @param lock_end absolute position until where to lock
1592 * @param excl GNUNET_YES for an exclusive lock
1593 * @return GNUNET_OK on success, GNUNET_SYSERR on error
1596 GNUNET_DISK_file_lock (struct GNUNET_DISK_FileHandle *fh, off_t lock_start,
1597 off_t lock_end, int excl)
1602 return GNUNET_SYSERR;
1608 memset (&fl, 0, sizeof (struct flock));
1609 fl.l_type = excl ? F_WRLCK : F_RDLCK;
1610 fl.l_whence = SEEK_SET;
1611 fl.l_start = lock_start;
1612 fl.l_len = lock_end;
1614 return fcntl (fh->fd, F_SETLK, &fl) != 0 ? GNUNET_SYSERR : GNUNET_OK;
1617 off_t diff = lock_end - lock_start;
1618 DWORD diff_low, diff_high;
1619 diff_low = (DWORD) (diff & 0xFFFFFFFF);
1620 diff_high = (DWORD) ((diff >> (sizeof (DWORD) * 8)) & 0xFFFFFFFF);
1622 memset (&o, 0, sizeof (OVERLAPPED));
1623 o.Offset = (DWORD) (lock_start & 0xFFFFFFFF);;
1624 o.OffsetHigh = (DWORD) (((lock_start & ~0xFFFFFFFF) >> (sizeof (DWORD) * 8)) & 0xFFFFFFFF);
1627 (fh->h, (excl ? LOCKFILE_EXCLUSIVE_LOCK : 0) | LOCKFILE_FAIL_IMMEDIATELY,
1628 0, diff_low, diff_high, &o))
1630 SetErrnoFromWinError (GetLastError ());
1631 return GNUNET_SYSERR;
1640 * Unlock a part of a file
1641 * @param fh file handle
1642 * @param unlock_start absolute position from where to unlock
1643 * @param unlock_end absolute position until where to unlock
1644 * @return #GNUNET_OK on success, #GNUNET_SYSERR on error
1647 GNUNET_DISK_file_unlock (struct GNUNET_DISK_FileHandle *fh, off_t unlock_start,
1653 return GNUNET_SYSERR;
1659 memset (&fl, 0, sizeof (struct flock));
1660 fl.l_type = F_UNLCK;
1661 fl.l_whence = SEEK_SET;
1662 fl.l_start = unlock_start;
1663 fl.l_len = unlock_end;
1665 return fcntl (fh->fd, F_SETLK, &fl) != 0 ? GNUNET_SYSERR : GNUNET_OK;
1668 off_t diff = unlock_end - unlock_start;
1669 DWORD diff_low, diff_high;
1670 diff_low = (DWORD) (diff & 0xFFFFFFFF);
1671 diff_high = (DWORD) ((diff >> (sizeof (DWORD) * 8)) & 0xFFFFFFFF);
1673 memset (&o, 0, sizeof (OVERLAPPED));
1674 o.Offset = (DWORD) (unlock_start & 0xFFFFFFFF);;
1675 o.OffsetHigh = (DWORD) (((unlock_start & ~0xFFFFFFFF) >> (sizeof (DWORD) * 8)) & 0xFFFFFFFF);
1677 if (!UnlockFileEx (fh->h, 0, diff_low, diff_high, &o))
1679 SetErrnoFromWinError (GetLastError ());
1680 return GNUNET_SYSERR;
1689 * Open a file. Note that the access permissions will only be
1690 * used if a new file is created and if the underlying operating
1691 * system supports the given permissions.
1693 * @param fn file name to be opened
1694 * @param flags opening flags, a combination of GNUNET_DISK_OPEN_xxx bit flags
1695 * @param perm permissions for the newly created file, use
1696 * #GNUNET_DISK_PERM_NONE if a file could not be created by this
1697 * call (because of flags)
1698 * @return IO handle on success, NULL on error
1700 struct GNUNET_DISK_FileHandle *
1701 GNUNET_DISK_file_open (const char *fn,
1702 enum GNUNET_DISK_OpenFlags flags,
1703 enum GNUNET_DISK_AccessPermissions perm)
1706 struct GNUNET_DISK_FileHandle *ret;
1712 wchar_t wexpfn[MAX_PATH + 1];
1719 expfn = GNUNET_STRINGS_filename_expand (fn);
1724 if (GNUNET_DISK_OPEN_READWRITE == (flags & GNUNET_DISK_OPEN_READWRITE))
1725 oflags = O_RDWR; /* note: O_RDWR is NOT always O_RDONLY | O_WRONLY */
1726 else if (flags & GNUNET_DISK_OPEN_READ)
1728 else if (flags & GNUNET_DISK_OPEN_WRITE)
1733 GNUNET_free (expfn);
1736 if (flags & GNUNET_DISK_OPEN_FAILIFEXISTS)
1737 oflags |= (O_CREAT | O_EXCL);
1738 if (flags & GNUNET_DISK_OPEN_TRUNCATE)
1740 if (flags & GNUNET_DISK_OPEN_APPEND)
1742 if (flags & GNUNET_DISK_OPEN_CREATE)
1744 (void) GNUNET_DISK_directory_create_for_file (expfn);
1746 mode = translate_unix_perms (perm);
1749 fd = open (expfn, oflags
1753 | O_LARGEFILE, mode);
1756 if (0 == (flags & GNUNET_DISK_OPEN_FAILIFEXISTS))
1757 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "open", expfn);
1759 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_DEBUG, "open", expfn);
1760 GNUNET_free (expfn);
1767 if (GNUNET_DISK_OPEN_READWRITE == (flags & GNUNET_DISK_OPEN_READWRITE))
1768 access = FILE_READ_DATA | FILE_WRITE_DATA;
1769 else if (flags & GNUNET_DISK_OPEN_READ)
1770 access = FILE_READ_DATA;
1771 else if (flags & GNUNET_DISK_OPEN_WRITE)
1772 access = FILE_WRITE_DATA;
1774 if (flags & GNUNET_DISK_OPEN_FAILIFEXISTS)
1778 else if (flags & GNUNET_DISK_OPEN_CREATE)
1780 (void) GNUNET_DISK_directory_create_for_file (expfn);
1781 if (flags & GNUNET_DISK_OPEN_TRUNCATE)
1782 disp = CREATE_ALWAYS;
1786 else if (flags & GNUNET_DISK_OPEN_TRUNCATE)
1788 disp = TRUNCATE_EXISTING;
1792 disp = OPEN_EXISTING;
1795 if (ERROR_SUCCESS == plibc_conv_to_win_pathwconv(expfn, wexpfn))
1796 h = CreateFileW (wexpfn, access,
1797 FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE, NULL,
1798 disp, FILE_ATTRIBUTE_NORMAL, NULL);
1800 h = INVALID_HANDLE_VALUE;
1801 if (h == INVALID_HANDLE_VALUE)
1804 SetErrnoFromWinError (GetLastError ());
1806 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_INFO, "open", expfn);
1807 GNUNET_free (expfn);
1812 if (flags & GNUNET_DISK_OPEN_APPEND)
1813 if (SetFilePointer (h, 0, 0, FILE_END) == INVALID_SET_FILE_POINTER)
1815 SetErrnoFromWinError (GetLastError ());
1816 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "SetFilePointer", expfn);
1818 GNUNET_free (expfn);
1823 ret = GNUNET_new (struct GNUNET_DISK_FileHandle);
1826 ret->type = GNUNET_DISK_HANLDE_TYPE_FILE;
1830 GNUNET_free (expfn);
1836 * Close an open file
1837 * @param h file handle
1838 * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
1841 GNUNET_DISK_file_close (struct GNUNET_DISK_FileHandle *h)
1847 return GNUNET_SYSERR;
1853 if (!CloseHandle (h->h))
1855 SetErrnoFromWinError (GetLastError ());
1856 LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING, "close");
1857 ret = GNUNET_SYSERR;
1859 if (h->oOverlapRead)
1861 if (!CloseHandle (h->oOverlapRead->hEvent))
1863 SetErrnoFromWinError (GetLastError ());
1864 LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING, "close");
1865 ret = GNUNET_SYSERR;
1867 GNUNET_free (h->oOverlapRead);
1869 if (h->oOverlapWrite)
1871 if (!CloseHandle (h->oOverlapWrite->hEvent))
1873 SetErrnoFromWinError (GetLastError ());
1874 LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING, "close");
1875 ret = GNUNET_SYSERR;
1877 GNUNET_free (h->oOverlapWrite);
1880 if (close (h->fd) != 0)
1882 LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING, "close");
1883 ret = GNUNET_SYSERR;
1892 * Get a GNUnet file handle from a W32 handle.
1894 * @param handle native handle
1895 * @return GNUnet file handle corresponding to the W32 handle
1897 struct GNUNET_DISK_FileHandle *
1898 GNUNET_DISK_get_handle_from_w32_handle (HANDLE osfh)
1900 struct GNUNET_DISK_FileHandle *fh;
1903 enum GNUNET_FILE_Type ftype;
1905 dwret = GetFileType (osfh);
1908 case FILE_TYPE_DISK:
1909 ftype = GNUNET_DISK_HANLDE_TYPE_FILE;
1911 case FILE_TYPE_PIPE:
1912 ftype = GNUNET_DISK_HANLDE_TYPE_PIPE;
1914 case FILE_TYPE_UNKNOWN:
1915 if (GetLastError () == NO_ERROR)
1917 if (0 != ResetEvent (osfh))
1918 ftype = GNUNET_DISK_HANLDE_TYPE_EVENT;
1927 fh = GNUNET_new (struct GNUNET_DISK_FileHandle);
1931 if (ftype == GNUNET_DISK_HANLDE_TYPE_PIPE)
1934 * Note that we can't make it overlapped if it isn't already.
1935 * (ReOpenFile() is only available in 2003/Vista).
1936 * The process that opened this file in the first place (usually a parent
1937 * process, if this is stdin/stdout/stderr) must make it overlapped,
1938 * otherwise we're screwed, as selecting on non-overlapped handle
1941 fh->oOverlapRead = GNUNET_new (OVERLAPPED);
1942 fh->oOverlapWrite = GNUNET_new (OVERLAPPED);
1943 fh->oOverlapRead->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
1944 fh->oOverlapWrite->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
1952 * Get a handle from a native integer FD.
1954 * @param fno native integer file descriptor
1955 * @return file handle corresponding to the descriptor, NULL on error
1957 struct GNUNET_DISK_FileHandle *
1958 GNUNET_DISK_get_handle_from_int_fd (int fno)
1960 struct GNUNET_DISK_FileHandle *fh;
1962 if ( (((off_t) -1) == lseek (fno, 0, SEEK_CUR)) &&
1964 return NULL; /* invalid FD */
1967 fh = GNUNET_new (struct GNUNET_DISK_FileHandle);
1973 osfh = _get_osfhandle (fno);
1974 if (INVALID_HANDLE_VALUE == (HANDLE) osfh)
1977 fh = GNUNET_DISK_get_handle_from_w32_handle ((HANDLE) osfh);
1985 * Get a handle from a native streaming FD.
1987 * @param fd native streaming file descriptor
1988 * @return file handle corresponding to the descriptor
1990 struct GNUNET_DISK_FileHandle *
1991 GNUNET_DISK_get_handle_from_native (FILE *fd)
1999 return GNUNET_DISK_get_handle_from_int_fd (fno);
2004 * Handle for a memory-mapping operation.
2006 struct GNUNET_DISK_MapHandle
2009 * Address where the map is in memory.
2015 * Underlying OS handle.
2020 * Number of bytes mapped.
2028 #define MAP_FAILED ((void *) -1)
2032 * Map a file into memory
2034 * @param h open file handle
2035 * @param m handle to the new mapping
2036 * @param access access specification, GNUNET_DISK_MAP_TYPE_xxx
2037 * @param len size of the mapping
2038 * @return pointer to the mapped memory region, NULL on failure
2041 GNUNET_DISK_file_map (const struct GNUNET_DISK_FileHandle *h,
2042 struct GNUNET_DISK_MapHandle **m,
2043 enum GNUNET_DISK_MapType access, size_t len)
2052 DWORD mapAccess, protect;
2054 if ((access & GNUNET_DISK_MAP_TYPE_READ) &&
2055 (access & GNUNET_DISK_MAP_TYPE_WRITE))
2057 protect = PAGE_READWRITE;
2058 mapAccess = FILE_MAP_ALL_ACCESS;
2060 else if (access & GNUNET_DISK_MAP_TYPE_READ)
2062 protect = PAGE_READONLY;
2063 mapAccess = FILE_MAP_READ;
2065 else if (access & GNUNET_DISK_MAP_TYPE_WRITE)
2067 protect = PAGE_READWRITE;
2068 mapAccess = FILE_MAP_WRITE;
2076 *m = GNUNET_new (struct GNUNET_DISK_MapHandle);
2077 (*m)->h = CreateFileMapping (h->h, NULL, protect, 0, 0, NULL);
2078 if ((*m)->h == INVALID_HANDLE_VALUE)
2080 SetErrnoFromWinError (GetLastError ());
2085 (*m)->addr = MapViewOfFile ((*m)->h, mapAccess, 0, 0, len);
2088 SetErrnoFromWinError (GetLastError ());
2089 CloseHandle ((*m)->h);
2098 if (access & GNUNET_DISK_MAP_TYPE_READ)
2100 if (access & GNUNET_DISK_MAP_TYPE_WRITE)
2102 *m = GNUNET_new (struct GNUNET_DISK_MapHandle);
2103 (*m)->addr = mmap (NULL, len, prot, MAP_SHARED, h->fd, 0);
2104 GNUNET_assert (NULL != (*m)->addr);
2105 if (MAP_FAILED == (*m)->addr)
2117 * @param h mapping handle
2118 * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
2121 GNUNET_DISK_file_unmap (struct GNUNET_DISK_MapHandle *h)
2128 return GNUNET_SYSERR;
2132 ret = UnmapViewOfFile (h->addr) ? GNUNET_OK : GNUNET_SYSERR;
2133 if (ret != GNUNET_OK)
2134 SetErrnoFromWinError (GetLastError ());
2135 if (!CloseHandle (h->h) && (ret == GNUNET_OK))
2137 ret = GNUNET_SYSERR;
2138 SetErrnoFromWinError (GetLastError ());
2141 ret = munmap (h->addr, h->len) != -1 ? GNUNET_OK : GNUNET_SYSERR;
2149 * Write file changes to disk
2150 * @param h handle to an open file
2151 * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
2154 GNUNET_DISK_file_sync (const struct GNUNET_DISK_FileHandle *h)
2159 return GNUNET_SYSERR;
2165 ret = FlushFileBuffers (h->h) ? GNUNET_OK : GNUNET_SYSERR;
2166 if (ret != GNUNET_OK)
2167 SetErrnoFromWinError (GetLastError ());
2169 #elif defined(FREEBSD) || defined(OPENBSD) || defined(DARWIN)
2170 return fsync (h->fd) == -1 ? GNUNET_SYSERR : GNUNET_OK;
2172 return fdatasync (h->fd) == -1 ? GNUNET_SYSERR : GNUNET_OK;
2179 #define PIPE_BUF 512
2181 /* Copyright Bob Byrnes <byrnes <at> curl.com>
2182 http://permalink.gmane.org/gmane.os.cygwin.patches/2121
2184 /* Create a pipe, and return handles to the read and write ends,
2185 just like CreatePipe, but ensure that the write end permits
2186 FILE_READ_ATTRIBUTES access, on later versions of win32 where
2187 this is supported. This access is needed by NtQueryInformationFile,
2188 which is used to implement select and nonblocking writes.
2189 Note that the return value is either NO_ERROR or GetLastError,
2190 unlike CreatePipe, which returns a bool for success or failure. */
2192 create_selectable_pipe (PHANDLE read_pipe_ptr, PHANDLE write_pipe_ptr,
2193 LPSECURITY_ATTRIBUTES sa_ptr, DWORD psize,
2194 DWORD dwReadMode, DWORD dwWriteMode)
2196 /* Default to error. */
2197 *read_pipe_ptr = *write_pipe_ptr = INVALID_HANDLE_VALUE;
2202 /* Ensure that there is enough pipe buffer space for atomic writes. */
2203 if (psize < PIPE_BUF)
2206 char pipename[MAX_PATH];
2208 /* Retry CreateNamedPipe as long as the pipe name is in use.
2209 * Retrying will probably never be necessary, but we want
2210 * to be as robust as possible. */
2213 static volatile LONG pipe_unique_id;
2215 snprintf (pipename, sizeof pipename, "\\\\.\\pipe\\gnunet-%d-%ld",
2216 getpid (), InterlockedIncrement ((LONG *) & pipe_unique_id));
2217 LOG (GNUNET_ERROR_TYPE_DEBUG, "CreateNamedPipe: name = %s, size = %lu\n",
2219 /* Use CreateNamedPipe instead of CreatePipe, because the latter
2220 * returns a write handle that does not permit FILE_READ_ATTRIBUTES
2221 * access, on versions of win32 earlier than WinXP SP2.
2222 * CreatePipe also stupidly creates a full duplex pipe, which is
2223 * a waste, since only a single direction is actually used.
2224 * It's important to only allow a single instance, to ensure that
2225 * the pipe was not created earlier by some other process, even if
2226 * the pid has been reused. */
2227 read_pipe = CreateNamedPipeA (pipename, PIPE_ACCESS_INBOUND | FILE_FLAG_FIRST_PIPE_INSTANCE | dwReadMode, PIPE_TYPE_BYTE | PIPE_READMODE_BYTE, 1, /* max instances */
2228 psize, /* output buffer size */
2229 psize, /* input buffer size */
2230 NMPWAIT_USE_DEFAULT_WAIT, sa_ptr);
2232 if (read_pipe != INVALID_HANDLE_VALUE)
2234 LOG (GNUNET_ERROR_TYPE_DEBUG, "pipe read handle = %p\n", read_pipe);
2238 DWORD err = GetLastError ();
2242 case ERROR_PIPE_BUSY:
2243 /* The pipe is already open with compatible parameters.
2244 * Pick a new name and retry. */
2245 LOG (GNUNET_ERROR_TYPE_DEBUG, "pipe busy, retrying\n");
2247 case ERROR_ACCESS_DENIED:
2248 /* The pipe is already open with incompatible parameters.
2249 * Pick a new name and retry. */
2250 LOG (GNUNET_ERROR_TYPE_DEBUG, "pipe access denied, retrying\n");
2252 case ERROR_CALL_NOT_IMPLEMENTED:
2253 /* We are on an older Win9x platform without named pipes.
2254 * Return an anonymous pipe as the best approximation. */
2255 LOG (GNUNET_ERROR_TYPE_DEBUG,
2256 "CreateNamedPipe not implemented, resorting to "
2257 "CreatePipe: size = %lu\n", psize);
2258 if (CreatePipe (read_pipe_ptr, write_pipe_ptr, sa_ptr, psize))
2260 LOG (GNUNET_ERROR_TYPE_DEBUG, "pipe read handle = %p, write handle = %p\n",
2265 err = GetLastError ();
2266 LOG (GNUNET_ERROR_TYPE_ERROR, "CreatePipe failed: %d\n", err);
2269 LOG (GNUNET_ERROR_TYPE_ERROR, "CreateNamedPipe failed: %d\n", err);
2274 LOG (GNUNET_ERROR_TYPE_DEBUG, "CreateFile: name = %s\n", pipename);
2276 /* Open the named pipe for writing.
2277 * Be sure to permit FILE_READ_ATTRIBUTES access. */
2278 write_pipe = CreateFileA (pipename, GENERIC_WRITE | FILE_READ_ATTRIBUTES, 0, /* share mode */
2279 sa_ptr, OPEN_EXISTING, dwWriteMode, /* flags and attributes */
2280 0); /* handle to template file */
2282 if (write_pipe == INVALID_HANDLE_VALUE)
2285 DWORD err = GetLastError ();
2287 LOG (GNUNET_ERROR_TYPE_DEBUG, "CreateFile failed: %d\n", err);
2288 CloseHandle (read_pipe);
2291 LOG (GNUNET_ERROR_TYPE_DEBUG, "pipe write handle = %p\n", write_pipe);
2293 *read_pipe_ptr = read_pipe;
2294 *write_pipe_ptr = write_pipe;
2301 * Creates an interprocess channel
2303 * @param blocking_read creates an asynchronous pipe for reading if set to GNUNET_NO
2304 * @param blocking_write creates an asynchronous pipe for writing if set to GNUNET_NO
2305 * @param inherit_read inherit the parent processes stdin (only for windows)
2306 * @param inherit_write inherit the parent processes stdout (only for windows)
2307 * @return handle to the new pipe, NULL on error
2309 struct GNUNET_DISK_PipeHandle *
2310 GNUNET_DISK_pipe (int blocking_read, int blocking_write, int inherit_read, int inherit_write)
2321 LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR, "pipe");
2325 return GNUNET_DISK_pipe_from_fd (blocking_read,
2329 struct GNUNET_DISK_PipeHandle *p;
2334 p = GNUNET_new (struct GNUNET_DISK_PipeHandle);
2335 p->fd[0] = GNUNET_new (struct GNUNET_DISK_FileHandle);
2336 p->fd[1] = GNUNET_new (struct GNUNET_DISK_FileHandle);
2338 /* All pipes are overlapped. If you want them to block - just
2339 * call WriteFile() and ReadFile() with NULL overlapped pointer.
2340 * NOTE: calling with NULL overlapped pointer works only
2341 * for pipes, and doesn't seem to be a documented feature.
2342 * It will NOT work for files, because overlapped files need
2343 * to read offsets from the overlapped structure, regardless.
2344 * Pipes are not seekable, and need no offsets, which is
2345 * probably why it works for them.
2348 create_selectable_pipe (&p->fd[0]->h, &p->fd[1]->h, NULL, 0,
2349 FILE_FLAG_OVERLAPPED,
2350 FILE_FLAG_OVERLAPPED);
2353 SetErrnoFromWinError (GetLastError ());
2355 GNUNET_free (p->fd[0]);
2356 GNUNET_free (p->fd[1]);
2361 if (!DuplicateHandle
2362 (GetCurrentProcess (), p->fd[0]->h, GetCurrentProcess (), &tmp_handle, 0,
2363 inherit_read == GNUNET_YES ? TRUE : FALSE, DUPLICATE_SAME_ACCESS))
2365 SetErrnoFromWinError (GetLastError ());
2367 CloseHandle (p->fd[0]->h);
2368 CloseHandle (p->fd[1]->h);
2369 GNUNET_free (p->fd[0]);
2370 GNUNET_free (p->fd[1]);
2375 CloseHandle (p->fd[0]->h);
2376 p->fd[0]->h = tmp_handle;
2378 if (!DuplicateHandle
2379 (GetCurrentProcess (), p->fd[1]->h, GetCurrentProcess (), &tmp_handle, 0,
2380 inherit_write == GNUNET_YES ? TRUE : FALSE, DUPLICATE_SAME_ACCESS))
2382 SetErrnoFromWinError (GetLastError ());
2384 CloseHandle (p->fd[0]->h);
2385 CloseHandle (p->fd[1]->h);
2386 GNUNET_free (p->fd[0]);
2387 GNUNET_free (p->fd[1]);
2392 CloseHandle (p->fd[1]->h);
2393 p->fd[1]->h = tmp_handle;
2395 p->fd[0]->type = GNUNET_DISK_HANLDE_TYPE_PIPE;
2396 p->fd[1]->type = GNUNET_DISK_HANLDE_TYPE_PIPE;
2398 p->fd[0]->oOverlapRead = GNUNET_new (OVERLAPPED);
2399 p->fd[0]->oOverlapWrite = GNUNET_new (OVERLAPPED);
2400 p->fd[1]->oOverlapRead = GNUNET_new (OVERLAPPED);
2401 p->fd[1]->oOverlapWrite = GNUNET_new (OVERLAPPED);
2403 p->fd[0]->oOverlapRead->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2404 p->fd[0]->oOverlapWrite->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2406 p->fd[1]->oOverlapRead->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2407 p->fd[1]->oOverlapWrite->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2415 * Creates a pipe object from a couple of file descriptors.
2416 * Useful for wrapping existing pipe FDs.
2418 * @param blocking_read creates an asynchronous pipe for reading if set to GNUNET_NO
2419 * @param blocking_write creates an asynchronous pipe for writing if set to GNUNET_NO
2420 * @param fd an array of two fd values. One of them may be -1 for read-only or write-only pipes
2422 * @return handle to the new pipe, NULL on error
2424 struct GNUNET_DISK_PipeHandle *
2425 GNUNET_DISK_pipe_from_fd (int blocking_read, int blocking_write, int fd[2])
2427 struct GNUNET_DISK_PipeHandle *p;
2429 p = GNUNET_new (struct GNUNET_DISK_PipeHandle);
2434 int eno = 0; /* make gcc happy */
2439 p->fd[0] = GNUNET_new (struct GNUNET_DISK_FileHandle);
2440 p->fd[0]->fd = fd[0];
2443 flags = fcntl (fd[0], F_GETFL);
2444 flags |= O_NONBLOCK;
2445 if (0 > fcntl (fd[0], F_SETFL, flags))
2451 flags = fcntl (fd[0], F_GETFD);
2452 flags |= FD_CLOEXEC;
2453 if (0 > fcntl (fd[0], F_SETFD, flags))
2462 p->fd[1] = GNUNET_new (struct GNUNET_DISK_FileHandle);
2463 p->fd[1]->fd = fd[1];
2464 if (!blocking_write)
2466 flags = fcntl (fd[1], F_GETFL);
2467 flags |= O_NONBLOCK;
2468 if (0 > fcntl (fd[1], F_SETFL, flags))
2474 flags = fcntl (fd[1], F_GETFD);
2475 flags |= FD_CLOEXEC;
2476 if (0 > fcntl (fd[1], F_SETFD, flags))
2485 LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR, "fcntl");
2486 if (p->fd[0]->fd >= 0)
2487 GNUNET_break (0 == close (p->fd[0]->fd));
2488 if (p->fd[1]->fd >= 0)
2489 GNUNET_break (0 == close (p->fd[1]->fd));
2490 GNUNET_free_non_null (p->fd[0]);
2491 GNUNET_free_non_null (p->fd[1]);
2499 p->fd[0] = GNUNET_new (struct GNUNET_DISK_FileHandle);
2500 p->fd[0]->h = (HANDLE) _get_osfhandle (fd[0]);
2501 if (p->fd[0]->h != INVALID_HANDLE_VALUE)
2503 p->fd[0]->type = GNUNET_DISK_HANLDE_TYPE_PIPE;
2504 p->fd[0]->oOverlapRead = GNUNET_new (OVERLAPPED);
2505 p->fd[0]->oOverlapWrite = GNUNET_new (OVERLAPPED);
2506 p->fd[0]->oOverlapRead->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2507 p->fd[0]->oOverlapWrite->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2511 GNUNET_free (p->fd[0]);
2517 p->fd[1] = GNUNET_new (struct GNUNET_DISK_FileHandle);
2518 p->fd[1]->h = (HANDLE) _get_osfhandle (fd[1]);
2519 if (p->fd[1]->h != INVALID_HANDLE_VALUE)
2521 p->fd[1]->type = GNUNET_DISK_HANLDE_TYPE_PIPE;
2522 p->fd[1]->oOverlapRead = GNUNET_new (OVERLAPPED);
2523 p->fd[1]->oOverlapWrite = GNUNET_new (OVERLAPPED);
2524 p->fd[1]->oOverlapRead->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2525 p->fd[1]->oOverlapWrite->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2529 GNUNET_free (p->fd[1]);
2540 * Closes an interprocess channel
2542 * @param p pipe to close
2543 * @param end which end of the pipe to close
2544 * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
2547 GNUNET_DISK_pipe_close_end (struct GNUNET_DISK_PipeHandle *p,
2548 enum GNUNET_DISK_PipeEnd end)
2550 int ret = GNUNET_OK;
2552 if (end == GNUNET_DISK_PIPE_END_READ)
2556 ret = GNUNET_DISK_file_close (p->fd[0]);
2560 else if (end == GNUNET_DISK_PIPE_END_WRITE)
2564 ret = GNUNET_DISK_file_close (p->fd[1]);
2573 * Detaches one of the ends from the pipe.
2574 * Detached end is a fully-functional FileHandle, it will
2575 * not be affected by anything you do with the pipe afterwards.
2576 * Each end of a pipe can only be detched from it once (i.e.
2577 * it is not duplicated).
2579 * @param p pipe to detach an end from
2580 * @param end which end of the pipe to detach
2581 * @return Detached end on success, NULL on failure
2582 * (or if that end is not present or is closed).
2584 struct GNUNET_DISK_FileHandle *
2585 GNUNET_DISK_pipe_detach_end (struct GNUNET_DISK_PipeHandle *p,
2586 enum GNUNET_DISK_PipeEnd end)
2588 struct GNUNET_DISK_FileHandle *ret = NULL;
2590 if (end == GNUNET_DISK_PIPE_END_READ)
2598 else if (end == GNUNET_DISK_PIPE_END_WRITE)
2612 * Closes an interprocess channel
2614 * @param p pipe to close
2615 * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
2618 GNUNET_DISK_pipe_close (struct GNUNET_DISK_PipeHandle *p)
2620 int ret = GNUNET_OK;
2623 int write_end_close;
2624 int read_end_close_errno;
2625 int write_end_close_errno;
2627 read_end_close = GNUNET_DISK_pipe_close_end (p, GNUNET_DISK_PIPE_END_READ);
2628 read_end_close_errno = errno;
2629 write_end_close = GNUNET_DISK_pipe_close_end (p, GNUNET_DISK_PIPE_END_WRITE);
2630 write_end_close_errno = errno;
2633 if (GNUNET_OK != read_end_close)
2635 errno = read_end_close_errno;
2636 ret = read_end_close;
2638 else if (GNUNET_OK != write_end_close)
2640 errno = write_end_close_errno;
2641 ret = write_end_close;
2649 * Get the handle to a particular pipe end
2652 * @param n end to access
2653 * @return handle for the respective end
2655 const struct GNUNET_DISK_FileHandle *
2656 GNUNET_DISK_pipe_handle (const struct GNUNET_DISK_PipeHandle *p,
2657 enum GNUNET_DISK_PipeEnd n)
2661 case GNUNET_DISK_PIPE_END_READ:
2662 case GNUNET_DISK_PIPE_END_WRITE:
2672 * Retrieve OS file handle
2674 * @param fh GNUnet file descriptor
2675 * @param dst destination buffer
2676 * @param dst_len length of dst
2677 * @return #GNUNET_OK on success, #GNUNET_SYSERR otherwise
2680 GNUNET_DISK_internal_file_handle_ (const struct GNUNET_DISK_FileHandle *fh,
2681 void *dst, size_t dst_len)
2684 return GNUNET_SYSERR;
2686 if (dst_len < sizeof (HANDLE))
2687 return GNUNET_SYSERR;
2688 *((HANDLE *) dst) = fh->h;
2690 if (dst_len < sizeof (int))
2691 return GNUNET_SYSERR;
2692 *((int *) dst) = fh->fd;