inodes: move dispatchers around

Summary:
Instead of having one "Dispatcher" type that the various backend overload,
let's simply have a per-mount type dispatcher type. The previous model worked
fine when EdenFS supported only one way of mounting a repository, but with NFS
coming, unix platform will support both FUSE and NFS, making the Dispatcher
overload nonsensical.

As a behavioral change, the dispatcher lifetime and ownership is changed a bit.
It used to live for the duration of the EdenMount object, but is now tied to
the channel lifetime, as it is now owned by it.

Reviewed By: kmancini

Differential Revision: D26329477

fbshipit-source-id: 3959b90a4909e3ab0898caa308f54686f59a943c
This commit is contained in:
Xavier Deguillard 2021-02-10 11:46:19 -08:00 committed by Facebook GitHub Bot
parent b842d11153
commit 62076b545e
29 changed files with 1114 additions and 1022 deletions

View File

@ -16,7 +16,7 @@
#include <signal.h>
#include <type_traits>
#include "eden/fs/fuse/DirList.h"
#include "eden/fs/fuse/Dispatcher.h"
#include "eden/fs/fuse/FuseDispatcher.h"
#include "eden/fs/fuse/FuseRequestContext.h"
#include "eden/fs/utils/Bug.h"
#include "eden/fs/utils/IDGen.h"
@ -769,14 +769,14 @@ FuseChannel::FuseChannel(
folly::File&& fuseDevice,
AbsolutePathPiece mountPath,
size_t numThreads,
Dispatcher* dispatcher,
std::unique_ptr<FuseDispatcher> dispatcher,
folly::Logger* straceLogger,
std::shared_ptr<ProcessNameCache> processNameCache,
folly::Duration requestTimeout,
Notifications* notifications)
: bufferSize_(std::max(size_t(getpagesize()) + 0x1000, MIN_BUFSIZE)),
numThreads_(numThreads),
dispatcher_(dispatcher),
dispatcher_(std::move(dispatcher)),
straceLogger_(straceLogger),
mountPath_(mountPath),
requestTimeout_(requestTimeout),
@ -1437,9 +1437,9 @@ void FuseChannel::readInitPacket(bool caseSensitive) {
// to update without synchronization.
connInfo_ = connInfo;
// Send the INIT reply before informing the Dispatcher or signalling
// Send the INIT reply before informing the FuseDispatcher or signalling
// initPromise_, so that the kernel will put the mount point in use and will
// not block further filesystem access on us while running the Dispatcher
// not block further filesystem access on us while running the FuseDispatcher
// callback code.
#ifdef __linux__
static_assert(
@ -1826,7 +1826,7 @@ folly::Future<folly::Unit> FuseChannel::fuseGetAttr(
ByteRange /*arg*/) {
XLOG(DBG7) << "FUSE_GETATTR inode=" << header.nodeid;
return dispatcher_->getattr(InodeNumber{header.nodeid}, request)
.thenValue([&request](Dispatcher::Attr attr) {
.thenValue([&request](FuseDispatcher::Attr attr) {
request.sendReply(attr.asFuseAttr());
});
}
@ -1838,7 +1838,7 @@ folly::Future<folly::Unit> FuseChannel::fuseSetAttr(
const auto setattr = reinterpret_cast<const fuse_setattr_in*>(arg.data());
XLOG(DBG7) << "FUSE_SETATTR inode=" << header.nodeid;
return dispatcher_->setattr(InodeNumber{header.nodeid}, *setattr)
.thenValue([&request](Dispatcher::Attr attr) {
.thenValue([&request](FuseDispatcher::Attr attr) {
request.sendReply(attr.asFuseAttr());
});
}

View File

@ -25,6 +25,7 @@
#include <variant>
#include <vector>
#include "eden/fs/fuse/FuseDispatcher.h"
#include "eden/fs/fuse/FuseTypes.h"
#include "eden/fs/inodes/InodeNumber.h"
#include "eden/fs/telemetry/RequestMetricsScope.h"
@ -38,7 +39,6 @@ struct Unit;
namespace facebook::eden {
class Dispatcher;
class Notifications;
class FuseRequestContext;
@ -202,7 +202,7 @@ class FuseChannel {
folly::File&& fuseDevice,
AbsolutePathPiece mountPath,
size_t numThreads,
Dispatcher* const dispatcher,
std::unique_ptr<FuseDispatcher> dispatcher,
folly::Logger* straceLogger,
std::shared_ptr<ProcessNameCache> processNameCache,
folly::Duration requestTimeout = std::chrono::seconds(60),
@ -714,7 +714,7 @@ class FuseChannel {
*/
const size_t bufferSize_{0};
const size_t numThreads_;
Dispatcher* const dispatcher_;
std::unique_ptr<FuseDispatcher> dispatcher_;
folly::Logger* const straceLogger_;
const AbsolutePath mountPath_;
const folly::Duration requestTimeout_;

View File

@ -7,7 +7,7 @@
#ifndef _WIN32
#include "eden/fs/fuse/Dispatcher.h"
#include "eden/fs/fuse/FuseDispatcher.h"
#include <folly/Exception.h>
#include <folly/Format.h>
@ -23,10 +23,10 @@ using namespace folly;
namespace facebook {
namespace eden {
Dispatcher::Attr::Attr(const struct stat& st, uint64_t timeout)
FuseDispatcher::Attr::Attr(const struct stat& st, uint64_t timeout)
: st(st), timeout_seconds(timeout) {}
fuse_attr_out Dispatcher::Attr::asFuseAttr() const {
fuse_attr_out FuseDispatcher::Attr::asFuseAttr() const {
// Ensure that we initialize the members to zeroes;
// This is important on macOS where there are a couple
// of additional fields (notably `flags`) that influence
@ -55,17 +55,17 @@ fuse_attr_out Dispatcher::Attr::asFuseAttr() const {
return result;
}
Dispatcher::~Dispatcher() {}
FuseDispatcher::~FuseDispatcher() {}
Dispatcher::Dispatcher(EdenStats* stats) : stats_(stats) {}
FuseDispatcher::FuseDispatcher(EdenStats* stats) : stats_(stats) {}
void Dispatcher::initConnection(const fuse_init_out& out) {
void FuseDispatcher::initConnection(const fuse_init_out& out) {
connInfo_ = out;
}
void Dispatcher::destroy() {}
void FuseDispatcher::destroy() {}
folly::Future<fuse_entry_out> Dispatcher::lookup(
folly::Future<fuse_entry_out> FuseDispatcher::lookup(
uint64_t /*requestID*/,
InodeNumber /*parent*/,
PathComponentPiece /*name*/,
@ -73,28 +73,28 @@ folly::Future<fuse_entry_out> Dispatcher::lookup(
throwSystemErrorExplicit(ENOENT);
}
void Dispatcher::forget(InodeNumber /*ino*/, unsigned long /*nlookup*/) {}
void FuseDispatcher::forget(InodeNumber /*ino*/, unsigned long /*nlookup*/) {}
folly::Future<Dispatcher::Attr> Dispatcher::getattr(
folly::Future<FuseDispatcher::Attr> FuseDispatcher::getattr(
InodeNumber /*ino*/,
ObjectFetchContext& /*context*/) {
throwSystemErrorExplicit(ENOENT);
}
folly::Future<Dispatcher::Attr> Dispatcher::setattr(
folly::Future<FuseDispatcher::Attr> FuseDispatcher::setattr(
InodeNumber /*ino*/,
const fuse_setattr_in& /*attr*/
) {
FUSELL_NOT_IMPL();
}
folly::Future<std::string> Dispatcher::readlink(
folly::Future<std::string> FuseDispatcher::readlink(
InodeNumber /*ino*/,
bool /*kernelCachesReadlink*/) {
FUSELL_NOT_IMPL();
}
folly::Future<fuse_entry_out> Dispatcher::mknod(
folly::Future<fuse_entry_out> FuseDispatcher::mknod(
InodeNumber /*parent*/,
PathComponentPiece /*name*/,
mode_t /*mode*/,
@ -103,24 +103,28 @@ folly::Future<fuse_entry_out> Dispatcher::mknod(
}
folly::Future<fuse_entry_out>
Dispatcher::mkdir(InodeNumber, PathComponentPiece, mode_t) {
FuseDispatcher::mkdir(InodeNumber, PathComponentPiece, mode_t) {
FUSELL_NOT_IMPL();
}
folly::Future<folly::Unit> Dispatcher::unlink(InodeNumber, PathComponentPiece) {
folly::Future<folly::Unit> FuseDispatcher::unlink(
InodeNumber,
PathComponentPiece) {
FUSELL_NOT_IMPL();
}
folly::Future<folly::Unit> Dispatcher::rmdir(InodeNumber, PathComponentPiece) {
folly::Future<folly::Unit> FuseDispatcher::rmdir(
InodeNumber,
PathComponentPiece) {
FUSELL_NOT_IMPL();
}
folly::Future<fuse_entry_out>
Dispatcher::symlink(InodeNumber, PathComponentPiece, folly::StringPiece) {
FuseDispatcher::symlink(InodeNumber, PathComponentPiece, folly::StringPiece) {
FUSELL_NOT_IMPL();
}
folly::Future<folly::Unit> Dispatcher::rename(
folly::Future<folly::Unit> FuseDispatcher::rename(
InodeNumber,
PathComponentPiece,
InodeNumber,
@ -129,33 +133,35 @@ folly::Future<folly::Unit> Dispatcher::rename(
}
folly::Future<fuse_entry_out>
Dispatcher::link(InodeNumber, InodeNumber, PathComponentPiece) {
FuseDispatcher::link(InodeNumber, InodeNumber, PathComponentPiece) {
FUSELL_NOT_IMPL();
}
folly::Future<uint64_t> Dispatcher::open(InodeNumber /*ino*/, int /*flags*/) {
FUSELL_NOT_IMPL();
}
folly::Future<folly::Unit> Dispatcher::release(
InodeNumber /*ino*/,
uint64_t /*fh*/) {
FUSELL_NOT_IMPL();
}
folly::Future<uint64_t> Dispatcher::opendir(
folly::Future<uint64_t> FuseDispatcher::open(
InodeNumber /*ino*/,
int /*flags*/) {
FUSELL_NOT_IMPL();
}
folly::Future<folly::Unit> Dispatcher::releasedir(
folly::Future<folly::Unit> FuseDispatcher::release(
InodeNumber /*ino*/,
uint64_t /*fh*/) {
FUSELL_NOT_IMPL();
}
folly::Future<BufVec> Dispatcher::read(
folly::Future<uint64_t> FuseDispatcher::opendir(
InodeNumber /*ino*/,
int /*flags*/) {
FUSELL_NOT_IMPL();
}
folly::Future<folly::Unit> FuseDispatcher::releasedir(
InodeNumber /*ino*/,
uint64_t /*fh*/) {
FUSELL_NOT_IMPL();
}
folly::Future<BufVec> FuseDispatcher::read(
InodeNumber /*ino*/,
size_t /*size*/,
off_t /*off*/,
@ -163,29 +169,31 @@ folly::Future<BufVec> Dispatcher::read(
FUSELL_NOT_IMPL();
}
folly::Future<size_t>
Dispatcher::write(InodeNumber /*ino*/, StringPiece /*data*/, off_t /*off*/) {
folly::Future<size_t> FuseDispatcher::write(
InodeNumber /*ino*/,
StringPiece /*data*/,
off_t /*off*/) {
FUSELL_NOT_IMPL();
}
folly::Future<folly::Unit> Dispatcher::flush(InodeNumber, uint64_t) {
folly::Future<folly::Unit> FuseDispatcher::flush(InodeNumber, uint64_t) {
FUSELL_NOT_IMPL();
}
folly::Future<folly::Unit>
Dispatcher::fallocate(InodeNumber, uint64_t, uint64_t) {
FuseDispatcher::fallocate(InodeNumber, uint64_t, uint64_t) {
FUSELL_NOT_IMPL();
}
folly::Future<folly::Unit> Dispatcher::fsync(InodeNumber, bool) {
folly::Future<folly::Unit> FuseDispatcher::fsync(InodeNumber, bool) {
FUSELL_NOT_IMPL();
}
folly::Future<folly::Unit> Dispatcher::fsyncdir(InodeNumber, bool) {
folly::Future<folly::Unit> FuseDispatcher::fsyncdir(InodeNumber, bool) {
FUSELL_NOT_IMPL();
}
folly::Future<DirList> Dispatcher::readdir(
folly::Future<DirList> FuseDispatcher::readdir(
InodeNumber,
DirList&&,
off_t,
@ -194,7 +202,7 @@ folly::Future<DirList> Dispatcher::readdir(
FUSELL_NOT_IMPL();
}
folly::Future<struct fuse_kstatfs> Dispatcher::statfs(InodeNumber /*ino*/) {
folly::Future<struct fuse_kstatfs> FuseDispatcher::statfs(InodeNumber /*ino*/) {
struct fuse_kstatfs info = {};
// Suggest a large blocksize to software that looks at that kind of thing
@ -216,7 +224,7 @@ folly::Future<struct fuse_kstatfs> Dispatcher::statfs(InodeNumber /*ino*/) {
return info;
}
folly::Future<folly::Unit> Dispatcher::setxattr(
folly::Future<folly::Unit> FuseDispatcher::setxattr(
InodeNumber /*ino*/,
folly::StringPiece /*name*/,
folly::StringPiece /*value*/,
@ -224,7 +232,7 @@ folly::Future<folly::Unit> Dispatcher::setxattr(
FUSELL_NOT_IMPL();
}
const int Dispatcher::kENOATTR =
const int FuseDispatcher::kENOATTR =
#ifndef ENOATTR
ENODATA // Linux
#else
@ -232,24 +240,24 @@ const int Dispatcher::kENOATTR =
#endif
;
folly::Future<std::string> Dispatcher::getxattr(
folly::Future<std::string> FuseDispatcher::getxattr(
InodeNumber /*ino*/,
folly::StringPiece /*name*/) {
throwSystemErrorExplicit(kENOATTR);
}
folly::Future<std::vector<std::string>> Dispatcher::listxattr(
folly::Future<std::vector<std::string>> FuseDispatcher::listxattr(
InodeNumber /*ino*/) {
return std::vector<std::string>();
}
folly::Future<folly::Unit> Dispatcher::removexattr(
folly::Future<folly::Unit> FuseDispatcher::removexattr(
InodeNumber /*ino*/,
folly::StringPiece /*name*/) {
FUSELL_NOT_IMPL();
}
folly::Future<folly::Unit> Dispatcher::access(
folly::Future<folly::Unit> FuseDispatcher::access(
InodeNumber /*ino*/,
int /*mask*/) {
// Note that if you mount with the "default_permissions" kernel mount option,
@ -262,20 +270,22 @@ folly::Future<folly::Unit> Dispatcher::access(
}
folly::Future<fuse_entry_out>
Dispatcher::create(InodeNumber, PathComponentPiece, mode_t, int) {
FuseDispatcher::create(InodeNumber, PathComponentPiece, mode_t, int) {
FUSELL_NOT_IMPL();
}
folly::Future<uint64_t>
Dispatcher::bmap(InodeNumber /*ino*/, size_t /*blocksize*/, uint64_t /*idx*/) {
folly::Future<uint64_t> FuseDispatcher::bmap(
InodeNumber /*ino*/,
size_t /*blocksize*/,
uint64_t /*idx*/) {
FUSELL_NOT_IMPL();
}
const fuse_init_out& Dispatcher::getConnInfo() const {
const fuse_init_out& FuseDispatcher::getConnInfo() const {
return connInfo_;
}
EdenStats* Dispatcher::getStats() const {
EdenStats* FuseDispatcher::getStats() const {
return stats_;
}

View File

@ -21,8 +21,7 @@ template <class T>
class Future;
} // namespace folly
namespace facebook {
namespace eden {
namespace facebook::eden {
#define FUSELL_NOT_IMPL() \
do { \
@ -31,17 +30,16 @@ namespace eden {
} while (0)
class DirList;
class Dispatcher;
class EdenStats;
class Dispatcher {
class FuseDispatcher {
fuse_init_out connInfo_{};
EdenStats* stats_{nullptr};
public:
virtual ~Dispatcher();
virtual ~FuseDispatcher();
explicit Dispatcher(EdenStats* stats);
explicit FuseDispatcher(EdenStats* stats);
EdenStats* getStats() const;
const fuse_init_out& getConnInfo() const;
@ -465,5 +463,4 @@ class Dispatcher {
bmap(InodeNumber ino, size_t blocksize, uint64_t idx);
};
} // namespace eden
} // namespace facebook
} // namespace facebook::eden

View File

@ -15,8 +15,8 @@
#include <gflags/gflags.h>
#include <signal.h>
#include <sysexits.h>
#include "eden/fs/fuse/Dispatcher.h"
#include "eden/fs/fuse/FuseChannel.h"
#include "eden/fs/fuse/FuseDispatcher.h"
#include "eden/fs/fuse/privhelper/PrivHelper.h"
#include "eden/fs/fuse/privhelper/PrivHelperImpl.h"
#include "eden/fs/store/ObjectStore.h"
@ -36,10 +36,10 @@ DEFINE_int32(numFuseThreads, 4, "The number of FUSE worker threads");
FOLLY_INIT_LOGGING_CONFIG("eden=DBG2,eden.fs.fuse=DBG7");
namespace {
class TestDispatcher : public Dispatcher {
class TestDispatcher : public FuseDispatcher {
public:
TestDispatcher(EdenStats* stats, const UserInfo& identity)
: Dispatcher(stats), identity_(identity) {}
: FuseDispatcher(stats), identity_(identity) {}
folly::Future<Attr> getattr(InodeNumber ino, ObjectFetchContext& /*context*/)
override {
@ -120,7 +120,7 @@ int main(int argc, char** argv) {
.get(100ms);
EdenStats stats;
TestDispatcher dispatcher(&stats, identity);
auto dispatcher = std::make_unique<TestDispatcher>(&stats, identity);
folly::Logger straceLogger{"eden.strace"};
@ -128,7 +128,7 @@ int main(int argc, char** argv) {
std::move(fuseDevice),
mountPath,
FLAGS_numFuseThreads,
&dispatcher,
std::move(dispatcher),
&straceLogger,
std::make_shared<ProcessNameCache>()));

View File

@ -12,7 +12,7 @@
#include <folly/test/TestUtils.h>
#include <gtest/gtest.h>
#include <unordered_map>
#include "eden/fs/fuse/Dispatcher.h"
#include "eden/fs/fuse/FuseDispatcher.h"
#include "eden/fs/telemetry/EdenStats.h"
#include "eden/fs/testharness/FakeFuse.h"
#include "eden/fs/testharness/TestDispatcher.h"
@ -66,11 +66,13 @@ class FuseChannelTest : public ::testing::Test {
protected:
unique_ptr<FuseChannel, FuseChannelDeleter> createChannel(
size_t numThreads = 2) {
auto testDispatcher = std::make_unique<TestDispatcher>(&stats_);
dispatcher_ = testDispatcher.get();
return unique_ptr<FuseChannel, FuseChannelDeleter>(new FuseChannel(
fuse_.start(),
mountPath_,
numThreads,
&dispatcher_,
std::move(testDispatcher),
&straceLogger,
std::make_shared<ProcessNameCache>()));
}
@ -104,7 +106,7 @@ class FuseChannelTest : public ::testing::Test {
FakeFuse fuse_;
EdenStats stats_;
TestDispatcher dispatcher_{&stats_};
TestDispatcher* dispatcher_;
AbsolutePath mountPath_{"/fake/mount/path"};
};
@ -281,9 +283,9 @@ TEST_F(FuseChannelTest, testDestroyWithPendingRequests) {
auto id2 = fuse_.sendLookup(FUSE_ROOT_ID, "some_file.txt");
auto id3 = fuse_.sendLookup(FUSE_ROOT_ID, "main.c");
auto req1 = dispatcher_.waitForLookup(id1);
auto req2 = dispatcher_.waitForLookup(id2);
auto req3 = dispatcher_.waitForLookup(id3);
auto req1 = dispatcher_->waitForLookup(id1);
auto req2 = dispatcher_->waitForLookup(id2);
auto req3 = dispatcher_->waitForLookup(id3);
// Destroy the channel object
channel.reset();
@ -354,7 +356,7 @@ TEST_F(FuseChannelTest, interruptLookups) {
// dispatcher will definitely receive the request.
// We may need to change this code in the future if we do add true
// interrupt support to FuseChannel.
auto req = dispatcher_.waitForLookup(requestId);
auto req = dispatcher_->waitForLookup(requestId);
auto nodeId = 5 + i * 7;
auto response = genRandomLookupResponse(nodeId);

View File

@ -1,785 +0,0 @@
/*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This software may be used and distributed according to the terms of the
* GNU General Public License version 2.
*/
#include "EdenDispatcher.h"
#include <cpptoml.h> // @manual=fbsource//third-party/cpptoml:cpptoml
#include <folly/Format.h>
#include <folly/logging/xlog.h>
#include <gflags/gflags.h>
#include <cstring>
#include <shared_mutex>
#include "eden/fs/config/CheckoutConfig.h"
#include "eden/fs/fuse/DirList.h"
#include "eden/fs/inodes/EdenMount.h"
#include "eden/fs/inodes/FileInode.h"
#include "eden/fs/inodes/InodeMap.h"
#include "eden/fs/inodes/Overlay.h"
#include "eden/fs/inodes/ServerState.h"
#include "eden/fs/inodes/TreeInode.h"
#include "eden/fs/store/ObjectFetchContext.h"
#include "eden/fs/utils/SystemError.h"
#include "eden/fs/utils/UnboundedQueueExecutor.h"
using namespace folly;
using std::string;
using std::vector;
DEFINE_int32(
inode_reserve,
1000000,
"pre-size inode hash table for this many entries");
namespace facebook {
namespace eden {
#ifndef _WIN32
EdenDispatcher::EdenDispatcher(EdenMount* mount)
: Dispatcher(mount->getStats()),
mount_(mount),
inodeMap_(mount_->getInodeMap()) {}
namespace {
/** Compute a fuse_entry_out */
fuse_entry_out computeEntryParam(const Dispatcher::Attr& attr) {
XDCHECK(attr.st.st_ino) << "We should never return a 0 inode to FUSE";
fuse_entry_out entry = {};
entry.nodeid = attr.st.st_ino;
entry.generation = 0;
auto fuse_attr = attr.asFuseAttr();
entry.attr = fuse_attr.attr;
entry.attr_valid = fuse_attr.attr_valid;
entry.attr_valid_nsec = fuse_attr.attr_valid_nsec;
entry.entry_valid = fuse_attr.attr_valid;
entry.entry_valid_nsec = fuse_attr.attr_valid_nsec;
return entry;
}
constexpr int64_t kBrokenInodeCacheSeconds = 5;
Dispatcher::Attr attrForInodeWithCorruptOverlay(InodeNumber ino) noexcept {
struct stat st = {};
st.st_ino = ino.get();
st.st_mode = S_IFREG;
return Dispatcher::Attr{st, kBrokenInodeCacheSeconds};
}
} // namespace
folly::Future<Dispatcher::Attr> EdenDispatcher::getattr(
InodeNumber ino,
ObjectFetchContext& context) {
return inodeMap_->lookupInode(ino)
.thenValue(
[&context](const InodePtr& inode) { return inode->stat(context); })
.thenValue([](const struct stat& st) { return Dispatcher::Attr{st}; });
}
folly::Future<uint64_t> EdenDispatcher::opendir(
InodeNumber /*ino*/,
int /*flags*/) {
#ifdef FUSE_NO_OPENDIR_SUPPORT
if (getConnInfo().flags & FUSE_NO_OPENDIR_SUPPORT) {
// If the kernel understands FUSE_NO_OPENDIR_SUPPORT, then returning ENOSYS
// means that no further opendir() nor releasedir() calls will make it into
// Eden.
folly::throwSystemErrorExplicit(
ENOSYS, "Eden opendir() calls are stateless and not required");
}
#endif
return 0;
}
folly::Future<folly::Unit> EdenDispatcher::releasedir(
InodeNumber /*ino*/,
uint64_t /*fh*/) {
return folly::unit;
}
folly::Future<fuse_entry_out> EdenDispatcher::lookup(
uint64_t /*requestID*/,
InodeNumber parent,
PathComponentPiece namepiece,
ObjectFetchContext& context) {
return inodeMap_->lookupTreeInode(parent)
.thenValue([name = PathComponent(namepiece),
&context](const TreeInodePtr& tree) {
return tree->getOrLoadChild(name, context);
})
.thenValue([&context](const InodePtr& inode) {
return folly::makeFutureWith([&]() { return inode->stat(context); })
.thenTry([inode](folly::Try<struct stat> maybeStat) {
if (maybeStat.hasValue()) {
inode->incFsRefcount();
return computeEntryParam(Dispatcher::Attr{maybeStat.value()});
} else {
// The most common case for stat() failing is if this file is
// materialized but the data for it in the overlay is missing
// or corrupt. This can happen after a hard reboot where the
// overlay data was not synced to disk first.
//
// We intentionally want to return a result here rather than
// failing; otherwise we can't return the inode number to the
// kernel at all. This blocks other operations on the file,
// like FUSE_UNLINK. By successfully returning from the
// lookup we allow clients to remove this corrupt file with an
// unlink operation. (Even though FUSE_UNLINK does not require
// the child inode number, the kernel does not appear to send a
// FUSE_UNLINK request to us if it could not get the child inode
// number first.)
XLOG(WARN) << "error getting attributes for inode "
<< inode->getNodeId() << " (" << inode->getLogPath()
<< "): " << maybeStat.exception().what();
inode->incFsRefcount();
return computeEntryParam(
attrForInodeWithCorruptOverlay(inode->getNodeId()));
}
});
})
.thenError(
folly::tag_t<std::system_error>{}, [](const std::system_error& err) {
// Translate ENOENT into a successful response with an
// inode number of 0 and a large entry_valid time, to let the kernel
// cache this negative lookup result.
if (isEnoent(err)) {
fuse_entry_out entry = {};
entry.attr_valid =
std::numeric_limits<decltype(entry.attr_valid)>::max();
entry.entry_valid =
std::numeric_limits<decltype(entry.entry_valid)>::max();
return entry;
}
throw err;
});
}
folly::Future<Dispatcher::Attr> EdenDispatcher::setattr(
InodeNumber ino,
const fuse_setattr_in& attr) {
// Even though mounts are created with the nosuid flag, explicitly disallow
// setting suid, sgid, and sticky bits on any inodes. This lets us avoid
// explicitly clearing these bits on writes() which is required for correct
// behavior under FUSE_HANDLE_KILLPRIV.
if ((attr.valid & FATTR_MODE) &&
(attr.mode & (S_ISUID | S_ISGID | S_ISVTX))) {
folly::throwSystemErrorExplicit(EPERM, "Extra mode bits are disallowed");
}
return inodeMap_->lookupInode(ino).thenValue(
[attr](const InodePtr& inode) { return inode->setattr(attr); });
}
void EdenDispatcher::forget(InodeNumber ino, unsigned long nlookup) {
inodeMap_->decFsRefcount(ino, nlookup);
}
folly::Future<uint64_t> EdenDispatcher::open(
InodeNumber /*ino*/,
int /*flags*/) {
#ifdef FUSE_NO_OPEN_SUPPORT
if (getConnInfo().flags & FUSE_NO_OPEN_SUPPORT) {
// If the kernel understands FUSE_NO_OPEN_SUPPORT, then returning ENOSYS
// means that no further open() nor release() calls will make it into Eden.
folly::throwSystemErrorExplicit(
ENOSYS, "Eden open() calls are stateless and not required");
}
#endif
return 0;
}
folly::Future<fuse_entry_out> EdenDispatcher::create(
InodeNumber parent,
PathComponentPiece name,
mode_t mode,
int /*flags*/) {
// force 'mode' to be regular file, in which case rdev arg to mknod is ignored
// (and thus can be zero)
mode = S_IFREG | (07777 & mode);
return inodeMap_->lookupTreeInode(parent).thenValue(
[=](const TreeInodePtr& inode) {
auto childName = PathComponent{name};
auto child = inode->mknod(childName, mode, 0, InvalidationRequired::No);
static auto context = ObjectFetchContext::getNullContextWithCauseDetail(
"EdenDispatcher::create");
return child->stat(*context).thenValue(
[child](struct stat st) -> fuse_entry_out {
child->incFsRefcount();
return computeEntryParam(Dispatcher::Attr{st});
});
});
}
folly::Future<BufVec> EdenDispatcher::read(
InodeNumber ino,
size_t size,
off_t off,
ObjectFetchContext& context) {
return inodeMap_->lookupFileInode(ino).thenValue(
[&context, size, off](FileInodePtr&& inode) {
return inode->read(size, off, context);
});
}
folly::Future<size_t>
EdenDispatcher::write(InodeNumber ino, folly::StringPiece data, off_t off) {
return inodeMap_->lookupFileInode(ino).thenValue(
[copy = data.str(), off](FileInodePtr&& inode) {
return inode->write(copy, off);
});
}
Future<Unit> EdenDispatcher::flush(
InodeNumber /* ino */,
uint64_t /* lock_owner */) {
// Return ENOSYS from flush.
// This will cause the kernel to stop sending future flush() calls.
return makeFuture<Unit>(makeSystemErrorExplicit(ENOSYS, "flush"));
}
folly::Future<folly::Unit>
EdenDispatcher::fallocate(InodeNumber ino, uint64_t offset, uint64_t length) {
return inodeMap_->lookupFileInode(ino).thenValue(
[offset, length](FileInodePtr inode) {
return inode->fallocate(offset, length);
});
}
folly::Future<folly::Unit> EdenDispatcher::fsync(
InodeNumber ino,
bool datasync) {
return inodeMap_->lookupFileInode(ino).thenValue(
[datasync](FileInodePtr inode) { return inode->fsync(datasync); });
}
Future<Unit> EdenDispatcher::fsyncdir(
InodeNumber /* ino */,
bool /* datasync */) {
// Return ENOSYS from fsyncdir. The kernel will stop sending them.
//
// In a possible future where the tree structure is stored in a SQLite
// database, we could handle this request by waiting for SQLite's
// write-ahead-log to be flushed.
return makeFuture<Unit>(makeSystemErrorExplicit(ENOSYS, "fsyncdir"));
}
folly::Future<std::string> EdenDispatcher::readlink(
InodeNumber ino,
bool kernelCachesReadlink) {
static auto context = ObjectFetchContext::getNullContextWithCauseDetail(
"EdenDispatcher::readlink");
return inodeMap_->lookupFileInode(ino).thenValue(
[kernelCachesReadlink](const FileInodePtr& inode) {
// Only release the symlink blob after it's loaded if we can assume the
// FUSE will cache the result in the kernel's page cache.
return inode->readlink(
*context,
kernelCachesReadlink ? CacheHint::NotNeededAgain
: CacheHint::LikelyNeededAgain);
});
}
folly::Future<DirList> EdenDispatcher::readdir(
InodeNumber ino,
DirList&& dirList,
off_t offset,
uint64_t /*fh*/,
ObjectFetchContext& context) {
return inodeMap_->lookupTreeInode(ino).thenValue(
[dirList = std::move(dirList), offset, &context](
TreeInodePtr inode) mutable {
return inode->readdir(std::move(dirList), offset, context);
});
}
folly::Future<fuse_entry_out> EdenDispatcher::mknod(
InodeNumber parent,
PathComponentPiece name,
mode_t mode,
dev_t rdev) {
static auto context = ObjectFetchContext::getNullContextWithCauseDetail(
"EdenDispatcher::mknod");
return inodeMap_->lookupTreeInode(parent).thenValue(
[childName = PathComponent{name}, mode, rdev](const TreeInodePtr& inode) {
auto child =
inode->mknod(childName, mode, rdev, InvalidationRequired::No);
return child->stat(*context).thenValue(
[child](struct stat st) -> fuse_entry_out {
child->incFsRefcount();
return computeEntryParam(Dispatcher::Attr{st});
});
});
}
folly::Future<fuse_entry_out> EdenDispatcher::mkdir(
InodeNumber parent,
PathComponentPiece name,
mode_t mode) {
static auto context = ObjectFetchContext::getNullContextWithCauseDetail(
"EdenDispatcher::mkdir");
return inodeMap_->lookupTreeInode(parent).thenValue(
[childName = PathComponent{name}, mode](const TreeInodePtr& inode) {
auto child = inode->mkdir(childName, mode, InvalidationRequired::No);
return child->stat(*context).thenValue([child](struct stat st) {
child->incFsRefcount();
return computeEntryParam(Dispatcher::Attr{st});
});
});
}
folly::Future<folly::Unit> EdenDispatcher::unlink(
InodeNumber parent,
PathComponentPiece name) {
return inodeMap_->lookupTreeInode(parent).thenValue(
[childName = PathComponent{name}](const TreeInodePtr& inode) {
// No need to flush the kernel cache because FUSE will do that for us.
return inode->unlink(childName, InvalidationRequired::No);
});
}
folly::Future<folly::Unit> EdenDispatcher::rmdir(
InodeNumber parent,
PathComponentPiece name) {
return inodeMap_->lookupTreeInode(parent).thenValue(
[childName = PathComponent{name}](const TreeInodePtr& inode) {
// No need to flush the kernel cache because FUSE will do that for us.
return inode->rmdir(childName, InvalidationRequired::No);
});
}
folly::Future<fuse_entry_out> EdenDispatcher::symlink(
InodeNumber parent,
PathComponentPiece name,
StringPiece link) {
static auto context = ObjectFetchContext::getNullContextWithCauseDetail(
"EdenDispatcher::symlink");
return inodeMap_->lookupTreeInode(parent).thenValue(
[linkContents = link.str(),
childName = PathComponent{name}](const TreeInodePtr& inode) {
auto symlinkInode =
inode->symlink(childName, linkContents, InvalidationRequired::No);
symlinkInode->incFsRefcount();
return symlinkInode->stat(*context).thenValue(
[symlinkInode](struct stat st) {
return computeEntryParam(Dispatcher::Attr{st});
});
});
}
folly::Future<folly::Unit> EdenDispatcher::rename(
InodeNumber parent,
PathComponentPiece namePiece,
InodeNumber newParent,
PathComponentPiece newNamePiece) {
// Start looking up both parents
auto parentFuture = inodeMap_->lookupTreeInode(parent);
auto newParentFuture = inodeMap_->lookupTreeInode(newParent);
// Do the rename once we have looked up both parents.
return std::move(parentFuture)
.thenValue([npFuture = std::move(newParentFuture),
name = PathComponent{namePiece},
newName = PathComponent{newNamePiece}](
const TreeInodePtr& parent) mutable {
return std::move(npFuture).thenValue(
[parent, name, newName](const TreeInodePtr& newParent) {
return parent->rename(
name, newParent, newName, InvalidationRequired::No);
});
});
}
folly::Future<fuse_entry_out> EdenDispatcher::link(
InodeNumber /*ino*/,
InodeNumber /*newParent*/,
PathComponentPiece newName) {
validatePathComponentLength(newName);
// We intentionally do not support hard links.
// These generally cannot be tracked in source control (git or mercurial)
// and are not portable to non-Unix platforms.
folly::throwSystemErrorExplicit(
EPERM, "hard links are not supported in eden mount points");
}
Future<string> EdenDispatcher::getxattr(InodeNumber ino, StringPiece name) {
return inodeMap_->lookupInode(ino).thenValue(
[attrName = name.str()](const InodePtr& inode) {
return inode->getxattr(attrName);
});
}
Future<vector<string>> EdenDispatcher::listxattr(InodeNumber ino) {
return inodeMap_->lookupInode(ino).thenValue(
[](const InodePtr& inode) { return inode->listxattr(); });
}
folly::Future<struct fuse_kstatfs> EdenDispatcher::statfs(InodeNumber /*ino*/) {
struct fuse_kstatfs info = {};
// Pass through the overlay free space stats; this gives a more reasonable
// estimation of available storage space than the zeroes that we'd report
// otherwise. This is important because eg: Finder on macOS inspects disk
// space prior to initiating a copy and will refuse to start a copy if
// the disk appears to be full.
auto overlayStats = mount_->getOverlay()->statFs();
info.blocks = overlayStats.f_blocks;
info.bfree = overlayStats.f_bfree;
info.bavail = overlayStats.f_bavail;
info.files = overlayStats.f_files;
info.ffree = overlayStats.f_ffree;
// Suggest a large blocksize to software that looks at that kind of thing
// bsize will be returned to applications that call pathconf() with
// _PC_REC_MIN_XFER_SIZE
info.bsize = getConnInfo().max_readahead;
// The fragment size is returned as the _PC_REC_XFER_ALIGN and
// _PC_ALLOC_SIZE_MIN pathconf() settings.
// 4096 is commonly used by many filesystem types.
info.frsize = 4096;
// Ensure that namelen is set to a non-zero value.
// The value we return here will be visible to programs that call pathconf()
// with _PC_NAME_MAX. Returning 0 will confuse programs that try to honor
// this value.
info.namelen = 255;
return info;
}
#else
namespace {
const RelativePath kDotEdenConfigPath{".eden/config"};
const std::string kConfigRootPath{"root"};
const std::string kConfigSocketPath{"socket"};
const std::string kConfigClientPath{"client"};
const std::string kConfigTable{"Config"};
std::string makeDotEdenConfig(EdenMount& mount) {
auto repoPath = mount.getPath();
auto socketPath = mount.getServerState()->getSocketPath();
auto clientPath = mount.getConfig()->getClientDirectory();
auto rootTable = cpptoml::make_table();
auto configTable = cpptoml::make_table();
configTable->insert(kConfigRootPath, repoPath.c_str());
configTable->insert(kConfigSocketPath, socketPath.c_str());
configTable->insert(kConfigClientPath, clientPath.c_str());
rootTable->insert(kConfigTable, configTable);
std::ostringstream stream;
stream << *rootTable;
return stream.str();
}
} // namespace
EdenDispatcher::EdenDispatcher(EdenMount* mount)
: Dispatcher(mount->getStats()),
mount_{mount},
dotEdenConfig_{makeDotEdenConfig(*mount)} {}
folly::Future<std::vector<FileMetadata>> EdenDispatcher::opendir(
RelativePathPiece path,
ObjectFetchContext& context) {
return mount_->getInode(path, context).thenValue([](const InodePtr inode) {
auto treePtr = inode.asTreePtr();
return treePtr->readdir();
});
}
folly::Future<std::optional<LookupResult>> EdenDispatcher::lookup(
RelativePath path,
ObjectFetchContext& context) {
return mount_->getInode(path, context)
.thenValue(
[&context](const InodePtr inode) mutable
-> folly::Future<std::optional<LookupResult>> {
return inode->stat(context).thenValue(
[inode = std::move(inode)](struct stat&& stat) {
size_t size = stat.st_size;
// Ensure that the OS has a record of the canonical
// file name, and not just whatever case was used to
// lookup the file
auto inodeMetadata =
InodeMetadata{*inode->getPath(), size, inode->isDir()};
auto incFsRefcount = [inode = std::move(inode)] {
inode->incFsRefcount();
};
return LookupResult{
std::move(inodeMetadata), std::move(incFsRefcount)};
});
})
.thenError(
folly::tag_t<std::system_error>{},
[path = std::move(path), this](const std::system_error& ex)
-> folly::Future<std::optional<LookupResult>> {
if (isEnoent(ex)) {
if (path == kDotEdenConfigPath) {
return folly::makeFuture(LookupResult{
InodeMetadata{
std::move(path), dotEdenConfig_.length(), false},
[] {}});
} else {
XLOG(DBG6) << path << ": File not found";
return folly::makeFuture(std::nullopt);
}
}
return folly::makeFuture<std::optional<LookupResult>>(ex);
});
}
folly::Future<bool> EdenDispatcher::access(
RelativePath path,
ObjectFetchContext& context) {
return mount_->getInode(path, context)
.thenValue([](const InodePtr) { return true; })
.thenError(
folly::tag_t<std::system_error>{},
[path = std::move(path)](const std::system_error& ex) {
if (isEnoent(ex)) {
if (path == kDotEdenConfigPath) {
return folly::makeFuture(true);
}
return folly::makeFuture(false);
}
return folly::makeFuture<bool>(ex);
});
}
folly::Future<std::string> EdenDispatcher::read(
RelativePath path,
ObjectFetchContext& context) {
return mount_->getInode(path, context)
.thenValue([&context](const InodePtr inode) {
auto fileInode = inode.asFilePtr();
return fileInode->readAll(context);
})
.thenError(
folly::tag_t<std::system_error>{},
[path = std::move(path), this](const std::system_error& ex) {
if (isEnoent(ex) && path == kDotEdenConfigPath) {
return folly::makeFuture<std::string>(
std::string(dotEdenConfig_));
}
return folly::makeFuture<std::string>(ex);
});
}
namespace {
folly::Future<TreeInodePtr> createDirInode(
const EdenMount& mount,
const RelativePathPiece path,
ObjectFetchContext& context) {
return mount.getInode(path, context)
.thenValue([](const InodePtr inode) { return inode.asTreePtr(); })
.thenError(
folly::tag_t<std::system_error>{},
[=, &mount](const std::system_error& ex) {
if (!isEnoent(ex)) {
return folly::makeFuture<TreeInodePtr>(ex);
}
mount.getStats()
->getChannelStatsForCurrentThread()
.outOfOrderCreate.addValue(1);
XLOG(DBG2) << "Out of order directory creation notification for: "
<< path;
/*
* ProjectedFS notifications are asynchronous and sent after the
* fact. This means that we can get a notification on a
* file/directory before the parent directory notification has been
* completed. This should be a very rare event and thus the code
* below is pessimistic and will try to create all parent
* directories.
*/
auto fut = folly::makeFuture(mount.getRootInode());
for (auto parent : path.paths()) {
fut = std::move(fut).thenValue([parent](TreeInodePtr treeInode) {
try {
auto inode = treeInode->mkdir(
parent.basename(), _S_IFDIR, InvalidationRequired::No);
inode->incFsRefcount();
} catch (const std::system_error& ex) {
if (ex.code().value() != EEXIST) {
throw;
}
}
return treeInode->getOrLoadChildTree(parent.basename());
});
}
return fut;
});
}
folly::Future<folly::Unit> createFile(
const EdenMount& mount,
const RelativePathPiece path,
bool isDirectory,
ObjectFetchContext& context) {
return createDirInode(mount, path.dirname(), context)
.thenValue([=](const TreeInodePtr treeInode) {
if (isDirectory) {
try {
auto inode = treeInode->mkdir(
path.basename(), _S_IFDIR, InvalidationRequired::No);
inode->incFsRefcount();
} catch (const std::system_error& ex) {
/*
* If a concurrent createFile for a child of this directory finished
* before this one, the directory will already exist. This is not an
* error.
*/
if (ex.code().value() != EEXIST) {
return folly::makeFuture<folly::Unit>(ex);
}
}
} else {
auto inode = treeInode->mknod(
path.basename(), _S_IFREG, 0, InvalidationRequired::No);
inode->incFsRefcount();
}
return folly::makeFuture(folly::unit);
});
}
folly::Future<folly::Unit> materializeFile(
const EdenMount& mount,
const RelativePathPiece path,
ObjectFetchContext& context) {
return mount.getInode(path, context).thenValue([](const InodePtr inode) {
auto fileInode = inode.asFilePtr();
fileInode->materialize();
return folly::unit;
});
}
folly::Future<folly::Unit> renameFile(
const EdenMount& mount,
const RelativePath oldPath,
const RelativePath newPath,
ObjectFetchContext& context) {
auto oldParentInode = createDirInode(mount, oldPath.dirname(), context);
auto newParentInode = createDirInode(mount, newPath.dirname(), context);
return folly::collect(oldParentInode, newParentInode)
.via(mount.getThreadPool().get())
.thenValue([oldPath = std::move(oldPath), newPath = std::move(newPath)](
const std::tuple<TreeInodePtr, TreeInodePtr> inodes) {
auto& oldParentTreePtr = std::get<0>(inodes);
auto& newParentTreePtr = std::get<1>(inodes);
// TODO(xavierd): In the case where the oldPath is actually being
// created in another thread, EdenFS simply might not know about
// it at this point. Creating the file and renaming it at this
// point won't help as the other thread will re-create it. In the
// future, we may want to try, wait a bit and retry, or re-think
// this and somehow order requests so the file creation always
// happens before the rename.
//
// This should be *extremely* rare, for now let's just let it
// error out.
return oldParentTreePtr->rename(
oldPath.basename(),
newParentTreePtr,
newPath.basename(),
InvalidationRequired::No);
});
}
folly::Future<folly::Unit> removeFile(
const EdenMount& mount,
const RelativePathPiece path,
bool isDirectory,
ObjectFetchContext& context) {
return mount.getInode(path.dirname(), context)
.thenValue([=](const InodePtr inode) {
auto treeInodePtr = inode.asTreePtr();
if (isDirectory) {
return treeInodePtr->rmdir(path.basename(), InvalidationRequired::No);
} else {
return treeInodePtr->unlink(
path.basename(), InvalidationRequired::No);
}
});
}
} // namespace
folly::Future<folly::Unit> EdenDispatcher::newFileCreated(
RelativePath relPath,
RelativePath /*destPath*/,
bool isDirectory,
ObjectFetchContext& context) {
return createFile(*mount_, relPath, isDirectory, context);
}
folly::Future<folly::Unit> EdenDispatcher::fileOverwritten(
RelativePath relPath,
RelativePath /*destPath*/,
bool /*isDirectory*/,
ObjectFetchContext& context) {
return materializeFile(*mount_, relPath, context);
}
folly::Future<folly::Unit> EdenDispatcher::fileHandleClosedFileModified(
RelativePath relPath,
RelativePath /*destPath*/,
bool /*isDirectory*/,
ObjectFetchContext& context) {
return materializeFile(*mount_, relPath, context);
}
folly::Future<folly::Unit> EdenDispatcher::fileRenamed(
RelativePath oldPath,
RelativePath newPath,
bool isDirectory,
ObjectFetchContext& context) {
// When files are moved in and out of the repo, the rename paths are
// empty, handle these like creation/removal of files.
if (oldPath.empty()) {
return createFile(*mount_, newPath, isDirectory, context);
} else if (newPath.empty()) {
return removeFile(*mount_, oldPath, isDirectory, context);
} else {
return renameFile(*mount_, std::move(oldPath), std::move(newPath), context);
}
}
folly::Future<folly::Unit> EdenDispatcher::preRename(
RelativePath oldPath,
RelativePath newPath,
bool /*isDirectory*/,
ObjectFetchContext& /*context*/) {
return folly::unit;
}
folly::Future<folly::Unit> EdenDispatcher::fileHandleClosedFileDeleted(
RelativePath oldPath,
RelativePath /*destPath*/,
bool isDirectory,
ObjectFetchContext& context) {
return removeFile(*mount_, oldPath, isDirectory, context);
}
folly::Future<folly::Unit> EdenDispatcher::preSetHardlink(
RelativePath relPath,
RelativePath /*destPath*/,
bool /*isDirectory*/,
ObjectFetchContext& /*context*/) {
return folly::makeFuture<folly::Unit>(makeHResultErrorExplicit(
HRESULT_FROM_WIN32(ERROR_ACCESS_DENIED),
fmt::format(FMT_STRING("Hardlinks are not supported: {}"), relPath)));
}
#endif // _WIN32
} // namespace eden
} // namespace facebook

View File

@ -0,0 +1,30 @@
/*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This software may be used and distributed according to the terms of the
* GNU General Public License version 2.
*/
#include "eden/fs/inodes/EdenDispatcherFactory.h"
#ifndef _WIN32
#include "eden/fs/inodes/FuseDispatcherImpl.h"
#else
#include "eden/fs/inodes/PrjfsDispatcherImpl.h"
#endif
namespace facebook::eden {
#ifndef _WIN32
std::unique_ptr<FuseDispatcher> EdenDispatcherFactory::makeFuseDispatcher(
EdenMount* mount) {
return std::make_unique<FuseDispatcherImpl>(mount);
}
#else
std::unique_ptr<PrjfsDispatcher> EdenDispatcherFactory::makePrjfsDispatcher(
EdenMount* mount) {
return std::make_unique<PrjfsDispatcherImpl>(mount);
}
#endif
} // namespace facebook::eden

View File

@ -0,0 +1,29 @@
/*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This software may be used and distributed according to the terms of the
* GNU General Public License version 2.
*/
#pragma once
#ifndef _WIN32
#include "eden/fs/fuse/FuseDispatcher.h"
#else
#include "eden/fs/prjfs/PrjfsDispatcher.h"
#endif
namespace facebook::eden {
class EdenMount;
class EdenDispatcherFactory {
public:
#ifndef _WIN32
static std::unique_ptr<FuseDispatcher> makeFuseDispatcher(EdenMount* mount);
#else
static std::unique_ptr<PrjfsDispatcher> makePrjfsDispatcher(EdenMount* mount);
#endif
};
} // namespace facebook::eden

View File

@ -23,7 +23,7 @@
#include "eden/fs/config/CheckoutConfig.h"
#include "eden/fs/config/EdenConfig.h"
#include "eden/fs/inodes/CheckoutContext.h"
#include "eden/fs/inodes/EdenDispatcher.h"
#include "eden/fs/inodes/EdenDispatcherFactory.h"
#include "eden/fs/inodes/FileInode.h"
#include "eden/fs/inodes/InodeError.h"
#include "eden/fs/inodes/InodeMap.h"
@ -200,7 +200,6 @@ EdenMount::EdenMount(
: config_{std::move(config)},
serverState_{std::move(serverState)},
inodeMap_{new InodeMap(this)},
dispatcher_{new EdenDispatcher(this)},
objectStore_{std::move(objectStore)},
blobCache_{std::move(blobCache)},
blobAccess_{objectStore_, blobCache_},
@ -1233,7 +1232,7 @@ folly::Future<EdenMount::channelType> EdenMount::channelMount(bool readOnly) {
readOnly]() -> folly::Future<PrjfsChannel*> {
auto channel = new PrjfsChannel(
mountPath,
getDispatcher(),
EdenDispatcherFactory::makePrjfsDispatcher(this),
&getStraceLogger(),
serverState_->getProcessNameCache(),
std::chrono::duration_cast<folly::Duration>(
@ -1275,7 +1274,7 @@ folly::Future<EdenMount::channelType> EdenMount::channelMount(bool readOnly) {
return nfsServer->registerMount(
mountPath,
getRootInode()->getNodeId(),
getDispatcher(),
nullptr, // TODO(xavierd): Add makeNfsDispatcher()
&getStraceLogger(),
serverState_->getProcessNameCache(),
std::chrono::duration_cast<folly::Duration>(
@ -1363,7 +1362,7 @@ void EdenMount::createChannel(EdenMount::channelType channel) {
std::move(channel),
getPath(),
FLAGS_fuseNumThreads,
dispatcher_.get(),
EdenDispatcherFactory::makeFuseDispatcher(this),
&straceLogger_,
serverState_->getProcessNameCache(),
std::chrono::duration_cast<folly::Duration>(

View File

@ -33,7 +33,6 @@
#include "eden/fs/utils/PathFuncs.h"
#ifndef _WIN32
#include "eden/fs/fuse/Dispatcher.h"
#include "eden/fs/fuse/FuseChannel.h"
#include "eden/fs/inodes/OverlayFileAccess.h"
#else
@ -65,7 +64,6 @@ class CheckoutConfig;
class CheckoutConflict;
class Clock;
class DiffContext;
class EdenDispatcher;
class FuseChannel;
class FuseDeviceUnmountedDuringInitialization;
class DiffCallback;
@ -326,13 +324,6 @@ class EdenMount {
return &blobAccess_;
}
/**
* Return the EdenDispatcher used for this mount.
*/
EdenDispatcher* getDispatcher() const {
return dispatcher_.get();
}
/**
* Return the InodeMap for this mount.
*/
@ -844,8 +835,6 @@ class EdenMount {
std::unique_ptr<InodeMap> inodeMap_;
std::unique_ptr<EdenDispatcher> dispatcher_;
std::shared_ptr<ObjectStore> objectStore_;
std::shared_ptr<BlobCache> blobCache_;
BlobAccess blobAccess_;

View File

@ -426,19 +426,19 @@ FileInode::FileInode(
state_(folly::in_place) {}
#ifndef _WIN32
folly::Future<Dispatcher::Attr> FileInode::setattr(
folly::Future<FuseDispatcher::Attr> FileInode::setattr(
const fuse_setattr_in& attr) {
// If this file is inside of .eden it cannot be reparented, so getParentRacy()
// is okay.
auto parent = getParentRacy();
if (parent && parent->getNodeId() == getMount()->getDotEdenInodeNumber()) {
return folly::makeFuture<Dispatcher::Attr>(
return folly::makeFuture<FuseDispatcher::Attr>(
InodeError(EPERM, inodePtrFromThis()));
}
auto setAttrs = [self = inodePtrFromThis(), attr](LockedState&& state) {
auto ino = self->getNodeId();
auto result = Dispatcher::Attr{self->getMount()->initStatData()};
auto result = FuseDispatcher::Attr{self->getMount()->initStatData()};
XDCHECK_EQ(State::MATERIALIZED_IN_OVERLAY, state->tag)
<< "Must have a file in the overlay at this point";

View File

@ -148,7 +148,8 @@ class FileInode final : public InodeBaseMetadata<FileInodeState> {
const InodeTimestamps& initialTimestamps);
#ifndef _WIN32
folly::Future<Dispatcher::Attr> setattr(const fuse_setattr_in& attr) override;
folly::Future<FuseDispatcher::Attr> setattr(
const fuse_setattr_in& attr) override;
/// Throws InodeError EINVAL if inode is not a symbolic node.
folly::Future<std::string> readlink(

View File

@ -0,0 +1,446 @@
/*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This software may be used and distributed according to the terms of the
* GNU General Public License version 2.
*/
#ifndef _WIN32
#include "eden/fs/inodes/FuseDispatcherImpl.h"
#include <folly/logging/xlog.h>
#include "eden/fs/fuse/DirList.h"
#include "eden/fs/inodes/EdenMount.h"
#include "eden/fs/inodes/FileInode.h"
#include "eden/fs/inodes/InodeMap.h"
#include "eden/fs/inodes/InodePtr.h"
#include "eden/fs/inodes/Overlay.h"
#include "eden/fs/inodes/TreeInode.h"
#include "eden/fs/store/ObjectFetchContext.h"
#include "eden/fs/utils/SystemError.h"
using namespace folly;
using std::string;
using std::vector;
namespace facebook::eden {
namespace {
/** Compute a fuse_entry_out */
fuse_entry_out computeEntryParam(const FuseDispatcher::Attr& attr) {
XDCHECK(attr.st.st_ino) << "We should never return a 0 inode to FUSE";
fuse_entry_out entry = {};
entry.nodeid = attr.st.st_ino;
entry.generation = 0;
auto fuse_attr = attr.asFuseAttr();
entry.attr = fuse_attr.attr;
entry.attr_valid = fuse_attr.attr_valid;
entry.attr_valid_nsec = fuse_attr.attr_valid_nsec;
entry.entry_valid = fuse_attr.attr_valid;
entry.entry_valid_nsec = fuse_attr.attr_valid_nsec;
return entry;
}
constexpr int64_t kBrokenInodeCacheSeconds = 5;
FuseDispatcher::Attr attrForInodeWithCorruptOverlay(InodeNumber ino) noexcept {
struct stat st = {};
st.st_ino = ino.get();
st.st_mode = S_IFREG;
return FuseDispatcher::Attr{st, kBrokenInodeCacheSeconds};
}
} // namespace
FuseDispatcherImpl::FuseDispatcherImpl(EdenMount* mount)
: FuseDispatcher(mount->getStats()),
mount_(mount),
inodeMap_(mount_->getInodeMap()) {}
folly::Future<FuseDispatcher::Attr> FuseDispatcherImpl::getattr(
InodeNumber ino,
ObjectFetchContext& context) {
return inodeMap_->lookupInode(ino)
.thenValue(
[&context](const InodePtr& inode) { return inode->stat(context); })
.thenValue(
[](const struct stat& st) { return FuseDispatcher::Attr{st}; });
}
folly::Future<uint64_t> FuseDispatcherImpl::opendir(
InodeNumber /*ino*/,
int /*flags*/) {
#ifdef FUSE_NO_OPENDIR_SUPPORT
if (getConnInfo().flags & FUSE_NO_OPENDIR_SUPPORT) {
// If the kernel understands FUSE_NO_OPENDIR_SUPPORT, then returning ENOSYS
// means that no further opendir() nor releasedir() calls will make it into
// Eden.
folly::throwSystemErrorExplicit(
ENOSYS, "Eden opendir() calls are stateless and not required");
}
#endif
return 0;
}
folly::Future<folly::Unit> FuseDispatcherImpl::releasedir(
InodeNumber /*ino*/,
uint64_t /*fh*/) {
return folly::unit;
}
folly::Future<fuse_entry_out> FuseDispatcherImpl::lookup(
uint64_t /*requestID*/,
InodeNumber parent,
PathComponentPiece namepiece,
ObjectFetchContext& context) {
return inodeMap_->lookupTreeInode(parent)
.thenValue([name = PathComponent(namepiece),
&context](const TreeInodePtr& tree) {
return tree->getOrLoadChild(name, context);
})
.thenValue([&context](const InodePtr& inode) {
return folly::makeFutureWith([&]() { return inode->stat(context); })
.thenTry([inode](folly::Try<struct stat> maybeStat) {
if (maybeStat.hasValue()) {
inode->incFsRefcount();
return computeEntryParam(
FuseDispatcher::Attr{maybeStat.value()});
} else {
// The most common case for stat() failing is if this file is
// materialized but the data for it in the overlay is missing
// or corrupt. This can happen after a hard reboot where the
// overlay data was not synced to disk first.
//
// We intentionally want to return a result here rather than
// failing; otherwise we can't return the inode number to the
// kernel at all. This blocks other operations on the file,
// like FUSE_UNLINK. By successfully returning from the
// lookup we allow clients to remove this corrupt file with an
// unlink operation. (Even though FUSE_UNLINK does not require
// the child inode number, the kernel does not appear to send a
// FUSE_UNLINK request to us if it could not get the child inode
// number first.)
XLOG(WARN) << "error getting attributes for inode "
<< inode->getNodeId() << " (" << inode->getLogPath()
<< "): " << maybeStat.exception().what();
inode->incFsRefcount();
return computeEntryParam(
attrForInodeWithCorruptOverlay(inode->getNodeId()));
}
});
})
.thenError(
folly::tag_t<std::system_error>{}, [](const std::system_error& err) {
// Translate ENOENT into a successful response with an
// inode number of 0 and a large entry_valid time, to let the kernel
// cache this negative lookup result.
if (isEnoent(err)) {
fuse_entry_out entry = {};
entry.attr_valid =
std::numeric_limits<decltype(entry.attr_valid)>::max();
entry.entry_valid =
std::numeric_limits<decltype(entry.entry_valid)>::max();
return entry;
}
throw err;
});
}
folly::Future<FuseDispatcher::Attr> FuseDispatcherImpl::setattr(
InodeNumber ino,
const fuse_setattr_in& attr) {
// Even though mounts are created with the nosuid flag, explicitly disallow
// setting suid, sgid, and sticky bits on any inodes. This lets us avoid
// explicitly clearing these bits on writes() which is required for correct
// behavior under FUSE_HANDLE_KILLPRIV.
if ((attr.valid & FATTR_MODE) &&
(attr.mode & (S_ISUID | S_ISGID | S_ISVTX))) {
folly::throwSystemErrorExplicit(EPERM, "Extra mode bits are disallowed");
}
return inodeMap_->lookupInode(ino).thenValue(
[attr](const InodePtr& inode) { return inode->setattr(attr); });
}
void FuseDispatcherImpl::forget(InodeNumber ino, unsigned long nlookup) {
inodeMap_->decFsRefcount(ino, nlookup);
}
folly::Future<uint64_t> FuseDispatcherImpl::open(
InodeNumber /*ino*/,
int /*flags*/) {
#ifdef FUSE_NO_OPEN_SUPPORT
if (getConnInfo().flags & FUSE_NO_OPEN_SUPPORT) {
// If the kernel understands FUSE_NO_OPEN_SUPPORT, then returning ENOSYS
// means that no further open() nor release() calls will make it into Eden.
folly::throwSystemErrorExplicit(
ENOSYS, "Eden open() calls are stateless and not required");
}
#endif
return 0;
}
folly::Future<fuse_entry_out> FuseDispatcherImpl::create(
InodeNumber parent,
PathComponentPiece name,
mode_t mode,
int /*flags*/) {
// force 'mode' to be regular file, in which case rdev arg to mknod is ignored
// (and thus can be zero)
mode = S_IFREG | (07777 & mode);
return inodeMap_->lookupTreeInode(parent).thenValue(
[=](const TreeInodePtr& inode) {
auto childName = PathComponent{name};
auto child = inode->mknod(childName, mode, 0, InvalidationRequired::No);
static auto context = ObjectFetchContext::getNullContextWithCauseDetail(
"FuseDispatcherImpl::create");
return child->stat(*context).thenValue(
[child](struct stat st) -> fuse_entry_out {
child->incFsRefcount();
return computeEntryParam(FuseDispatcher::Attr{st});
});
});
}
folly::Future<BufVec> FuseDispatcherImpl::read(
InodeNumber ino,
size_t size,
off_t off,
ObjectFetchContext& context) {
return inodeMap_->lookupFileInode(ino).thenValue(
[&context, size, off](FileInodePtr&& inode) {
return inode->read(size, off, context);
});
}
folly::Future<size_t>
FuseDispatcherImpl::write(InodeNumber ino, folly::StringPiece data, off_t off) {
return inodeMap_->lookupFileInode(ino).thenValue(
[copy = data.str(), off](FileInodePtr&& inode) {
return inode->write(copy, off);
});
}
Future<Unit> FuseDispatcherImpl::flush(
InodeNumber /* ino */,
uint64_t /* lock_owner */) {
// Return ENOSYS from flush.
// This will cause the kernel to stop sending future flush() calls.
return makeFuture<Unit>(makeSystemErrorExplicit(ENOSYS, "flush"));
}
folly::Future<folly::Unit> FuseDispatcherImpl::fallocate(
InodeNumber ino,
uint64_t offset,
uint64_t length) {
return inodeMap_->lookupFileInode(ino).thenValue(
[offset, length](FileInodePtr inode) {
return inode->fallocate(offset, length);
});
}
folly::Future<folly::Unit> FuseDispatcherImpl::fsync(
InodeNumber ino,
bool datasync) {
return inodeMap_->lookupFileInode(ino).thenValue(
[datasync](FileInodePtr inode) { return inode->fsync(datasync); });
}
Future<Unit> FuseDispatcherImpl::fsyncdir(
InodeNumber /* ino */,
bool /* datasync */) {
// Return ENOSYS from fsyncdir. The kernel will stop sending them.
//
// In a possible future where the tree structure is stored in a SQLite
// database, we could handle this request by waiting for SQLite's
// write-ahead-log to be flushed.
return makeFuture<Unit>(makeSystemErrorExplicit(ENOSYS, "fsyncdir"));
}
folly::Future<std::string> FuseDispatcherImpl::readlink(
InodeNumber ino,
bool kernelCachesReadlink) {
static auto context = ObjectFetchContext::getNullContextWithCauseDetail(
"FuseDispatcherImpl::readlink");
return inodeMap_->lookupFileInode(ino).thenValue(
[kernelCachesReadlink](const FileInodePtr& inode) {
// Only release the symlink blob after it's loaded if we can assume the
// FUSE will cache the result in the kernel's page cache.
return inode->readlink(
*context,
kernelCachesReadlink ? CacheHint::NotNeededAgain
: CacheHint::LikelyNeededAgain);
});
}
folly::Future<DirList> FuseDispatcherImpl::readdir(
InodeNumber ino,
DirList&& dirList,
off_t offset,
uint64_t /*fh*/,
ObjectFetchContext& context) {
return inodeMap_->lookupTreeInode(ino).thenValue(
[dirList = std::move(dirList), offset, &context](
TreeInodePtr inode) mutable {
return inode->readdir(std::move(dirList), offset, context);
});
}
folly::Future<fuse_entry_out> FuseDispatcherImpl::mknod(
InodeNumber parent,
PathComponentPiece name,
mode_t mode,
dev_t rdev) {
static auto context = ObjectFetchContext::getNullContextWithCauseDetail(
"FuseDispatcherImpl::mknod");
return inodeMap_->lookupTreeInode(parent).thenValue(
[childName = PathComponent{name}, mode, rdev](const TreeInodePtr& inode) {
auto child =
inode->mknod(childName, mode, rdev, InvalidationRequired::No);
return child->stat(*context).thenValue(
[child](struct stat st) -> fuse_entry_out {
child->incFsRefcount();
return computeEntryParam(FuseDispatcher::Attr{st});
});
});
}
folly::Future<fuse_entry_out> FuseDispatcherImpl::mkdir(
InodeNumber parent,
PathComponentPiece name,
mode_t mode) {
static auto context = ObjectFetchContext::getNullContextWithCauseDetail(
"FuseDispatcherImpl::mkdir");
return inodeMap_->lookupTreeInode(parent).thenValue(
[childName = PathComponent{name}, mode](const TreeInodePtr& inode) {
auto child = inode->mkdir(childName, mode, InvalidationRequired::No);
return child->stat(*context).thenValue([child](struct stat st) {
child->incFsRefcount();
return computeEntryParam(FuseDispatcher::Attr{st});
});
});
}
folly::Future<folly::Unit> FuseDispatcherImpl::unlink(
InodeNumber parent,
PathComponentPiece name) {
return inodeMap_->lookupTreeInode(parent).thenValue(
[childName = PathComponent{name}](const TreeInodePtr& inode) {
// No need to flush the kernel cache because FUSE will do that for us.
return inode->unlink(childName, InvalidationRequired::No);
});
}
folly::Future<folly::Unit> FuseDispatcherImpl::rmdir(
InodeNumber parent,
PathComponentPiece name) {
return inodeMap_->lookupTreeInode(parent).thenValue(
[childName = PathComponent{name}](const TreeInodePtr& inode) {
// No need to flush the kernel cache because FUSE will do that for us.
return inode->rmdir(childName, InvalidationRequired::No);
});
}
folly::Future<fuse_entry_out> FuseDispatcherImpl::symlink(
InodeNumber parent,
PathComponentPiece name,
StringPiece link) {
static auto context = ObjectFetchContext::getNullContextWithCauseDetail(
"FuseDispatcherImpl::symlink");
return inodeMap_->lookupTreeInode(parent).thenValue(
[linkContents = link.str(),
childName = PathComponent{name}](const TreeInodePtr& inode) {
auto symlinkInode =
inode->symlink(childName, linkContents, InvalidationRequired::No);
symlinkInode->incFsRefcount();
return symlinkInode->stat(*context).thenValue(
[symlinkInode](struct stat st) {
return computeEntryParam(FuseDispatcher::Attr{st});
});
});
}
folly::Future<folly::Unit> FuseDispatcherImpl::rename(
InodeNumber parent,
PathComponentPiece namePiece,
InodeNumber newParent,
PathComponentPiece newNamePiece) {
// Start looking up both parents
auto parentFuture = inodeMap_->lookupTreeInode(parent);
auto newParentFuture = inodeMap_->lookupTreeInode(newParent);
// Do the rename once we have looked up both parents.
return std::move(parentFuture)
.thenValue([npFuture = std::move(newParentFuture),
name = PathComponent{namePiece},
newName = PathComponent{newNamePiece}](
const TreeInodePtr& parent) mutable {
return std::move(npFuture).thenValue(
[parent, name, newName](const TreeInodePtr& newParent) {
return parent->rename(
name, newParent, newName, InvalidationRequired::No);
});
});
}
folly::Future<fuse_entry_out> FuseDispatcherImpl::link(
InodeNumber /*ino*/,
InodeNumber /*newParent*/,
PathComponentPiece newName) {
validatePathComponentLength(newName);
// We intentionally do not support hard links.
// These generally cannot be tracked in source control (git or mercurial)
// and are not portable to non-Unix platforms.
folly::throwSystemErrorExplicit(
EPERM, "hard links are not supported in eden mount points");
}
Future<string> FuseDispatcherImpl::getxattr(InodeNumber ino, StringPiece name) {
return inodeMap_->lookupInode(ino).thenValue(
[attrName = name.str()](const InodePtr& inode) {
return inode->getxattr(attrName);
});
}
Future<vector<string>> FuseDispatcherImpl::listxattr(InodeNumber ino) {
return inodeMap_->lookupInode(ino).thenValue(
[](const InodePtr& inode) { return inode->listxattr(); });
}
folly::Future<struct fuse_kstatfs> FuseDispatcherImpl::statfs(
InodeNumber /*ino*/) {
struct fuse_kstatfs info = {};
// Pass through the overlay free space stats; this gives a more reasonable
// estimation of available storage space than the zeroes that we'd report
// otherwise. This is important because eg: Finder on macOS inspects disk
// space prior to initiating a copy and will refuse to start a copy if
// the disk appears to be full.
auto overlayStats = mount_->getOverlay()->statFs();
info.blocks = overlayStats.f_blocks;
info.bfree = overlayStats.f_bfree;
info.bavail = overlayStats.f_bavail;
info.files = overlayStats.f_files;
info.ffree = overlayStats.f_ffree;
// Suggest a large blocksize to software that looks at that kind of thing
// bsize will be returned to applications that call pathconf() with
// _PC_REC_MIN_XFER_SIZE
info.bsize = getConnInfo().max_readahead;
// The fragment size is returned as the _PC_REC_XFER_ALIGN and
// _PC_ALLOC_SIZE_MIN pathconf() settings.
// 4096 is commonly used by many filesystem types.
info.frsize = 4096;
// Ensure that namelen is set to a non-zero value.
// The value we return here will be visible to programs that call pathconf()
// with _PC_NAME_MAX. Returning 0 will confuse programs that try to honor
// this value.
info.namelen = 255;
return info;
}
} // namespace facebook::eden
#endif

View File

@ -6,35 +6,24 @@
*/
#pragma once
#include "eden/fs/inodes/InodePtr.h"
#include "eden/fs/store/IObjectStore.h"
#ifndef _WIN32
#include "eden/fs/fuse/Dispatcher.h"
#else
#include "eden/fs/prjfs/Dispatcher.h"
#endif
namespace facebook {
namespace eden {
#include "eden/fs/fuse/FuseDispatcher.h"
namespace facebook::eden {
class EdenMount;
class FileInode;
class InodeBase;
class InodeMap;
class TreeInode;
/**
* A FUSE request dispatcher for eden mount points.
* Implement the FuseDispatcher interface.
*
* For unsupported operations, the corresponding methods are explicitly not
* overridden and will directly fail in FuseDispatcher.
*/
class EdenDispatcher : public Dispatcher {
class FuseDispatcherImpl : public FuseDispatcher {
public:
/*
* Create an EdenDispatcher.
* setRootInode() must be called before using this dispatcher.
*/
explicit EdenDispatcher(EdenMount* mount);
explicit FuseDispatcherImpl(EdenMount* mount);
#ifndef _WIN32
folly::Future<struct fuse_kstatfs> statfs(InodeNumber ino) override;
folly::Future<Attr> getattr(InodeNumber ino, ObjectFetchContext& context)
override;
@ -110,78 +99,16 @@ class EdenDispatcher : public Dispatcher {
folly::Future<std::string> getxattr(InodeNumber ino, folly::StringPiece name)
override;
folly::Future<std::vector<std::string>> listxattr(InodeNumber ino) override;
#else
folly::Future<std::vector<FileMetadata>> opendir(
RelativePathPiece path,
ObjectFetchContext& context) override;
folly::Future<std::optional<LookupResult>> lookup(
RelativePath path,
ObjectFetchContext& context) override;
folly::Future<bool> access(RelativePath path, ObjectFetchContext& context)
override;
folly::Future<std::string> read(
RelativePath path,
ObjectFetchContext& context) override;
folly::Future<folly::Unit> newFileCreated(
RelativePath relPath,
RelativePath destPath,
bool isDirectory,
ObjectFetchContext& context) override;
folly::Future<folly::Unit> fileOverwritten(
RelativePath relPath,
RelativePath destPath,
bool isDirectory,
ObjectFetchContext& context) override;
folly::Future<folly::Unit> fileHandleClosedFileModified(
RelativePath relPath,
RelativePath destPath,
bool isDirectory,
ObjectFetchContext& context) override;
folly::Future<folly::Unit> fileRenamed(
RelativePath oldPath,
RelativePath newPath,
bool isDirectory,
ObjectFetchContext& context) override;
folly::Future<folly::Unit> preRename(
RelativePath oldPath,
RelativePath newPath,
bool isDirectory,
ObjectFetchContext& context) override;
folly::Future<folly::Unit> fileHandleClosedFileDeleted(
RelativePath relPath,
RelativePath destPath,
bool isDirectory,
ObjectFetchContext& context) override;
folly::Future<folly::Unit> preSetHardlink(
RelativePath oldPath,
RelativePath newPath,
bool isDirectory,
ObjectFetchContext& context) override;
#endif
private:
// The EdenMount that owns this EdenDispatcher.
// The EdenMount associated with this dispatcher.
EdenMount* const mount_;
#ifndef _WIN32
// The EdenMount's InodeMap.
// We store this pointer purely for convenience. We need it on pretty much
// every FUSE request, and having it locally avoids having to dereference
// We store this pointer purely for convenience. We need it on pretty much
// every FUSE request, and having it locally avoids having to dereference
// mount_ first.
InodeMap* const inodeMap_;
#else
const std::string dotEdenConfig_;
#endif
};
} // namespace eden
} // namespace facebook
} // namespace facebook::eden

View File

@ -20,7 +20,7 @@
#include "eden/fs/utils/PathFuncs.h"
#ifndef _WIN32
#include "eden/fs/fuse/Dispatcher.h"
#include "eden/fs/fuse/FuseDispatcher.h"
#include "eden/fs/inodes/InodeMetadata.h"
#endif
@ -133,7 +133,7 @@ class InodeBase {
#ifndef _WIN32
// See Dispatcher::setattr
virtual folly::Future<Dispatcher::Attr> setattr(
virtual folly::Future<FuseDispatcher::Attr> setattr(
const fuse_setattr_in& attr) = 0;
FOLLY_NODISCARD folly::Future<folly::Unit>

View File

@ -0,0 +1,356 @@
/*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This software may be used and distributed according to the terms of the
* GNU General Public License version 2.
*/
#ifdef _WIN32
#include "eden/fs/inodes/PrjfsDispatcherImpl.h"
#include <cpptoml.h> // @manual=fbsource//third-party/cpptoml:cpptoml
#include <folly/logging/xlog.h>
#include "eden/fs/config/CheckoutConfig.h"
#include "eden/fs/inodes/EdenMount.h"
#include "eden/fs/inodes/FileInode.h"
#include "eden/fs/inodes/ServerState.h"
#include "eden/fs/inodes/TreeInode.h"
#include "eden/fs/store/ObjectFetchContext.h"
#include "eden/fs/utils/PathFuncs.h"
#include "eden/fs/utils/SystemError.h"
#include "eden/fs/utils/UnboundedQueueExecutor.h"
namespace facebook::eden {
namespace {
const RelativePath kDotEdenConfigPath{".eden/config"};
const std::string kConfigRootPath{"root"};
const std::string kConfigSocketPath{"socket"};
const std::string kConfigClientPath{"client"};
const std::string kConfigTable{"Config"};
std::string makeDotEdenConfig(EdenMount& mount) {
auto repoPath = mount.getPath();
auto socketPath = mount.getServerState()->getSocketPath();
auto clientPath = mount.getConfig()->getClientDirectory();
auto rootTable = cpptoml::make_table();
auto configTable = cpptoml::make_table();
configTable->insert(kConfigRootPath, repoPath.c_str());
configTable->insert(kConfigSocketPath, socketPath.c_str());
configTable->insert(kConfigClientPath, clientPath.c_str());
rootTable->insert(kConfigTable, configTable);
std::ostringstream stream;
stream << *rootTable;
return stream.str();
}
} // namespace
PrjfsDispatcherImpl::PrjfsDispatcherImpl(EdenMount* mount)
: PrjfsDispatcher(mount->getStats()),
mount_{mount},
dotEdenConfig_{makeDotEdenConfig(*mount)} {}
folly::Future<std::vector<FileMetadata>> PrjfsDispatcherImpl::opendir(
RelativePathPiece path,
ObjectFetchContext& context) {
return mount_->getInode(path, context).thenValue([](const InodePtr inode) {
auto treePtr = inode.asTreePtr();
return treePtr->readdir();
});
}
folly::Future<std::optional<LookupResult>> PrjfsDispatcherImpl::lookup(
RelativePath path,
ObjectFetchContext& context) {
return mount_->getInode(path, context)
.thenValue(
[&context](const InodePtr inode) mutable
-> folly::Future<std::optional<LookupResult>> {
return inode->stat(context).thenValue(
[inode = std::move(inode)](struct stat&& stat) {
size_t size = stat.st_size;
// Ensure that the OS has a record of the canonical
// file name, and not just whatever case was used to
// lookup the file
auto inodeMetadata =
InodeMetadata{*inode->getPath(), size, inode->isDir()};
auto incFsRefcount = [inode = std::move(inode)] {
inode->incFsRefcount();
};
return LookupResult{
std::move(inodeMetadata), std::move(incFsRefcount)};
});
})
.thenError(
folly::tag_t<std::system_error>{},
[path = std::move(path), this](const std::system_error& ex)
-> folly::Future<std::optional<LookupResult>> {
if (isEnoent(ex)) {
if (path == kDotEdenConfigPath) {
return folly::makeFuture(LookupResult{
InodeMetadata{
std::move(path), dotEdenConfig_.length(), false},
[] {}});
} else {
XLOG(DBG6) << path << ": File not found";
return folly::makeFuture(std::nullopt);
}
}
return folly::makeFuture<std::optional<LookupResult>>(ex);
});
}
folly::Future<bool> PrjfsDispatcherImpl::access(
RelativePath path,
ObjectFetchContext& context) {
return mount_->getInode(path, context)
.thenValue([](const InodePtr) { return true; })
.thenError(
folly::tag_t<std::system_error>{},
[path = std::move(path)](const std::system_error& ex) {
if (isEnoent(ex)) {
if (path == kDotEdenConfigPath) {
return folly::makeFuture(true);
}
return folly::makeFuture(false);
}
return folly::makeFuture<bool>(ex);
});
}
folly::Future<std::string> PrjfsDispatcherImpl::read(
RelativePath path,
ObjectFetchContext& context) {
return mount_->getInode(path, context)
.thenValue([&context](const InodePtr inode) {
auto fileInode = inode.asFilePtr();
return fileInode->readAll(context);
})
.thenError(
folly::tag_t<std::system_error>{},
[path = std::move(path), this](const std::system_error& ex) {
if (isEnoent(ex) && path == kDotEdenConfigPath) {
return folly::makeFuture<std::string>(
std::string(dotEdenConfig_));
}
return folly::makeFuture<std::string>(ex);
});
}
namespace {
folly::Future<TreeInodePtr> createDirInode(
const EdenMount& mount,
const RelativePathPiece path,
ObjectFetchContext& context) {
return mount.getInode(path, context)
.thenValue([](const InodePtr inode) { return inode.asTreePtr(); })
.thenError(
folly::tag_t<std::system_error>{},
[=, &mount](const std::system_error& ex) {
if (!isEnoent(ex)) {
return folly::makeFuture<TreeInodePtr>(ex);
}
mount.getStats()
->getChannelStatsForCurrentThread()
.outOfOrderCreate.addValue(1);
XLOG(DBG2) << "Out of order directory creation notification for: "
<< path;
/*
* ProjectedFS notifications are asynchronous and sent after the
* fact. This means that we can get a notification on a
* file/directory before the parent directory notification has been
* completed. This should be a very rare event and thus the code
* below is pessimistic and will try to create all parent
* directories.
*/
auto fut = folly::makeFuture(mount.getRootInode());
for (auto parent : path.paths()) {
fut = std::move(fut).thenValue([parent](TreeInodePtr treeInode) {
try {
auto inode = treeInode->mkdir(
parent.basename(), _S_IFDIR, InvalidationRequired::No);
inode->incFsRefcount();
} catch (const std::system_error& ex) {
if (ex.code().value() != EEXIST) {
throw;
}
}
return treeInode->getOrLoadChildTree(parent.basename());
});
}
return fut;
});
}
folly::Future<folly::Unit> createFile(
const EdenMount& mount,
const RelativePathPiece path,
bool isDirectory,
ObjectFetchContext& context) {
return createDirInode(mount, path.dirname(), context)
.thenValue([=](const TreeInodePtr treeInode) {
if (isDirectory) {
try {
auto inode = treeInode->mkdir(
path.basename(), _S_IFDIR, InvalidationRequired::No);
inode->incFsRefcount();
} catch (const std::system_error& ex) {
/*
* If a concurrent createFile for a child of this directory finished
* before this one, the directory will already exist. This is not an
* error.
*/
if (ex.code().value() != EEXIST) {
return folly::makeFuture<folly::Unit>(ex);
}
}
} else {
auto inode = treeInode->mknod(
path.basename(), _S_IFREG, 0, InvalidationRequired::No);
inode->incFsRefcount();
}
return folly::makeFuture(folly::unit);
});
}
folly::Future<folly::Unit> materializeFile(
const EdenMount& mount,
const RelativePathPiece path,
ObjectFetchContext& context) {
return mount.getInode(path, context).thenValue([](const InodePtr inode) {
auto fileInode = inode.asFilePtr();
fileInode->materialize();
return folly::unit;
});
}
folly::Future<folly::Unit> renameFile(
const EdenMount& mount,
const RelativePath oldPath,
const RelativePath newPath,
ObjectFetchContext& context) {
auto oldParentInode = createDirInode(mount, oldPath.dirname(), context);
auto newParentInode = createDirInode(mount, newPath.dirname(), context);
return folly::collect(oldParentInode, newParentInode)
.via(mount.getThreadPool().get())
.thenValue([oldPath = std::move(oldPath), newPath = std::move(newPath)](
const std::tuple<TreeInodePtr, TreeInodePtr> inodes) {
auto& oldParentTreePtr = std::get<0>(inodes);
auto& newParentTreePtr = std::get<1>(inodes);
// TODO(xavierd): In the case where the oldPath is actually being
// created in another thread, EdenFS simply might not know about
// it at this point. Creating the file and renaming it at this
// point won't help as the other thread will re-create it. In the
// future, we may want to try, wait a bit and retry, or re-think
// this and somehow order requests so the file creation always
// happens before the rename.
//
// This should be *extremely* rare, for now let's just let it
// error out.
return oldParentTreePtr->rename(
oldPath.basename(),
newParentTreePtr,
newPath.basename(),
InvalidationRequired::No);
});
}
folly::Future<folly::Unit> removeFile(
const EdenMount& mount,
const RelativePathPiece path,
bool isDirectory,
ObjectFetchContext& context) {
return mount.getInode(path.dirname(), context)
.thenValue([=](const InodePtr inode) {
auto treeInodePtr = inode.asTreePtr();
if (isDirectory) {
return treeInodePtr->rmdir(path.basename(), InvalidationRequired::No);
} else {
return treeInodePtr->unlink(
path.basename(), InvalidationRequired::No);
}
});
}
} // namespace
folly::Future<folly::Unit> PrjfsDispatcherImpl::newFileCreated(
RelativePath relPath,
RelativePath /*destPath*/,
bool isDirectory,
ObjectFetchContext& context) {
return createFile(*mount_, relPath, isDirectory, context);
}
folly::Future<folly::Unit> PrjfsDispatcherImpl::fileOverwritten(
RelativePath relPath,
RelativePath /*destPath*/,
bool /*isDirectory*/,
ObjectFetchContext& context) {
return materializeFile(*mount_, relPath, context);
}
folly::Future<folly::Unit> PrjfsDispatcherImpl::fileHandleClosedFileModified(
RelativePath relPath,
RelativePath /*destPath*/,
bool /*isDirectory*/,
ObjectFetchContext& context) {
return materializeFile(*mount_, relPath, context);
}
folly::Future<folly::Unit> PrjfsDispatcherImpl::fileRenamed(
RelativePath oldPath,
RelativePath newPath,
bool isDirectory,
ObjectFetchContext& context) {
// When files are moved in and out of the repo, the rename paths are
// empty, handle these like creation/removal of files.
if (oldPath.empty()) {
return createFile(*mount_, newPath, isDirectory, context);
} else if (newPath.empty()) {
return removeFile(*mount_, oldPath, isDirectory, context);
} else {
return renameFile(*mount_, std::move(oldPath), std::move(newPath), context);
}
}
folly::Future<folly::Unit> PrjfsDispatcherImpl::preRename(
RelativePath oldPath,
RelativePath newPath,
bool /*isDirectory*/,
ObjectFetchContext& /*context*/) {
return folly::unit;
}
folly::Future<folly::Unit> PrjfsDispatcherImpl::fileHandleClosedFileDeleted(
RelativePath oldPath,
RelativePath /*destPath*/,
bool isDirectory,
ObjectFetchContext& context) {
return removeFile(*mount_, oldPath, isDirectory, context);
}
folly::Future<folly::Unit> PrjfsDispatcherImpl::preSetHardlink(
RelativePath relPath,
RelativePath /*destPath*/,
bool /*isDirectory*/,
ObjectFetchContext& /*context*/) {
return folly::makeFuture<folly::Unit>(makeHResultErrorExplicit(
HRESULT_FROM_WIN32(ERROR_ACCESS_DENIED),
fmt::format(FMT_STRING("Hardlinks are not supported: {}"), relPath)));
}
} // namespace facebook::eden
#endif

View File

@ -0,0 +1,84 @@
/*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This software may be used and distributed according to the terms of the
* GNU General Public License version 2.
*/
#pragma once
#include "eden/fs/prjfs/PrjfsDispatcher.h"
namespace facebook::eden {
class EdenMount;
class PrjfsDispatcherImpl : public PrjfsDispatcher {
public:
explicit PrjfsDispatcherImpl(EdenMount* mount);
folly::Future<std::vector<FileMetadata>> opendir(
RelativePathPiece path,
ObjectFetchContext& context) override;
folly::Future<std::optional<LookupResult>> lookup(
RelativePath path,
ObjectFetchContext& context) override;
folly::Future<bool> access(RelativePath path, ObjectFetchContext& context)
override;
folly::Future<std::string> read(
RelativePath path,
ObjectFetchContext& context) override;
folly::Future<folly::Unit> newFileCreated(
RelativePath relPath,
RelativePath destPath,
bool isDirectory,
ObjectFetchContext& context) override;
folly::Future<folly::Unit> fileOverwritten(
RelativePath relPath,
RelativePath destPath,
bool isDirectory,
ObjectFetchContext& context) override;
folly::Future<folly::Unit> fileHandleClosedFileModified(
RelativePath relPath,
RelativePath destPath,
bool isDirectory,
ObjectFetchContext& context) override;
folly::Future<folly::Unit> fileRenamed(
RelativePath oldPath,
RelativePath newPath,
bool isDirectory,
ObjectFetchContext& context) override;
folly::Future<folly::Unit> preRename(
RelativePath oldPath,
RelativePath newPath,
bool isDirectory,
ObjectFetchContext& context) override;
folly::Future<folly::Unit> fileHandleClosedFileDeleted(
RelativePath relPath,
RelativePath destPath,
bool isDirectory,
ObjectFetchContext& context) override;
folly::Future<folly::Unit> preSetHardlink(
RelativePath oldPath,
RelativePath newPath,
bool isDirectory,
ObjectFetchContext& context) override;
private:
// The EdenMount associated with this dispatcher.
EdenMount* const mount_;
const std::string dotEdenConfig_;
};
} // namespace facebook::eden

View File

@ -204,8 +204,8 @@ folly::Future<struct stat> TreeInode::stat(ObjectFetchContext& /*context*/) {
}
#ifndef _WIN32
Dispatcher::Attr TreeInode::getAttrLocked(const DirContents& contents) {
Dispatcher::Attr attr(getMount()->initStatData());
FuseDispatcher::Attr TreeInode::getAttrLocked(const DirContents& contents) {
FuseDispatcher::Attr attr(getMount()->initStatData());
attr.st.st_ino = getNodeId().get();
getMetadataLocked(contents).applyToStat(attr.st);
@ -3555,10 +3555,10 @@ void TreeInode::prefetch(ObjectFetchContext& context) {
});
}
folly::Future<Dispatcher::Attr> TreeInode::setattr(
folly::Future<FuseDispatcher::Attr> TreeInode::setattr(
const fuse_setattr_in& attr) {
materialize();
Dispatcher::Attr result(getMount()->initStatData());
FuseDispatcher::Attr result(getMount()->initStatData());
// We do not have size field for directories and currently TreeInode does not
// have any field like FileInode::state_::mode to set the mode. May be in the

View File

@ -111,12 +111,13 @@ class TreeInode final : public InodeBaseMetadata<DirContents> {
folly::Future<struct stat> stat(ObjectFetchContext& context) override;
#ifndef _WIN32
folly::Future<Dispatcher::Attr> setattr(const fuse_setattr_in& attr) override;
folly::Future<FuseDispatcher::Attr> setattr(
const fuse_setattr_in& attr) override;
folly::Future<std::vector<std::string>> listxattr() override;
folly::Future<std::string> getxattr(folly::StringPiece name) override;
Dispatcher::Attr getAttrLocked(const DirContents& contents);
FuseDispatcher::Attr getAttrLocked(const DirContents& contents);
#endif // !_WIN32
/**

View File

@ -7,7 +7,7 @@
#ifndef _WIN32
#include "eden/fs/inodes/EdenDispatcher.h"
#include "eden/fs/fuse/FuseDispatcher.h"
#include <folly/experimental/TestUtil.h>
#include <folly/test/TestUtils.h>

View File

@ -72,10 +72,10 @@ bool operator==(const timespec& ts, std::chrono::system_clock::time_point tp) {
namespace {
Dispatcher::Attr getFileAttr(const FileInodePtr& inode) {
FuseDispatcher::Attr getFileAttr(const FileInodePtr& inode) {
auto attrFuture =
inode->stat(ObjectFetchContext::getNullContext())
.thenValue([](struct stat st) { return Dispatcher::Attr{st}; });
.thenValue([](struct stat st) { return FuseDispatcher::Attr{st}; });
// We unfortunately can't use an ASSERT_* check here, since it tries
// to return from the function normally, rather than throwing.
if (!attrFuture.isReady()) {
@ -87,7 +87,7 @@ Dispatcher::Attr getFileAttr(const FileInodePtr& inode) {
return std::move(attrFuture).get();
}
Dispatcher::Attr setFileAttr(
FuseDispatcher::Attr setFileAttr(
const FileInodePtr& inode,
const fuse_setattr_in& desired) {
auto attrFuture = inode->setattr(desired);
@ -101,7 +101,9 @@ Dispatcher::Attr setFileAttr(
/**
* Helper function used by BASIC_ATTR_XCHECKS()
*/
void basicAttrChecks(const FileInodePtr& inode, const Dispatcher::Attr& attr) {
void basicAttrChecks(
const FileInodePtr& inode,
const FuseDispatcher::Attr& attr) {
EXPECT_EQ(inode->getNodeId().getRawValue(), attr.st.st_ino);
EXPECT_EQ(1, attr.st.st_nlink);
EXPECT_EQ(inode->getMount()->getOwner().uid, attr.st.st_uid);

View File

@ -10,7 +10,7 @@
#include "eden/fs/prjfs/PrjfsChannel.h"
#include <fmt/format.h>
#include <folly/logging/xlog.h>
#include "eden/fs/prjfs/Dispatcher.h"
#include "eden/fs/prjfs/PrjfsDispatcher.h"
#include "eden/fs/prjfs/PrjfsRequestContext.h"
#include "eden/fs/utils/Bug.h"
#include "eden/fs/utils/Guid.h"
@ -154,12 +154,12 @@ HRESULT notification(
} // namespace
PrjfsChannelInner::PrjfsChannelInner(
Dispatcher* const dispatcher,
std::unique_ptr<PrjfsDispatcher> dispatcher,
const folly::Logger* straceLogger,
ProcessAccessLog& processAccessLog,
folly::Duration requestTimeout,
Notifications* notifications)
: dispatcher_(dispatcher),
: dispatcher_(std::move(dispatcher)),
straceLogger_(straceLogger),
processAccessLog_(processAccessLog),
requestTimeout_(requestTimeout),
@ -579,7 +579,7 @@ HRESULT PrjfsChannelInner::getFileData(
}
namespace {
typedef folly::Future<folly::Unit> (Dispatcher::*NotificationHandler)(
typedef folly::Future<folly::Unit> (PrjfsDispatcher::*NotificationHandler)(
RelativePath oldPath,
RelativePath destPath,
bool isDirectory,
@ -657,43 +657,43 @@ const std::unordered_map<PRJ_NOTIFICATION, NotificationHandlerEntry>
notificationHandlerMap = {
{
PRJ_NOTIFICATION_NEW_FILE_CREATED,
{&Dispatcher::newFileCreated,
{&PrjfsDispatcher::newFileCreated,
newFileCreatedRenderer,
&ChannelThreadStats::newFileCreated},
},
{
PRJ_NOTIFICATION_FILE_OVERWRITTEN,
{&Dispatcher::fileOverwritten,
{&PrjfsDispatcher::fileOverwritten,
fileOverwrittenRenderer,
&ChannelThreadStats::fileOverwritten},
},
{
PRJ_NOTIFICATION_FILE_HANDLE_CLOSED_FILE_MODIFIED,
{&Dispatcher::fileHandleClosedFileModified,
{&PrjfsDispatcher::fileHandleClosedFileModified,
fileHandleClosedFileModifiedRenderer,
&ChannelThreadStats::fileHandleClosedFileModified},
},
{
PRJ_NOTIFICATION_FILE_RENAMED,
{&Dispatcher::fileRenamed,
{&PrjfsDispatcher::fileRenamed,
fileRenamedRenderer,
&ChannelThreadStats::fileRenamed},
},
{
PRJ_NOTIFICATION_PRE_RENAME,
{&Dispatcher::preRename,
{&PrjfsDispatcher::preRename,
preRenamedRenderer,
&ChannelThreadStats::preRenamed},
},
{
PRJ_NOTIFICATION_FILE_HANDLE_CLOSED_FILE_DELETED,
{&Dispatcher::fileHandleClosedFileDeleted,
{&PrjfsDispatcher::fileHandleClosedFileDeleted,
fileHandleClosedFileDeletedRenderer,
&ChannelThreadStats::fileHandleClosedFileDeleted},
},
{
PRJ_NOTIFICATION_PRE_SET_HARDLINK,
{&Dispatcher::preSetHardlink,
{&PrjfsDispatcher::preSetHardlink,
preSetHardlinkRenderer,
&ChannelThreadStats::preSetHardlink},
},
@ -732,7 +732,7 @@ HRESULT PrjfsChannelInner::notification(
context->startRequest(dispatcher_->getStats(), histogram, requestWatch);
FB_LOG(getStraceLogger(), DBG7, renderer(relPath, destPath, isDirectory));
return (dispatcher_->*handler)(
return (dispatcher_.get()->*handler)(
std::move(relPath), std::move(destPath), isDirectory, *context)
.thenValue([context = std::move(context)](auto&&) {
context->sendNotificationSuccess();
@ -776,7 +776,7 @@ folly::Indestructible<folly::rcu_domain<detail::RcuTag>> prjfsRcuDomain;
PrjfsChannel::PrjfsChannel(
AbsolutePathPiece mountPath,
Dispatcher* const dispatcher,
std::unique_ptr<PrjfsDispatcher> dispatcher,
const folly::Logger* straceLogger,
std::shared_ptr<ProcessNameCache> processNameCache,
folly::Duration requestTimeout,
@ -787,7 +787,7 @@ PrjfsChannel::PrjfsChannel(
processAccessLog_(std::move(processNameCache)),
inner_(
*prjfsRcuDomain,
dispatcher,
std::move(dispatcher),
straceLogger,
processAccessLog_,
requestTimeout,

View File

@ -13,6 +13,7 @@
#include <ProjectedFSLib.h> // @manual
#include "eden/fs/prjfs/Enumerator.h"
#include "eden/fs/prjfs/PrjfsDispatcher.h"
#include "eden/fs/utils/Guid.h"
#include "eden/fs/utils/PathFuncs.h"
#include "eden/fs/utils/ProcessAccessLog.h"
@ -21,7 +22,6 @@
namespace facebook {
namespace eden {
class EdenMount;
class Dispatcher;
class Notifications;
class PrjfsChannelInner;
class PrjfsRequestContext;
@ -34,7 +34,7 @@ using RcuLockedPtr = RcuPtr<PrjfsChannelInner, RcuTag>::RcuLockedPtr;
class PrjfsChannelInner {
public:
PrjfsChannelInner(
Dispatcher* const dispatcher,
std::unique_ptr<PrjfsDispatcher> dispatcher,
const folly::Logger* straceLogger,
ProcessAccessLog& processAccessLog,
folly::Duration requestTimeout,
@ -165,7 +165,7 @@ class PrjfsChannelInner {
// Internal ProjectedFS channel used to communicate with ProjectedFS.
PRJ_NAMESPACE_VIRTUALIZATION_CONTEXT mountChannel_{nullptr};
Dispatcher* const dispatcher_{nullptr};
std::unique_ptr<PrjfsDispatcher> dispatcher_;
const folly::Logger* const straceLogger_{nullptr};
// The processAccessLog_ is owned by PrjfsChannel which is guaranteed to have
@ -189,7 +189,7 @@ class PrjfsChannel {
PrjfsChannel(
AbsolutePathPiece mountPath,
Dispatcher* const dispatcher,
std::unique_ptr<PrjfsDispatcher> dispatcher,
const folly::Logger* straceLogger,
std::shared_ptr<ProcessNameCache> processNameCache,
folly::Duration requestTimeout,

View File

@ -7,14 +7,14 @@
#ifdef _WIN32
#include "eden/fs/prjfs/Dispatcher.h"
#include "eden/fs/prjfs/PrjfsDispatcher.h"
namespace facebook::eden {
Dispatcher::~Dispatcher() {}
PrjfsDispatcher::~PrjfsDispatcher() {}
Dispatcher::Dispatcher(EdenStats* stats) : stats_(stats) {}
PrjfsDispatcher::PrjfsDispatcher(EdenStats* stats) : stats_(stats) {}
EdenStats* Dispatcher::getStats() const {
EdenStats* PrjfsDispatcher::getStats() const {
return stats_;
}
} // namespace facebook::eden

View File

@ -38,10 +38,10 @@ struct LookupResult {
std::function<void()> incFsRefcount;
};
class Dispatcher {
class PrjfsDispatcher {
public:
virtual ~Dispatcher();
explicit Dispatcher(EdenStats* stats);
virtual ~PrjfsDispatcher();
explicit PrjfsDispatcher(EdenStats* stats);
EdenStats* getStats() const;

View File

@ -14,12 +14,11 @@
#include <condition_variable>
#include <unordered_map>
#include "eden/fs/fuse/Dispatcher.h"
#include "eden/fs/fuse/FuseDispatcher.h"
#include "eden/fs/store/ObjectStore.h"
#include "eden/fs/utils/PathFuncs.h"
namespace facebook {
namespace eden {
namespace facebook::eden {
/**
* A FUSE Dispatcher implementation for use in unit tests.
@ -27,7 +26,7 @@ namespace eden {
* It allows the test code to generate responses to specific requests on
* demand.
*/
class TestDispatcher : public Dispatcher {
class TestDispatcher : public FuseDispatcher {
public:
/**
* Data for a pending FUSE_LOOKUP request.
@ -41,7 +40,7 @@ class TestDispatcher : public Dispatcher {
folly::Promise<fuse_entry_out> promise;
};
using Dispatcher::Dispatcher;
using FuseDispatcher::FuseDispatcher;
folly::Future<fuse_entry_out> lookup(
uint64_t requestID,
@ -68,5 +67,4 @@ class TestDispatcher : public Dispatcher {
std::condition_variable requestReceived_;
};
} // namespace eden
} // namespace facebook
} // namespace facebook::eden

View File

@ -19,7 +19,7 @@
#include <sys/types.h>
#include "eden/fs/config/CheckoutConfig.h"
#include "eden/fs/config/EdenConfig.h"
#include "eden/fs/inodes/EdenDispatcher.h"
#include "eden/fs/inodes/EdenDispatcherFactory.h"
#include "eden/fs/inodes/FileInode.h"
#include "eden/fs/inodes/Overlay.h"
#include "eden/fs/inodes/TreeInode.h"
@ -209,6 +209,9 @@ void TestMount::createMount() {
blobCache_,
serverState_,
std::move(journal));
#ifndef _WIN32
dispatcher_ = EdenDispatcherFactory::makeFuseDispatcher(edenMount_.get());
#endif
}
#ifndef _WIN32
@ -256,8 +259,8 @@ void TestMount::initTestDirectory() {
}
#ifndef _WIN32
Dispatcher* TestMount::getDispatcher() const {
return edenMount_->getDispatcher();
FuseDispatcher* TestMount::getDispatcher() const {
return dispatcher_.get();
}
void TestMount::startFuseAndWait(std::shared_ptr<FakeFuse> fuse) {

View File

@ -191,7 +191,7 @@ class TestMount {
}
#ifndef _WIN32
Dispatcher* getDispatcher() const;
FuseDispatcher* getDispatcher() const;
#endif // !_WIN32
/**
@ -362,6 +362,9 @@ class TestMount {
std::optional<folly::test::TemporaryDirectory> testDir_;
std::shared_ptr<EdenMount> edenMount_;
#ifndef _WIN32
std::unique_ptr<FuseDispatcher> dispatcher_;
#endif
std::shared_ptr<LocalStore> localStore_;
std::shared_ptr<FakeBackingStore> backingStore_;
std::shared_ptr<EdenStats> stats_;