aboutsummaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
authorNiels de Vos <ndevos@redhat.com>2018-06-18 19:31:43 +0200
committerNikolaus Rath <Nikolaus@rath.org>2018-11-19 12:33:56 +0000
commitfe4f9428fc403fa8b99051f52d84ea5bd13f3855 (patch)
treeb1a5256ba956152490d14a3acd05083a91bb2811 /lib
parent4c699e7debd99b178b9574e7dd20f36937ef296e (diff)
downloadlibfuse-fe4f9428fc403fa8b99051f52d84ea5bd13f3855.tar.gz
libfuse: add copy_file_range() support
Add support for the relatively new copy_file_range() syscall. Backend filesystems can now implement an efficient way of cloning/duplicating data ranges within files. See 'man 2 copy_file_range' for more details.
Diffstat (limited to 'lib')
-rw-r--r--lib/fuse.c63
-rw-r--r--lib/fuse_lowlevel.c22
-rw-r--r--lib/fuse_versionscript6
3 files changed, 91 insertions, 0 deletions
diff --git a/lib/fuse.c b/lib/fuse.c
index b88ef1a..946ae2f 100644
--- a/lib/fuse.c
+++ b/lib/fuse.c
@@ -2359,6 +2359,29 @@ int fuse_fs_fallocate(struct fuse_fs *fs, const char *path, int mode,
return -ENOSYS;
}
+ssize_t fuse_fs_copy_file_range(struct fuse_fs *fs, const char *path_in,
+ struct fuse_file_info *fi_in, off_t off_in,
+ const char *path_out,
+ struct fuse_file_info *fi_out, off_t off_out,
+ size_t len, int flags)
+{
+ fuse_get_context()->private_data = fs->user_data;
+ if (fs->op.copy_file_range) {
+ if (fs->debug)
+ fprintf(stderr, "copy_file_range from %s:%llu to "
+ "%s:%llu, length: %llu\n",
+ path_in,
+ (unsigned long long) off_in,
+ path_out,
+ (unsigned long long) off_out,
+ (unsigned long long) len);
+
+ return fs->op.copy_file_range(path_in, fi_in, off_in, path_out,
+ fi_out, off_out, len, flags);
+ } else
+ return -ENOSYS;
+}
+
static int is_open(struct fuse *f, fuse_ino_t dir, const char *name)
{
struct node *node;
@@ -4290,6 +4313,45 @@ static void fuse_lib_fallocate(fuse_req_t req, fuse_ino_t ino, int mode,
reply_err(req, err);
}
+static void fuse_lib_copy_file_range(fuse_req_t req, fuse_ino_t nodeid_in,
+ off_t off_in, struct fuse_file_info *fi_in,
+ fuse_ino_t nodeid_out, off_t off_out,
+ struct fuse_file_info *fi_out, size_t len,
+ int flags)
+{
+ struct fuse *f = req_fuse_prepare(req);
+ struct fuse_intr_data d;
+ char *path_in, *path_out;
+ int err;
+ ssize_t res;
+
+ err = get_path_nullok(f, nodeid_in, &path_in);
+ if (err) {
+ reply_err(req, err);
+ return;
+ }
+
+ err = get_path_nullok(f, nodeid_out, &path_out);
+ if (err) {
+ free_path(f, nodeid_in, path_in);
+ reply_err(req, err);
+ return;
+ }
+
+ fuse_prepare_interrupt(f, req, &d);
+ res = fuse_fs_copy_file_range(f->fs, path_in, fi_in, off_in, path_out,
+ fi_out, off_out, len, flags);
+ fuse_finish_interrupt(f, req, &d);
+
+ if (res >= 0)
+ fuse_reply_write(req, res);
+ else
+ reply_err(req, res);
+
+ free_path(f, nodeid_in, path_in);
+ free_path(f, nodeid_out, path_out);
+}
+
static int clean_delay(struct fuse *f)
{
/*
@@ -4386,6 +4448,7 @@ static struct fuse_lowlevel_ops fuse_path_ops = {
.ioctl = fuse_lib_ioctl,
.poll = fuse_lib_poll,
.fallocate = fuse_lib_fallocate,
+ .copy_file_range = fuse_lib_copy_file_range,
};
int fuse_notify_poll(struct fuse_pollhandle *ph)
diff --git a/lib/fuse_lowlevel.c b/lib/fuse_lowlevel.c
index 844e797..cd59ec0 100644
--- a/lib/fuse_lowlevel.c
+++ b/lib/fuse_lowlevel.c
@@ -1810,6 +1810,27 @@ static void do_fallocate(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
fuse_reply_err(req, ENOSYS);
}
+static void do_copy_file_range(fuse_req_t req, fuse_ino_t nodeid_in, const void *inarg)
+{
+ struct fuse_copy_file_range_in *arg = (struct fuse_copy_file_range_in *) inarg;
+ struct fuse_file_info fi_in, fi_out;
+
+ memset(&fi_in, 0, sizeof(fi_in));
+ fi_in.fh = arg->fh_in;
+
+ memset(&fi_out, 0, sizeof(fi_out));
+ fi_out.fh = arg->fh_out;
+
+
+ if (req->se->op.copy_file_range)
+ req->se->op.copy_file_range(req, nodeid_in, arg->off_in,
+ &fi_in, arg->nodeid_out,
+ arg->off_out, &fi_out, arg->len,
+ arg->flags);
+ else
+ fuse_reply_err(req, ENOSYS);
+}
+
static void do_init(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
{
struct fuse_init_in *arg = (struct fuse_init_in *) inarg;
@@ -2395,6 +2416,7 @@ static struct {
[FUSE_BATCH_FORGET] = { do_batch_forget, "BATCH_FORGET" },
[FUSE_READDIRPLUS] = { do_readdirplus, "READDIRPLUS"},
[FUSE_RENAME2] = { do_rename2, "RENAME2" },
+ [FUSE_COPY_FILE_RANGE] = { do_copy_file_range, "COPY_FILE_RANGE" },
[CUSE_INIT] = { cuse_lowlevel_init, "CUSE_INIT" },
};
diff --git a/lib/fuse_versionscript b/lib/fuse_versionscript
index 2802bb4..00f955d 100644
--- a/lib/fuse_versionscript
+++ b/lib/fuse_versionscript
@@ -153,6 +153,12 @@ FUSE_3.3 {
fuse_open_channel;
} FUSE_3.2;
+FUSE_3.4 {
+ global:
+ fuse_fs_copy_file_range;
+ fuse_reply_copy_file_range;
+} FUSE_3.3;
+
# Local Variables:
# indent-tabs-mode: t
# End: