sapling/eden/fs/fuse/Dispatcher.cpp

950 lines
31 KiB
C++
Raw Normal View History

/*
* Copyright (c) 2016-present, 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 "eden/fs/fuse/Dispatcher.h"
#include <folly/Exception.h>
#include <folly/Format.h>
#include <folly/MoveWrapper.h>
#include <folly/experimental/logging/xlog.h>
#include <wangle/concurrent/GlobalExecutor.h>
#include "eden/fs/fuse/DirHandle.h"
#include "eden/fs/fuse/FileHandle.h"
#include "eden/fs/fuse/RequestData.h"
using namespace folly;
using namespace std::chrono;
namespace facebook {
namespace eden {
namespace fusell {
Dispatcher::Attr::Attr(const struct stat& st, double timeout)
: st(st), timeout(timeout) {}
Dispatcher::~Dispatcher() {}
Dispatcher::Dispatcher(ThreadLocalEdenStats* stats) : stats_(stats) {}
void Dispatcher::initConnection(fuse_conn_info& /*conn*/) {}
FileHandleMap& Dispatcher::getFileHandles() {
return fileHandles_;
}
std::shared_ptr<FileHandleBase> Dispatcher::getGenericFileHandle(uint64_t fh) {
return fileHandles_.getGenericFileHandle(fh);
}
std::shared_ptr<FileHandle> Dispatcher::getFileHandle(uint64_t fh) {
return fileHandles_.getFileHandle(fh);
}
std::shared_ptr<DirHandle> Dispatcher::getDirHandle(uint64_t dh) {
return fileHandles_.getDirHandle(dh);
}
static std::string flagsToLabel(
const std::unordered_map<int32_t, const char*>& labels, uint32_t flags) {
std::vector<const char*> 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<int32_t, const char*> 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<Dispatcher*>(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;
XLOG(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<Dispatcher*>(userdata)->destroy();
}
folly::Future<fuse_entry_param> Dispatcher::lookup(
fuse_ino_t /*parent*/,
PathComponentPiece /*name*/) {
throwSystemErrorExplicit(ENOENT);
}
static void disp_lookup(fuse_req_t req, fuse_ino_t parent, const char* name) {
auto& request = RequestData::create(req);
auto* dispatcher = request.getDispatcher();
request.setRequestFuture(
request.startRequest(dispatcher->getStats(), &EdenStats::lookup)
.then([=, &request] {
return dispatcher->lookup(parent, PathComponentPiece(name));
})
.then([](fuse_entry_param&& param) {
RequestData::get().replyEntry(param);
}));
}
folly::Future<folly::Unit> 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);
auto* dispatcher = request.getDispatcher();
request.catchErrors(
request.startRequest(dispatcher->getStats(), &EdenStats::forget)
.then([=, &request] { return dispatcher->forget(ino, nlookup); })
.then([]() { RequestData::get().replyNone(); }));
}
#if FUSE_MAJOR_VERSION == 2 && 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<fuse_forget_data> forget(forgets, forgets + count);
auto* dispatcher = request.getDispatcher();
request.catchErrors(
request.startRequest(dispatcher->getStats(), &EdenStats::forgetmulti)
.then([ =, &request, forget = std::move(forget) ] {
for (auto& f : forget) {
dispatcher->forget(f.ino, f.nlookup);
}
return Unit{};
})
.then([]() { RequestData::get().replyNone(); }));
}
#endif
folly::Future<Dispatcher::Attr> 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);
auto* dispatcher = request.getDispatcher();
if (fi) {
request.setRequestFuture(
request.startRequest(dispatcher->getStats(), &EdenStats::getattr)
.then([ =, &request, fi = *fi ] {
return dispatcher->getGenericFileHandle(fi.fh)->getattr();
})
.then([](Dispatcher::Attr&& attr) {
RequestData::get().replyAttr(attr.st, attr.timeout);
}));
} else {
request.setRequestFuture(
request.startRequest(dispatcher->getStats(), &EdenStats::getattr)
.then([=, &request] { return dispatcher->getattr(ino); })
.then([](Dispatcher::Attr&& attr) {
RequestData::get().replyAttr(attr.st, attr.timeout);
}));
}
}
folly::Future<Dispatcher::Attr> 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);
auto* dispatcher = request.getDispatcher();
if (fi) {
request.setRequestFuture(
request.startRequest(dispatcher->getStats(), &EdenStats::setattr)
.then([ =, &request, fi = *fi ]() {
return dispatcher->getGenericFileHandle(fi.fh)->setattr(
*attr, to_set);
})
.then([](Dispatcher::Attr&& attr) {
RequestData::get().replyAttr(attr.st, attr.timeout);
}));
} else {
request.setRequestFuture(
request.startRequest(dispatcher->getStats(), &EdenStats::setattr)
.then([=, &request]() {
return dispatcher->setattr(ino, *attr, to_set);
})
.then([](Dispatcher::Attr&& attr) {
RequestData::get().replyAttr(attr.st, attr.timeout);
}));
}
}
folly::Future<std::string> 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);
auto* dispatcher = request.getDispatcher();
request.setRequestFuture(
request.startRequest(dispatcher->getStats(), &EdenStats::readlink)
.then([=, &request] { return dispatcher->readlink(ino); })
.then([](std::string&& str) {
RequestData::get().replyReadLink(str);
}));
}
folly::Future<fuse_entry_param> 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);
auto* dispatcher = request.getDispatcher();
request.setRequestFuture(
request.startRequest(dispatcher->getStats(), &EdenStats::mknod)
.then([=, &request] {
return dispatcher->mknod(
parent, PathComponentPiece(name), mode, rdev);
})
.then([](fuse_entry_param&& param) {
RequestData::get().replyEntry(param);
}));
}
folly::Future<fuse_entry_param>
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);
auto* dispatcher = request.getDispatcher();
request.setRequestFuture(
request.startRequest(dispatcher->getStats(), &EdenStats::mkdir)
.then([=, &request] {
return dispatcher->mkdir(parent, PathComponentPiece(name), mode);
})
.then([](fuse_entry_param&& param) {
RequestData::get().replyEntry(param);
}));
}
folly::Future<folly::Unit> 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);
auto* dispatcher = request.getDispatcher();
request.setRequestFuture(
request.startRequest(dispatcher->getStats(), &EdenStats::unlink)
.then([=, &request] {
return dispatcher->unlink(parent, PathComponentPiece(name));
})
.then([]() { RequestData::get().replyError(0); }));
}
folly::Future<folly::Unit> 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);
auto* dispatcher = request.getDispatcher();
request.setRequestFuture(
request.startRequest(dispatcher->getStats(), &EdenStats::rmdir)
.then([=, &request] {
return dispatcher->rmdir(parent, PathComponentPiece(name));
})
.then([]() { RequestData::get().replyError(0); }));
}
folly::Future<fuse_entry_param>
Dispatcher::symlink(fuse_ino_t, PathComponentPiece, folly::StringPiece) {
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);
auto* dispatcher = request.getDispatcher();
request.setRequestFuture(
request.startRequest(dispatcher->getStats(), &EdenStats::symlink)
.then([=, &request] {
return dispatcher->symlink(parent, PathComponentPiece(name), link);
})
.then([](fuse_entry_param&& param) {
RequestData::get().replyEntry(param);
}));
}
folly::Future<folly::Unit> 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);
auto* dispatcher = request.getDispatcher();
request.setRequestFuture(request
.startRequest(
dispatcher->getStats(), &EdenStats::rename)
.then([=, &request] {
return dispatcher->rename(
parent,
PathComponentPiece(name),
newparent,
PathComponentPiece(newname));
})
.then(
[]() { RequestData::get().replyError(0); }));
}
folly::Future<fuse_entry_param>
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);
auto* dispatcher = request.getDispatcher();
request.setRequestFuture(
request.startRequest(dispatcher->getStats(), &EdenStats::link)
.then([=, &request] {
return dispatcher->link(
ino, newparent, PathComponentPiece(newname));
})
.then([](fuse_entry_param&& param) {
RequestData::get().replyEntry(param);
}));
}
folly::Future<std::shared_ptr<FileHandle>> 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);
auto* dispatcher = request.getDispatcher();
request.setRequestFuture(
request.startRequest(dispatcher->getStats(), &EdenStats::open)
.then([ =, &request, fi = *fi ] { return dispatcher->open(ino, fi); })
.then([ req, dispatcher, orig_info = *fi ](
std::shared_ptr<FileHandle> fh) {
if (!fh) {
throw std::runtime_error("Dispatcher::open failed to set fh");
}
fuse_file_info fi = orig_info;
fi.direct_io = fh->usesDirectIO();
fi.keep_cache = fh->preserveCache();
#if FUSE_MINOR_VERSION >= 8
fi.nonseekable = !fh->isSeekable();
#endif
fi.fh = dispatcher->getFileHandles().recordHandle(std::move(fh));
if (!RequestData::get().replyOpen(fi)) {
// Was interrupted, tidy up.
dispatcher->getFileHandles().forgetGenericHandle(fi.fh);
}
}));
}
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);
auto* dispatcher = request.getDispatcher();
request.setRequestFuture(
request.startRequest(dispatcher->getStats(), &EdenStats::read)
.then([ =, &request, fi = *fi ] {
auto fh = dispatcher->getFileHandle(fi.fh);
return fh->read(size, off);
})
.then([](BufVec&& buf) {
auto iov = buf.getIov();
RequestData::get().replyIov(iov.data(), iov.size());
}));
}
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);
auto* dispatcher = request.getDispatcher();
request.setRequestFuture(
request.startRequest(dispatcher->getStats(), &EdenStats::write)
.then([ =, &request, fi = *fi ] {
auto fh = dispatcher->getFileHandle(fi.fh);
return fh->write(folly::StringPiece(buf, size), off);
})
.then([](size_t wrote) { RequestData::get().replyWrite(wrote); }));
}
static void
disp_flush(fuse_req_t req, fuse_ino_t /*ino*/, struct fuse_file_info* fi) {
auto& request = RequestData::create(req);
auto* dispatcher = request.getDispatcher();
request.setRequestFuture(
request.startRequest(dispatcher->getStats(), &EdenStats::flush)
.then([ =, &request, fi = *fi ] {
auto fh = dispatcher->getFileHandle(fi.fh);
return fh->flush(fi.lock_owner);
})
.then([]() { RequestData::get().replyError(0); }));
}
static void
disp_release(fuse_req_t req, fuse_ino_t /*ino*/, struct fuse_file_info* fi) {
auto& request = RequestData::create(req);
auto* dispatcher = request.getDispatcher();
request.setRequestFuture(
request.startRequest(dispatcher->getStats(), &EdenStats::release)
.then([ =, &request, fi = *fi ] {
dispatcher->getFileHandles().forgetGenericHandle(fi.fh);
RequestData::get().replyError(0);
}));
}
static void disp_fsync(
fuse_req_t req,
fuse_ino_t /*ino*/,
int datasync,
struct fuse_file_info* fi) {
auto& request = RequestData::create(req);
auto* dispatcher = request.getDispatcher();
request.setRequestFuture(
request.startRequest(dispatcher->getStats(), &EdenStats::fsync)
.then([ =, &request, fi = *fi ] {
auto fh = dispatcher->getFileHandle(fi.fh);
return fh->fsync(datasync);
})
.then([]() { RequestData::get().replyError(0); }));
}
folly::Future<std::shared_ptr<DirHandle>> 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);
auto* dispatcher = request.getDispatcher();
request.setRequestFuture(
request.startRequest(dispatcher->getStats(), &EdenStats::opendir)
.then([ =, &request, fi = *fi ] {
return dispatcher->opendir(ino, fi);
})
.then([ dispatcher, orig_info = *fi ](std::shared_ptr<DirHandle> dh) {
if (!dh) {
throw std::runtime_error("Dispatcher::opendir failed to set dh");
}
fuse_file_info fi = orig_info;
fi.fh = dispatcher->getFileHandles().recordHandle(std::move(dh));
if (!RequestData::get().replyOpen(fi)) {
// Was interrupted, tidy up
dispatcher->getFileHandles().forgetGenericHandle(fi.fh);
}
}));
}
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);
auto* dispatcher = request.getDispatcher();
request.setRequestFuture(
request.startRequest(dispatcher->getStats(), &EdenStats::readdir)
.then([ =, &request, fi = *fi ] {
auto dh = dispatcher->getDirHandle(fi.fh);
return dh->readdir(DirList(size), off);
})
.then([](DirList&& list) {
auto buf = list.getBuf();
RequestData::get().replyBuf(buf.data(), buf.size());
}));
}
static void
disp_releasedir(fuse_req_t req, fuse_ino_t /*ino*/, struct fuse_file_info* fi) {
auto& request = RequestData::create(req);
auto* dispatcher = request.getDispatcher();
request.setRequestFuture(
request.startRequest(dispatcher->getStats(), &EdenStats::releasedir)
.then([ =, &request, fi = *fi ] {
dispatcher->getFileHandles().forgetGenericHandle(fi.fh);
RequestData::get().replyError(0);
}));
}
static void disp_fsyncdir(
fuse_req_t req,
fuse_ino_t /*ino*/,
int datasync,
struct fuse_file_info* fi) {
auto& request = RequestData::create(req);
auto* dispatcher = request.getDispatcher();
request.setRequestFuture(
request.startRequest(dispatcher->getStats(), &EdenStats::fsyncdir)
.then([ =, &request, fi = *fi ] {
auto dh = dispatcher->getDirHandle(fi.fh);
return dh->fsyncdir(datasync);
})
.then([]() { RequestData::get().replyError(0); }));
}
folly::Future<struct statvfs> 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);
auto* dispatcher = request.getDispatcher();
request.setRequestFuture(
request.startRequest(dispatcher->getStats(), &EdenStats::statfs)
.then([=, &request] { return dispatcher->statfs(ino); })
.then([](struct statvfs&& info) {
RequestData::get().replyStatfs(info);
}));
}
folly::Future<folly::Unit> 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);
auto* dispatcher = request.getDispatcher();
#ifdef __APPLE__
if (position != 0) {
request.replyError(EINVAL);
return;
}
#endif
request.setRequestFuture(
request.startRequest(dispatcher->getStats(), &EdenStats::setxattr)
.then([=, &request] {
return dispatcher->setxattr(
ino, name, folly::StringPiece(value, size), flags);
})
.then([]() { RequestData::get().replyError(0); }));
}
const int Dispatcher::kENOATTR =
#ifndef ENOATTR
ENODATA // Linux
#else
ENOATTR
#endif
;
folly::Future<std::string> 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);
auto* dispatcher = request.getDispatcher();
#ifdef __APPLE__
if (position != 0) {
request.replyError(EINVAL);
return;
}
#endif
request.setRequestFuture(
request.startRequest(dispatcher->getStats(), &EdenStats::getxattr)
.then([=, &request] { return dispatcher->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());
}
}));
}
folly::Future<std::vector<std::string>> Dispatcher::listxattr(
fuse_ino_t /*ino*/) {
return std::vector<std::string>();
}
static void disp_listxattr(fuse_req_t req, fuse_ino_t ino, size_t size) {
auto& request = RequestData::create(req);
auto* dispatcher = request.getDispatcher();
request.setRequestFuture(
request.startRequest(dispatcher->getStats(), &EdenStats::listxattr)
.then([=, &request] { return dispatcher->listxattr(ino); })
.then([size](std::vector<std::string>&& 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);
}
}));
}
folly::Future<folly::Unit> 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);
auto* dispatcher = request.getDispatcher();
request.setRequestFuture(
request.startRequest(dispatcher->getStats(), &EdenStats::removexattr)
.then([=, &request] { return dispatcher->removexattr(ino, name); })
.then([]() { RequestData::get().replyError(0); }));
}
folly::Future<folly::Unit> Dispatcher::access(
fuse_ino_t /*ino*/,
int /*mask*/) {
// Note that if you mount with the "default_permissions" kernel mount option,
// the kernel will perform all permissions checks for you, and will never
// invoke access() directly.
//
// Implementing access() is only needed when not using the
// "default_permissions" option.
FUSELL_NOT_IMPL();
}
static void disp_access(fuse_req_t req, fuse_ino_t ino, int mask) {
auto& request = RequestData::create(req);
auto* dispatcher = request.getDispatcher();
request.setRequestFuture(
request.startRequest(dispatcher->getStats(), &EdenStats::access)
.then([=, &request] { return dispatcher->access(ino, mask); })
.then([]() { RequestData::get().replyError(0); }));
}
folly::Future<Dispatcher::Create>
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);
auto* dispatcher = request.getDispatcher();
request.setRequestFuture(
request.startRequest(dispatcher->getStats(), &EdenStats::create)
.then([ =, &request, fi = *fi ] {
return dispatcher->create(
parent, PathComponentPiece(name), mode, fi.flags);
})
.then([ dispatcher, orig_info = *fi ](Dispatcher::Create info) {
fuse_file_info fi = orig_info;
fi.direct_io = info.fh->usesDirectIO();
fi.keep_cache = info.fh->preserveCache();
#if FUSE_MINOR_VERSION >= 8
fi.nonseekable = !info.fh->isSeekable();
#endif
fi.fh =
dispatcher->getFileHandles().recordHandle(std::move(info.fh));
if (!RequestData::get().replyCreate(info.entry, fi)) {
// Interrupted, tidy up
dispatcher->getFileHandles().forgetGenericHandle(fi.fh);
}
}));
}
folly::Future<uint64_t>
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);
auto* dispatcher = request.getDispatcher();
request.setRequestFuture(
request.startRequest(dispatcher->getStats(), &EdenStats::bmap)
.then([=, &request] { return dispatcher->bmap(ino, blocksize, idx); })
.then([](uint64_t resultIdx) {
RequestData::get().replyBmap(resultIdx);
}));
}
#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);
auto* dispatcher = request.getDispatcher();
if (flags & FUSE_IOCTL_UNRESTRICTED) {
// We only support restricted ioctls
request.replyError(-EPERM);
return;
}
request.setRequestFuture(
request.startRequest(dispatcher->getStats(), &EdenStats::ioctl)
.then([ =, &request, fi = *fi ] {
auto fh = dispatcher->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());
}));
}
#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);
auto* dispatcher = request.getDispatcher();
request.setRequestFuture(
request.startRequest(dispatcher->getStats(), &EdenStats::poll)
.then([ =, &request, fi = *fi ] {
auto fh = dispatcher->getGenericFileHandle(fi.fh);
std::unique_ptr<PollHandle> poll_handle;
if (ph) {
poll_handle = std::make_unique<PollHandle>(ph);
}
return fh->poll(std::move(poll_handle));
})
.then(
[](unsigned revents) { RequestData::get().replyPoll(revents); }));
}
#endif
re-organize the fuse Channel and Session code Summary: The higher level goal is to make it easier to deal with the graceful restart scenario. This diff removes the SessionDeleter class and effectively renames the Channel class to FuseChannel. The FuseChannel represents the channel to the kernel; it can be constructed from a fuse device descriptor that has been obtained either from the privhelper at mount time, or from the graceful restart procedure. Importantly for graceful restart, it is possible to move the fuse device descriptor out of the FuseChannel so that it can be transferred to a new eden process. The graceful restart procedure requires a bit more control over the lifecycle of the fuse event loop so this diff also takes over managing the thread pool for the worker threads. The threads are owned by the MountPoint class which continues to be responsible for starting and stopping the fuse session and notifying EdenServer when it has finished. A nice side effect of this change is that we can remove a couple of inelegant aspects of the integration; the stack size env var stuff and the redundant extra thread to wait for the loop to finish. I opted to expose the dispatcher ops struct via an `extern` to simplify the code in the MountPoint class and avoid adding special interfaces for passing the ops around; they're constant anyway so this doesn't feel especially egregious. Reviewed By: bolinfest Differential Revision: D5751521 fbshipit-source-id: 5ba4fff48f3efb31a809adfc7787555711f649c9
2017-09-09 05:15:17 +03:00
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,
// Leave it to the kernel to handle locking
.getlk = nullptr,
.setlk = nullptr,
.bmap = disp_bmap,
#if FUSE_MAJOR_VERSION == 2 && FUSE_MINOR_VERSION >= 8
.ioctl = disp_ioctl,
.poll = disp_poll,
#endif
#if FUSE_MAJOR_VERSION == 2 && FUSE_MINOR_VERSION >= 9
.write_buf = nullptr,
.retrieve_reply = nullptr,
.forget_multi = disp_forget_multi,
.flock = nullptr,
.fallocate = nullptr,
#endif
};
const fuse_conn_info& Dispatcher::getConnInfo() const { return connInfo_; }
ThreadLocalEdenStats* Dispatcher::getStats() const {
return stats_;
}
}
}
}