fuse: add support for copy_file_range()

There are several FUSE filesystems that can implement server-side copy
or other efficient copy/duplication/clone methods. The copy_file_range()
syscall is the standard interface that users have access to while not
depending on external libraries that bypass FUSE.

Signed-off-by: Niels de Vos <ndevos@redhat.com>
Signed-off-by: Miklos Szeredi <mszeredi@redhat.com>
This commit is contained in:
Niels de Vos 2018-08-21 14:36:31 +02:00 committed by Miklos Szeredi
parent 908a572b80
commit 88bc7d5097
3 changed files with 140 additions and 46 deletions

View File

@ -3011,6 +3011,82 @@ out:
return err; return err;
} }
static ssize_t fuse_copy_file_range(struct file *file_in, loff_t pos_in,
struct file *file_out, loff_t pos_out,
size_t len, unsigned int flags)
{
struct fuse_file *ff_in = file_in->private_data;
struct fuse_file *ff_out = file_out->private_data;
struct inode *inode_out = file_inode(file_out);
struct fuse_inode *fi_out = get_fuse_inode(inode_out);
struct fuse_conn *fc = ff_in->fc;
FUSE_ARGS(args);
struct fuse_copy_file_range_in inarg = {
.fh_in = ff_in->fh,
.off_in = pos_in,
.nodeid_out = ff_out->nodeid,
.fh_out = ff_out->fh,
.off_out = pos_out,
.len = len,
.flags = flags
};
struct fuse_write_out outarg;
ssize_t err;
/* mark unstable when write-back is not used, and file_out gets
* extended */
bool is_unstable = (!fc->writeback_cache) &&
((pos_out + len) > inode_out->i_size);
if (fc->no_copy_file_range)
return -EOPNOTSUPP;
inode_lock(inode_out);
if (fc->writeback_cache) {
err = filemap_write_and_wait_range(inode_out->i_mapping,
pos_out, pos_out + len);
if (err)
goto out;
fuse_sync_writes(inode_out);
}
if (is_unstable)
set_bit(FUSE_I_SIZE_UNSTABLE, &fi_out->state);
args.in.h.opcode = FUSE_COPY_FILE_RANGE;
args.in.h.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;
err = fuse_simple_request(fc, &args);
if (err == -ENOSYS) {
fc->no_copy_file_range = 1;
err = -EOPNOTSUPP;
}
if (err)
goto out;
if (fc->writeback_cache) {
fuse_write_update_size(inode_out, pos_out + outarg.size);
file_update_time(file_out);
}
fuse_invalidate_attr(inode_out);
err = outarg.size;
out:
if (is_unstable)
clear_bit(FUSE_I_SIZE_UNSTABLE, &fi_out->state);
inode_unlock(inode_out);
return err;
}
static const struct file_operations fuse_file_operations = { static const struct file_operations fuse_file_operations = {
.llseek = fuse_file_llseek, .llseek = fuse_file_llseek,
.read_iter = fuse_file_read_iter, .read_iter = fuse_file_read_iter,
@ -3027,6 +3103,7 @@ static const struct file_operations fuse_file_operations = {
.compat_ioctl = fuse_file_compat_ioctl, .compat_ioctl = fuse_file_compat_ioctl,
.poll = fuse_file_poll, .poll = fuse_file_poll,
.fallocate = fuse_file_fallocate, .fallocate = fuse_file_fallocate,
.copy_file_range = fuse_copy_file_range,
}; };
static const struct file_operations fuse_direct_io_file_operations = { static const struct file_operations fuse_direct_io_file_operations = {

View File

@ -637,6 +637,9 @@ struct fuse_conn {
/** Allow other than the mounter user to access the filesystem ? */ /** Allow other than the mounter user to access the filesystem ? */
unsigned allow_other:1; unsigned allow_other:1;
/** Does the filesystem support copy_file_range? */
unsigned no_copy_file_range:1;
/** The number of requests waiting for completion */ /** The number of requests waiting for completion */
atomic_t num_waiting; atomic_t num_waiting;

View File

@ -116,6 +116,9 @@
* *
* 7.27 * 7.27
* - add FUSE_ABORT_ERROR * - add FUSE_ABORT_ERROR
*
* 7.28
* - add FUSE_COPY_FILE_RANGE
*/ */
#ifndef _LINUX_FUSE_H #ifndef _LINUX_FUSE_H
@ -151,7 +154,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 27 #define FUSE_KERNEL_MINOR_VERSION 28
/** The node ID of the root inode */ /** The node ID of the root inode */
#define FUSE_ROOT_ID 1 #define FUSE_ROOT_ID 1
@ -337,53 +340,54 @@ struct fuse_file_lock {
#define FUSE_POLL_SCHEDULE_NOTIFY (1 << 0) #define FUSE_POLL_SCHEDULE_NOTIFY (1 << 0)
enum fuse_opcode { enum fuse_opcode {
FUSE_LOOKUP = 1, FUSE_LOOKUP = 1,
FUSE_FORGET = 2, /* no reply */ FUSE_FORGET = 2, /* no reply */
FUSE_GETATTR = 3, FUSE_GETATTR = 3,
FUSE_SETATTR = 4, FUSE_SETATTR = 4,
FUSE_READLINK = 5, FUSE_READLINK = 5,
FUSE_SYMLINK = 6, FUSE_SYMLINK = 6,
FUSE_MKNOD = 8, FUSE_MKNOD = 8,
FUSE_MKDIR = 9, FUSE_MKDIR = 9,
FUSE_UNLINK = 10, FUSE_UNLINK = 10,
FUSE_RMDIR = 11, FUSE_RMDIR = 11,
FUSE_RENAME = 12, FUSE_RENAME = 12,
FUSE_LINK = 13, FUSE_LINK = 13,
FUSE_OPEN = 14, FUSE_OPEN = 14,
FUSE_READ = 15, FUSE_READ = 15,
FUSE_WRITE = 16, FUSE_WRITE = 16,
FUSE_STATFS = 17, FUSE_STATFS = 17,
FUSE_RELEASE = 18, FUSE_RELEASE = 18,
FUSE_FSYNC = 20, FUSE_FSYNC = 20,
FUSE_SETXATTR = 21, FUSE_SETXATTR = 21,
FUSE_GETXATTR = 22, FUSE_GETXATTR = 22,
FUSE_LISTXATTR = 23, FUSE_LISTXATTR = 23,
FUSE_REMOVEXATTR = 24, FUSE_REMOVEXATTR = 24,
FUSE_FLUSH = 25, FUSE_FLUSH = 25,
FUSE_INIT = 26, FUSE_INIT = 26,
FUSE_OPENDIR = 27, FUSE_OPENDIR = 27,
FUSE_READDIR = 28, FUSE_READDIR = 28,
FUSE_RELEASEDIR = 29, FUSE_RELEASEDIR = 29,
FUSE_FSYNCDIR = 30, FUSE_FSYNCDIR = 30,
FUSE_GETLK = 31, FUSE_GETLK = 31,
FUSE_SETLK = 32, FUSE_SETLK = 32,
FUSE_SETLKW = 33, FUSE_SETLKW = 33,
FUSE_ACCESS = 34, FUSE_ACCESS = 34,
FUSE_CREATE = 35, FUSE_CREATE = 35,
FUSE_INTERRUPT = 36, FUSE_INTERRUPT = 36,
FUSE_BMAP = 37, FUSE_BMAP = 37,
FUSE_DESTROY = 38, FUSE_DESTROY = 38,
FUSE_IOCTL = 39, FUSE_IOCTL = 39,
FUSE_POLL = 40, FUSE_POLL = 40,
FUSE_NOTIFY_REPLY = 41, FUSE_NOTIFY_REPLY = 41,
FUSE_BATCH_FORGET = 42, FUSE_BATCH_FORGET = 42,
FUSE_FALLOCATE = 43, FUSE_FALLOCATE = 43,
FUSE_READDIRPLUS = 44, FUSE_READDIRPLUS = 44,
FUSE_RENAME2 = 45, FUSE_RENAME2 = 45,
FUSE_LSEEK = 46, FUSE_LSEEK = 46,
FUSE_COPY_FILE_RANGE = 47,
/* CUSE specific operations */ /* CUSE specific operations */
CUSE_INIT = 4096, CUSE_INIT = 4096,
}; };
enum fuse_notify_code { enum fuse_notify_code {
@ -792,4 +796,14 @@ struct fuse_lseek_out {
uint64_t offset; uint64_t offset;
}; };
struct fuse_copy_file_range_in {
uint64_t fh_in;
uint64_t off_in;
uint64_t nodeid_out;
uint64_t fh_out;
uint64_t off_out;
uint64_t len;
uint64_t flags;
};
#endif /* _LINUX_FUSE_H */ #endif /* _LINUX_FUSE_H */