nfs: implement LOOKUP RPC

Summary:
The LOOKUP RPC is a bit tricky as we need to special case the "." and ".."
names to represent the current and parent directories. Obtaining the parent
directory is inherently racy from the client perspective, and thus EdenFS won't
try to hold the renameLock in this case.

Reviewed By: kmancini

Differential Revision: D26597789

fbshipit-source-id: 9c8dcaf7db84f8c09f7505cab7afed48df79b754
This commit is contained in:
Xavier Deguillard 2021-02-25 18:49:16 -08:00 committed by Facebook GitHub Bot
parent 9cb5d833a2
commit 11b657d27d
4 changed files with 148 additions and 4 deletions

View File

@ -29,6 +29,32 @@ folly::Future<struct stat> NfsDispatcherImpl::getattr(
[&context](const InodePtr& inode) { return inode->stat(context); });
}
folly::Future<InodeNumber> NfsDispatcherImpl::getParent(
InodeNumber ino,
ObjectFetchContext& /*context*/) {
return inodeMap_->lookupTreeInode(ino).thenValue(
[](const TreeInodePtr& inode) {
return inode->getParentRacy()->getNodeId();
});
}
folly::Future<std::tuple<InodeNumber, struct stat>> NfsDispatcherImpl::lookup(
InodeNumber dir,
PathComponent name,
ObjectFetchContext& context) {
return inodeMap_->lookupTreeInode(dir)
.thenValue([name = std::move(name), &context](const TreeInodePtr& inode) {
return inode->getOrLoadChild(name, context);
})
.thenValue([&context](const InodePtr& inode) {
return inode->stat(context).thenValue(
[ino = inode->getNodeId()](
struct stat stat) -> std::tuple<InodeNumber, struct stat> {
return {ino, stat};
});
});
}
} // namespace facebook::eden
#endif

View File

@ -23,6 +23,15 @@ class NfsDispatcherImpl : public NfsDispatcher {
InodeNumber ino,
ObjectFetchContext& context) override;
folly::Future<InodeNumber> getParent(
InodeNumber ino,
ObjectFetchContext& context) override;
folly::Future<std::tuple<InodeNumber, struct stat>> lookup(
InodeNumber dir,
PathComponent name,
ObjectFetchContext& context) override;
private:
// The EdenMount associated with this dispatcher.
EdenMount* const mount_;

View File

@ -13,6 +13,7 @@
#include "eden/fs/inodes/InodeNumber.h"
#include "eden/fs/store/ObjectFetchContext.h"
#include "eden/fs/utils/PathFuncs.h"
namespace folly {
template <class T>
@ -40,6 +41,22 @@ class NfsDispatcher {
InodeNumber ino,
ObjectFetchContext& context) = 0;
/**
* Racily obtain the parent directory of the passed in directory.
*
* Can be used to handle a ".." filename.
*/
virtual folly::Future<InodeNumber> getParent(
InodeNumber ino,
ObjectFetchContext& context) = 0;
/**
* Find the given file in the passed in directory. It's InodeNumber and
* attributes are returned.
*/
virtual folly::Future<std::tuple<InodeNumber, struct stat>>
lookup(InodeNumber dir, PathComponent name, ObjectFetchContext& context) = 0;
private:
EdenStats* stats_{nullptr};
};

View File

