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,
292 int include_symbolic_links,
293 int single_file_mode)
295 struct GetFileSizeData gfsd;
298 GNUNET_assert (size != NULL);
300 gfsd.include_sym_links = include_symbolic_links;
301 gfsd.single_file_mode = single_file_mode;
302 ret = getSizeRec (&gfsd, filename);
309 * Obtain some unique identifiers for the given file
310 * that can be used to identify it in the local system.
311 * This function is used between GNUnet processes to
312 * quickly check if two files with the same absolute path
313 * are actually identical. The two processes represent
314 * the same peer but may communicate over the network
315 * (and the file may be on an NFS volume). This function
316 * may not be supported on all operating systems.
318 * @param filename name of the file
319 * @param dev set to the device ID
320 * @param ino set to the inode ID
321 * @return #GNUNET_OK on success
324 GNUNET_DISK_file_get_identifiers (const char *filename, uint64_t * dev,
329 // FIXME NILS: test this
330 struct GNUNET_DISK_FileHandle *fh;
331 BY_HANDLE_FILE_INFORMATION info;
334 fh = GNUNET_DISK_file_open (filename, GNUNET_DISK_OPEN_READ, 0);
336 return GNUNET_SYSERR;
337 succ = GetFileInformationByHandle (fh->h, &info);
338 GNUNET_DISK_file_close (fh);
341 return GNUNET_SYSERR;
343 *dev = info.dwVolumeSerialNumber;
344 *ino = ((((uint64_t) info.nFileIndexHigh) << (sizeof (DWORD) * 8)) | info.nFileIndexLow);
351 if (0 != stat (filename, &sbuf))
353 return GNUNET_SYSERR;
355 *ino = (uint64_t) sbuf.st_ino;
364 if (0 != statvfs (filename, &fbuf))
366 return GNUNET_SYSERR;
368 *dev = (uint64_t) fbuf.f_fsid;
374 if (0 != statfs (filename, &fbuf))
376 return GNUNET_SYSERR;
378 *dev = ((uint64_t) fbuf.f_fsid.val[0]) << 32 ||
379 ((uint64_t) fbuf.f_fsid.val[1]);
384 #endif /* !WINDOWS */
390 * Create the name for a temporary file or directory from a template.
392 * @param t template (without XXXXX or "/tmp/")
393 * @return name ready for passing to 'mktemp' or 'mkdtemp', NULL on error
396 mktemp_name (const char *t)
402 if ((t[0] != '/') && (t[0] != '\\')
404 && !(isalpha ((int) t[0]) && (t[0] != '\0') && (t[1] == ':'))
408 /* FIXME: This uses system codepage on W32, not UTF-8 */
409 tmpdir = getenv ("TMPDIR");
411 tmpdir = getenv ("TMP");
413 tmpdir = getenv ("TEMP");
416 GNUNET_asprintf (&tmpl, "%s/%s%s", tmpdir, t, "XXXXXX");
420 GNUNET_asprintf (&tmpl, "%s%s", t, "XXXXXX");
423 fn = (char *) GNUNET_malloc (MAX_PATH + 1);
424 if (ERROR_SUCCESS != plibc_conv_to_win_path (tmpl, fn))
447 tfn = GNUNET_strdup (fn);
448 random_fn = _mktemp (tfn);
449 if (NULL == random_fn)
454 /* FIXME: assume fn to be UTF-8-encoded and do the right thing */
455 if (0 == CreateDirectoryA (tfn, NULL))
457 DWORD error = GetLastError ();
459 if (ERROR_ALREADY_EXISTS == error)
471 * Update POSIX permissions mask of a file on disk. If both argumets
472 * are #GNUNET_NO, the file is made world-read-write-executable (777).
474 * @param fn name of the file to update
475 * @param require_uid_match #GNUNET_YES means 700
476 * @param require_gid_match #GNUNET_YES means 770 unless @a require_uid_match is set
479 GNUNET_DISK_fix_permissions (const char *fn,
480 int require_uid_match,
481 int require_gid_match)
485 if (GNUNET_YES == require_uid_match)
486 mode = S_IRUSR | S_IWUSR | S_IXUSR;
487 else if (GNUNET_YES == require_gid_match)
488 mode = S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IWGRP | S_IXGRP;
490 mode = S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IWGRP | S_IXGRP | S_IROTH | S_IWOTH | S_IXOTH;
491 if (0 != chmod (fn, mode))
492 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING,
500 * Create an (empty) temporary directory on disk. If the given name is not
501 * an absolute path, the current 'TMPDIR' will be prepended. In any case,
502 * 6 random characters will be appended to the name to create a unique
505 * @param t component to use for the name;
506 * does NOT contain "XXXXXX" or "/tmp/".
507 * @return NULL on error, otherwise name of fresh
508 * file on disk in directory for temporary files
511 GNUNET_DISK_mkdtemp (const char *t)
515 fn = mktemp_name (t);
516 if (fn != mkdtemp (fn))
518 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_ERROR, "mkstemp", fn);
527 * Move a file out of the way (create a backup) by
528 * renaming it to "orig.NUM~" where NUM is the smallest
529 * number that is not used yet.
531 * @param fil name of the file to back up
534 GNUNET_DISK_file_backup (const char *fil)
540 slen = strlen (fil) + 20;
541 target = GNUNET_malloc (slen);
545 GNUNET_snprintf (target, slen,
549 } while (0 == access (target, F_OK));
550 if (0 != rename (fil, target))
551 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR,
554 GNUNET_free (target);
559 * Create an (empty) temporary file on disk. If the given name is not
560 * an absolute path, the current 'TMPDIR' will be prepended. In any case,
561 * 6 random characters will be appended to the name to create a unique
564 * @param t component to use for the name;
565 * does NOT contain "XXXXXX" or "/tmp/".
566 * @return NULL on error, otherwise name of fresh
567 * file on disk in directory for temporary files
570 GNUNET_DISK_mktemp (const char *t)
575 fn = mktemp_name (t);
576 if (-1 == (fd = mkstemp (fn)))
578 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_ERROR, "mkstemp", fn);
583 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "close", fn);
589 * Test if @a fil is a directory and listable. Optionally, also check if the
590 * directory is readable. Will not print an error message if the directory does
591 * not exist. Will log errors if #GNUNET_SYSERR is returned (i.e., a file exists
592 * with the same name).
594 * @param fil filename to test
595 * @param is_readable GNUNET_YES to additionally check if @a fil is readable;
596 * #GNUNET_NO to disable this check
597 * @return #GNUNET_YES if yes, #GNUNET_NO if not; #GNUNET_SYSERR if it
598 * does not exist or stat'ed
601 GNUNET_DISK_directory_test (const char *fil, int is_readable)
603 struct stat filestat;
606 ret = STAT (fil, &filestat);
610 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "stat", fil);
611 return GNUNET_SYSERR;
613 if (!S_ISDIR (filestat.st_mode))
615 LOG (GNUNET_ERROR_TYPE_DEBUG,
616 "A file already exits with the same name %s\n", fil);
619 if (GNUNET_YES == is_readable)
620 ret = ACCESS (fil, R_OK | X_OK);
622 ret = ACCESS (fil, X_OK);
625 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "access", fil);
633 * Check that fil corresponds to a filename
634 * (of a file that exists and that is not a directory).
636 * @param fil filename to check
637 * @return #GNUNET_YES if yes, #GNUNET_NO if not a file, #GNUNET_SYSERR if something
638 * else (will print an error message in that case, too).
641 GNUNET_DISK_file_test (const char *fil)
643 struct stat filestat;
647 rdir = GNUNET_STRINGS_filename_expand (fil);
649 return GNUNET_SYSERR;
651 ret = STAT (rdir, &filestat);
656 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "stat", rdir);
658 return GNUNET_SYSERR;
663 if (!S_ISREG (filestat.st_mode))
668 if (ACCESS (rdir, F_OK) < 0)
670 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "access", rdir);
672 return GNUNET_SYSERR;
680 * Implementation of "mkdir -p"
682 * @param dir the directory to create
683 * @return #GNUNET_OK on success, #GNUNET_SYSERR on failure
686 GNUNET_DISK_directory_create (const char *dir)
694 rdir = GNUNET_STRINGS_filename_expand (dir);
696 return GNUNET_SYSERR;
700 pos = 1; /* skip heading '/' */
702 /* Local or Network path? */
703 if (strncmp (rdir, "\\\\", 2) == 0)
708 if (rdir[pos] == '\\')
718 pos = 3; /* strlen("C:\\") */
721 /* Check which low level directories already exist */
723 rdir[len] = DIR_SEPARATOR;
726 if (DIR_SEPARATOR == rdir[pos2])
729 ret = GNUNET_DISK_directory_test (rdir, GNUNET_NO);
730 if (GNUNET_NO == ret)
733 return GNUNET_SYSERR;
735 rdir[pos2] = DIR_SEPARATOR;
736 if (GNUNET_YES == ret)
747 /* Start creating directories */
750 if ((rdir[pos] == DIR_SEPARATOR) || (pos == len))
753 ret = GNUNET_DISK_directory_test (rdir, GNUNET_NO);
754 if (GNUNET_NO == ret)
757 return GNUNET_SYSERR;
759 if (GNUNET_SYSERR == ret)
762 ret = mkdir (rdir, S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH); /* 755 */
764 wchar_t wrdir[MAX_PATH + 1];
765 if (ERROR_SUCCESS == plibc_conv_to_win_pathwconv(rdir, wrdir))
766 ret = !CreateDirectoryW (wrdir, NULL);
770 if ((ret != 0) && (errno != EEXIST))
772 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_ERROR, "mkdir", rdir);
774 return GNUNET_SYSERR;
777 rdir[pos] = DIR_SEPARATOR;
787 * Create the directory structure for storing
790 * @param filename name of a file in the directory
791 * @returns #GNUNET_OK on success,
792 * #GNUNET_SYSERR on failure,
793 * #GNUNET_NO if the directory
794 * exists but is not writeable for us
797 GNUNET_DISK_directory_create_for_file (const char *filename)
803 rdir = GNUNET_STRINGS_filename_expand (filename);
805 return GNUNET_SYSERR;
807 while ((len > 0) && (rdir[len] != DIR_SEPARATOR))
810 ret = GNUNET_DISK_directory_create (rdir);
811 if ((ret == GNUNET_OK) && (0 != ACCESS (rdir, W_OK)))
819 * Read the contents of a binary file into a buffer.
821 * @param h handle to an open file
822 * @param result the buffer to write the result to
823 * @param len the maximum number of bytes to read
824 * @return the number of bytes read on success, #GNUNET_SYSERR on failure
827 GNUNET_DISK_file_read (const struct GNUNET_DISK_FileHandle *h,
834 return GNUNET_SYSERR;
840 if (h->type != GNUNET_DISK_HANLDE_TYPE_PIPE)
842 if (!ReadFile (h->h, result, len, &bytes_read, NULL))
844 SetErrnoFromWinError (GetLastError ());
845 return GNUNET_SYSERR;
850 if (!ReadFile (h->h, result, len, &bytes_read, h->oOverlapRead))
852 if (GetLastError () != ERROR_IO_PENDING)
854 LOG (GNUNET_ERROR_TYPE_DEBUG, "Error reading from pipe: %u\n", GetLastError ());
855 SetErrnoFromWinError (GetLastError ());
856 return GNUNET_SYSERR;
858 LOG (GNUNET_ERROR_TYPE_DEBUG, "Will get overlapped result\n");
859 GetOverlappedResult (h->h, h->oOverlapRead, &bytes_read, TRUE);
861 LOG (GNUNET_ERROR_TYPE_DEBUG, "Read %u bytes from pipe\n", bytes_read);
865 return read (h->fd, result, len);
871 * Read the contents of a binary file into a buffer.
872 * Guarantees not to block (returns GNUNET_SYSERR and sets errno to EAGAIN
873 * when no data can be read).
875 * @param h handle to an open file
876 * @param result the buffer to write the result to
877 * @param len the maximum number of bytes to read
878 * @return the number of bytes read on success, #GNUNET_SYSERR on failure
881 GNUNET_DISK_file_read_non_blocking (const struct GNUNET_DISK_FileHandle *h,
888 return GNUNET_SYSERR;
894 if (h->type != GNUNET_DISK_HANLDE_TYPE_PIPE)
896 if (!ReadFile (h->h, result, len, &bytes_read, NULL))
898 SetErrnoFromWinError (GetLastError ());
899 return GNUNET_SYSERR;
904 if (!ReadFile (h->h, result, len, &bytes_read, h->oOverlapRead))
906 if (GetLastError () != ERROR_IO_PENDING)
908 LOG (GNUNET_ERROR_TYPE_DEBUG, "Error reading from pipe: %u\n", GetLastError ());
909 SetErrnoFromWinError (GetLastError ());
910 return GNUNET_SYSERR;
914 LOG (GNUNET_ERROR_TYPE_DEBUG,
915 "ReadFile() queued a read, cancelling\n");
918 return GNUNET_SYSERR;
921 LOG (GNUNET_ERROR_TYPE_DEBUG, "Read %u bytes\n", bytes_read);
928 /* set to non-blocking, read, then set back */
929 flags = fcntl (h->fd, F_GETFL);
930 if (0 == (flags & O_NONBLOCK))
931 (void) fcntl (h->fd, F_SETFL, flags | O_NONBLOCK);
932 ret = read (h->fd, result, len);
933 if (0 == (flags & O_NONBLOCK))
936 (void) fcntl (h->fd, F_SETFL, flags);
945 * Read the contents of a binary file into a buffer.
947 * @param fn file name
948 * @param result the buffer to write the result to
949 * @param len the maximum number of bytes to read
950 * @return number of bytes read, #GNUNET_SYSERR on failure
953 GNUNET_DISK_fn_read (const char *fn,
957 struct GNUNET_DISK_FileHandle *fh;
960 fh = GNUNET_DISK_file_open (fn, GNUNET_DISK_OPEN_READ, GNUNET_DISK_PERM_NONE);
962 return GNUNET_SYSERR;
963 ret = GNUNET_DISK_file_read (fh, result, len);
964 GNUNET_assert (GNUNET_OK == GNUNET_DISK_file_close (fh));
971 * Write a buffer to a file.
973 * @param h handle to open file
974 * @param buffer the data to write
975 * @param n number of bytes to write
976 * @return number of bytes written on success, #GNUNET_SYSERR on error
979 GNUNET_DISK_file_write (const struct GNUNET_DISK_FileHandle * h,
980 const void *buffer, size_t n)
985 return GNUNET_SYSERR;
991 if (h->type != GNUNET_DISK_HANLDE_TYPE_PIPE)
993 if (!WriteFile (h->h, buffer, n, &bytes_written, NULL))
995 SetErrnoFromWinError (GetLastError ());
996 return GNUNET_SYSERR;
1001 LOG (GNUNET_ERROR_TYPE_DEBUG, "It is a pipe trying to write %u bytes\n", n);
1002 if (!WriteFile (h->h, buffer, n, &bytes_written, h->oOverlapWrite))
1004 if (GetLastError () != ERROR_IO_PENDING)
1006 SetErrnoFromWinError (GetLastError ());
1007 LOG (GNUNET_ERROR_TYPE_DEBUG, "Error writing to pipe: %u\n",
1009 return GNUNET_SYSERR;
1011 LOG (GNUNET_ERROR_TYPE_DEBUG, "Will get overlapped result\n");
1012 if (!GetOverlappedResult (h->h, h->oOverlapWrite, &bytes_written, TRUE))
1014 SetErrnoFromWinError (GetLastError ());
1015 LOG (GNUNET_ERROR_TYPE_DEBUG,
1016 "Error getting overlapped result while writing to pipe: %u\n",
1018 return GNUNET_SYSERR;
1024 if (!GetOverlappedResult (h->h, h->oOverlapWrite, &ovr, TRUE))
1026 LOG (GNUNET_ERROR_TYPE_DEBUG,
1027 "Error getting control overlapped result while writing to pipe: %u\n",
1032 LOG (GNUNET_ERROR_TYPE_DEBUG,
1033 "Wrote %u bytes (ovr says %u), picking the greatest\n",
1034 bytes_written, ovr);
1037 if (bytes_written == 0)
1041 LOG (GNUNET_ERROR_TYPE_DEBUG, "Wrote %u bytes, returning -1 with EAGAIN\n", bytes_written);
1043 return GNUNET_SYSERR;
1046 LOG (GNUNET_ERROR_TYPE_DEBUG, "Wrote %u bytes\n", bytes_written);
1048 return bytes_written;
1050 return write (h->fd, buffer, n);
1056 * Write a buffer to a file, blocking, if necessary.
1058 * @param h handle to open file
1059 * @param buffer the data to write
1060 * @param n number of bytes to write
1061 * @return number of bytes written on success, #GNUNET_SYSERR on error
1064 GNUNET_DISK_file_write_blocking (const struct GNUNET_DISK_FileHandle * h,
1071 return GNUNET_SYSERR;
1075 DWORD bytes_written;
1076 /* We do a non-overlapped write, which is as blocking as it gets */
1077 LOG (GNUNET_ERROR_TYPE_DEBUG, "Writing %u bytes\n", n);
1078 if (!WriteFile (h->h, buffer, n, &bytes_written, NULL))
1080 SetErrnoFromWinError (GetLastError ());
1081 LOG (GNUNET_ERROR_TYPE_DEBUG, "Error writing to pipe: %u\n",
1083 return GNUNET_SYSERR;
1085 if (bytes_written == 0 && n > 0)
1087 LOG (GNUNET_ERROR_TYPE_DEBUG, "Waiting for pipe to clean\n");
1088 WaitForSingleObject (h->h, INFINITE);
1089 if (!WriteFile (h->h, buffer, n, &bytes_written, NULL))
1091 SetErrnoFromWinError (GetLastError ());
1092 LOG (GNUNET_ERROR_TYPE_DEBUG, "Error writing to pipe: %u\n",
1094 return GNUNET_SYSERR;
1097 LOG (GNUNET_ERROR_TYPE_DEBUG,
1100 return bytes_written;
1105 /* set to blocking, write, then set back */
1106 flags = fcntl (h->fd, F_GETFL);
1107 if (0 != (flags & O_NONBLOCK))
1108 (void) fcntl (h->fd, F_SETFL, flags - O_NONBLOCK);
1109 ret = write (h->fd, buffer, n);
1110 if (0 == (flags & O_NONBLOCK))
1111 (void) fcntl (h->fd, F_SETFL, flags);
1118 * Write a buffer to a file. If the file is longer than the
1119 * number of bytes that will be written, it will be truncated.
1121 * @param fn file name
1122 * @param buffer the data to write
1123 * @param n number of bytes to write
1124 * @param mode file permissions
1125 * @return number of bytes written on success, #GNUNET_SYSERR on error
1128 GNUNET_DISK_fn_write (const char *fn, const void *buffer, size_t n,
1129 enum GNUNET_DISK_AccessPermissions mode)
1131 struct GNUNET_DISK_FileHandle *fh;
1134 fh = GNUNET_DISK_file_open (fn,
1135 GNUNET_DISK_OPEN_WRITE | GNUNET_DISK_OPEN_TRUNCATE
1136 | GNUNET_DISK_OPEN_CREATE, mode);
1138 return GNUNET_SYSERR;
1139 ret = GNUNET_DISK_file_write (fh, buffer, n);
1140 GNUNET_assert (GNUNET_OK == GNUNET_DISK_file_close (fh));
1146 * Scan a directory for files.
1148 * @param dir_name the name of the directory
1149 * @param callback the method to call for each file,
1150 * can be NULL, in that case, we only count
1151 * @param callback_cls closure for @a callback
1152 * @return the number of files found, #GNUNET_SYSERR on error or
1153 * ieration aborted by callback returning #GNUNET_SYSERR
1156 GNUNET_DISK_directory_scan (const char *dir_name,
1157 GNUNET_FileNameCallback callback,
1161 struct dirent *finfo;
1167 unsigned int name_len;
1168 unsigned int n_size;
1170 GNUNET_assert (dir_name != NULL);
1171 dname = GNUNET_STRINGS_filename_expand (dir_name);
1173 return GNUNET_SYSERR;
1174 while ((strlen (dname) > 0) && (dname[strlen (dname) - 1] == DIR_SEPARATOR))
1175 dname[strlen (dname) - 1] = '\0';
1176 if (0 != STAT (dname, &istat))
1178 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "stat", dname);
1179 GNUNET_free (dname);
1180 return GNUNET_SYSERR;
1182 if (!S_ISDIR (istat.st_mode))
1184 LOG (GNUNET_ERROR_TYPE_WARNING,
1185 _("Expected `%s' to be a directory!\n"),
1187 GNUNET_free (dname);
1188 return GNUNET_SYSERR;
1191 dinfo = OPENDIR (dname);
1192 if ((errno == EACCES) || (dinfo == NULL))
1194 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "opendir", dname);
1197 GNUNET_free (dname);
1198 return GNUNET_SYSERR;
1201 n_size = strlen (dname) + name_len + 2;
1202 name = GNUNET_malloc (n_size);
1203 while ((finfo = READDIR (dinfo)) != NULL)
1205 if ((0 == strcmp (finfo->d_name, ".")) ||
1206 (0 == strcmp (finfo->d_name, "..")))
1208 if (callback != NULL)
1210 if (name_len < strlen (finfo->d_name))
1213 name_len = strlen (finfo->d_name);
1214 n_size = strlen (dname) + name_len + 2;
1215 name = GNUNET_malloc (n_size);
1217 /* dname can end in "/" only if dname == "/";
1218 * if dname does not end in "/", we need to add
1219 * a "/" (otherwise, we must not!) */
1220 GNUNET_snprintf (name, n_size, "%s%s%s", dname,
1221 (strcmp (dname, DIR_SEPARATOR_STR) ==
1222 0) ? "" : DIR_SEPARATOR_STR, finfo->d_name);
1223 ret = callback (callback_cls, name);
1224 if (GNUNET_OK != ret)
1228 GNUNET_free (dname);
1229 if (GNUNET_NO == ret)
1231 return GNUNET_SYSERR;
1238 GNUNET_free (dname);
1244 * Opaque handle used for iterating over a directory.
1246 struct GNUNET_DISK_DirectoryIterator
1250 * Function to call on directory entries.
1252 GNUNET_DISK_DirectoryIteratorCallback callback;
1255 * Closure for callback.
1260 * Reference to directory.
1270 * Next filename to process.
1277 enum GNUNET_SCHEDULER_Priority priority;
1283 * Task used by the directory iterator.
1286 directory_iterator_task (void *cls,
1287 const struct GNUNET_SCHEDULER_TaskContext *tc)
1289 struct GNUNET_DISK_DirectoryIterator *iter = cls;
1292 name = iter->next_name;
1293 GNUNET_assert (name != NULL);
1294 iter->next_name = NULL;
1295 iter->callback (iter->callback_cls, iter, name, iter->dirname);
1301 * This function must be called during the DiskIteratorCallback
1302 * (exactly once) to schedule the task to process the next
1303 * filename in the directory (if there is one).
1305 * @param iter opaque handle for the iterator
1306 * @param can set to GNUNET_YES to terminate the iteration early
1307 * @return GNUNET_YES if iteration will continue,
1308 * GNUNET_NO if this was the last entry (and iteration is complete),
1309 * GNUNET_SYSERR if abort was YES
1312 GNUNET_DISK_directory_iterator_next (struct GNUNET_DISK_DirectoryIterator *iter,
1315 struct dirent *finfo;
1317 GNUNET_assert (iter->next_name == NULL);
1318 if (can == GNUNET_YES)
1320 CLOSEDIR (iter->directory);
1321 GNUNET_free (iter->dirname);
1323 return GNUNET_SYSERR;
1325 while (NULL != (finfo = READDIR (iter->directory)))
1327 if ((0 == strcmp (finfo->d_name, ".")) ||
1328 (0 == strcmp (finfo->d_name, "..")))
1330 GNUNET_asprintf (&iter->next_name, "%s%s%s", iter->dirname,
1331 DIR_SEPARATOR_STR, finfo->d_name);
1336 GNUNET_DISK_directory_iterator_next (iter, GNUNET_YES);
1339 GNUNET_SCHEDULER_add_with_priority (iter->priority, &directory_iterator_task,
1346 * Scan a directory for files using the scheduler to run a task for
1347 * each entry. The name of the directory must be expanded first (!).
1348 * If a scheduler does not need to be used, GNUNET_DISK_directory_scan
1349 * may provide a simpler API.
1351 * @param prio priority to use
1352 * @param dir_name the name of the directory
1353 * @param callback the method to call for each file
1354 * @param callback_cls closure for callback
1355 * @return GNUNET_YES if directory is not empty and 'callback'
1356 * will be called later, GNUNET_NO otherwise, GNUNET_SYSERR on error.
1359 GNUNET_DISK_directory_iterator_start (enum GNUNET_SCHEDULER_Priority prio,
1360 const char *dir_name,
1361 GNUNET_DISK_DirectoryIteratorCallback
1362 callback, void *callback_cls)
1364 struct GNUNET_DISK_DirectoryIterator *di;
1366 di = GNUNET_new (struct GNUNET_DISK_DirectoryIterator);
1367 di->callback = callback;
1368 di->callback_cls = callback_cls;
1369 di->directory = OPENDIR (dir_name);
1370 if (di->directory == NULL)
1373 callback (callback_cls, NULL, NULL, NULL);
1374 return GNUNET_SYSERR;
1376 di->dirname = GNUNET_strdup (dir_name);
1377 di->priority = prio;
1378 return GNUNET_DISK_directory_iterator_next (di, GNUNET_NO);
1383 * Function that removes the given directory by calling
1384 * "GNUNET_DISK_directory_remove".
1386 * @param unused not used
1387 * @param fn directory to remove
1388 * @return #GNUNET_OK
1391 remove_helper (void *unused, const char *fn)
1393 (void) GNUNET_DISK_directory_remove (fn);
1399 * Remove all files in a directory (rm -rf). Call with
1402 * @param filename the file to remove
1403 * @return #GNUNET_OK on success, #GNUNET_SYSERR on error
1406 GNUNET_DISK_directory_remove (const char *filename)
1410 if (NULL == filename)
1413 return GNUNET_SYSERR;
1415 if (0 != LSTAT (filename, &istat))
1416 return GNUNET_NO; /* file may not exist... */
1417 (void) CHMOD (filename, S_IWUSR | S_IRUSR | S_IXUSR);
1418 if (UNLINK (filename) == 0)
1420 if ((errno != EISDIR) &&
1421 /* EISDIR is not sufficient in all cases, e.g.
1422 * sticky /tmp directory may result in EPERM on BSD.
1423 * So we also explicitly check "isDirectory" */
1424 (GNUNET_YES != GNUNET_DISK_directory_test (filename, GNUNET_YES)))
1426 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "rmdir", filename);
1427 return GNUNET_SYSERR;
1429 if (GNUNET_SYSERR ==
1430 GNUNET_DISK_directory_scan (filename, &remove_helper, NULL))
1431 return GNUNET_SYSERR;
1432 if (0 != RMDIR (filename))
1434 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "rmdir", filename);
1435 return GNUNET_SYSERR;
1444 * @param src file to copy
1445 * @param dst destination file name
1446 * @return #GNUNET_OK on success, #GNUNET_SYSERR on error
1449 GNUNET_DISK_file_copy (const char *src,
1456 struct GNUNET_DISK_FileHandle *in;
1457 struct GNUNET_DISK_FileHandle *out;
1459 if (GNUNET_OK != GNUNET_DISK_file_size (src, &size, GNUNET_YES, GNUNET_YES))
1460 return GNUNET_SYSERR;
1462 in = GNUNET_DISK_file_open (src, GNUNET_DISK_OPEN_READ,
1463 GNUNET_DISK_PERM_NONE);
1465 return GNUNET_SYSERR;
1467 GNUNET_DISK_file_open (dst,
1468 GNUNET_DISK_OPEN_WRITE | GNUNET_DISK_OPEN_CREATE |
1469 GNUNET_DISK_OPEN_FAILIFEXISTS,
1470 GNUNET_DISK_PERM_USER_READ |
1471 GNUNET_DISK_PERM_USER_WRITE |
1472 GNUNET_DISK_PERM_GROUP_READ |
1473 GNUNET_DISK_PERM_GROUP_WRITE);
1476 GNUNET_DISK_file_close (in);
1477 return GNUNET_SYSERR;
1479 buf = GNUNET_malloc (COPY_BLK_SIZE);
1482 len = COPY_BLK_SIZE;
1483 if (len > size - pos)
1485 if (len != GNUNET_DISK_file_read (in, buf, len))
1487 if (len != GNUNET_DISK_file_write (out, buf, len))
1492 GNUNET_DISK_file_close (in);
1493 GNUNET_DISK_file_close (out);
1497 GNUNET_DISK_file_close (in);
1498 GNUNET_DISK_file_close (out);
1499 return GNUNET_SYSERR;
1504 * @brief Removes special characters as ':' from a filename.
1505 * @param fn the filename to canonicalize
1508 GNUNET_DISK_filename_canonicalize (char *fn)
1518 if (c == '/' || c == '\\' || c == ':' || c == '*' || c == '?' || c == '"' ||
1519 c == '<' || c == '>' || c == '|')
1531 * @brief Change owner of a file
1533 * @param filename name of file to change the owner of
1534 * @param user name of the new owner
1535 * @return GNUNET_OK on success, GNUNET_SYSERR on failure
1538 GNUNET_DISK_file_change_owner (const char *filename, const char *user)
1543 pws = getpwnam (user);
1546 LOG (GNUNET_ERROR_TYPE_ERROR,
1547 _("Cannot obtain information about user `%s': %s\n"), user,
1549 return GNUNET_SYSERR;
1551 if (0 != chown (filename, pws->pw_uid, pws->pw_gid))
1552 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "chown", filename);
1559 * Lock a part of a file
1560 * @param fh file handle
1561 * @param lock_start absolute position from where to lock
1562 * @param lock_end absolute position until where to lock
1563 * @param excl GNUNET_YES for an exclusive lock
1564 * @return GNUNET_OK on success, GNUNET_SYSERR on error
1567 GNUNET_DISK_file_lock (struct GNUNET_DISK_FileHandle *fh, off_t lock_start,
1568 off_t lock_end, int excl)
1573 return GNUNET_SYSERR;
1579 memset (&fl, 0, sizeof (struct flock));
1580 fl.l_type = excl ? F_WRLCK : F_RDLCK;
1581 fl.l_whence = SEEK_SET;
1582 fl.l_start = lock_start;
1583 fl.l_len = lock_end;
1585 return fcntl (fh->fd, F_SETLK, &fl) != 0 ? GNUNET_SYSERR : GNUNET_OK;
1588 off_t diff = lock_end - lock_start;
1589 DWORD diff_low, diff_high;
1590 diff_low = (DWORD) (diff & 0xFFFFFFFF);
1591 diff_high = (DWORD) ((diff >> (sizeof (DWORD) * 8)) & 0xFFFFFFFF);
1593 memset (&o, 0, sizeof (OVERLAPPED));
1594 o.Offset = (DWORD) (lock_start & 0xFFFFFFFF);;
1595 o.OffsetHigh = (DWORD) (((lock_start & ~0xFFFFFFFF) >> (sizeof (DWORD) * 8)) & 0xFFFFFFFF);
1598 (fh->h, (excl ? LOCKFILE_EXCLUSIVE_LOCK : 0) | LOCKFILE_FAIL_IMMEDIATELY,
1599 0, diff_low, diff_high, &o))
1601 SetErrnoFromWinError (GetLastError ());
1602 return GNUNET_SYSERR;
1611 * Unlock a part of a file
1612 * @param fh file handle
1613 * @param unlock_start absolute position from where to unlock
1614 * @param unlock_end absolute position until where to unlock
1615 * @return GNUNET_OK on success, GNUNET_SYSERR on error
1618 GNUNET_DISK_file_unlock (struct GNUNET_DISK_FileHandle *fh, off_t unlock_start,
1624 return GNUNET_SYSERR;
1630 memset (&fl, 0, sizeof (struct flock));
1631 fl.l_type = F_UNLCK;
1632 fl.l_whence = SEEK_SET;
1633 fl.l_start = unlock_start;
1634 fl.l_len = unlock_end;
1636 return fcntl (fh->fd, F_SETLK, &fl) != 0 ? GNUNET_SYSERR : GNUNET_OK;
1639 off_t diff = unlock_end - unlock_start;
1640 DWORD diff_low, diff_high;
1641 diff_low = (DWORD) (diff & 0xFFFFFFFF);
1642 diff_high = (DWORD) ((diff >> (sizeof (DWORD) * 8)) & 0xFFFFFFFF);
1644 memset (&o, 0, sizeof (OVERLAPPED));
1645 o.Offset = (DWORD) (unlock_start & 0xFFFFFFFF);;
1646 o.OffsetHigh = (DWORD) (((unlock_start & ~0xFFFFFFFF) >> (sizeof (DWORD) * 8)) & 0xFFFFFFFF);
1648 if (!UnlockFileEx (fh->h, 0, diff_low, diff_high, &o))
1650 SetErrnoFromWinError (GetLastError ());
1651 return GNUNET_SYSERR;
1660 * Open a file. Note that the access permissions will only be
1661 * used if a new file is created and if the underlying operating
1662 * system supports the given permissions.
1664 * @param fn file name to be opened
1665 * @param flags opening flags, a combination of GNUNET_DISK_OPEN_xxx bit flags
1666 * @param perm permissions for the newly created file, use
1667 * #GNUNET_DISK_PERM_NONE if a file could not be created by this
1668 * call (because of flags)
1669 * @return IO handle on success, NULL on error
1671 struct GNUNET_DISK_FileHandle *
1672 GNUNET_DISK_file_open (const char *fn,
1673 enum GNUNET_DISK_OpenFlags flags,
1674 enum GNUNET_DISK_AccessPermissions perm)
1677 struct GNUNET_DISK_FileHandle *ret;
1683 wchar_t wexpfn[MAX_PATH + 1];
1690 expfn = GNUNET_STRINGS_filename_expand (fn);
1695 if (GNUNET_DISK_OPEN_READWRITE == (flags & GNUNET_DISK_OPEN_READWRITE))
1696 oflags = O_RDWR; /* note: O_RDWR is NOT always O_RDONLY | O_WRONLY */
1697 else if (flags & GNUNET_DISK_OPEN_READ)
1699 else if (flags & GNUNET_DISK_OPEN_WRITE)
1704 GNUNET_free (expfn);
1707 if (flags & GNUNET_DISK_OPEN_FAILIFEXISTS)
1708 oflags |= (O_CREAT | O_EXCL);
1709 if (flags & GNUNET_DISK_OPEN_TRUNCATE)
1711 if (flags & GNUNET_DISK_OPEN_APPEND)
1713 if (flags & GNUNET_DISK_OPEN_CREATE)
1715 (void) GNUNET_DISK_directory_create_for_file (expfn);
1717 mode = translate_unix_perms (perm);
1720 fd = open (expfn, oflags
1724 | O_LARGEFILE, mode);
1727 if (0 == (flags & GNUNET_DISK_OPEN_FAILIFEXISTS))
1728 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "open", expfn);
1730 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_DEBUG, "open", expfn);
1731 GNUNET_free (expfn);
1738 if (GNUNET_DISK_OPEN_READWRITE == (flags & GNUNET_DISK_OPEN_READWRITE))
1739 access = FILE_READ_DATA | FILE_WRITE_DATA;
1740 else if (flags & GNUNET_DISK_OPEN_READ)
1741 access = FILE_READ_DATA;
1742 else if (flags & GNUNET_DISK_OPEN_WRITE)
1743 access = FILE_WRITE_DATA;
1745 if (flags & GNUNET_DISK_OPEN_FAILIFEXISTS)
1749 else if (flags & GNUNET_DISK_OPEN_CREATE)
1751 (void) GNUNET_DISK_directory_create_for_file (expfn);
1752 if (flags & GNUNET_DISK_OPEN_TRUNCATE)
1753 disp = CREATE_ALWAYS;
1757 else if (flags & GNUNET_DISK_OPEN_TRUNCATE)
1759 disp = TRUNCATE_EXISTING;
1763 disp = OPEN_EXISTING;
1766 if (ERROR_SUCCESS == plibc_conv_to_win_pathwconv(expfn, wexpfn))
1767 h = CreateFileW (wexpfn, access,
1768 FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE, NULL,
1769 disp, FILE_ATTRIBUTE_NORMAL, NULL);
1771 h = INVALID_HANDLE_VALUE;
1772 if (h == INVALID_HANDLE_VALUE)
1775 SetErrnoFromWinError (GetLastError ());
1777 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_INFO, "open", expfn);
1778 GNUNET_free (expfn);
1783 if (flags & GNUNET_DISK_OPEN_APPEND)
1784 if (SetFilePointer (h, 0, 0, FILE_END) == INVALID_SET_FILE_POINTER)
1786 SetErrnoFromWinError (GetLastError ());
1787 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "SetFilePointer", expfn);
1789 GNUNET_free (expfn);
1794 ret = GNUNET_new (struct GNUNET_DISK_FileHandle);
1797 ret->type = GNUNET_DISK_HANLDE_TYPE_FILE;
1801 GNUNET_free (expfn);
1807 * Close an open file
1808 * @param h file handle
1809 * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
1812 GNUNET_DISK_file_close (struct GNUNET_DISK_FileHandle *h)
1818 return GNUNET_SYSERR;
1824 if (!CloseHandle (h->h))
1826 SetErrnoFromWinError (GetLastError ());
1827 LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING, "close");
1828 ret = GNUNET_SYSERR;
1830 if (h->oOverlapRead)
1832 if (!CloseHandle (h->oOverlapRead->hEvent))
1834 SetErrnoFromWinError (GetLastError ());
1835 LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING, "close");
1836 ret = GNUNET_SYSERR;
1838 GNUNET_free (h->oOverlapRead);
1840 if (h->oOverlapWrite)
1842 if (!CloseHandle (h->oOverlapWrite->hEvent))
1844 SetErrnoFromWinError (GetLastError ());
1845 LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING, "close");
1846 ret = GNUNET_SYSERR;
1848 GNUNET_free (h->oOverlapWrite);
1851 if (close (h->fd) != 0)
1853 LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING, "close");
1854 ret = GNUNET_SYSERR;
1863 * Get a GNUnet file handle from a W32 handle.
1865 * @param handle native handle
1866 * @return GNUnet file handle corresponding to the W32 handle
1868 struct GNUNET_DISK_FileHandle *
1869 GNUNET_DISK_get_handle_from_w32_handle (HANDLE osfh)
1871 struct GNUNET_DISK_FileHandle *fh;
1874 enum GNUNET_FILE_Type ftype;
1876 dwret = GetFileType (osfh);
1879 case FILE_TYPE_DISK:
1880 ftype = GNUNET_DISK_HANLDE_TYPE_FILE;
1882 case FILE_TYPE_PIPE:
1883 ftype = GNUNET_DISK_HANLDE_TYPE_PIPE;
1889 fh = GNUNET_new (struct GNUNET_DISK_FileHandle);
1893 if (ftype == GNUNET_DISK_HANLDE_TYPE_PIPE)
1896 * Note that we can't make it overlapped if it isn't already.
1897 * (ReOpenFile() is only available in 2003/Vista).
1898 * The process that opened this file in the first place (usually a parent
1899 * process, if this is stdin/stdout/stderr) must make it overlapped,
1900 * otherwise we're screwed, as selecting on non-overlapped handle
1903 fh->oOverlapRead = GNUNET_malloc (sizeof (OVERLAPPED));
1904 fh->oOverlapWrite = GNUNET_malloc (sizeof (OVERLAPPED));
1905 fh->oOverlapRead->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
1906 fh->oOverlapWrite->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
1914 * Get a handle from a native integer FD.
1916 * @param fno native integer file descriptor
1917 * @return file handle corresponding to the descriptor, NULL on error
1919 struct GNUNET_DISK_FileHandle *
1920 GNUNET_DISK_get_handle_from_int_fd (int fno)
1922 struct GNUNET_DISK_FileHandle *fh;
1924 if ( (((off_t) -1) == lseek (fno, 0, SEEK_CUR)) &&
1926 return NULL; /* invalid FD */
1929 fh = GNUNET_new (struct GNUNET_DISK_FileHandle);
1935 osfh = _get_osfhandle (fno);
1936 if (INVALID_HANDLE_VALUE == (HANDLE) osfh)
1939 fh = GNUNET_DISK_get_handle_from_w32_handle ((HANDLE) osfh);
1947 * Get a handle from a native streaming FD.
1949 * @param fd native streaming file descriptor
1950 * @return file handle corresponding to the descriptor
1952 struct GNUNET_DISK_FileHandle *
1953 GNUNET_DISK_get_handle_from_native (FILE *fd)
1961 return GNUNET_DISK_get_handle_from_int_fd (fno);
1966 * Handle for a memory-mapping operation.
1968 struct GNUNET_DISK_MapHandle
1971 * Address where the map is in memory.
1977 * Underlying OS handle.
1982 * Number of bytes mapped.
1990 #define MAP_FAILED ((void *) -1)
1994 * Map a file into memory
1996 * @param h open file handle
1997 * @param m handle to the new mapping
1998 * @param access access specification, GNUNET_DISK_MAP_TYPE_xxx
1999 * @param len size of the mapping
2000 * @return pointer to the mapped memory region, NULL on failure
2003 GNUNET_DISK_file_map (const struct GNUNET_DISK_FileHandle *h,
2004 struct GNUNET_DISK_MapHandle **m,
2005 enum GNUNET_DISK_MapType access, size_t len)
2014 DWORD mapAccess, protect;
2016 if ((access & GNUNET_DISK_MAP_TYPE_READ) &&
2017 (access & GNUNET_DISK_MAP_TYPE_WRITE))
2019 protect = PAGE_READWRITE;
2020 mapAccess = FILE_MAP_ALL_ACCESS;
2022 else if (access & GNUNET_DISK_MAP_TYPE_READ)
2024 protect = PAGE_READONLY;
2025 mapAccess = FILE_MAP_READ;
2027 else if (access & GNUNET_DISK_MAP_TYPE_WRITE)
2029 protect = PAGE_READWRITE;
2030 mapAccess = FILE_MAP_WRITE;
2038 *m = GNUNET_new (struct GNUNET_DISK_MapHandle);
2039 (*m)->h = CreateFileMapping (h->h, NULL, protect, 0, 0, NULL);
2040 if ((*m)->h == INVALID_HANDLE_VALUE)
2042 SetErrnoFromWinError (GetLastError ());
2047 (*m)->addr = MapViewOfFile ((*m)->h, mapAccess, 0, 0, len);
2050 SetErrnoFromWinError (GetLastError ());
2051 CloseHandle ((*m)->h);
2060 if (access & GNUNET_DISK_MAP_TYPE_READ)
2062 if (access & GNUNET_DISK_MAP_TYPE_WRITE)
2064 *m = GNUNET_new (struct GNUNET_DISK_MapHandle);
2065 (*m)->addr = mmap (NULL, len, prot, MAP_SHARED, h->fd, 0);
2066 GNUNET_assert (NULL != (*m)->addr);
2067 if (MAP_FAILED == (*m)->addr)
2079 * @param h mapping handle
2080 * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
2083 GNUNET_DISK_file_unmap (struct GNUNET_DISK_MapHandle *h)
2090 return GNUNET_SYSERR;
2094 ret = UnmapViewOfFile (h->addr) ? GNUNET_OK : GNUNET_SYSERR;
2095 if (ret != GNUNET_OK)
2096 SetErrnoFromWinError (GetLastError ());
2097 if (!CloseHandle (h->h) && (ret == GNUNET_OK))
2099 ret = GNUNET_SYSERR;
2100 SetErrnoFromWinError (GetLastError ());
2103 ret = munmap (h->addr, h->len) != -1 ? GNUNET_OK : GNUNET_SYSERR;
2111 * Write file changes to disk
2112 * @param h handle to an open file
2113 * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
2116 GNUNET_DISK_file_sync (const struct GNUNET_DISK_FileHandle *h)
2121 return GNUNET_SYSERR;
2127 ret = FlushFileBuffers (h->h) ? GNUNET_OK : GNUNET_SYSERR;
2128 if (ret != GNUNET_OK)
2129 SetErrnoFromWinError (GetLastError ());
2131 #elif defined(FREEBSD) || defined(OPENBSD) || defined(DARWIN)
2132 return fsync (h->fd) == -1 ? GNUNET_SYSERR : GNUNET_OK;
2134 return fdatasync (h->fd) == -1 ? GNUNET_SYSERR : GNUNET_OK;
2141 #define PIPE_BUF 512
2143 /* Copyright Bob Byrnes <byrnes <at> curl.com>
2144 http://permalink.gmane.org/gmane.os.cygwin.patches/2121
2146 /* Create a pipe, and return handles to the read and write ends,
2147 just like CreatePipe, but ensure that the write end permits
2148 FILE_READ_ATTRIBUTES access, on later versions of win32 where
2149 this is supported. This access is needed by NtQueryInformationFile,
2150 which is used to implement select and nonblocking writes.
2151 Note that the return value is either NO_ERROR or GetLastError,
2152 unlike CreatePipe, which returns a bool for success or failure. */
2154 create_selectable_pipe (PHANDLE read_pipe_ptr, PHANDLE write_pipe_ptr,
2155 LPSECURITY_ATTRIBUTES sa_ptr, DWORD psize,
2156 DWORD dwReadMode, DWORD dwWriteMode)
2158 /* Default to error. */
2159 *read_pipe_ptr = *write_pipe_ptr = INVALID_HANDLE_VALUE;
2164 /* Ensure that there is enough pipe buffer space for atomic writes. */
2165 if (psize < PIPE_BUF)
2168 char pipename[MAX_PATH];
2170 /* Retry CreateNamedPipe as long as the pipe name is in use.
2171 * Retrying will probably never be necessary, but we want
2172 * to be as robust as possible. */
2175 static volatile LONG pipe_unique_id;
2177 snprintf (pipename, sizeof pipename, "\\\\.\\pipe\\gnunet-%d-%ld",
2178 getpid (), InterlockedIncrement ((LONG *) & pipe_unique_id));
2179 LOG (GNUNET_ERROR_TYPE_DEBUG, "CreateNamedPipe: name = %s, size = %lu\n",
2181 /* Use CreateNamedPipe instead of CreatePipe, because the latter
2182 * returns a write handle that does not permit FILE_READ_ATTRIBUTES
2183 * access, on versions of win32 earlier than WinXP SP2.
2184 * CreatePipe also stupidly creates a full duplex pipe, which is
2185 * a waste, since only a single direction is actually used.
2186 * It's important to only allow a single instance, to ensure that
2187 * the pipe was not created earlier by some other process, even if
2188 * the pid has been reused. */
2189 read_pipe = CreateNamedPipeA (pipename, PIPE_ACCESS_INBOUND | FILE_FLAG_FIRST_PIPE_INSTANCE | dwReadMode, PIPE_TYPE_BYTE | PIPE_READMODE_BYTE, 1, /* max instances */
2190 psize, /* output buffer size */
2191 psize, /* input buffer size */
2192 NMPWAIT_USE_DEFAULT_WAIT, sa_ptr);
2194 if (read_pipe != INVALID_HANDLE_VALUE)
2196 LOG (GNUNET_ERROR_TYPE_DEBUG, "pipe read handle = %p\n", read_pipe);
2200 DWORD err = GetLastError ();
2204 case ERROR_PIPE_BUSY:
2205 /* The pipe is already open with compatible parameters.
2206 * Pick a new name and retry. */
2207 LOG (GNUNET_ERROR_TYPE_DEBUG, "pipe busy, retrying\n");
2209 case ERROR_ACCESS_DENIED:
2210 /* The pipe is already open with incompatible parameters.
2211 * Pick a new name and retry. */
2212 LOG (GNUNET_ERROR_TYPE_DEBUG, "pipe access denied, retrying\n");
2214 case ERROR_CALL_NOT_IMPLEMENTED:
2215 /* We are on an older Win9x platform without named pipes.
2216 * Return an anonymous pipe as the best approximation. */
2217 LOG (GNUNET_ERROR_TYPE_DEBUG,
2218 "CreateNamedPipe not implemented, resorting to "
2219 "CreatePipe: size = %lu\n", psize);
2220 if (CreatePipe (read_pipe_ptr, write_pipe_ptr, sa_ptr, psize))
2222 LOG (GNUNET_ERROR_TYPE_DEBUG, "pipe read handle = %p, write handle = %p\n",
2227 err = GetLastError ();
2228 LOG (GNUNET_ERROR_TYPE_ERROR, "CreatePipe failed: %d\n", err);
2231 LOG (GNUNET_ERROR_TYPE_ERROR, "CreateNamedPipe failed: %d\n", err);
2236 LOG (GNUNET_ERROR_TYPE_DEBUG, "CreateFile: name = %s\n", pipename);
2238 /* Open the named pipe for writing.
2239 * Be sure to permit FILE_READ_ATTRIBUTES access. */
2240 write_pipe = CreateFileA (pipename, GENERIC_WRITE | FILE_READ_ATTRIBUTES, 0, /* share mode */
2241 sa_ptr, OPEN_EXISTING, dwWriteMode, /* flags and attributes */
2242 0); /* handle to template file */
2244 if (write_pipe == INVALID_HANDLE_VALUE)
2247 DWORD err = GetLastError ();
2249 LOG (GNUNET_ERROR_TYPE_DEBUG, "CreateFile failed: %d\n", err);
2250 CloseHandle (read_pipe);
2253 LOG (GNUNET_ERROR_TYPE_DEBUG, "pipe write handle = %p\n", write_pipe);
2255 *read_pipe_ptr = read_pipe;
2256 *write_pipe_ptr = write_pipe;
2263 * Creates an interprocess channel
2265 * @param blocking_read creates an asynchronous pipe for reading if set to GNUNET_NO
2266 * @param blocking_write creates an asynchronous pipe for writing if set to GNUNET_NO
2267 * @param inherit_read inherit the parent processes stdin (only for windows)
2268 * @param inherit_write inherit the parent processes stdout (only for windows)
2269 * @return handle to the new pipe, NULL on error
2271 struct GNUNET_DISK_PipeHandle *
2272 GNUNET_DISK_pipe (int blocking_read, int blocking_write, int inherit_read, int inherit_write)
2283 LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR, "pipe");
2287 return GNUNET_DISK_pipe_from_fd (blocking_read,
2291 struct GNUNET_DISK_PipeHandle *p;
2296 p = GNUNET_new (struct GNUNET_DISK_PipeHandle);
2297 p->fd[0] = GNUNET_new (struct GNUNET_DISK_FileHandle);
2298 p->fd[1] = GNUNET_new (struct GNUNET_DISK_FileHandle);
2300 /* All pipes are overlapped. If you want them to block - just
2301 * call WriteFile() and ReadFile() with NULL overlapped pointer.
2302 * NOTE: calling with NULL overlapped pointer works only
2303 * for pipes, and doesn't seem to be a documented feature.
2304 * It will NOT work for files, because overlapped files need
2305 * to read offsets from the overlapped structure, regardless.
2306 * Pipes are not seekable, and need no offsets, which is
2307 * probably why it works for them.
2310 create_selectable_pipe (&p->fd[0]->h, &p->fd[1]->h, NULL, 0,
2311 FILE_FLAG_OVERLAPPED,
2312 FILE_FLAG_OVERLAPPED);
2315 SetErrnoFromWinError (GetLastError ());
2317 GNUNET_free (p->fd[0]);
2318 GNUNET_free (p->fd[1]);
2323 if (!DuplicateHandle
2324 (GetCurrentProcess (), p->fd[0]->h, GetCurrentProcess (), &tmp_handle, 0,
2325 inherit_read == GNUNET_YES ? TRUE : FALSE, DUPLICATE_SAME_ACCESS))
2327 SetErrnoFromWinError (GetLastError ());
2329 CloseHandle (p->fd[0]->h);
2330 CloseHandle (p->fd[1]->h);
2331 GNUNET_free (p->fd[0]);
2332 GNUNET_free (p->fd[1]);
2337 CloseHandle (p->fd[0]->h);
2338 p->fd[0]->h = tmp_handle;
2340 if (!DuplicateHandle
2341 (GetCurrentProcess (), p->fd[1]->h, GetCurrentProcess (), &tmp_handle, 0,
2342 inherit_write == GNUNET_YES ? TRUE : FALSE, DUPLICATE_SAME_ACCESS))
2344 SetErrnoFromWinError (GetLastError ());
2346 CloseHandle (p->fd[0]->h);
2347 CloseHandle (p->fd[1]->h);
2348 GNUNET_free (p->fd[0]);
2349 GNUNET_free (p->fd[1]);
2354 CloseHandle (p->fd[1]->h);
2355 p->fd[1]->h = tmp_handle;
2357 p->fd[0]->type = GNUNET_DISK_HANLDE_TYPE_PIPE;
2358 p->fd[1]->type = GNUNET_DISK_HANLDE_TYPE_PIPE;
2360 p->fd[0]->oOverlapRead = GNUNET_malloc (sizeof (OVERLAPPED));
2361 p->fd[0]->oOverlapWrite = GNUNET_malloc (sizeof (OVERLAPPED));
2362 p->fd[1]->oOverlapRead = GNUNET_malloc (sizeof (OVERLAPPED));
2363 p->fd[1]->oOverlapWrite = GNUNET_malloc (sizeof (OVERLAPPED));
2365 p->fd[0]->oOverlapRead->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2366 p->fd[0]->oOverlapWrite->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2368 p->fd[1]->oOverlapRead->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2369 p->fd[1]->oOverlapWrite->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2377 * Creates a pipe object from a couple of file descriptors.
2378 * Useful for wrapping existing pipe FDs.
2380 * @param blocking_read creates an asynchronous pipe for reading if set to GNUNET_NO
2381 * @param blocking_write creates an asynchronous pipe for writing if set to GNUNET_NO
2382 * @param fd an array of two fd values. One of them may be -1 for read-only or write-only pipes
2384 * @return handle to the new pipe, NULL on error
2386 struct GNUNET_DISK_PipeHandle *
2387 GNUNET_DISK_pipe_from_fd (int blocking_read, int blocking_write, int fd[2])
2389 struct GNUNET_DISK_PipeHandle *p;
2391 p = GNUNET_new (struct GNUNET_DISK_PipeHandle);
2396 int eno = 0; /* make gcc happy */
2401 p->fd[0] = GNUNET_new (struct GNUNET_DISK_FileHandle);
2402 p->fd[0]->fd = fd[0];
2405 flags = fcntl (fd[0], F_GETFL);
2406 flags |= O_NONBLOCK;
2407 if (0 > fcntl (fd[0], F_SETFL, flags))
2413 flags = fcntl (fd[0], F_GETFD);
2414 flags |= FD_CLOEXEC;
2415 if (0 > fcntl (fd[0], F_SETFD, flags))
2424 p->fd[1] = GNUNET_new (struct GNUNET_DISK_FileHandle);
2425 p->fd[1]->fd = fd[1];
2426 if (!blocking_write)
2428 flags = fcntl (fd[1], F_GETFL);
2429 flags |= O_NONBLOCK;
2430 if (0 > fcntl (fd[1], F_SETFL, flags))
2436 flags = fcntl (fd[1], F_GETFD);
2437 flags |= FD_CLOEXEC;
2438 if (0 > fcntl (fd[1], F_SETFD, flags))
2447 LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR, "fcntl");
2448 if (p->fd[0]->fd >= 0)
2449 GNUNET_break (0 == close (p->fd[0]->fd));
2450 if (p->fd[1]->fd >= 0)
2451 GNUNET_break (0 == close (p->fd[1]->fd));
2452 GNUNET_free_non_null (p->fd[0]);
2453 GNUNET_free_non_null (p->fd[1]);
2461 p->fd[0] = GNUNET_new (struct GNUNET_DISK_FileHandle);
2462 p->fd[0]->h = (HANDLE) _get_osfhandle (fd[0]);
2463 if (p->fd[0]->h != INVALID_HANDLE_VALUE)
2465 p->fd[0]->type = GNUNET_DISK_HANLDE_TYPE_PIPE;
2466 p->fd[0]->oOverlapRead = GNUNET_malloc (sizeof (OVERLAPPED));
2467 p->fd[0]->oOverlapWrite = GNUNET_malloc (sizeof (OVERLAPPED));
2468 p->fd[0]->oOverlapRead->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2469 p->fd[0]->oOverlapWrite->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2473 GNUNET_free (p->fd[0]);
2479 p->fd[1] = GNUNET_new (struct GNUNET_DISK_FileHandle);
2480 p->fd[1]->h = (HANDLE) _get_osfhandle (fd[1]);
2481 if (p->fd[1]->h != INVALID_HANDLE_VALUE)
2483 p->fd[1]->type = GNUNET_DISK_HANLDE_TYPE_PIPE;
2484 p->fd[1]->oOverlapRead = GNUNET_malloc (sizeof (OVERLAPPED));
2485 p->fd[1]->oOverlapWrite = GNUNET_malloc (sizeof (OVERLAPPED));
2486 p->fd[1]->oOverlapRead->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2487 p->fd[1]->oOverlapWrite->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2491 GNUNET_free (p->fd[1]);
2502 * Closes an interprocess channel
2504 * @param p pipe to close
2505 * @param end which end of the pipe to close
2506 * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
2509 GNUNET_DISK_pipe_close_end (struct GNUNET_DISK_PipeHandle *p,
2510 enum GNUNET_DISK_PipeEnd end)
2512 int ret = GNUNET_OK;
2514 if (end == GNUNET_DISK_PIPE_END_READ)
2518 ret = GNUNET_DISK_file_close (p->fd[0]);
2522 else if (end == GNUNET_DISK_PIPE_END_WRITE)
2526 ret = GNUNET_DISK_file_close (p->fd[1]);
2535 * Detaches one of the ends from the pipe.
2536 * Detached end is a fully-functional FileHandle, it will
2537 * not be affected by anything you do with the pipe afterwards.
2538 * Each end of a pipe can only be detched from it once (i.e.
2539 * it is not duplicated).
2541 * @param p pipe to detach an end from
2542 * @param end which end of the pipe to detach
2543 * @return Detached end on success, NULL on failure
2544 * (or if that end is not present or is closed).
2546 struct GNUNET_DISK_FileHandle *
2547 GNUNET_DISK_pipe_detach_end (struct GNUNET_DISK_PipeHandle *p,
2548 enum GNUNET_DISK_PipeEnd end)
2550 struct GNUNET_DISK_FileHandle *ret = NULL;
2552 if (end == GNUNET_DISK_PIPE_END_READ)
2560 else if (end == GNUNET_DISK_PIPE_END_WRITE)
2574 * Closes an interprocess channel
2576 * @param p pipe to close
2577 * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
2580 GNUNET_DISK_pipe_close (struct GNUNET_DISK_PipeHandle *p)
2582 int ret = GNUNET_OK;
2585 int write_end_close;
2586 int read_end_close_errno;
2587 int write_end_close_errno;
2589 read_end_close = GNUNET_DISK_pipe_close_end (p, GNUNET_DISK_PIPE_END_READ);
2590 read_end_close_errno = errno;
2591 write_end_close = GNUNET_DISK_pipe_close_end (p, GNUNET_DISK_PIPE_END_WRITE);
2592 write_end_close_errno = errno;
2595 if (GNUNET_OK != read_end_close)
2597 errno = read_end_close_errno;
2598 ret = read_end_close;
2600 else if (GNUNET_OK != write_end_close)
2602 errno = write_end_close_errno;
2603 ret = write_end_close;
2611 * Get the handle to a particular pipe end
2614 * @param n end to access
2615 * @return handle for the respective end
2617 const struct GNUNET_DISK_FileHandle *
2618 GNUNET_DISK_pipe_handle (const struct GNUNET_DISK_PipeHandle *p,
2619 enum GNUNET_DISK_PipeEnd n)
2623 case GNUNET_DISK_PIPE_END_READ:
2624 case GNUNET_DISK_PIPE_END_WRITE:
2634 * Retrieve OS file handle
2636 * @param fh GNUnet file descriptor
2637 * @param dst destination buffer
2638 * @param dst_len length of dst
2639 * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
2642 GNUNET_DISK_internal_file_handle_ (const struct GNUNET_DISK_FileHandle *fh,
2643 void *dst, size_t dst_len)
2646 if (dst_len < sizeof (HANDLE))
2647 return GNUNET_SYSERR;
2648 *((HANDLE *) dst) = fh->h;
2650 if (dst_len < sizeof (int))
2651 return GNUNET_SYSERR;
2652 *((int *) dst) = fh->fd;