From cb0a600acfca76c5b4653e4c6f34c1712a2da9de Mon Sep 17 00:00:00 2001 From: Xiubo Li Date: Wed, 15 Apr 2020 02:21:44 -0400 Subject: [PATCH] ceph-fuse: link to libfuse3 if fuse3 is installed Fixes: https://tracker.ceph.com/issues/44891 Signed-off-by: Xiubo Li --- src/ceph_fuse.cc | 11 +++++ src/client/fuse_ll.cc | 84 +++++++++++++++++++++++++++++++++---- src/include/ceph_fuse.h | 32 ++++++++++++++ src/os/FuseStore.cc | 91 +++++++++++++++++++++++++++++++--------- src/rbd_fuse/rbd-fuse.cc | 47 +++++++++++++++++---- 5 files changed, 228 insertions(+), 37 deletions(-) create mode 100644 src/include/ceph_fuse.h diff --git a/src/ceph_fuse.cc b/src/ceph_fuse.cc index 5ac18116f12..9d6141c1eaa 100644 --- a/src/ceph_fuse.cc +++ b/src/ceph_fuse.cc @@ -41,6 +41,7 @@ #include #include +#include #define dout_context g_ceph_context @@ -51,7 +52,12 @@ static void fuse_usage() "-h", }; struct fuse_args args = FUSE_ARGS_INIT(2, (char**)argv); +#if FUSE_VERSION >= FUSE_MAKE_VERSION(3, 0) + struct fuse_cmdline_opts opts = {}; + if (fuse_parse_cmdline(&args, &opts) == -1) { +#else if (fuse_parse_cmdline(&args, nullptr, nullptr, nullptr) == -1) { +#endif derr << "fuse_parse_cmdline failed." << dendl; } ceph_assert(args.allocated); @@ -105,7 +111,12 @@ int main(int argc, const char **argv, const char *envp[]) { }; struct fuse_args fargs = FUSE_ARGS_INIT(2, (char**)tmpargv); +#if FUSE_VERSION >= FUSE_MAKE_VERSION(3, 0) + struct fuse_cmdline_opts opts = {}; + if (fuse_parse_cmdline(&fargs, &opts) == -1) { +#else if (fuse_parse_cmdline(&fargs, nullptr, nullptr, nullptr) == -1) { +#endif derr << "fuse_parse_cmdline failed." << dendl; } ceph_assert(fargs.allocated); diff --git a/src/client/fuse_ll.cc b/src/client/fuse_ll.cc index e1f2a10d701..06af444f1b2 100644 --- a/src/client/fuse_ll.cc +++ b/src/client/fuse_ll.cc @@ -85,9 +85,13 @@ public: int fd_on_success; Client *client; - struct fuse_chan *ch; struct fuse_session *se; +#if FUSE_VERSION >= FUSE_MAKE_VERSION(3, 0) + struct fuse_cmdline_opts opts; +#else + struct fuse_chan *ch; char *mountpoint; +#endif ceph::mutex stag_lock = ceph::make_mutex("fuse_ll.cc stag_lock"); int last_stag; @@ -418,7 +422,11 @@ static void fuse_ll_mkdir(fuse_req_t req, fuse_ino_t parent, const char *name, if (cfuse->fino_snap(parent) == CEPH_SNAPDIR && fuse_multithreaded && fuse_syncfs_on_mksnap) { int err = 0; +#if FUSE_VERSION >= FUSE_MAKE_VERSION(3, 0) + int fd = ::open(cfuse->opts.mountpoint, O_RDONLY | O_DIRECTORY | O_CLOEXEC); +#else int fd = ::open(cfuse->mountpoint, O_RDONLY | O_DIRECTORY | O_CLOEXEC); +#endif if (fd < 0) { err = errno; } else { @@ -504,7 +512,11 @@ static void fuse_ll_symlink(fuse_req_t req, const char *existing, } static void fuse_ll_rename(fuse_req_t req, fuse_ino_t parent, const char *name, - fuse_ino_t newparent, const char *newname) + fuse_ino_t newparent, const char *newname +#if FUSE_VERSION >= FUSE_MAKE_VERSION(3, 0) + , unsigned int flags +#endif + ) { CephFuse::Handle *cfuse = fuse_ll_req_prepare(req); const struct fuse_ctx *ctx = fuse_req_ctx(req); @@ -916,8 +928,12 @@ static void ino_invalidate_cb(void *handle, vinodeno_t vino, int64_t off, #if FUSE_VERSION >= FUSE_MAKE_VERSION(2, 8) CephFuse::Handle *cfuse = (CephFuse::Handle *)handle; fuse_ino_t fino = cfuse->make_fake_ino(vino.ino, vino.snapid); +#if FUSE_VERSION >= FUSE_MAKE_VERSION(3, 0) + fuse_lowlevel_notify_inval_inode(cfuse->se, fino, off, len); +#else fuse_lowlevel_notify_inval_inode(cfuse->ch, fino, off, len); #endif +#endif } static void dentry_invalidate_cb(void *handle, vinodeno_t dirino, @@ -929,7 +945,11 @@ static void dentry_invalidate_cb(void *handle, vinodeno_t dirino, fuse_ino_t fino = 0; if (ino.ino != inodeno_t()) fino = cfuse->make_fake_ino(ino.ino, ino.snapid); +#if FUSE_VERSION >= FUSE_MAKE_VERSION(3, 0) + fuse_lowlevel_notify_delete(cfuse->se, fdirino, fino, name.c_str(), name.length()); +#else fuse_lowlevel_notify_delete(cfuse->ch, fdirino, fino, name.c_str(), name.length()); +#endif #elif FUSE_VERSION >= FUSE_MAKE_VERSION(2, 8) fuse_lowlevel_notify_inval_entry(cfuse->ch, fdirino, name.c_str(), name.length()); #endif @@ -941,7 +961,12 @@ static int remount_cb(void *handle) // trims all unused dentries in the file system char cmd[128+PATH_MAX]; CephFuse::Handle *cfuse = (CephFuse::Handle *)handle; - snprintf(cmd, sizeof(cmd), "LIBMOUNT_FSTAB=/dev/null mount -i -o remount %s", cfuse->mountpoint); + snprintf(cmd, sizeof(cmd), "LIBMOUNT_FSTAB=/dev/null mount -i -o remount %s", +#if FUSE_VERSION >= FUSE_MAKE_VERSION(3, 0) + cfuse->opts.mountpoint); +#else + cfuse->mountpoint); +#endif int r = system(cmd); if (r != 0 && r != -1) { r = WEXITSTATUS(r); @@ -1043,14 +1068,19 @@ const static struct fuse_lowlevel_ops fuse_ll_oper = { CephFuse::Handle::Handle(Client *c, int fd) : fd_on_success(fd), client(c), - ch(NULL), se(NULL), +#if FUSE_VERSION < FUSE_MAKE_VERSION(3, 0) + ch(NULL), mountpoint(NULL), +#endif last_stag(0) { snap_stag_map[CEPH_NOSNAP] = 0; stag_snap_map[0] = CEPH_NOSNAP; memset(&args, 0, sizeof(args)); +#if FUSE_VERSION >= FUSE_MAKE_VERSION(3, 0) + memset(&opts, 0, sizeof(opts)); +#endif } CephFuse::Handle::~Handle() @@ -1060,6 +1090,15 @@ CephFuse::Handle::~Handle() void CephFuse::Handle::finalize() { +#if FUSE_VERSION >= FUSE_MAKE_VERSION(3, 0) + if (se) { + fuse_remove_signal_handlers(se); + fuse_session_unmount(se); + fuse_session_destroy(se); + } + if (opts.mountpoint) + free(opts.mountpoint); +#else if (se) fuse_remove_signal_handlers(se); if (ch) @@ -1068,6 +1107,7 @@ void CephFuse::Handle::finalize() fuse_session_destroy(se); if (ch) fuse_unmount(mountpoint, ch); +#endif pthread_key_delete(fuse_req_key); } @@ -1094,14 +1134,16 @@ int CephFuse::Handle::init(int argc, const char *argv[]) "fuse_allow_other"); auto fuse_default_permissions = client->cct->_conf.get_val( "fuse_default_permissions"); +#if FUSE_VERSION < FUSE_MAKE_VERSION(3, 0) auto fuse_big_writes = client->cct->_conf.get_val( "fuse_big_writes"); - auto fuse_atomic_o_trunc = client->cct->_conf.get_val( - "fuse_atomic_o_trunc"); - auto fuse_debug = client->cct->_conf.get_val( - "fuse_debug"); auto fuse_max_write = client->cct->_conf.get_val( "fuse_max_write"); + auto fuse_atomic_o_trunc = client->cct->_conf.get_val( + "fuse_atomic_o_trunc"); +#endif + auto fuse_debug = client->cct->_conf.get_val( + "fuse_debug"); if (fuse_allow_other) { newargv[newargc++] = "-o"; @@ -1112,6 +1154,7 @@ int CephFuse::Handle::init(int argc, const char *argv[]) newargv[newargc++] = "default_permissions"; } #if defined(__linux__) +#if FUSE_VERSION < FUSE_MAKE_VERSION(3, 0) if (fuse_big_writes) { newargv[newargc++] = "-o"; newargv[newargc++] = "big_writes"; @@ -1126,6 +1169,7 @@ int CephFuse::Handle::init(int argc, const char *argv[]) newargv[newargc++] = "-o"; newargv[newargc++] = "atomic_o_trunc"; } +#endif #endif if (fuse_debug) newargv[newargc++] = "-d"; @@ -1137,7 +1181,11 @@ int CephFuse::Handle::init(int argc, const char *argv[]) struct fuse_args a = FUSE_ARGS_INIT(newargc, (char**)newargv); args = a; // Roundabout construction b/c FUSE_ARGS_INIT is for initialization not assignment +#if FUSE_VERSION >= FUSE_MAKE_VERSION(3, 0) + if (fuse_parse_cmdline(&args, &opts) == -1) { +#else if (fuse_parse_cmdline(&args, &mountpoint, NULL, NULL) == -1) { +#endif derr << "fuse_parse_cmdline failed." << dendl; fuse_opt_free_args(&args); free(newargv); @@ -1151,6 +1199,9 @@ int CephFuse::Handle::init(int argc, const char *argv[]) int CephFuse::Handle::start() { +#if FUSE_VERSION >= FUSE_MAKE_VERSION(3, 0) + se = fuse_session_new(&args, &fuse_ll_oper, sizeof(fuse_ll_oper), this); +#else ch = fuse_mount(mountpoint, &args); if (!ch) { derr << "fuse_mount(mountpoint=" << mountpoint << ") failed." << dendl; @@ -1158,6 +1209,7 @@ int CephFuse::Handle::start() } se = fuse_lowlevel_new(&args, &fuse_ll_oper, sizeof(fuse_ll_oper), this); +#endif if (!se) { derr << "fuse_lowlevel_new failed" << dendl; return EDOM; @@ -1170,7 +1222,14 @@ int CephFuse::Handle::start() return ENOSYS; } +#if FUSE_VERSION >= FUSE_MAKE_VERSION(3, 0) + if (fuse_session_mount(se, opts.mountpoint) != 0) { + derr << "fuse_session_mount failed" << dendl; + return ENOSYS; + } +#else fuse_session_add_chan(se, ch); +#endif struct client_callback_args args = { @@ -1196,7 +1255,11 @@ int CephFuse::Handle::loop() auto fuse_multithreaded = client->cct->_conf.get_val( "fuse_multithreaded"); if (fuse_multithreaded) { +#if FUSE_VERSION >= FUSE_MAKE_VERSION(3, 0) + return fuse_session_loop_mt(se, opts.clone_fd); +#else return fuse_session_loop_mt(se); +#endif } else { return fuse_session_loop(se); } @@ -1326,8 +1389,13 @@ void CephFuse::finalize() std::string CephFuse::get_mount_point() const { +#if FUSE_VERSION >= FUSE_MAKE_VERSION(3, 0) + if (_handle->opts.mountpoint) { + return _handle->opts.mountpoint; +#else if (_handle->mountpoint) { return _handle->mountpoint; +#endif } else { return ""; } diff --git a/src/include/ceph_fuse.h b/src/include/ceph_fuse.h new file mode 100644 index 00000000000..45881930b87 --- /dev/null +++ b/src/include/ceph_fuse.h @@ -0,0 +1,32 @@ +// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- +// vim: ts=8 sw=2 smarttab +/* + * Ceph - scalable distributed file system + * + * Copyright (C) 2012 Inktank Storage, Inc. + * Copyright (C) 2014 Red Hat + * + * This is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2.1, as published by the Free Software + * Foundation. See file COPYING. + */ +#ifndef CEPH_FUSE_H +#define CEPH_FUSE_H + +#define FUSE_USE_VERSION 30 +#include "acconfig.h" +#include + +static inline int filler_compat(fuse_fill_dir_t filler, + void *buf, const char *name, + const struct stat *stbuf, + off_t off) +{ + return filler(buf, name, stbuf, off +#if FUSE_VERSION >= FUSE_MAKE_VERSION(3, 0) + , static_cast(0) +#endif + ); +} +#endif /* CEPH_FUSE_H */ diff --git a/src/os/FuseStore.cc b/src/os/FuseStore.cc index 98d4b12d328..35755f737f1 100644 --- a/src/os/FuseStore.cc +++ b/src/os/FuseStore.cc @@ -9,6 +9,8 @@ #define FUSE_USE_VERSION 30 #include +#include +#include "include/ceph_fuse.h" #include #include @@ -40,7 +42,9 @@ using ceph::bufferptr; struct fs_info { struct fuse_args args; struct fuse *f; +#if FUSE_VERSION < FUSE_MAKE_VERSION(3, 0) struct fuse_chan *ch; +#endif char *mountpoint; }; @@ -238,7 +242,11 @@ static int parse_fn(CephContext* cct, const char *path, coll_t *cid, } -static int os_getattr(const char *path, struct stat *stbuf) +static int os_getattr(const char *path, struct stat *stbuf +#if FUSE_VERSION >= FUSE_MAKE_VERSION(3, 0) + , struct fuse_file_info *fi +#endif + ) { fuse_context *fc = fuse_get_context(); FuseStore *fs = static_cast(fc->private_data); @@ -395,7 +403,11 @@ static int os_readdir(const char *path, void *buf, fuse_fill_dir_t filler, off_t offset, - struct fuse_file_info *fi) + struct fuse_file_info *fi +#if FUSE_VERSION >= FUSE_MAKE_VERSION(3, 0) + , enum fuse_readdir_flags +#endif + ) { fuse_context *fc = fuse_get_context(); FuseStore *fs = static_cast(fc->private_data); @@ -420,11 +432,11 @@ static int os_readdir(const char *path, switch (t) { case FN_ROOT: { - filler(buf, "type", NULL, 0); + filler_compat(filler, buf, "type", NULL, 0); vector cls; fs->store->list_collections(cls); for (auto c : cls) { - int r = filler(buf, stringify(c).c_str(), NULL, 0); + int r = filler_compat(filler, buf, stringify(c).c_str(), NULL, 0); if (r > 0) break; } @@ -436,28 +448,28 @@ static int os_readdir(const char *path, if (!ch) { return -ENOENT; } - filler(buf, "bitwise_hash_start", NULL, 0); + filler_compat(filler, buf, "bitwise_hash_start", NULL, 0); if (fs->store->collection_bits(ch) >= 0) { - filler(buf, "bitwise_hash_end", NULL, 0); - filler(buf, "bitwise_hash_bits", NULL, 0); + filler_compat(filler, buf, "bitwise_hash_end", NULL, 0); + filler_compat(filler, buf, "bitwise_hash_bits", NULL, 0); } - filler(buf, "all", NULL, 0); - filler(buf, "by_bitwise_hash", NULL, 0); + filler_compat(filler, buf, "all", NULL, 0); + filler_compat(filler, buf, "by_bitwise_hash", NULL, 0); spg_t pgid; if (cid.is_pg(&pgid) && fs->store->exists(ch, pgid.make_pgmeta_oid())) { - filler(buf, "pgmeta", NULL, 0); + filler_compat(filler, buf, "pgmeta", NULL, 0); } } break; case FN_OBJECT: { - filler(buf, "bitwise_hash", NULL, 0); - filler(buf, "data", NULL, 0); - filler(buf, "omap", NULL, 0); - filler(buf, "attr", NULL, 0); - filler(buf, "omap_header", NULL, 0); + filler_compat(filler, buf, "bitwise_hash", NULL, 0); + filler_compat(filler, buf, "data", NULL, 0); + filler_compat(filler, buf, "omap", NULL, 0); + filler_compat(filler, buf, "attr", NULL, 0); + filler_compat(filler, buf, "omap_header", NULL, 0); } break; @@ -512,7 +524,7 @@ static int os_readdir(const char *path, uint64_t cur_off = ((uint64_t)bitwise_hash << hash_shift) | (uint64_t)hashoff; string s = stringify(p); - r = filler(buf, s.c_str(), NULL, cur_off); + r = filler_compat(filler, buf, s.c_str(), NULL, cur_off); if (r) break; } @@ -535,7 +547,7 @@ static int os_readdir(const char *path, continue; } ++offset; - int r = filler(buf, k.c_str(), NULL, offset); + int r = filler_compat(filler, buf, k.c_str(), NULL, offset); if (r) break; } @@ -553,7 +565,7 @@ static int os_readdir(const char *path, continue; } ++offset; - int r = filler(buf, a.first.c_str(), NULL, offset); + int r = filler_compat(filler, buf, a.first.c_str(), NULL, offset); if (r) break; } @@ -790,7 +802,11 @@ static int os_mkdir(const char *path, mode_t mode) return 0; } -static int os_chmod(const char *path, mode_t mode) +static int os_chmod(const char *path, mode_t mode +#if FUSE_VERSION >= FUSE_MAKE_VERSION(3, 0) + , struct fuse_file_info *fi +#endif + ) { fuse_context *fc = fuse_get_context(); FuseStore *fs = static_cast(fc->private_data); @@ -1061,7 +1077,11 @@ static int os_unlink(const char *path) return 0; } -static int os_truncate(const char *path, off_t size) +static int os_truncate(const char *path, off_t size +#if FUSE_VERSION >= FUSE_MAKE_VERSION(3, 0) + , struct fuse_file_info *fi +#endif + ) { fuse_context *fc = fuse_get_context(); FuseStore *fs = static_cast(fc->private_data); @@ -1127,7 +1147,9 @@ static int os_statfs(const char *path, struct statvfs *stbuf) static struct fuse_operations fs_oper = { getattr: os_getattr, readlink: 0, +#if FUSE_VERSION < FUSE_MAKE_VERSION(3, 0) getdir: 0, +#endif mknod: 0, mkdir: os_mkdir, unlink: os_unlink, @@ -1138,7 +1160,9 @@ static struct fuse_operations fs_oper = { chmod: os_chmod, chown: 0, truncate: os_truncate, +#if FUSE_VERSION < FUSE_MAKE_VERSION(3, 0) utime: 0, +#endif open: os_open, read: os_read, write: os_write, @@ -1187,16 +1211,38 @@ int FuseStore::start() "-d", // debug }; int c = 3; +#if FUSE_VERSION >= FUSE_MAKE_VERSION(3, 0) + int rc; + struct fuse_cmdline_opts opts = {}; +#endif auto fuse_debug = store->cct->_conf.get_val("fuse_debug"); if (fuse_debug) ++c; fuse_args a = FUSE_ARGS_INIT(c, (char**)v); info->args = a; +#if FUSE_VERSION >= FUSE_MAKE_VERSION(3, 0) + if (fuse_parse_cmdline(&info->args, &opts) == -1) { +#else if (fuse_parse_cmdline(&info->args, &info->mountpoint, NULL, NULL) == -1) { +#endif derr << __func__ << " failed to parse args" << dendl; return -EINVAL; } +#if FUSE_VERSION >= FUSE_MAKE_VERSION(3, 0) + info->mountpoint = opts.mountpoint; + info->f = fuse_new(&info->args, &fs_oper, sizeof(fs_oper), (void*)this); + if (!info->f) { + derr << __func__ << " fuse_new failed" << dendl; + return -EIO; + } + + rc = fuse_mount(info->f, info->mountpoint); + if (rc != 0) { + derr << __func__ << " fuse_mount failed" << dendl; + return -EIO; + } +#else info->ch = fuse_mount(info->mountpoint, &info->args); if (!info->ch) { derr << __func__ << " fuse_mount failed" << dendl; @@ -1210,6 +1256,7 @@ int FuseStore::start() derr << __func__ << " fuse_new failed" << dendl; return -EIO; } +#endif fuse_thread.create("fusestore"); dout(10) << __func__ << " done" << dendl; @@ -1229,7 +1276,11 @@ int FuseStore::loop() int FuseStore::stop() { dout(10) << __func__ << " enter" << dendl; +#if FUSE_VERSION >= FUSE_MAKE_VERSION(3, 0) + fuse_unmount(info->f); +#else fuse_unmount(info->mountpoint, info->ch); +#endif fuse_thread.join(); fuse_destroy(info->f); dout(10) << __func__ << " exit" << dendl; diff --git a/src/rbd_fuse/rbd-fuse.cc b/src/rbd_fuse/rbd-fuse.cc index b7feea7bfd0..e7c8166542a 100644 --- a/src/rbd_fuse/rbd-fuse.cc +++ b/src/rbd_fuse/rbd-fuse.cc @@ -32,6 +32,7 @@ #include "common/ceph_argparse.h" #include "common/ceph_context.h" +#include "include/ceph_fuse.h" #include "global/global_init.h" #include "global/global_context.h" @@ -244,7 +245,11 @@ static int count_images(void) extern "C" { -static int rbdfs_getattr(const char *path, struct stat *stbuf) +static int rbdfs_getattr(const char *path, struct stat *stbuf +#if FUSE_VERSION >= FUSE_MAKE_VERSION(3, 0) + , struct fuse_file_info *fi +#endif + ) { int fd; time_t now; @@ -456,11 +461,15 @@ static void rbdfs_readdir_cb(void *_info, const char *name) { struct rbdfs_readdir_info *info = (struct rbdfs_readdir_info*) _info; - info->filler(info->buf, name, NULL, 0); + filler_compat(info->filler, info->buf, name, NULL, 0); } static int rbdfs_readdir(const char *path, void *buf, fuse_fill_dir_t filler, - off_t offset, struct fuse_file_info *fi) + off_t offset, struct fuse_file_info *fi +#if FUSE_VERSION >= FUSE_MAKE_VERSION(3, 0) + , enum fuse_readdir_flags +#endif + ) { struct rbdfs_readdir_info info = { buf, filler }; @@ -472,8 +481,8 @@ static int rbdfs_readdir(const char *path, void *buf, fuse_fill_dir_t filler, if (strcmp(path, "/") != 0) return -ENOENT; - filler(buf, ".", NULL, 0); - filler(buf, "..", NULL, 0); + filler_compat(filler, buf, ".", NULL, 0); + filler_compat(filler, buf, "..", NULL, 0); iter_images(&info, rbdfs_readdir_cb); return 0; @@ -488,7 +497,11 @@ static int rbdfs_releasedir(const char *path, struct fuse_file_info *fi) } void * -rbdfs_init(struct fuse_conn_info *conn) +rbdfs_init(struct fuse_conn_info *conn +#if FUSE_VERSION >= FUSE_MAKE_VERSION(3, 0) + , struct fuse_config *cfg +#endif + ) { int ret; @@ -570,7 +583,11 @@ rbdfs_create(const char *path, mode_t mode, struct fuse_file_info *fi) } int -rbdfs_rename(const char *path, const char *destname) +rbdfs_rename(const char *path, const char *destname +#if FUSE_VERSION >= FUSE_MAKE_VERSION(3, 0) + , unsigned int flags +#endif + ) { int r; @@ -587,7 +604,11 @@ rbdfs_rename(const char *path, const char *destname) } int -rbdfs_utimens(const char *path, const struct timespec tv[2]) +rbdfs_utimens(const char *path, const struct timespec tv[2] +#if FUSE_VERSION >= FUSE_MAKE_VERSION(3, 0) + , struct fuse_file_info *fi +#endif + ) { // called on create; not relevant return 0; @@ -609,7 +630,11 @@ rbdfs_unlink(const char *path) int -rbdfs_truncate(const char *path, off_t size) +rbdfs_truncate(const char *path, off_t size +#if FUSE_VERSION >= FUSE_MAKE_VERSION(3, 0) + , struct fuse_file_info *fi +#endif + ) { int fd; int r; @@ -724,7 +749,9 @@ rbdfs_listxattr(const char *path, char *list, size_t len) const static struct fuse_operations rbdfs_oper = { getattr: rbdfs_getattr, readlink: 0, +#if FUSE_VERSION < FUSE_MAKE_VERSION(3, 0) getdir: 0, +#endif mknod: 0, mkdir: 0, unlink: rbdfs_unlink, @@ -757,8 +784,10 @@ const static struct fuse_operations rbdfs_oper = { destroy: rbdfs_destroy, access: 0, create: rbdfs_create, +#if FUSE_VERSION < FUSE_MAKE_VERSION(3, 0) ftruncate: 0, fgetattr: 0, +#endif lock: 0, utimens: rbdfs_utimens, /* skip unimplemented */