mirror of
https://github.com/facebook/sapling.git
synced 2024-10-05 14:28:17 +03:00
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:
parent
b842d11153
commit
62076b545e
@ -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());
|
||||
});
|
||||
}
|
||||
|
@ -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_;
|
||||
|
@ -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_;
|
||||
}
|
||||
|
@ -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
|
@ -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>()));
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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
|
30
eden/fs/inodes/EdenDispatcherFactory.cpp
Normal file
30
eden/fs/inodes/EdenDispatcherFactory.cpp
Normal 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
|
29
eden/fs/inodes/EdenDispatcherFactory.h
Normal file
29
eden/fs/inodes/EdenDispatcherFactory.h
Normal 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
|
@ -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>(
|
||||
|
@ -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_;
|
||||
|
@ -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";
|
||||
|
@ -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(
|
||||
|
446
eden/fs/inodes/FuseDispatcherImpl.cpp
Normal file
446
eden/fs/inodes/FuseDispatcherImpl.cpp
Normal 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
|
@ -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
|
@ -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>
|
||||
|
356
eden/fs/inodes/PrjfsDispatcherImpl.cpp
Normal file
356
eden/fs/inodes/PrjfsDispatcherImpl.cpp
Normal 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
|
84
eden/fs/inodes/PrjfsDispatcherImpl.h
Normal file
84
eden/fs/inodes/PrjfsDispatcherImpl.h
Normal 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
|
@ -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
|
||||
|
@ -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
|
||||
|
||||
/**
|
||||
|
@ -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>
|
||||
|
@ -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);
|
||||
|
@ -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,
|
||||
|
@ -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,
|
||||
|
@ -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
|
@ -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;
|
||||
|
@ -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
|
||||
|
@ -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) {
|
||||
|
@ -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_;
|
||||
|
Loading…
Reference in New Issue
Block a user