add copy_file_range support

This commit is contained in:
Antonio SJ Musumeci 2019-05-25 14:25:57 -04:00
parent c8e178bf71
commit 8cb7195c3e
9 changed files with 425 additions and 144 deletions

View File

@ -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 |
| create | create, mkdir, mknod, symlink |
| 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.

View File

@ -608,6 +608,30 @@ 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
@ -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_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);

View File

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

View File

@ -2296,7 +2296,32 @@ int fuse_fs_fallocate(struct fuse_fs *fs, const char *path, int mode,
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
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);
}
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)
{
struct lock *l;
@ -4113,45 +4193,46 @@ int fuse_clean_cache(struct fuse *f)
}
static struct fuse_lowlevel_ops fuse_path_ops = {
.init = fuse_lib_init,
.destroy = fuse_lib_destroy,
.lookup = fuse_lib_lookup,
.forget = fuse_lib_forget,
.forget_multi = fuse_lib_forget_multi,
.getattr = fuse_lib_getattr,
.setattr = fuse_lib_setattr,
.access = fuse_lib_access,
.readlink = fuse_lib_readlink,
.mknod = fuse_lib_mknod,
.mkdir = fuse_lib_mkdir,
.unlink = fuse_lib_unlink,
.rmdir = fuse_lib_rmdir,
.symlink = fuse_lib_symlink,
.rename = fuse_lib_rename,
.link = fuse_lib_link,
.create = fuse_lib_create,
.open = fuse_lib_open,
.read = fuse_lib_read,
.write_buf = fuse_lib_write_buf,
.flush = fuse_lib_flush,
.release = fuse_lib_release,
.fsync = fuse_lib_fsync,
.opendir = fuse_lib_opendir,
.readdir = fuse_lib_readdir,
.releasedir = fuse_lib_releasedir,
.fsyncdir = fuse_lib_fsyncdir,
.statfs = fuse_lib_statfs,
.setxattr = fuse_lib_setxattr,
.getxattr = fuse_lib_getxattr,
.listxattr = fuse_lib_listxattr,
.removexattr = fuse_lib_removexattr,
.getlk = fuse_lib_getlk,
.setlk = fuse_lib_setlk,
.flock = fuse_lib_flock,
.bmap = fuse_lib_bmap,
.ioctl = fuse_lib_ioctl,
.poll = fuse_lib_poll,
.fallocate = fuse_lib_fallocate,
.init = fuse_lib_init,
.destroy = fuse_lib_destroy,
.lookup = fuse_lib_lookup,
.forget = fuse_lib_forget,
.forget_multi = fuse_lib_forget_multi,
.getattr = fuse_lib_getattr,
.setattr = fuse_lib_setattr,
.access = fuse_lib_access,
.readlink = fuse_lib_readlink,
.mknod = fuse_lib_mknod,
.mkdir = fuse_lib_mkdir,
.unlink = fuse_lib_unlink,
.rmdir = fuse_lib_rmdir,
.symlink = fuse_lib_symlink,
.rename = fuse_lib_rename,
.link = fuse_lib_link,
.create = fuse_lib_create,
.open = fuse_lib_open,
.read = fuse_lib_read,
.write_buf = fuse_lib_write_buf,
.flush = fuse_lib_flush,
.release = fuse_lib_release,
.fsync = fuse_lib_fsync,
.opendir = fuse_lib_opendir,
.readdir = fuse_lib_readdir,
.releasedir = fuse_lib_releasedir,
.fsyncdir = fuse_lib_fsyncdir,
.statfs = fuse_lib_statfs,
.setxattr = fuse_lib_setxattr,
.getxattr = fuse_lib_getxattr,
.listxattr = fuse_lib_listxattr,
.removexattr = fuse_lib_removexattr,
.getlk = fuse_lib_getlk,
.setlk = fuse_lib_setlk,
.flock = fuse_lib_flock,
.bmap = fuse_lib_bmap,
.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)

View File