@ -268,11 +268,103 @@ folly::Future<folly::Unit> Nfsd3ServerProcessor::setattr(
}
folly::Future<folly::Unit> Nfsd3ServerProcessor::lookup(
folly::io::Cursor /*deser*/,
folly::io::Cursor deser,
folly::io::Appender ser,
uint32_t xid) {
serializeReply(ser, accept_stat::PROC_UNAVAIL, xid);
return folly::unit;
serializeReply(ser, accept_stat::SUCCESS, xid);
auto args = XdrTrait<LOOKUP3args>::deserialize(deser);
// TODO(xavierd): make an NfsRequestContext.
static auto context =
ObjectFetchContext::getNullContextWithCauseDetail("lookup");
// TODO(xavierd): the lifetime of this future is a bit tricky and it needs to
// be consumed in this function to avoid use-after-free. This future may also
// need to be executed after the lookup call to conform to fill the "post-op"
// attributes
auto dirAttrFut = dispatcher_->getattr(args.what.dir.ino, *context);
if (args.what.name.length() > NAME_MAX) {
// The filename is too long, let's try to get the attributes of the
// directory and fail.
return std::move(dirAttrFut)
.thenTry(
[ser = std::move(ser)](folly::Try<struct stat>&& try_) mutable {
if (try_.hasException()) {
LOOKUP3res res{
{nfsstat3::NFS3ERR_NAMETOOLONG,
LOOKUP3resfail{post_op_attr{{false, std::monostate{}}}}}};
XdrTrait<LOOKUP3res>::serialize(ser, res);
} else {
LOOKUP3res res{
{nfsstat3::NFS3ERR_NAMETOOLONG,
LOOKUP3resfail{
post_op_attr{{true, statToFattr3(try_.value())}}}}};
XdrTrait<LOOKUP3res>::serialize(ser, res);
}
return folly::unit;
});
}
return folly::makeFutureWith([this, args = std::move(args)]() mutable {
if (args.what.name == ".") {
return dispatcher_->getattr(args.what.dir.ino, *context)
.thenValue(
[ino = args.what.dir.ino](struct stat && stat)
-> std::tuple<InodeNumber, struct stat> {
return {ino, std::move(stat)};
});
} else if (args.what.name == "..") {
return dispatcher_->getParent(args.what.dir.ino, *context)
.thenValue([this](InodeNumber ino) {
return dispatcher_->getattr(ino, *context)
.thenValue(
[ino](struct stat && stat)
-> std::tuple<InodeNumber, struct stat> {
return {ino, std::move(stat)};
});
});
} else {
return dispatcher_->lookup(
args.what.dir.ino, PathComponent(args.what.name), *context);
}
})
.thenTry([ser = std::move(ser), dirAttrFut = std::move(dirAttrFut)](
folly::Try<std::tuple<InodeNumber, struct stat>>&&
lookupTry) mutable {
return std::move(dirAttrFut)
.thenTry([ser = std::move(ser), lookupTry = std::move(lookupTry)](
folly::Try<struct stat>&& dirStat) mutable {
auto dirStatToPostOpAttr = [&]() {
if (dirStat.hasException()) {
return post_op_attr{{false, std::monostate{}}};
} else {
return post_op_attr{{true, statToFattr3(dirStat.value())}};
}
};
if (lookupTry.hasException()) {
LOOKUP3res res{
{exceptionToNfsError(lookupTry.exception()),
LOOKUP3resfail{dirStatToPostOpAttr()}}};
XdrTrait<LOOKUP3res>::serialize(ser, res);
} else {
auto& [ino, stat] = lookupTry.value();
LOOKUP3res res{
{nfsstat3::NFS3_OK,
LOOKUP3resok{
/*object*/ nfs_fh3{ino},
/*obj_attributes*/
post_op_attr{{true, statToFattr3(stat)}},
/*dir_attributes*/ dirStatToPostOpAttr(),
}}};
XdrTrait<LOOKUP3res>::serialize(ser, res);
}
return folly::unit;
});
});
}
uint32_t getEffectiveAccessRights(
@ -300,7 +392,7 @@ folly::Future<folly::Unit> Nfsd3ServerProcessor::access(
folly::Try<struct stat>&& try_) mutable {
if (try_.hasException()) {
ACCESS3res res{
{nfsstat3::NFS3ERR_IO,
{exceptionToNfsError(try_.exception()),
ACCESS3resfail{post_op_attr{{false, std::monostate{}}}}}};
XdrTrait<ACCESS3res>::serialize(ser, res);
} else {