Skip to content

Commit 88bc7d5

Browse files
nixpanicMiklos Szeredi
authored andcommitted
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>
1 parent 908a572 commit 88bc7d5

File tree

3 files changed

+140
-46
lines changed

3 files changed

+140
-46
lines changed

fs/fuse/file.c

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3011,6 +3011,82 @@ static long fuse_file_fallocate(struct file *file, int mode, loff_t offset,
30113011
return err;
30123012
}
30133013

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+
30143090
static const struct file_operations fuse_file_operations = {
30153091
.llseek = fuse_file_llseek,
30163092
.read_iter = fuse_file_read_iter,
@@ -3027,6 +3103,7 @@ static const struct file_operations fuse_file_operations = {
30273103
.compat_ioctl = fuse_file_compat_ioctl,
30283104
.poll = fuse_file_poll,
30293105
.fallocate = fuse_file_fallocate,
3106+
.copy_file_range = fuse_copy_file_range,
30303107
};
30313108

30323109
static const struct file_operations fuse_direct_io_file_operations = {

fs/fuse/fuse_i.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -637,6 +637,9 @@ struct fuse_conn {
637637
/** Allow other than the mounter user to access the filesystem ? */
638638
unsigned allow_other:1;
639639

640+
/** Does the filesystem support copy_file_range? */
641+
unsigned no_copy_file_range:1;
642+
640643
/** The number of requests waiting for completion */
641644
atomic_t num_waiting;
642645

include/uapi/linux/fuse.h

Lines changed: 60 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,9 @@
116116
*
117117
* 7.27
118118
* - add FUSE_ABORT_ERROR
119+
*
120+
* 7.28
121+
* - add FUSE_COPY_FILE_RANGE
119122
*/
120123

121124
#ifndef _LINUX_FUSE_H
@@ -151,7 +154,7 @@
151154
#define FUSE_KERNEL_VERSION 7
152155

153156
/** Minor version number of this interface */
154-
#define FUSE_KERNEL_MINOR_VERSION 27
157+
#define FUSE_KERNEL_MINOR_VERSION 28
155158

156159
/** The node ID of the root inode */
157160
#define FUSE_ROOT_ID 1
@@ -337,53 +340,54 @@ struct fuse_file_lock {
337340
#define FUSE_POLL_SCHEDULE_NOTIFY (1 << 0)
338341

339342
enum fuse_opcode {
340-
FUSE_LOOKUP = 1,
341-
FUSE_FORGET = 2, /* no reply */
342-
FUSE_GETATTR = 3,
343-
FUSE_SETATTR = 4,
344-
FUSE_READLINK = 5,
345-
FUSE_SYMLINK = 6,
346-
FUSE_MKNOD = 8,
347-
FUSE_MKDIR = 9,
348-
FUSE_UNLINK = 10,
349-
FUSE_RMDIR = 11,
350-
FUSE_RENAME = 12,
351-
FUSE_LINK = 13,
352-
FUSE_OPEN = 14,
353-
FUSE_READ = 15,
354-
FUSE_WRITE = 16,
355-
FUSE_STATFS = 17,
356-
FUSE_RELEASE = 18,
357-
FUSE_FSYNC = 20,
358-
FUSE_SETXATTR = 21,
359-
FUSE_GETXATTR = 22,
360-
FUSE_LISTXATTR = 23,
361-
FUSE_REMOVEXATTR = 24,
362-
FUSE_FLUSH = 25,
363-
FUSE_INIT = 26,
364-
FUSE_OPENDIR = 27,
365-
FUSE_READDIR = 28,
366-
FUSE_RELEASEDIR = 29,
367-
FUSE_FSYNCDIR = 30,
368-
FUSE_GETLK = 31,
369-
FUSE_SETLK = 32,
370-
FUSE_SETLKW = 33,
371-
FUSE_ACCESS = 34,
372-
FUSE_CREATE = 35,
373-
FUSE_INTERRUPT = 36,
374-
FUSE_BMAP = 37,
375-
FUSE_DESTROY = 38,
376-
FUSE_IOCTL = 39,
377-
FUSE_POLL = 40,
378-
FUSE_NOTIFY_REPLY = 41,
379-
FUSE_BATCH_FORGET = 42,
380-
FUSE_FALLOCATE = 43,
381-
FUSE_READDIRPLUS = 44,
382-
FUSE_RENAME2 = 45,
383-
FUSE_LSEEK = 46,
343+
FUSE_LOOKUP = 1,
344+
FUSE_FORGET = 2, /* no reply */
345+
FUSE_GETATTR = 3,
346+
FUSE_SETATTR = 4,
347+
FUSE_READLINK = 5,
348+
FUSE_SYMLINK = 6,
349+
FUSE_MKNOD = 8,
350+
FUSE_MKDIR = 9,
351+
FUSE_UNLINK = 10,
352+
FUSE_RMDIR = 11,
353+
FUSE_RENAME = 12,
354+
FUSE_LINK = 13,
355+
FUSE_OPEN = 14,
356+
FUSE_READ = 15,
357+
FUSE_WRITE = 16,
358+
FUSE_STATFS = 17,
359+
FUSE_RELEASE = 18,
360+
FUSE_FSYNC = 20,
361+
FUSE_SETXATTR = 21,
362+
FUSE_GETXATTR = 22,
363+
FUSE_LISTXATTR = 23,
364+
FUSE_REMOVEXATTR = 24,
365+
FUSE_FLUSH = 25,
366+
FUSE_INIT = 26,
367+
FUSE_OPENDIR = 27,
368+
FUSE_READDIR = 28,
369+
FUSE_RELEASEDIR = 29,
370+
FUSE_FSYNCDIR = 30,
371+
FUSE_GETLK = 31,
372+
FUSE_SETLK = 32,
373+
FUSE_SETLKW = 33,
374+
FUSE_ACCESS = 34,
375+
FUSE_CREATE = 35,
376+
FUSE_INTERRUPT = 36,
377+
FUSE_BMAP = 37,
378+
FUSE_DESTROY = 38,
379+
FUSE_IOCTL = 39,
380+
FUSE_POLL = 40,
381+
FUSE_NOTIFY_REPLY = 41,
382+
FUSE_BATCH_FORGET = 42,
383+
FUSE_FALLOCATE = 43,
384+
FUSE_READDIRPLUS = 44,
385+
FUSE_RENAME2 = 45,
386+
FUSE_LSEEK = 46,
387+
FUSE_COPY_FILE_RANGE = 47,
384388

385389
/* CUSE specific operations */
386-
CUSE_INIT = 4096,
390+
CUSE_INIT = 4096,
387391
};
388392

389393
enum fuse_notify_code {
@@ -792,4 +796,14 @@ struct fuse_lseek_out {
792796
uint64_t offset;
793797
};
794798

799+
struct fuse_copy_file_range_in {
800+
uint64_t fh_in;
801+
uint64_t off_in;
802+
uint64_t nodeid_out;
803+
uint64_t fh_out;
804+
uint64_t off_out;
805+
uint64_t len;
806+
uint64_t flags;
807+
};
808+
795809
#endif /* _LINUX_FUSE_H */

0 commit comments

Comments
 (0)