Browse Source

Merge pull request #635 from trapexit/copy_file_range

add copy_file_range support
pull/636/head
trapexit 6 years ago
committed by GitHub
parent
commit
24af276542
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 2
      README.md
  2. 30
      libfuse/include/fuse.h
  3. 46
      libfuse/include/fuse_lowlevel.h
  4. 83
      libfuse/lib/fuse.c
  5. 32
      libfuse/lib/fuse_lowlevel.c
  6. 2
      man/mergerfs.1
  7. 71
      src/fuse_copy_file_range.cpp
  8. 30
      src/fuse_copy_file_range.hpp
  9. 29
      src/mergerfs.cpp

2
README.md

@ -195,7 +195,7 @@ When using policies which are based on a branch's available space the base path
| action | chmod, chown, link, removexattr, rename, rmdir, setxattr, truncate, unlink, utimens | | action | chmod, chown, link, removexattr, rename, rmdir, setxattr, truncate, unlink, utimens |
| create | create, mkdir, mknod, symlink | | create | create, mkdir, mknod, symlink |
| search | access, getattr, getxattr, ioctl (directories), listxattr, open, readlink | | search | access, getattr, getxattr, ioctl (directories), listxattr, open, readlink |
| N/A | fchmod, fchown, futimens, ftruncate, fallocate, fgetattr, fsync, ioctl (files), read, readdir, release, statfs, write |
| N/A | fchmod, fchown, futimens, ftruncate, fallocate, fgetattr, fsync, ioctl (files), read, readdir, release, statfs, write, copy_file_range |
In cases where something may be searched (to confirm a directory exists across all source mounts) **getattr** will be used. In cases where something may be searched (to confirm a directory exists across all source mounts) **getattr** will be used.

30
libfuse/include/fuse.h

