mirror of
https://github.com/transmission/transmission
synced 2025-03-19 18:25:38 +00:00
(trunk, libT) #4160 'foreign character support' -- merge mike.dld's 4160-03a-file.platch, which introduces tr_sys_file_*() portability wrappers
This commit is contained in:
parent
0323639e57
commit
c1beabfea6
7 changed files with 1831 additions and 19 deletions
|
@ -7,28 +7,86 @@
|
|||
* $Id$
|
||||
*/
|
||||
|
||||
#if defined (HAVE_CANONICALIZE_FILE_NAME) && !defined (_GNU_SOURCE)
|
||||
#if (defined (HAVE_POSIX_FADVISE) || defined (HAVE_POSIX_FALLOCATE)) && (!defined (_XOPEN_SOURCE) || _XOPEN_SOURCE < 600)
|
||||
#ifdef _XOPEN_SOURCE
|
||||
#undef _XOPEN_SOURCE
|
||||
#endif
|
||||
#define _XOPEN_SOURCE 600
|
||||
#endif
|
||||
|
||||
#if (defined (HAVE_FALLOCATE64) || defined (HAVE_CANONICALIZE_FILE_NAME)) && !defined (_GNU_SOURCE)
|
||||
#define _GNU_SOURCE
|
||||
#endif
|
||||
|
||||
#if defined (__APPLE__) && !defined (_DARWIN_C_SOURCE)
|
||||
#define _DARWIN_C_SOURCE
|
||||
#endif
|
||||
|
||||
#include <assert.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h> /* O_LARGEFILE, posix_fadvise (), [posix_]fallocate () */
|
||||
#include <libgen.h> /* basename (), dirname () */
|
||||
#include <limits.h> /* PATH_MAX */
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/mman.h> /* mmap (), munmap () */
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
#include <unistd.h> /* lseek (), write (), ftruncate (), pread (), pwrite (), pathconf (), etc */
|
||||
|
||||
#ifdef HAVE_XFS_XFS_H
|
||||
#include <xfs/xfs.h>
|
||||
#endif
|
||||
|
||||
#include "transmission.h"
|
||||
#include "file.h"
|
||||
#include "platform.h"
|
||||
#include "utils.h"
|
||||
|
||||
#ifndef O_LARGEFILE
|
||||
#define O_LARGEFILE 0
|
||||
#endif
|
||||
#ifndef O_BINARY
|
||||
#define O_BINARY 0
|
||||
#endif
|
||||
#ifndef O_SEQUENTIAL
|
||||
#define O_SEQUENTIAL 0
|
||||
#endif
|
||||
#ifndef O_CLOEXEC
|
||||
#define O_CLOEXEC 0
|
||||
#endif
|
||||
|
||||
#ifndef PATH_MAX
|
||||
#define PATH_MAX 4096
|
||||
#endif
|
||||
|
||||
/* don't use pread/pwrite on old versions of uClibc because they're buggy.
|
||||
* https://trac.transmissionbt.com/ticket/3826 */
|
||||
#ifdef __UCLIBC__
|
||||
#define TR_UCLIBC_CHECK_VERSION(major,minor,micro) \
|
||||
(__UCLIBC_MAJOR__ > (major) || \
|
||||
(__UCLIBC_MAJOR__ == (major) && __UCLIBC_MINOR__ > (minor)) || \
|
||||
(__UCLIBC_MAJOR__ == (major) && __UCLIBC_MINOR__ == (minor) && \
|
||||
__UCLIBC_SUBLEVEL__ >= (micro)))
|
||||
#if !TR_UCLIBC_CHECK_VERSION (0,9,28)
|
||||
#undef HAVE_PREAD
|
||||
#undef HAVE_PWRITE
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifdef __APPLE__
|
||||
#ifndef HAVE_PREAD
|
||||
#define HAVE_PREAD
|
||||
#endif
|
||||
#ifndef HAVE_PWRITE
|
||||
#define HAVE_PWRITE
|
||||
#endif
|
||||
#ifndef HAVE_MKDTEMP
|
||||
#define HAVE_MKDTEMP
|
||||
#endif
|
||||
#endif
|
||||
|
||||
static void
|
||||
set_system_error (tr_error ** error,
|
||||
int code)
|
||||
|
@ -62,6 +120,33 @@ stat_to_sys_path_info (const struct stat * sb,
|
|||
info->last_modified_at = sb->st_mtime;
|
||||
}
|
||||
|
||||
static void
|
||||
set_file_for_single_pass (tr_sys_file_t handle)
|
||||
{
|
||||
/* Set hints about the lookahead buffer and caching. It's okay
|
||||
for these to fail silently, so don't let them affect errno */
|
||||
|
||||
const int err = errno;
|
||||
|
||||
if (handle == TR_BAD_SYS_FILE)
|
||||
return;
|
||||
|
||||
#ifdef HAVE_POSIX_FADVISE
|
||||
|
||||
posix_fadvise (handle, 0, 0, POSIX_FADV_SEQUENTIAL);
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef __APPLE__
|
||||
|
||||
fcntl (handle, F_RDAHEAD, 1);
|
||||
fcntl (handle, F_NOCACHE, 1);
|
||||
|
||||
#endif
|
||||
|
||||
errno = err;
|
||||
}
|
||||
|
||||
bool
|
||||
tr_sys_path_exists (const char * path,
|
||||
tr_error ** error)
|
||||
|
@ -238,3 +323,504 @@ tr_sys_path_remove (const char * path,
|
|||
|
||||
return ret;
|
||||
}
|
||||
|
||||
tr_sys_file_t
|
||||
tr_sys_file_get_std (tr_std_sys_file_t std_file,
|
||||
tr_error ** error)
|
||||
{
|
||||
tr_sys_file_t ret = TR_BAD_SYS_FILE;
|
||||
|
||||
switch (std_file)
|
||||
{
|
||||
case TR_STD_SYS_FILE_IN:
|
||||
ret = STDIN_FILENO;
|
||||
break;
|
||||
case TR_STD_SYS_FILE_OUT:
|
||||
ret = STDOUT_FILENO;
|
||||
break;
|
||||
case TR_STD_SYS_FILE_ERR:
|
||||
ret = STDERR_FILENO;
|
||||
break;
|
||||
default:
|
||||
assert (0 && "Unknown standard file");
|
||||
set_system_error (error, EINVAL);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
tr_sys_file_t
|
||||
tr_sys_file_open (const char * path,
|
||||
int flags,
|
||||
int permissions,
|
||||
tr_error ** error)
|
||||
{
|
||||
tr_sys_file_t ret;
|
||||
int native_flags = 0;
|
||||
|
||||
assert (path != NULL);
|
||||
assert ((flags & (TR_SYS_FILE_READ | TR_SYS_FILE_WRITE)) != 0);
|
||||
|
||||
if ((flags & (TR_SYS_FILE_READ | TR_SYS_FILE_WRITE)) == (TR_SYS_FILE_READ | TR_SYS_FILE_WRITE))
|
||||
native_flags |= O_RDWR;
|
||||
else if (flags & TR_SYS_FILE_READ)
|
||||
native_flags |= O_RDONLY;
|
||||
else if (flags & TR_SYS_FILE_WRITE)
|
||||
native_flags |= O_WRONLY;
|
||||
|
||||
native_flags |=
|
||||
(flags & TR_SYS_FILE_CREATE ? O_CREAT : 0) |
|
||||
(flags & TR_SYS_FILE_CREATE_NEW ? O_CREAT | O_EXCL : 0) |
|
||||
(flags & TR_SYS_FILE_APPEND ? O_APPEND : 0) |
|
||||
(flags & TR_SYS_FILE_TRUNCATE ? O_TRUNC : 0) |
|
||||
(flags & TR_SYS_FILE_SEQUENTIAL ? O_SEQUENTIAL : 0) |
|
||||
O_BINARY | O_LARGEFILE | O_CLOEXEC;
|
||||
|
||||
ret = open (path, native_flags, permissions);
|
||||
|
||||
if (ret != TR_BAD_SYS_FILE)
|
||||
{
|
||||
if (flags & TR_SYS_FILE_SEQUENTIAL)
|
||||
set_file_for_single_pass (ret);
|
||||
}
|
||||
else
|
||||
{
|
||||
set_system_error (error, errno);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
tr_sys_file_t
|
||||
tr_sys_file_open_temp (char * path_template,
|
||||
tr_error ** error)
|
||||
{
|
||||
tr_sys_file_t ret;
|
||||
|
||||
assert (path_template != NULL);
|
||||
|
||||
ret = mkstemp (path_template);
|
||||
|
||||
if (ret == TR_BAD_SYS_FILE)
|
||||
set_system_error (error, errno);
|
||||
|
||||
set_file_for_single_pass (ret);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool
|
||||
tr_sys_file_close (tr_sys_file_t handle,
|
||||
tr_error ** error)
|
||||
{
|
||||
bool ret;
|
||||
|
||||
assert (handle != TR_BAD_SYS_FILE);
|
||||
|
||||
ret = close (handle) != -1;
|
||||
|
||||
if (!ret)
|
||||
set_system_error (error, errno);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool
|
||||
tr_sys_file_get_info (tr_sys_file_t handle,
|
||||
tr_sys_path_info * info,
|
||||
tr_error ** error)
|
||||
{
|
||||
bool ret;
|
||||
struct stat sb;
|
||||
|
||||
assert (handle != TR_BAD_SYS_FILE);
|
||||
assert (info != NULL);
|
||||
|
||||
ret = fstat (handle, &sb) != -1;
|
||||
|
||||
if (ret)
|
||||
stat_to_sys_path_info (&sb, info);
|
||||
else
|
||||
set_system_error (error, errno);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool
|
||||
tr_sys_file_seek (tr_sys_file_t handle,
|
||||
int64_t offset,
|
||||
tr_seek_origin_t origin,
|
||||
uint64_t * new_offset,
|
||||
tr_error ** error)
|
||||
{
|
||||
bool ret = false;
|
||||
off_t my_new_offset;
|
||||
|
||||
TR_STATIC_ASSERT (TR_SEEK_SET == SEEK_SET, "values should match");
|
||||
TR_STATIC_ASSERT (TR_SEEK_CUR == SEEK_CUR, "values should match");
|
||||
TR_STATIC_ASSERT (TR_SEEK_END == SEEK_END, "values should match");
|
||||
|
||||
TR_STATIC_ASSERT (sizeof (*new_offset) >= sizeof (my_new_offset), "");
|
||||
|
||||
assert (handle != TR_BAD_SYS_FILE);
|
||||
assert (origin == TR_SEEK_SET || origin == TR_SEEK_CUR || origin == TR_SEEK_END);
|
||||
|
||||
my_new_offset = lseek (handle, offset, origin);
|
||||
|
||||
if (my_new_offset != (off_t)-1)
|
||||
{
|
||||
if (new_offset != NULL)
|
||||
*new_offset = my_new_offset;
|
||||
ret = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
set_system_error (error, errno);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool
|
||||
tr_sys_file_read (tr_sys_file_t handle,
|
||||
void * buffer,
|
||||
uint64_t size,
|
||||
uint64_t * bytes_read,
|
||||
tr_error ** error)
|
||||
{
|
||||
bool ret = false;
|
||||
ssize_t my_bytes_read;
|
||||
|
||||
TR_STATIC_ASSERT (sizeof (*bytes_read) >= sizeof (my_bytes_read), "");
|
||||
|
||||
assert (handle != TR_BAD_SYS_FILE);
|
||||
assert (buffer != NULL || size == 0);
|
||||
|
||||
my_bytes_read = read (handle, buffer, size);
|
||||
|
||||
if (my_bytes_read != -1)
|
||||
{
|
||||
if (bytes_read != NULL)
|
||||
*bytes_read = my_bytes_read;
|
||||
ret = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
set_system_error (error, errno);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool
|
||||
tr_sys_file_read_at (tr_sys_file_t handle,
|
||||
void * buffer,
|
||||
uint64_t size,
|
||||
uint64_t offset,
|
||||
uint64_t * bytes_read,
|
||||
tr_error ** error)
|
||||
{
|
||||
bool ret = false;
|
||||
ssize_t my_bytes_read;
|
||||
|
||||
TR_STATIC_ASSERT (sizeof (*bytes_read) >= sizeof (my_bytes_read), "");
|
||||
|
||||
assert (handle != TR_BAD_SYS_FILE);
|
||||
assert (buffer != NULL || size == 0);
|
||||
|
||||
#ifdef HAVE_PREAD
|
||||
|
||||
my_bytes_read = pread (handle, buffer, size, offset);
|
||||
|
||||
#else
|
||||
|
||||
if (lseek (handle, offset, SEEK_SET) != -1)
|
||||
my_bytes_read = read (handle, buffer, size);
|
||||
else
|
||||
my_bytes_read = -1;
|
||||
|
||||
#endif
|
||||
|
||||
if (my_bytes_read != -1)
|
||||
{
|
||||
if (bytes_read != NULL)
|
||||
*bytes_read = my_bytes_read;
|
||||
ret = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
set_system_error (error, errno);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool
|
||||
tr_sys_file_write (tr_sys_file_t handle,
|
||||
const void * buffer,
|
||||
uint64_t size,
|
||||
uint64_t * bytes_written,
|
||||
tr_error ** error)
|
||||
{
|
||||
bool ret = false;
|
||||
ssize_t my_bytes_written;
|
||||
|
||||
TR_STATIC_ASSERT (sizeof (*bytes_written) >= sizeof (my_bytes_written), "");
|
||||
|
||||
assert (handle != TR_BAD_SYS_FILE);
|
||||
assert (buffer != NULL || size == 0);
|
||||
|
||||
my_bytes_written = write (handle, buffer, size);
|
||||
|
||||
if (my_bytes_written != -1)
|
||||
{
|
||||
if (bytes_written != NULL)
|
||||
*bytes_written = my_bytes_written;
|
||||
ret = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
set_system_error (error, errno);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool
|
||||
tr_sys_file_write_at (tr_sys_file_t handle,
|
||||
const void * buffer,
|
||||
uint64_t size,
|
||||
uint64_t offset,
|
||||
uint64_t * bytes_written,
|
||||
tr_error ** error)
|
||||
{
|
||||
bool ret = false;
|
||||
ssize_t my_bytes_written;
|
||||
|
||||
TR_STATIC_ASSERT (sizeof (*bytes_written) >= sizeof (my_bytes_written), "");
|
||||
|
||||
assert (handle != TR_BAD_SYS_FILE);
|
||||
assert (buffer != NULL || size == 0);
|
||||
|
||||
#ifdef HAVE_PWRITE
|
||||
|
||||
my_bytes_written = pwrite (handle, buffer, size, offset);
|
||||
|
||||
#else
|
||||
|
||||
if (lseek (handle, offset, SEEK_SET) != -1)
|
||||
my_bytes_written = write (handle, buffer, size);
|
||||
else
|
||||
my_bytes_written = -1;
|
||||
|
||||
#endif
|
||||
|
||||
if (my_bytes_written != -1)
|
||||
{
|
||||
if (bytes_written != NULL)
|
||||
*bytes_written = my_bytes_written;
|
||||
ret = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
set_system_error (error, errno);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool
|
||||
tr_sys_file_flush (tr_sys_file_t handle,
|
||||
tr_error ** error)
|
||||
{
|
||||
bool ret;
|
||||
|
||||
assert (handle != TR_BAD_SYS_FILE);
|
||||
|
||||
ret = fsync (handle) != -1;
|
||||
|
||||
if (!ret)
|
||||
set_system_error (error, errno);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool
|
||||
tr_sys_file_truncate (tr_sys_file_t handle,
|
||||
uint64_t size,
|
||||
tr_error ** error)
|
||||
{
|
||||
bool ret;
|
||||
|
||||
assert (handle != TR_BAD_SYS_FILE);
|
||||
|
||||
ret = ftruncate (handle, size) != -1;
|
||||
|
||||
if (!ret)
|
||||
set_system_error (error, errno);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool
|
||||
tr_sys_file_prefetch (tr_sys_file_t handle,
|
||||
uint64_t offset,
|
||||
uint64_t size,
|
||||
tr_error ** error)
|
||||
{
|
||||
bool ret = false;
|
||||
|
||||
#if defined (HAVE_POSIX_FADVISE)
|
||||
|
||||
int code;
|
||||
|
||||
assert (handle != TR_BAD_SYS_FILE);
|
||||
assert (size > 0);
|
||||
|
||||
code = posix_fadvise (handle, offset, size, POSIX_FADV_WILLNEED);
|
||||
|
||||
if (code == 0)
|
||||
ret = true;
|
||||
else
|
||||
set_system_error (error, code);
|
||||
|
||||
#elif defined (__APPLE__)
|
||||
|
||||
struct radvisory radv;
|
||||
|
||||
assert (handle != TR_BAD_SYS_FILE);
|
||||
assert (size > 0);
|
||||
|
||||
radv.ra_offset = offset;
|
||||
radv.ra_count = size;
|
||||
|
||||
ret = fcntl (handle, F_RDADVISE, &radv) != -1;
|
||||
|
||||
if (!ret)
|
||||
set_system_error (error, errno);
|
||||
|
||||
#endif
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool
|
||||
tr_sys_file_preallocate (tr_sys_file_t handle,
|
||||
uint64_t size,
|
||||
int flags,
|
||||
tr_error ** error)
|
||||
{
|
||||
bool ret = false;
|
||||
|
||||
assert (handle != TR_BAD_SYS_FILE);
|
||||
|
||||
errno = 0;
|
||||
|
||||
#ifdef HAVE_FALLOCATE64
|
||||
|
||||
/* fallocate64 is always preferred, so try it first */
|
||||
ret = fallocate64 (handle, 0, 0, size) != -1;
|
||||
|
||||
#endif
|
||||
|
||||
if (!ret && (flags & TR_SYS_FILE_PREALLOC_SPARSE) == 0)
|
||||
{
|
||||
int code = errno;
|
||||
|
||||
#ifdef HAVE_XFS_XFS_H
|
||||
|
||||
if (!ret && platform_test_xfs_fd (handle))
|
||||
{
|
||||
xfs_flock64_t fl;
|
||||
|
||||
fl.l_whence = 0;
|
||||
fl.l_start = 0;
|
||||
fl.l_len = size;
|
||||
|
||||
ret = xfsctl (NULL, handle, XFS_IOC_RESVSP64, &fl) != -1;
|
||||
|
||||
code = errno;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef __APPLE__
|
||||
|
||||
if (!ret)
|
||||
{
|
||||
fstore_t fst;
|
||||
|
||||
fst.fst_flags = F_ALLOCATECONTIG;
|
||||
fst.fst_posmode = F_PEOFPOSMODE;
|
||||
fst.fst_offset = 0;
|
||||
fst.fst_length = size;
|
||||
fst.fst_bytesalloc = 0;
|
||||
|
||||
ret = fcntl (handle, F_PREALLOCATE, &fst) != -1;
|
||||
|
||||
if (ret)
|
||||
ret = ftruncate (handle, size) != -1;
|
||||
|
||||
code = errno;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_POSIX_FALLOCATE
|
||||
|
||||
if (!ret)
|
||||
{
|
||||
code = posix_fallocate (handle, 0, size);
|
||||
ret = code == 0;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
errno = code;
|
||||
}
|
||||
|
||||
if (!ret)
|
||||
set_system_error (error, errno);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void *
|
||||
tr_sys_file_map_for_reading (tr_sys_file_t handle,
|
||||
uint64_t offset,
|
||||
uint64_t size,
|
||||
tr_error ** error)
|
||||
{
|
||||
void * ret;
|
||||
|
||||
assert (handle != TR_BAD_SYS_FILE);
|
||||
assert (size > 0);
|
||||
|
||||
ret = mmap (NULL, size, PROT_READ, MAP_SHARED, handle, offset);
|
||||
|
||||
if (ret == MAP_FAILED)
|
||||
{
|
||||
set_system_error (error, errno);
|
||||
ret = NULL;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool
|
||||
tr_sys_file_unmap (const void * address,
|
||||
uint64_t size,
|
||||
tr_error ** error)
|
||||
{
|
||||
bool ret;
|
||||
|
||||
assert (address != NULL);
|
||||
assert (size > 0);
|
||||
|
||||
ret = munmap ((void *) address, size) != -1;
|
||||
|
||||
if (!ret)
|
||||
set_system_error (error, errno);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
|
|
@ -137,11 +137,29 @@ path_contains_no_symlinks (const char * path)
|
|||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
validate_permissions (const char * path,
|
||||
unsigned int permissions)
|
||||
{
|
||||
#ifndef _WIN32
|
||||
|
||||
struct stat sb;
|
||||
return stat (path, &sb) != -1 && (sb.st_mode & 0777) == permissions;
|
||||
|
||||
#else
|
||||
|
||||
/* No UNIX permissions on Windows */
|
||||
return true;
|
||||
|
||||
#endif
|
||||
}
|
||||
|
||||
static int
|
||||
test_get_info (void)
|
||||
{
|
||||
char * const test_dir = create_test_dir (__FUNCTION__);
|
||||
tr_sys_path_info info;
|
||||
tr_sys_file_t fd;
|
||||
tr_error * err = NULL;
|
||||
char * path1, * path2;
|
||||
|
||||
|
@ -163,6 +181,16 @@ test_get_info (void)
|
|||
check_int_eq (4, info.size);
|
||||
check (info.last_modified_at >= time(0) - 1 && info.last_modified_at <= time(0));
|
||||
|
||||
/* Good file info (by handle) */
|
||||
fd = tr_sys_file_open (path1, TR_SYS_FILE_READ, 0, NULL);
|
||||
clear_path_info (&info);
|
||||
check (tr_sys_file_get_info (fd, &info, &err));
|
||||
check (err == NULL);
|
||||
check_int_eq (TR_SYS_PATH_IS_FILE, info.type);
|
||||
check_int_eq (4, info.size);
|
||||
check (info.last_modified_at >= time(0) - 1 && info.last_modified_at <= time(0));
|
||||
tr_sys_file_close (fd, NULL);
|
||||
|
||||
tr_sys_path_remove (path1, NULL);
|
||||
|
||||
/* Good directory info */
|
||||
|
@ -192,6 +220,16 @@ test_get_info (void)
|
|||
check_int_eq (4, info.size);
|
||||
check (info.last_modified_at >= time(0) - 1 && info.last_modified_at <= time(0));
|
||||
|
||||
/* Good file info (by handle) */
|
||||
fd = tr_sys_file_open (path1, TR_SYS_FILE_READ, 0, NULL);
|
||||
clear_path_info (&info);
|
||||
check (tr_sys_file_get_info (fd, &info, &err));
|
||||
check (err == NULL);
|
||||
check_int_eq (TR_SYS_PATH_IS_FILE, info.type);
|
||||
check_int_eq (4, info.size);
|
||||
check (info.last_modified_at >= time(0) - 1 && info.last_modified_at <= time(0));
|
||||
tr_sys_file_close (fd, NULL);
|
||||
|
||||
tr_sys_path_remove (path2, NULL);
|
||||
|
||||
/* Good directory info */
|
||||
|
@ -775,6 +813,345 @@ test_path_remove (void)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
test_file_open (void)
|
||||
{
|
||||
char * const test_dir = create_test_dir (__FUNCTION__);
|
||||
tr_error * err = NULL;
|
||||
char * path1;
|
||||
tr_sys_file_t fd;
|
||||
uint64_t n;
|
||||
tr_sys_path_info info;
|
||||
|
||||
path1 = tr_buildPath (test_dir, "a", NULL);
|
||||
|
||||
/* Can't open non-existent file */
|
||||
check (!tr_sys_path_exists (path1, NULL));
|
||||
check (tr_sys_file_open (path1, TR_SYS_FILE_READ, 0600, &err) == TR_BAD_SYS_FILE);
|
||||
check (err != NULL);
|
||||
check (!tr_sys_path_exists (path1, NULL));
|
||||
tr_error_clear (&err);
|
||||
check (tr_sys_file_open (path1, TR_SYS_FILE_WRITE, 0600, &err) == TR_BAD_SYS_FILE);
|
||||
check (err != NULL);
|
||||
check (!tr_sys_path_exists (path1, NULL));
|
||||
tr_error_clear (&err);
|
||||
|
||||
/* Can't open directory */
|
||||
tr_mkdirp (path1, 0777);
|
||||
#ifdef _WIN32
|
||||
/* This works on *NIX */
|
||||
check (tr_sys_file_open (path1, TR_SYS_FILE_READ, 0600, &err) == TR_BAD_SYS_FILE);
|
||||
check (err != NULL);
|
||||
tr_error_clear (&err);
|
||||
#endif
|
||||
check (tr_sys_file_open (path1, TR_SYS_FILE_WRITE, 0600, &err) == TR_BAD_SYS_FILE);
|
||||
check (err != NULL);
|
||||
tr_error_clear (&err);
|
||||
|
||||
tr_sys_path_remove (path1, NULL);
|
||||
|
||||
/* Can create non-existent file */
|
||||
fd = tr_sys_file_open (path1, TR_SYS_FILE_WRITE | TR_SYS_FILE_CREATE, 0640, &err);
|
||||
check (fd != TR_BAD_SYS_FILE);
|
||||
check (err == NULL);
|
||||
tr_sys_file_close (fd, NULL);
|
||||
check (tr_sys_path_exists (path1, NULL));
|
||||
check (validate_permissions (path1, 0640));
|
||||
|
||||
/* Can open existing file */
|
||||
check (tr_sys_path_exists (path1, NULL));
|
||||
fd = tr_sys_file_open (path1, TR_SYS_FILE_READ, 0600, &err);
|
||||
check (fd != TR_BAD_SYS_FILE);
|
||||
check (err == NULL);
|
||||
tr_sys_file_close (fd, NULL);
|
||||
fd = tr_sys_file_open (path1, TR_SYS_FILE_WRITE, 0600, &err);
|
||||
check (fd != TR_BAD_SYS_FILE);
|
||||
check (err == NULL);
|
||||
tr_sys_file_close (fd, NULL);
|
||||
|
||||
tr_sys_path_remove (path1, NULL);
|
||||
libtest_create_file_with_string_contents (path1, "test");
|
||||
|
||||
/* Can't create new file if it already exists */
|
||||
fd = tr_sys_file_open (path1, TR_SYS_FILE_WRITE | TR_SYS_FILE_CREATE_NEW, 0640, &err);
|
||||
check (fd == TR_BAD_SYS_FILE);
|
||||
check (err != NULL);
|
||||
tr_error_clear (&err);
|
||||
tr_sys_path_get_info (path1, TR_SYS_PATH_NO_FOLLOW, &info, NULL);
|
||||
check_int_eq (4, info.size);
|
||||
|
||||
/* Pointer is at the end of file */
|
||||
tr_sys_path_get_info (path1, TR_SYS_PATH_NO_FOLLOW, &info, NULL);
|
||||
check_int_eq (4, info.size);
|
||||
fd = tr_sys_file_open (path1, TR_SYS_FILE_WRITE | TR_SYS_FILE_APPEND, 0600, &err);
|
||||
check (fd != TR_BAD_SYS_FILE);
|
||||
check (err == NULL);
|
||||
tr_sys_file_write (fd, "s", 1, NULL, NULL); /* On *NIX, pointer is positioned on each write but not initially */
|
||||
tr_sys_file_seek (fd, 0, TR_SEEK_CUR, &n, NULL);
|
||||
check_int_eq (5, n);
|
||||
tr_sys_file_close (fd, NULL);
|
||||
|
||||
/* File gets truncated */
|
||||
tr_sys_path_get_info (path1, TR_SYS_PATH_NO_FOLLOW, &info, NULL);
|
||||
check_int_eq (5, info.size);
|
||||
fd = tr_sys_file_open (path1, TR_SYS_FILE_WRITE | TR_SYS_FILE_TRUNCATE, 0600, &err);
|
||||
check (fd != TR_BAD_SYS_FILE);
|
||||
check (err == NULL);
|
||||
tr_sys_file_get_info (fd, &info, NULL);
|
||||
check_int_eq (0, info.size);
|
||||
tr_sys_file_close (fd, NULL);
|
||||
tr_sys_path_get_info (path1, TR_SYS_PATH_NO_FOLLOW, &info, NULL);
|
||||
check_int_eq (0, info.size);
|
||||
|
||||
/* TODO: symlink and hardlink tests */
|
||||
|
||||
tr_sys_path_remove (path1, NULL);
|
||||
|
||||
tr_free (path1);
|
||||
|
||||
tr_free (test_dir);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
test_file_read_write_seek (void)
|
||||
{
|
||||
char * const test_dir = create_test_dir (__FUNCTION__);
|
||||
tr_error * err = NULL;
|
||||
char * path1;
|
||||
tr_sys_file_t fd;
|
||||
uint64_t n;
|
||||
char buf[100];
|
||||
|
||||
path1 = tr_buildPath (test_dir, "a", NULL);
|
||||
|
||||
fd = tr_sys_file_open (path1, TR_SYS_FILE_READ | TR_SYS_FILE_WRITE | TR_SYS_FILE_CREATE, 0600, NULL);
|
||||
|
||||
check (tr_sys_file_seek (fd, 0, TR_SEEK_CUR, &n, &err));
|
||||
check (err == NULL);
|
||||
check_int_eq (0, n);
|
||||
|
||||
check (tr_sys_file_write (fd, "test", 4, &n, &err));
|
||||
check (err == NULL);
|
||||
check_int_eq (4, n);
|
||||
|
||||
check (tr_sys_file_seek (fd, 0, TR_SEEK_CUR, &n, &err));
|
||||
check (err == NULL);
|
||||
check_int_eq (4, n);
|
||||
|
||||
check (tr_sys_file_seek (fd, 0, TR_SEEK_SET, &n, &err));
|
||||
check (err == NULL);
|
||||
check_int_eq (0, n);
|
||||
|
||||
check (tr_sys_file_read (fd, buf, sizeof (buf), &n, &err));
|
||||
check (err == NULL);
|
||||
check_int_eq (4, n);
|
||||
|
||||
check_int_eq (0, memcmp (buf, "test", 4));
|
||||
|
||||
check (tr_sys_file_seek (fd, -3, TR_SEEK_CUR, &n, &err));
|
||||
check (err == NULL);
|
||||
check_int_eq (1, n);
|
||||
|
||||
check (tr_sys_file_write (fd, "E", 1, &n, &err));
|
||||
check (err == NULL);
|
||||
check_int_eq (1, n);
|
||||
|
||||
check (tr_sys_file_seek (fd, -2, TR_SEEK_CUR, &n, &err));
|
||||
check (err == NULL);
|
||||
check_int_eq (0, n);
|
||||
|
||||
check (tr_sys_file_read (fd, buf, sizeof (buf), &n, &err));
|
||||
check (err == NULL);
|
||||
check_int_eq (4, n);
|
||||
|
||||
check_int_eq (0, memcmp (buf, "tEst", 4));
|
||||
|
||||
check (tr_sys_file_seek (fd, 0, TR_SEEK_END, &n, &err));
|
||||
check (err == NULL);
|
||||
check_int_eq (4, n);
|
||||
|
||||
check (tr_sys_file_write (fd, " ok", 3, &n, &err));
|
||||
check (err == NULL);
|
||||
check_int_eq (3, n);
|
||||
|
||||
check (tr_sys_file_seek (fd, 0, TR_SEEK_SET, &n, &err));
|
||||
check (err == NULL);
|
||||
check_int_eq (0, n);
|
||||
|
||||
check (tr_sys_file_read (fd, buf, sizeof (buf), &n, &err));
|
||||
check (err == NULL);
|
||||
check_int_eq (7, n);
|
||||
|
||||
check_int_eq (0, memcmp (buf, "tEst ok", 7));
|
||||
|
||||
check (tr_sys_file_write_at (fd, "-", 1, 4, &n, &err));
|
||||
check (err == NULL);
|
||||
check_int_eq (1, n);
|
||||
|
||||
check (tr_sys_file_read_at (fd, buf, 5, 2, &n, &err));
|
||||
check (err == NULL);
|
||||
check_int_eq (5, n);
|
||||
|
||||
check_int_eq (0, memcmp (buf, "st-ok", 5));
|
||||
|
||||
tr_sys_file_close (fd, NULL);
|
||||
|
||||
tr_sys_path_remove (path1, NULL);
|
||||
|
||||
tr_free (path1);
|
||||
|
||||
tr_free (test_dir);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
test_file_truncate (void)
|
||||
{
|
||||
char * const test_dir = create_test_dir (__FUNCTION__);
|
||||
tr_error * err = NULL;
|
||||
char * path1;
|
||||
tr_sys_file_t fd;
|
||||
tr_sys_path_info info;
|
||||
|
||||
path1 = tr_buildPath (test_dir, "a", NULL);
|
||||
|
||||
fd = tr_sys_file_open (path1, TR_SYS_FILE_WRITE | TR_SYS_FILE_CREATE, 0600, NULL);
|
||||
|
||||
check (tr_sys_file_truncate (fd, 10, &err));
|
||||
check (err == NULL);
|
||||
tr_sys_file_get_info (fd, &info, NULL);
|
||||
check_int_eq (10, info.size);
|
||||
|
||||
check (tr_sys_file_truncate (fd, 20, &err));
|
||||
check (err == NULL);
|
||||
tr_sys_file_get_info (fd, &info, NULL);
|
||||
check_int_eq (20, info.size);
|
||||
|
||||
check (tr_sys_file_truncate (fd, 0, &err));
|
||||
check (err == NULL);
|
||||
tr_sys_file_get_info (fd, &info, NULL);
|
||||
check_int_eq (0, info.size);
|
||||
|
||||
check (tr_sys_file_truncate (fd, 50, &err));
|
||||
check (err == NULL);
|
||||
|
||||
tr_sys_file_close (fd, NULL);
|
||||
|
||||
tr_sys_path_get_info (path1, 0, &info, NULL);
|
||||
check_int_eq (50, info.size);
|
||||
|
||||
fd = tr_sys_file_open (path1, TR_SYS_FILE_WRITE | TR_SYS_FILE_CREATE, 0600, NULL);
|
||||
|
||||
check (tr_sys_file_truncate (fd, 25, &err));
|
||||
check (err == NULL);
|
||||
|
||||
tr_sys_file_close (fd, NULL);
|
||||
|
||||
tr_sys_path_get_info (path1, 0, &info, NULL);
|
||||
check_int_eq (25, info.size);
|
||||
|
||||
tr_sys_path_remove (path1, NULL);
|
||||
|
||||
tr_free (path1);
|
||||
|
||||
tr_free (test_dir);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
test_file_preallocate (void)
|
||||
{
|
||||
char * const test_dir = create_test_dir (__FUNCTION__);
|
||||
tr_error * err = NULL;
|
||||
char * path1;
|
||||
tr_sys_file_t fd;
|
||||
tr_sys_path_info info;
|
||||
|
||||
path1 = tr_buildPath (test_dir, "a", NULL);
|
||||
|
||||
fd = tr_sys_file_open (path1, TR_SYS_FILE_WRITE | TR_SYS_FILE_CREATE, 0600, NULL);
|
||||
|
||||
if (tr_sys_file_preallocate (fd, 50, 0, &err))
|
||||
{
|
||||
check (err == NULL);
|
||||
tr_sys_file_get_info (fd, &info, NULL);
|
||||
check_int_eq (50, info.size);
|
||||
}
|
||||
else
|
||||
{
|
||||
check (err != NULL);
|
||||
fprintf (stderr, "WARNING: [%s] unable to preallocate file (full): %s (%d)\n", __FUNCTION__, err->message, err->code);
|
||||
tr_error_clear (&err);
|
||||
}
|
||||
|
||||
tr_sys_file_close (fd, NULL);
|
||||
|
||||
tr_sys_path_remove (path1, NULL);
|
||||
|
||||
fd = tr_sys_file_open (path1, TR_SYS_FILE_WRITE | TR_SYS_FILE_CREATE, 0600, NULL);
|
||||
|
||||
if (tr_sys_file_preallocate (fd, 500 * 1024 * 1024, TR_SYS_FILE_PREALLOC_SPARSE, &err))
|
||||
{
|
||||
check (err == NULL);
|
||||
tr_sys_file_get_info (fd, &info, NULL);
|
||||
check_int_eq (500 * 1024 * 1024, info.size);
|
||||
}
|
||||
else
|
||||
{
|
||||
check (err != NULL);
|
||||
fprintf (stderr, "WARNING: [%s] unable to preallocate file (sparse): %s (%d)\n", __FUNCTION__, err->message, err->code);
|
||||
tr_error_clear (&err);
|
||||
}
|
||||
|
||||
tr_sys_file_close (fd, NULL);
|
||||
|
||||
tr_sys_path_remove (path1, NULL);
|
||||
|
||||
tr_free (path1);
|
||||
|
||||
tr_free (test_dir);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
test_file_map (void)
|
||||
{
|
||||
char * const test_dir = create_test_dir (__FUNCTION__);
|
||||
tr_error * err = NULL;
|
||||
char * path1;
|
||||
tr_sys_file_t fd;
|
||||
char * view;
|
||||
|
||||
path1 = tr_buildPath (test_dir, "a", NULL);
|
||||
|
||||
libtest_create_file_with_string_contents (path1, "test");
|
||||
|
||||
fd = tr_sys_file_open (path1, TR_SYS_FILE_READ | TR_SYS_FILE_WRITE, 0600, NULL);
|
||||
|
||||
view = tr_sys_file_map_for_reading (fd, 0, 4, &err);
|
||||
check (view != NULL);
|
||||
check (err == NULL);
|
||||
|
||||
check_int_eq (0, memcmp (view, "test", 4));
|
||||
|
||||
tr_sys_file_write_at (fd, "E", 1, 1, NULL, NULL);
|
||||
|
||||
check_int_eq (0, memcmp (view, "tEst", 4));
|
||||
|
||||
check (tr_sys_file_unmap (view, 4, &err));
|
||||
check (err == NULL);
|
||||
|
||||
tr_sys_file_close (fd, NULL);
|
||||
|
||||
tr_sys_path_remove (path1, NULL);
|
||||
|
||||
tr_free (path1);
|
||||
|
||||
tr_free (test_dir);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
main (void)
|
||||
{
|
||||
|
@ -786,7 +1163,12 @@ main (void)
|
|||
test_path_resolve,
|
||||
test_path_basename_dirname,
|
||||
test_path_rename,
|
||||
test_path_remove
|
||||
test_path_remove,
|
||||
test_file_open,
|
||||
test_file_read_write_seek,
|
||||
test_file_truncate,
|
||||
test_file_preallocate,
|
||||
test_file_map
|
||||
};
|
||||
int ret;
|
||||
|
||||
|
|
|
@ -10,10 +10,17 @@
|
|||
#include <assert.h>
|
||||
#include <stdlib.h> /* _splitpath_s (), _makepath_s () */
|
||||
|
||||
#include <winioctl.h> /* FSCTL_SET_SPARSE */
|
||||
|
||||
#include "transmission.h"
|
||||
#include "crypto.h" /* tr_cryptoRandInt () */
|
||||
#include "file.h"
|
||||
#include "utils.h"
|
||||
|
||||
#ifndef MAXSIZE_T
|
||||
#define MAXSIZE_T ((SIZE_T)~((SIZE_T)0))
|
||||
#endif
|
||||
|
||||
/* MSDN (http://msdn.microsoft.com/en-us/library/2k2xf226.aspx) only mentions
|
||||
"i64" suffix for C code, but no warning is issued */
|
||||
#define DELTA_EPOCH_IN_MICROSECS 11644473600000000ULL
|
||||
|
@ -90,10 +97,85 @@ stat_to_sys_path_info (DWORD attributes,
|
|||
info->last_modified_at = filetime_to_unix_time (mtime);
|
||||
}
|
||||
|
||||
static bool
|
||||
get_file_info (HANDLE handle,
|
||||
tr_sys_path_info * info,
|
||||
tr_error ** error);
|
||||
static tr_sys_file_t
|
||||
open_file (const char * path,
|
||||
DWORD access,
|
||||
DWORD disposition,
|
||||
DWORD flags,
|
||||
tr_error ** error)
|
||||
{
|
||||
tr_sys_file_t ret = TR_BAD_SYS_FILE;
|
||||
wchar_t * wide_path;
|
||||
|
||||
assert (path != NULL);
|
||||
|
||||
wide_path = tr_win32_utf8_to_native (path, -1);
|
||||
|
||||
if (wide_path != NULL)
|
||||
ret = CreateFileW (wide_path, access, FILE_SHARE_READ | FILE_SHARE_WRITE,
|
||||
NULL, disposition, flags, NULL);
|
||||
|
||||
if (ret == TR_BAD_SYS_FILE)
|
||||
set_system_error (error, GetLastError ());
|
||||
|
||||
tr_free (wide_path);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void
|
||||
create_temp_path (char * path_template,
|
||||
void (* callback) (const char * path, void * param, tr_error ** error),
|
||||
void * callback_param,
|
||||
tr_error ** error)
|
||||
{
|
||||
char * path;
|
||||
size_t path_size;
|
||||
int attempt;
|
||||
tr_error * my_error = NULL;
|
||||
|
||||
assert (path_template != NULL);
|
||||
assert (callback != NULL);
|
||||
|
||||
path = tr_strdup (path_template);
|
||||
path_size = strlen (path);
|
||||
|
||||
assert (path_size > 0);
|
||||
|
||||
for (attempt = 0; attempt < 100; ++attempt)
|
||||
{
|
||||
size_t i = path_size;
|
||||
|
||||
while (i > 0 && path_template[i - 1] == 'X')
|
||||
{
|
||||
const int c = tr_cryptoRandInt (26 + 26 + 10);
|
||||
path[i - 1] = c < 26 ? c + 'A' : (c < 26 + 26 ? (c - 26) + 'a' : (c - 26 - 26) + '0');
|
||||
--i;
|
||||
}
|
||||
|
||||
assert (path_size >= i + 6);
|
||||
|
||||
tr_error_clear (&my_error);
|
||||
|
||||
(*callback) (path, callback_param, &my_error);
|
||||
|
||||
if (my_error == NULL)
|
||||
break;
|
||||
}
|
||||
|
||||
if (my_error != NULL)
|
||||
tr_error_propagate(error, &my_error);
|
||||
else
|
||||
memcpy (path_template, path, path_size);
|
||||
|
||||
goto cleanup;
|
||||
|
||||
fail:
|
||||
set_system_error (error, GetLastError ());
|
||||
|
||||
cleanup:
|
||||
tr_free (path);
|
||||
}
|
||||
|
||||
bool
|
||||
tr_sys_path_exists (const char * path,
|
||||
|
@ -161,7 +243,7 @@ tr_sys_path_get_info (const char * path,
|
|||
if (handle != INVALID_HANDLE_VALUE)
|
||||
{
|
||||
tr_error * my_error = NULL;
|
||||
ret = get_file_info (handle, info, &my_error);
|
||||
ret = tr_sys_file_get_info (handle, info, &my_error);
|
||||
if (!ret)
|
||||
tr_error_propagate (error, &my_error);
|
||||
CloseHandle (handle);
|
||||
|
@ -428,15 +510,140 @@ tr_sys_path_remove (const char * path,
|
|||
return ret;
|
||||
}
|
||||
|
||||
static bool
|
||||
get_file_info (HANDLE handle,
|
||||
tr_sys_path_info * info,
|
||||
tr_error ** error)
|
||||
tr_sys_file_t
|
||||
tr_sys_file_get_std (tr_std_sys_file_t std_file,
|
||||
tr_error ** error)
|
||||
{
|
||||
tr_sys_file_t ret = TR_BAD_SYS_FILE;
|
||||
|
||||
switch (std_file)
|
||||
{
|
||||
case TR_STD_SYS_FILE_IN:
|
||||
ret = GetStdHandle (STD_INPUT_HANDLE);
|
||||
break;
|
||||
case TR_STD_SYS_FILE_OUT:
|
||||
ret = GetStdHandle (STD_OUTPUT_HANDLE);
|
||||
break;
|
||||
case TR_STD_SYS_FILE_ERR:
|
||||
ret = GetStdHandle (STD_ERROR_HANDLE);
|
||||
break;
|
||||
default:
|
||||
assert (0 && "Unknown standard file");
|
||||
set_system_error (error, ERROR_INVALID_PARAMETER);
|
||||
return TR_BAD_SYS_FILE;
|
||||
}
|
||||
|
||||
if (ret == TR_BAD_SYS_FILE)
|
||||
set_system_error (error, GetLastError ());
|
||||
else if (ret == NULL)
|
||||
ret = TR_BAD_SYS_FILE;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
tr_sys_file_t
|
||||
tr_sys_file_open (const char * path,
|
||||
int flags,
|
||||
int permissions,
|
||||
tr_error ** error)
|
||||
{
|
||||
tr_sys_file_t ret;
|
||||
DWORD native_access = 0;
|
||||
DWORD native_disposition = OPEN_EXISTING;
|
||||
DWORD native_flags = FILE_ATTRIBUTE_NORMAL;
|
||||
bool success;
|
||||
|
||||
assert (path != NULL);
|
||||
assert ((flags & (TR_SYS_FILE_READ | TR_SYS_FILE_WRITE)) != 0);
|
||||
|
||||
if (flags & TR_SYS_FILE_READ)
|
||||
native_access |= GENERIC_READ;
|
||||
if (flags & TR_SYS_FILE_WRITE)
|
||||
native_access |= GENERIC_WRITE;
|
||||
|
||||
if (flags & TR_SYS_FILE_CREATE_NEW)
|
||||
native_disposition = CREATE_NEW;
|
||||
else if (flags & TR_SYS_FILE_CREATE)
|
||||
native_disposition = flags & TR_SYS_FILE_TRUNCATE ? CREATE_ALWAYS : OPEN_ALWAYS;
|
||||
else if (flags & TR_SYS_FILE_TRUNCATE)
|
||||
native_disposition = TRUNCATE_EXISTING;
|
||||
|
||||
if (flags & TR_SYS_FILE_SEQUENTIAL)
|
||||
native_flags |= FILE_FLAG_SEQUENTIAL_SCAN;
|
||||
|
||||
ret = open_file (path, native_access, native_disposition, native_flags, error);
|
||||
|
||||
success = ret != TR_BAD_SYS_FILE;
|
||||
|
||||
if (success && (flags & TR_SYS_FILE_APPEND))
|
||||
success = SetFilePointer (ret, 0, NULL, FILE_END) != INVALID_SET_FILE_POINTER;
|
||||
|
||||
if (!success)
|
||||
{
|
||||
if (error == NULL)
|
||||
set_system_error (error, GetLastError ());
|
||||
|
||||
CloseHandle (ret);
|
||||
ret = TR_BAD_SYS_FILE;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void
|
||||
file_open_temp_callback (const char * path,
|
||||
void * param,
|
||||
tr_error ** error)
|
||||
{
|
||||
tr_sys_file_t * result = (tr_sys_file_t *) param;
|
||||
|
||||
assert (result != NULL);
|
||||
|
||||
*result = open_file (path,
|
||||
GENERIC_READ | GENERIC_WRITE,
|
||||
CREATE_NEW,
|
||||
FILE_ATTRIBUTE_TEMPORARY,
|
||||
error);
|
||||
}
|
||||
|
||||
tr_sys_file_t
|
||||
tr_sys_file_open_temp (char * path_template,
|
||||
tr_error ** error)
|
||||
{
|
||||
tr_sys_file_t ret = TR_BAD_SYS_FILE;
|
||||
|
||||
assert (path_template != NULL);
|
||||
|
||||
create_temp_path (path_template, file_open_temp_callback, &ret, error);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool
|
||||
tr_sys_file_close (tr_sys_file_t handle,
|
||||
tr_error ** error)
|
||||
{
|
||||
bool ret;
|
||||
|
||||
assert (handle != TR_BAD_SYS_FILE);
|
||||
|
||||
ret = CloseHandle (handle);
|
||||
|
||||
if (!ret)
|
||||
set_system_error (error, GetLastError ());
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool
|
||||
tr_sys_file_get_info (tr_sys_file_t handle,
|
||||
tr_sys_path_info * info,
|
||||
tr_error ** error)
|
||||
{
|
||||
bool ret;
|
||||
BY_HANDLE_FILE_INFORMATION attributes;
|
||||
|
||||
assert (handle != INVALID_HANDLE_VALUE);
|
||||
assert (handle != TR_BAD_SYS_FILE);
|
||||
assert (info != NULL);
|
||||
|
||||
ret = GetFileInformationByHandle (handle, &attributes);
|
||||
|
@ -450,3 +657,311 @@ get_file_info (HANDLE handle,
|
|||
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool
|
||||
tr_sys_file_seek (tr_sys_file_t handle,
|
||||
int64_t offset,
|
||||
tr_seek_origin_t origin,
|
||||
uint64_t * new_offset,
|
||||
tr_error ** error)
|
||||
{
|
||||
bool ret = false;
|
||||
LARGE_INTEGER native_offset, new_native_pointer;
|
||||
|
||||
TR_STATIC_ASSERT (TR_SEEK_SET == FILE_BEGIN, "values should match");
|
||||
TR_STATIC_ASSERT (TR_SEEK_CUR == FILE_CURRENT, "values should match");
|
||||
TR_STATIC_ASSERT (TR_SEEK_END == FILE_END, "values should match");
|
||||
|
||||
assert (handle != TR_BAD_SYS_FILE);
|
||||
assert (origin == TR_SEEK_SET || origin == TR_SEEK_CUR || origin == TR_SEEK_END);
|
||||
|
||||
native_offset.QuadPart = offset;
|
||||
|
||||
if (SetFilePointerEx (handle, native_offset, &new_native_pointer, origin))
|
||||
{
|
||||
if (new_offset != NULL)
|
||||
*new_offset = new_native_pointer.QuadPart;
|
||||
ret = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
set_system_error (error, GetLastError ());
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool
|
||||
tr_sys_file_read (tr_sys_file_t handle,
|
||||
void * buffer,
|
||||
uint64_t size,
|
||||
uint64_t * bytes_read,
|
||||
tr_error ** error)
|
||||
{
|
||||
bool ret = false;
|
||||
DWORD my_bytes_read;
|
||||
|
||||
assert (handle != TR_BAD_SYS_FILE);
|
||||
assert (buffer != NULL || size == 0);
|
||||
|
||||
if (size > MAXDWORD)
|
||||
{
|
||||
set_system_error (error, ERROR_INVALID_PARAMETER);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (ReadFile (handle, buffer, (DWORD)size, &my_bytes_read, NULL))
|
||||
{
|
||||
if (bytes_read != NULL)
|
||||
*bytes_read = my_bytes_read;
|
||||
ret = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
set_system_error (error, GetLastError ());
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool
|
||||
tr_sys_file_read_at (tr_sys_file_t handle,
|
||||
void * buffer,
|
||||
uint64_t size,
|
||||
uint64_t offset,
|
||||
uint64_t * bytes_read,
|
||||
tr_error ** error)
|
||||
{
|
||||
bool ret = false;
|
||||
OVERLAPPED overlapped;
|
||||
DWORD my_bytes_read;
|
||||
|
||||
assert (handle != TR_BAD_SYS_FILE);
|
||||
assert (buffer != NULL || size == 0);
|
||||
|
||||
if (size > MAXDWORD)
|
||||
{
|
||||
set_system_error (error, ERROR_INVALID_PARAMETER);
|
||||
return false;
|
||||
}
|
||||
|
||||
overlapped.Offset = (DWORD)offset;
|
||||
offset >>= 32;
|
||||
overlapped.OffsetHigh = (DWORD)offset;
|
||||
overlapped.hEvent = NULL;
|
||||
|
||||
if (ReadFile (handle, buffer, (DWORD)size, &my_bytes_read, &overlapped))
|
||||
{
|
||||
if (bytes_read != NULL)
|
||||
*bytes_read = my_bytes_read;
|
||||
ret = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
set_system_error (error, GetLastError ());
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool
|
||||
tr_sys_file_write (tr_sys_file_t handle,
|
||||
const void * buffer,
|
||||
uint64_t size,
|
||||
uint64_t * bytes_written,
|
||||
tr_error ** error)
|
||||
{
|
||||
bool ret = false;
|
||||
DWORD my_bytes_written;
|
||||
|
||||
assert (handle != TR_BAD_SYS_FILE);
|
||||
assert (buffer != NULL || size == 0);
|
||||
|
||||
if (size > MAXDWORD)
|
||||
{
|
||||
set_system_error (error, ERROR_INVALID_PARAMETER);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (WriteFile (handle, buffer, (DWORD)size, &my_bytes_written, NULL))
|
||||
{
|
||||
if (bytes_written != NULL)
|
||||
*bytes_written = my_bytes_written;
|
||||
ret = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
set_system_error (error, GetLastError ());
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool
|
||||
tr_sys_file_write_at (tr_sys_file_t handle,
|
||||
const void * buffer,
|
||||
uint64_t size,
|
||||
uint64_t offset,
|
||||
uint64_t * bytes_written,
|
||||
tr_error ** error)
|
||||
{
|
||||
bool ret = false;
|
||||
OVERLAPPED overlapped;
|
||||
DWORD my_bytes_written;
|
||||
|
||||
assert (handle != TR_BAD_SYS_FILE);
|
||||
assert (buffer != NULL || size == 0);
|
||||
|
||||
if (size > MAXDWORD)
|
||||
{
|
||||
set_system_error (error, ERROR_INVALID_PARAMETER);
|
||||
return false;
|
||||
}
|
||||
|
||||
overlapped.Offset = (DWORD)offset;
|
||||
offset >>= 32;
|
||||
overlapped.OffsetHigh = (DWORD)offset;
|
||||
overlapped.hEvent = NULL;
|
||||
|
||||
if (WriteFile (handle, buffer, (DWORD)size, &my_bytes_written, &overlapped))
|
||||
{
|
||||
if (bytes_written != NULL)
|
||||
*bytes_written = my_bytes_written;
|
||||
ret = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
set_system_error (error, GetLastError ());
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool
|
||||
tr_sys_file_flush (tr_sys_file_t handle,
|
||||
tr_error ** error)
|
||||
{
|
||||
bool ret;
|
||||
|
||||
assert (handle != TR_BAD_SYS_FILE);
|
||||
|
||||
ret = FlushFileBuffers (handle);
|
||||
|
||||
if (!ret)
|
||||
set_system_error (error, GetLastError ());
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool
|
||||
tr_sys_file_truncate (tr_sys_file_t handle,
|
||||
uint64_t size,
|
||||
tr_error ** error)
|
||||
{
|
||||
bool ret = false;
|
||||
FILE_END_OF_FILE_INFO info;
|
||||
|
||||
assert (handle != TR_BAD_SYS_FILE);
|
||||
|
||||
info.EndOfFile.QuadPart = size;
|
||||
|
||||
ret = SetFileInformationByHandle (handle, FileEndOfFileInfo, &info, sizeof (info));
|
||||
|
||||
if (!ret)
|
||||
set_system_error (error, GetLastError ());
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool
|
||||
tr_sys_file_prefetch (tr_sys_file_t handle,
|
||||
uint64_t offset,
|
||||
uint64_t size,
|
||||
tr_error ** error)
|
||||
{
|
||||
bool ret = false;
|
||||
|
||||
assert (handle != TR_BAD_SYS_FILE);
|
||||
assert (size > 0);
|
||||
|
||||
/* ??? */
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool
|
||||
tr_sys_file_preallocate (tr_sys_file_t handle,
|
||||
uint64_t size,
|
||||
int flags,
|
||||
tr_error ** error)
|
||||
{
|
||||
assert (handle != TR_BAD_SYS_FILE);
|
||||
|
||||
if ((flags & TR_SYS_FILE_PREALLOC_SPARSE) != 0)
|
||||
{
|
||||
DWORD tmp;
|
||||
if (!DeviceIoControl (handle, FSCTL_SET_SPARSE, NULL, 0, NULL, 0, &tmp, NULL))
|
||||
{
|
||||
set_system_error (error, GetLastError ());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return tr_sys_file_truncate (handle, size, error);
|
||||
}
|
||||
|
||||
void *
|
||||
tr_sys_file_map_for_reading (tr_sys_file_t handle,
|
||||
uint64_t offset,
|
||||
uint64_t size,
|
||||
tr_error ** error)
|
||||
{
|
||||
void * ret = NULL;
|
||||
HANDLE mappingHandle;
|
||||
|
||||
assert (handle != TR_BAD_SYS_FILE);
|
||||
assert (size > 0);
|
||||
|
||||
if (size > MAXSIZE_T)
|
||||
{
|
||||
set_system_error (error, ERROR_INVALID_PARAMETER);
|
||||
return false;
|
||||
}
|
||||
|
||||
mappingHandle = CreateFileMappingW (handle, NULL, PAGE_READONLY, 0, 0, NULL);
|
||||
|
||||
if (mappingHandle != NULL)
|
||||
{
|
||||
ULARGE_INTEGER native_offset;
|
||||
|
||||
native_offset.QuadPart = offset;
|
||||
|
||||
ret = MapViewOfFile (mappingHandle, FILE_MAP_READ, native_offset.u.HighPart,
|
||||
native_offset.u.LowPart, (SIZE_T)size);
|
||||
}
|
||||
|
||||
if (ret == NULL)
|
||||
set_system_error (error, GetLastError ());
|
||||
|
||||
CloseHandle (mappingHandle);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool
|
||||
tr_sys_file_unmap (const void * address,
|
||||
uint64_t size,
|
||||
tr_error ** error)
|
||||
{
|
||||
bool ret;
|
||||
|
||||
assert (address != NULL);
|
||||
assert (size > 0);
|
||||
|
||||
ret = UnmapViewOfFile (address);
|
||||
|
||||
if (!ret)
|
||||
set_system_error (error, GetLastError ());
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
|
|
@ -28,12 +28,56 @@ extern "C" {
|
|||
* @{
|
||||
*/
|
||||
|
||||
#ifndef _WIN32
|
||||
/** @brief Platform-specific file descriptor type. */
|
||||
typedef int tr_sys_file_t;
|
||||
/** @brief Platform-specific invalid file descriptor constant. */
|
||||
#define TR_BAD_SYS_FILE (-1)
|
||||
#else
|
||||
typedef HANDLE tr_sys_file_t;
|
||||
#define TR_BAD_SYS_FILE INVALID_HANDLE_VALUE
|
||||
#endif
|
||||
|
||||
typedef enum
|
||||
{
|
||||
TR_STD_SYS_FILE_IN,
|
||||
TR_STD_SYS_FILE_OUT,
|
||||
TR_STD_SYS_FILE_ERR
|
||||
}
|
||||
tr_std_sys_file_t;
|
||||
|
||||
typedef enum
|
||||
{
|
||||
TR_SYS_FILE_READ = 1 << 0,
|
||||
TR_SYS_FILE_WRITE = 1 << 1,
|
||||
TR_SYS_FILE_CREATE = 1 << 2,
|
||||
TR_SYS_FILE_CREATE_NEW = 1 << 3,
|
||||
TR_SYS_FILE_APPEND = 1 << 4,
|
||||
TR_SYS_FILE_TRUNCATE = 1 << 5,
|
||||
TR_SYS_FILE_SEQUENTIAL = 1 << 6
|
||||
}
|
||||
tr_sys_file_open_flags_t;
|
||||
|
||||
typedef enum
|
||||
{
|
||||
TR_SEEK_SET,
|
||||
TR_SEEK_CUR,
|
||||
TR_SEEK_END
|
||||
}
|
||||
tr_seek_origin_t;
|
||||
|
||||
typedef enum
|
||||
{
|
||||
TR_SYS_PATH_NO_FOLLOW = 1 << 0
|
||||
}
|
||||
tr_sys_path_get_info_flags_t;
|
||||
|
||||
typedef enum
|
||||
{
|
||||
TR_SYS_FILE_PREALLOC_SPARSE = 1 << 0
|
||||
}
|
||||
tr_sys_file_preallocate_flags_t;
|
||||
|
||||
typedef enum
|
||||
{
|
||||
TR_SYS_PATH_IS_FILE,
|
||||
|
@ -55,6 +99,9 @@ tr_sys_path_info;
|
|||
*
|
||||
* Following functions accept paths in UTF-8 encoding and convert them to native
|
||||
* encoding internally if needed.
|
||||
* Descriptors returned (@ref tr_sys_file_t) may have different type depending
|
||||
* on platform and should generally not be passed to native functions, but to
|
||||
* wrapper functions instead.
|
||||
*
|
||||
* @{
|
||||
*/
|
||||
|
@ -183,6 +230,273 @@ bool tr_sys_path_rename (const char * src_path,
|
|||
bool tr_sys_path_remove (const char * path,
|
||||
tr_error ** error);
|
||||
|
||||
/* File-related wrappers */
|
||||
|
||||
/**
|
||||
* @brief Get handle to one of standard I/O files.
|
||||
*
|
||||
* @param[in] std_file Standard file identifier.
|
||||
* @param[out] error Pointer to error object. Optional, pass `NULL` if you
|
||||
* are not interested in error details.
|
||||
*
|
||||
* @return Opened file descriptor on success, `TR_BAD_SYS_FILE` otherwise (with
|
||||
* `error` set accordingly). DO NOT pass this descriptor to
|
||||
* @ref tr_sys_file_close (unless you know what you are doing).
|
||||
*/
|
||||
tr_sys_file_t tr_sys_file_get_std (tr_std_sys_file_t std_file,
|
||||
tr_error ** error);
|
||||
|
||||
/**
|
||||
* @brief Portability wrapper for `open ()`.
|
||||
*
|
||||
* @param[in] path Path to file.
|
||||
* @param[in] flags Combination of @ref tr_sys_file_open_flags_t values.
|
||||
* @param[in] permissions Permissions to create file with (in case
|
||||
@ref TR_SYS_FILE_CREATE is used). Not used on Windows.
|
||||
* @param[out] error Pointer to error object. Optional, pass `NULL` if you
|
||||
* are not interested in error details.
|
||||
*
|
||||
* @return Opened file descriptor on success, `TR_BAD_SYS_FILE` otherwise (with
|
||||
* `error` set accordingly).
|
||||
*/
|
||||
tr_sys_file_t tr_sys_file_open (const char * path,
|
||||
int flags,
|
||||
int permissions,
|
||||
tr_error ** error);
|
||||
|
||||
/**
|
||||
* @brief Portability wrapper for `mkstemp ()`.
|
||||
*
|
||||
* @param[in,out] path_template Template path to file. Should end with at least
|
||||
* six 'X' characters. Upon success, trailing 'X'
|
||||
* characters are replaced with actual random
|
||||
* characters used to form a unique path to
|
||||
* temporary file.
|
||||
* @param[out] error Pointer to error object. Optional, pass `NULL`
|
||||
* if you are not interested in error details.
|
||||
*
|
||||
* @return Opened file descriptor on success, `TR_BAD_SYS_FILE` otherwise (with
|
||||
* `error` set accordingly).
|
||||
*/
|
||||
tr_sys_file_t tr_sys_file_open_temp (char * path_template,
|
||||
tr_error ** error);
|
||||
|
||||
/**
|
||||
* @brief Portability wrapper for `close ()`.
|
||||
*
|
||||
* @param[in] handle Valid file descriptor.
|
||||
* @param[out] error Pointer to error object. Optional, pass `NULL` if you are
|
||||
* not interested in error details.
|
||||
*
|
||||
* @return `True` on success, `false` otherwise (with `error` set accordingly).
|
||||
*/
|
||||
bool tr_sys_file_close (tr_sys_file_t handle,
|
||||
tr_error ** error);
|
||||
|
||||
/**
|
||||
* @brief Portability wrapper for `fstat ()`.
|
||||
*
|
||||
* @param[in] handle Valid file descriptor.
|
||||
* @param[out] info Result buffer.
|
||||
* @param[out] error Pointer to error object. Optional, pass `NULL` if you are
|
||||
* not interested in error details.
|
||||
*
|
||||
* @return `True` on success, `false` otherwise (with `error` set accordingly).
|
||||
*/
|
||||
bool tr_sys_file_get_info (tr_sys_file_t handle,
|
||||
tr_sys_path_info * info,
|
||||
tr_error ** error);
|
||||
|
||||
/**
|
||||
* @brief Portability wrapper for `lseek ()`.
|
||||
*
|
||||
* @param[in] handle Valid file descriptor.
|
||||
* @param[in] offset Relative file offset in bytes to seek to.
|
||||
* @param[in] origin Offset origin.
|
||||
* @param[out] new_offset New offset in bytes from beginning of file. Optional,
|
||||
* pass `NULL` if you are not interested.
|
||||
* @param[out] error Pointer to error object. Optional, pass `NULL` if you
|
||||
* are not interested in error details.
|
||||
*
|
||||
* @return `True` on success, `false` otherwise (with `error` set accordingly).
|
||||
*/
|
||||
bool tr_sys_file_seek (tr_sys_file_t handle,
|
||||
int64_t offset,
|
||||
tr_seek_origin_t origin,
|
||||
uint64_t * new_offset,
|
||||
tr_error ** error);
|
||||
|
||||
/**
|
||||
* @brief Portability wrapper for `read ()`.
|
||||
*
|
||||
* @param[in] handle Valid file descriptor.
|
||||
* @param[out] buffer Buffer to store read data to.
|
||||
* @param[in] size Number of bytes to read.
|
||||
* @param[out] bytes_read Number of bytes actually read. Optional, pass `NULL`
|
||||
* if you are not interested.
|
||||
* @param[out] error Pointer to error object. Optional, pass `NULL` if you
|
||||
* are not interested in error details.
|
||||
*
|
||||
* @return `True` on success, `false` otherwise (with `error` set accordingly).
|
||||
*/
|
||||
bool tr_sys_file_read (tr_sys_file_t handle,
|
||||
void * buffer,
|
||||
uint64_t size,
|
||||
uint64_t * bytes_read,
|
||||
tr_error ** error);
|
||||
|
||||
/**
|
||||
* @brief Like `pread ()`, except that the position is undefined afterwards.
|
||||
* Not thread-safe.
|
||||
*
|
||||
* @param[in] handle Valid file descriptor.
|
||||
* @param[out] buffer Buffer to store read data to.
|
||||
* @param[in] size Number of bytes to read.
|
||||
* @param[in] offset File offset in bytes to start reading from.
|
||||
* @param[out] bytes_read Number of bytes actually read. Optional, pass `NULL`
|
||||
* if you are not interested.
|
||||
* @param[out] error Pointer to error object. Optional, pass `NULL` if you
|
||||
* are not interested in error details.
|
||||
*
|
||||
* @return `True` on success, `false` otherwise (with `error` set accordingly).
|
||||
*/
|
||||
bool tr_sys_file_read_at (tr_sys_file_t handle,
|
||||
void * buffer,
|
||||
uint64_t size,
|
||||
uint64_t offset,
|
||||
uint64_t * bytes_read,
|
||||
tr_error ** error);
|
||||
|
||||
/**
|
||||
* @brief Portability wrapper for `write ()`.
|
||||
*
|
||||
* @param[in] handle Valid file descriptor.
|
||||
* @param[in] buffer Buffer to get data being written from.
|
||||
* @param[in] size Number of bytes to write.
|
||||
* @param[out] bytes_written Number of bytes actually written. Optional, pass
|
||||
* `NULL` if you are not interested.
|
||||
* @param[out] error Pointer to error object. Optional, pass `NULL` if
|
||||
* you are not interested in error details.
|
||||
*
|
||||
* @return `True` on success, `false` otherwise (with `error` set accordingly).
|
||||
*/
|
||||
bool tr_sys_file_write (tr_sys_file_t handle,
|
||||
const void * buffer,
|
||||
uint64_t size,
|
||||
uint64_t * bytes_written,
|
||||
tr_error ** error);
|
||||
|
||||
/**
|
||||
* @brief Like `pwrite ()`, except that the position is undefined afterwards.
|
||||
* Not thread-safe.
|
||||
*
|
||||
* @param[in] handle Valid file descriptor.
|
||||
* @param[in] buffer Buffer to get data being written from.
|
||||
* @param[in] size Number of bytes to write.
|
||||
* @param[in] offset File offset in bytes to start writing from.
|
||||
* @param[out] bytes_written Number of bytes actually written. Optional, pass
|
||||
* `NULL` if you are not interested.
|
||||
* @param[out] error Pointer to error object. Optional, pass `NULL` if you
|
||||
* are not interested in error details.
|
||||
*
|
||||
* @return `True` on success, `false` otherwise (with `error` set accordingly).
|
||||
*/
|
||||
bool tr_sys_file_write_at (tr_sys_file_t handle,
|
||||
const void * buffer,
|
||||
uint64_t size,
|
||||
uint64_t offset,
|
||||
uint64_t * bytes_written,
|
||||
tr_error ** error);
|
||||
|
||||
/**
|
||||
* @brief Portability wrapper for `fsync ()`.
|
||||
*
|
||||
* @param[in] handle Valid file descriptor.
|
||||
* @param[out] error Pointer to error object. Optional, pass `NULL` if you are
|
||||
* not interested in error details.
|
||||
*
|
||||
* @return `True` on success, `false` otherwise (with `error` set accordingly).
|
||||
*/
|
||||
bool tr_sys_file_flush (tr_sys_file_t handle,
|
||||
tr_error ** error);
|
||||
|
||||
/**
|
||||
* @brief Portability wrapper for `ftruncate ()`.
|
||||
*
|
||||
* @param[in] handle Valid file descriptor.
|
||||
* @param[in] size Number of bytes to truncate (or extend) file to.
|
||||
* @param[out] error Pointer to error object. Optional, pass `NULL` if you are
|
||||
* not interested in error details.
|
||||
*
|
||||
* @return `True` on success, `false` otherwise (with `error` set accordingly).
|
||||
*/
|
||||
bool tr_sys_file_truncate (tr_sys_file_t handle,
|
||||
uint64_t size,
|
||||
tr_error ** error);
|
||||
|
||||
/**
|
||||
* @brief Tell system to prefetch some part of file which is to be read soon.
|
||||
*
|
||||
* @param[in] handle Valid file descriptor.
|
||||
* @param[in] offset Offset in file to prefetch from.
|
||||
* @param[in] size Number of bytes to prefetch.
|
||||
* @param[out] error Pointer to error object. Optional, pass `NULL` if you are
|
||||
* not interested in error details.
|
||||
*
|
||||
* @return `True` on success, `false` otherwise (with `error` set accordingly).
|
||||
*/
|
||||
bool tr_sys_file_prefetch (tr_sys_file_t handle,
|
||||
uint64_t offset,
|
||||
uint64_t size,
|
||||
tr_error ** error);
|
||||
|
||||
/**
|
||||
* @brief Preallocate file to specified size in full or sparse mode.
|
||||
*
|
||||
* @param[in] handle Valid file descriptor.
|
||||
* @param[in] size Number of bytes to preallocate file to.
|
||||
* @param[in] flags Combination of @ref tr_sys_file_preallocate_flags_t values.
|
||||
* @param[out] error Pointer to error object. Optional, pass `NULL` if you are
|
||||
* not interested in error details.
|
||||
*
|
||||
* @return `True` on success, `false` otherwise (with `error` set accordingly).
|
||||
*/
|
||||
bool tr_sys_file_preallocate (tr_sys_file_t handle,
|
||||
uint64_t size,
|
||||
int flags,
|
||||
tr_error ** error);
|
||||
|
||||
/**
|
||||
* @brief Portability wrapper for `mmap ()` for files.
|
||||
*
|
||||
* @param[in] handle Valid file descriptor.
|
||||
* @param[in] offset Offset in file to map from.
|
||||
* @param[in] size Number of bytes to map.
|
||||
* @param[out] error Pointer to error object. Optional, pass `NULL` if you are
|
||||
* not interested in error details.
|
||||
*
|
||||
* @return Pointer to mapped file data on success, `NULL` otherwise (with
|
||||
* `error` set accordingly).
|
||||
*/
|
||||
void * tr_sys_file_map_for_reading (tr_sys_file_t handle,
|
||||
uint64_t offset,
|
||||
uint64_t size,
|
||||
tr_error ** error);
|
||||
|
||||
/**
|
||||
* @brief Portability wrapper for `munmap ()` for files.
|
||||
*
|
||||
* @param[in] address Pointer to mapped file data.
|
||||
* @param[in] size Size of mapped data in bytes.
|
||||
* @param[out] error Pointer to error object. Optional, pass `NULL` if you are
|
||||
* not interested in error details.
|
||||
*
|
||||
* @return `True` on success, `false` otherwise (with `error` set accordingly).
|
||||
*/
|
||||
bool tr_sys_file_unmap (const void * address,
|
||||
uint64_t size,
|
||||
tr_error ** error);
|
||||
|
||||
/** @} */
|
||||
/** @} */
|
||||
|
||||
|
|
|
@ -411,7 +411,7 @@ static inline void lpd_consistencyCheck (void)
|
|||
* without our knowledge; revise string handling in functions tr_lpdSendAnnounce
|
||||
* and tr_lpdConsiderAnnounce. However, the code is designed to function as long
|
||||
* as interfaces to the rest of the lib remain compatible with char* strings. */
|
||||
STATIC_ASSERT (sizeof (lpd_torStaticType->info.hashString[0]) == sizeof (char));
|
||||
TR_STATIC_ASSERT (sizeof (lpd_torStaticType->info.hashString[0]) == sizeof (char), "");
|
||||
}
|
||||
/**
|
||||
* @endcond */
|
||||
|
|
|
@ -45,11 +45,6 @@ bool tr_lpdSendAnnounce (const tr_torrent*);
|
|||
* Meaningful return values are only guaranteed for true array types. */
|
||||
#define lengthof(arr)(sizeof (* (arr)) > 0 ? sizeof (arr) / sizeof (* (arr)) : 0)
|
||||
|
||||
/**
|
||||
* @def STATIC_ASSERT
|
||||
* @brief This helper allows to perform static checks at compile time */
|
||||
#define STATIC_ASSERT(x) { const char static_check[ ((x) ? 1 : -1)] UNUSED; }
|
||||
|
||||
/**
|
||||
* @} */
|
||||
|
||||
|
|
|
@ -70,6 +70,26 @@ extern "C" {
|
|||
#endif
|
||||
|
||||
|
||||
#ifndef __has_feature
|
||||
#define __has_feature(x) 0
|
||||
#endif
|
||||
#ifndef __has_extension
|
||||
#define __has_extension __has_feature
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @def TR_STATIC_ASSERT
|
||||
* @brief This helper allows to perform static checks at compile time
|
||||
*/
|
||||
#if defined (static_assert)
|
||||
#define TR_STATIC_ASSERT static_assert
|
||||
#elif __has_feature (c_static_assert) || __has_extension (c_static_assert)
|
||||
#define TR_STATIC_ASSERT _Static_assert
|
||||
#else
|
||||
#define TR_STATIC_ASSERT(x, msg) { const char static_check[((x) ? 1 : -1)] UNUSED; }
|
||||
#endif
|
||||
|
||||
|
||||
/***
|
||||
****
|
||||
***/
|
||||
|
|
Loading…
Add table
Reference in a new issue