@ -1962,6 +1962,34 @@ static void do_notify_reply(fuse_req_t req, fuse_ino_t nodeid,
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,
int notify_code, struct iovec *iov, int count)
{
@ -2262,50 +2290,52 @@ int fuse_req_interrupted(fuse_req_t req)
static struct {
void (*func)(fuse_req_t, fuse_ino_t, const void *);
const char *name;
} fuse_ll_ops[] = {
[FUSE_LOOKUP] = { do_lookup, "LOOKUP" },
[FUSE_FORGET] = { do_forget, "FORGET" },
[FUSE_GETATTR] = { do_getattr, "GETATTR" },
[FUSE_SETATTR] = { do_setattr, "SETATTR" },
[FUSE_READLINK] = { do_readlink, "READLINK" },
[FUSE_SYMLINK] = { do_symlink, "SYMLINK" },
[FUSE_MKNOD] = { do_mknod, "MKNOD" },
[FUSE_MKDIR] = { do_mkdir, "MKDIR" },
[FUSE_UNLINK] = { do_unlink, "UNLINK" },
[FUSE_RMDIR] = { do_rmdir, "RMDIR" },
[FUSE_RENAME] = { do_rename, "RENAME" },
[FUSE_LINK] = { do_link, "LINK" },
[FUSE_OPEN] = { do_open, "OPEN" },
[FUSE_READ] = { do_read, "READ" },
[FUSE_WRITE] = { do_write, "WRITE" },
[FUSE_STATFS] = { do_statfs, "STATFS" },
[FUSE_RELEASE] = { do_release, "RELEASE" },
[FUSE_FSYNC] = { do_fsync, "FSYNC" },
[FUSE_SETXATTR] = { do_setxattr, "SETXATTR" },
[FUSE_GETXATTR] = { do_getxattr, "GETXATTR" },
[FUSE_LISTXATTR] = { do_listxattr, "LISTXATTR" },
[FUSE_REMOVEXATTR] = { do_removexattr, "REMOVEXATTR" },
[FUSE_FLUSH] = { do_flush, "FLUSH" },
[FUSE_INIT] = { do_init, "INIT" },
[FUSE_OPENDIR] = { do_opendir, "OPENDIR" },
[FUSE_READDIR] = { do_readdir, "READDIR" },
[FUSE_RELEASEDIR] = { do_releasedir, "RELEASEDIR" },
[FUSE_FSYNCDIR] = { do_fsyncdir, "FSYNCDIR" },
[FUSE_GETLK] = { do_getlk, "GETLK" },
[FUSE_SETLK] = { do_setlk, "SETLK" },
[FUSE_SETLKW] = { do_setlkw, "SETLKW" },
[FUSE_ACCESS] = { do_access, "ACCESS" },
[FUSE_CREATE] = { do_create, "CREATE" },
[FUSE_INTERRUPT] = { do_interrupt, "INTERRUPT" },
[FUSE_BMAP] = { do_bmap, "BMAP" },
[FUSE_IOCTL] = { do_ioctl, "IOCTL" },
[FUSE_POLL] = { do_poll, "POLL" },
[FUSE_FALLOCATE] = { do_fallocate, "FALLOCATE" },
[FUSE_DESTROY] = { do_destroy, "DESTROY" },
[FUSE_NOTIFY_REPLY] = { (void *) 1, "NOTIFY_REPLY" },
[FUSE_BATCH_FORGET] = { do_batch_forget, "BATCH_FORGET" },
[CUSE_INIT] = { cuse_lowlevel_init, "CUSE_INIT" },
};
} fuse_ll_ops[] =
{
[FUSE_LOOKUP] = { do_lookup, "LOOKUP" },
[FUSE_FORGET] = { do_forget, "FORGET" },
[FUSE_GETATTR] = { do_getattr, "GETATTR" },
[FUSE_SETATTR] = { do_setattr, "SETATTR" },
[FUSE_READLINK] = { do_readlink, "READLINK" },
[FUSE_SYMLINK] = { do_symlink, "SYMLINK" },
[FUSE_MKNOD] = { do_mknod, "MKNOD" },
[FUSE_MKDIR] = { do_mkdir, "MKDIR" },
[FUSE_UNLINK] = { do_unlink, "UNLINK" },
[FUSE_RMDIR] = { do_rmdir, "RMDIR" },
[FUSE_RENAME] = { do_rename, "RENAME" },
[FUSE_LINK] = { do_link, "LINK" },
[FUSE_OPEN] = { do_open, "OPEN" },
[FUSE_READ] = { do_read, "READ" },
[FUSE_WRITE] = { do_write, "WRITE" },
[FUSE_STATFS] = { do_statfs, "STATFS" },
[FUSE_RELEASE] = { do_release, "RELEASE" },
[FUSE_FSYNC] = { do_fsync, "FSYNC" },
[FUSE_SETXATTR] = { do_setxattr, "SETXATTR" },
[FUSE_GETXATTR] = { do_getxattr, "GETXATTR" },
[FUSE_LISTXATTR] = { do_listxattr, "LISTXATTR" },
[FUSE_REMOVEXATTR] = { do_removexattr, "REMOVEXATTR" },
[FUSE_FLUSH] = { do_flush, "FLUSH" },
[FUSE_INIT] = { do_init, "INIT" },
[FUSE_OPENDIR] = { do_opendir, "OPENDIR" },
[FUSE_READDIR] = { do_readdir, "READDIR" },
[FUSE_RELEASEDIR] = { do_releasedir, "RELEASEDIR" },
[FUSE_FSYNCDIR] = { do_fsyncdir, "FSYNCDIR" },
[FUSE_GETLK] = { do_getlk, "GETLK" },
[FUSE_SETLK] = { do_setlk, "SETLK" },
[FUSE_SETLKW] = { do_setlkw, "SETLKW" },
[FUSE_ACCESS] = { do_access, "ACCESS" },
[FUSE_CREATE] = { do_create, "CREATE" },
[FUSE_INTERRUPT] = { do_interrupt, "INTERRUPT" },
[FUSE_BMAP] = { do_bmap, "BMAP" },
[FUSE_IOCTL] = { do_ioctl, "IOCTL" },
[FUSE_POLL] = { do_poll, "POLL" },
[FUSE_FALLOCATE] = { do_fallocate, "FALLOCATE" },
[FUSE_DESTROY] = { do_destroy, "DESTROY" },
[FUSE_NOTIFY_REPLY] = { (void *) 1, "NOTIFY_REPLY" },
[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" },
};
#define FUSE_MAXOP (sizeof(fuse_ll_ops) / sizeof(fuse_ll_ops[0]))

View File

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

View File

@ -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_);
}
}

