mirror of
https://github.com/torvalds/linux.git
synced 2026-11-07 06:17:02 +01: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,
|
.nodeid_out = ff_out->nodeid,
|
||||||
.fh_out = ff_out->fh,
|
.fh_out = ff_out->fh,
|
||||||
.off_out = pos_out,
|
.off_out = pos_out,
|
||||||
.len = min_t(size_t, len, UINT_MAX & PAGE_MASK),
|
.len = len,
|
||||||
.flags = flags
|
.flags = flags
|
||||||
};
|
};
|
||||||
struct fuse_write_out outarg;
|
struct fuse_write_out outarg;
|
||||||
|
struct fuse_copy_file_range_out outarg_64;
|
||||||
|
u64 bytes_copied;
|
||||||
ssize_t err;
|
ssize_t err;
|
||||||
/* mark unstable when write-back is not used, and file_out gets
|
/* mark unstable when write-back is not used, and file_out gets
|
||||||
* extended */
|
* extended */
|
||||||
|
|
@ -3013,33 +3015,51 @@ static ssize_t __fuse_copy_file_range(struct file *file_in, loff_t pos_in,
|
||||||
if (is_unstable)
|
if (is_unstable)
|
||||||
set_bit(FUSE_I_SIZE_UNSTABLE, &fi_out->state);
|
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.nodeid = ff_in->nodeid;
|
||||||
args.in_numargs = 1;
|
args.in_numargs = 1;
|
||||||
args.in_args[0].size = sizeof(inarg);
|
args.in_args[0].size = sizeof(inarg);
|
||||||
args.in_args[0].value = &inarg;
|
args.in_args[0].value = &inarg;
|
||||||
args.out_numargs = 1;
|
args.out_numargs = 1;
|
||||||
args.out_args[0].size = sizeof(outarg);
|
args.out_args[0].size = sizeof(outarg_64);
|
||||||
args.out_args[0].value = &outarg;
|
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);
|
err = fuse_simple_request(fm, &args);
|
||||||
if (err == -ENOSYS) {
|
if (err == -ENOSYS) {
|
||||||
fc->no_copy_file_range = 1;
|
if (fc->no_copy_file_range_64) {
|
||||||
err = -EOPNOTSUPP;
|
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)
|
if (err)
|
||||||
goto out;
|
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,
|
truncate_inode_pages_range(inode_out->i_mapping,
|
||||||
ALIGN_DOWN(pos_out, PAGE_SIZE),
|
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);
|
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:
|
out:
|
||||||
if (is_unstable)
|
if (is_unstable)
|
||||||
clear_bit(FUSE_I_SIZE_UNSTABLE, &fi_out->state);
|
clear_bit(FUSE_I_SIZE_UNSTABLE, &fi_out->state);
|
||||||
|
|
|
||||||
|
|
@ -856,6 +856,9 @@ struct fuse_conn {
|
||||||
/** Does the filesystem support copy_file_range? */
|
/** Does the filesystem support copy_file_range? */
|
||||||
unsigned no_copy_file_range:1;
|
unsigned no_copy_file_range:1;
|
||||||
|
|
||||||
|
/** Does the filesystem support copy_file_range_64? */
|
||||||
|
unsigned no_copy_file_range_64:1;
|
||||||
|
|
||||||
/* Send DESTROY request */
|
/* Send DESTROY request */
|
||||||
unsigned int destroy:1;
|
unsigned int destroy:1;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -235,6 +235,10 @@
|
||||||
*
|
*
|
||||||
* 7.44
|
* 7.44
|
||||||
* - add FUSE_NOTIFY_INC_EPOCH
|
* - add FUSE_NOTIFY_INC_EPOCH
|
||||||
|
*
|
||||||
|
* 7.45
|
||||||
|
* - add FUSE_COPY_FILE_RANGE_64
|
||||||
|
* - add struct fuse_copy_file_range_out
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef _LINUX_FUSE_H
|
#ifndef _LINUX_FUSE_H
|
||||||
|
|
@ -270,7 +274,7 @@
|
||||||
#define FUSE_KERNEL_VERSION 7
|
#define FUSE_KERNEL_VERSION 7
|
||||||
|
|
||||||
/** Minor version number of this interface */
|
/** 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 */
|
/** The node ID of the root inode */
|
||||||
#define FUSE_ROOT_ID 1
|
#define FUSE_ROOT_ID 1
|
||||||
|
|
@ -657,6 +661,7 @@ enum fuse_opcode {
|
||||||
FUSE_SYNCFS = 50,
|
FUSE_SYNCFS = 50,
|
||||||
FUSE_TMPFILE = 51,
|
FUSE_TMPFILE = 51,
|
||||||
FUSE_STATX = 52,
|
FUSE_STATX = 52,
|
||||||
|
FUSE_COPY_FILE_RANGE_64 = 53,
|
||||||
|
|
||||||
/* CUSE specific operations */
|
/* CUSE specific operations */
|
||||||
CUSE_INIT = 4096,
|
CUSE_INIT = 4096,
|
||||||
|
|
@ -1148,6 +1153,11 @@ struct fuse_copy_file_range_in {
|
||||||
uint64_t flags;
|
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_WRITE (1ull << 0)
|
||||||
#define FUSE_SETUPMAPPING_FLAG_READ (1ull << 1)
|
#define FUSE_SETUPMAPPING_FLAG_READ (1ull << 1)
|
||||||
struct fuse_setupmapping_in {
|
struct fuse_setupmapping_in {
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user