aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--include/fuse.h22
-rw-r--r--include/fuse_kernel.h104
-rw-r--r--include/fuse_lowlevel.h36
-rw-r--r--lib/fuse.c63
-rw-r--r--lib/fuse_lowlevel.c22
-rw-r--r--lib/fuse_versionscript6
6 files changed, 208 insertions, 45 deletions
diff --git a/include/fuse.h b/include/fuse.h
index 24e04bc..d3644ad 100644
--- a/include/fuse.h
+++ b/include/fuse.h
@@ -750,6 +750,23 @@ struct fuse_operations {
*/
int (*fallocate) (const char *, int, off_t, off_t,
struct fuse_file_info *);
+
+ /**
+ * Copy a range of data from one file to another
+ *
+ * Performs an optimized copy between two file descriptors without the
+ * additional cost of transferring data through the FUSE kernel module
+ * to user space (glibc) and then back into the FUSE filesystem again.
+ *
+ * In case this method is not implemented, glibc falls back to reading
+ * data from the source and writing to the destination. Effectively
+ * doing an inefficient copy of the data.
+ */
+ ssize_t (*copy_file_range) (const char *path_in,
+ struct fuse_file_info *fi_in,
+ off_t offset_in, const char *path_out,
+ struct fuse_file_info *fi_out,
+ off_t offset_out, size_t size, int flags);
};
/** Extra context that may be needed by some filesystems
@@ -1165,6 +1182,11 @@ int fuse_fs_poll(struct fuse_fs *fs, const char *path,
unsigned *reventsp);
int fuse_fs_fallocate(struct fuse_fs *fs, const char *path, int mode,
off_t offset, off_t length, struct fuse_file_info *fi);
+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);
void fuse_fs_init(struct fuse_fs *fs, struct fuse_conn_info *conn,
struct fuse_config *cfg);
void fuse_fs_destroy(struct fuse_fs *fs);
diff --git a/include/fuse_kernel.h b/include/fuse_kernel.h
index 92fa24c..c806a17 100644
--- a/include/fuse_kernel.h
+++ b/include/fuse_kernel.h
@@ -116,6 +116,9 @@
*
* 7.27
* - add FUSE_ABORT_ERROR
+ *
+ * 7.28
+ * - add FUSE_COPY_FILE_RANGE
*/
#ifndef _LINUX_FUSE_H
@@ -337,53 +340,54 @@ struct fuse_file_lock {
#define FUSE_POLL_SCHEDULE_NOTIFY (1 << 0)
enum fuse_opcode {
- FUSE_LOOKUP = 1,
- FUSE_FORGET = 2, /* no reply */
- FUSE_GETATTR = 3,
- FUSE_SETATTR = 4,
- FUSE_READLINK = 5,
- FUSE_SYMLINK = 6,
- FUSE_MKNOD = 8,
- FUSE_MKDIR = 9,
- FUSE_UNLINK = 10,
- FUSE_RMDIR = 11,
- FUSE_RENAME = 12,
- FUSE_LINK = 13,
- FUSE_OPEN = 14,
- FUSE_READ = 15,
- FUSE_WRITE = 16,
- FUSE_STATFS = 17,
- FUSE_RELEASE = 18,
- FUSE_FSYNC = 20,
- FUSE_SETXATTR = 21,
- FUSE_GETXATTR = 22,
- FUSE_LISTXATTR = 23,
- FUSE_REMOVEXATTR = 24,
- FUSE_FLUSH = 25,
- FUSE_INIT = 26,
- FUSE_OPENDIR = 27,
- FUSE_READDIR = 28,
- FUSE_RELEASEDIR = 29,
- FUSE_FSYNCDIR = 30,
- FUSE_GETLK = 31,
- FUSE_SETLK = 32,
- FUSE_SETLKW = 33,
- FUSE_ACCESS = 34,
- FUSE_CREATE = 35,
- FUSE_INTERRUPT = 36,
- FUSE_BMAP = 37,
- FUSE_DESTROY = 38,
- FUSE_IOCTL = 39,
- FUSE_POLL = 40,
- FUSE_NOTIFY_REPLY = 41,
- FUSE_BATCH_FORGET = 42,
- FUSE_FALLOCATE = 43,
- FUSE_READDIRPLUS = 44,
- FUSE_RENAME2 = 45,
- FUSE_LSEEK = 46,
+ FUSE_LOOKUP = 1,
+ FUSE_FORGET = 2, /* no reply */
+ FUSE_GETATTR = 3,
+ FUSE_SETATTR = 4,
+ FUSE_READLINK = 5,
+ FUSE_SYMLINK = 6,
+ FUSE_MKNOD = 8,
+ FUSE_MKDIR = 9,
+ FUSE_UNLINK = 10,
+ FUSE_RMDIR = 11,
+ FUSE_RENAME = 12,
+ FUSE_LINK = 13,
+ FUSE_OPEN = 14,
+ FUSE_READ = 15,
+ FUSE_WRITE = 16,
+ FUSE_STATFS = 17,
+ FUSE_RELEASE = 18,
+ FUSE_FSYNC = 20,
+ FUSE_SETXATTR = 21,
+ FUSE_GETXATTR = 22,
+ FUSE_LISTXATTR = 23,
+ FUSE_REMOVEXATTR = 24,
+ FUSE_FLUSH = 25,
+ FUSE_INIT = 26,
+ FUSE_OPENDIR = 27,
+ FUSE_READDIR = 28,
+ FUSE_RELEASEDIR = 29,
+ FUSE_FSYNCDIR = 30,
+ FUSE_GETLK = 31,
+ FUSE_SETLK = 32,
+ FUSE_SETLKW = 33,
+ FUSE_ACCESS = 34,
+ FUSE_CREATE = 35,
+ FUSE_INTERRUPT = 36,
+ FUSE_BMAP = 37,
+ FUSE_DESTROY = 38,
+ FUSE_IOCTL = 39,
+ FUSE_POLL = 40,
+ FUSE_NOTIFY_REPLY = 41,
+ FUSE_BATCH_FORGET = 42,
+ FUSE_FALLOCATE = 43,
+ FUSE_READDIRPLUS = 44,
+ FUSE_RENAME2 = 45,
+ FUSE_LSEEK = 46,
+ FUSE_COPY_FILE_RANGE = 47,
/* CUSE specific operations */
- CUSE_INIT = 4096,
+ CUSE_INIT = 4096,
};
enum fuse_notify_code {
@@ -792,4 +796,14 @@ struct fuse_lseek_out {
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 */
diff --git a/include/fuse_lowlevel.h b/include/fuse_lowlevel.h
index 395c0d9..12e3946 100644
--- a/include/fuse_lowlevel.h
+++ b/include/fuse_lowlevel.h
@@ -1160,6 +1160,42 @@ struct fuse_lowlevel_ops {
*/
void (*readdirplus) (fuse_req_t req, fuse_ino_t ino, size_t size, off_t off,
struct fuse_file_info *fi);
+
+ /**
+ * Copy a range of data from one file to another
+ *
+ * Performs an optimized copy between two file descriptors without the
+ * additional cost of transferring data through the FUSE kernel module
+ * to user space (glibc) and then back into the FUSE filesystem again.
+ *
+ * In case this method is not implemented, glibc falls back to reading
+ * data from the source and writing to the destination. Effectively
+ * doing an inefficient copy of the data.
+ *
+ * If this request is answered with an error code of ENOSYS, this is
+ * treated as a permanent failure with error code EOPNOTSUPP, i.e. all
+ * future copy_file_range() requests will fail with EOPNOTSUPP without
+ * being send to the filesystem process.
+ *
+ * Valid replies:
+ * fuse_reply_write
+ * fuse_reply_err
+ *
+ * @param req request handle
+ * @param ino_in the inode number or the source file
+ * @param off_in starting point from were the data should be read
+ * @param fi_in file information of the source file
+ * @param ino_out the inode number or the destination file
+ * @param off_out starting point where the data should be written
+ * @param fi_out file information of the destination file
+ * @param len maximum size of the data to copy
+ * @param flags passed along with the copy_file_range() syscall
+ */
+ void (*copy_file_range) (fuse_req_t req, fuse_ino_t ino_in,
+ off_t off_in, struct fuse_file_info *fi_in,
+ fuse_ino_t ino_out, off_t off_out,
+ struct fuse_file_info *fi_out, size_t len,
+ int flags);
};
/**
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: