2 This file is part of GNUnet.
3 (C) 2001, 2002, 2005, 2006, 2009 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 2, 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_common.h"
30 #include "gnunet_directories.h"
31 #include "gnunet_disk_lib.h"
32 #include "gnunet_scheduler_lib.h"
33 #include "gnunet_strings_lib.h"
34 #include "gnunet_crypto_lib.h"
37 #define LOG(kind,...) GNUNET_log_from (kind, "util", __VA_ARGS__)
39 #define LOG_STRERROR(kind,syscall) GNUNET_log_from_strerror (kind, "util", syscall)
41 #define LOG_STRERROR_FILE(kind,syscall,filename) GNUNET_log_from_strerror_file (kind, "util", syscall, filename)
43 #define DEBUG_NPIPE GNUNET_EXTRA_LOGGING
45 #define DEBUG_PIPE GNUNET_EXTRA_LOGGING
48 * Block size for IO for copying files.
50 #define COPY_BLK_SIZE 65536
54 #if defined(LINUX) || defined(CYGWIN)
57 #if defined(SOMEBSD) || defined(DARWIN)
58 #include <sys/param.h>
59 #include <sys/mount.h>
62 #include <sys/types.h>
63 #include <sys/statvfs.h>
68 ULONG PipeSerialNumber;
70 #define _IFMT 0170000 /* type of file */
71 #define _IFLNK 0120000 /* symbolic link */
72 #define S_ISLNK(m) (((m)&_IFMT) == _IFLNK)
74 #error PORT-ME: need to port statfs (how much space is left on the drive?)
80 #if !defined(SOMEBSD) && !defined(DARWIN) && !defined(WINDOWS)
84 #include <sys/statvfs.h>
89 * Handle used to manage a pipe.
91 struct GNUNET_DISK_PipeHandle
94 * File descriptors for the pipe.
96 struct GNUNET_DISK_FileHandle *fd[2];
101 * Closure for the recursion to determine the file size
104 struct GetFileSizeData
107 * Set to the total file size.
112 * GNUNET_YES if symbolic links should be included.
114 int include_sym_links;
119 translate_unix_perms (enum GNUNET_DISK_AccessPermissions perm)
124 if (perm & GNUNET_DISK_PERM_USER_READ)
126 if (perm & GNUNET_DISK_PERM_USER_WRITE)
128 if (perm & GNUNET_DISK_PERM_USER_EXEC)
130 if (perm & GNUNET_DISK_PERM_GROUP_READ)
132 if (perm & GNUNET_DISK_PERM_GROUP_WRITE)
134 if (perm & GNUNET_DISK_PERM_GROUP_EXEC)
136 if (perm & GNUNET_DISK_PERM_OTHER_READ)
138 if (perm & GNUNET_DISK_PERM_OTHER_WRITE)
140 if (perm & GNUNET_DISK_PERM_OTHER_EXEC)
148 * Iterate over all files in the given directory and
149 * accumulate their size.
151 * @param cls closure of type "struct GetFileSizeData"
152 * @param fn current filename we are looking at
153 * @return GNUNET_SYSERR on serious errors, otherwise GNUNET_OK
156 getSizeRec (void *cls, const char *fn)
158 struct GetFileSizeData *gfsd = cls;
167 if (0 != STAT64 (fn, &buf))
169 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "stat64", fn);
170 return GNUNET_SYSERR;
173 if (0 != STAT (fn, &buf))
175 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "stat", fn);
176 return GNUNET_SYSERR;
179 if ((!S_ISLNK (buf.st_mode)) || (gfsd->include_sym_links == GNUNET_YES))
180 gfsd->total += buf.st_size;
181 if ((S_ISDIR (buf.st_mode)) && (0 == ACCESS (fn, X_OK)) &&
182 ((!S_ISLNK (buf.st_mode)) || (gfsd->include_sym_links == GNUNET_YES)))
184 if (GNUNET_SYSERR == GNUNET_DISK_directory_scan (fn, &getSizeRec, gfsd))
185 return GNUNET_SYSERR;
192 * Checks whether a handle is invalid
194 * @param h handle to check
195 * @return GNUNET_YES if invalid, GNUNET_NO if valid
198 GNUNET_DISK_handle_invalid (const struct GNUNET_DISK_FileHandle *h)
201 return ((!h) || (h->h == INVALID_HANDLE_VALUE)) ? GNUNET_YES : GNUNET_NO;
203 return ((!h) || (h->fd == -1)) ? GNUNET_YES : GNUNET_NO;
208 * Get the size of an open file.
210 * @param fh open file handle
211 * @param size where to write size of the file
212 * @return GNUNET_OK on success, GNUNET_SYSERR on error
215 GNUNET_DISK_file_handle_size (struct GNUNET_DISK_FileHandle *fh,
221 b = GetFileSizeEx (fh->h, &li);
224 SetErrnoFromWinError (GetLastError ());
225 return GNUNET_SYSERR;
227 *size = (OFF_T) li.QuadPart;
231 if (0 != FSTAT (fh->fd, &sbuf))
232 return GNUNET_SYSERR;
233 *size = sbuf.st_size;
240 * Move the read/write pointer in a file
242 * @param h handle of an open file
243 * @param offset position to move to
244 * @param whence specification to which position the offset parameter relates to
245 * @return the new position on success, GNUNET_SYSERR otherwise
248 GNUNET_DISK_file_seek (const struct GNUNET_DISK_FileHandle * h, OFF_T offset,
249 enum GNUNET_DISK_Seek whence)
254 return GNUNET_SYSERR;
258 LARGE_INTEGER li, new_pos;
261 static DWORD t[] = {[GNUNET_DISK_SEEK_SET] = FILE_BEGIN,
262 [GNUNET_DISK_SEEK_CUR] = FILE_CURRENT,[GNUNET_DISK_SEEK_END] = FILE_END
264 li.QuadPart = offset;
266 b = SetFilePointerEx (h->h, li, &new_pos, t[whence]);
269 SetErrnoFromWinError (GetLastError ());
270 return GNUNET_SYSERR;
272 return (OFF_T) new_pos.QuadPart;
274 static int t[] = {[GNUNET_DISK_SEEK_SET] = SEEK_SET,
275 [GNUNET_DISK_SEEK_CUR] = SEEK_CUR,[GNUNET_DISK_SEEK_END] = SEEK_END
278 return lseek (h->fd, offset, t[whence]);
284 * Get the size of the file (or directory) of the given file (in
287 * @param filename name of the file or directory
288 * @param size set to the size of the file (or,
289 * in the case of directories, the sum
290 * of all sizes of files in the directory)
291 * @param includeSymLinks should symbolic links be
293 * @return GNUNET_SYSERR on error, GNUNET_OK on success
296 GNUNET_DISK_file_size (const char *filename, uint64_t * size,
299 struct GetFileSizeData gfsd;
302 GNUNET_assert (size != NULL);
304 gfsd.include_sym_links = includeSymLinks;
305 ret = getSizeRec (&gfsd, filename);
312 * Obtain some unique identifiers for the given file
313 * that can be used to identify it in the local system.
314 * This function is used between GNUnet processes to
315 * quickly check if two files with the same absolute path
316 * are actually identical. The two processes represent
317 * the same peer but may communicate over the network
318 * (and the file may be on an NFS volume). This function
319 * may not be supported on all operating systems.
321 * @param filename name of the file
322 * @param dev set to the device ID
323 * @param ino set to the inode ID
324 * @return GNUNET_OK on success
327 GNUNET_DISK_file_get_identifiers (const char *filename, uint64_t * dev,
334 if ((0 == stat (filename, &sbuf)) && (0 == statvfs (filename, &fbuf)))
336 *dev = (uint64_t) fbuf.f_fsid;
337 *ino = (uint64_t) sbuf.st_ino;
344 if ((0 == stat (filename, &sbuf)) && (0 == statfs (filename, &fbuf)))
346 *dev = ((uint64_t) fbuf.f_fsid.val[0]) << 32 ||
347 ((uint64_t) fbuf.f_fsid.val[1]);
348 *ino = (uint64_t) sbuf.st_ino;
352 // FIXME NILS: test this
353 struct GNUNET_DISK_FileHandle *fh;
354 BY_HANDLE_FILE_INFORMATION info;
357 fh = GNUNET_DISK_file_open (filename, GNUNET_DISK_OPEN_READ, 0);
359 return GNUNET_SYSERR;
360 succ = GetFileInformationByHandle (fh->h, &info);
361 GNUNET_DISK_file_close (fh);
364 *dev = info.dwVolumeSerialNumber;
365 *ino = ((((uint64_t) info.nFileIndexHigh) << (sizeof (DWORD) * 8)) | info.nFileIndexLow);
369 return GNUNET_SYSERR;
372 return GNUNET_SYSERR;
377 * Create an (empty) temporary file on disk. If the given name is not
378 * an absolute path, the current 'TMPDIR' will be prepended. In any case,
379 * 6 random characters will be appended to the name to create a unique
382 * @param t component to use for the name;
383 * does NOT contain "XXXXXX" or "/tmp/".
384 * @return NULL on error, otherwise name of fresh
385 * file on disk in directory for temporary files
388 GNUNET_DISK_mktemp (const char *t)
395 if ((t[0] != '/') && (t[0] != '\\')
397 && !(isalpha ((int) t[0]) && (t[0] != '\0') && (t[1] == ':'))
401 /* FIXME: This uses system codepage on W32, not UTF-8 */
402 tmpdir = getenv ("TMPDIR");
403 tmpdir = tmpdir ? tmpdir : "/tmp";
404 GNUNET_asprintf (&tmpl, "%s/%s%s", tmpdir, t, "XXXXXX");
408 GNUNET_asprintf (&tmpl, "%s%s", t, "XXXXXX");
411 fn = (char *) GNUNET_malloc (MAX_PATH + 1);
412 if (ERROR_SUCCESS != plibc_conv_to_win_path (tmpl, fn))
422 /* FIXME: why is this not MKSTEMP()? This function is implemented in plibc.
423 * It will assume that fn is UTF-8-encoded, if compiled with UTF-8 support.
428 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_ERROR, "mkstemp", fn);
433 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "close", fn);
439 * Get the number of blocks that are left on the partition that
440 * contains the given file (for normal users).
442 * @param part a file on the partition to check
443 * @return -1 on errors, otherwise the number of free blocks
446 GNUNET_DISK_get_blocks_available (const char *part)
451 if (0 != statvfs (part, &buf))
453 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "statfs", part);
461 wchar_t wpath[MAX_PATH + 1];
464 path = GNUNET_STRINGS_filename_expand (part);
467 /* "part" was in UTF-8, and so is "path" */
468 if (ERROR_SUCCESS != plibc_conv_to_win_pathwconv(path, wpath))
474 wcsncpy (szDrive, wpath, 3);
477 if (!GetDiskFreeSpaceW (szDrive, &dwDummy, &dwDummy, &dwBlocks, &dwDummy))
479 LOG (GNUNET_ERROR_TYPE_WARNING, _("`%s' failed for drive `%S': %u\n"),
480 "GetDiskFreeSpace", szDrive, GetLastError ());
488 if (0 != statfs (part, &s))
490 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "statfs", part);
499 * Test if "fil" is a directory.
500 * Will not print an error message if the directory
501 * does not exist. Will log errors if GNUNET_SYSERR is
502 * returned (i.e., a file exists with the same name).
504 * @param fil filename to test
505 * @return GNUNET_YES if yes, GNUNET_NO if not, GNUNET_SYSERR if it
509 GNUNET_DISK_directory_test (const char *fil)
511 struct stat filestat;
514 ret = STAT (fil, &filestat);
519 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "stat", fil);
520 return GNUNET_SYSERR;
524 if (!S_ISDIR (filestat.st_mode))
526 if (ACCESS (fil, R_OK | X_OK) < 0)
528 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "access", fil);
529 return GNUNET_SYSERR;
535 * Check that fil corresponds to a filename
536 * (of a file that exists and that is not a directory).
538 * @param fil filename to check
539 * @return GNUNET_YES if yes, GNUNET_NO if not a file, GNUNET_SYSERR if something
540 * else (will print an error message in that case, too).
543 GNUNET_DISK_file_test (const char *fil)
545 struct stat filestat;
549 rdir = GNUNET_STRINGS_filename_expand (fil);
551 return GNUNET_SYSERR;
553 ret = STAT (rdir, &filestat);
558 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "stat", rdir);
560 return GNUNET_SYSERR;
565 if (!S_ISREG (filestat.st_mode))
570 if (ACCESS (rdir, R_OK) < 0)
572 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "access", rdir);
574 return GNUNET_SYSERR;
582 * Implementation of "mkdir -p"
583 * @param dir the directory to create
584 * @returns GNUNET_OK on success, GNUNET_SYSERR on failure
587 GNUNET_DISK_directory_create (const char *dir)
594 rdir = GNUNET_STRINGS_filename_expand (dir);
596 return GNUNET_SYSERR;
600 pos = 1; /* skip heading '/' */
602 /* Local or Network path? */
603 if (strncmp (rdir, "\\\\", 2) == 0)
608 if (rdir[pos] == '\\')
618 pos = 3; /* strlen("C:\\") */
623 if ((rdir[pos] == DIR_SEPARATOR) || (pos == len))
626 ret = GNUNET_DISK_directory_test (rdir);
627 if (ret == GNUNET_SYSERR)
630 return GNUNET_SYSERR;
632 if (ret == GNUNET_NO)
635 ret = mkdir (rdir, S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH); /* 755 */
637 wchar_t wrdir[MAX_PATH + 1];
638 if (ERROR_SUCCESS == plibc_conv_to_win_pathwconv(rdir, wrdir))
639 ret = !CreateDirectoryW (wrdir, NULL);
643 if ((ret != 0) && (errno != EEXIST))
645 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_ERROR, "mkdir", rdir);
647 return GNUNET_SYSERR;
650 rdir[pos] = DIR_SEPARATOR;
660 * Create the directory structure for storing
663 * @param filename name of a file in the directory
664 * @returns GNUNET_OK on success,
665 * GNUNET_SYSERR on failure,
666 * GNUNET_NO if the directory
667 * exists but is not writeable for us
670 GNUNET_DISK_directory_create_for_file (const char *filename)
676 rdir = GNUNET_STRINGS_filename_expand (filename);
678 return GNUNET_SYSERR;
680 while ((len > 0) && (rdir[len] != DIR_SEPARATOR))
683 ret = GNUNET_DISK_directory_create (rdir);
684 if ((ret == GNUNET_OK) && (0 != ACCESS (rdir, W_OK)))
692 * Read the contents of a binary file into a buffer.
693 * @param h handle to an open file
694 * @param result the buffer to write the result to
695 * @param len the maximum number of bytes to read
696 * @return the number of bytes read on success, GNUNET_SYSERR on failure
699 GNUNET_DISK_file_read (const struct GNUNET_DISK_FileHandle * h, void *result,
705 return GNUNET_SYSERR;
711 if (h->type != GNUNET_PIPE)
713 if (!ReadFile (h->h, result, len, &bytesRead, NULL))
715 SetErrnoFromWinError (GetLastError ());
716 return GNUNET_SYSERR;
722 LOG (GNUNET_ERROR_TYPE_DEBUG, "It is a pipe trying to read\n");
724 if (!ReadFile (h->h, result, len, &bytesRead, h->oOverlapRead))
726 if (GetLastError () != ERROR_IO_PENDING)
729 LOG (GNUNET_ERROR_TYPE_DEBUG, "Error reading from pipe: %u\n", GetLastError ());
731 SetErrnoFromWinError (GetLastError ());
732 return GNUNET_SYSERR;
735 LOG (GNUNET_ERROR_TYPE_DEBUG, "Will get overlapped result\n");
737 GetOverlappedResult (h->h, h->oOverlapRead, &bytesRead, TRUE);
740 LOG (GNUNET_ERROR_TYPE_DEBUG, "Read %u bytes\n", bytesRead);
745 return read (h->fd, result, len);
750 * Read the contents of a binary file into a buffer.
751 * Guarantees not to block (returns GNUNET_SYSERR and sets errno to EAGAIN
752 * when no data can be read).
754 * @param h handle to an open file
755 * @param result the buffer to write the result to
756 * @param len the maximum number of bytes to read
757 * @return the number of bytes read on success, GNUNET_SYSERR on failure
760 GNUNET_DISK_file_read_non_blocking (const struct GNUNET_DISK_FileHandle * h,
761 void *result, size_t len)
766 return GNUNET_SYSERR;
772 if (h->type != GNUNET_PIPE)
774 if (!ReadFile (h->h, result, len, &bytesRead, NULL))
776 SetErrnoFromWinError (GetLastError ());
777 return GNUNET_SYSERR;
783 LOG (GNUNET_ERROR_TYPE_DEBUG, "It is a pipe, trying to read\n");
785 if (!ReadFile (h->h, result, len, &bytesRead, h->oOverlapRead))
787 if (GetLastError () != ERROR_IO_PENDING)
790 LOG (GNUNET_ERROR_TYPE_DEBUG, "Error reading from pipe: %u\n", GetLastError ());
792 SetErrnoFromWinError (GetLastError ());
793 return GNUNET_SYSERR;
798 LOG (GNUNET_ERROR_TYPE_DEBUG,
799 "ReadFile() queued a read, cancelling\n");
803 return GNUNET_SYSERR;
807 LOG (GNUNET_ERROR_TYPE_DEBUG, "Read %u bytes\n", bytesRead);
812 /* FIXME: set to non-blocking (fcntl?), read, then set back? */
813 return read (h->fd, result, len);
819 * Read the contents of a binary file into a buffer.
821 * @param fn file name
822 * @param result the buffer to write the result to
823 * @param len the maximum number of bytes to read
824 * @return number of bytes read, GNUNET_SYSERR on failure
827 GNUNET_DISK_fn_read (const char *fn, void *result, size_t len)
829 struct GNUNET_DISK_FileHandle *fh;
832 fh = GNUNET_DISK_file_open (fn, GNUNET_DISK_OPEN_READ, GNUNET_DISK_PERM_NONE);
834 return GNUNET_SYSERR;
835 ret = GNUNET_DISK_file_read (fh, result, len);
836 GNUNET_assert (GNUNET_OK == GNUNET_DISK_file_close (fh));
843 * Write a buffer to a file.
844 * @param h handle to open file
845 * @param buffer the data to write
846 * @param n number of bytes to write
847 * @return number of bytes written on success, GNUNET_SYSERR on error
850 GNUNET_DISK_file_write (const struct GNUNET_DISK_FileHandle * h,
851 const void *buffer, size_t n)
856 return GNUNET_SYSERR;
862 if (h->type != GNUNET_PIPE)
864 if (!WriteFile (h->h, buffer, n, &bytesWritten, NULL))
866 SetErrnoFromWinError (GetLastError ());
867 return GNUNET_SYSERR;
873 LOG (GNUNET_ERROR_TYPE_DEBUG, "It is a pipe trying to write %u bytes\n", n);
875 if (!WriteFile (h->h, buffer, n, &bytesWritten, h->oOverlapWrite))
877 if (GetLastError () != ERROR_IO_PENDING)
879 SetErrnoFromWinError (GetLastError ());
881 LOG (GNUNET_ERROR_TYPE_DEBUG, "Error writing to pipe: %u\n",
884 return GNUNET_SYSERR;
887 LOG (GNUNET_ERROR_TYPE_DEBUG, "Will get overlapped result\n");
889 if (!GetOverlappedResult (h->h, h->oOverlapWrite, &bytesWritten, TRUE))
891 SetErrnoFromWinError (GetLastError ());
893 LOG (GNUNET_ERROR_TYPE_DEBUG,
894 "Error getting overlapped result while writing to pipe: %u\n",
897 return GNUNET_SYSERR;
903 if (!GetOverlappedResult (h->h, h->oOverlapWrite, &ovr, TRUE))
906 LOG (GNUNET_ERROR_TYPE_DEBUG,
907 "Error getting control overlapped result while writing to pipe: %u\n",
914 LOG (GNUNET_ERROR_TYPE_DEBUG,
915 "Wrote %u bytes (ovr says %u), picking the greatest\n",
920 if (bytesWritten == 0)
925 LOG (GNUNET_ERROR_TYPE_DEBUG, "Wrote %u bytes, returning -1 with EAGAIN\n", bytesWritten);
928 return GNUNET_SYSERR;
932 LOG (GNUNET_ERROR_TYPE_DEBUG, "Wrote %u bytes\n", bytesWritten);
937 return write (h->fd, buffer, n);
942 * Write a buffer to a file, blocking, if necessary.
943 * @param h handle to open file
944 * @param buffer the data to write
945 * @param n number of bytes to write
946 * @return number of bytes written on success, GNUNET_SYSERR on error
949 GNUNET_DISK_file_write_blocking (const struct GNUNET_DISK_FileHandle * h,
950 const void *buffer, size_t n)
955 return GNUNET_SYSERR;
960 /* We do a non-overlapped write, which is as blocking as it gets */
962 LOG (GNUNET_ERROR_TYPE_DEBUG, "Writing %u bytes\n", n);
964 if (!WriteFile (h->h, buffer, n, &bytesWritten, NULL))
966 SetErrnoFromWinError (GetLastError ());
968 LOG (GNUNET_ERROR_TYPE_DEBUG, "Error writing to pipe: %u\n",
971 return GNUNET_SYSERR;
973 if (bytesWritten == 0 && n > 0)
976 LOG (GNUNET_ERROR_TYPE_DEBUG, "Waiting for pipe to clean\n");
978 WaitForSingleObject (h->h, INFINITE);
979 if (!WriteFile (h->h, buffer, n, &bytesWritten, NULL))
981 SetErrnoFromWinError (GetLastError ());
983 LOG (GNUNET_ERROR_TYPE_DEBUG, "Error writing to pipe: %u\n",
986 return GNUNET_SYSERR;
990 LOG (GNUNET_ERROR_TYPE_DEBUG, "Wrote %u bytes\n", bytesWritten);
994 /* FIXME: switch to blocking mode (fcntl?), write, then switch back? */
995 return write (h->fd, buffer, n);
1000 * Write a buffer to a file. If the file is longer than the
1001 * number of bytes that will be written, it will be truncated.
1003 * @param fn file name
1004 * @param buffer the data to write
1005 * @param n number of bytes to write
1006 * @param mode file permissions
1007 * @return number of bytes written on success, GNUNET_SYSERR on error
1010 GNUNET_DISK_fn_write (const char *fn, const void *buffer, size_t n,
1011 enum GNUNET_DISK_AccessPermissions mode)
1013 struct GNUNET_DISK_FileHandle *fh;
1016 fh = GNUNET_DISK_file_open (fn,
1017 GNUNET_DISK_OPEN_WRITE | GNUNET_DISK_OPEN_TRUNCATE
1018 | GNUNET_DISK_OPEN_CREATE, mode);
1020 return GNUNET_SYSERR;
1021 ret = GNUNET_DISK_file_write (fh, buffer, n);
1022 GNUNET_assert (GNUNET_OK == GNUNET_DISK_file_close (fh));
1028 * Scan a directory for files.
1030 * @param dirName the name of the directory
1031 * @param callback the method to call for each file,
1032 * can be NULL, in that case, we only count
1033 * @param callback_cls closure for callback
1034 * @return the number of files found, GNUNET_SYSERR on error or
1035 * ieration aborted by callback returning GNUNET_SYSERR
1038 GNUNET_DISK_directory_scan (const char *dirName,
1039 GNUNET_FileNameCallback callback,
1043 struct dirent *finfo;
1048 unsigned int name_len;
1049 unsigned int n_size;
1051 GNUNET_assert (dirName != NULL);
1052 dname = GNUNET_STRINGS_filename_expand (dirName);
1054 return GNUNET_SYSERR;
1055 while ((strlen (dname) > 0) && (dname[strlen (dname) - 1] == DIR_SEPARATOR))
1056 dname[strlen (dname) - 1] = '\0';
1057 if (0 != STAT (dname, &istat))
1059 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "stat", dname);
1060 GNUNET_free (dname);
1061 return GNUNET_SYSERR;
1063 if (!S_ISDIR (istat.st_mode))
1065 LOG (GNUNET_ERROR_TYPE_WARNING, _("Expected `%s' to be a directory!\n"),
1067 GNUNET_free (dname);
1068 return GNUNET_SYSERR;
1071 dinfo = OPENDIR (dname);
1072 if ((errno == EACCES) || (dinfo == NULL))
1074 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "opendir", dname);
1077 GNUNET_free (dname);
1078 return GNUNET_SYSERR;
1081 n_size = strlen (dname) + name_len + 2;
1082 name = GNUNET_malloc (n_size);
1083 while ((finfo = READDIR (dinfo)) != NULL)
1085 if ((0 == strcmp (finfo->d_name, ".")) ||
1086 (0 == strcmp (finfo->d_name, "..")))
1088 if (callback != NULL)
1090 if (name_len < strlen (finfo->d_name))
1093 name_len = strlen (finfo->d_name);
1094 n_size = strlen (dname) + name_len + 2;
1095 name = GNUNET_malloc (n_size);
1097 /* dname can end in "/" only if dname == "/";
1098 * if dname does not end in "/", we need to add
1099 * a "/" (otherwise, we must not!) */
1100 GNUNET_snprintf (name, n_size, "%s%s%s", dname,
1101 (strcmp (dname, DIR_SEPARATOR_STR) ==
1102 0) ? "" : DIR_SEPARATOR_STR, finfo->d_name);
1103 if (GNUNET_OK != callback (callback_cls, name))
1107 GNUNET_free (dname);
1108 return GNUNET_SYSERR;
1115 GNUNET_free (dname);
1121 * Opaque handle used for iterating over a directory.
1123 struct GNUNET_DISK_DirectoryIterator
1127 * Function to call on directory entries.
1129 GNUNET_DISK_DirectoryIteratorCallback callback;
1132 * Closure for callback.
1137 * Reference to directory.
1147 * Next filename to process.
1154 enum GNUNET_SCHEDULER_Priority priority;
1160 * Task used by the directory iterator.
1163 directory_iterator_task (void *cls,
1164 const struct GNUNET_SCHEDULER_TaskContext *tc)
1166 struct GNUNET_DISK_DirectoryIterator *iter = cls;
1169 name = iter->next_name;
1170 GNUNET_assert (name != NULL);
1171 iter->next_name = NULL;
1172 iter->callback (iter->callback_cls, iter, name, iter->dirname);
1178 * This function must be called during the DiskIteratorCallback
1179 * (exactly once) to schedule the task to process the next
1180 * filename in the directory (if there is one).
1182 * @param iter opaque handle for the iterator
1183 * @param can set to GNUNET_YES to terminate the iteration early
1184 * @return GNUNET_YES if iteration will continue,
1185 * GNUNET_NO if this was the last entry (and iteration is complete),
1186 * GNUNET_SYSERR if abort was YES
1189 GNUNET_DISK_directory_iterator_next (struct GNUNET_DISK_DirectoryIterator *iter,
1192 struct dirent *finfo;
1194 GNUNET_assert (iter->next_name == NULL);
1195 if (can == GNUNET_YES)
1197 CLOSEDIR (iter->directory);
1198 GNUNET_free (iter->dirname);
1200 return GNUNET_SYSERR;
1202 while (NULL != (finfo = READDIR (iter->directory)))
1204 if ((0 == strcmp (finfo->d_name, ".")) ||
1205 (0 == strcmp (finfo->d_name, "..")))
1207 GNUNET_asprintf (&iter->next_name, "%s%s%s", iter->dirname,
1208 DIR_SEPARATOR_STR, finfo->d_name);
1213 GNUNET_DISK_directory_iterator_next (iter, GNUNET_YES);
1216 GNUNET_SCHEDULER_add_with_priority (iter->priority, &directory_iterator_task,
1223 * Scan a directory for files using the scheduler to run a task for
1224 * each entry. The name of the directory must be expanded first (!).
1225 * If a scheduler does not need to be used, GNUNET_DISK_directory_scan
1226 * may provide a simpler API.
1228 * @param prio priority to use
1229 * @param dirName the name of the directory
1230 * @param callback the method to call for each file
1231 * @param callback_cls closure for callback
1232 * @return GNUNET_YES if directory is not empty and @callback
1233 * will be called later, GNUNET_NO otherwise, GNUNET_SYSERR on error.
1236 GNUNET_DISK_directory_iterator_start (enum GNUNET_SCHEDULER_Priority prio,
1237 const char *dirName,
1238 GNUNET_DISK_DirectoryIteratorCallback
1239 callback, void *callback_cls)
1241 struct GNUNET_DISK_DirectoryIterator *di;
1243 di = GNUNET_malloc (sizeof (struct GNUNET_DISK_DirectoryIterator));
1244 di->callback = callback;
1245 di->callback_cls = callback_cls;
1246 di->directory = OPENDIR (dirName);
1247 if (di->directory == NULL)
1250 callback (callback_cls, NULL, NULL, NULL);
1251 return GNUNET_SYSERR;
1253 di->dirname = GNUNET_strdup (dirName);
1254 di->priority = prio;
1255 return GNUNET_DISK_directory_iterator_next (di, GNUNET_NO);
1260 * Function that removes the given directory by calling
1261 * "GNUNET_DISK_directory_remove".
1263 * @param unused not used
1264 * @param fn directory to remove
1268 remove_helper (void *unused, const char *fn)
1270 (void) GNUNET_DISK_directory_remove (fn);
1276 * Remove all files in a directory (rm -rf). Call with
1280 * @param fileName the file to remove
1281 * @return GNUNET_OK on success, GNUNET_SYSERR on error
1284 GNUNET_DISK_directory_remove (const char *fileName)
1288 if (0 != LSTAT (fileName, &istat))
1289 return GNUNET_NO; /* file may not exist... */
1290 CHMOD (fileName, S_IWUSR | S_IRUSR | S_IXUSR);
1291 if (UNLINK (fileName) == 0)
1293 if ((errno != EISDIR) &&
1294 /* EISDIR is not sufficient in all cases, e.g.
1295 * sticky /tmp directory may result in EPERM on BSD.
1296 * So we also explicitly check "isDirectory" */
1297 (GNUNET_YES != GNUNET_DISK_directory_test (fileName)))
1299 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "rmdir", fileName);
1300 return GNUNET_SYSERR;
1302 if (GNUNET_SYSERR ==
1303 GNUNET_DISK_directory_scan (fileName, &remove_helper, NULL))
1304 return GNUNET_SYSERR;
1305 if (0 != RMDIR (fileName))
1307 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "rmdir", fileName);
1308 return GNUNET_SYSERR;
1317 * @param src file to copy
1318 * @param dst destination file name
1319 * @return GNUNET_OK on success, GNUNET_SYSERR on error
1322 GNUNET_DISK_file_copy (const char *src, const char *dst)
1328 struct GNUNET_DISK_FileHandle *in;
1329 struct GNUNET_DISK_FileHandle *out;
1331 if (GNUNET_OK != GNUNET_DISK_file_size (src, &size, GNUNET_YES))
1332 return GNUNET_SYSERR;
1334 in = GNUNET_DISK_file_open (src, GNUNET_DISK_OPEN_READ,
1335 GNUNET_DISK_PERM_NONE);
1337 return GNUNET_SYSERR;
1339 GNUNET_DISK_file_open (dst,
1340 GNUNET_DISK_OPEN_WRITE | GNUNET_DISK_OPEN_CREATE |
1341 GNUNET_DISK_OPEN_FAILIFEXISTS,
1342 GNUNET_DISK_PERM_USER_READ |
1343 GNUNET_DISK_PERM_USER_WRITE |
1344 GNUNET_DISK_PERM_GROUP_READ |
1345 GNUNET_DISK_PERM_GROUP_WRITE);
1348 GNUNET_DISK_file_close (in);
1349 return GNUNET_SYSERR;
1351 buf = GNUNET_malloc (COPY_BLK_SIZE);
1354 len = COPY_BLK_SIZE;
1355 if (len > size - pos)
1357 if (len != GNUNET_DISK_file_read (in, buf, len))
1359 if (len != GNUNET_DISK_file_write (out, buf, len))
1364 GNUNET_DISK_file_close (in);
1365 GNUNET_DISK_file_close (out);
1369 GNUNET_DISK_file_close (in);
1370 GNUNET_DISK_file_close (out);
1371 return GNUNET_SYSERR;
1376 * @brief Removes special characters as ':' from a filename.
1377 * @param fn the filename to canonicalize
1380 GNUNET_DISK_filename_canonicalize (char *fn)
1390 if (c == '/' || c == '\\' || c == ':' || c == '*' || c == '?' || c == '"' ||
1391 c == '<' || c == '>' || c == '|')
1403 * @brief Change owner of a file
1405 * @param filename name of file to change the owner of
1406 * @param user name of the new owner
1407 * @return GNUNET_OK on success, GNUNET_SYSERR on failure
1410 GNUNET_DISK_file_change_owner (const char *filename, const char *user)
1415 pws = getpwnam (user);
1418 LOG (GNUNET_ERROR_TYPE_ERROR,
1419 _("Cannot obtain information about user `%s': %s\n"), user,
1421 return GNUNET_SYSERR;
1423 if (0 != chown (filename, pws->pw_uid, pws->pw_gid))
1424 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "chown", filename);
1431 * Lock a part of a file
1432 * @param fh file handle
1433 * @param lockStart absolute position from where to lock
1434 * @param lockEnd absolute position until where to lock
1435 * @param excl GNUNET_YES for an exclusive lock
1436 * @return GNUNET_OK on success, GNUNET_SYSERR on error
1439 GNUNET_DISK_file_lock (struct GNUNET_DISK_FileHandle *fh, OFF_T lockStart,
1440 OFF_T lockEnd, int excl)
1445 return GNUNET_SYSERR;
1451 memset (&fl, 0, sizeof (struct flock));
1452 fl.l_type = excl ? F_WRLCK : F_RDLCK;
1453 fl.l_whence = SEEK_SET;
1454 fl.l_start = lockStart;
1457 return fcntl (fh->fd, F_SETLK, &fl) != 0 ? GNUNET_SYSERR : GNUNET_OK;
1460 OFF_T diff = lockEnd - lockStart;
1461 DWORD diff_low, diff_high;
1462 diff_low = (DWORD) (diff & 0xFFFFFFFF);
1463 diff_high = (DWORD) ((diff >> (sizeof (DWORD) * 8)) & 0xFFFFFFFF);
1465 memset (&o, 0, sizeof (OVERLAPPED));
1466 o.Offset = (DWORD) (lockStart & 0xFFFFFFFF);;
1467 o.OffsetHigh = (DWORD) (((lockStart & ~0xFFFFFFFF) >> (sizeof (DWORD) * 8)) & 0xFFFFFFFF);
1470 (fh->h, (excl ? LOCKFILE_EXCLUSIVE_LOCK : 0) | LOCKFILE_FAIL_IMMEDIATELY,
1471 0, diff_low, diff_high, &o))
1473 SetErrnoFromWinError (GetLastError ());
1474 return GNUNET_SYSERR;
1483 * Unlock a part of a file
1484 * @param fh file handle
1485 * @param unlockStart absolute position from where to unlock
1486 * @param unlockEnd absolute position until where to unlock
1487 * @return GNUNET_OK on success, GNUNET_SYSERR on error
1490 GNUNET_DISK_file_unlock (struct GNUNET_DISK_FileHandle *fh, OFF_T unlockStart,
1496 return GNUNET_SYSERR;
1502 memset (&fl, 0, sizeof (struct flock));
1503 fl.l_type = F_UNLCK;
1504 fl.l_whence = SEEK_SET;
1505 fl.l_start = unlockStart;
1506 fl.l_len = unlockEnd;
1508 return fcntl (fh->fd, F_SETLK, &fl) != 0 ? GNUNET_SYSERR : GNUNET_OK;
1511 OFF_T diff = unlockEnd - unlockStart;
1512 DWORD diff_low, diff_high;
1513 diff_low = (DWORD) (diff & 0xFFFFFFFF);
1514 diff_high = (DWORD) ((diff >> (sizeof (DWORD) * 8)) & 0xFFFFFFFF);
1516 memset (&o, 0, sizeof (OVERLAPPED));
1517 o.Offset = (DWORD) (unlockStart & 0xFFFFFFFF);;
1518 o.OffsetHigh = (DWORD) (((unlockStart & ~0xFFFFFFFF) >> (sizeof (DWORD) * 8)) & 0xFFFFFFFF);
1520 if (!UnlockFileEx (fh->h, 0, diff_low, diff_high, &o))
1522 SetErrnoFromWinError (GetLastError ());
1523 return GNUNET_SYSERR;
1532 * Open a file. Note that the access permissions will only be
1533 * used if a new file is created and if the underlying operating
1534 * system supports the given permissions.
1536 * @param fn file name to be opened
1537 * @param flags opening flags, a combination of GNUNET_DISK_OPEN_xxx bit flags
1538 * @param perm permissions for the newly created file, use
1539 * GNUNET_DISK_PERM_USER_NONE if a file could not be created by this
1540 * call (because of flags)
1541 * @return IO handle on success, NULL on error
1543 struct GNUNET_DISK_FileHandle *
1544 GNUNET_DISK_file_open (const char *fn, enum GNUNET_DISK_OpenFlags flags,
1545 enum GNUNET_DISK_AccessPermissions perm)
1548 struct GNUNET_DISK_FileHandle *ret;
1554 wchar_t wexpfn[MAX_PATH + 1];
1561 expfn = GNUNET_STRINGS_filename_expand (fn);
1566 if (GNUNET_DISK_OPEN_READWRITE == (flags & GNUNET_DISK_OPEN_READWRITE))
1567 oflags = O_RDWR; /* note: O_RDWR is NOT always O_RDONLY | O_WRONLY */
1568 else if (flags & GNUNET_DISK_OPEN_READ)
1570 else if (flags & GNUNET_DISK_OPEN_WRITE)
1575 GNUNET_free (expfn);
1578 if (flags & GNUNET_DISK_OPEN_FAILIFEXISTS)
1579 oflags |= (O_CREAT | O_EXCL);
1580 if (flags & GNUNET_DISK_OPEN_TRUNCATE)
1582 if (flags & GNUNET_DISK_OPEN_APPEND)
1584 if (flags & GNUNET_DISK_OPEN_CREATE)
1586 (void) GNUNET_DISK_directory_create_for_file (expfn);
1588 mode = translate_unix_perms (perm);
1591 fd = open (expfn, oflags | O_LARGEFILE, mode);
1594 if (0 == (flags & GNUNET_DISK_OPEN_FAILIFEXISTS))
1595 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "open", expfn);
1597 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_DEBUG, "open", expfn);
1598 GNUNET_free (expfn);
1605 if (GNUNET_DISK_OPEN_READWRITE == (flags & GNUNET_DISK_OPEN_READWRITE))
1606 access = FILE_READ_DATA | FILE_WRITE_DATA;
1607 else if (flags & GNUNET_DISK_OPEN_READ)
1608 access = FILE_READ_DATA;
1609 else if (flags & GNUNET_DISK_OPEN_WRITE)
1610 access = FILE_WRITE_DATA;
1612 if (flags & GNUNET_DISK_OPEN_FAILIFEXISTS)
1616 else if (flags & GNUNET_DISK_OPEN_CREATE)
1618 (void) GNUNET_DISK_directory_create_for_file (expfn);
1619 if (flags & GNUNET_DISK_OPEN_TRUNCATE)
1620 disp = CREATE_ALWAYS;
1624 else if (flags & GNUNET_DISK_OPEN_TRUNCATE)
1626 disp = TRUNCATE_EXISTING;
1630 disp = OPEN_EXISTING;
1633 if (ERROR_SUCCESS == plibc_conv_to_win_pathwconv(expfn, wexpfn))
1634 h = CreateFileW (wexpfn, access,
1635 FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE, NULL,
1636 disp, FILE_ATTRIBUTE_NORMAL, NULL);
1638 h = INVALID_HANDLE_VALUE;
1639 if (h == INVALID_HANDLE_VALUE)
1641 SetErrnoFromWinError (GetLastError ());
1642 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "open", expfn);
1643 GNUNET_free (expfn);
1647 if (flags & GNUNET_DISK_OPEN_APPEND)
1648 if (SetFilePointer (h, 0, 0, FILE_END) == INVALID_SET_FILE_POINTER)
1650 SetErrnoFromWinError (GetLastError ());
1651 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "SetFilePointer", expfn);
1653 GNUNET_free (expfn);
1658 ret = GNUNET_malloc (sizeof (struct GNUNET_DISK_FileHandle));
1661 ret->type = GNUNET_DISK_FILE;
1665 GNUNET_free (expfn);
1671 * Close an open file
1672 * @param h file handle
1673 * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
1676 GNUNET_DISK_file_close (struct GNUNET_DISK_FileHandle *h)
1681 return GNUNET_SYSERR;
1685 if (!CloseHandle (h->h))
1687 SetErrnoFromWinError (GetLastError ());
1688 LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING, "close");
1689 GNUNET_free (h->oOverlapRead);
1690 GNUNET_free (h->oOverlapWrite);
1692 return GNUNET_SYSERR;
1695 if (close (h->fd) != 0)
1697 LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING, "close");
1699 return GNUNET_SYSERR;
1708 * Construct full path to a file inside of the private
1709 * directory used by GNUnet. Also creates the corresponding
1710 * directory. If the resulting name is supposed to be
1711 * a directory, end the last argument in '/' (or pass
1712 * DIR_SEPARATOR_STR as the last argument before NULL).
1714 * @param cfg configuration to use (determines HOME)
1715 * @param serviceName name of the service
1716 * @param ... is NULL-terminated list of
1717 * path components to append to the
1718 * private directory name.
1719 * @return the constructed filename
1722 GNUNET_DISK_get_home_filename (const struct GNUNET_CONFIGURATION_Handle *cfg,
1723 const char *serviceName, ...)
1729 unsigned int needed;
1732 GNUNET_CONFIGURATION_get_value_filename (cfg, serviceName, "HOME", &pfx))
1736 LOG (GNUNET_ERROR_TYPE_WARNING,
1737 _("No `%s' specified for service `%s' in configuration.\n"), "HOME",
1741 needed = strlen (pfx) + 2;
1742 if ((pfx[strlen (pfx) - 1] != '/') && (pfx[strlen (pfx) - 1] != '\\'))
1744 va_start (ap, serviceName);
1747 c = va_arg (ap, const char *);
1751 needed += strlen (c);
1752 if ((c[strlen (c) - 1] != '/') && (c[strlen (c) - 1] != '\\'))
1756 ret = GNUNET_malloc (needed);
1759 va_start (ap, serviceName);
1762 c = va_arg (ap, const char *);
1766 if ((c[strlen (c) - 1] != '/') && (c[strlen (c) - 1] != '\\'))
1767 strcat (ret, DIR_SEPARATOR_STR);
1771 if ((ret[strlen (ret) - 1] != '/') && (ret[strlen (ret) - 1] != '\\'))
1772 (void) GNUNET_DISK_directory_create_for_file (ret);
1774 (void) GNUNET_DISK_directory_create (ret);
1780 * Handle for a memory-mapping operation.
1782 struct GNUNET_DISK_MapHandle
1785 * Address where the map is in memory.
1791 * Underlying OS handle.
1796 * Number of bytes mapped.
1804 #define MAP_FAILED ((void *) -1)
1808 * Map a file into memory
1810 * @param h open file handle
1811 * @param m handle to the new mapping
1812 * @param access access specification, GNUNET_DISK_MAP_TYPE_xxx
1813 * @param len size of the mapping
1814 * @return pointer to the mapped memory region, NULL on failure
1817 GNUNET_DISK_file_map (const struct GNUNET_DISK_FileHandle *h,
1818 struct GNUNET_DISK_MapHandle **m,
1819 enum GNUNET_DISK_MapType access, size_t len)
1828 DWORD mapAccess, protect;
1830 if ((access & GNUNET_DISK_MAP_TYPE_READ) &&
1831 (access & GNUNET_DISK_MAP_TYPE_WRITE))
1833 protect = PAGE_READWRITE;
1834 mapAccess = FILE_MAP_ALL_ACCESS;
1836 else if (access & GNUNET_DISK_MAP_TYPE_READ)
1838 protect = PAGE_READONLY;
1839 mapAccess = FILE_MAP_READ;
1841 else if (access & GNUNET_DISK_MAP_TYPE_WRITE)
1843 protect = PAGE_READWRITE;
1844 mapAccess = FILE_MAP_WRITE;
1852 *m = GNUNET_malloc (sizeof (struct GNUNET_DISK_MapHandle));
1853 (*m)->h = CreateFileMapping (h->h, NULL, protect, 0, 0, NULL);
1854 if ((*m)->h == INVALID_HANDLE_VALUE)
1856 SetErrnoFromWinError (GetLastError ());
1861 (*m)->addr = MapViewOfFile ((*m)->h, mapAccess, 0, 0, len);
1864 SetErrnoFromWinError (GetLastError ());
1865 CloseHandle ((*m)->h);
1874 if (access & GNUNET_DISK_MAP_TYPE_READ)
1876 if (access & GNUNET_DISK_MAP_TYPE_WRITE)
1878 *m = GNUNET_malloc (sizeof (struct GNUNET_DISK_MapHandle));
1879 (*m)->addr = mmap (NULL, len, prot, MAP_SHARED, h->fd, 0);
1880 GNUNET_assert (NULL != (*m)->addr);
1881 if (MAP_FAILED == (*m)->addr)
1893 * @param h mapping handle
1894 * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
1897 GNUNET_DISK_file_unmap (struct GNUNET_DISK_MapHandle *h)
1904 return GNUNET_SYSERR;
1908 ret = UnmapViewOfFile (h->addr) ? GNUNET_OK : GNUNET_SYSERR;
1909 if (ret != GNUNET_OK)
1910 SetErrnoFromWinError (GetLastError ());
1911 if (!CloseHandle (h->h) && (ret == GNUNET_OK))
1913 ret = GNUNET_SYSERR;
1914 SetErrnoFromWinError (GetLastError ());
1917 ret = munmap (h->addr, h->len) != -1 ? GNUNET_OK : GNUNET_SYSERR;
1925 * Write file changes to disk
1926 * @param h handle to an open file
1927 * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
1930 GNUNET_DISK_file_sync (const struct GNUNET_DISK_FileHandle *h)
1935 return GNUNET_SYSERR;
1941 ret = FlushFileBuffers (h->h) ? GNUNET_OK : GNUNET_SYSERR;
1942 if (ret != GNUNET_OK)
1943 SetErrnoFromWinError (GetLastError ());
1945 #elif defined(FREEBSD) || defined(OPENBSD) || defined(DARWIN)
1946 return fsync (h->fd) == -1 ? GNUNET_SYSERR : GNUNET_OK;
1948 return fdatasync (h->fd) == -1 ? GNUNET_SYSERR : GNUNET_OK;
1953 /* Copyright Bob Byrnes <byrnes <at> curl.com>
1954 http://permalink.gmane.org/gmane.os.cygwin.patches/2121
1956 /* Create a pipe, and return handles to the read and write ends,
1957 just like CreatePipe, but ensure that the write end permits
1958 FILE_READ_ATTRIBUTES access, on later versions of win32 where
1959 this is supported. This access is needed by NtQueryInformationFile,
1960 which is used to implement select and nonblocking writes.
1961 Note that the return value is either NO_ERROR or GetLastError,
1962 unlike CreatePipe, which returns a bool for success or failure. */
1964 create_selectable_pipe (PHANDLE read_pipe_ptr, PHANDLE write_pipe_ptr,
1965 LPSECURITY_ATTRIBUTES sa_ptr, DWORD psize,
1966 DWORD dwReadMode, DWORD dwWriteMode)
1968 /* Default to error. */
1969 *read_pipe_ptr = *write_pipe_ptr = INVALID_HANDLE_VALUE;
1971 HANDLE read_pipe = INVALID_HANDLE_VALUE, write_pipe = INVALID_HANDLE_VALUE;
1973 /* Ensure that there is enough pipe buffer space for atomic writes. */
1974 if (psize < PIPE_BUF)
1977 char pipename[MAX_PATH];
1979 /* Retry CreateNamedPipe as long as the pipe name is in use.
1980 * Retrying will probably never be necessary, but we want
1981 * to be as robust as possible. */
1984 static volatile LONG pipe_unique_id;
1986 snprintf (pipename, sizeof pipename, "\\\\.\\pipe\\gnunet-%d-%ld",
1987 getpid (), InterlockedIncrement ((LONG *) & pipe_unique_id));
1989 LOG (GNUNET_ERROR_TYPE_DEBUG, "CreateNamedPipe: name = %s, size = %lu\n",
1992 /* Use CreateNamedPipe instead of CreatePipe, because the latter
1993 * returns a write handle that does not permit FILE_READ_ATTRIBUTES
1994 * access, on versions of win32 earlier than WinXP SP2.
1995 * CreatePipe also stupidly creates a full duplex pipe, which is
1996 * a waste, since only a single direction is actually used.
1997 * It's important to only allow a single instance, to ensure that
1998 * the pipe was not created earlier by some other process, even if
1999 * the pid has been reused. We avoid FILE_FLAG_FIRST_PIPE_INSTANCE
2000 * because that is only available for Win2k SP2 and WinXP. */
2001 read_pipe = CreateNamedPipeA (pipename, PIPE_ACCESS_INBOUND | dwReadMode, PIPE_TYPE_BYTE | PIPE_READMODE_BYTE, 1, /* max instances */
2002 psize, /* output buffer size */
2003 psize, /* input buffer size */
2004 NMPWAIT_USE_DEFAULT_WAIT, sa_ptr);
2006 if (read_pipe != INVALID_HANDLE_VALUE)
2009 LOG (GNUNET_ERROR_TYPE_DEBUG, "pipe read handle = %p\n", read_pipe);
2014 DWORD err = GetLastError ();
2018 case ERROR_PIPE_BUSY:
2019 /* The pipe is already open with compatible parameters.
2020 * Pick a new name and retry. */
2022 LOG (GNUNET_ERROR_TYPE_DEBUG, "pipe busy, retrying\n");
2025 case ERROR_ACCESS_DENIED:
2026 /* The pipe is already open with incompatible parameters.
2027 * Pick a new name and retry. */
2029 LOG (GNUNET_ERROR_TYPE_DEBUG, "pipe access denied, retrying\n");
2032 case ERROR_CALL_NOT_IMPLEMENTED:
2033 /* We are on an older Win9x platform without named pipes.
2034 * Return an anonymous pipe as the best approximation. */
2036 LOG (GNUNET_ERROR_TYPE_DEBUG,
2037 "CreateNamedPipe not implemented, resorting to "
2038 "CreatePipe: size = %lu\n", psize);
2040 if (CreatePipe (read_pipe_ptr, write_pipe_ptr, sa_ptr, psize))
2043 LOG (GNUNET_ERROR_TYPE_DEBUG, "pipe read handle = %p\n",
2045 LOG (GNUNET_ERROR_TYPE_DEBUG, "pipe write handle = %p\n",
2050 err = GetLastError ();
2051 LOG (GNUNET_ERROR_TYPE_ERROR, "CreatePipe failed: %d\n", err);
2054 LOG (GNUNET_ERROR_TYPE_ERROR, "CreateNamedPipe failed: %d\n", err);
2060 LOG (GNUNET_ERROR_TYPE_DEBUG, "CreateFile: name = %s\n", pipename);
2063 /* Open the named pipe for writing.
2064 * Be sure to permit FILE_READ_ATTRIBUTES access. */
2065 write_pipe = CreateFileA (pipename, GENERIC_WRITE | FILE_READ_ATTRIBUTES, 0, /* share mode */
2066 sa_ptr, OPEN_EXISTING, dwWriteMode, /* flags and attributes */
2067 0); /* handle to template file */
2069 if (write_pipe == INVALID_HANDLE_VALUE)
2072 DWORD err = GetLastError ();
2075 LOG (GNUNET_ERROR_TYPE_DEBUG, "CreateFile failed: %d\n", err);
2077 CloseHandle (read_pipe);
2081 LOG (GNUNET_ERROR_TYPE_DEBUG, "pipe write handle = %p\n", write_pipe);
2084 *read_pipe_ptr = read_pipe;
2085 *write_pipe_ptr = write_pipe;
2091 * Creates an interprocess channel
2093 * @param blocking creates an asynchronous pipe if set to GNUNET_NO
2094 * @param inherit_read inherit the parent processes stdin (only for windows)
2095 * @param inherit_write inherit the parent processes stdout (only for windows)
2097 * @return handle to the new pipe, NULL on error
2099 struct GNUNET_DISK_PipeHandle *
2100 GNUNET_DISK_pipe (int blocking_read, int blocking_write, int inherit_read, int inherit_write)
2102 struct GNUNET_DISK_PipeHandle *p;
2103 struct GNUNET_DISK_FileHandle *fds;
2105 p = GNUNET_malloc (sizeof (struct GNUNET_DISK_PipeHandle) +
2106 2 * sizeof (struct GNUNET_DISK_FileHandle));
2107 fds = (struct GNUNET_DISK_FileHandle *) &p[1];
2120 LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR, "pipe");
2125 p->fd[0]->fd = fd[0];
2126 p->fd[1]->fd = fd[1];
2128 flags = fcntl (fd[0], F_GETFL);
2130 flags |= O_NONBLOCK;
2131 if (0 > fcntl (fd[0], F_SETFL, flags))
2133 flags = fcntl (fd[0], F_GETFD);
2134 flags |= FD_CLOEXEC;
2135 if (0 > fcntl (fd[0], F_SETFD, flags))
2138 flags = fcntl (fd[1], F_GETFL);
2139 if (!blocking_write)
2140 flags |= O_NONBLOCK;
2141 if (0 > fcntl (fd[1], F_SETFL, flags))
2143 flags = fcntl (fd[1], F_GETFD);
2144 flags |= FD_CLOEXEC;
2145 if (0 > fcntl (fd[1], F_SETFD, flags))
2150 LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR, "fcntl");
2151 GNUNET_break (0 == close (p->fd[0]->fd));
2152 GNUNET_break (0 == close (p->fd[1]->fd));
2162 create_selectable_pipe (&p->fd[0]->h, &p->fd[1]->h, NULL, 0,
2163 blocking_read ? 0 : FILE_FLAG_OVERLAPPED,
2164 blocking_write ? 0 : FILE_FLAG_OVERLAPPED);
2168 SetErrnoFromWinError (GetLastError ());
2171 if (!DuplicateHandle
2172 (GetCurrentProcess (), p->fd[0]->h, GetCurrentProcess (), &tmp_handle, 0,
2173 inherit_read == GNUNET_YES ? TRUE : FALSE, DUPLICATE_SAME_ACCESS))
2175 SetErrnoFromWinError (GetLastError ());
2176 CloseHandle (p->fd[0]->h);
2177 CloseHandle (p->fd[1]->h);
2181 CloseHandle (p->fd[0]->h);
2182 p->fd[0]->h = tmp_handle;
2184 if (!DuplicateHandle
2185 (GetCurrentProcess (), p->fd[1]->h, GetCurrentProcess (), &tmp_handle, 0,
2186 inherit_write == GNUNET_YES ? TRUE : FALSE, DUPLICATE_SAME_ACCESS))
2188 SetErrnoFromWinError (GetLastError ());
2189 CloseHandle (p->fd[0]->h);
2190 CloseHandle (p->fd[1]->h);
2194 CloseHandle (p->fd[1]->h);
2195 p->fd[1]->h = tmp_handle;
2197 p->fd[0]->type = GNUNET_PIPE;
2198 p->fd[1]->type = GNUNET_PIPE;
2200 p->fd[0]->oOverlapRead = GNUNET_malloc (sizeof (OVERLAPPED));
2201 p->fd[0]->oOverlapWrite = GNUNET_malloc (sizeof (OVERLAPPED));
2202 p->fd[1]->oOverlapRead = GNUNET_malloc (sizeof (OVERLAPPED));
2203 p->fd[1]->oOverlapWrite = GNUNET_malloc (sizeof (OVERLAPPED));
2205 p->fd[0]->oOverlapRead->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2206 p->fd[0]->oOverlapWrite->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2208 p->fd[1]->oOverlapRead->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2209 p->fd[1]->oOverlapWrite->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2216 * Creates a pipe object from a couple of file descriptors.
2217 * Useful for wrapping existing pipe FDs.
2219 * @param blocking creates an asynchronous pipe if set to GNUNET_NO
2220 * @param fd an array of two fd values. One of them may be -1 for read-only or write-only pipes
2222 * @return handle to the new pipe, NULL on error
2224 struct GNUNET_DISK_PipeHandle *
2225 GNUNET_DISK_pipe_from_fd (int blocking_read, int blocking_write, int fd[2])
2227 struct GNUNET_DISK_PipeHandle *p;
2228 struct GNUNET_DISK_FileHandle *fds;
2230 p = GNUNET_malloc (sizeof (struct GNUNET_DISK_PipeHandle) +
2231 2 * sizeof (struct GNUNET_DISK_FileHandle));
2232 fds = (struct GNUNET_DISK_FileHandle *) &p[1];
2240 p->fd[0]->fd = fd[0];
2241 p->fd[1]->fd = fd[1];
2245 flags = fcntl (fd[0], F_GETFL);
2247 flags |= O_NONBLOCK;
2248 if (0 > fcntl (fd[0], F_SETFL, flags))
2250 flags = fcntl (fd[0], F_GETFD);
2251 flags |= FD_CLOEXEC;
2252 if (0 > fcntl (fd[0], F_SETFD, flags))
2258 flags = fcntl (fd[1], F_GETFL);
2259 if (!blocking_write)
2260 flags |= O_NONBLOCK;
2261 if (0 > fcntl (fd[1], F_SETFL, flags))
2263 flags = fcntl (fd[1], F_GETFD);
2264 flags |= FD_CLOEXEC;
2265 if (0 > fcntl (fd[1], F_SETFD, flags))
2271 LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR, "fcntl");
2272 if (p->fd[0]->fd >= 0)
2273 GNUNET_break (0 == close (p->fd[0]->fd));
2274 if (p->fd[1]->fd >= 0)
2275 GNUNET_break (0 == close (p->fd[1]->fd));
2284 p->fd[0]->h = _get_osfhandle (fd[0]);
2286 p->fd[0]->h = INVALID_HANDLE_VALUE;
2288 p->fd[1]->h = _get_osfhandle (fd[1]);
2290 p->fd[1]->h = INVALID_HANDLE_VALUE;
2292 if (p->fd[0]->h != INVALID_HANDLE_VALUE)
2294 p->fd[0]->type = GNUNET_PIPE;
2295 p->fd[0]->oOverlapRead = GNUNET_malloc (sizeof (OVERLAPPED));
2296 p->fd[0]->oOverlapWrite = GNUNET_malloc (sizeof (OVERLAPPED));
2297 p->fd[0]->oOverlapRead->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2298 p->fd[0]->oOverlapWrite->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2301 if (p->fd[1]->h != INVALID_HANDLE_VALUE)
2303 p->fd[1]->type = GNUNET_PIPE;
2304 p->fd[1]->oOverlapRead = GNUNET_malloc (sizeof (OVERLAPPED));
2305 p->fd[1]->oOverlapWrite = GNUNET_malloc (sizeof (OVERLAPPED));
2306 p->fd[1]->oOverlapRead->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2307 p->fd[1]->oOverlapWrite->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2315 * Closes an interprocess channel
2317 * @param p pipe to close
2318 * @param end which end of the pipe to close
2319 * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
2322 GNUNET_DISK_pipe_close_end (struct GNUNET_DISK_PipeHandle *p,
2323 enum GNUNET_DISK_PipeEnd end)
2325 int ret = GNUNET_OK;
2329 if (end == GNUNET_DISK_PIPE_END_READ)
2331 if (p->fd[0]->h != INVALID_HANDLE_VALUE)
2333 if (!CloseHandle (p->fd[0]->h))
2335 SetErrnoFromWinError (GetLastError ());
2336 ret = GNUNET_SYSERR;
2338 GNUNET_free (p->fd[0]->oOverlapRead);
2339 GNUNET_free (p->fd[0]->oOverlapWrite);
2340 p->fd[0]->h = INVALID_HANDLE_VALUE;
2343 else if (end == GNUNET_DISK_PIPE_END_WRITE)
2345 if (p->fd[0]->h != INVALID_HANDLE_VALUE)
2347 if (!CloseHandle (p->fd[1]->h))
2349 SetErrnoFromWinError (GetLastError ());
2350 ret = GNUNET_SYSERR;
2352 GNUNET_free (p->fd[1]->oOverlapRead);
2353 GNUNET_free (p->fd[1]->oOverlapWrite);
2354 p->fd[1]->h = INVALID_HANDLE_VALUE;
2360 if (end == GNUNET_DISK_PIPE_END_READ)
2362 if (0 != close (p->fd[0]->fd))
2364 ret = GNUNET_SYSERR;
2369 else if (end == GNUNET_DISK_PIPE_END_WRITE)
2371 if (0 != close (p->fd[1]->fd))
2373 ret = GNUNET_SYSERR;
2384 * Closes an interprocess channel
2386 * @param p pipe to close
2387 * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
2390 GNUNET_DISK_pipe_close (struct GNUNET_DISK_PipeHandle *p)
2392 int ret = GNUNET_OK;
2396 if (p->fd[0]->h != INVALID_HANDLE_VALUE)
2398 if (!CloseHandle (p->fd[0]->h))
2400 SetErrnoFromWinError (GetLastError ());
2401 ret = GNUNET_SYSERR;
2403 GNUNET_free (p->fd[0]->oOverlapRead);
2404 GNUNET_free (p->fd[0]->oOverlapWrite);
2406 if (p->fd[1]->h != INVALID_HANDLE_VALUE)
2408 if (!CloseHandle (p->fd[1]->h))
2410 SetErrnoFromWinError (GetLastError ());
2411 ret = GNUNET_SYSERR;
2413 GNUNET_free (p->fd[1]->oOverlapRead);
2414 GNUNET_free (p->fd[1]->oOverlapWrite);
2419 if (p->fd[0]->fd != -1)
2421 if (0 != close (p->fd[0]->fd))
2423 ret = GNUNET_SYSERR;
2428 if (p->fd[1]->fd != -1)
2430 if (0 != close (p->fd[1]->fd))
2432 ret = GNUNET_SYSERR;
2444 * Creates a named pipe/FIFO and opens it
2445 * @param fn pointer to the name of the named pipe or to NULL
2446 * @param flags open flags
2447 * @param perm access permissions
2448 * @return pipe handle on success, NULL on error
2450 struct GNUNET_DISK_FileHandle *
2451 GNUNET_DISK_npipe_create (char **fn, enum GNUNET_DISK_OpenFlags flags,
2452 enum GNUNET_DISK_AccessPermissions perm)
2455 struct GNUNET_DISK_FileHandle *ret;
2461 if (flags & GNUNET_DISK_OPEN_READWRITE)
2462 openMode = PIPE_ACCESS_DUPLEX;
2463 else if (flags & GNUNET_DISK_OPEN_READ)
2464 openMode = PIPE_ACCESS_INBOUND;
2465 else if (flags & GNUNET_DISK_OPEN_WRITE)
2466 openMode = PIPE_ACCESS_OUTBOUND;
2468 if (flags & GNUNET_DISK_OPEN_FAILIFEXISTS)
2469 openMode |= FILE_FLAG_FIRST_PIPE_INSTANCE;
2478 GNUNET_asprintf (&name, "\\\\.\\pipe\\%.246s", fn);
2480 LOG (GNUNET_ERROR_TYPE_DEBUG,
2481 "Trying to create an instance of named pipe `%s'\n", name);
2483 /* 1) This might work just fine with UTF-8 strings as it is.
2484 * 2) This is only used by GNUnet itself, and only with latin names.
2486 h = CreateNamedPipe (name, openMode | FILE_FLAG_OVERLAPPED,
2487 PIPE_TYPE_BYTE | PIPE_READMODE_BYTE, 2, 1, 1, 0,
2492 GNUNET_asprintf (fn, "\\\\.\\pipe\\gnunet-%llu",
2493 GNUNET_CRYPTO_random_u64 (GNUNET_CRYPTO_QUALITY_WEAK,
2496 LOG (GNUNET_ERROR_TYPE_DEBUG, "Trying to create unique named pipe `%s'\n",
2499 h = CreateNamedPipe (*fn,
2500 openMode | FILE_FLAG_OVERLAPPED |
2501 FILE_FLAG_FIRST_PIPE_INSTANCE,
2502 PIPE_TYPE_BYTE | PIPE_READMODE_BYTE, 2, 1, 1, 0,
2505 error_code = GetLastError ();
2508 /* don't re-set name to NULL yet */
2509 if (h == INVALID_HANDLE_VALUE)
2511 SetErrnoFromWinError (error_code);
2513 LOG (GNUNET_ERROR_TYPE_DEBUG,
2514 "Pipe creation have failed because of %d, errno is %d\n", error_code,
2520 LOG (GNUNET_ERROR_TYPE_DEBUG,
2521 "Pipe was to be unique, considering re-creation\n");
2525 if (error_code != ERROR_ACCESS_DENIED && error_code != ERROR_PIPE_BUSY)
2530 LOG (GNUNET_ERROR_TYPE_DEBUG,
2531 "Pipe name was not unique, trying again\n");
2541 ret = GNUNET_malloc (sizeof (*ret));
2543 ret->type = GNUNET_PIPE;
2545 ret->oOverlapRead = GNUNET_malloc (sizeof (OVERLAPPED));
2546 ret->oOverlapWrite = GNUNET_malloc (sizeof (OVERLAPPED));
2548 ret->oOverlapRead->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2549 ret->oOverlapWrite->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2555 char dir[] = "/tmp/gnunet-pipe-XXXXXX";
2557 if (mkdtemp (dir) == NULL)
2559 LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR, "mkdtemp");
2562 GNUNET_asprintf (fn, "%s/child-control", dir);
2565 if (mkfifo (*fn, translate_unix_perms (perm)) == -1)
2567 if ((errno != EEXIST) || (0 != (flags & GNUNET_DISK_OPEN_FAILIFEXISTS)))
2571 flags = flags & (~GNUNET_DISK_OPEN_FAILIFEXISTS);
2572 return GNUNET_DISK_file_open (*fn, flags, perm);
2578 * Opens already existing named pipe/FIFO
2580 * @param fn name of an existing named pipe
2581 * @param flags open flags
2582 * @param perm access permissions
2583 * @return pipe handle on success, NULL on error
2585 struct GNUNET_DISK_FileHandle *
2586 GNUNET_DISK_npipe_open (const char *fn, enum GNUNET_DISK_OpenFlags flags,
2587 enum GNUNET_DISK_AccessPermissions perm)
2590 struct GNUNET_DISK_FileHandle *ret;
2595 if (flags & GNUNET_DISK_OPEN_READWRITE)
2596 openMode = GENERIC_WRITE | GENERIC_READ;
2597 else if (flags & GNUNET_DISK_OPEN_READ)
2598 openMode = GENERIC_READ;
2599 else if (flags & GNUNET_DISK_OPEN_WRITE)
2600 openMode = GENERIC_WRITE;
2602 h = CreateFile (fn, openMode, 0, NULL, OPEN_EXISTING,
2603 FILE_FLAG_OVERLAPPED | FILE_READ_ATTRIBUTES, NULL);
2604 if (h == INVALID_HANDLE_VALUE)
2606 SetErrnoFromWinError (GetLastError ());
2610 ret = GNUNET_malloc (sizeof (*ret));
2612 ret->type = GNUNET_PIPE;
2613 ret->oOverlapRead = GNUNET_malloc (sizeof (OVERLAPPED));
2614 ret->oOverlapWrite = GNUNET_malloc (sizeof (OVERLAPPED));
2615 ret->oOverlapRead->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2616 ret->oOverlapWrite->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2620 flags = flags & (~GNUNET_DISK_OPEN_FAILIFEXISTS);
2621 return GNUNET_DISK_file_open (fn, flags, perm);
2626 * Closes a named pipe/FIFO
2627 * @param pipe named pipe
2628 * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
2631 GNUNET_DISK_npipe_close (struct GNUNET_DISK_FileHandle *pipe)
2634 return close (pipe->fd) == 0 ? GNUNET_OK : GNUNET_SYSERR;
2638 ret = CloseHandle (pipe->h);
2641 SetErrnoFromWinError (GetLastError ());
2642 return GNUNET_SYSERR;
2651 * Get the handle to a particular pipe end
2654 * @param n end to access
2655 * @return handle for the respective end
2657 const struct GNUNET_DISK_FileHandle *
2658 GNUNET_DISK_pipe_handle (const struct GNUNET_DISK_PipeHandle *p,
2659 enum GNUNET_DISK_PipeEnd n)
2663 case GNUNET_DISK_PIPE_END_READ:
2664 case GNUNET_DISK_PIPE_END_WRITE:
2674 * Retrieve OS file handle
2676 * @param fh GNUnet file descriptor
2677 * @param dst destination buffer
2678 * @param dst_len length of dst
2679 * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
2682 GNUNET_DISK_internal_file_handle_ (const struct GNUNET_DISK_FileHandle *fh,
2683 void *dst, size_t dst_len)
2686 if (dst_len < sizeof (HANDLE))
2687 return GNUNET_SYSERR;
2688 *((HANDLE *) dst) = fh->h;
2690 if (dst_len < sizeof (int))
2691 return GNUNET_SYSERR;
2692 *((int *) dst) = fh->fd;