X-Git-Url: https://git.librecmc.org/?a=blobdiff_plain;f=src%2Futil%2Fdisk.c;h=d536ec8979b4297e153ca56a6692707844d45000;hb=5b085881ab278a85c3ef3a1d91c58a5724a4e430;hp=667e350d97f33b437ffaed561a12ac018d475b45;hpb=a5ca53b73e643a0e62ded4ac9595bff38d08cb0b;p=oweals%2Fgnunet.git diff --git a/src/util/disk.c b/src/util/disk.c index 667e350d9..d536ec897 100644 --- a/src/util/disk.c +++ b/src/util/disk.c @@ -1,10 +1,10 @@ /* This file is part of GNUnet. - (C) 2001--2012 Christian Grothoff (and other contributing authors) + Copyright (C) 2001--2013, 2016 GNUnet e.V. GNUnet is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published - by the Free Software Foundation; either version 2, or (at your + by the Free Software Foundation; either version 3, or (at your option) any later version. GNUnet is distributed in the hope that it will be useful, but @@ -14,70 +14,49 @@ You should have received a copy of the GNU General Public License along with GNUnet; see the file COPYING. If not, write to the - Free Software Foundation, Inc., 59 Temple Place - Suite 330, - Boston, MA 02111-1307, USA. + Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. */ - /** * @file util/disk.c * @brief disk IO convenience methods * @author Christian Grothoff * @author Nils Durner */ - #include "platform.h" -#include "gnunet_common.h" -#include "gnunet_directories.h" -#include "gnunet_disk_lib.h" -#include "gnunet_scheduler_lib.h" -#include "gnunet_strings_lib.h" -#include "gnunet_crypto_lib.h" #include "disk.h" +#include "gnunet_strings_lib.h" +#include "gnunet_disk_lib.h" -#define LOG(kind,...) GNUNET_log_from (kind, "util", __VA_ARGS__) +#define LOG(kind,...) GNUNET_log_from (kind, "util-disk", __VA_ARGS__) -#define LOG_STRERROR(kind,syscall) GNUNET_log_from_strerror (kind, "util", syscall) +#define LOG_STRERROR(kind,syscall) GNUNET_log_from_strerror (kind, "util-disk", syscall) -#define LOG_STRERROR_FILE(kind,syscall,filename) GNUNET_log_from_strerror_file (kind, "util", syscall, filename) +#define LOG_STRERROR_FILE(kind,syscall,filename) GNUNET_log_from_strerror_file (kind, "util-disk", syscall, filename) /** * Block size for IO for copying files. */ #define COPY_BLK_SIZE 65536 - - -#if defined(LINUX) || defined(CYGWIN) +#include +#if HAVE_SYS_VFS_H #include -#else -#if defined(SOMEBSD) || defined(DARWIN) +#endif +#if HAVE_SYS_PARAM_H #include +#endif +#if HAVE_SYS_MOUNT_H #include -#else -#ifdef SOLARIS -#include +#endif +#if HAVE_SYS_STATVFS_H #include -#else -#ifdef MINGW -#ifndef PIPE_BUF -#define PIPE_BUF 512 -ULONG PipeSerialNumber; #endif + +#ifndef S_ISLNK #define _IFMT 0170000 /* type of file */ #define _IFLNK 0120000 /* symbolic link */ #define S_ISLNK(m) (((m)&_IFMT) == _IFLNK) -#else -#error PORT-ME: need to port statfs (how much space is left on the drive?) -#endif -#endif -#endif -#endif - -#if !defined(SOMEBSD) && !defined(DARWIN) && !defined(WINDOWS) -#include -#endif -#if LINUX -#include #endif @@ -88,6 +67,7 @@ struct GNUNET_DISK_PipeHandle { /** * File descriptors for the pipe. + * One or both of them could be NULL. */ struct GNUNET_DISK_FileHandle *fd[2]; }; @@ -116,6 +96,14 @@ struct GetFileSizeData }; +#ifndef MINGW +/** + * Translate GNUnet-internal permission bitmap to UNIX file + * access permission bitmap. + * + * @param perm file permissions, GNUnet style + * @return file permissions, UNIX style + */ static int translate_unix_perms (enum GNUNET_DISK_AccessPermissions perm) { @@ -143,34 +131,33 @@ translate_unix_perms (enum GNUNET_DISK_AccessPermissions perm) return mode; } +#endif /** * Iterate over all files in the given directory and * accumulate their size. * - * @param cls closure of type "struct GetFileSizeData" + * @param cls closure of type `struct GetFileSizeData` * @param fn current filename we are looking at - * @return GNUNET_SYSERR on serious errors, otherwise GNUNET_OK + * @return #GNUNET_SYSERR on serious errors, otherwise #GNUNET_OK */ static int getSizeRec (void *cls, const char *fn) { struct GetFileSizeData *gfsd = cls; -#ifdef HAVE_STAT64 - struct stat64 buf; -#else - struct stat buf; -#endif +#if defined (HAVE_STAT64) && !(defined (_FILE_OFFSET_BITS) && _FILE_OFFSET_BITS == 64) + STRUCT_STAT64 buf; -#ifdef HAVE_STAT64 if (0 != STAT64 (fn, &buf)) { LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_DEBUG, "stat64", fn); return GNUNET_SYSERR; } #else + struct stat buf; + if (0 != STAT (fn, &buf)) { LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_DEBUG, "stat", fn); @@ -198,7 +185,7 @@ getSizeRec (void *cls, const char *fn) * Checks whether a handle is invalid * * @param h handle to check - * @return GNUNET_YES if invalid, GNUNET_NO if valid + * @return #GNUNET_YES if invalid, #GNUNET_NO if valid */ int GNUNET_DISK_handle_invalid (const struct GNUNET_DISK_FileHandle *h) @@ -215,11 +202,11 @@ GNUNET_DISK_handle_invalid (const struct GNUNET_DISK_FileHandle *h) * * @param fh open file handle * @param size where to write size of the file - * @return GNUNET_OK on success, GNUNET_SYSERR on error + * @return #GNUNET_OK on success, #GNUNET_SYSERR on error */ int GNUNET_DISK_file_handle_size (struct GNUNET_DISK_FileHandle *fh, - OFF_T *size) + off_t *size) { #if WINDOWS BOOL b; @@ -230,7 +217,7 @@ GNUNET_DISK_file_handle_size (struct GNUNET_DISK_FileHandle *fh, SetErrnoFromWinError (GetLastError ()); return GNUNET_SYSERR; } - *size = (OFF_T) li.QuadPart; + *size = (off_t) li.QuadPart; #else struct stat sbuf; @@ -248,10 +235,10 @@ GNUNET_DISK_file_handle_size (struct GNUNET_DISK_FileHandle *fh, * @param h handle of an open file * @param offset position to move to * @param whence specification to which position the offset parameter relates to - * @return the new position on success, GNUNET_SYSERR otherwise + * @return the new position on success, #GNUNET_SYSERR otherwise */ -OFF_T -GNUNET_DISK_file_seek (const struct GNUNET_DISK_FileHandle * h, OFF_T offset, +off_t +GNUNET_DISK_file_seek (const struct GNUNET_DISK_FileHandle * h, off_t offset, enum GNUNET_DISK_Seek whence) { if (h == NULL) @@ -261,12 +248,11 @@ GNUNET_DISK_file_seek (const struct GNUNET_DISK_FileHandle * h, OFF_T offset, } #ifdef MINGW - LARGE_INTEGER li, new_pos; + LARGE_INTEGER li; + LARGE_INTEGER new_pos; BOOL b; - static DWORD t[] = {[GNUNET_DISK_SEEK_SET] = FILE_BEGIN, - [GNUNET_DISK_SEEK_CUR] = FILE_CURRENT,[GNUNET_DISK_SEEK_END] = FILE_END - }; + static DWORD t[] = { FILE_BEGIN, FILE_CURRENT, FILE_END }; li.QuadPart = offset; b = SetFilePointerEx (h->h, li, &new_pos, t[whence]); @@ -275,11 +261,9 @@ GNUNET_DISK_file_seek (const struct GNUNET_DISK_FileHandle * h, OFF_T offset, SetErrnoFromWinError (GetLastError ()); return GNUNET_SYSERR; } - return (OFF_T) new_pos.QuadPart; + return (off_t) new_pos.QuadPart; #else - static int t[] = {[GNUNET_DISK_SEEK_SET] = SEEK_SET, - [GNUNET_DISK_SEEK_CUR] = SEEK_CUR,[GNUNET_DISK_SEEK_END] = SEEK_END - }; + static int t[] = { SEEK_SET, SEEK_CUR, SEEK_END }; return lseek (h->fd, offset, t[whence]); #endif @@ -294,23 +278,25 @@ GNUNET_DISK_file_seek (const struct GNUNET_DISK_FileHandle * h, OFF_T offset, * @param size set to the size of the file (or, * in the case of directories, the sum * of all sizes of files in the directory) - * @param includeSymLinks should symbolic links be + * @param include_symbolic_links should symbolic links be * included? - * @param singleFileMode GNUNET_YES to only get size of one file - * and return GNUNET_SYSERR for directories. - * @return GNUNET_SYSERR on error, GNUNET_OK on success + * @param single_file_mode #GNUNET_YES to only get size of one file + * and return #GNUNET_SYSERR for directories. + * @return #GNUNET_SYSERR on error, #GNUNET_OK on success */ int -GNUNET_DISK_file_size (const char *filename, uint64_t * size, - int includeSymLinks, int singleFileMode) +GNUNET_DISK_file_size (const char *filename, + uint64_t * size, + int include_symbolic_links, + int single_file_mode) { struct GetFileSizeData gfsd; int ret; GNUNET_assert (size != NULL); gfsd.total = 0; - gfsd.include_sym_links = includeSymLinks; - gfsd.single_file_mode = singleFileMode; + gfsd.include_sym_links = include_symbolic_links; + gfsd.single_file_mode = single_file_mode; ret = getSizeRec (&gfsd, filename); *size = gfsd.total; return ret; @@ -330,74 +316,86 @@ GNUNET_DISK_file_size (const char *filename, uint64_t * size, * @param filename name of the file * @param dev set to the device ID * @param ino set to the inode ID - * @return GNUNET_OK on success + * @return #GNUNET_OK on success */ int GNUNET_DISK_file_get_identifiers (const char *filename, uint64_t * dev, uint64_t * ino) { -#if LINUX - struct stat sbuf; - struct statvfs fbuf; +#if WINDOWS + { + // FIXME NILS: test this + struct GNUNET_DISK_FileHandle *fh; + BY_HANDLE_FILE_INFORMATION info; + int succ; - if ((0 == stat (filename, &sbuf)) && (0 == statvfs (filename, &fbuf))) + fh = GNUNET_DISK_file_open (filename, + GNUNET_DISK_OPEN_READ, + GNUNET_DISK_PERM_NONE); + if (NULL == fh) + return GNUNET_SYSERR; + succ = GetFileInformationByHandle (fh->h, &info); + GNUNET_DISK_file_close (fh); + if (!succ) + { + return GNUNET_SYSERR; + } + *dev = info.dwVolumeSerialNumber; + *ino = ((((uint64_t) info.nFileIndexHigh) << (sizeof (DWORD) * 8)) | info.nFileIndexLow); + } +#else /* !WINDOWS */ +#if HAVE_STAT { - *dev = (uint64_t) fbuf.f_fsid; + struct stat sbuf; + + if (0 != stat (filename, &sbuf)) + { + return GNUNET_SYSERR; + } *ino = (uint64_t) sbuf.st_ino; - return GNUNET_OK; } -#elif SOMEBSD - struct stat sbuf; - struct statfs fbuf; +#else + *ino = 0; +#endif +#if HAVE_STATVFS + { + struct statvfs fbuf; - if ((0 == stat (filename, &sbuf)) && (0 == statfs (filename, &fbuf))) + if (0 != statvfs (filename, &fbuf)) + { + return GNUNET_SYSERR; + } + *dev = (uint64_t) fbuf.f_fsid; + } +#elif HAVE_STATFS { + struct statfs fbuf; + + if (0 != statfs (filename, &fbuf)) + { + return GNUNET_SYSERR; + } *dev = ((uint64_t) fbuf.f_fsid.val[0]) << 32 || ((uint64_t) fbuf.f_fsid.val[1]); - *ino = (uint64_t) sbuf.st_ino; - return GNUNET_OK; - } -#elif WINDOWS - // FIXME NILS: test this - struct GNUNET_DISK_FileHandle *fh; - BY_HANDLE_FILE_INFORMATION info; - int succ; - - fh = GNUNET_DISK_file_open (filename, GNUNET_DISK_OPEN_READ, 0); - if (fh == NULL) - return GNUNET_SYSERR; - succ = GetFileInformationByHandle (fh->h, &info); - GNUNET_DISK_file_close (fh); - if (succ) - { - *dev = info.dwVolumeSerialNumber; - *ino = ((((uint64_t) info.nFileIndexHigh) << (sizeof (DWORD) * 8)) | info.nFileIndexLow); - return GNUNET_OK; } - else - return GNUNET_SYSERR; - +#else + *dev = 0; #endif - return GNUNET_SYSERR; +#endif /* !WINDOWS */ + return GNUNET_OK; } /** - * Create an (empty) temporary file on disk. If the given name is not - * an absolute path, the current 'TMPDIR' will be prepended. In any case, - * 6 random characters will be appended to the name to create a unique - * filename. + * Create the name for a temporary file or directory from a template. * - * @param t component to use for the name; - * does NOT contain "XXXXXX" or "/tmp/". - * @return NULL on error, otherwise name of fresh - * file on disk in directory for temporary files + * @param t template (without XXXXX or "/tmp/") + * @return name ready for passing to 'mktemp' or 'mkdtemp', NULL on error */ -char * -GNUNET_DISK_mktemp (const char *t) +static char * +mktemp_name (const char *t) { const char *tmpdir; - int fd; char *tmpl; char *fn; @@ -409,7 +407,12 @@ GNUNET_DISK_mktemp (const char *t) { /* FIXME: This uses system codepage on W32, not UTF-8 */ tmpdir = getenv ("TMPDIR"); - tmpdir = tmpdir ? tmpdir : "/tmp"; + if (NULL == tmpdir) + tmpdir = getenv ("TMP"); + if (NULL == tmpdir) + tmpdir = getenv ("TEMP"); + if (NULL == tmpdir) + tmpdir = "/tmp"; GNUNET_asprintf (&tmpl, "%s/%s%s", tmpdir, t, "XXXXXX"); } else @@ -428,94 +431,200 @@ GNUNET_DISK_mktemp (const char *t) #else fn = tmpl; #endif - /* FIXME: why is this not MKSTEMP()? This function is implemented in plibc. - * CG: really? If I put MKSTEMP here, I get a compilation error... - * It will assume that fn is UTF-8-encoded, if compiled with UTF-8 support. - */ - fd = mkstemp (fn); - if (fd == -1) + return fn; +} + + +#if WINDOWS +static char * +mkdtemp (char *fn) +{ + char *random_fn; + char *tfn; + + while (1) { - LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_ERROR, "mkstemp", fn); - GNUNET_free (fn); - return NULL; + tfn = GNUNET_strdup (fn); + random_fn = _mktemp (tfn); + if (NULL == random_fn) + { + GNUNET_free (tfn); + return NULL; + } + /* FIXME: assume fn to be UTF-8-encoded and do the right thing */ + if (0 == CreateDirectoryA (tfn, NULL)) + { + DWORD error = GetLastError (); + GNUNET_free (tfn); + if (ERROR_ALREADY_EXISTS == error) + continue; + return NULL; + } + break; } - if (0 != CLOSE (fd)) - LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "close", fn); + strcpy (fn, tfn); return fn; } +/** + * Update POSIX permissions mask of a file on disk. If both argumets + * are #GNUNET_NO, the file is made world-read-write-executable (777). + * Does nothing on W32. + * + * @param fn name of the file to update + * @param require_uid_match #GNUNET_YES means 700 + * @param require_gid_match #GNUNET_YES means 770 unless @a require_uid_match is set + */ +void +GNUNET_DISK_fix_permissions (const char *fn, + int require_uid_match, + int require_gid_match) +{ + /* nothing on W32 */ +} + +#else /** - * Get the number of blocks that are left on the partition that - * contains the given file (for normal users). + * Update POSIX permissions mask of a file on disk. If both argumets + * are #GNUNET_NO, the file is made world-read-write-executable (777). * - * @param part a file on the partition to check - * @return -1 on errors, otherwise the number of free blocks + * @param fn name of the file to update + * @param require_uid_match #GNUNET_YES means 700 + * @param require_gid_match #GNUNET_YES means 770 unless @a require_uid_match is set */ -long -GNUNET_DISK_get_blocks_available (const char *part) +void +GNUNET_DISK_fix_permissions (const char *fn, + int require_uid_match, + int require_gid_match) { -#ifdef SOLARIS - struct statvfs buf; + mode_t mode; - if (0 != statvfs (part, &buf)) - { - LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "statfs", part); - return -1; - } - return buf.f_bavail; -#elif MINGW - DWORD dwDummy; - DWORD dwBlocks; - wchar_t szDrive[4]; - wchar_t wpath[MAX_PATH + 1]; - char *path; + if (GNUNET_YES == require_uid_match) + mode = S_IRUSR | S_IWUSR | S_IXUSR; + else if (GNUNET_YES == require_gid_match) + mode = S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IWGRP | S_IXGRP; + else + mode = S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IWGRP | S_IXGRP | S_IROTH | S_IWOTH | S_IXOTH; + if (0 != chmod (fn, mode)) + GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, + "chmod", + fn); +} + +#endif + +/** + * Create an (empty) temporary directory on disk. If the given name is not + * an absolute path, the current 'TMPDIR' will be prepended. In any case, + * 6 random characters will be appended to the name to create a unique + * filename. + * + * @param t component to use for the name; + * does NOT contain "XXXXXX" or "/tmp/". + * @return NULL on error, otherwise name of fresh + * file on disk in directory for temporary files + */ +char * +GNUNET_DISK_mkdtemp (const char *t) +{ + char *fn; + mode_t omask; - path = GNUNET_STRINGS_filename_expand (part); - if (path == NULL) - return -1; - /* "part" was in UTF-8, and so is "path" */ - if (ERROR_SUCCESS != plibc_conv_to_win_pathwconv(path, wpath)) + omask = umask (S_IWGRP | S_IWOTH | S_IRGRP | S_IROTH); + fn = mktemp_name (t); + if (fn != mkdtemp (fn)) { - GNUNET_free (path); - return -1; + LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_ERROR, "mkdtemp", fn); + GNUNET_free (fn); + umask (omask); + return NULL; } - GNUNET_free (path); - wcsncpy (szDrive, wpath, 3); - szDrive[3] = 0; - if (!GetDiskFreeSpaceW (szDrive, &dwDummy, &dwDummy, &dwBlocks, &dwDummy)) - { - LOG (GNUNET_ERROR_TYPE_WARNING, _("`%s' failed for drive `%S': %u\n"), - "GetDiskFreeSpace", szDrive, GetLastError ()); + umask (omask); + return fn; +} - return -1; - } - return dwBlocks; -#else - struct statfs s; - if (0 != statfs (part, &s)) +/** + * Move a file out of the way (create a backup) by + * renaming it to "orig.NUM~" where NUM is the smallest + * number that is not used yet. + * + * @param fil name of the file to back up + */ +void +GNUNET_DISK_file_backup (const char *fil) +{ + size_t slen; + char *target; + unsigned int num; + + slen = strlen (fil) + 20; + target = GNUNET_malloc (slen); + num = 0; + do + { + GNUNET_snprintf (target, slen, + "%s.%u~", + fil, + num++); + } while (0 == access (target, F_OK)); + if (0 != rename (fil, target)) + GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR, + "rename", + fil); + GNUNET_free (target); +} + + +/** + * Create an (empty) temporary file on disk. If the given name is not + * an absolute path, the current 'TMPDIR' will be prepended. In any case, + * 6 random characters will be appended to the name to create a unique + * filename. + * + * @param t component to use for the name; + * does NOT contain "XXXXXX" or "/tmp/". + * @return NULL on error, otherwise name of fresh + * file on disk in directory for temporary files + */ +char * +GNUNET_DISK_mktemp (const char *t) +{ + int fd; + char *fn; + mode_t omask; + + omask = umask (S_IWGRP | S_IWOTH | S_IRGRP | S_IROTH); + fn = mktemp_name (t); + if (-1 == (fd = mkstemp (fn))) { - LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "statfs", part); - return -1; + LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_ERROR, "mkstemp", fn); + GNUNET_free (fn); + umask (omask); + return NULL; } - return s.f_bavail; -#endif + umask (omask); + if (0 != CLOSE (fd)) + LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "close", fn); + return fn; } /** - * Test if "fil" is a directory. - * Will not print an error message if the directory - * does not exist. Will log errors if GNUNET_SYSERR is - * returned (i.e., a file exists with the same name). + * Test if @a fil is a directory and listable. Optionally, also check if the + * directory is readable. Will not print an error message if the directory does + * not exist. Will log errors if #GNUNET_SYSERR is returned (i.e., a file exists + * with the same name). * * @param fil filename to test - * @return GNUNET_YES if yes, GNUNET_NO if not, GNUNET_SYSERR if it - * does not exist + * @param is_readable #GNUNET_YES to additionally check if @a fil is readable; + * #GNUNET_NO to disable this check + * @return #GNUNET_YES if yes, #GNUNET_NO if not; #GNUNET_SYSERR if it + * does not exist or stat'ed */ int -GNUNET_DISK_directory_test (const char *fil) +GNUNET_DISK_directory_test (const char *fil, int is_readable) { struct stat filestat; int ret; @@ -524,18 +633,23 @@ GNUNET_DISK_directory_test (const char *fil) if (ret != 0) { if (errno != ENOENT) - { LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "stat", fil); - return GNUNET_SYSERR; - } - return GNUNET_NO; + return GNUNET_SYSERR; } if (!S_ISDIR (filestat.st_mode)) + { + LOG (GNUNET_ERROR_TYPE_DEBUG, + "A file already exits with the same name %s\n", fil); return GNUNET_NO; - if (ACCESS (fil, R_OK | X_OK) < 0) + } + if (GNUNET_YES == is_readable) + ret = ACCESS (fil, R_OK | X_OK); + else + ret = ACCESS (fil, X_OK); + if (ret < 0) { LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "access", fil); - return GNUNET_SYSERR; + return GNUNET_NO; } return GNUNET_YES; } @@ -546,7 +660,7 @@ GNUNET_DISK_directory_test (const char *fil) * (of a file that exists and that is not a directory). * * @param fil filename to check - * @return GNUNET_YES if yes, GNUNET_NO if not a file, GNUNET_SYSERR if something + * @return #GNUNET_YES if yes, #GNUNET_NO if not a file, #GNUNET_SYSERR if something * else (will print an error message in that case, too). */ int @@ -577,7 +691,7 @@ GNUNET_DISK_file_test (const char *fil) GNUNET_free (rdir); return GNUNET_NO; } - if (ACCESS (rdir, R_OK) < 0) + if (ACCESS (rdir, F_OK) < 0) { LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "access", rdir); GNUNET_free (rdir); @@ -590,15 +704,17 @@ GNUNET_DISK_file_test (const char *fil) /** * Implementation of "mkdir -p" + * * @param dir the directory to create - * @returns GNUNET_OK on success, GNUNET_SYSERR on failure + * @return #GNUNET_OK on success, #GNUNET_SYSERR on failure */ int GNUNET_DISK_directory_create (const char *dir) { char *rdir; - int len; - int pos; + unsigned int len; + unsigned int pos; + unsigned int pos2; int ret = GNUNET_OK; rdir = GNUNET_STRINGS_filename_expand (dir); @@ -628,18 +744,45 @@ GNUNET_DISK_directory_create (const char *dir) pos = 3; /* strlen("C:\\") */ } #endif + /* Check which low level directories already exist */ + pos2 = len; + rdir[len] = DIR_SEPARATOR; + while (pos <= pos2) + { + if (DIR_SEPARATOR == rdir[pos2]) + { + rdir[pos2] = '\0'; + ret = GNUNET_DISK_directory_test (rdir, GNUNET_NO); + if (GNUNET_NO == ret) + { + GNUNET_free (rdir); + return GNUNET_SYSERR; + } + rdir[pos2] = DIR_SEPARATOR; + if (GNUNET_YES == ret) + { + pos2++; + break; + } + } + pos2--; + } + rdir[len] = '\0'; + if (pos < pos2) + pos = pos2; + /* Start creating directories */ while (pos <= len) { if ((rdir[pos] == DIR_SEPARATOR) || (pos == len)) { rdir[pos] = '\0'; - ret = GNUNET_DISK_directory_test (rdir); - if (ret == GNUNET_SYSERR) + ret = GNUNET_DISK_directory_test (rdir, GNUNET_NO); + if (GNUNET_NO == ret) { GNUNET_free (rdir); return GNUNET_SYSERR; } - if (ret == GNUNET_NO) + if (GNUNET_SYSERR == ret) { #ifndef MINGW ret = mkdir (rdir, S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH); /* 755 */ @@ -667,81 +810,100 @@ GNUNET_DISK_directory_create (const char *dir) /** - * Create the directory structure for storing - * a file. + * Create the directory structure for storing a file. * * @param filename name of a file in the directory - * @returns GNUNET_OK on success, - * GNUNET_SYSERR on failure, - * GNUNET_NO if the directory + * @returns #GNUNET_OK on success, + * #GNUNET_SYSERR on failure, + * #GNUNET_NO if the directory * exists but is not writeable for us */ int GNUNET_DISK_directory_create_for_file (const char *filename) { char *rdir; - int len; + size_t len; int ret; + int eno; rdir = GNUNET_STRINGS_filename_expand (filename); - if (rdir == NULL) + if (NULL == rdir) + { + errno = EINVAL; return GNUNET_SYSERR; + } len = strlen (rdir); while ((len > 0) && (rdir[len] != DIR_SEPARATOR)) len--; rdir[len] = '\0'; + /* The empty path is invalid and in this case refers to / */ + if (0 == len) + { + GNUNET_free (rdir); + rdir = GNUNET_strdup ("/"); + } ret = GNUNET_DISK_directory_create (rdir); - if ((ret == GNUNET_OK) && (0 != ACCESS (rdir, W_OK))) + if ((GNUNET_OK == ret) && (0 != ACCESS (rdir, W_OK))) ret = GNUNET_NO; + eno = errno; GNUNET_free (rdir); + errno = eno; return ret; } /** * Read the contents of a binary file into a buffer. + * * @param h handle to an open file * @param result the buffer to write the result to * @param len the maximum number of bytes to read - * @return the number of bytes read on success, GNUNET_SYSERR on failure + * @return the number of bytes read on success, #GNUNET_SYSERR on failure */ ssize_t -GNUNET_DISK_file_read (const struct GNUNET_DISK_FileHandle * h, void *result, +GNUNET_DISK_file_read (const struct GNUNET_DISK_FileHandle *h, + void *result, size_t len) { - if (h == NULL) + if (NULL == h) { errno = EINVAL; return GNUNET_SYSERR; } #ifdef MINGW - DWORD bytesRead; + DWORD bytes_read; - if (h->type != GNUNET_PIPE) + if (h->type == GNUNET_DISK_HANLDE_TYPE_FILE) { - if (!ReadFile (h->h, result, len, &bytesRead, NULL)) + if (!ReadFile (h->h, result, len, &bytes_read, NULL)) { SetErrnoFromWinError (GetLastError ()); return GNUNET_SYSERR; } } - else + else if (h->type == GNUNET_DISK_HANLDE_TYPE_PIPE) { - if (!ReadFile (h->h, result, len, &bytesRead, h->oOverlapRead)) + if (!ReadFile (h->h, result, len, &bytes_read, h->oOverlapRead)) { if (GetLastError () != ERROR_IO_PENDING) { - LOG (GNUNET_ERROR_TYPE_DEBUG, "Error reading from pipe: %u\n", GetLastError ()); + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Error reading from pipe: %u\n", + GetLastError ()); SetErrnoFromWinError (GetLastError ()); return GNUNET_SYSERR; } LOG (GNUNET_ERROR_TYPE_DEBUG, "Will get overlapped result\n"); - GetOverlappedResult (h->h, h->oOverlapRead, &bytesRead, TRUE); + GetOverlappedResult (h->h, h->oOverlapRead, &bytes_read, TRUE); } - LOG (GNUNET_ERROR_TYPE_DEBUG, "Read %u bytes from pipe\n", bytesRead); + LOG (GNUNET_ERROR_TYPE_DEBUG, "Read %u bytes from pipe\n", bytes_read); } - return bytesRead; + else + { + bytes_read = 0; + } + return bytes_read; #else return read (h->fd, result, len); #endif @@ -756,32 +918,33 @@ GNUNET_DISK_file_read (const struct GNUNET_DISK_FileHandle * h, void *result, * @param h handle to an open file * @param result the buffer to write the result to * @param len the maximum number of bytes to read - * @return the number of bytes read on success, GNUNET_SYSERR on failure + * @return the number of bytes read on success, #GNUNET_SYSERR on failure */ ssize_t -GNUNET_DISK_file_read_non_blocking (const struct GNUNET_DISK_FileHandle * h, - void *result, size_t len) +GNUNET_DISK_file_read_non_blocking (const struct GNUNET_DISK_FileHandle *h, + void *result, + size_t len) { - if (h == NULL) + if (NULL == h) { errno = EINVAL; return GNUNET_SYSERR; } #ifdef MINGW - DWORD bytesRead; + DWORD bytes_read; - if (h->type != GNUNET_PIPE) + if (h->type == GNUNET_DISK_HANLDE_TYPE_FILE) { - if (!ReadFile (h->h, result, len, &bytesRead, NULL)) + if (!ReadFile (h->h, result, len, &bytes_read, NULL)) { SetErrnoFromWinError (GetLastError ()); return GNUNET_SYSERR; } } - else + else if (h->type == GNUNET_DISK_HANLDE_TYPE_PIPE) { - if (!ReadFile (h->h, result, len, &bytesRead, h->oOverlapRead)) + if (!ReadFile (h->h, result, len, &bytes_read, h->oOverlapRead)) { if (GetLastError () != ERROR_IO_PENDING) { @@ -798,9 +961,15 @@ GNUNET_DISK_file_read_non_blocking (const struct GNUNET_DISK_FileHandle * h, return GNUNET_SYSERR; } } - LOG (GNUNET_ERROR_TYPE_DEBUG, "Read %u bytes\n", bytesRead); + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Read %u bytes\n", + bytes_read); } - return bytesRead; + else + { + bytes_read = 0; + } + return bytes_read; #else int flags; ssize_t ret; @@ -808,10 +977,14 @@ GNUNET_DISK_file_read_non_blocking (const struct GNUNET_DISK_FileHandle * h, /* set to non-blocking, read, then set back */ flags = fcntl (h->fd, F_GETFL); if (0 == (flags & O_NONBLOCK)) - fcntl (h->fd, F_SETFL, flags | O_NONBLOCK); + (void) fcntl (h->fd, F_SETFL, flags | O_NONBLOCK); ret = read (h->fd, result, len); if (0 == (flags & O_NONBLOCK)) - fcntl (h->fd, F_SETFL, flags); + { + int eno = errno; + (void) fcntl (h->fd, F_SETFL, flags); + errno = eno; + } return ret; #endif } @@ -823,56 +996,63 @@ GNUNET_DISK_file_read_non_blocking (const struct GNUNET_DISK_FileHandle * h, * @param fn file name * @param result the buffer to write the result to * @param len the maximum number of bytes to read - * @return number of bytes read, GNUNET_SYSERR on failure + * @return number of bytes read, #GNUNET_SYSERR on failure */ ssize_t -GNUNET_DISK_fn_read (const char *fn, void *result, size_t len) +GNUNET_DISK_fn_read (const char *fn, + void *result, + size_t len) { struct GNUNET_DISK_FileHandle *fh; ssize_t ret; + int eno; - fh = GNUNET_DISK_file_open (fn, GNUNET_DISK_OPEN_READ, GNUNET_DISK_PERM_NONE); - if (!fh) + fh = GNUNET_DISK_file_open (fn, + GNUNET_DISK_OPEN_READ, + GNUNET_DISK_PERM_NONE); + if (NULL == fh) return GNUNET_SYSERR; ret = GNUNET_DISK_file_read (fh, result, len); + eno = errno; GNUNET_assert (GNUNET_OK == GNUNET_DISK_file_close (fh)); - + errno = eno; return ret; } /** * Write a buffer to a file. + * * @param h handle to open file * @param buffer the data to write * @param n number of bytes to write - * @return number of bytes written on success, GNUNET_SYSERR on error + * @return number of bytes written on success, #GNUNET_SYSERR on error */ ssize_t GNUNET_DISK_file_write (const struct GNUNET_DISK_FileHandle * h, const void *buffer, size_t n) { - if (h == NULL) + if (NULL == h) { errno = EINVAL; return GNUNET_SYSERR; } #ifdef MINGW - DWORD bytesWritten; + DWORD bytes_written; - if (h->type != GNUNET_PIPE) + if (h->type == GNUNET_DISK_HANLDE_TYPE_FILE) { - if (!WriteFile (h->h, buffer, n, &bytesWritten, NULL)) + if (!WriteFile (h->h, buffer, n, &bytes_written, NULL)) { SetErrnoFromWinError (GetLastError ()); return GNUNET_SYSERR; } } - else + else if (h->type == GNUNET_DISK_HANLDE_TYPE_PIPE) { LOG (GNUNET_ERROR_TYPE_DEBUG, "It is a pipe trying to write %u bytes\n", n); - if (!WriteFile (h->h, buffer, n, &bytesWritten, h->oOverlapWrite)) + if (!WriteFile (h->h, buffer, n, &bytes_written, h->oOverlapWrite)) { if (GetLastError () != ERROR_IO_PENDING) { @@ -882,7 +1062,7 @@ GNUNET_DISK_file_write (const struct GNUNET_DISK_FileHandle * h, return GNUNET_SYSERR; } LOG (GNUNET_ERROR_TYPE_DEBUG, "Will get overlapped result\n"); - if (!GetOverlappedResult (h->h, h->oOverlapWrite, &bytesWritten, TRUE)) + if (!GetOverlappedResult (h->h, h->oOverlapWrite, &bytes_written, TRUE)) { SetErrnoFromWinError (GetLastError ()); LOG (GNUNET_ERROR_TYPE_DEBUG, @@ -904,21 +1084,25 @@ GNUNET_DISK_file_write (const struct GNUNET_DISK_FileHandle * h, { LOG (GNUNET_ERROR_TYPE_DEBUG, "Wrote %u bytes (ovr says %u), picking the greatest\n", - bytesWritten, ovr); + bytes_written, ovr); } } - if (bytesWritten == 0) + if (bytes_written == 0) { if (n > 0) { - LOG (GNUNET_ERROR_TYPE_DEBUG, "Wrote %u bytes, returning -1 with EAGAIN\n", bytesWritten); + LOG (GNUNET_ERROR_TYPE_DEBUG, "Wrote %u bytes, returning -1 with EAGAIN\n", bytes_written); errno = EAGAIN; return GNUNET_SYSERR; } } - LOG (GNUNET_ERROR_TYPE_DEBUG, "Wrote %u bytes\n", bytesWritten); + LOG (GNUNET_ERROR_TYPE_DEBUG, "Wrote %u bytes\n", bytes_written); + } + else + { + bytes_written = 0; } - return bytesWritten; + return bytes_written; #else return write (h->fd, buffer, n); #endif @@ -927,37 +1111,39 @@ GNUNET_DISK_file_write (const struct GNUNET_DISK_FileHandle * h, /** * Write a buffer to a file, blocking, if necessary. + * * @param h handle to open file * @param buffer the data to write * @param n number of bytes to write - * @return number of bytes written on success, GNUNET_SYSERR on error + * @return number of bytes written on success, #GNUNET_SYSERR on error */ ssize_t GNUNET_DISK_file_write_blocking (const struct GNUNET_DISK_FileHandle * h, - const void *buffer, size_t n) + const void *buffer, + size_t n) { - if (h == NULL) + if (NULL == h) { errno = EINVAL; return GNUNET_SYSERR; } #ifdef MINGW - DWORD bytesWritten; + DWORD bytes_written; /* We do a non-overlapped write, which is as blocking as it gets */ LOG (GNUNET_ERROR_TYPE_DEBUG, "Writing %u bytes\n", n); - if (!WriteFile (h->h, buffer, n, &bytesWritten, NULL)) + if (!WriteFile (h->h, buffer, n, &bytes_written, NULL)) { SetErrnoFromWinError (GetLastError ()); LOG (GNUNET_ERROR_TYPE_DEBUG, "Error writing to pipe: %u\n", GetLastError ()); return GNUNET_SYSERR; } - if (bytesWritten == 0 && n > 0) + if (bytes_written == 0 && n > 0) { LOG (GNUNET_ERROR_TYPE_DEBUG, "Waiting for pipe to clean\n"); WaitForSingleObject (h->h, INFINITE); - if (!WriteFile (h->h, buffer, n, &bytesWritten, NULL)) + if (!WriteFile (h->h, buffer, n, &bytes_written, NULL)) { SetErrnoFromWinError (GetLastError ()); LOG (GNUNET_ERROR_TYPE_DEBUG, "Error writing to pipe: %u\n", @@ -965,8 +1151,10 @@ GNUNET_DISK_file_write_blocking (const struct GNUNET_DISK_FileHandle * h, return GNUNET_SYSERR; } } - LOG (GNUNET_ERROR_TYPE_DEBUG, "Wrote %u bytes\n", bytesWritten); - return bytesWritten; + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Wrote %u bytes\n", + bytes_written); + return bytes_written; #else int flags; ssize_t ret; @@ -974,10 +1162,10 @@ GNUNET_DISK_file_write_blocking (const struct GNUNET_DISK_FileHandle * h, /* set to blocking, write, then set back */ flags = fcntl (h->fd, F_GETFL); if (0 != (flags & O_NONBLOCK)) - fcntl (h->fd, F_SETFL, flags - O_NONBLOCK); + (void) fcntl (h->fd, F_SETFL, flags - O_NONBLOCK); ret = write (h->fd, buffer, n); if (0 == (flags & O_NONBLOCK)) - fcntl (h->fd, F_SETFL, flags); + (void) fcntl (h->fd, F_SETFL, flags); return ret; #endif } @@ -991,10 +1179,12 @@ GNUNET_DISK_file_write_blocking (const struct GNUNET_DISK_FileHandle * h, * @param buffer the data to write * @param n number of bytes to write * @param mode file permissions - * @return number of bytes written on success, GNUNET_SYSERR on error + * @return number of bytes written on success, #GNUNET_SYSERR on error */ ssize_t -GNUNET_DISK_fn_write (const char *fn, const void *buffer, size_t n, +GNUNET_DISK_fn_write (const char *fn, + const void *buffer, + size_t n, enum GNUNET_DISK_AccessPermissions mode) { struct GNUNET_DISK_FileHandle *fh; @@ -1003,7 +1193,7 @@ GNUNET_DISK_fn_write (const char *fn, const void *buffer, size_t n, fh = GNUNET_DISK_file_open (fn, GNUNET_DISK_OPEN_WRITE | GNUNET_DISK_OPEN_TRUNCATE | GNUNET_DISK_OPEN_CREATE, mode); - if (!fh) + if (! fh) return GNUNET_SYSERR; ret = GNUNET_DISK_file_write (fh, buffer, n); GNUNET_assert (GNUNET_OK == GNUNET_DISK_file_close (fh)); @@ -1014,15 +1204,15 @@ GNUNET_DISK_fn_write (const char *fn, const void *buffer, size_t n, /** * Scan a directory for files. * - * @param dirName the name of the directory + * @param dir_name the name of the directory * @param callback the method to call for each file, * can be NULL, in that case, we only count - * @param callback_cls closure for callback - * @return the number of files found, GNUNET_SYSERR on error or - * ieration aborted by callback returning GNUNET_SYSERR + * @param callback_cls closure for @a callback + * @return the number of files found, #GNUNET_SYSERR on error or + * ieration aborted by callback returning #GNUNET_SYSERR */ int -GNUNET_DISK_directory_scan (const char *dirName, +GNUNET_DISK_directory_scan (const char *dir_name, GNUNET_FileNameCallback callback, void *callback_cls) { @@ -1030,68 +1220,86 @@ GNUNET_DISK_directory_scan (const char *dirName, struct dirent *finfo; struct stat istat; int count = 0; + int ret; char *name; char *dname; unsigned int name_len; unsigned int n_size; - GNUNET_assert (dirName != NULL); - dname = GNUNET_STRINGS_filename_expand (dirName); - if (dname == NULL) + GNUNET_assert (NULL != dir_name); + dname = GNUNET_STRINGS_filename_expand (dir_name); + if (NULL == dname) return GNUNET_SYSERR; - while ((strlen (dname) > 0) && (dname[strlen (dname) - 1] == DIR_SEPARATOR)) + while ( (strlen (dname) > 0) && + (dname[strlen (dname) - 1] == DIR_SEPARATOR) ) dname[strlen (dname) - 1] = '\0'; if (0 != STAT (dname, &istat)) { - LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "stat", dname); + LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, + "stat", + dname); GNUNET_free (dname); return GNUNET_SYSERR; } - if (!S_ISDIR (istat.st_mode)) + if (! S_ISDIR (istat.st_mode)) { - LOG (GNUNET_ERROR_TYPE_WARNING, _("Expected `%s' to be a directory!\n"), - dirName); + LOG (GNUNET_ERROR_TYPE_WARNING, + _("Expected `%s' to be a directory!\n"), + dir_name); GNUNET_free (dname); return GNUNET_SYSERR; } errno = 0; dinfo = OPENDIR (dname); - if ((errno == EACCES) || (dinfo == NULL)) + if ( (EACCES == errno) || + (NULL == dinfo) ) { - LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "opendir", dname); - if (dinfo != NULL) + LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, + "opendir", + dname); + if (NULL != dinfo) CLOSEDIR (dinfo); GNUNET_free (dname); return GNUNET_SYSERR; } name_len = 256; - n_size = strlen (dname) + name_len + 2; + n_size = strlen (dname) + name_len + strlen (DIR_SEPARATOR_STR) + 1; name = GNUNET_malloc (n_size); - while ((finfo = READDIR (dinfo)) != NULL) + while (NULL != (finfo = READDIR (dinfo))) { - if ((0 == strcmp (finfo->d_name, ".")) || - (0 == strcmp (finfo->d_name, ".."))) + if ( (0 == strcmp (finfo->d_name, ".")) || + (0 == strcmp (finfo->d_name, "..")) ) continue; - if (callback != NULL) + if (NULL != callback) { if (name_len < strlen (finfo->d_name)) { GNUNET_free (name); name_len = strlen (finfo->d_name); - n_size = strlen (dname) + name_len + 2; + n_size = strlen (dname) + name_len + strlen (DIR_SEPARATOR_STR) + 1; name = GNUNET_malloc (n_size); } /* dname can end in "/" only if dname == "/"; * if dname does not end in "/", we need to add * a "/" (otherwise, we must not!) */ - GNUNET_snprintf (name, n_size, "%s%s%s", dname, - (strcmp (dname, DIR_SEPARATOR_STR) == - 0) ? "" : DIR_SEPARATOR_STR, finfo->d_name); - if (GNUNET_OK != callback (callback_cls, name)) + GNUNET_snprintf (name, + n_size, + "%s%s%s", + dname, + (0 == strcmp (dname, + DIR_SEPARATOR_STR)) + ? "" + : DIR_SEPARATOR_STR, + finfo->d_name); + ret = callback (callback_cls, + name); + if (GNUNET_OK != ret) { CLOSEDIR (dinfo); GNUNET_free (name); GNUNET_free (dname); + if (GNUNET_NO == ret) + return count; return GNUNET_SYSERR; } } @@ -1104,155 +1312,17 @@ GNUNET_DISK_directory_scan (const char *dirName, } -/** - * Opaque handle used for iterating over a directory. - */ -struct GNUNET_DISK_DirectoryIterator -{ - - /** - * Function to call on directory entries. - */ - GNUNET_DISK_DirectoryIteratorCallback callback; - - /** - * Closure for callback. - */ - void *callback_cls; - - /** - * Reference to directory. - */ - DIR *directory; - - /** - * Directory name. - */ - char *dirname; - - /** - * Next filename to process. - */ - char *next_name; - - /** - * Our priority. - */ - enum GNUNET_SCHEDULER_Priority priority; - -}; - - -/** - * Task used by the directory iterator. - */ -static void -directory_iterator_task (void *cls, - const struct GNUNET_SCHEDULER_TaskContext *tc) -{ - struct GNUNET_DISK_DirectoryIterator *iter = cls; - char *name; - - name = iter->next_name; - GNUNET_assert (name != NULL); - iter->next_name = NULL; - iter->callback (iter->callback_cls, iter, name, iter->dirname); - GNUNET_free (name); -} - - -/** - * This function must be called during the DiskIteratorCallback - * (exactly once) to schedule the task to process the next - * filename in the directory (if there is one). - * - * @param iter opaque handle for the iterator - * @param can set to GNUNET_YES to terminate the iteration early - * @return GNUNET_YES if iteration will continue, - * GNUNET_NO if this was the last entry (and iteration is complete), - * GNUNET_SYSERR if abort was YES - */ -int -GNUNET_DISK_directory_iterator_next (struct GNUNET_DISK_DirectoryIterator *iter, - int can) -{ - struct dirent *finfo; - - GNUNET_assert (iter->next_name == NULL); - if (can == GNUNET_YES) - { - CLOSEDIR (iter->directory); - GNUNET_free (iter->dirname); - GNUNET_free (iter); - return GNUNET_SYSERR; - } - while (NULL != (finfo = READDIR (iter->directory))) - { - if ((0 == strcmp (finfo->d_name, ".")) || - (0 == strcmp (finfo->d_name, ".."))) - continue; - GNUNET_asprintf (&iter->next_name, "%s%s%s", iter->dirname, - DIR_SEPARATOR_STR, finfo->d_name); - break; - } - if (finfo == NULL) - { - GNUNET_DISK_directory_iterator_next (iter, GNUNET_YES); - return GNUNET_NO; - } - GNUNET_SCHEDULER_add_with_priority (iter->priority, &directory_iterator_task, - iter); - return GNUNET_YES; -} - - -/** - * Scan a directory for files using the scheduler to run a task for - * each entry. The name of the directory must be expanded first (!). - * If a scheduler does not need to be used, GNUNET_DISK_directory_scan - * may provide a simpler API. - * - * @param prio priority to use - * @param dirName the name of the directory - * @param callback the method to call for each file - * @param callback_cls closure for callback - * @return GNUNET_YES if directory is not empty and 'callback' - * will be called later, GNUNET_NO otherwise, GNUNET_SYSERR on error. - */ -int -GNUNET_DISK_directory_iterator_start (enum GNUNET_SCHEDULER_Priority prio, - const char *dirName, - GNUNET_DISK_DirectoryIteratorCallback - callback, void *callback_cls) -{ - struct GNUNET_DISK_DirectoryIterator *di; - - di = GNUNET_malloc (sizeof (struct GNUNET_DISK_DirectoryIterator)); - di->callback = callback; - di->callback_cls = callback_cls; - di->directory = OPENDIR (dirName); - if (di->directory == NULL) - { - GNUNET_free (di); - callback (callback_cls, NULL, NULL, NULL); - return GNUNET_SYSERR; - } - di->dirname = GNUNET_strdup (dirName); - di->priority = prio; - return GNUNET_DISK_directory_iterator_next (di, GNUNET_NO); -} - - /** * Function that removes the given directory by calling - * "GNUNET_DISK_directory_remove". + * #GNUNET_DISK_directory_remove(). * * @param unused not used * @param fn directory to remove - * @return GNUNET_OK + * @return #GNUNET_OK */ static int -remove_helper (void *unused, const char *fn) +remove_helper (void *unused, + const char *fn) { (void) GNUNET_DISK_directory_remove (fn); return GNUNET_OK; @@ -1260,38 +1330,51 @@ remove_helper (void *unused, const char *fn) /** - * Remove all files in a directory (rm -rf). Call with + * Remove all files in a directory (rm -r). Call with * caution. * - * - * @param fileName the file to remove - * @return GNUNET_OK on success, GNUNET_SYSERR on error + * @param filename the file to remove + * @return #GNUNET_OK on success, #GNUNET_SYSERR on error */ int -GNUNET_DISK_directory_remove (const char *fileName) +GNUNET_DISK_directory_remove (const char *filename) { struct stat istat; - if (0 != LSTAT (fileName, &istat)) + if (NULL == filename) + { + GNUNET_break (0); + return GNUNET_SYSERR; + } + if (0 != LSTAT (filename, &istat)) return GNUNET_NO; /* file may not exist... */ - CHMOD (fileName, S_IWUSR | S_IRUSR | S_IXUSR); - if (UNLINK (fileName) == 0) + (void) CHMOD (filename, + S_IWUSR | S_IRUSR | S_IXUSR); + if (0 == UNLINK (filename)) return GNUNET_OK; - if ((errno != EISDIR) && - /* EISDIR is not sufficient in all cases, e.g. - * sticky /tmp directory may result in EPERM on BSD. - * So we also explicitly check "isDirectory" */ - (GNUNET_YES != GNUNET_DISK_directory_test (fileName))) - { - LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "rmdir", fileName); + if ( (errno != EISDIR) && + /* EISDIR is not sufficient in all cases, e.g. + * sticky /tmp directory may result in EPERM on BSD. + * So we also explicitly check "isDirectory" */ + (GNUNET_YES != + GNUNET_DISK_directory_test (filename, + GNUNET_YES)) ) + { + LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, + "rmdir", + filename); return GNUNET_SYSERR; } if (GNUNET_SYSERR == - GNUNET_DISK_directory_scan (fileName, &remove_helper, NULL)) + GNUNET_DISK_directory_scan (filename, + &remove_helper, + NULL)) return GNUNET_SYSERR; - if (0 != RMDIR (fileName)) + if (0 != RMDIR (filename)) { - LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "rmdir", fileName); + LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, + "rmdir", + filename); return GNUNET_SYSERR; } return GNUNET_OK; @@ -1303,10 +1386,11 @@ GNUNET_DISK_directory_remove (const char *fileName) * * @param src file to copy * @param dst destination file name - * @return GNUNET_OK on success, GNUNET_SYSERR on error + * @return #GNUNET_OK on success, #GNUNET_SYSERR on error */ int -GNUNET_DISK_file_copy (const char *src, const char *dst) +GNUNET_DISK_file_copy (const char *src, + const char *dst) { char *buf; uint64_t pos; @@ -1369,8 +1453,7 @@ GNUNET_DISK_filename_canonicalize (char *fn) char *idx; char c; - idx = fn; - while (*idx) + for (idx = fn; *idx; idx++) { c = *idx; @@ -1379,8 +1462,6 @@ GNUNET_DISK_filename_canonicalize (char *fn) { *idx = '_'; } - - idx++; } } @@ -1391,24 +1472,33 @@ GNUNET_DISK_filename_canonicalize (char *fn) * * @param filename name of file to change the owner of * @param user name of the new owner - * @return GNUNET_OK on success, GNUNET_SYSERR on failure + * @return #GNUNET_OK on success, #GNUNET_SYSERR on failure */ int -GNUNET_DISK_file_change_owner (const char *filename, const char *user) +GNUNET_DISK_file_change_owner (const char *filename, + const char *user) { #ifndef MINGW struct passwd *pws; pws = getpwnam (user); - if (pws == NULL) + if (NULL == pws) { LOG (GNUNET_ERROR_TYPE_ERROR, - _("Cannot obtain information about user `%s': %s\n"), user, + _("Cannot obtain information about user `%s': %s\n"), + user, STRERROR (errno)); return GNUNET_SYSERR; } - if (0 != chown (filename, pws->pw_uid, pws->pw_gid)) - LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "chown", filename); + if (0 != chown (filename, + pws->pw_uid, + pws->pw_gid)) + { + LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, + "chown", + filename); + return GNUNET_SYSERR; + } #endif return GNUNET_OK; } @@ -1416,15 +1506,18 @@ GNUNET_DISK_file_change_owner (const char *filename, const char *user) /** * Lock a part of a file + * * @param fh file handle - * @param lockStart absolute position from where to lock - * @param lockEnd absolute position until where to lock - * @param excl GNUNET_YES for an exclusive lock - * @return GNUNET_OK on success, GNUNET_SYSERR on error + * @param lock_start absolute position from where to lock + * @param lock_end absolute position until where to lock + * @param excl #GNUNET_YES for an exclusive lock + * @return #GNUNET_OK on success, #GNUNET_SYSERR on error */ int -GNUNET_DISK_file_lock (struct GNUNET_DISK_FileHandle *fh, OFF_T lockStart, - OFF_T lockEnd, int excl) +GNUNET_DISK_file_lock (struct GNUNET_DISK_FileHandle *fh, + off_t lock_start, + off_t lock_end, + int excl) { if (fh == NULL) { @@ -1438,20 +1531,20 @@ GNUNET_DISK_file_lock (struct GNUNET_DISK_FileHandle *fh, OFF_T lockStart, memset (&fl, 0, sizeof (struct flock)); fl.l_type = excl ? F_WRLCK : F_RDLCK; fl.l_whence = SEEK_SET; - fl.l_start = lockStart; - fl.l_len = lockEnd; + fl.l_start = lock_start; + fl.l_len = lock_end; return fcntl (fh->fd, F_SETLK, &fl) != 0 ? GNUNET_SYSERR : GNUNET_OK; #else OVERLAPPED o; - OFF_T diff = lockEnd - lockStart; + off_t diff = lock_end - lock_start; DWORD diff_low, diff_high; diff_low = (DWORD) (diff & 0xFFFFFFFF); diff_high = (DWORD) ((diff >> (sizeof (DWORD) * 8)) & 0xFFFFFFFF); memset (&o, 0, sizeof (OVERLAPPED)); - o.Offset = (DWORD) (lockStart & 0xFFFFFFFF);; - o.OffsetHigh = (DWORD) (((lockStart & ~0xFFFFFFFF) >> (sizeof (DWORD) * 8)) & 0xFFFFFFFF); + o.Offset = (DWORD) (lock_start & 0xFFFFFFFF);; + o.OffsetHigh = (DWORD) (((lock_start & ~0xFFFFFFFF) >> (sizeof (DWORD) * 8)) & 0xFFFFFFFF); if (!LockFileEx (fh->h, (excl ? LOCKFILE_EXCLUSIVE_LOCK : 0) | LOCKFILE_FAIL_IMMEDIATELY, @@ -1468,14 +1561,16 @@ GNUNET_DISK_file_lock (struct GNUNET_DISK_FileHandle *fh, OFF_T lockStart, /** * Unlock a part of a file + * * @param fh file handle - * @param unlockStart absolute position from where to unlock - * @param unlockEnd absolute position until where to unlock - * @return GNUNET_OK on success, GNUNET_SYSERR on error + * @param unlock_start absolute position from where to unlock + * @param unlock_end absolute position until where to unlock + * @return #GNUNET_OK on success, #GNUNET_SYSERR on error */ int -GNUNET_DISK_file_unlock (struct GNUNET_DISK_FileHandle *fh, OFF_T unlockStart, - OFF_T unlockEnd) +GNUNET_DISK_file_unlock (struct GNUNET_DISK_FileHandle *fh, + off_t unlock_start, + off_t unlock_end) { if (fh == NULL) { @@ -1489,20 +1584,20 @@ GNUNET_DISK_file_unlock (struct GNUNET_DISK_FileHandle *fh, OFF_T unlockStart, memset (&fl, 0, sizeof (struct flock)); fl.l_type = F_UNLCK; fl.l_whence = SEEK_SET; - fl.l_start = unlockStart; - fl.l_len = unlockEnd; + fl.l_start = unlock_start; + fl.l_len = unlock_end; return fcntl (fh->fd, F_SETLK, &fl) != 0 ? GNUNET_SYSERR : GNUNET_OK; #else OVERLAPPED o; - OFF_T diff = unlockEnd - unlockStart; + off_t diff = unlock_end - unlock_start; DWORD diff_low, diff_high; diff_low = (DWORD) (diff & 0xFFFFFFFF); diff_high = (DWORD) ((diff >> (sizeof (DWORD) * 8)) & 0xFFFFFFFF); memset (&o, 0, sizeof (OVERLAPPED)); - o.Offset = (DWORD) (unlockStart & 0xFFFFFFFF);; - o.OffsetHigh = (DWORD) (((unlockStart & ~0xFFFFFFFF) >> (sizeof (DWORD) * 8)) & 0xFFFFFFFF); + o.Offset = (DWORD) (unlock_start & 0xFFFFFFFF);; + o.OffsetHigh = (DWORD) (((unlock_start & ~0xFFFFFFFF) >> (sizeof (DWORD) * 8)) & 0xFFFFFFFF); if (!UnlockFileEx (fh->h, 0, diff_low, diff_high, &o)) { @@ -1523,12 +1618,13 @@ GNUNET_DISK_file_unlock (struct GNUNET_DISK_FileHandle *fh, OFF_T unlockStart, * @param fn file name to be opened * @param flags opening flags, a combination of GNUNET_DISK_OPEN_xxx bit flags * @param perm permissions for the newly created file, use - * GNUNET_DISK_PERM_USER_NONE if a file could not be created by this + * #GNUNET_DISK_PERM_NONE if a file could not be created by this * call (because of flags) * @return IO handle on success, NULL on error */ struct GNUNET_DISK_FileHandle * -GNUNET_DISK_file_open (const char *fn, enum GNUNET_DISK_OpenFlags flags, +GNUNET_DISK_file_open (const char *fn, + enum GNUNET_DISK_OpenFlags flags, enum GNUNET_DISK_AccessPermissions perm) { char *expfn; @@ -1575,7 +1671,11 @@ GNUNET_DISK_file_open (const char *fn, enum GNUNET_DISK_OpenFlags flags, mode = translate_unix_perms (perm); } - fd = open (expfn, oflags | O_LARGEFILE, mode); + fd = open (expfn, oflags +#if O_CLOEXEC + | O_CLOEXEC +#endif + | O_LARGEFILE, mode); if (fd == -1) { if (0 == (flags & GNUNET_DISK_OPEN_FAILIFEXISTS)) @@ -1625,9 +1725,12 @@ GNUNET_DISK_file_open (const char *fn, enum GNUNET_DISK_OpenFlags flags, h = INVALID_HANDLE_VALUE; if (h == INVALID_HANDLE_VALUE) { + int err; SetErrnoFromWinError (GetLastError ()); - LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "open", expfn); + err = errno; + LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_INFO, "open", expfn); GNUNET_free (expfn); + errno = err; return NULL; } @@ -1642,10 +1745,10 @@ GNUNET_DISK_file_open (const char *fn, enum GNUNET_DISK_OpenFlags flags, } #endif - ret = GNUNET_malloc (sizeof (struct GNUNET_DISK_FileHandle)); + ret = GNUNET_new (struct GNUNET_DISK_FileHandle); #ifdef MINGW ret->h = h; - ret->type = GNUNET_DISK_FILE; + ret->type = GNUNET_DISK_HANLDE_TYPE_FILE; #else ret->fd = fd; #endif @@ -1655,111 +1758,174 @@ GNUNET_DISK_file_open (const char *fn, enum GNUNET_DISK_OpenFlags flags, /** - * Close an open file + * Close an open file. + * * @param h file handle - * @return GNUNET_OK on success, GNUNET_SYSERR otherwise + * @return #GNUNET_OK on success, #GNUNET_SYSERR otherwise */ int GNUNET_DISK_file_close (struct GNUNET_DISK_FileHandle *h) { + int ret; if (h == NULL) { errno = EINVAL; return GNUNET_SYSERR; } + ret = GNUNET_OK; + #if MINGW - if (!CloseHandle (h->h)) + if (! CloseHandle (h->h)) { SetErrnoFromWinError (GetLastError ()); LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING, "close"); + ret = GNUNET_SYSERR; + } + if (h->oOverlapRead) + { + if (! CloseHandle (h->oOverlapRead->hEvent)) + { + SetErrnoFromWinError (GetLastError ()); + LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING, "close"); + ret = GNUNET_SYSERR; + } GNUNET_free (h->oOverlapRead); + } + if (h->oOverlapWrite) + { + if (!CloseHandle (h->oOverlapWrite->hEvent)) + { + SetErrnoFromWinError (GetLastError ()); + LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING, "close"); + ret = GNUNET_SYSERR; + } GNUNET_free (h->oOverlapWrite); - GNUNET_free (h); - return GNUNET_SYSERR; } #else if (close (h->fd) != 0) { LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING, "close"); - GNUNET_free (h); - return GNUNET_SYSERR; + ret = GNUNET_SYSERR; } #endif GNUNET_free (h); - return GNUNET_OK; + return ret; } +#ifdef WINDOWS /** - * Construct full path to a file inside of the private - * directory used by GNUnet. Also creates the corresponding - * directory. If the resulting name is supposed to be - * a directory, end the last argument in '/' (or pass - * DIR_SEPARATOR_STR as the last argument before NULL). + * Get a GNUnet file handle from a W32 handle. * - * @param cfg configuration to use (determines HOME) - * @param serviceName name of the service - * @param ... is NULL-terminated list of - * path components to append to the - * private directory name. - * @return the constructed filename + * @param handle native handle + * @return GNUnet file handle corresponding to the W32 handle */ -char * -GNUNET_DISK_get_home_filename (const struct GNUNET_CONFIGURATION_Handle *cfg, - const char *serviceName, ...) +struct GNUNET_DISK_FileHandle * +GNUNET_DISK_get_handle_from_w32_handle (HANDLE osfh) { - const char *c; - char *pfx; - char *ret; - va_list ap; - unsigned int needed; - - if (GNUNET_OK != - GNUNET_CONFIGURATION_get_value_filename (cfg, serviceName, "HOME", &pfx)) - return NULL; - if (pfx == NULL) + struct GNUNET_DISK_FileHandle *fh; + DWORD dwret; + enum GNUNET_FILE_Type ftype; + + dwret = GetFileType (osfh); + switch (dwret) { - LOG (GNUNET_ERROR_TYPE_WARNING, - _("No `%s' specified for service `%s' in configuration.\n"), "HOME", - serviceName); + case FILE_TYPE_DISK: + ftype = GNUNET_DISK_HANLDE_TYPE_FILE; + break; + case FILE_TYPE_PIPE: + ftype = GNUNET_DISK_HANLDE_TYPE_PIPE; + break; + case FILE_TYPE_UNKNOWN: + if ( (GetLastError () == NO_ERROR) || + (GetLastError () == ERROR_INVALID_HANDLE) ) + { + if (0 != ResetEvent (osfh)) + ftype = GNUNET_DISK_HANLDE_TYPE_EVENT; + else + return NULL; + } + else + return NULL; + break; + default: return NULL; } - needed = strlen (pfx) + 2; - if ((pfx[strlen (pfx) - 1] != '/') && (pfx[strlen (pfx) - 1] != '\\')) - needed++; - va_start (ap, serviceName); - while (1) - { - c = va_arg (ap, const char *); - if (c == NULL) - break; - needed += strlen (c); - if ((c[strlen (c) - 1] != '/') && (c[strlen (c) - 1] != '\\')) - needed++; - } - va_end (ap); - ret = GNUNET_malloc (needed); - strcpy (ret, pfx); - GNUNET_free (pfx); - va_start (ap, serviceName); - while (1) - { - c = va_arg (ap, const char *); + fh = GNUNET_new (struct GNUNET_DISK_FileHandle); - if (c == NULL) - break; - if ((c[strlen (c) - 1] != '/') && (c[strlen (c) - 1] != '\\')) - strcat (ret, DIR_SEPARATOR_STR); - strcat (ret, c); + fh->h = osfh; + fh->type = ftype; + if (ftype == GNUNET_DISK_HANLDE_TYPE_PIPE) + { + /** + * Note that we can't make it overlapped if it isn't already. + * (ReOpenFile() is only available in 2003/Vista). + * The process that opened this file in the first place (usually a parent + * process, if this is stdin/stdout/stderr) must make it overlapped, + * otherwise we're screwed, as selecting on non-overlapped handle + * will block. + */ + fh->oOverlapRead = GNUNET_new (OVERLAPPED); + fh->oOverlapWrite = GNUNET_new (OVERLAPPED); + fh->oOverlapRead->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL); + fh->oOverlapWrite->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL); } - va_end (ap); - if ((ret[strlen (ret) - 1] != '/') && (ret[strlen (ret) - 1] != '\\')) - (void) GNUNET_DISK_directory_create_for_file (ret); - else - (void) GNUNET_DISK_directory_create (ret); - return ret; + + return fh; +} +#endif + +/** + * Get a handle from a native integer FD. + * + * @param fno native integer file descriptor + * @return file handle corresponding to the descriptor, NULL on error + */ +struct GNUNET_DISK_FileHandle * +GNUNET_DISK_get_handle_from_int_fd (int fno) +{ + struct GNUNET_DISK_FileHandle *fh; + + if ( (((off_t) -1) == lseek (fno, 0, SEEK_CUR)) && + (EBADF == errno) ) + return NULL; /* invalid FD */ + +#ifndef WINDOWS + fh = GNUNET_new (struct GNUNET_DISK_FileHandle); + + fh->fd = fno; +#else + intptr_t osfh; + + osfh = _get_osfhandle (fno); + if (INVALID_HANDLE_VALUE == (HANDLE) osfh) + return NULL; + + fh = GNUNET_DISK_get_handle_from_w32_handle ((HANDLE) osfh); +#endif + + return fh; +} + + +/** + * Get a handle from a native streaming FD. + * + * @param fd native streaming file descriptor + * @return file handle corresponding to the descriptor + */ +struct GNUNET_DISK_FileHandle * +GNUNET_DISK_get_handle_from_native (FILE *fd) +{ + int fno; + + fno = fileno (fd); + if (-1 == fno) + return NULL; + + return GNUNET_DISK_get_handle_from_int_fd (fno); } @@ -1836,7 +2002,7 @@ GNUNET_DISK_file_map (const struct GNUNET_DISK_FileHandle *h, return NULL; } - *m = GNUNET_malloc (sizeof (struct GNUNET_DISK_MapHandle)); + *m = GNUNET_new (struct GNUNET_DISK_MapHandle); (*m)->h = CreateFileMapping (h->h, NULL, protect, 0, 0, NULL); if ((*m)->h == INVALID_HANDLE_VALUE) { @@ -1862,7 +2028,7 @@ GNUNET_DISK_file_map (const struct GNUNET_DISK_FileHandle *h, prot = PROT_READ; if (access & GNUNET_DISK_MAP_TYPE_WRITE) prot |= PROT_WRITE; - *m = GNUNET_malloc (sizeof (struct GNUNET_DISK_MapHandle)); + *m = GNUNET_new (struct GNUNET_DISK_MapHandle); (*m)->addr = mmap (NULL, len, prot, MAP_SHARED, h->fd, 0); GNUNET_assert (NULL != (*m)->addr); if (MAP_FAILED == (*m)->addr) @@ -1938,6 +2104,9 @@ GNUNET_DISK_file_sync (const struct GNUNET_DISK_FileHandle *h) #if WINDOWS +#ifndef PIPE_BUF +#define PIPE_BUF 512 +#endif /* Copyright Bob Byrnes curl.com> http://permalink.gmane.org/gmane.os.cygwin.patches/2121 */ @@ -1956,7 +2125,8 @@ create_selectable_pipe (PHANDLE read_pipe_ptr, PHANDLE write_pipe_ptr, /* Default to error. */ *read_pipe_ptr = *write_pipe_ptr = INVALID_HANDLE_VALUE; - HANDLE read_pipe = INVALID_HANDLE_VALUE, write_pipe = INVALID_HANDLE_VALUE; + HANDLE read_pipe; + HANDLE write_pipe; /* Ensure that there is enough pipe buffer space for atomic writes. */ if (psize < PIPE_BUF) @@ -1982,9 +2152,8 @@ create_selectable_pipe (PHANDLE read_pipe_ptr, PHANDLE write_pipe_ptr, * a waste, since only a single direction is actually used. * It's important to only allow a single instance, to ensure that * the pipe was not created earlier by some other process, even if - * the pid has been reused. We avoid FILE_FLAG_FIRST_PIPE_INSTANCE - * because that is only available for Win2k SP2 and WinXP. */ - read_pipe = CreateNamedPipeA (pipename, PIPE_ACCESS_INBOUND | dwReadMode, PIPE_TYPE_BYTE | PIPE_READMODE_BYTE, 1, /* max instances */ + * the pid has been reused. */ + read_pipe = CreateNamedPipeA (pipename, PIPE_ACCESS_INBOUND | FILE_FLAG_FIRST_PIPE_INSTANCE | dwReadMode, PIPE_TYPE_BYTE | PIPE_READMODE_BYTE, 1, /* max instances */ psize, /* output buffer size */ psize, /* input buffer size */ NMPWAIT_USE_DEFAULT_WAIT, sa_ptr); @@ -2087,19 +2256,22 @@ GNUNET_DISK_pipe (int blocking_read, int blocking_write, int inherit_read, int i fd); #else struct GNUNET_DISK_PipeHandle *p; - struct GNUNET_DISK_FileHandle *fds; BOOL ret; HANDLE tmp_handle; - + int save_errno; - p = GNUNET_malloc (sizeof (struct GNUNET_DISK_PipeHandle) + - 2 * sizeof (struct GNUNET_DISK_FileHandle)); - fds = (struct GNUNET_DISK_FileHandle *) &p[1]; - p->fd[0] = &fds[0]; - p->fd[1] = &fds[1]; + p = GNUNET_new (struct GNUNET_DISK_PipeHandle); + p->fd[0] = GNUNET_new (struct GNUNET_DISK_FileHandle); + p->fd[1] = GNUNET_new (struct GNUNET_DISK_FileHandle); /* All pipes are overlapped. If you want them to block - just * call WriteFile() and ReadFile() with NULL overlapped pointer. + * NOTE: calling with NULL overlapped pointer works only + * for pipes, and doesn't seem to be a documented feature. + * It will NOT work for files, because overlapped files need + * to read offsets from the overlapped structure, regardless. + * Pipes are not seekable, and need no offsets, which is + * probably why it works for them. */ ret = create_selectable_pipe (&p->fd[0]->h, &p->fd[1]->h, NULL, 0, @@ -2107,8 +2279,12 @@ GNUNET_DISK_pipe (int blocking_read, int blocking_write, int inherit_read, int i FILE_FLAG_OVERLAPPED); if (!ret) { - GNUNET_free (p); SetErrnoFromWinError (GetLastError ()); + save_errno = errno; + GNUNET_free (p->fd[0]); + GNUNET_free (p->fd[1]); + GNUNET_free (p); + errno = save_errno; return NULL; } if (!DuplicateHandle @@ -2116,9 +2292,13 @@ GNUNET_DISK_pipe (int blocking_read, int blocking_write, int inherit_read, int i inherit_read == GNUNET_YES ? TRUE : FALSE, DUPLICATE_SAME_ACCESS)) { SetErrnoFromWinError (GetLastError ()); + save_errno = errno; CloseHandle (p->fd[0]->h); CloseHandle (p->fd[1]->h); + GNUNET_free (p->fd[0]); + GNUNET_free (p->fd[1]); GNUNET_free (p); + errno = save_errno; return NULL; } CloseHandle (p->fd[0]->h); @@ -2129,21 +2309,25 @@ GNUNET_DISK_pipe (int blocking_read, int blocking_write, int inherit_read, int i inherit_write == GNUNET_YES ? TRUE : FALSE, DUPLICATE_SAME_ACCESS)) { SetErrnoFromWinError (GetLastError ()); + save_errno = errno; CloseHandle (p->fd[0]->h); CloseHandle (p->fd[1]->h); + GNUNET_free (p->fd[0]); + GNUNET_free (p->fd[1]); GNUNET_free (p); + errno = save_errno; return NULL; } CloseHandle (p->fd[1]->h); p->fd[1]->h = tmp_handle; - p->fd[0]->type = GNUNET_PIPE; - p->fd[1]->type = GNUNET_PIPE; + p->fd[0]->type = GNUNET_DISK_HANLDE_TYPE_PIPE; + p->fd[1]->type = GNUNET_DISK_HANLDE_TYPE_PIPE; - p->fd[0]->oOverlapRead = GNUNET_malloc (sizeof (OVERLAPPED)); - p->fd[0]->oOverlapWrite = GNUNET_malloc (sizeof (OVERLAPPED)); - p->fd[1]->oOverlapRead = GNUNET_malloc (sizeof (OVERLAPPED)); - p->fd[1]->oOverlapWrite = GNUNET_malloc (sizeof (OVERLAPPED)); + p->fd[0]->oOverlapRead = GNUNET_new (OVERLAPPED); + p->fd[0]->oOverlapWrite = GNUNET_new (OVERLAPPED); + p->fd[1]->oOverlapRead = GNUNET_new (OVERLAPPED); + p->fd[1]->oOverlapWrite = GNUNET_new (OVERLAPPED); p->fd[0]->oOverlapRead->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL); p->fd[0]->oOverlapWrite->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL); @@ -2170,23 +2354,19 @@ struct GNUNET_DISK_PipeHandle * GNUNET_DISK_pipe_from_fd (int blocking_read, int blocking_write, int fd[2]) { struct GNUNET_DISK_PipeHandle *p; - struct GNUNET_DISK_FileHandle *fds; - p = GNUNET_malloc (sizeof (struct GNUNET_DISK_PipeHandle) + - 2 * sizeof (struct GNUNET_DISK_FileHandle)); - fds = (struct GNUNET_DISK_FileHandle *) &p[1]; - p->fd[0] = &fds[0]; - p->fd[1] = &fds[1]; + p = GNUNET_new (struct GNUNET_DISK_PipeHandle); + #ifndef MINGW int ret; int flags; int eno = 0; /* make gcc happy */ - p->fd[0]->fd = fd[0]; - p->fd[1]->fd = fd[1]; ret = 0; if (fd[0] >= 0) { + p->fd[0] = GNUNET_new (struct GNUNET_DISK_FileHandle); + p->fd[0]->fd = fd[0]; if (!blocking_read) { flags = fcntl (fd[0], F_GETFL); @@ -2208,6 +2388,8 @@ GNUNET_DISK_pipe_from_fd (int blocking_read, int blocking_write, int fd[2]) if (fd[1] >= 0) { + p->fd[1] = GNUNET_new (struct GNUNET_DISK_FileHandle); + p->fd[1]->fd = fd[1]; if (!blocking_write) { flags = fcntl (fd[1], F_GETFL); @@ -2234,37 +2416,50 @@ GNUNET_DISK_pipe_from_fd (int blocking_read, int blocking_write, int fd[2]) GNUNET_break (0 == close (p->fd[0]->fd)); if (p->fd[1]->fd >= 0) GNUNET_break (0 == close (p->fd[1]->fd)); + GNUNET_free_non_null (p->fd[0]); + GNUNET_free_non_null (p->fd[1]); GNUNET_free (p); errno = eno; return NULL; } #else if (fd[0] >= 0) - p->fd[0]->h = _get_osfhandle (fd[0]); - else - p->fd[0]->h = INVALID_HANDLE_VALUE; - if (fd[1] >= 0) - p->fd[1]->h = _get_osfhandle (fd[1]); - else - p->fd[1]->h = INVALID_HANDLE_VALUE; - - if (p->fd[0]->h != INVALID_HANDLE_VALUE) { - p->fd[0]->type = GNUNET_PIPE; - p->fd[0]->oOverlapRead = GNUNET_malloc (sizeof (OVERLAPPED)); - p->fd[0]->oOverlapWrite = GNUNET_malloc (sizeof (OVERLAPPED)); - p->fd[0]->oOverlapRead->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL); - p->fd[0]->oOverlapWrite->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL); + p->fd[0] = GNUNET_new (struct GNUNET_DISK_FileHandle); + p->fd[0]->h = (HANDLE) _get_osfhandle (fd[0]); + if (p->fd[0]->h != INVALID_HANDLE_VALUE) + { + p->fd[0]->type = GNUNET_DISK_HANLDE_TYPE_PIPE; + p->fd[0]->oOverlapRead = GNUNET_new (OVERLAPPED); + p->fd[0]->oOverlapWrite = GNUNET_new (OVERLAPPED); + p->fd[0]->oOverlapRead->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL); + p->fd[0]->oOverlapWrite->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL); + } + else + { + GNUNET_free (p->fd[0]); + p->fd[0] = NULL; + } } - - if (p->fd[1]->h != INVALID_HANDLE_VALUE) + if (fd[1] >= 0) { - p->fd[1]->type = GNUNET_PIPE; - p->fd[1]->oOverlapRead = GNUNET_malloc (sizeof (OVERLAPPED)); - p->fd[1]->oOverlapWrite = GNUNET_malloc (sizeof (OVERLAPPED)); - p->fd[1]->oOverlapRead->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL); - p->fd[1]->oOverlapWrite->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL); + p->fd[1] = GNUNET_new (struct GNUNET_DISK_FileHandle); + p->fd[1]->h = (HANDLE) _get_osfhandle (fd[1]); + if (p->fd[1]->h != INVALID_HANDLE_VALUE) + { + p->fd[1]->type = GNUNET_DISK_HANLDE_TYPE_PIPE; + p->fd[1]->oOverlapRead = GNUNET_new (OVERLAPPED); + p->fd[1]->oOverlapWrite = GNUNET_new (OVERLAPPED); + p->fd[1]->oOverlapRead->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL); + p->fd[1]->oOverlapWrite->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL); + } + else + { + GNUNET_free (p->fd[1]); + p->fd[1] = NULL; + } } + #endif return p; } @@ -2282,60 +2477,62 @@ GNUNET_DISK_pipe_close_end (struct GNUNET_DISK_PipeHandle *p, enum GNUNET_DISK_PipeEnd end) { int ret = GNUNET_OK; - int save; -#ifdef MINGW if (end == GNUNET_DISK_PIPE_END_READ) { - if (p->fd[0]->h != INVALID_HANDLE_VALUE) + if (p->fd[0]) { - if (!CloseHandle (p->fd[0]->h)) - { - SetErrnoFromWinError (GetLastError ()); - ret = GNUNET_SYSERR; - } - GNUNET_free (p->fd[0]->oOverlapRead); - GNUNET_free (p->fd[0]->oOverlapWrite); - p->fd[0]->h = INVALID_HANDLE_VALUE; + ret = GNUNET_DISK_file_close (p->fd[0]); + p->fd[0] = NULL; } } else if (end == GNUNET_DISK_PIPE_END_WRITE) { - if (p->fd[0]->h != INVALID_HANDLE_VALUE) + if (p->fd[1]) { - if (!CloseHandle (p->fd[1]->h)) - { - SetErrnoFromWinError (GetLastError ()); - ret = GNUNET_SYSERR; - } - GNUNET_free (p->fd[1]->oOverlapRead); - GNUNET_free (p->fd[1]->oOverlapWrite); - p->fd[1]->h = INVALID_HANDLE_VALUE; + ret = GNUNET_DISK_file_close (p->fd[1]); + p->fd[1] = NULL; } } - save = errno; -#else - save = 0; + + return ret; +} + +/** + * Detaches one of the ends from the pipe. + * Detached end is a fully-functional FileHandle, it will + * not be affected by anything you do with the pipe afterwards. + * Each end of a pipe can only be detched from it once (i.e. + * it is not duplicated). + * + * @param p pipe to detach an end from + * @param end which end of the pipe to detach + * @return Detached end on success, NULL on failure + * (or if that end is not present or is closed). + */ +struct GNUNET_DISK_FileHandle * +GNUNET_DISK_pipe_detach_end (struct GNUNET_DISK_PipeHandle *p, + enum GNUNET_DISK_PipeEnd end) +{ + struct GNUNET_DISK_FileHandle *ret = NULL; + if (end == GNUNET_DISK_PIPE_END_READ) { - if (0 != close (p->fd[0]->fd)) + if (p->fd[0]) { - ret = GNUNET_SYSERR; - save = errno; + ret = p->fd[0]; + p->fd[0] = NULL; } - p->fd[0]->fd = -1; } else if (end == GNUNET_DISK_PIPE_END_WRITE) { - if (0 != close (p->fd[1]->fd)) + if (p->fd[1]) { - ret = GNUNET_SYSERR; - save = errno; + ret = p->fd[1]; + p->fd[1] = NULL; } - p->fd[1]->fd = -1; } -#endif - errno = save; + return ret; } @@ -2350,52 +2547,29 @@ int GNUNET_DISK_pipe_close (struct GNUNET_DISK_PipeHandle *p) { int ret = GNUNET_OK; - int save; -#ifdef MINGW - if (p->fd[0]->h != INVALID_HANDLE_VALUE) + int read_end_close; + int write_end_close; + int read_end_close_errno; + int write_end_close_errno; + + read_end_close = GNUNET_DISK_pipe_close_end (p, GNUNET_DISK_PIPE_END_READ); + read_end_close_errno = errno; + write_end_close = GNUNET_DISK_pipe_close_end (p, GNUNET_DISK_PIPE_END_WRITE); + write_end_close_errno = errno; + GNUNET_free (p); + + if (GNUNET_OK != read_end_close) { - if (!CloseHandle (p->fd[0]->h)) - { - SetErrnoFromWinError (GetLastError ()); - ret = GNUNET_SYSERR; - } - GNUNET_free (p->fd[0]->oOverlapRead); - GNUNET_free (p->fd[0]->oOverlapWrite); + errno = read_end_close_errno; + ret = read_end_close; } - if (p->fd[1]->h != INVALID_HANDLE_VALUE) + else if (GNUNET_OK != write_end_close) { - if (!CloseHandle (p->fd[1]->h)) - { - SetErrnoFromWinError (GetLastError ()); - ret = GNUNET_SYSERR; - } - GNUNET_free (p->fd[1]->oOverlapRead); - GNUNET_free (p->fd[1]->oOverlapWrite); - } - save = errno; -#else - save = 0; - if (p->fd[0]->fd != -1) - { - if (0 != close (p->fd[0]->fd)) - { - ret = GNUNET_SYSERR; - save = errno; - } + errno = write_end_close_errno; + ret = write_end_close; } - if (p->fd[1]->fd != -1) - { - if (0 != close (p->fd[1]->fd)) - { - ret = GNUNET_SYSERR; - save = errno; - } - } -#endif - GNUNET_free (p); - errno = save; return ret; } @@ -2429,12 +2603,14 @@ GNUNET_DISK_pipe_handle (const struct GNUNET_DISK_PipeHandle *p, * @param fh GNUnet file descriptor * @param dst destination buffer * @param dst_len length of dst - * @return GNUNET_OK on success, GNUNET_SYSERR otherwise + * @return #GNUNET_OK on success, #GNUNET_SYSERR otherwise */ int GNUNET_DISK_internal_file_handle_ (const struct GNUNET_DISK_FileHandle *fh, void *dst, size_t dst_len) { + if (NULL == fh) + return GNUNET_SYSERR; #ifdef MINGW if (dst_len < sizeof (HANDLE)) return GNUNET_SYSERR;