diff options
author | Niels de Vos <ndevos@redhat.com> | 2018-08-21 08:36:31 -0400 |
---|---|---|
committer | Miklos Szeredi <mszeredi@redhat.com> | 2018-09-28 10:43:22 -0400 |
commit | 88bc7d5097a11d9bdcf08ecf85c81ba998353437 (patch) | |
tree | 40bceda7dc1db073a51d8ed34675073fbf70dfd0 /fs/fuse | |
parent | 908a572b80f6e9577b45e81b3dfe2e22111286b8 (diff) |
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>
Diffstat (limited to 'fs/fuse')
-rw-r--r-- | fs/fuse/file.c | 77 | ||||
-rw-r--r-- | fs/fuse/fuse_i.h | 3 |
2 files changed, 80 insertions, 0 deletions
diff --git a/fs/fuse/file.c b/fs/fuse/file.c index 32d0b883e74f..63136a2c23ab 100644 --- a/fs/fuse/file.c +++ b/fs/fuse/file.c | |||
@@ -3011,6 +3011,82 @@ out: | |||
3011 | return err; | 3011 | return err; |
3012 | } | 3012 | } |
3013 | 3013 | ||
3014 | static ssize_t fuse_copy_file_range(struct file *file_in, loff_t pos_in, | ||
3015 | struct file *file_out, loff_t pos_out, | ||
3016 | size_t len, unsigned int flags) | ||
3017 | { | ||
3018 | struct fuse_file *ff_in = file_in->private_data; | ||
3019 | struct fuse_file *ff_out = file_out->private_data; | ||
3020 | struct inode *inode_out = file_inode(file_out); | ||
3021 | struct fuse_inode *fi_out = get_fuse_inode(inode_out); | ||
3022 | struct fuse_conn *fc = ff_in->fc; | ||
3023 | FUSE_ARGS(args); | ||
3024 | struct fuse_copy_file_range_in inarg = { | ||
3025 | .fh_in = ff_in->fh, | ||
3026 | .off_in = pos_in, | ||
3027 | .nodeid_out = ff_out->nodeid, | ||
3028 | .fh_out = ff_out->fh, | ||
3029 | .off_out = pos_out, | ||
3030 | .len = len, | ||
3031 | .flags = flags | ||
3032 | }; | ||
3033 | struct fuse_write_out outarg; | ||
3034 | ssize_t err; | ||
3035 | /* mark unstable when write-back is not used, and file_out gets | ||
3036 | * extended */ | ||
3037 | bool is_unstable = (!fc->writeback_cache) && | ||
3038 | ((pos_out + len) > inode_out->i_size); | ||
3039 | |||
3040 | if (fc->no_copy_file_range) | ||
3041 | return -EOPNOTSUPP; | ||
3042 | |||
3043 | inode_lock(inode_out); | ||
3044 | |||
3045 | if (fc->writeback_cache) { | ||
3046 | err = filemap_write_and_wait_range(inode_out->i_mapping, | ||
3047 | pos_out, pos_out + len); | ||
3048 | if (err) | ||
3049 | goto out; | ||
3050 | |||
3051 | fuse_sync_writes(inode_out); | ||
3052 | } | ||
3053 | |||
3054 | if (is_unstable) | ||
3055 | set_bit(FUSE_I_SIZE_UNSTABLE, &fi_out->state); | ||
3056 | |||
3057 | args.in.h.opcode = FUSE_COPY_FILE_RANGE; | ||
3058 | args.in.h.nodeid = ff_in->nodeid; | ||
3059 | args.in.numargs = 1; | ||
3060 | args.in.args[0].size = sizeof(inarg); | ||
3061 | args.in.args[0].value = &inarg; | ||
3062 | args.out.numargs = 1; | ||
3063 | args.out.args[0].size = sizeof(outarg); | ||
3064 | args.out.args[0].value = &outarg; | ||
3065 | err = fuse_simple_request(fc, &args); | ||
3066 | if (err == -ENOSYS) { | ||
3067 | fc->no_copy_file_range = 1; | ||
3068 | err = -EOPNOTSUPP; | ||
3069 | } | ||
3070 | if (err) | ||
3071 | goto out; | ||
3072 | |||
3073 | if (fc->writeback_cache) { | ||
3074 | fuse_write_update_size(inode_out, pos_out + outarg.size); | ||
3075 | file_update_time(file_out); | ||
3076 | } | ||
3077 | |||
3078 | fuse_invalidate_attr(inode_out); | ||
3079 | |||
3080 | err = outarg.size; | ||
3081 | out: | ||
3082 | if (is_unstable) | ||
3083 | clear_bit(FUSE_I_SIZE_UNSTABLE, &fi_out->state); | ||
3084 | |||
3085 | inode_unlock(inode_out); | ||
3086 | |||
3087 | return err; | ||
3088 | } | ||
3089 | |||
3014 | static const struct file_operations fuse_file_operations = { | 3090 | static const struct file_operations fuse_file_operations = { |
3015 | .llseek = fuse_file_llseek, | 3091 | .llseek = fuse_file_llseek, |
3016 | .read_iter = fuse_file_read_iter, | 3092 | .read_iter = fuse_file_read_iter, |
@@ -3027,6 +3103,7 @@ static const struct file_operations fuse_file_operations = { | |||
3027 | .compat_ioctl = fuse_file_compat_ioctl, | 3103 | .compat_ioctl = fuse_file_compat_ioctl, |
3028 | .poll = fuse_file_poll, | 3104 | .poll = fuse_file_poll, |
3029 | .fallocate = fuse_file_fallocate, | 3105 | .fallocate = fuse_file_fallocate, |
3106 | .copy_file_range = fuse_copy_file_range, | ||
3030 | }; | 3107 | }; |
3031 | 3108 | ||
3032 | static const struct file_operations fuse_direct_io_file_operations = { | 3109 | static const struct file_operations fuse_direct_io_file_operations = { |
diff --git a/fs/fuse/fuse_i.h b/fs/fuse/fuse_i.h index f78e9614bb5f..3e45d408a644 100644 --- a/fs/fuse/fuse_i.h +++ b/fs/fuse/fuse_i.h | |||
@@ -637,6 +637,9 @@ struct fuse_conn { | |||
637 | /** Allow other than the mounter user to access the filesystem ? */ | 637 | /** Allow other than the mounter user to access the filesystem ? */ |
638 | unsigned allow_other:1; | 638 | unsigned allow_other:1; |
639 | 639 | ||
640 | /** Does the filesystem support copy_file_range? */ | ||
641 | unsigned no_copy_file_range:1; | ||
642 | |||
640 | /** The number of requests waiting for completion */ | 643 | /** The number of requests waiting for completion */ |
641 | atomic_t num_waiting; | 644 | atomic_t num_waiting; |
642 | 645 | ||