@ -608,6 +608,30 @@ struct fuse_operations {
*/ */
int (*fallocate) (const char *, int, off_t, off_t, int (*fallocate) (const char *, int, off_t, off_t,
struct fuse_file_info *); 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 /** Extra context that may be needed by some filesystems
@ -923,6 +947,12 @@ void fuse_fs_destroy(struct fuse_fs *fs);
int fuse_fs_prepare_hide(struct fuse_fs *fs, const char *path, uint64_t *fh); int fuse_fs_prepare_hide(struct fuse_fs *fs, const char *path, uint64_t *fh);
int fuse_fs_free_hide(struct fuse_fs *fs, uint64_t fh); int fuse_fs_free_hide(struct fuse_fs *fs, uint64_t fh);
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);
int fuse_notify_poll(struct fuse_pollhandle *ph); int fuse_notify_poll(struct fuse_pollhandle *ph);

46
libfuse/include/fuse_lowlevel.h

@ -1021,6 +1021,52 @@ struct fuse_lowlevel_ops {
*/ */
void (*fallocate) (fuse_req_t req, fuse_ino_t ino, int mode, void (*fallocate) (fuse_req_t req, fuse_ino_t ino, int mode,
off_t offset, off_t length, struct fuse_file_info *fi); off_t offset, off_t length, 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 of 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 of 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);
}; };
/** /**

83
libfuse/lib/fuse.c

@ -2296,7 +2296,32 @@ int fuse_fs_fallocate(struct fuse_fs *fs, const char *path, int mode,
return -ENOSYS; return -ENOSYS;
} }
static
ssize_t
fuse_fs_copy_file_range(struct fuse_fs *fs_,
const char *path_in_,
struct fuse_file_info *ffi_in_,
off_t off_in_,
const char *path_out_,
struct fuse_file_info *ffi_out_,
off_t off_out_,
size_t len_,
int flags_)
{
fuse_get_context()->private_data = fs_->user_data;
if(fs_->op.copy_file_range == NULL)
return -ENOSYS;
return fs_->op.copy_file_range(path_in_,
ffi_in_,
off_in_,
path_out_,
ffi_out_,
off_out_,
len_,
flags_);
}
int int
node_open(const struct node *node_) node_open(const struct node *node_)
{ {
@ -3675,6 +3700,61 @@ static void fuse_lib_removexattr(fuse_req_t req, fuse_ino_t ino,
reply_err(req, err); 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 *ffi_in_,
fuse_ino_t nodeid_out_,
off_t off_out_,
struct fuse_file_info *ffi_out_,
size_t len_,
int flags_)
{
int err;
ssize_t rv;
char *path_in;
char *path_out;
struct fuse *f;
struct fuse_intr_data d;
f = req_fuse_prepare(req_);
err = get_path_nullok(f,nodeid_in_,&path_in);
if(err)
return reply_err(req_,err);
err = get_path_nullok(f,nodeid_out_,&path_out);
if(err)
{
free_path(f,nodeid_in_,path_in);
return reply_err(req_,err);
}
fuse_prepare_interrupt(f,req_,&d);
rv = fuse_fs_copy_file_range(f->fs,
path_in,
ffi_in_,
off_in_,
path_out,
ffi_out_,
off_out_,
len_,
flags_);
fuse_finish_interrupt(f,req_,&d);
if(rv >= 0)
fuse_reply_write(req_,rv);
else
reply_err(req_,rv);
free_path(f,nodeid_in_,path_in);
free_path(f,nodeid_out_,path_out);
}
static struct lock *locks_conflict(struct node *node, const struct lock *lock) static struct lock *locks_conflict(struct node *node, const struct lock *lock)
{ {
struct lock *l; struct lock *l;
@ -4152,6 +4232,7 @@ static struct fuse_lowlevel_ops fuse_path_ops = {
.ioctl = fuse_lib_ioctl, .ioctl = fuse_lib_ioctl,
.poll = fuse_lib_poll, .poll = fuse_lib_poll,
.fallocate = fuse_lib_fallocate, .fallocate = fuse_lib_fallocate,
.copy_file_range = fuse_lib_copy_file_range,
}; };
int fuse_notify_poll(struct fuse_pollhandle *ph) int fuse_notify_poll(struct fuse_pollhandle *ph)

32
libfuse/lib/fuse_lowlevel.c

@ -1962,6 +1962,34 @@ static void do_notify_reply(fuse_req_t req, fuse_ino_t nodeid,
nreq->reply(nreq, req, nodeid, inarg, buf); nreq->reply(nreq, req, nodeid, inarg, buf);
} }
static
void
do_copy_file_range(fuse_req_t req_,
fuse_ino_t nodeid_in_,
const void *arg_)
{
struct fuse_file_info ffi_in = {0};
struct fuse_file_info ffi_out = {0};
struct fuse_copy_file_range_in *arg = (struct fuse_copy_file_range_in*)arg_;
ffi_in.fh = arg->fh_in;
ffi_out.fh = arg->fh_out;
if(req_->f->op.copy_file_range == NULL)
fuse_reply_err(req_,ENOSYS);
else
req_->f->op.copy_file_range(req_,
nodeid_in_,
arg->off_in,
&ffi_in,
arg->nodeid_out,
arg->off_out,
&ffi_out,
arg->len,
arg->flags);
}
static int send_notify_iov(struct fuse_ll *f, struct fuse_chan *ch, static int send_notify_iov(struct fuse_ll *f, struct fuse_chan *ch,
int notify_code, struct iovec *iov, int count) int notify_code, struct iovec *iov, int count)
{ {
@ -2262,7 +2290,8 @@ int fuse_req_interrupted(fuse_req_t req)
static struct { static struct {
void (*func)(fuse_req_t, fuse_ino_t, const void *); void (*func)(fuse_req_t, fuse_ino_t, const void *);
const char *name; const char *name;
} fuse_ll_ops[] = {
} fuse_ll_ops[] =
{
[FUSE_LOOKUP] = { do_lookup, "LOOKUP" }, [FUSE_LOOKUP] = { do_lookup, "LOOKUP" },
[FUSE_FORGET] = { do_forget, "FORGET" }, [FUSE_FORGET] = { do_forget, "FORGET" },
[FUSE_GETATTR] = { do_getattr, "GETATTR" }, [FUSE_GETATTR] = { do_getattr, "GETATTR" },
@ -2304,6 +2333,7 @@ static struct {
[FUSE_DESTROY] = { do_destroy, "DESTROY" }, [FUSE_DESTROY] = { do_destroy, "DESTROY" },
[FUSE_NOTIFY_REPLY] = { (void *) 1, "NOTIFY_REPLY" }, [FUSE_NOTIFY_REPLY] = { (void *) 1, "NOTIFY_REPLY" },
[FUSE_BATCH_FORGET] = { do_batch_forget, "BATCH_FORGET" }, [FUSE_BATCH_FORGET] = { do_batch_forget, "BATCH_FORGET" },
[FUSE_COPY_FILE_RANGE] = { do_copy_file_range, "COPY_FILE_RANGE" },
[CUSE_INIT] = { cuse_lowlevel_init, "CUSE_INIT" }, [CUSE_INIT] = { cuse_lowlevel_init, "CUSE_INIT" },
}; };

2
man/mergerfs.1

@ -467,7 +467,7 @@ T{
N/A N/A
T}@T{ T}@T{
fchmod, fchown, futimens, ftruncate, fallocate, fgetattr, fsync, ioctl fchmod, fchown, futimens, ftruncate, fallocate, fgetattr, fsync, ioctl
(files), read, readdir, release, statfs, write
(files), read, readdir, release, statfs, write, copy_file_range
T} T}
.TE .TE
.PP .PP

71
src/fuse_copy_file_range.cpp

@ -0,0 +1,71 @@
/*
Copyright (c) 2019, Antonio SJ Musumeci <trapexit@spawn.link>
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include "errno.hpp"
#include "fileinfo.hpp"
#include "fs_copy_file_range.hpp"
#include <fuse.h>
#include <stdio.h>
namespace l
{
static
ssize_t
copy_file_range(const int fd_in_,
off_t offset_in_,
const int fd_out_,
off_t offset_out_,
size_t size_,
int flags_)
{
ssize_t rv;
rv = fs::copy_file_range(fd_in_,
&offset_in_,
fd_out_,
&offset_out_,
size_,
flags_);
return ((rv == -1) ? -errno : rv);
}
}
namespace FUSE
{
ssize_t
copy_file_range(const char *path_in_,
struct fuse_file_info *ffi_in_,
off_t offset_in_,
const char *path_out_,
struct fuse_file_info *ffi_out_,
off_t offset_out_,
size_t size_,
int flags_)
{
FileInfo *fi_in = reinterpret_cast<FileInfo*>(ffi_in_->fh);
FileInfo *fi_out = reinterpret_cast<FileInfo*>(ffi_out_->fh);
return l::copy_file_range(fi_in->fd,
offset_in_,
fi_out->fd,
offset_out_,
size_,
flags_);
}
}

30
src/fuse_copy_file_range.hpp

@ -0,0 +1,30 @@
/*
Copyright (c) 2019, Antonio SJ Musumeci <trapexit@spawn.link>
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#pragma once
namespace FUSE
{
ssize_t
copy_file_range(const char *path_in,
struct fuse_file_info *ffi_in,
off_t offset_in,
const char *path_out,
struct fuse_file_info *ffi_out,
off_t offset_out,
size_t size,
int flags);
}

29
src/mergerfs.cpp

@ -22,6 +22,7 @@
#include "fuse_access.hpp" #include "fuse_access.hpp"
#include "fuse_chmod.hpp" #include "fuse_chmod.hpp"
#include "fuse_chown.hpp" #include "fuse_chown.hpp"
#include "fuse_copy_file_range.hpp"
#include "fuse_create.hpp" #include "fuse_create.hpp"
#include "fuse_destroy.hpp" #include "fuse_destroy.hpp"
#include "fuse_fallocate.hpp" #include "fuse_fallocate.hpp"
@ -82,25 +83,24 @@ namespace l
ops.flag_nopath = true; ops.flag_nopath = true;
ops.flag_utime_omit_ok = true; ops.flag_utime_omit_ok = true;
ops.prepare_hide = FUSE::prepare_hide;
ops.free_hide = FUSE::free_hide;
ops.fchown = FUSE::fchown;
ops.fchmod = FUSE::fchmod;
ops.futimens = FUSE::futimens;
ops.access = FUSE::access; ops.access = FUSE::access;
ops.bmap = NULL; ops.bmap = NULL;
ops.chmod = FUSE::chmod; ops.chmod = FUSE::chmod;
ops.chown = FUSE::chown; ops.chown = FUSE::chown;
ops.copy_file_range = FUSE::copy_file_range;
ops.create = FUSE::create; ops.create = FUSE::create;
ops.destroy = FUSE::destroy; ops.destroy = FUSE::destroy;
ops.fallocate = FUSE::fallocate; ops.fallocate = FUSE::fallocate;
ops.fchmod = FUSE::fchmod;
ops.fchown = FUSE::fchown;
ops.fgetattr = FUSE::fgetattr; ops.fgetattr = FUSE::fgetattr;
ops.flock = NULL; // FUSE::flock; ops.flock = NULL; // FUSE::flock;
ops.flush = FUSE::flush; ops.flush = FUSE::flush;
ops.free_hide = FUSE::free_hide;
ops.fsync = FUSE::fsync; ops.fsync = FUSE::fsync;
ops.fsyncdir = FUSE::fsyncdir; ops.fsyncdir = FUSE::fsyncdir;
ops.ftruncate = FUSE::ftruncate; ops.ftruncate = FUSE::ftruncate;
ops.futimens = FUSE::futimens;
ops.getattr = FUSE::getattr; ops.getattr = FUSE::getattr;
ops.getdir = NULL; /* deprecated; use readdir */ ops.getdir = NULL; /* deprecated; use readdir */
ops.getxattr = FUSE::getxattr; ops.getxattr = FUSE::getxattr;
@ -114,12 +114,9 @@ namespace l
ops.open = FUSE::open; ops.open = FUSE::open;
ops.opendir = FUSE::opendir; ops.opendir = FUSE::opendir;
ops.poll = NULL; ops.poll = NULL;
ops.read = (nullrw ?
FUSE::read_null :
FUSE::read);
ops.read_buf = (nullrw ?
NULL :
FUSE::read_buf);
ops.prepare_hide = FUSE::prepare_hide;
ops.read = (nullrw ? FUSE::read_null : FUSE::read);
ops.read_buf = (nullrw ? NULL : FUSE::read_buf);
ops.readdir = FUSE::readdir; ops.readdir = FUSE::readdir;
ops.readlink = FUSE::readlink; ops.readlink = FUSE::readlink;
ops.release = FUSE::release; ops.release = FUSE::release;
@ -134,12 +131,8 @@ namespace l
ops.unlink = FUSE::unlink; ops.unlink = FUSE::unlink;
ops.utime = NULL; /* deprecated; use utimens() */ ops.utime = NULL; /* deprecated; use utimens() */
ops.utimens = FUSE::utimens; ops.utimens = FUSE::utimens;
ops.write = (nullrw ?
FUSE::write_null :
FUSE::write);
ops.write_buf = (nullrw ?
FUSE::write_buf_null :
FUSE::write_buf);
ops.write = (nullrw ? FUSE::write_null : FUSE::write);
ops.write_buf = (nullrw ? FUSE::write_buf_null : FUSE::write_buf);
return; return;
} }

Loading…
Cancel
Save