diff --git a/eden/fs/inodes/NfsDispatcherImpl.cpp b/eden/fs/inodes/NfsDispatcherImpl.cpp index b06707c95e..8a1dfb11f2 100644 --- a/eden/fs/inodes/NfsDispatcherImpl.cpp +++ b/eden/fs/inodes/NfsDispatcherImpl.cpp @@ -29,6 +29,32 @@ folly::Future NfsDispatcherImpl::getattr( [&context](const InodePtr& inode) { return inode->stat(context); }); } +folly::Future NfsDispatcherImpl::getParent( + InodeNumber ino, + ObjectFetchContext& /*context*/) { + return inodeMap_->lookupTreeInode(ino).thenValue( + [](const TreeInodePtr& inode) { + return inode->getParentRacy()->getNodeId(); + }); +} + +folly::Future> 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 { + return {ino, stat}; + }); + }); +} + } // namespace facebook::eden #endif diff --git a/eden/fs/inodes/NfsDispatcherImpl.h b/eden/fs/inodes/NfsDispatcherImpl.h index cdfaad59a3..0959b77389 100644 --- a/eden/fs/inodes/NfsDispatcherImpl.h +++ b/eden/fs/inodes/NfsDispatcherImpl.h @@ -23,6 +23,15 @@ class NfsDispatcherImpl : public NfsDispatcher { InodeNumber ino, ObjectFetchContext& context) override; + folly::Future getParent( + InodeNumber ino, + ObjectFetchContext& context) override; + + folly::Future> lookup( + InodeNumber dir, + PathComponent name, + ObjectFetchContext& context) override; + private: // The EdenMount associated with this dispatcher. EdenMount* const mount_; diff --git a/eden/fs/nfs/NfsDispatcher.h b/eden/fs/nfs/NfsDispatcher.h index ffe68026a7..4dcabc9399 100644 --- a/eden/fs/nfs/NfsDispatcher.h +++ b/eden/fs/nfs/NfsDispatcher.h @@ -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 @@ -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 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> + lookup(InodeNumber dir, PathComponent name, ObjectFetchContext& context) = 0; + private: EdenStats* stats_{nullptr}; }; diff --git a/eden/fs/nfs/Nfsd3.cpp b/eden/fs/nfs/Nfsd3.cpp index f181d9393a..119dff4b1b 100644 --- a/eden/fs/nfs/Nfsd3.cpp +++ b/eden/fs/nfs/Nfsd3.cpp @@ -268,11 +268,103 @@ folly::Future Nfsd3ServerProcessor::setattr( } folly::Future 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::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&& try_) mutable { + if (try_.hasException()) { + LOOKUP3res res{ + {nfsstat3::NFS3ERR_NAMETOOLONG, + LOOKUP3resfail{post_op_attr{{false, std::monostate{}}}}}}; + XdrTrait::serialize(ser, res); + } else { + LOOKUP3res res{ + {nfsstat3::NFS3ERR_NAMETOOLONG, + LOOKUP3resfail{ + post_op_attr{{true, statToFattr3(try_.value())}}}}}; + XdrTrait::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 { + 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 { + 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>&& + lookupTry) mutable { + return std::move(dirAttrFut) + .thenTry([ser = std::move(ser), lookupTry = std::move(lookupTry)]( + folly::Try&& 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::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::serialize(ser, res); + } + return folly::unit; + }); + }); } uint32_t getEffectiveAccessRights( @@ -300,7 +392,7 @@ folly::Future Nfsd3ServerProcessor::access( folly::Try&& try_) mutable { if (try_.hasException()) { ACCESS3res res{ - {nfsstat3::NFS3ERR_IO, + {exceptionToNfsError(try_.exception()), ACCESS3resfail{post_op_attr{{false, std::monostate{}}}}}}; XdrTrait::serialize(ser, res); } else {