View File

@ -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);
}

View File

@ -22,6 +22,7 @@
#include "fuse_access.hpp"
#include "fuse_chmod.hpp"
#include "fuse_chown.hpp"
#include "fuse_copy_file_range.hpp"
#include "fuse_create.hpp"
#include "fuse_destroy.hpp"
#include "fuse_fallocate.hpp"
@ -82,64 +83,56 @@ namespace l
ops.flag_nopath = 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.bmap = NULL;
ops.chmod = FUSE::chmod;
ops.chown = FUSE::chown;
ops.create = FUSE::create;
ops.destroy = FUSE::destroy;
ops.fallocate = FUSE::fallocate;
ops.fgetattr = FUSE::fgetattr;
ops.flock = NULL; // FUSE::flock;
ops.flush = FUSE::flush;
ops.fsync = FUSE::fsync;
ops.fsyncdir = FUSE::fsyncdir;
ops.ftruncate = FUSE::ftruncate;
ops.getattr = FUSE::getattr;
ops.getdir = NULL; /* deprecated; use readdir */
ops.getxattr = FUSE::getxattr;
ops.init = FUSE::init;
ops.ioctl = FUSE::ioctl;
ops.link = FUSE::link;
ops.listxattr = FUSE::listxattr;
ops.lock = NULL;
ops.mkdir = FUSE::mkdir;
ops.mknod = FUSE::mknod;
ops.open = FUSE::open;
ops.opendir = FUSE::opendir;
ops.poll = NULL;
ops.read = (nullrw ?
FUSE::read_null :
FUSE::read);
ops.read_buf = (nullrw ?
NULL :
FUSE::read_buf);
ops.readdir = FUSE::readdir;
ops.readlink = FUSE::readlink;
ops.release = FUSE::release;
ops.releasedir = FUSE::releasedir;
ops.removexattr = FUSE::removexattr;
ops.rename = FUSE::rename;
ops.rmdir = FUSE::rmdir;
ops.setxattr = FUSE::setxattr;
ops.statfs = FUSE::statfs;
ops.symlink = FUSE::symlink;
ops.truncate = FUSE::truncate;
ops.unlink = FUSE::unlink;
ops.utime = NULL; /* deprecated; use 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.access = FUSE::access;
ops.bmap = NULL;
ops.chmod = FUSE::chmod;
ops.chown = FUSE::chown;
ops.copy_file_range = FUSE::copy_file_range;
ops.create = FUSE::create;
ops.destroy = FUSE::destroy;
ops.fallocate = FUSE::fallocate;
ops.fchmod = FUSE::fchmod;
ops.fchown = FUSE::fchown;
ops.fgetattr = FUSE::fgetattr;
ops.flock = NULL; // FUSE::flock;
ops.flush = FUSE::flush;
ops.free_hide = FUSE::free_hide;
ops.fsync = FUSE::fsync;
ops.fsyncdir = FUSE::fsyncdir;
ops.ftruncate = FUSE::ftruncate;
ops.futimens = FUSE::futimens;
ops.getattr = FUSE::getattr;
ops.getdir = NULL; /* deprecated; use readdir */
ops.getxattr = FUSE::getxattr;
ops.init = FUSE::init;
ops.ioctl = FUSE::ioctl;
ops.link = FUSE::link;
ops.listxattr = FUSE::listxattr;
ops.lock = NULL;
ops.mkdir = FUSE::mkdir;
ops.mknod = FUSE::mknod;
ops.open = FUSE::open;
ops.opendir = FUSE::opendir;
ops.poll = NULL;
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.readlink = FUSE::readlink;
ops.release = FUSE::release;
ops.releasedir = FUSE::releasedir;
ops.removexattr = FUSE::removexattr;
ops.rename = FUSE::rename;
ops.rmdir = FUSE::rmdir;
ops.setxattr = FUSE::setxattr;
ops.statfs = FUSE::statfs;
ops.symlink = FUSE::symlink;
ops.truncate = FUSE::truncate;
ops.unlink = FUSE::unlink;
ops.utime = NULL; /* deprecated; use utimens() */
ops.utimens = FUSE::utimens;
ops.write = (nullrw ? FUSE::write_null : FUSE::write);
ops.write_buf = (nullrw ? FUSE::write_buf_null : FUSE::write_buf);
return;
}