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_directories.h"
30 #include "gnunet_util_lib.h"
33 #define LOG(kind,...) GNUNET_log_from (kind, "util", __VA_ARGS__)
35 #define LOG_STRERROR(kind,syscall) GNUNET_log_from_strerror (kind, "util", syscall)
37 #define LOG_STRERROR_FILE(kind,syscall,filename) GNUNET_log_from_strerror_file (kind, "util", syscall, filename)
40 * Block size for IO for copying files.
42 #define COPY_BLK_SIZE 65536
44 #include <sys/types.h>
49 #include <sys/param.h>
52 #include <sys/mount.h>
54 #if HAVE_SYS_STATVFS_H
55 #include <sys/statvfs.h>
59 #define _IFMT 0170000 /* type of file */
60 #define _IFLNK 0120000 /* symbolic link */
61 #define S_ISLNK(m) (((m)&_IFMT) == _IFLNK)
66 * Handle used to manage a pipe.
68 struct GNUNET_DISK_PipeHandle
71 * File descriptors for the pipe.
72 * One or both of them could be NULL.
74 struct GNUNET_DISK_FileHandle *fd[2];
79 * Closure for the recursion to determine the file size
82 struct GetFileSizeData
85 * Set to the total file size.
90 * GNUNET_YES if symbolic links should be included.
92 int include_sym_links;
95 * GNUNET_YES if mode is file-only (return total == -1 for directories).
103 * Translate GNUnet-internal permission bitmap to UNIX file
104 * access permission bitmap.
106 * @param perm file permissions, GNUnet style
107 * @return file permissions, UNIX style
110 translate_unix_perms (enum GNUNET_DISK_AccessPermissions perm)
115 if (perm & GNUNET_DISK_PERM_USER_READ)
117 if (perm & GNUNET_DISK_PERM_USER_WRITE)
119 if (perm & GNUNET_DISK_PERM_USER_EXEC)
121 if (perm & GNUNET_DISK_PERM_GROUP_READ)
123 if (perm & GNUNET_DISK_PERM_GROUP_WRITE)
125 if (perm & GNUNET_DISK_PERM_GROUP_EXEC)
127 if (perm & GNUNET_DISK_PERM_OTHER_READ)
129 if (perm & GNUNET_DISK_PERM_OTHER_WRITE)
131 if (perm & GNUNET_DISK_PERM_OTHER_EXEC)
140 * Iterate over all files in the given directory and
141 * accumulate their size.
143 * @param cls closure of type "struct GetFileSizeData"
144 * @param fn current filename we are looking at
145 * @return GNUNET_SYSERR on serious errors, otherwise GNUNET_OK
148 getSizeRec (void *cls, const char *fn)
150 struct GetFileSizeData *gfsd = cls;
152 #if defined (HAVE_STAT64) && !(defined (_FILE_OFFSET_BITS) && _FILE_OFFSET_BITS == 64)
155 if (0 != STAT64 (fn, &buf))
157 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_DEBUG, "stat64", fn);
158 return GNUNET_SYSERR;
163 if (0 != STAT (fn, &buf))
165 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_DEBUG, "stat", fn);
166 return GNUNET_SYSERR;
169 if ((S_ISDIR (buf.st_mode)) && (gfsd->single_file_mode == GNUNET_YES))
172 return GNUNET_SYSERR;
174 if ((!S_ISLNK (buf.st_mode)) || (gfsd->include_sym_links == GNUNET_YES))
175 gfsd->total += buf.st_size;
176 if ((S_ISDIR (buf.st_mode)) && (0 == ACCESS (fn, X_OK)) &&
177 ((!S_ISLNK (buf.st_mode)) || (gfsd->include_sym_links == GNUNET_YES)))
179 if (GNUNET_SYSERR == GNUNET_DISK_directory_scan (fn, &getSizeRec, gfsd))
180 return GNUNET_SYSERR;
187 * Checks whether a handle is invalid
189 * @param h handle to check
190 * @return GNUNET_YES if invalid, GNUNET_NO if valid
193 GNUNET_DISK_handle_invalid (const struct GNUNET_DISK_FileHandle *h)
196 return ((!h) || (h->h == INVALID_HANDLE_VALUE)) ? GNUNET_YES : GNUNET_NO;
198 return ((!h) || (h->fd == -1)) ? GNUNET_YES : GNUNET_NO;
203 * Get the size of an open file.
205 * @param fh open file handle
206 * @param size where to write size of the file
207 * @return GNUNET_OK on success, GNUNET_SYSERR on error
210 GNUNET_DISK_file_handle_size (struct GNUNET_DISK_FileHandle *fh,
216 b = GetFileSizeEx (fh->h, &li);
219 SetErrnoFromWinError (GetLastError ());
220 return GNUNET_SYSERR;
222 *size = (off_t) li.QuadPart;
226 if (0 != FSTAT (fh->fd, &sbuf))
227 return GNUNET_SYSERR;
228 *size = sbuf.st_size;
235 * Move the read/write pointer in a file
237 * @param h handle of an open file
238 * @param offset position to move to
239 * @param whence specification to which position the offset parameter relates to
240 * @return the new position on success, GNUNET_SYSERR otherwise
243 GNUNET_DISK_file_seek (const struct GNUNET_DISK_FileHandle * h, off_t offset,
244 enum GNUNET_DISK_Seek whence)
249 return GNUNET_SYSERR;
254 LARGE_INTEGER new_pos;
257 static DWORD t[] = { FILE_BEGIN, FILE_CURRENT, FILE_END };
258 li.QuadPart = offset;
260 b = SetFilePointerEx (h->h, li, &new_pos, t[whence]);
263 SetErrnoFromWinError (GetLastError ());
264 return GNUNET_SYSERR;
266 return (off_t) new_pos.QuadPart;
268 static int t[] = { SEEK_SET, SEEK_CUR, SEEK_END };
270 return lseek (h->fd, offset, t[whence]);
276 * Get the size of the file (or directory) of the given file (in
279 * @param filename name of the file or directory
280 * @param size set to the size of the file (or,
281 * in the case of directories, the sum
282 * of all sizes of files in the directory)
283 * @param include_symbolic_links should symbolic links be
285 * @param single_file_mode GNUNET_YES to only get size of one file
286 * and return GNUNET_SYSERR for directories.
287 * @return GNUNET_SYSERR on error, GNUNET_OK on success
290 GNUNET_DISK_file_size (const char *filename, uint64_t * size,
291 int include_symbolic_links, int single_file_mode)
293 struct GetFileSizeData gfsd;
296 GNUNET_assert (size != NULL);
298 gfsd.include_sym_links = include_symbolic_links;
299 gfsd.single_file_mode = single_file_mode;
300 ret = getSizeRec (&gfsd, filename);
307 * Obtain some unique identifiers for the given file
308 * that can be used to identify it in the local system.
309 * This function is used between GNUnet processes to
310 * quickly check if two files with the same absolute path
311 * are actually identical. The two processes represent
312 * the same peer but may communicate over the network
313 * (and the file may be on an NFS volume). This function
314 * may not be supported on all operating systems.
316 * @param filename name of the file
317 * @param dev set to the device ID
318 * @param ino set to the inode ID
319 * @return #GNUNET_OK on success
322 GNUNET_DISK_file_get_identifiers (const char *filename, uint64_t * dev,
327 // FIXME NILS: test this
328 struct GNUNET_DISK_FileHandle *fh;
329 BY_HANDLE_FILE_INFORMATION info;
332 fh = GNUNET_DISK_file_open (filename, GNUNET_DISK_OPEN_READ, 0);
334 return GNUNET_SYSERR;
335 succ = GetFileInformationByHandle (fh->h, &info);
336 GNUNET_DISK_file_close (fh);
339 return GNUNET_SYSERR;
341 *dev = info.dwVolumeSerialNumber;
342 *ino = ((((uint64_t) info.nFileIndexHigh) << (sizeof (DWORD) * 8)) | info.nFileIndexLow);
349 if (0 != stat (filename, &sbuf))
351 return GNUNET_SYSERR;
353 *ino = (uint64_t) sbuf.st_ino;
362 if (0 != statvfs (filename, &fbuf))
364 return GNUNET_SYSERR;
366 *dev = (uint64_t) fbuf.f_fsid;
372 if (0 != statfs (filename, &fbuf))
374 return GNUNET_SYSERR;
376 *dev = ((uint64_t) fbuf.f_fsid.val[0]) << 32 ||
377 ((uint64_t) fbuf.f_fsid.val[1]);
382 #endif /* !WINDOWS */
388 * Create the name for a temporary file or directory from a template.
390 * @param t template (without XXXXX or "/tmp/")
391 * @return name ready for passing to 'mktemp' or 'mkdtemp', NULL on error
394 mktemp_name (const char *t)
400 if ((t[0] != '/') && (t[0] != '\\')
402 && !(isalpha ((int) t[0]) && (t[0] != '\0') && (t[1] == ':'))
406 /* FIXME: This uses system codepage on W32, not UTF-8 */
407 tmpdir = getenv ("TMPDIR");
409 tmpdir = getenv ("TMP");
411 tmpdir = getenv ("TEMP");
414 GNUNET_asprintf (&tmpl, "%s/%s%s", tmpdir, t, "XXXXXX");
418 GNUNET_asprintf (&tmpl, "%s%s", t, "XXXXXX");
421 fn = (char *) GNUNET_malloc (MAX_PATH + 1);
422 if (ERROR_SUCCESS != plibc_conv_to_win_path (tmpl, fn))
445 tfn = GNUNET_strdup (fn);
446 random_fn = _mktemp (tfn);
447 if (NULL == random_fn)
452 /* FIXME: assume fn to be UTF-8-encoded and do the right thing */
453 if (0 == CreateDirectoryA (tfn, NULL))
455 DWORD error = GetLastError ();
457 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).
472 * @param fn name of the file to update
473 * @param require_uid_match #GNUNET_YES means 700
474 * @param require_gid_match #GNUNET_YES means 770 unless @a require_uid_match is set
477 GNUNET_DISK_fix_permissions (const char *fn,
478 int require_uid_match,
479 int require_gid_match)
483 if (GNUNET_YES == require_uid_match)
484 mode = S_IRUSR | S_IWUSR | S_IXUSR;
485 else if (GNUNET_YES == require_gid_match)
486 mode = S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IWGRP | S_IXGRP;
488 mode = S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IWGRP | S_IXGRP | S_IROTH | S_IWOTH | S_IXOTH;
489 if (0 != chmod (fn, mode))
490 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING,
498 * Create an (empty) temporary directory on disk. If the given name is not
499 * an absolute path, the current 'TMPDIR' will be prepended. In any case,
500 * 6 random characters will be appended to the name to create a unique
503 * @param t component to use for the name;
504 * does NOT contain "XXXXXX" or "/tmp/".
505 * @return NULL on error, otherwise name of fresh
506 * file on disk in directory for temporary files
509 GNUNET_DISK_mkdtemp (const char *t)
513 fn = mktemp_name (t);
514 if (fn != mkdtemp (fn))
516 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_ERROR, "mkstemp", fn);
525 * Move a file out of the way (create a backup) by
526 * renaming it to "orig.NUM~" where NUM is the smallest
527 * number that is not used yet.
529 * @param fil name of the file to back up
532 GNUNET_DISK_file_backup (const char *fil)
538 slen = strlen (fil) + 20;
539 target = GNUNET_malloc (slen);
543 GNUNET_snprintf (target, slen,
547 } while (0 == access (target, F_OK));
548 if (0 != rename (fil, target))
549 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR,
552 GNUNET_free (target);
557 * Create an (empty) temporary file on disk. If the given name is not
558 * an absolute path, the current 'TMPDIR' will be prepended. In any case,
559 * 6 random characters will be appended to the name to create a unique
562 * @param t component to use for the name;
563 * does NOT contain "XXXXXX" or "/tmp/".
564 * @return NULL on error, otherwise name of fresh
565 * file on disk in directory for temporary files
568 GNUNET_DISK_mktemp (const char *t)
573 fn = mktemp_name (t);
574 if (-1 == (fd = mkstemp (fn)))
576 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_ERROR, "mkstemp", fn);
581 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "close", fn);
587 * Test if @a fil is a directory and listable. Optionally, also check if the
588 * directory is readable. Will not print an error message if the directory does
589 * not exist. Will log errors if #GNUNET_SYSERR is returned (i.e., a file exists
590 * with the same name).
592 * @param fil filename to test
593 * @param is_readable GNUNET_YES to additionally check if @a fil is readable;
594 * #GNUNET_NO to disable this check
595 * @return #GNUNET_YES if yes, #GNUNET_NO if not; #GNUNET_SYSERR if it
596 * does not exist or stat'ed
599 GNUNET_DISK_directory_test (const char *fil, int is_readable)
601 struct stat filestat;
604 ret = STAT (fil, &filestat);
608 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "stat", fil);
609 return GNUNET_SYSERR;
611 if (!S_ISDIR (filestat.st_mode))
613 LOG (GNUNET_ERROR_TYPE_DEBUG,
614 "A file already exits with the same name %s\n", fil);
617 if (GNUNET_YES == is_readable)
618 ret = ACCESS (fil, R_OK | X_OK);
620 ret = ACCESS (fil, X_OK);
623 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "access", fil);
631 * Check that fil corresponds to a filename
632 * (of a file that exists and that is not a directory).
634 * @param fil filename to check
635 * @return #GNUNET_YES if yes, GNUNET_NO if not a file, #GNUNET_SYSERR if something
636 * else (will print an error message in that case, too).
639 GNUNET_DISK_file_test (const char *fil)
641 struct stat filestat;
645 rdir = GNUNET_STRINGS_filename_expand (fil);
647 return GNUNET_SYSERR;
649 ret = STAT (rdir, &filestat);
654 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "stat", rdir);
656 return GNUNET_SYSERR;
661 if (!S_ISREG (filestat.st_mode))
666 if (ACCESS (rdir, F_OK) < 0)
668 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "access", rdir);
670 return GNUNET_SYSERR;
678 * Implementation of "mkdir -p"
680 * @param dir the directory to create
681 * @return #GNUNET_OK on success, #GNUNET_SYSERR on failure
684 GNUNET_DISK_directory_create (const char *dir)
692 rdir = GNUNET_STRINGS_filename_expand (dir);
694 return GNUNET_SYSERR;
698 pos = 1; /* skip heading '/' */
700 /* Local or Network path? */
701 if (strncmp (rdir, "\\\\", 2) == 0)
706 if (rdir[pos] == '\\')
716 pos = 3; /* strlen("C:\\") */
719 /* Check which low level directories already exist */
721 rdir[len] = DIR_SEPARATOR;
724 if (DIR_SEPARATOR == rdir[pos2])
727 ret = GNUNET_DISK_directory_test (rdir, GNUNET_NO);
728 if (GNUNET_NO == ret)
731 return GNUNET_SYSERR;
733 rdir[pos2] = DIR_SEPARATOR;
734 if (GNUNET_YES == ret)
745 /* Start creating directories */
748 if ((rdir[pos] == DIR_SEPARATOR) || (pos == len))
751 ret = GNUNET_DISK_directory_test (rdir, GNUNET_NO);
752 if (GNUNET_NO == ret)
755 return GNUNET_SYSERR;
757 if (GNUNET_SYSERR == ret)
760 ret = mkdir (rdir, S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH); /* 755 */
762 wchar_t wrdir[MAX_PATH + 1];
763 if (ERROR_SUCCESS == plibc_conv_to_win_pathwconv(rdir, wrdir))
764 ret = !CreateDirectoryW (wrdir, NULL);
768 if ((ret != 0) && (errno != EEXIST))
770 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_ERROR, "mkdir", rdir);
772 return GNUNET_SYSERR;
775 rdir[pos] = DIR_SEPARATOR;
785 * Create the directory structure for storing
788 * @param filename name of a file in the directory
789 * @returns #GNUNET_OK on success,
790 * #GNUNET_SYSERR on failure,
791 * #GNUNET_NO if the directory
792 * exists but is not writeable for us
795 GNUNET_DISK_directory_create_for_file (const char *filename)
801 rdir = GNUNET_STRINGS_filename_expand (filename);
803 return GNUNET_SYSERR;
805 while ((len > 0) && (rdir[len] != DIR_SEPARATOR))
808 ret = GNUNET_DISK_directory_create (rdir);
809 if ((ret == GNUNET_OK) && (0 != ACCESS (rdir, W_OK)))
817 * Read the contents of a binary file into a buffer.
819 * @param h handle to an open file
820 * @param result the buffer to write the result to
821 * @param len the maximum number of bytes to read
822 * @return the number of bytes read on success, #GNUNET_SYSERR on failure
825 GNUNET_DISK_file_read (const struct GNUNET_DISK_FileHandle *h,
832 return GNUNET_SYSERR;
838 if (h->type != GNUNET_DISK_HANLDE_TYPE_PIPE)
840 if (!ReadFile (h->h, result, len, &bytes_read, NULL))
842 SetErrnoFromWinError (GetLastError ());
843 return GNUNET_SYSERR;
848 if (!ReadFile (h->h, result, len, &bytes_read, h->oOverlapRead))
850 if (GetLastError () != ERROR_IO_PENDING)
852 LOG (GNUNET_ERROR_TYPE_DEBUG, "Error reading from pipe: %u\n", GetLastError ());
853 SetErrnoFromWinError (GetLastError ());
854 return GNUNET_SYSERR;
856 LOG (GNUNET_ERROR_TYPE_DEBUG, "Will get overlapped result\n");
857 GetOverlappedResult (h->h, h->oOverlapRead, &bytes_read, TRUE);
859 LOG (GNUNET_ERROR_TYPE_DEBUG, "Read %u bytes from pipe\n", bytes_read);
863 return read (h->fd, result, len);
869 * Read the contents of a binary file into a buffer.
870 * Guarantees not to block (returns GNUNET_SYSERR and sets errno to EAGAIN
871 * when no data can be read).
873 * @param h handle to an open file
874 * @param result the buffer to write the result to
875 * @param len the maximum number of bytes to read
876 * @return the number of bytes read on success, #GNUNET_SYSERR on failure
879 GNUNET_DISK_file_read_non_blocking (const struct GNUNET_DISK_FileHandle *h,
886 return GNUNET_SYSERR;
892 if (h->type != GNUNET_DISK_HANLDE_TYPE_PIPE)
894 if (!ReadFile (h->h, result, len, &bytes_read, NULL))
896 SetErrnoFromWinError (GetLastError ());
897 return GNUNET_SYSERR;
902 if (!ReadFile (h->h, result, len, &bytes_read, h->oOverlapRead))
904 if (GetLastError () != ERROR_IO_PENDING)
906 LOG (GNUNET_ERROR_TYPE_DEBUG, "Error reading from pipe: %u\n", GetLastError ());
907 SetErrnoFromWinError (GetLastError ());
908 return GNUNET_SYSERR;
912 LOG (GNUNET_ERROR_TYPE_DEBUG,
913 "ReadFile() queued a read, cancelling\n");
916 return GNUNET_SYSERR;
919 LOG (GNUNET_ERROR_TYPE_DEBUG, "Read %u bytes\n", bytes_read);
926 /* set to non-blocking, read, then set back */
927 flags = fcntl (h->fd, F_GETFL);
928 if (0 == (flags & O_NONBLOCK))
929 (void) fcntl (h->fd, F_SETFL, flags | O_NONBLOCK);
930 ret = read (h->fd, result, len);
931 if (0 == (flags & O_NONBLOCK))
934 (void) fcntl (h->fd, F_SETFL, flags);
943 * Read the contents of a binary file into a buffer.
945 * @param fn file name
946 * @param result the buffer to write the result to
947 * @param len the maximum number of bytes to read
948 * @return number of bytes read, #GNUNET_SYSERR on failure
951 GNUNET_DISK_fn_read (const char *fn,
955 struct GNUNET_DISK_FileHandle *fh;
958 fh = GNUNET_DISK_file_open (fn, GNUNET_DISK_OPEN_READ, GNUNET_DISK_PERM_NONE);
960 return GNUNET_SYSERR;
961 ret = GNUNET_DISK_file_read (fh, result, len);
962 GNUNET_assert (GNUNET_OK == GNUNET_DISK_file_close (fh));
969 * Write a buffer to a file.
971 * @param h handle to open file
972 * @param buffer the data to write
973 * @param n number of bytes to write
974 * @return number of bytes written on success, #GNUNET_SYSERR on error
977 GNUNET_DISK_file_write (const struct GNUNET_DISK_FileHandle * h,
978 const void *buffer, size_t n)
983 return GNUNET_SYSERR;
989 if (h->type != GNUNET_DISK_HANLDE_TYPE_PIPE)
991 if (!WriteFile (h->h, buffer, n, &bytes_written, NULL))
993 SetErrnoFromWinError (GetLastError ());
994 return GNUNET_SYSERR;
999 LOG (GNUNET_ERROR_TYPE_DEBUG, "It is a pipe trying to write %u bytes\n", n);
1000 if (!WriteFile (h->h, buffer, n, &bytes_written, h->oOverlapWrite))
1002 if (GetLastError () != ERROR_IO_PENDING)
1004 SetErrnoFromWinError (GetLastError ());
1005 LOG (GNUNET_ERROR_TYPE_DEBUG, "Error writing to pipe: %u\n",
1007 return GNUNET_SYSERR;
1009 LOG (GNUNET_ERROR_TYPE_DEBUG, "Will get overlapped result\n");
1010 if (!GetOverlappedResult (h->h, h->oOverlapWrite, &bytes_written, TRUE))
1012 SetErrnoFromWinError (GetLastError ());
1013 LOG (GNUNET_ERROR_TYPE_DEBUG,
1014 "Error getting overlapped result while writing to pipe: %u\n",
1016 return GNUNET_SYSERR;
1022 if (!GetOverlappedResult (h->h, h->oOverlapWrite, &ovr, TRUE))
1024 LOG (GNUNET_ERROR_TYPE_DEBUG,
1025 "Error getting control overlapped result while writing to pipe: %u\n",
1030 LOG (GNUNET_ERROR_TYPE_DEBUG,
1031 "Wrote %u bytes (ovr says %u), picking the greatest\n",
1032 bytes_written, ovr);
1035 if (bytes_written == 0)
1039 LOG (GNUNET_ERROR_TYPE_DEBUG, "Wrote %u bytes, returning -1 with EAGAIN\n", bytes_written);
1041 return GNUNET_SYSERR;
1044 LOG (GNUNET_ERROR_TYPE_DEBUG, "Wrote %u bytes\n", bytes_written);
1046 return bytes_written;
1048 return write (h->fd, buffer, n);
1054 * Write a buffer to a file, blocking, if necessary.
1056 * @param h handle to open file
1057 * @param buffer the data to write
1058 * @param n number of bytes to write
1059 * @return number of bytes written on success, #GNUNET_SYSERR on error
1062 GNUNET_DISK_file_write_blocking (const struct GNUNET_DISK_FileHandle * h,
1069 return GNUNET_SYSERR;
1073 DWORD bytes_written;
1074 /* We do a non-overlapped write, which is as blocking as it gets */
1075 LOG (GNUNET_ERROR_TYPE_DEBUG, "Writing %u bytes\n", n);
1076 if (!WriteFile (h->h, buffer, n, &bytes_written, NULL))
1078 SetErrnoFromWinError (GetLastError ());
1079 LOG (GNUNET_ERROR_TYPE_DEBUG, "Error writing to pipe: %u\n",
1081 return GNUNET_SYSERR;
1083 if (bytes_written == 0 && n > 0)
1085 LOG (GNUNET_ERROR_TYPE_DEBUG, "Waiting for pipe to clean\n");
1086 WaitForSingleObject (h->h, INFINITE);
1087 if (!WriteFile (h->h, buffer, n, &bytes_written, NULL))
1089 SetErrnoFromWinError (GetLastError ());
1090 LOG (GNUNET_ERROR_TYPE_DEBUG, "Error writing to pipe: %u\n",
1092 return GNUNET_SYSERR;
1095 LOG (GNUNET_ERROR_TYPE_DEBUG,
1098 return bytes_written;
1103 /* set to blocking, write, then set back */
1104 flags = fcntl (h->fd, F_GETFL);
1105 if (0 != (flags & O_NONBLOCK))
1106 (void) fcntl (h->fd, F_SETFL, flags - O_NONBLOCK);
1107 ret = write (h->fd, buffer, n);
1108 if (0 == (flags & O_NONBLOCK))
1109 (void) fcntl (h->fd, F_SETFL, flags);
1116 * Write a buffer to a file. If the file is longer than the
1117 * number of bytes that will be written, it will be truncated.
1119 * @param fn file name
1120 * @param buffer the data to write
1121 * @param n number of bytes to write
1122 * @param mode file permissions
1123 * @return number of bytes written on success, #GNUNET_SYSERR on error
1126 GNUNET_DISK_fn_write (const char *fn, const void *buffer, size_t n,
1127 enum GNUNET_DISK_AccessPermissions mode)
1129 struct GNUNET_DISK_FileHandle *fh;
1132 fh = GNUNET_DISK_file_open (fn,
1133 GNUNET_DISK_OPEN_WRITE | GNUNET_DISK_OPEN_TRUNCATE
1134 | GNUNET_DISK_OPEN_CREATE, mode);
1136 return GNUNET_SYSERR;
1137 ret = GNUNET_DISK_file_write (fh, buffer, n);
1138 GNUNET_assert (GNUNET_OK == GNUNET_DISK_file_close (fh));
1144 * Scan a directory for files.
1146 * @param dir_name the name of the directory
1147 * @param callback the method to call for each file,
1148 * can be NULL, in that case, we only count
1149 * @param callback_cls closure for @a callback
1150 * @return the number of files found, #GNUNET_SYSERR on error or
1151 * ieration aborted by callback returning #GNUNET_SYSERR
1154 GNUNET_DISK_directory_scan (const char *dir_name,
1155 GNUNET_FileNameCallback callback,
1159 struct dirent *finfo;
1165 unsigned int name_len;
1166 unsigned int n_size;
1168 GNUNET_assert (dir_name != NULL);
1169 dname = GNUNET_STRINGS_filename_expand (dir_name);
1171 return GNUNET_SYSERR;
1172 while ((strlen (dname) > 0) && (dname[strlen (dname) - 1] == DIR_SEPARATOR))
1173 dname[strlen (dname) - 1] = '\0';
1174 if (0 != STAT (dname, &istat))
1176 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "stat", dname);
1177 GNUNET_free (dname);
1178 return GNUNET_SYSERR;
1180 if (!S_ISDIR (istat.st_mode))
1182 LOG (GNUNET_ERROR_TYPE_WARNING,
1183 _("Expected `%s' to be a directory!\n"),
1185 GNUNET_free (dname);
1186 return GNUNET_SYSERR;
1189 dinfo = OPENDIR (dname);
1190 if ((errno == EACCES) || (dinfo == NULL))
1192 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "opendir", dname);
1195 GNUNET_free (dname);
1196 return GNUNET_SYSERR;
1199 n_size = strlen (dname) + name_len + 2;
1200 name = GNUNET_malloc (n_size);
1201 while ((finfo = READDIR (dinfo)) != NULL)
1203 if ((0 == strcmp (finfo->d_name, ".")) ||
1204 (0 == strcmp (finfo->d_name, "..")))
1206 if (callback != NULL)
1208 if (name_len < strlen (finfo->d_name))
1211 name_len = strlen (finfo->d_name);
1212 n_size = strlen (dname) + name_len + 2;
1213 name = GNUNET_malloc (n_size);
1215 /* dname can end in "/" only if dname == "/";
1216 * if dname does not end in "/", we need to add
1217 * a "/" (otherwise, we must not!) */
1218 GNUNET_snprintf (name, n_size, "%s%s%s", dname,
1219 (strcmp (dname, DIR_SEPARATOR_STR) ==
1220 0) ? "" : DIR_SEPARATOR_STR, finfo->d_name);
1221 ret = callback (callback_cls, name);
1222 if (GNUNET_OK != ret)
1226 GNUNET_free (dname);
1227 if (GNUNET_NO == ret)
1229 return GNUNET_SYSERR;
1236 GNUNET_free (dname);
1242 * Opaque handle used for iterating over a directory.
1244 struct GNUNET_DISK_DirectoryIterator
1248 * Function to call on directory entries.
1250 GNUNET_DISK_DirectoryIteratorCallback callback;
1253 * Closure for callback.
1258 * Reference to directory.
1268 * Next filename to process.
1275 enum GNUNET_SCHEDULER_Priority priority;
1281 * Task used by the directory iterator.
1284 directory_iterator_task (void *cls,
1285 const struct GNUNET_SCHEDULER_TaskContext *tc)
1287 struct GNUNET_DISK_DirectoryIterator *iter = cls;
1290 name = iter->next_name;
1291 GNUNET_assert (name != NULL);
1292 iter->next_name = NULL;
1293 iter->callback (iter->callback_cls, iter, name, iter->dirname);
1299 * This function must be called during the DiskIteratorCallback
1300 * (exactly once) to schedule the task to process the next
1301 * filename in the directory (if there is one).
1303 * @param iter opaque handle for the iterator
1304 * @param can set to GNUNET_YES to terminate the iteration early
1305 * @return GNUNET_YES if iteration will continue,
1306 * GNUNET_NO if this was the last entry (and iteration is complete),
1307 * GNUNET_SYSERR if abort was YES
1310 GNUNET_DISK_directory_iterator_next (struct GNUNET_DISK_DirectoryIterator *iter,
1313 struct dirent *finfo;
1315 GNUNET_assert (iter->next_name == NULL);
1316 if (can == GNUNET_YES)
1318 CLOSEDIR (iter->directory);
1319 GNUNET_free (iter->dirname);
1321 return GNUNET_SYSERR;
1323 while (NULL != (finfo = READDIR (iter->directory)))
1325 if ((0 == strcmp (finfo->d_name, ".")) ||
1326 (0 == strcmp (finfo->d_name, "..")))
1328 GNUNET_asprintf (&iter->next_name, "%s%s%s", iter->dirname,
1329 DIR_SEPARATOR_STR, finfo->d_name);
1334 GNUNET_DISK_directory_iterator_next (iter, GNUNET_YES);
1337 GNUNET_SCHEDULER_add_with_priority (iter->priority, &directory_iterator_task,
1344 * Scan a directory for files using the scheduler to run a task for
1345 * each entry. The name of the directory must be expanded first (!).
1346 * If a scheduler does not need to be used, GNUNET_DISK_directory_scan
1347 * may provide a simpler API.
1349 * @param prio priority to use
1350 * @param dir_name the name of the directory
1351 * @param callback the method to call for each file
1352 * @param callback_cls closure for callback
1353 * @return GNUNET_YES if directory is not empty and 'callback'
1354 * will be called later, GNUNET_NO otherwise, GNUNET_SYSERR on error.
1357 GNUNET_DISK_directory_iterator_start (enum GNUNET_SCHEDULER_Priority prio,
1358 const char *dir_name,
1359 GNUNET_DISK_DirectoryIteratorCallback
1360 callback, void *callback_cls)
1362 struct GNUNET_DISK_DirectoryIterator *di;
1364 di = GNUNET_new (struct GNUNET_DISK_DirectoryIterator);
1365 di->callback = callback;
1366 di->callback_cls = callback_cls;
1367 di->directory = OPENDIR (dir_name);
1368 if (di->directory == NULL)
1371 callback (callback_cls, NULL, NULL, NULL);
1372 return GNUNET_SYSERR;
1374 di->dirname = GNUNET_strdup (dir_name);
1375 di->priority = prio;
1376 return GNUNET_DISK_directory_iterator_next (di, GNUNET_NO);
1381 * Function that removes the given directory by calling
1382 * "GNUNET_DISK_directory_remove".
1384 * @param unused not used
1385 * @param fn directory to remove
1389 remove_helper (void *unused, const char *fn)
1391 (void) GNUNET_DISK_directory_remove (fn);
1397 * Remove all files in a directory (rm -rf). Call with
1401 * @param filename the file to remove
1402 * @return GNUNET_OK on success, GNUNET_SYSERR on error
1405 GNUNET_DISK_directory_remove (const char *filename)
1409 if (0 != LSTAT (filename, &istat))
1410 return GNUNET_NO; /* file may not exist... */
1411 (void) CHMOD (filename, S_IWUSR | S_IRUSR | S_IXUSR);
1412 if (UNLINK (filename) == 0)
1414 if ((errno != EISDIR) &&
1415 /* EISDIR is not sufficient in all cases, e.g.
1416 * sticky /tmp directory may result in EPERM on BSD.
1417 * So we also explicitly check "isDirectory" */
1418 (GNUNET_YES != GNUNET_DISK_directory_test (filename, GNUNET_YES)))
1420 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "rmdir", filename);
1421 return GNUNET_SYSERR;
1423 if (GNUNET_SYSERR ==
1424 GNUNET_DISK_directory_scan (filename, &remove_helper, NULL))
1425 return GNUNET_SYSERR;
1426 if (0 != RMDIR (filename))
1428 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "rmdir", filename);
1429 return GNUNET_SYSERR;
1438 * @param src file to copy
1439 * @param dst destination file name
1440 * @return #GNUNET_OK on success, #GNUNET_SYSERR on error
1443 GNUNET_DISK_file_copy (const char *src,
1450 struct GNUNET_DISK_FileHandle *in;
1451 struct GNUNET_DISK_FileHandle *out;
1453 if (GNUNET_OK != GNUNET_DISK_file_size (src, &size, GNUNET_YES, GNUNET_YES))
1454 return GNUNET_SYSERR;
1456 in = GNUNET_DISK_file_open (src, GNUNET_DISK_OPEN_READ,
1457 GNUNET_DISK_PERM_NONE);
1459 return GNUNET_SYSERR;
1461 GNUNET_DISK_file_open (dst,
1462 GNUNET_DISK_OPEN_WRITE | GNUNET_DISK_OPEN_CREATE |
1463 GNUNET_DISK_OPEN_FAILIFEXISTS,
1464 GNUNET_DISK_PERM_USER_READ |
1465 GNUNET_DISK_PERM_USER_WRITE |
1466 GNUNET_DISK_PERM_GROUP_READ |
1467 GNUNET_DISK_PERM_GROUP_WRITE);
1470 GNUNET_DISK_file_close (in);
1471 return GNUNET_SYSERR;
1473 buf = GNUNET_malloc (COPY_BLK_SIZE);
1476 len = COPY_BLK_SIZE;
1477 if (len > size - pos)
1479 if (len != GNUNET_DISK_file_read (in, buf, len))
1481 if (len != GNUNET_DISK_file_write (out, buf, len))
1486 GNUNET_DISK_file_close (in);
1487 GNUNET_DISK_file_close (out);
1491 GNUNET_DISK_file_close (in);
1492 GNUNET_DISK_file_close (out);
1493 return GNUNET_SYSERR;
1498 * @brief Removes special characters as ':' from a filename.
1499 * @param fn the filename to canonicalize
1502 GNUNET_DISK_filename_canonicalize (char *fn)
1512 if (c == '/' || c == '\\' || c == ':' || c == '*' || c == '?' || c == '"' ||
1513 c == '<' || c == '>' || c == '|')
1525 * @brief Change owner of a file
1527 * @param filename name of file to change the owner of
1528 * @param user name of the new owner
1529 * @return GNUNET_OK on success, GNUNET_SYSERR on failure
1532 GNUNET_DISK_file_change_owner (const char *filename, const char *user)
1537 pws = getpwnam (user);
1540 LOG (GNUNET_ERROR_TYPE_ERROR,
1541 _("Cannot obtain information about user `%s': %s\n"), user,
1543 return GNUNET_SYSERR;
1545 if (0 != chown (filename, pws->pw_uid, pws->pw_gid))
1546 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "chown", filename);
1553 * Lock a part of a file
1554 * @param fh file handle
1555 * @param lock_start absolute position from where to lock
1556 * @param lock_end absolute position until where to lock
1557 * @param excl GNUNET_YES for an exclusive lock
1558 * @return GNUNET_OK on success, GNUNET_SYSERR on error
1561 GNUNET_DISK_file_lock (struct GNUNET_DISK_FileHandle *fh, off_t lock_start,
1562 off_t lock_end, int excl)
1567 return GNUNET_SYSERR;
1573 memset (&fl, 0, sizeof (struct flock));
1574 fl.l_type = excl ? F_WRLCK : F_RDLCK;
1575 fl.l_whence = SEEK_SET;
1576 fl.l_start = lock_start;
1577 fl.l_len = lock_end;
1579 return fcntl (fh->fd, F_SETLK, &fl) != 0 ? GNUNET_SYSERR : GNUNET_OK;
1582 off_t diff = lock_end - lock_start;
1583 DWORD diff_low, diff_high;
1584 diff_low = (DWORD) (diff & 0xFFFFFFFF);
1585 diff_high = (DWORD) ((diff >> (sizeof (DWORD) * 8)) & 0xFFFFFFFF);
1587 memset (&o, 0, sizeof (OVERLAPPED));
1588 o.Offset = (DWORD) (lock_start & 0xFFFFFFFF);;
1589 o.OffsetHigh = (DWORD) (((lock_start & ~0xFFFFFFFF) >> (sizeof (DWORD) * 8)) & 0xFFFFFFFF);
1592 (fh->h, (excl ? LOCKFILE_EXCLUSIVE_LOCK : 0) | LOCKFILE_FAIL_IMMEDIATELY,
1593 0, diff_low, diff_high, &o))
1595 SetErrnoFromWinError (GetLastError ());
1596 return GNUNET_SYSERR;
1605 * Unlock a part of a file
1606 * @param fh file handle
1607 * @param unlock_start absolute position from where to unlock
1608 * @param unlock_end absolute position until where to unlock
1609 * @return GNUNET_OK on success, GNUNET_SYSERR on error
1612 GNUNET_DISK_file_unlock (struct GNUNET_DISK_FileHandle *fh, off_t unlock_start,
1618 return GNUNET_SYSERR;
1624 memset (&fl, 0, sizeof (struct flock));
1625 fl.l_type = F_UNLCK;
1626 fl.l_whence = SEEK_SET;
1627 fl.l_start = unlock_start;
1628 fl.l_len = unlock_end;
1630 return fcntl (fh->fd, F_SETLK, &fl) != 0 ? GNUNET_SYSERR : GNUNET_OK;
1633 off_t diff = unlock_end - unlock_start;
1634 DWORD diff_low, diff_high;
1635 diff_low = (DWORD) (diff & 0xFFFFFFFF);
1636 diff_high = (DWORD) ((diff >> (sizeof (DWORD) * 8)) & 0xFFFFFFFF);
1638 memset (&o, 0, sizeof (OVERLAPPED));
1639 o.Offset = (DWORD) (unlock_start & 0xFFFFFFFF);;
1640 o.OffsetHigh = (DWORD) (((unlock_start & ~0xFFFFFFFF) >> (sizeof (DWORD) * 8)) & 0xFFFFFFFF);
1642 if (!UnlockFileEx (fh->h, 0, diff_low, diff_high, &o))
1644 SetErrnoFromWinError (GetLastError ());
1645 return GNUNET_SYSERR;
1654 * Open a file. Note that the access permissions will only be
1655 * used if a new file is created and if the underlying operating
1656 * system supports the given permissions.
1658 * @param fn file name to be opened
1659 * @param flags opening flags, a combination of GNUNET_DISK_OPEN_xxx bit flags
1660 * @param perm permissions for the newly created file, use
1661 * #GNUNET_DISK_PERM_NONE if a file could not be created by this
1662 * call (because of flags)
1663 * @return IO handle on success, NULL on error
1665 struct GNUNET_DISK_FileHandle *
1666 GNUNET_DISK_file_open (const char *fn,
1667 enum GNUNET_DISK_OpenFlags flags,
1668 enum GNUNET_DISK_AccessPermissions perm)
1671 struct GNUNET_DISK_FileHandle *ret;
1677 wchar_t wexpfn[MAX_PATH + 1];
1684 expfn = GNUNET_STRINGS_filename_expand (fn);
1689 if (GNUNET_DISK_OPEN_READWRITE == (flags & GNUNET_DISK_OPEN_READWRITE))
1690 oflags = O_RDWR; /* note: O_RDWR is NOT always O_RDONLY | O_WRONLY */
1691 else if (flags & GNUNET_DISK_OPEN_READ)
1693 else if (flags & GNUNET_DISK_OPEN_WRITE)
1698 GNUNET_free (expfn);
1701 if (flags & GNUNET_DISK_OPEN_FAILIFEXISTS)
1702 oflags |= (O_CREAT | O_EXCL);
1703 if (flags & GNUNET_DISK_OPEN_TRUNCATE)
1705 if (flags & GNUNET_DISK_OPEN_APPEND)
1707 if (flags & GNUNET_DISK_OPEN_CREATE)
1709 (void) GNUNET_DISK_directory_create_for_file (expfn);
1711 mode = translate_unix_perms (perm);
1714 fd = open (expfn, oflags
1718 | O_LARGEFILE, mode);
1721 if (0 == (flags & GNUNET_DISK_OPEN_FAILIFEXISTS))
1722 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "open", expfn);
1724 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_DEBUG, "open", expfn);
1725 GNUNET_free (expfn);
1732 if (GNUNET_DISK_OPEN_READWRITE == (flags & GNUNET_DISK_OPEN_READWRITE))
1733 access = FILE_READ_DATA | FILE_WRITE_DATA;
1734 else if (flags & GNUNET_DISK_OPEN_READ)
1735 access = FILE_READ_DATA;
1736 else if (flags & GNUNET_DISK_OPEN_WRITE)
1737 access = FILE_WRITE_DATA;
1739 if (flags & GNUNET_DISK_OPEN_FAILIFEXISTS)
1743 else if (flags & GNUNET_DISK_OPEN_CREATE)
1745 (void) GNUNET_DISK_directory_create_for_file (expfn);
1746 if (flags & GNUNET_DISK_OPEN_TRUNCATE)
1747 disp = CREATE_ALWAYS;
1751 else if (flags & GNUNET_DISK_OPEN_TRUNCATE)
1753 disp = TRUNCATE_EXISTING;
1757 disp = OPEN_EXISTING;
1760 if (ERROR_SUCCESS == plibc_conv_to_win_pathwconv(expfn, wexpfn))
1761 h = CreateFileW (wexpfn, access,
1762 FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE, NULL,
1763 disp, FILE_ATTRIBUTE_NORMAL, NULL);
1765 h = INVALID_HANDLE_VALUE;
1766 if (h == INVALID_HANDLE_VALUE)
1769 SetErrnoFromWinError (GetLastError ());
1771 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_INFO, "open", expfn);
1772 GNUNET_free (expfn);
1777 if (flags & GNUNET_DISK_OPEN_APPEND)
1778 if (SetFilePointer (h, 0, 0, FILE_END) == INVALID_SET_FILE_POINTER)
1780 SetErrnoFromWinError (GetLastError ());
1781 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "SetFilePointer", expfn);
1783 GNUNET_free (expfn);
1788 ret = GNUNET_new (struct GNUNET_DISK_FileHandle);
1791 ret->type = GNUNET_DISK_HANLDE_TYPE_FILE;
1795 GNUNET_free (expfn);
1801 * Close an open file
1802 * @param h file handle
1803 * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
1806 GNUNET_DISK_file_close (struct GNUNET_DISK_FileHandle *h)
1812 return GNUNET_SYSERR;
1818 if (!CloseHandle (h->h))
1820 SetErrnoFromWinError (GetLastError ());
1821 LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING, "close");
1822 ret = GNUNET_SYSERR;
1824 if (h->oOverlapRead)
1826 if (!CloseHandle (h->oOverlapRead->hEvent))
1828 SetErrnoFromWinError (GetLastError ());
1829 LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING, "close");
1830 ret = GNUNET_SYSERR;
1832 GNUNET_free (h->oOverlapRead);
1834 if (h->oOverlapWrite)
1836 if (!CloseHandle (h->oOverlapWrite->hEvent))
1838 SetErrnoFromWinError (GetLastError ());
1839 LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING, "close");
1840 ret = GNUNET_SYSERR;
1842 GNUNET_free (h->oOverlapWrite);
1845 if (close (h->fd) != 0)
1847 LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING, "close");
1848 ret = GNUNET_SYSERR;
1857 * Get a GNUnet file handle from a W32 handle.
1859 * @param handle native handle
1860 * @return GNUnet file handle corresponding to the W32 handle
1862 struct GNUNET_DISK_FileHandle *
1863 GNUNET_DISK_get_handle_from_w32_handle (HANDLE osfh)
1865 struct GNUNET_DISK_FileHandle *fh;
1868 enum GNUNET_FILE_Type ftype;
1870 dwret = GetFileType (osfh);
1873 case FILE_TYPE_DISK:
1874 ftype = GNUNET_DISK_HANLDE_TYPE_FILE;
1876 case FILE_TYPE_PIPE:
1877 ftype = GNUNET_DISK_HANLDE_TYPE_PIPE;
1883 fh = GNUNET_new (struct GNUNET_DISK_FileHandle);
1887 if (ftype == GNUNET_DISK_HANLDE_TYPE_PIPE)
1890 * Note that we can't make it overlapped if it isn't already.
1891 * (ReOpenFile() is only available in 2003/Vista).
1892 * The process that opened this file in the first place (usually a parent
1893 * process, if this is stdin/stdout/stderr) must make it overlapped,
1894 * otherwise we're screwed, as selecting on non-overlapped handle
1897 fh->oOverlapRead = GNUNET_malloc (sizeof (OVERLAPPED));
1898 fh->oOverlapWrite = GNUNET_malloc (sizeof (OVERLAPPED));
1899 fh->oOverlapRead->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
1900 fh->oOverlapWrite->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
1908 * Get a handle from a native integer FD.
1910 * @param fno native integer file descriptor
1911 * @return file handle corresponding to the descriptor, NULL on error
1913 struct GNUNET_DISK_FileHandle *
1914 GNUNET_DISK_get_handle_from_int_fd (int fno)
1916 struct GNUNET_DISK_FileHandle *fh;
1918 if ( (((off_t) -1) == lseek (fno, 0, SEEK_CUR)) &&
1920 return NULL; /* invalid FD */
1923 fh = GNUNET_new (struct GNUNET_DISK_FileHandle);
1929 osfh = _get_osfhandle (fno);
1930 if (INVALID_HANDLE_VALUE == (HANDLE) osfh)
1933 fh = GNUNET_DISK_get_handle_from_w32_handle ((HANDLE) osfh);
1941 * Get a handle from a native streaming FD.
1943 * @param fd native streaming file descriptor
1944 * @return file handle corresponding to the descriptor
1946 struct GNUNET_DISK_FileHandle *
1947 GNUNET_DISK_get_handle_from_native (FILE *fd)
1955 return GNUNET_DISK_get_handle_from_int_fd (fno);
1960 * Handle for a memory-mapping operation.
1962 struct GNUNET_DISK_MapHandle
1965 * Address where the map is in memory.
1971 * Underlying OS handle.
1976 * Number of bytes mapped.
1984 #define MAP_FAILED ((void *) -1)
1988 * Map a file into memory
1990 * @param h open file handle
1991 * @param m handle to the new mapping
1992 * @param access access specification, GNUNET_DISK_MAP_TYPE_xxx
1993 * @param len size of the mapping
1994 * @return pointer to the mapped memory region, NULL on failure
1997 GNUNET_DISK_file_map (const struct GNUNET_DISK_FileHandle *h,
1998 struct GNUNET_DISK_MapHandle **m,
1999 enum GNUNET_DISK_MapType access, size_t len)
2008 DWORD mapAccess, protect;
2010 if ((access & GNUNET_DISK_MAP_TYPE_READ) &&
2011 (access & GNUNET_DISK_MAP_TYPE_WRITE))
2013 protect = PAGE_READWRITE;
2014 mapAccess = FILE_MAP_ALL_ACCESS;
2016 else if (access & GNUNET_DISK_MAP_TYPE_READ)
2018 protect = PAGE_READONLY;
2019 mapAccess = FILE_MAP_READ;
2021 else if (access & GNUNET_DISK_MAP_TYPE_WRITE)
2023 protect = PAGE_READWRITE;
2024 mapAccess = FILE_MAP_WRITE;
2032 *m = GNUNET_new (struct GNUNET_DISK_MapHandle);
2033 (*m)->h = CreateFileMapping (h->h, NULL, protect, 0, 0, NULL);
2034 if ((*m)->h == INVALID_HANDLE_VALUE)
2036 SetErrnoFromWinError (GetLastError ());
2041 (*m)->addr = MapViewOfFile ((*m)->h, mapAccess, 0, 0, len);
2044 SetErrnoFromWinError (GetLastError ());
2045 CloseHandle ((*m)->h);
2054 if (access & GNUNET_DISK_MAP_TYPE_READ)
2056 if (access & GNUNET_DISK_MAP_TYPE_WRITE)
2058 *m = GNUNET_new (struct GNUNET_DISK_MapHandle);
2059 (*m)->addr = mmap (NULL, len, prot, MAP_SHARED, h->fd, 0);
2060 GNUNET_assert (NULL != (*m)->addr);
2061 if (MAP_FAILED == (*m)->addr)
2073 * @param h mapping handle
2074 * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
2077 GNUNET_DISK_file_unmap (struct GNUNET_DISK_MapHandle *h)
2084 return GNUNET_SYSERR;
2088 ret = UnmapViewOfFile (h->addr) ? GNUNET_OK : GNUNET_SYSERR;
2089 if (ret != GNUNET_OK)
2090 SetErrnoFromWinError (GetLastError ());
2091 if (!CloseHandle (h->h) && (ret == GNUNET_OK))
2093 ret = GNUNET_SYSERR;
2094 SetErrnoFromWinError (GetLastError ());
2097 ret = munmap (h->addr, h->len) != -1 ? GNUNET_OK : GNUNET_SYSERR;
2105 * Write file changes to disk
2106 * @param h handle to an open file
2107 * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
2110 GNUNET_DISK_file_sync (const struct GNUNET_DISK_FileHandle *h)
2115 return GNUNET_SYSERR;
2121 ret = FlushFileBuffers (h->h) ? GNUNET_OK : GNUNET_SYSERR;
2122 if (ret != GNUNET_OK)
2123 SetErrnoFromWinError (GetLastError ());
2125 #elif defined(FREEBSD) || defined(OPENBSD) || defined(DARWIN)
2126 return fsync (h->fd) == -1 ? GNUNET_SYSERR : GNUNET_OK;
2128 return fdatasync (h->fd) == -1 ? GNUNET_SYSERR : GNUNET_OK;
2135 #define PIPE_BUF 512
2137 /* Copyright Bob Byrnes <byrnes <at> curl.com>
2138 http://permalink.gmane.org/gmane.os.cygwin.patches/2121
2140 /* Create a pipe, and return handles to the read and write ends,
2141 just like CreatePipe, but ensure that the write end permits
2142 FILE_READ_ATTRIBUTES access, on later versions of win32 where
2143 this is supported. This access is needed by NtQueryInformationFile,
2144 which is used to implement select and nonblocking writes.
2145 Note that the return value is either NO_ERROR or GetLastError,
2146 unlike CreatePipe, which returns a bool for success or failure. */
2148 create_selectable_pipe (PHANDLE read_pipe_ptr, PHANDLE write_pipe_ptr,
2149 LPSECURITY_ATTRIBUTES sa_ptr, DWORD psize,
2150 DWORD dwReadMode, DWORD dwWriteMode)
2152 /* Default to error. */
2153 *read_pipe_ptr = *write_pipe_ptr = INVALID_HANDLE_VALUE;
2158 /* Ensure that there is enough pipe buffer space for atomic writes. */
2159 if (psize < PIPE_BUF)
2162 char pipename[MAX_PATH];
2164 /* Retry CreateNamedPipe as long as the pipe name is in use.
2165 * Retrying will probably never be necessary, but we want
2166 * to be as robust as possible. */
2169 static volatile LONG pipe_unique_id;
2171 snprintf (pipename, sizeof pipename, "\\\\.\\pipe\\gnunet-%d-%ld",
2172 getpid (), InterlockedIncrement ((LONG *) & pipe_unique_id));
2173 LOG (GNUNET_ERROR_TYPE_DEBUG, "CreateNamedPipe: name = %s, size = %lu\n",
2175 /* Use CreateNamedPipe instead of CreatePipe, because the latter
2176 * returns a write handle that does not permit FILE_READ_ATTRIBUTES
2177 * access, on versions of win32 earlier than WinXP SP2.
2178 * CreatePipe also stupidly creates a full duplex pipe, which is
2179 * a waste, since only a single direction is actually used.
2180 * It's important to only allow a single instance, to ensure that
2181 * the pipe was not created earlier by some other process, even if
2182 * the pid has been reused. */
2183 read_pipe = CreateNamedPipeA (pipename, PIPE_ACCESS_INBOUND | FILE_FLAG_FIRST_PIPE_INSTANCE | dwReadMode, PIPE_TYPE_BYTE | PIPE_READMODE_BYTE, 1, /* max instances */
2184 psize, /* output buffer size */
2185 psize, /* input buffer size */
2186 NMPWAIT_USE_DEFAULT_WAIT, sa_ptr);
2188 if (read_pipe != INVALID_HANDLE_VALUE)
2190 LOG (GNUNET_ERROR_TYPE_DEBUG, "pipe read handle = %p\n", read_pipe);
2194 DWORD err = GetLastError ();
2198 case ERROR_PIPE_BUSY:
2199 /* The pipe is already open with compatible parameters.
2200 * Pick a new name and retry. */
2201 LOG (GNUNET_ERROR_TYPE_DEBUG, "pipe busy, retrying\n");
2203 case ERROR_ACCESS_DENIED:
2204 /* The pipe is already open with incompatible parameters.
2205 * Pick a new name and retry. */
2206 LOG (GNUNET_ERROR_TYPE_DEBUG, "pipe access denied, retrying\n");
2208 case ERROR_CALL_NOT_IMPLEMENTED:
2209 /* We are on an older Win9x platform without named pipes.
2210 * Return an anonymous pipe as the best approximation. */
2211 LOG (GNUNET_ERROR_TYPE_DEBUG,
2212 "CreateNamedPipe not implemented, resorting to "
2213 "CreatePipe: size = %lu\n", psize);
2214 if (CreatePipe (read_pipe_ptr, write_pipe_ptr, sa_ptr, psize))
2216 LOG (GNUNET_ERROR_TYPE_DEBUG, "pipe read handle = %p, write handle = %p\n",
2221 err = GetLastError ();
2222 LOG (GNUNET_ERROR_TYPE_ERROR, "CreatePipe failed: %d\n", err);
2225 LOG (GNUNET_ERROR_TYPE_ERROR, "CreateNamedPipe failed: %d\n", err);
2230 LOG (GNUNET_ERROR_TYPE_DEBUG, "CreateFile: name = %s\n", pipename);
2232 /* Open the named pipe for writing.
2233 * Be sure to permit FILE_READ_ATTRIBUTES access. */
2234 write_pipe = CreateFileA (pipename, GENERIC_WRITE | FILE_READ_ATTRIBUTES, 0, /* share mode */
2235 sa_ptr, OPEN_EXISTING, dwWriteMode, /* flags and attributes */
2236 0); /* handle to template file */
2238 if (write_pipe == INVALID_HANDLE_VALUE)
2241 DWORD err = GetLastError ();
2243 LOG (GNUNET_ERROR_TYPE_DEBUG, "CreateFile failed: %d\n", err);
2244 CloseHandle (read_pipe);
2247 LOG (GNUNET_ERROR_TYPE_DEBUG, "pipe write handle = %p\n", write_pipe);
2249 *read_pipe_ptr = read_pipe;
2250 *write_pipe_ptr = write_pipe;
2257 * Creates an interprocess channel
2259 * @param blocking_read creates an asynchronous pipe for reading if set to GNUNET_NO
2260 * @param blocking_write creates an asynchronous pipe for writing if set to GNUNET_NO
2261 * @param inherit_read inherit the parent processes stdin (only for windows)
2262 * @param inherit_write inherit the parent processes stdout (only for windows)
2263 * @return handle to the new pipe, NULL on error
2265 struct GNUNET_DISK_PipeHandle *
2266 GNUNET_DISK_pipe (int blocking_read, int blocking_write, int inherit_read, int inherit_write)
2277 LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR, "pipe");
2281 return GNUNET_DISK_pipe_from_fd (blocking_read,
2285 struct GNUNET_DISK_PipeHandle *p;
2290 p = GNUNET_new (struct GNUNET_DISK_PipeHandle);
2291 p->fd[0] = GNUNET_new (struct GNUNET_DISK_FileHandle);
2292 p->fd[1] = GNUNET_new (struct GNUNET_DISK_FileHandle);
2294 /* All pipes are overlapped. If you want them to block - just
2295 * call WriteFile() and ReadFile() with NULL overlapped pointer.
2296 * NOTE: calling with NULL overlapped pointer works only
2297 * for pipes, and doesn't seem to be a documented feature.
2298 * It will NOT work for files, because overlapped files need
2299 * to read offsets from the overlapped structure, regardless.
2300 * Pipes are not seekable, and need no offsets, which is
2301 * probably why it works for them.
2304 create_selectable_pipe (&p->fd[0]->h, &p->fd[1]->h, NULL, 0,
2305 FILE_FLAG_OVERLAPPED,
2306 FILE_FLAG_OVERLAPPED);
2309 SetErrnoFromWinError (GetLastError ());
2311 GNUNET_free (p->fd[0]);
2312 GNUNET_free (p->fd[1]);
2317 if (!DuplicateHandle
2318 (GetCurrentProcess (), p->fd[0]->h, GetCurrentProcess (), &tmp_handle, 0,
2319 inherit_read == GNUNET_YES ? TRUE : FALSE, DUPLICATE_SAME_ACCESS))
2321 SetErrnoFromWinError (GetLastError ());
2323 CloseHandle (p->fd[0]->h);
2324 CloseHandle (p->fd[1]->h);
2325 GNUNET_free (p->fd[0]);
2326 GNUNET_free (p->fd[1]);
2331 CloseHandle (p->fd[0]->h);
2332 p->fd[0]->h = tmp_handle;
2334 if (!DuplicateHandle
2335 (GetCurrentProcess (), p->fd[1]->h, GetCurrentProcess (), &tmp_handle, 0,
2336 inherit_write == GNUNET_YES ? TRUE : FALSE, DUPLICATE_SAME_ACCESS))
2338 SetErrnoFromWinError (GetLastError ());
2340 CloseHandle (p->fd[0]->h);
2341 CloseHandle (p->fd[1]->h);
2342 GNUNET_free (p->fd[0]);
2343 GNUNET_free (p->fd[1]);
2348 CloseHandle (p->fd[1]->h);
2349 p->fd[1]->h = tmp_handle;
2351 p->fd[0]->type = GNUNET_DISK_HANLDE_TYPE_PIPE;
2352 p->fd[1]->type = GNUNET_DISK_HANLDE_TYPE_PIPE;
2354 p->fd[0]->oOverlapRead = GNUNET_malloc (sizeof (OVERLAPPED));
2355 p->fd[0]->oOverlapWrite = GNUNET_malloc (sizeof (OVERLAPPED));
2356 p->fd[1]->oOverlapRead = GNUNET_malloc (sizeof (OVERLAPPED));
2357 p->fd[1]->oOverlapWrite = GNUNET_malloc (sizeof (OVERLAPPED));
2359 p->fd[0]->oOverlapRead->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2360 p->fd[0]->oOverlapWrite->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2362 p->fd[1]->oOverlapRead->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2363 p->fd[1]->oOverlapWrite->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2371 * Creates a pipe object from a couple of file descriptors.
2372 * Useful for wrapping existing pipe FDs.
2374 * @param blocking_read creates an asynchronous pipe for reading if set to GNUNET_NO
2375 * @param blocking_write creates an asynchronous pipe for writing if set to GNUNET_NO
2376 * @param fd an array of two fd values. One of them may be -1 for read-only or write-only pipes
2378 * @return handle to the new pipe, NULL on error
2380 struct GNUNET_DISK_PipeHandle *
2381 GNUNET_DISK_pipe_from_fd (int blocking_read, int blocking_write, int fd[2])
2383 struct GNUNET_DISK_PipeHandle *p;
2385 p = GNUNET_new (struct GNUNET_DISK_PipeHandle);
2390 int eno = 0; /* make gcc happy */
2395 p->fd[0] = GNUNET_new (struct GNUNET_DISK_FileHandle);
2396 p->fd[0]->fd = fd[0];
2399 flags = fcntl (fd[0], F_GETFL);
2400 flags |= O_NONBLOCK;
2401 if (0 > fcntl (fd[0], F_SETFL, flags))
2407 flags = fcntl (fd[0], F_GETFD);
2408 flags |= FD_CLOEXEC;
2409 if (0 > fcntl (fd[0], F_SETFD, flags))
2418 p->fd[1] = GNUNET_new (struct GNUNET_DISK_FileHandle);
2419 p->fd[1]->fd = fd[1];
2420 if (!blocking_write)
2422 flags = fcntl (fd[1], F_GETFL);
2423 flags |= O_NONBLOCK;
2424 if (0 > fcntl (fd[1], F_SETFL, flags))
2430 flags = fcntl (fd[1], F_GETFD);
2431 flags |= FD_CLOEXEC;
2432 if (0 > fcntl (fd[1], F_SETFD, flags))
2441 LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR, "fcntl");
2442 if (p->fd[0]->fd >= 0)
2443 GNUNET_break (0 == close (p->fd[0]->fd));
2444 if (p->fd[1]->fd >= 0)
2445 GNUNET_break (0 == close (p->fd[1]->fd));
2446 GNUNET_free_non_null (p->fd[0]);
2447 GNUNET_free_non_null (p->fd[1]);
2455 p->fd[0] = GNUNET_new (struct GNUNET_DISK_FileHandle);
2456 p->fd[0]->h = (HANDLE) _get_osfhandle (fd[0]);
2457 if (p->fd[0]->h != INVALID_HANDLE_VALUE)
2459 p->fd[0]->type = GNUNET_DISK_HANLDE_TYPE_PIPE;
2460 p->fd[0]->oOverlapRead = GNUNET_malloc (sizeof (OVERLAPPED));
2461 p->fd[0]->oOverlapWrite = GNUNET_malloc (sizeof (OVERLAPPED));
2462 p->fd[0]->oOverlapRead->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2463 p->fd[0]->oOverlapWrite->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2467 GNUNET_free (p->fd[0]);
2473 p->fd[1] = GNUNET_new (struct GNUNET_DISK_FileHandle);
2474 p->fd[1]->h = (HANDLE) _get_osfhandle (fd[1]);
2475 if (p->fd[1]->h != INVALID_HANDLE_VALUE)
2477 p->fd[1]->type = GNUNET_DISK_HANLDE_TYPE_PIPE;
2478 p->fd[1]->oOverlapRead = GNUNET_malloc (sizeof (OVERLAPPED));
2479 p->fd[1]->oOverlapWrite = GNUNET_malloc (sizeof (OVERLAPPED));
2480 p->fd[1]->oOverlapRead->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2481 p->fd[1]->oOverlapWrite->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2485 GNUNET_free (p->fd[1]);
2496 * Closes an interprocess channel
2498 * @param p pipe to close
2499 * @param end which end of the pipe to close
2500 * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
2503 GNUNET_DISK_pipe_close_end (struct GNUNET_DISK_PipeHandle *p,
2504 enum GNUNET_DISK_PipeEnd end)
2506 int ret = GNUNET_OK;
2508 if (end == GNUNET_DISK_PIPE_END_READ)
2512 ret = GNUNET_DISK_file_close (p->fd[0]);
2516 else if (end == GNUNET_DISK_PIPE_END_WRITE)
2520 ret = GNUNET_DISK_file_close (p->fd[1]);
2529 * Detaches one of the ends from the pipe.
2530 * Detached end is a fully-functional FileHandle, it will
2531 * not be affected by anything you do with the pipe afterwards.
2532 * Each end of a pipe can only be detched from it once (i.e.
2533 * it is not duplicated).
2535 * @param p pipe to detach an end from
2536 * @param end which end of the pipe to detach
2537 * @return Detached end on success, NULL on failure
2538 * (or if that end is not present or is closed).
2540 struct GNUNET_DISK_FileHandle *
2541 GNUNET_DISK_pipe_detach_end (struct GNUNET_DISK_PipeHandle *p,
2542 enum GNUNET_DISK_PipeEnd end)
2544 struct GNUNET_DISK_FileHandle *ret = NULL;
2546 if (end == GNUNET_DISK_PIPE_END_READ)
2554 else if (end == GNUNET_DISK_PIPE_END_WRITE)
2568 * Closes an interprocess channel
2570 * @param p pipe to close
2571 * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
2574 GNUNET_DISK_pipe_close (struct GNUNET_DISK_PipeHandle *p)
2576 int ret = GNUNET_OK;
2579 int write_end_close;
2580 int read_end_close_errno;
2581 int write_end_close_errno;
2583 read_end_close = GNUNET_DISK_pipe_close_end (p, GNUNET_DISK_PIPE_END_READ);
2584 read_end_close_errno = errno;
2585 write_end_close = GNUNET_DISK_pipe_close_end (p, GNUNET_DISK_PIPE_END_WRITE);
2586 write_end_close_errno = errno;
2589 if (GNUNET_OK != read_end_close)
2591 errno = read_end_close_errno;
2592 ret = read_end_close;
2594 else if (GNUNET_OK != write_end_close)
2596 errno = write_end_close_errno;
2597 ret = write_end_close;
2605 * Get the handle to a particular pipe end
2608 * @param n end to access
2609 * @return handle for the respective end
2611 const struct GNUNET_DISK_FileHandle *
2612 GNUNET_DISK_pipe_handle (const struct GNUNET_DISK_PipeHandle *p,
2613 enum GNUNET_DISK_PipeEnd n)
2617 case GNUNET_DISK_PIPE_END_READ:
2618 case GNUNET_DISK_PIPE_END_WRITE:
2628 * Retrieve OS file handle
2630 * @param fh GNUnet file descriptor
2631 * @param dst destination buffer
2632 * @param dst_len length of dst
2633 * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
2636 GNUNET_DISK_internal_file_handle_ (const struct GNUNET_DISK_FileHandle *fh,
2637 void *dst, size_t dst_len)
2640 if (dst_len < sizeof (HANDLE))
2641 return GNUNET_SYSERR;
2642 *((HANDLE *) dst) = fh->h;
2644 if (dst_len < sizeof (int))
2645 return GNUNET_SYSERR;
2646 *((int *) dst) = fh->fd;