mirror of
https://github.com/trapexit/mergerfs.git
synced 2026-10-26 02:48:59 -04:00
Add support for FUSE_COPY_FILE_RANGE_64
This commit is contained in:
parent
1ebc8cfa5f
commit
041ece5d09
7 changed files with 160 additions and 41 deletions
|
|
@ -71,6 +71,52 @@ def main():
|
|||
os.close(mdfd)
|
||||
os.close(ndfd)
|
||||
|
||||
# Exercise very large copy lengths to validate the 64-bit copy result
|
||||
# path when available. Use sparse files to keep runtime and disk usage low.
|
||||
large_len = (5 << 30) + 4096
|
||||
merge_large_src = join(mount, "posix-cfr/src-large")
|
||||
merge_large_dst = join(mount, "posix-cfr/dst-large")
|
||||
native_large_src = join(native, "posix-cfr/src-large")
|
||||
native_large_dst = join(native, "posix-cfr/dst-large")
|
||||
touch(merge_large_src, b"")
|
||||
touch(merge_large_dst, b"")
|
||||
touch(native_large_src, b"")
|
||||
touch(native_large_dst, b"")
|
||||
|
||||
msfd = os.open(merge_large_src, os.O_RDWR)
|
||||
mdfd = os.open(merge_large_dst, os.O_RDWR)
|
||||
nsfd = os.open(native_large_src, os.O_RDWR)
|
||||
ndfd = os.open(native_large_dst, os.O_RDWR)
|
||||
try:
|
||||
# Make sparse files with one non-zero byte at the end so both sides
|
||||
# have comparable logical contents.
|
||||
os.ftruncate(msfd, large_len)
|
||||
os.ftruncate(nsfd, large_len)
|
||||
os.pwrite(msfd, b"z", large_len - 1)
|
||||
os.pwrite(nsfd, b"z", large_len - 1)
|
||||
|
||||
err = compare_calls(
|
||||
"copy_file_range large sparse",
|
||||
lambda: os.copy_file_range(msfd, mdfd, large_len),
|
||||
lambda: os.copy_file_range(nsfd, ndfd, large_len),
|
||||
lambda a, b: a == b,
|
||||
)
|
||||
if err:
|
||||
return fail(err)
|
||||
|
||||
mstat = os.fstat(mdfd)
|
||||
nstat = os.fstat(ndfd)
|
||||
if mstat.st_size != nstat.st_size:
|
||||
return fail(
|
||||
"copy_file_range large sparse dst size mismatch "
|
||||
f"mergerfs={mstat.st_size} native={nstat.st_size}"
|
||||
)
|
||||
finally:
|
||||
os.close(msfd)
|
||||
os.close(mdfd)
|
||||
os.close(nsfd)
|
||||
os.close(ndfd)
|
||||
|
||||
return 0
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -2,11 +2,15 @@
|
|||
|
||||
#include <stdint.h>
|
||||
|
||||
typedef uint16_t u16;
|
||||
typedef int16_t s16;
|
||||
typedef uint32_t u32;
|
||||
typedef int32_t s32;
|
||||
typedef uint64_t u64;
|
||||
typedef int64_t s64;
|
||||
|
||||
typedef const uint16_t cu16;
|
||||
typedef const int16_t cs16;
|
||||
typedef const uint32_t cu32;
|
||||
typedef const int32_t cs32;
|
||||
typedef const uint64_t cu64;
|
||||
|
|
|
|||
|
|
@ -229,6 +229,17 @@
|
|||
* - FUSE_URING_IN_OUT_HEADER_SZ
|
||||
* - FUSE_URING_OP_IN_OUT_SZ
|
||||
* - enum fuse_uring_cmd
|
||||
*
|
||||
* 7.43
|
||||
* - add FUSE_REQUEST_TIMEOUT
|
||||
*
|
||||
* 7.44
|
||||
* - add FUSE_NOTIFY_INC_EPOCH
|
||||
*
|
||||
* 7.45
|
||||
* - add FUSE_COPY_FILE_RANGE_64
|
||||
* - add struct fuse_copy_file_range_out
|
||||
* - add FUSE_NOTIFY_PRUNE
|
||||
*/
|
||||
|
||||
#ifndef _LINUX_FUSE_H
|
||||
|
|
@ -264,7 +275,7 @@
|
|||
#define FUSE_KERNEL_VERSION 7
|
||||
|
||||
/** Minor version number of this interface */
|
||||
#define FUSE_KERNEL_MINOR_VERSION 42
|
||||
#define FUSE_KERNEL_MINOR_VERSION 45
|
||||
|
||||
/** The node ID of the root inode */
|
||||
#define FUSE_ROOT_ID 1
|
||||
|
|
@ -435,39 +446,41 @@ struct fuse_file_lock {
|
|||
* of the request ID indicates resend requests
|
||||
* FUSE_ALLOW_IDMAP: allow creation of idmapped mounts
|
||||
* FUSE_OVER_IO_URING: Indicate that client supports io-uring
|
||||
* FUSE_REQUEST_TIMEOUT: kernel supports timing out requests.
|
||||
* init_out.request_timeout contains the timeout (in secs)
|
||||
*/
|
||||
#define FUSE_ASYNC_READ (1ULL << 0)
|
||||
#define FUSE_POSIX_LOCKS (1ULL << 1)
|
||||
#define FUSE_FILE_OPS (1ULL << 2)
|
||||
#define FUSE_ATOMIC_O_TRUNC (1ULL << 3)
|
||||
#define FUSE_EXPORT_SUPPORT (1ULL << 4)
|
||||
#define FUSE_BIG_WRITES (1ULL << 5)
|
||||
#define FUSE_DONT_MASK (1ULL << 6)
|
||||
#define FUSE_SPLICE_WRITE (1ULL << 7)
|
||||
#define FUSE_SPLICE_MOVE (1ULL << 8)
|
||||
#define FUSE_SPLICE_READ (1ULL << 9)
|
||||
#define FUSE_FLOCK_LOCKS (1ULL << 10)
|
||||
#define FUSE_HAS_IOCTL_DIR (1ULL << 11)
|
||||
#define FUSE_AUTO_INVAL_DATA (1ULL << 12)
|
||||
#define FUSE_DO_READDIRPLUS (1ULL << 13)
|
||||
#define FUSE_READDIRPLUS_AUTO (1ULL << 14)
|
||||
#define FUSE_ASYNC_DIO (1ULL << 15)
|
||||
#define FUSE_WRITEBACK_CACHE (1ULL << 16)
|
||||
#define FUSE_NO_OPEN_SUPPORT (1ULL << 17)
|
||||
#define FUSE_PARALLEL_DIROPS (1ULL << 18)
|
||||
#define FUSE_HANDLE_KILLPRIV (1ULL << 19)
|
||||
#define FUSE_POSIX_ACL (1ULL << 20)
|
||||
#define FUSE_ABORT_ERROR (1ULL << 21)
|
||||
#define FUSE_MAX_PAGES (1ULL << 22)
|
||||
#define FUSE_CACHE_SYMLINKS (1ULL << 23)
|
||||
#define FUSE_NO_OPENDIR_SUPPORT (1ULL << 24)
|
||||
#define FUSE_EXPLICIT_INVAL_DATA (1ULL << 25)
|
||||
#define FUSE_MAP_ALIGNMENT (1ULL << 26)
|
||||
#define FUSE_SUBMOUNTS (1ULL << 27)
|
||||
#define FUSE_HANDLE_KILLPRIV_V2 (1ULL << 28)
|
||||
#define FUSE_SETXATTR_EXT (1ULL << 29)
|
||||
#define FUSE_INIT_EXT (1ULL << 30)
|
||||
#define FUSE_INIT_RESERVED (1ULL << 31)
|
||||
#define FUSE_ASYNC_READ (1 << 0)
|
||||
#define FUSE_POSIX_LOCKS (1 << 1)
|
||||
#define FUSE_FILE_OPS (1 << 2)
|
||||
#define FUSE_ATOMIC_O_TRUNC (1 << 3)
|
||||
#define FUSE_EXPORT_SUPPORT (1 << 4)
|
||||
#define FUSE_BIG_WRITES (1 << 5)
|
||||
#define FUSE_DONT_MASK (1 << 6)
|
||||
#define FUSE_SPLICE_WRITE (1 << 7)
|
||||
#define FUSE_SPLICE_MOVE (1 << 8)
|
||||
#define FUSE_SPLICE_READ (1 << 9)
|
||||
#define FUSE_FLOCK_LOCKS (1 << 10)
|
||||
#define FUSE_HAS_IOCTL_DIR (1 << 11)
|
||||
#define FUSE_AUTO_INVAL_DATA (1 << 12)
|
||||
#define FUSE_DO_READDIRPLUS (1 << 13)
|
||||
#define FUSE_READDIRPLUS_AUTO (1 << 14)
|
||||
#define FUSE_ASYNC_DIO (1 << 15)
|
||||
#define FUSE_WRITEBACK_CACHE (1 << 16)
|
||||
#define FUSE_NO_OPEN_SUPPORT (1 << 17)
|
||||
#define FUSE_PARALLEL_DIROPS (1 << 18)
|
||||
#define FUSE_HANDLE_KILLPRIV (1 << 19)
|
||||
#define FUSE_POSIX_ACL (1 << 20)
|
||||
#define FUSE_ABORT_ERROR (1 << 21)
|
||||
#define FUSE_MAX_PAGES (1 << 22)
|
||||
#define FUSE_CACHE_SYMLINKS (1 << 23)
|
||||
#define FUSE_NO_OPENDIR_SUPPORT (1 << 24)
|
||||
#define FUSE_EXPLICIT_INVAL_DATA (1 << 25)
|
||||
#define FUSE_MAP_ALIGNMENT (1 << 26)
|
||||
#define FUSE_SUBMOUNTS (1 << 27)
|
||||
#define FUSE_HANDLE_KILLPRIV_V2 (1 << 28)
|
||||
#define FUSE_SETXATTR_EXT (1 << 29)
|
||||
#define FUSE_INIT_EXT (1 << 30)
|
||||
#define FUSE_INIT_RESERVED (1U << 31)
|
||||
/* bits 32..63 get shifted down 32 bits into the flags2 field */
|
||||
#define FUSE_SECURITY_CTX (1ULL << 32)
|
||||
#define FUSE_HAS_INODE_DAX (1ULL << 33)
|
||||
|
|
@ -477,11 +490,11 @@ struct fuse_file_lock {
|
|||
#define FUSE_PASSTHROUGH (1ULL << 37)
|
||||
#define FUSE_NO_EXPORT_SUPPORT (1ULL << 38)
|
||||
#define FUSE_HAS_RESEND (1ULL << 39)
|
||||
|
||||
/* Obsolete alias for FUSE_DIRECT_IO_ALLOW_MMAP */
|
||||
#define FUSE_DIRECT_IO_RELAX FUSE_DIRECT_IO_ALLOW_MMAP
|
||||
#define FUSE_ALLOW_IDMAP (1ULL << 40)
|
||||
#define FUSE_OVER_IO_URING (1ULL << 41)
|
||||
#define FUSE_REQUEST_TIMEOUT (1ULL << 42)
|
||||
|
||||
/**
|
||||
* CUSE INIT request/reply flags
|
||||
|
|
@ -649,6 +662,7 @@ enum fuse_opcode {
|
|||
FUSE_SYNCFS = 50,
|
||||
FUSE_TMPFILE = 51,
|
||||
FUSE_STATX = 52,
|
||||
FUSE_COPY_FILE_RANGE_64 = 53,
|
||||
|
||||
/* CUSE specific operations */
|
||||
CUSE_INIT = 4096,
|
||||
|
|
@ -666,7 +680,8 @@ enum fuse_notify_code {
|
|||
FUSE_NOTIFY_RETRIEVE = 5,
|
||||
FUSE_NOTIFY_DELETE = 6,
|
||||
FUSE_NOTIFY_RESEND = 7,
|
||||
FUSE_NOTIFY_CODE_MAX,
|
||||
FUSE_NOTIFY_INC_EPOCH = 8,
|
||||
FUSE_NOTIFY_PRUNE = 9,
|
||||
};
|
||||
|
||||
/* The read buffer is required to be at least 8k, but may be much larger */
|
||||
|
|
@ -909,7 +924,8 @@ struct fuse_init_out {
|
|||
uint16_t map_alignment;
|
||||
uint32_t flags2;
|
||||
uint32_t max_stack_depth;
|
||||
uint32_t unused[6];
|
||||
uint16_t request_timeout;
|
||||
uint16_t unused[11];
|
||||
};
|
||||
|
||||
#define CUSE_INIT_INFO_MAX 4096
|
||||
|
|
@ -1104,6 +1120,12 @@ struct fuse_notify_retrieve_in {
|
|||
uint64_t dummy4;
|
||||
};
|
||||
|
||||
struct fuse_notify_prune_out {
|
||||
uint32_t count;
|
||||
uint32_t padding;
|
||||
uint64_t spare;
|
||||
};
|
||||
|
||||
struct fuse_backing_map {
|
||||
int32_t fd;
|
||||
uint32_t flags;
|
||||
|
|
@ -1116,6 +1138,7 @@ struct fuse_backing_map {
|
|||
#define FUSE_DEV_IOC_BACKING_OPEN _IOW(FUSE_DEV_IOC_MAGIC, 1, \
|
||||
struct fuse_backing_map)
|
||||
#define FUSE_DEV_IOC_BACKING_CLOSE _IOW(FUSE_DEV_IOC_MAGIC, 2, uint32_t)
|
||||
#define FUSE_DEV_IOC_SYNC_INIT _IO(FUSE_DEV_IOC_MAGIC, 3)
|
||||
|
||||
struct fuse_lseek_in {
|
||||
uint64_t fh;
|
||||
|
|
@ -1138,6 +1161,11 @@ struct fuse_copy_file_range_in {
|
|||
uint64_t flags;
|
||||
};
|
||||
|
||||
/* For FUSE_COPY_FILE_RANGE_64 */
|
||||
struct fuse_copy_file_range_out {
|
||||
uint64_t bytes_copied;
|
||||
};
|
||||
|
||||
#define FUSE_SETUPMAPPING_FLAG_WRITE (1ull << 0)
|
||||
#define FUSE_SETUPMAPPING_FLAG_READ (1ull << 1)
|
||||
struct fuse_setupmapping_in {
|
||||
|
|
|
|||
|
|
@ -271,6 +271,8 @@ int fuse_reply_open(fuse_req_t *req,
|
|||
*/
|
||||
int fuse_reply_write(fuse_req_t *req, size_t count);
|
||||
|
||||
int fuse_reply_copy_file_range_64(fuse_req_t *req, size_t count);
|
||||
|
||||
/**
|
||||
* Reply with data
|
||||
*
|
||||
|
|
|
|||
|
|
@ -226,7 +226,7 @@ _open_flag_to_str(const uint64_t offset_)
|
|||
|
||||
#undef OPEN_FLAG_CASE
|
||||
|
||||
#define FUSE_INIT_FLAG_CASE(X) case FUSE_##X: return #X
|
||||
#define FUSE_INIT_FLAG_CASE(X) case static_cast<uint64_t>(FUSE_##X): return #X
|
||||
|
||||
static
|
||||
const
|
||||
|
|
@ -277,6 +277,7 @@ _fuse_init_flag_name(const uint64_t flag_)
|
|||
FUSE_INIT_FLAG_CASE(HAS_RESEND);
|
||||
FUSE_INIT_FLAG_CASE(ALLOW_IDMAP);
|
||||
FUSE_INIT_FLAG_CASE(OVER_IO_URING);
|
||||
FUSE_INIT_FLAG_CASE(REQUEST_TIMEOUT);
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
|
|
@ -1493,7 +1494,8 @@ _opcode_name(enum fuse_opcode op_)
|
|||
"REMOVEMAPPING",
|
||||
"SYNCFS",
|
||||
"TMPFILE",
|
||||
"STATX"
|
||||
"STATX",
|
||||
"COPY_FILE_RANGE_64"
|
||||
};
|
||||
|
||||
if(op_ >= (sizeof(names) / sizeof(names[0])))
|
||||
|
|
@ -1653,6 +1655,7 @@ fuse_debug_in_header(const struct fuse_in_header *hdr_)
|
|||
_debug_fuse_lseek_in(arg);
|
||||
break;
|
||||
case FUSE_COPY_FILE_RANGE:
|
||||
case FUSE_COPY_FILE_RANGE_64:
|
||||
_debug_fuse_copy_file_range_in(arg);
|
||||
break;
|
||||
case FUSE_TMPFILE:
|
||||
|
|
|
|||
|
|
@ -2711,8 +2711,17 @@ fuse_lib_copy_file_range(fuse_req_t *req_,
|
|||
fuse_file_info_t ffi_in = {};
|
||||
fuse_file_info_t ffi_out = {};
|
||||
const struct fuse_copy_file_range_in *arg;
|
||||
struct fuse_copy_file_range_in arg_tmp;
|
||||
|
||||
arg = (fuse_copy_file_range_in*)fuse_hdr_arg(hdr_);
|
||||
if(((hdr_->opcode == FUSE_COPY_FILE_RANGE) || (sizeof(size_t) == 4)) &&
|
||||
(arg->len > 0xfffff000ULL))
|
||||
{
|
||||
/* Avoid truncation in the old reply format or 32-bit size_t conversion. */
|
||||
arg_tmp = *arg;
|
||||
arg_tmp.len = 0xfffff000ULL;
|
||||
arg = &arg_tmp;
|
||||
}
|
||||
ffi_in.fh = arg->fh_in;
|
||||
ffi_out.fh = arg->fh_out;
|
||||
|
||||
|
|
@ -2725,7 +2734,12 @@ fuse_lib_copy_file_range(fuse_req_t *req_,
|
|||
arg->flags);
|
||||
|
||||
if(rv >= 0)
|
||||
fuse_reply_write(req_,rv);
|
||||
{
|
||||
if(hdr_->opcode == FUSE_COPY_FILE_RANGE_64)
|
||||
fuse_reply_copy_file_range_64(req_,rv);
|
||||
else
|
||||
fuse_reply_write(req_,rv);
|
||||
}
|
||||
else
|
||||
fuse_reply_err(req_,rv);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -395,6 +395,17 @@ fuse_reply_write(fuse_req_t *req,
|
|||
return send_reply_ok(req, &arg, sizeof(arg));
|
||||
}
|
||||
|
||||
int
|
||||
fuse_reply_copy_file_range_64(fuse_req_t *req,
|
||||
size_t count)
|
||||
{
|
||||
struct fuse_copy_file_range_out arg = {};
|
||||
|
||||
arg.bytes_copied = count;
|
||||
|
||||
return send_reply_ok(req, &arg, sizeof(arg));
|
||||
}
|
||||
|
||||
int
|
||||
fuse_reply_buf(fuse_req_t *req,
|
||||
const char *buf,
|
||||
|
|
@ -1359,6 +1370,14 @@ do_copy_file_range(fuse_req_t *req_,
|
|||
f.op.copy_file_range(req_,hdr_);
|
||||
}
|
||||
|
||||
static
|
||||
void
|
||||
do_copy_file_range_64(fuse_req_t *req_,
|
||||
struct fuse_in_header *hdr_)
|
||||
{
|
||||
f.op.copy_file_range(req_,hdr_);
|
||||
}
|
||||
|
||||
static
|
||||
void
|
||||
do_setupmapping(fuse_req_t *req_,
|
||||
|
|
@ -1602,7 +1621,7 @@ fuse_lowlevel_notify_retrieve(struct fuse_session *se,
|
|||
return err;
|
||||
}
|
||||
|
||||
#define FUSE_OPCODE_LEN (FUSE_STATX + 1)
|
||||
#define FUSE_OPCODE_LEN (FUSE_COPY_FILE_RANGE_64 + 1)
|
||||
|
||||
typedef void (*fuse_ll_func)(fuse_req_t*, struct fuse_in_header *);
|
||||
const
|
||||
|
|
@ -1661,7 +1680,8 @@ fuse_ll_funcs[FUSE_OPCODE_LEN] =
|
|||
do_removemapping,
|
||||
do_syncfs,
|
||||
do_tmpfile,
|
||||
do_statx
|
||||
do_statx,
|
||||
do_copy_file_range_64
|
||||
};
|
||||
|
||||
#define FUSE_MAXOPS (sizeof(fuse_ll_funcs) / sizeof(fuse_ll_funcs[0]))
|
||||
|
|
@ -1757,6 +1777,7 @@ fuse_ll_buf_process_read(struct fuse_session *se_,
|
|||
req->ctx.umask = 0;
|
||||
req->conn = f.conn;
|
||||
req->se = se_;
|
||||
req->ioctl_64bit = 0;
|
||||
|
||||
err = ENOSYS;
|
||||
if(in->opcode >= FUSE_MAXOPS)
|
||||
|
|
@ -1795,6 +1816,7 @@ fuse_ll_buf_process_read_init(struct fuse_session *se_,
|
|||
req->ctx.opcode = in->opcode;
|
||||
req->ctx.unique = in->unique;
|
||||
req->ctx.nodeid = in->nodeid;
|
||||
req->ioctl_64bit = 0;
|
||||
req->ctx.uid = in->uid;
|
||||
req->ctx.gid = in->gid;
|
||||
req->ctx.pid = in->pid;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue