/* * 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 #include #include #include "eden/fs/fuse/FuseChannel.h" #include "eden/fs/fuse/FuseTypes.h" #include "eden/fs/inodes/RequestContext.h" #include "eden/fs/store/ImportPriority.h" #include "eden/fs/store/ObjectFetchContext.h" #include "eden/fs/telemetry/EdenStats.h" #include "eden/fs/telemetry/RequestMetricsScope.h" namespace facebook { namespace eden { /** * Each FUSE request has a corresponding FuseRequestContext object that is * allocated at request start and deallocated when it finishes. * * Unless a member function indicates otherwise, FuseRequestContext may be used * from multiple threads, but only by one thread at a time. */ class FuseRequestContext : public RequestContext { public: FuseRequestContext(const FuseRequestContext&) = delete; FuseRequestContext& operator=(const FuseRequestContext&) = delete; FuseRequestContext(FuseRequestContext&&) = delete; FuseRequestContext& operator=(FuseRequestContext&&) = delete; explicit FuseRequestContext( FuseChannel* channel, const fuse_in_header& fuseHeader); // Override of `ObjectFetchContext` std::optional getClientPid() const override { return static_cast(fuseHeader_.pid); } /** * After sendReply or replyError, this returns the error code we returned to * the kernel, negated. * * After sendReplyWithInode, this returns the inode number that the kernel * will reference until it sends FORGET. */ const std::optional& getResult() const { return result_; } /** * Returns the underlying fuse request, throwing an error if it has * already been released */ const fuse_in_header& getReq() const; /** * Returns the underlying fuse request. Unlike getReq this function doesn't * throw. The caller is responsible to verify that the fuse_in_header is * valid by checking if (fuseHeader.opcode != 0) */ const fuse_in_header& examineReq() const; /** * Append error handling clauses to a future chain. These clauses result in * reporting a fuse request error back to the kernel. */ folly::Future catchErrors( folly::Future&& fut, Notifications* FOLLY_NULLABLE notifications) { return std::move(fut).thenTryInline([this, notifications]( folly::Try&& try_) { SCOPE_EXIT { finishRequest(); }; if (try_.hasException()) { if (auto* err = try_.tryGetExceptionObject()) { timeoutErrorHandler(*err, notifications); } else if ( auto* err = try_.tryGetExceptionObject()) { systemErrorHandler(*err, notifications); } else if (auto* err = try_.tryGetExceptionObject()) { genericErrorHandler(*err, notifications); } else { genericErrorHandler( std::runtime_error{"unknown exception type"}, notifications); } } }); } void systemErrorHandler( const std::system_error& err, Notifications* FOLLY_NULLABLE notifications); void genericErrorHandler( const std::exception& err, Notifications* FOLLY_NULLABLE notifications); void timeoutErrorHandler( const folly::FutureTimeout& err, Notifications* FOLLY_NULLABLE notifications); template void sendReply(T&&... payload) { result_ = 0; channel_->sendReply(stealReq(), std::forward(payload)...); } /** * Same as sendReply, but is called when the kernel will take a reference to * the returned inode. The returned inode value will be logged to make trace * logs more useful. */ template void sendReplyWithInode(uint64_t nodeid, T&& reply) { result_ = nodeid; channel_->sendReply(stealReq(), std::forward(reply)); } // Reply with a negative errno value or 0 for success void replyError(int err); // Don't send a reply, just release req_ void replyNone(); private: fuse_in_header stealReq(); FuseChannel* channel_; fuse_in_header fuseHeader_; std::optional result_; }; } // namespace eden } // namespace facebook