/* * Copyright (c) 2016, Facebook, Inc. * All rights reserved. * * This source code is licensed under the BSD-style license found in the * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * */ #include "Dispatcher.h" #include #include #include #include #include "DirHandle.h" #include "FileHandle.h" #include "MountPoint.h" #include "RequestData.h" #include "SessionDeleter.h" using namespace folly; namespace facebook { namespace eden { namespace fusell { Dispatcher::Attr::Attr() : timeout(1.0) { memset(&st, 0, sizeof(st)); auto& req = RequestData::get(); auto mount = req.getChannel().getMountPoint(); st.st_uid = mount->getUid(); st.st_gid = mount->getGid(); } Dispatcher::~Dispatcher() {} void Dispatcher::initConnection(fuse_conn_info& conn) {} static std::string flagsToLabel( const std::unordered_map& labels, uint32_t flags) { std::vector bits; for (auto& it : labels) { if (it.first == 0) { // Sometimes a define evaluates to zero; it's not useful so skip it continue; } if ((flags & it.first) == it.first) { bits.push_back(it.second); flags &= ~it.first; } } std::string str; folly::join(" ", bits, str); if (flags == 0) { return str; } return folly::format("{} unknown:0x{:x}", str, flags).str(); } static std::unordered_map capsLabels = { {FUSE_CAP_ASYNC_READ, "ASYNC_READ"}, {FUSE_CAP_POSIX_LOCKS, "POSIX_LOCKS"}, {FUSE_CAP_ATOMIC_O_TRUNC, "ATOMIC_O_TRUNC"}, {FUSE_CAP_EXPORT_SUPPORT, "EXPORT_SUPPORT"}, {FUSE_CAP_BIG_WRITES, "BIG_WRITES"}, {FUSE_CAP_DONT_MASK, "DONT_MASK"}, #ifdef FUSE_CAP_SPLICE_WRITE {FUSE_CAP_SPLICE_WRITE, "SPLICE_WRITE"}, {FUSE_CAP_SPLICE_MOVE, "SPLICE_MOVE"}, {FUSE_CAP_SPLICE_READ, "SPLICE_READ"}, {FUSE_CAP_FLOCK_LOCKS, "FLOCK_LOCKS"}, {FUSE_CAP_IOCTL_DIR, "IOCTL_DIR"}, #endif #ifdef __APPLE__ {FUSE_CAP_ALLOCATE, "ALLOCATE"}, {FUSE_CAP_EXCHANGE_DATA, "EXCHANGE_DATA"}, {FUSE_CAP_CASE_INSENSITIVE, "CASE_INSENSITIVE"}, {FUSE_CAP_VOL_RENAME, "VOL_RENAME"}, {FUSE_CAP_XTIMES, "XTIMES"}, #endif }; void Dispatcher::disp_init(void* userdata, struct fuse_conn_info* conn) { auto disp = reinterpret_cast(userdata); conn->want |= conn->capable & ( #ifdef FUSE_CAP_IOCTL_DIR FUSE_CAP_IOCTL_DIR | #endif FUSE_CAP_ATOMIC_O_TRUNC | FUSE_CAP_BIG_WRITES | FUSE_CAP_ASYNC_READ); disp->initConnection(*conn); disp->connInfo_ = *conn; LOG(INFO) << "Speaking fuse protocol " << conn->proto_major << "." << conn->proto_minor << ", async_read=" << conn->async_read << ", max_write=" << conn->max_write << ", max_readahead=" << conn->max_readahead << ", capable=" << flagsToLabel(capsLabels, conn->capable) << ", want=" << flagsToLabel(capsLabels, conn->want); } void Dispatcher::destroy() {} static void disp_destroy(void* userdata) { static_cast(userdata)->destroy(); } #define CATCH_ERRORS() \ onError([](const std::system_error& err) { \ int errnum = EIO; \ if (err.code().category() == std::system_category()) { \ errnum = err.code().value(); \ } \ RequestData::get().replyError(errnum); \ }) \ .onError([](const std::exception& err) { \ RequestData::get().replyError(EIO); \ }) folly::Future Dispatcher::lookup( fuse_ino_t parent, PathComponentPiece name) { throwSystemErrorExplicit(ENOENT); } #define startRequest() makeFuture() static void disp_lookup(fuse_req_t req, fuse_ino_t parent, const char* name) { auto& request = RequestData::create(req); request.setRequestFuture(startRequest() .then([=, &request] { return request.getDispatcher()->lookup( parent, PathComponentPiece(name)); }) .then([](fuse_entry_param&& param) { RequestData::get().replyEntry(param); }) .CATCH_ERRORS()); } folly::Future Dispatcher::forget(fuse_ino_t ino, unsigned long nlookup) { return Unit{}; } static void disp_forget(fuse_req_t req, fuse_ino_t ino, unsigned long nlookup) { auto& request = RequestData::create(req); startRequest() .then([=, &request] { return request.getDispatcher()->forget(ino, nlookup); }) .then([]() { RequestData::get().replyNone(); }) .CATCH_ERRORS(); } #if FUSE_MINOR_VERSION >= 9 static void disp_forget_multi(fuse_req_t req, size_t count, fuse_forget_data *forgets) { auto& request = RequestData::create(req); std::vector forget(forgets, forgets + count); startRequest() .then([ =, &request, forget = std::move(forget) ] { for (auto &f:forget) { request.getDispatcher()->forget(f.ino, f.nlookup); } return Unit{}; }) .then([]() { RequestData::get().replyNone(); }) .CATCH_ERRORS(); } #endif static FileHandleBase* getGenericFileHandle(uint64_t fh) { if (fh == 0) { throwSystemErrorExplicit(EBADF); } return reinterpret_cast(fh); } static FileHandle* getFileHandle(uint64_t fh) { auto f = dynamic_cast(getGenericFileHandle(fh)); if (!f) { throwSystemErrorExplicit(EISDIR); } return f; } static DirHandle* getDirHandle(uint64_t fh) { auto f = dynamic_cast(getGenericFileHandle(fh)); if (!f) { throwSystemErrorExplicit(ENOTDIR); } return f; } folly::Future Dispatcher::getattr(fuse_ino_t ino) { throwSystemErrorExplicit(ENOENT); } static void disp_getattr(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info* fi) { auto& request = RequestData::create(req); if (fi) { request.setRequestFuture( startRequest() .then([ =, &request, fi = *fi ] { return getGenericFileHandle(fi.fh)->getattr(); }) .then([](Dispatcher::Attr&& attr) { RequestData::get().replyAttr(attr.st, attr.timeout); }) .CATCH_ERRORS()); } else { request.setRequestFuture(startRequest() .then([=, &request] { return request.getDispatcher()->getattr(ino); }) .then([](Dispatcher::Attr&& attr) { RequestData::get().replyAttr(attr.st, attr.timeout); }) .CATCH_ERRORS()); } } folly::Future Dispatcher::setattr(fuse_ino_t ino, const struct stat& attr, int to_set) { FUSELL_NOT_IMPL(); } static void disp_setattr(fuse_req_t req, fuse_ino_t ino, struct stat* attr, int to_set, struct fuse_file_info* fi) { auto& request = RequestData::create(req); if (fi) { request.setRequestFuture( startRequest() .then([ =, &request, fi = *fi ]() { return getGenericFileHandle(fi.fh)->setattr(*attr, to_set); }) .then([](Dispatcher::Attr&& attr) { RequestData::get().replyAttr(attr.st, attr.timeout); }) .CATCH_ERRORS()); } else { request.setRequestFuture( startRequest() .then([=, &request]() { return request.getDispatcher()->setattr(ino, *attr, to_set); }) .then([](Dispatcher::Attr&& attr) { RequestData::get().replyAttr(attr.st, attr.timeout); }) .CATCH_ERRORS()); } } folly::Future Dispatcher::readlink(fuse_ino_t ino) { FUSELL_NOT_IMPL(); } static void disp_readlink(fuse_req_t req, fuse_ino_t ino) { auto& request = RequestData::create(req); request.setRequestFuture(startRequest() .then([=, &request] { return request.getDispatcher()->readlink(ino); }) .then([](std::string&& str) { RequestData::get().replyReadLink(str); }) .CATCH_ERRORS()); } folly::Future Dispatcher::mknod( fuse_ino_t parent, PathComponentPiece name, mode_t mode, dev_t rdev) { FUSELL_NOT_IMPL(); } static void disp_mknod(fuse_req_t req, fuse_ino_t parent, const char* name, mode_t mode, dev_t rdev) { auto& request = RequestData::create(req); request.setRequestFuture( startRequest() .then([=, &request] { return request.getDispatcher()->mknod( parent, PathComponentPiece(name), mode, rdev); }) .then([](fuse_entry_param&& param) { RequestData::get().replyEntry(param); }) .CATCH_ERRORS()); } folly::Future Dispatcher::mkdir(fuse_ino_t, PathComponentPiece, mode_t) { FUSELL_NOT_IMPL(); } static void disp_mkdir(fuse_req_t req, fuse_ino_t parent, const char* name, mode_t mode) { auto& request = RequestData::create(req); request.setRequestFuture(startRequest() .then([=, &request] { return request.getDispatcher()->mkdir( parent, PathComponentPiece(name), mode); }) .then([](fuse_entry_param&& param) { RequestData::get().replyEntry(param); }) .CATCH_ERRORS()); } folly::Future Dispatcher::unlink(fuse_ino_t, PathComponentPiece) { FUSELL_NOT_IMPL(); } static void disp_unlink(fuse_req_t req, fuse_ino_t parent, const char* name) { auto& request = RequestData::create(req); request.setRequestFuture(startRequest() .then([=, &request] { return request.getDispatcher()->unlink( parent, PathComponentPiece(name)); }) .then([]() { RequestData::get().replyError(0); }) .CATCH_ERRORS()); } folly::Future Dispatcher::rmdir(fuse_ino_t, PathComponentPiece) { FUSELL_NOT_IMPL(); } static void disp_rmdir(fuse_req_t req, fuse_ino_t parent, const char* name) { auto& request = RequestData::create(req); request.setRequestFuture(startRequest() .then([=, &request] { return request.getDispatcher()->rmdir( parent, PathComponentPiece(name)); }) .then([]() { RequestData::get().replyError(0); }) .CATCH_ERRORS()); } folly::Future Dispatcher::symlink(PathComponentPiece, fuse_ino_t, PathComponentPiece) { FUSELL_NOT_IMPL(); } static void disp_symlink(fuse_req_t req, const char* link, fuse_ino_t parent, const char* name) { auto& request = RequestData::create(req); request.setRequestFuture( startRequest() .then([=, &request] { return request.getDispatcher()->symlink( PathComponentPiece(link), parent, PathComponentPiece(name)); }) .then([](fuse_entry_param&& param) { RequestData::get().replyEntry(param); }) .CATCH_ERRORS()); } folly::Future Dispatcher::rename( fuse_ino_t, PathComponentPiece, fuse_ino_t, PathComponentPiece) { FUSELL_NOT_IMPL(); } static void disp_rename(fuse_req_t req, fuse_ino_t parent, const char* name, fuse_ino_t newparent, const char* newname) { auto& request = RequestData::create(req); request.setRequestFuture(startRequest() .then([=, &request] { return request.getDispatcher()->rename( parent, PathComponentPiece(name), newparent, PathComponentPiece(newname)); }) .then([]() { RequestData::get().replyError(0); }) .CATCH_ERRORS()); } folly::Future Dispatcher::link(fuse_ino_t, fuse_ino_t, PathComponentPiece) { FUSELL_NOT_IMPL(); } static void disp_link(fuse_req_t req, fuse_ino_t ino, fuse_ino_t newparent, const char* newname) { auto& request = RequestData::create(req); request.setRequestFuture( startRequest() .then([=, &request] { return request.getDispatcher()->link( ino, newparent, PathComponentPiece(newname)); }) .then([](fuse_entry_param&& param) { RequestData::get().replyEntry(param); }) .CATCH_ERRORS()); } folly::Future> Dispatcher::open( fuse_ino_t ino, const struct fuse_file_info& fi) { FUSELL_NOT_IMPL(); } static void disp_open(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info* fi) { auto& request = RequestData::create(req); request.setRequestFuture( startRequest() .then([ =, &request, fi = *fi ] { return request.getDispatcher()->open(ino, fi); }) .then([ req, orig_info = *fi ](std::unique_ptr fh) { if (!fh) { throw std::runtime_error("Dispatcher::open failed to set fh"); } fuse_file_info fi = orig_info; fi.fh = reinterpret_cast(fh.get()); fi.direct_io = fh->usesDirectIO(); fi.keep_cache = fh->preserveCache(); #if FUSE_MINOR_VERSION >= 8 fi.nonseekable = !fh->isSeekable(); #endif if (!RequestData::get().replyOpen(fi)) { // Was interrupted, tidy up. fh->releasefile().then([fh = std::move(fh)]() mutable { // This reset() is going to happen when fh falls out of scope, // we're just being clear that we want to extend the lifetime // until after the FileHandle::releasefile has finished. fh.reset(); }); } else { // The kernel now owns the pointer and it is no longer tracked // as a unique_ptr reference. fh.release(); } }) .CATCH_ERRORS()); } static void disp_read(fuse_req_t req, fuse_ino_t ino, size_t size, off_t off, struct fuse_file_info* fi) { auto& request = RequestData::create(req); request.setRequestFuture(startRequest() .then([ =, &request, fi = *fi ] { auto fh = getFileHandle(fi.fh); return fh->read(size, off); }) .then([](BufVec&& buf) { auto iov = buf.getIov(); RequestData::get().replyIov(iov.data(), iov.size()); }) .CATCH_ERRORS()); } static void disp_write(fuse_req_t req, fuse_ino_t ino, const char* buf, size_t size, off_t off, struct fuse_file_info* fi) { auto& request = RequestData::create(req); request.setRequestFuture( startRequest() .then([ =, &request, fi = *fi ] { auto fh = getFileHandle(fi.fh); return fh->write(folly::StringPiece(buf, size), off); }) .then([](size_t wrote) { RequestData::get().replyWrite(wrote); }) .CATCH_ERRORS()); } static void disp_flush(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info* fi) { auto& request = RequestData::create(req); request.setRequestFuture(startRequest() .then([ =, &request, fi = *fi ] { auto fh = getFileHandle(fi.fh); return fh->flush(fi.lock_owner); }) .then([]() { RequestData::get().replyError(0); }) .CATCH_ERRORS()); } static void disp_release(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info* fi) { auto& request = RequestData::create(req); request.setRequestFuture(startRequest() .then([ =, &request, fi = *fi ] { auto fh = getFileHandle(fi.fh); return fh->releasefile().then([fh]() { delete fh; RequestData::get().replyError(0); }); }) .CATCH_ERRORS()); } static void disp_fsync(fuse_req_t req, fuse_ino_t ino, int datasync, struct fuse_file_info* fi) { auto& request = RequestData::create(req); request.setRequestFuture(startRequest() .then([ =, &request, fi = *fi ] { auto fh = getFileHandle(fi.fh); return fh->fsync(datasync); }) .then([]() { RequestData::get().replyError(0); }) .CATCH_ERRORS()); } folly::Future> Dispatcher::opendir( fuse_ino_t ino, const struct fuse_file_info& fi) { FUSELL_NOT_IMPL(); } static void disp_opendir(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info* fi) { auto& request = RequestData::create(req); request.setRequestFuture( startRequest() .then([ =, &request, fi = *fi ] { return request.getDispatcher()->opendir(ino, fi); }) .then([orig_info = *fi](std::unique_ptr dh) { if (!dh) { throw std::runtime_error("Dispatcher::opendir failed to set dh"); } fuse_file_info fi = orig_info; fi.fh = reinterpret_cast(dh.get()); if (!RequestData::get().replyOpen(fi)) { // Was interrupted, tidy up dh->releasedir().then([dh = std::move(dh)]() mutable { // This reset() is going to happen when dh falls out of scope, // we're just being clear that we want to extend the lifetime // until after the DirHandle::release() has finished. dh.reset(); }); } else { // The kernel now owns the pointer and it is no longer tracked // as a unique_ptr reference. dh.release(); } }) .CATCH_ERRORS()); } static void disp_readdir(fuse_req_t req, fuse_ino_t ino, size_t size, off_t off, struct fuse_file_info* fi) { auto& request = RequestData::create(req); request.setRequestFuture(startRequest() .then([ =, &request, fi = *fi ] { auto dh = getDirHandle(fi.fh); return dh->readdir(DirList(size), off); }) .then([](DirList&& list) { auto buf = list.getBuf(); RequestData::get().replyBuf(buf.data(), buf.size()); }) .CATCH_ERRORS()); } static void disp_releasedir(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info* fi) { auto& request = RequestData::create(req); request.setRequestFuture(startRequest() .then([ =, &request, fi = *fi ] { auto dh = getDirHandle(fi.fh); return dh->releasedir().then([dh]() { delete dh; RequestData::get().replyError(0); }); }) .CATCH_ERRORS()); } static void disp_fsyncdir(fuse_req_t req, fuse_ino_t ino, int datasync, struct fuse_file_info* fi) { auto& request = RequestData::create(req); request.setRequestFuture(startRequest() .then([ =, &request, fi = *fi ] { auto dh = getDirHandle(fi.fh); return dh->fsyncdir(datasync); }) .then([]() { RequestData::get().replyError(0); }) .CATCH_ERRORS()); } folly::Future Dispatcher::statfs(fuse_ino_t ino) { struct statvfs info; memset(&info, 0, sizeof(info)); // Suggest a large blocksize to software that looks at that kind of thing info.f_bsize = getConnInfo().max_readahead; return info; } static void disp_statfs(fuse_req_t req, fuse_ino_t ino) { auto& request = RequestData::create(req); request.setRequestFuture( startRequest() .then([=, &request] { return request.getDispatcher()->statfs(ino); }) .then([](struct statvfs&& info) { RequestData::get().replyStatfs(info); }) .CATCH_ERRORS()); } folly::Future Dispatcher::setxattr(fuse_ino_t ino, folly::StringPiece name, folly::StringPiece value, int flags) { FUSELL_NOT_IMPL(); } static void disp_setxattr(fuse_req_t req, fuse_ino_t ino, const char* name, const char* value, size_t size, int flags #ifdef __APPLE__ , uint32_t position #endif ) { auto& request = RequestData::create(req); #ifdef __APPLE__ if (position != 0) { request.replyError(EINVAL); return; } #endif request.setRequestFuture( startRequest() .then([=, &request] { return request.getDispatcher()->setxattr( ino, name, folly::StringPiece(value, size), flags); }) .then([]() { RequestData::get().replyError(0); }) .CATCH_ERRORS()); } const int Dispatcher::kENOATTR = #ifndef ENOATTR ENODATA // Linux #else ENOATTR #endif ; folly::Future Dispatcher::getxattr(fuse_ino_t ino, folly::StringPiece name) { throwSystemErrorExplicit(kENOATTR); } static void disp_getxattr(fuse_req_t req, fuse_ino_t ino, const char* name, size_t size #ifdef __APPLE__ , uint32_t position #endif ) { auto& request = RequestData::create(req); #ifdef __APPLE__ if (position != 0) { request.replyError(EINVAL); return; } #endif request.setRequestFuture(startRequest() .then([=, &request] { return request.getDispatcher()->getxattr(ino, name); }) .then([size](std::string attr) { auto& request = RequestData::get(); if (size == 0) { request.replyXattr(attr.size()); } else if (size < attr.size()) { request.replyError(ERANGE); } else { request.replyBuf(attr.data(), attr.size()); } }) .CATCH_ERRORS()); } folly::Future> Dispatcher::listxattr(fuse_ino_t ino) { return std::vector(); } static void disp_listxattr(fuse_req_t req, fuse_ino_t ino, size_t size) { auto& request = RequestData::create(req); request.setRequestFuture(startRequest() .then([=, &request] { return request.getDispatcher()->listxattr(ino); }) .then([size](std::vector&& attrs) { auto& request = RequestData::get(); // Initialize count to include the \0 for each // entry. size_t count = attrs.size(); for (auto& attr : attrs) { count += attr.size(); } if (size == 0) { request.replyXattr(count); } else if (size < count) { request.replyError(ERANGE); } else { std::string buf; folly::join('\0', attrs, buf); buf.push_back('\0'); DCHECK(count == buf.size()); request.replyBuf(buf.data(), count); } }) .CATCH_ERRORS()); } folly::Future Dispatcher::removexattr(fuse_ino_t ino, folly::StringPiece name) { FUSELL_NOT_IMPL(); } static void disp_removexattr(fuse_req_t req, fuse_ino_t ino, const char* name) { auto& request = RequestData::create(req); request.setRequestFuture(startRequest() .then([=, &request] { return request.getDispatcher()->removexattr( ino, name); }) .then([]() { RequestData::get().replyError(0); }) .CATCH_ERRORS()); } folly::Future Dispatcher::access(fuse_ino_t ino, int mask) { FUSELL_NOT_IMPL(); } static void disp_access(fuse_req_t req, fuse_ino_t ino, int mask) { auto& request = RequestData::create(req); request.setRequestFuture(startRequest() .then([=, &request] { return request.getDispatcher()->access(ino, mask); }) .then([]() { RequestData::get().replyError(0); }) .CATCH_ERRORS()); } folly::Future Dispatcher::create(fuse_ino_t, PathComponentPiece, mode_t, int) { FUSELL_NOT_IMPL(); } static void disp_create(fuse_req_t req, fuse_ino_t parent, const char* name, mode_t mode, struct fuse_file_info* fi) { auto& request = RequestData::create(req); request.setRequestFuture( startRequest() .then([ =, &request, fi = *fi ] { return request.getDispatcher()->create( parent, PathComponentPiece(name), mode, fi.flags); }) .then([orig_info = *fi](Dispatcher::Create info) { fuse_file_info fi = orig_info; fi.fh = reinterpret_cast(info.fh.get()); fi.direct_io = info.fh->usesDirectIO(); fi.keep_cache = info.fh->preserveCache(); #if FUSE_MINOR_VERSION >= 8 fi.nonseekable = !info.fh->isSeekable(); #endif if (!RequestData::get().replyCreate(info.entry, fi)) { // Interrupted, tidy up info.fh->releasefile().then([fh = std::move(info.fh)]() mutable { // This reset() is going to happen when fh falls out of scope, // we're just being clear that we want to extend the lifetime // until after the FileHandle::releasefile has finished. fh.reset(); }); } else { // The kernel now owns the file handle and it is no longer // tracked as a unique_ptr reference. info.fh.release(); } }) .CATCH_ERRORS()); } static void disp_getlk(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info* fi, struct flock* lock) { auto& request = RequestData::create(req); request.setRequestFuture( startRequest() .then([ =, &request, fi = *fi ] { auto fh = getFileHandle(fi.fh); return fh->getlk(*lock, fi.lock_owner); }) .then([](struct flock lock) { RequestData::get().replyLock(lock); }) .CATCH_ERRORS()); } static void disp_setlk(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info* fi, struct flock* lock, int sleep) { auto& request = RequestData::create(req); request.setRequestFuture(startRequest() .then([ =, &request, fi = *fi ] { auto fh = getFileHandle(fi.fh); return fh->setlk(*lock, sleep, fi.lock_owner); }) .then([]() { RequestData::get().replyError(0); }) .CATCH_ERRORS()); } folly::Future Dispatcher::bmap(fuse_ino_t ino, size_t blocksize, uint64_t idx) { FUSELL_NOT_IMPL(); } static void disp_bmap(fuse_req_t req, fuse_ino_t ino, size_t blocksize, uint64_t idx) { auto& request = RequestData::create(req); request.setRequestFuture( startRequest() .then([=, &request] { return request.getDispatcher()->bmap(ino, blocksize, idx); }) .then([](uint64_t idx) { RequestData::get().replyBmap(idx); }) .CATCH_ERRORS()); } #if FUSE_MINOR_VERSION >= 8 static void disp_ioctl(fuse_req_t req, fuse_ino_t ino, int cmd, void* arg, struct fuse_file_info* fi, unsigned flags, const void* in_buf, size_t in_bufsz, size_t out_bufsz) { auto& request = RequestData::create(req); if (flags & FUSE_IOCTL_UNRESTRICTED) { // We only support restricted ioctls request.replyError(-EPERM); return; } request.setRequestFuture( startRequest() .then([ =, &request, fi = *fi ] { auto fh = getGenericFileHandle(fi.fh); return fh->ioctl(cmd, arg, folly::ByteRange((uint8_t*)in_buf, in_bufsz), out_bufsz); }) .then([](FileHandleBase::Ioctl&& result) { auto iov = result.buf.getIov(); RequestData::get().replyIoctl( result.result, iov.data(), iov.size()); }) .CATCH_ERRORS()); } #endif #if FUSE_MINOR_VERSION >= 8 static void disp_poll(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info* fi, struct fuse_pollhandle* ph) { auto& request = RequestData::create(req); request.setRequestFuture( startRequest() .then([ =, &request, fi = *fi ] { auto fh = getGenericFileHandle(fi.fh); std::unique_ptr poll_handle; if (ph) { poll_handle = std::make_unique(ph); } return fh->poll(std::move(poll_handle)); }) .then([](unsigned revents) { RequestData::get().replyPoll(revents); }) .CATCH_ERRORS()); } #endif static const fuse_lowlevel_ops dispatcher_ops = { .init = Dispatcher::disp_init, .destroy = disp_destroy, .lookup = disp_lookup, .forget = disp_forget, .getattr = disp_getattr, .setattr = disp_setattr, .readlink = disp_readlink, .mknod = disp_mknod, .mkdir = disp_mkdir, .unlink = disp_unlink, .rmdir = disp_rmdir, .symlink = disp_symlink, .rename = disp_rename, .link = disp_link, .open = disp_open, .read = disp_read, .write = disp_write, .flush = disp_flush, .release = disp_release, .fsync = disp_fsync, .opendir = disp_opendir, .readdir = disp_readdir, .releasedir = disp_releasedir, .fsyncdir = disp_fsyncdir, .statfs = disp_statfs, .setxattr = disp_setxattr, .getxattr = disp_getxattr, .listxattr = disp_listxattr, .removexattr = disp_removexattr, .access = disp_access, .create = disp_create, .getlk = disp_getlk, .setlk = disp_setlk, .bmap = disp_bmap, #if FUSE_MINOR_VERSION >= 8 .ioctl = disp_ioctl, .poll = disp_poll, #endif #if FUSE_MINOR_VERSION >= 9 .forget_multi = disp_forget_multi, #endif }; const fuse_conn_info& Dispatcher::getConnInfo() const { return connInfo_; } Channel& Dispatcher::getChannel() const { DCHECK(chan_ != nullptr) << "Channel not yet assigned!?"; return *chan_; } std::unique_ptr Dispatcher::makeSession( Channel& channel, bool debug) { chan_ = &channel; fuse_args fargs{0, nullptr, 0}; std::vector fuseArgs = { "fuse", "-o", "allow_root", }; if (debug) { fuseArgs.push_back("-d"); } fargs.argc = fuseArgs.size(); fargs.argv = const_cast(fuseArgs.data()); auto sess = fuse_lowlevel_new(&fargs, &dispatcher_ops, sizeof(dispatcher_ops), this); if (!sess) { throw std::runtime_error("failed to create session"); } return std::unique_ptr(sess, SessionDeleter(chan_)); } } } }