mirror of
https://github.com/torvalds/linux.git
synced 2026-05-25 23:52:08 +02:00
fuse: add COPY_FILE_RANGE_64 that allows large copies
The FUSE protocol uses struct fuse_write_out to convey the return value of copy_file_range, which is restricted to uint32_t. But the COPY_FILE_RANGE interface supports a 64-bit size copies and there's no reason why copies should be limited to 32-bit. Introduce a new op COPY_FILE_RANGE_64, which is identical, except the number of bytes copied is returned in a 64-bit value. If the fuse server does not support COPY_FILE_RANGE_64, fall back to COPY_FILE_RANGE. Reported-by: Florian Weimer <fweimer@redhat.com> Closes: https://lore.kernel.org/all/lhuh5ynl8z5.fsf@oldenburg.str.redhat.com/ Signed-off-by: Miklos Szeredi <mszeredi@redhat.com>
This commit is contained in:
parent
9d81ba6d49
commit
7a37f55af7
|
|
@ -2960,10 +2960,12 @@ static ssize_t __fuse_copy_file_range(struct file *file_in, loff_t pos_in,
|
|||
.nodeid_out = ff_out->nodeid,
|
||||
.fh_out = ff_out->fh,
|
||||
.off_out = pos_out,
|
||||
.len = min_t(size_t, len, UINT_MAX & PAGE_MASK),
|
||||
.len = len,
|
||||
.flags = flags
|
||||
};
|
||||
struct fuse_write_out outarg;
|
||||
struct fuse_copy_file_range_out outarg_64;
|
||||
u64 bytes_copied;
|
||||
ssize_t err;
|
||||
/* mark unstable when write-back is not used, and file_out gets
|
||||
* extended */
|
||||
|
|
@ -3013,33 +3015,51 @@ static ssize_t __fuse_copy_file_range(struct file *file_in, loff_t pos_in,
|
|||
if (is_unstable)
|
||||
set_bit(FUSE_I_SIZE_UNSTABLE, &fi_out->state);
|
||||
|
||||
args.opcode = FUSE_COPY_FILE_RANGE;
|
||||
args.opcode = FUSE_COPY_FILE_RANGE_64;
|
||||
args.nodeid = ff_in->nodeid;
|
||||
args.in_numargs = 1;
|
||||
args.in_args[0].size = sizeof(inarg);
|
||||
args.in_args[0].value = &inarg;
|
||||
args.out_numargs = 1;
|
||||
args.out_args[0].size = sizeof(outarg);
|
||||
args.out_args[0].value = &outarg;
|
||||
args.out_args[0].size = sizeof(outarg_64);
|
||||
args.out_args[0].value = &outarg_64;
|
||||
if (fc->no_copy_file_range_64) {
|
||||
fallback:
|
||||
/* Fall back to old op that can't handle large copy length */
|
||||
args.opcode = FUSE_COPY_FILE_RANGE;
|
||||
args.out_args[0].size = sizeof(outarg);
|
||||
args.out_args[0].value = &outarg;
|
||||
inarg.len = len = min_t(size_t, len, UINT_MAX & PAGE_MASK);
|
||||
}
|
||||
err = fuse_simple_request(fm, &args);
|
||||
if (err == -ENOSYS) {
|
||||
fc->no_copy_file_range = 1;
|
||||
err = -EOPNOTSUPP;
|
||||
if (fc->no_copy_file_range_64) {
|
||||
fc->no_copy_file_range = 1;
|
||||
err = -EOPNOTSUPP;
|
||||
} else {
|
||||
fc->no_copy_file_range_64 = 1;
|
||||
goto fallback;
|
||||
}
|
||||
}
|
||||
if (!err && outarg.size > len)
|
||||
err = -EIO;
|
||||
|
||||
if (err)
|
||||
goto out;
|
||||
|
||||
bytes_copied = fc->no_copy_file_range_64 ?
|
||||
outarg.size : outarg_64.bytes_copied;
|
||||
|
||||
if (bytes_copied > len) {
|
||||
err = -EIO;
|
||||
goto out;
|
||||
}
|
||||
|
||||
truncate_inode_pages_range(inode_out->i_mapping,
|
||||
ALIGN_DOWN(pos_out, PAGE_SIZE),
|
||||
ALIGN(pos_out + outarg.size, PAGE_SIZE) - 1);
|
||||
ALIGN(pos_out + bytes_copied, PAGE_SIZE) - 1);
|
||||
|
||||
file_update_time(file_out);
|
||||
fuse_write_update_attr(inode_out, pos_out + outarg.size, outarg.size);
|
||||
fuse_write_update_attr(inode_out, pos_out + bytes_copied, bytes_copied);
|
||||
|
||||
err = outarg.size;
|
||||
err = bytes_copied;
|
||||
out:
|
||||
if (is_unstable)
|
||||
clear_bit(FUSE_I_SIZE_UNSTABLE, &fi_out->state);
|
||||
|
|
|
|||
|
|
@ -856,6 +856,9 @@ struct fuse_conn {
|
|||
/** Does the filesystem support copy_file_range? */
|
||||
unsigned no_copy_file_range:1;
|
||||
|
||||
/** Does the filesystem support copy_file_range_64? */
|
||||
unsigned no_copy_file_range_64:1;
|
||||
|
||||
/* Send DESTROY request */
|
||||
unsigned int destroy:1;
|
||||
|
||||
|
|
|
|||
|
|
@ -235,6 +235,10 @@
|
|||
*
|
||||
* 7.44
|
||||
* - add FUSE_NOTIFY_INC_EPOCH
|
||||
*
|
||||
* 7.45
|
||||
* - add FUSE_COPY_FILE_RANGE_64
|
||||
* - add struct fuse_copy_file_range_out
|
||||
*/
|
||||
|
||||
#ifndef _LINUX_FUSE_H
|
||||
|
|
@ -270,7 +274,7 @@
|
|||
#define FUSE_KERNEL_VERSION 7
|
||||
|
||||
/** Minor version number of this interface */
|
||||
#define FUSE_KERNEL_MINOR_VERSION 44
|
||||
#define FUSE_KERNEL_MINOR_VERSION 45
|
||||
|
||||
/** The node ID of the root inode */
|
||||
#define FUSE_ROOT_ID 1
|
||||
|
|
@ -657,6 +661,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,
|
||||
|
|
@ -1148,6 +1153,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 {
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user