2017-09-09 05:15:17 +03:00
|
|
|
/*
|
2019-06-20 02:58:25 +03:00
|
|
|
* Copyright (c) Facebook, Inc. and its affiliates.
|
2017-09-09 05:15:17 +03:00
|
|
|
*
|
2019-06-20 02:58:25 +03:00
|
|
|
* This software may be used and distributed according to the terms of the
|
|
|
|
* GNU General Public License version 2.
|
2017-09-09 05:15:17 +03:00
|
|
|
*/
|
|
|
|
#pragma once
|
2019-08-01 23:34:28 +03:00
|
|
|
|
2017-09-09 05:15:17 +03:00
|
|
|
#include <folly/File.h>
|
2018-11-07 19:56:49 +03:00
|
|
|
#include <folly/Range.h>
|
2018-03-13 22:52:48 +03:00
|
|
|
#include <folly/Synchronized.h>
|
2018-01-03 03:25:05 +03:00
|
|
|
#include <folly/futures/Future.h>
|
2018-01-03 20:26:45 +03:00
|
|
|
#include <folly/futures/Promise.h>
|
2019-01-12 02:08:35 +03:00
|
|
|
#include <folly/synchronization/CallOnce.h>
|
2018-01-03 03:25:03 +03:00
|
|
|
#include <stdlib.h>
|
|
|
|
#include <sys/uio.h>
|
2018-03-27 21:12:59 +03:00
|
|
|
#include <condition_variable>
|
|
|
|
#include <iosfwd>
|
2017-09-09 05:15:17 +03:00
|
|
|
#include <memory>
|
2018-10-24 04:48:38 +03:00
|
|
|
#include <optional>
|
2019-03-12 06:45:20 +03:00
|
|
|
#include <stdexcept>
|
2017-09-09 05:15:17 +03:00
|
|
|
#include <thread>
|
2018-01-03 03:25:07 +03:00
|
|
|
#include <unordered_map>
|
2017-09-09 05:15:17 +03:00
|
|
|
#include <vector>
|
2018-01-03 03:25:03 +03:00
|
|
|
|
|
|
|
#include "eden/fs/fuse/FuseTypes.h"
|
2019-02-09 01:57:33 +03:00
|
|
|
#include "eden/fs/fuse/InodeNumber.h"
|
2017-09-09 05:15:17 +03:00
|
|
|
#include "eden/fs/utils/PathFuncs.h"
|
2018-09-10 23:42:11 +03:00
|
|
|
#include "eden/fs/utils/ProcessAccessLog.h"
|
2018-01-03 20:26:45 +03:00
|
|
|
|
2018-03-13 22:52:48 +03:00
|
|
|
namespace folly {
|
|
|
|
class RequestContext;
|
2018-03-27 21:12:59 +03:00
|
|
|
struct Unit;
|
2018-03-13 22:52:48 +03:00
|
|
|
} // namespace folly
|
|
|
|
|
2017-09-09 05:15:17 +03:00
|
|
|
namespace facebook {
|
|
|
|
namespace eden {
|
|
|
|
|
|
|
|
class Dispatcher;
|
|
|
|
|
|
|
|
class FuseChannel {
|
|
|
|
public:
|
2018-03-14 04:25:47 +03:00
|
|
|
enum class StopReason {
|
|
|
|
RUNNING, // not stopped
|
|
|
|
INIT_FAILED,
|
|
|
|
UNMOUNTED,
|
|
|
|
TAKEOVER,
|
|
|
|
DESTRUCTOR,
|
|
|
|
FUSE_READ_ERROR,
|
|
|
|
FUSE_WRITE_ERROR,
|
|
|
|
FUSE_TRUNCATED_REQUEST,
|
|
|
|
WORKER_EXCEPTION,
|
|
|
|
};
|
2018-03-17 03:30:49 +03:00
|
|
|
struct StopData {
|
|
|
|
/**
|
|
|
|
* The reason why the FUSE channel was stopped.
|
|
|
|
*
|
|
|
|
* If multiple events occurred that triggered shutdown, only one will be
|
|
|
|
* reported here. (e.g., If a takeover was initiated and one of the worker
|
|
|
|
* threads saw that the device was unmounted before it stopped.)
|
|
|
|
*/
|
|
|
|
StopReason reason{StopReason::RUNNING};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* The FUSE device for communicating with the kernel, if it is still valid
|
|
|
|
* to use.
|
|
|
|
*
|
|
|
|
* This will be a closed File object if the FUSE device is no longer valid
|
|
|
|
* (e.g., if it has been unmounted or if an error occurred on the FUSE
|
|
|
|
* device).
|
|
|
|
*/
|
|
|
|
folly::File fuseDevice;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* The FUSE connection settings negotiated on the FUSE device.
|
|
|
|
*
|
|
|
|
* This will have valid data only when fuseDevice is also valid.
|
|
|
|
* This can be passed to initializeFromTakeover() to start a new
|
|
|
|
* FuseChannel object that uses this FuseDevice.
|
|
|
|
*/
|
|
|
|
fuse_init_out fuseSettings = {};
|
|
|
|
};
|
|
|
|
using StopFuture = folly::SemiFuture<StopData>;
|
2018-03-14 04:25:47 +03:00
|
|
|
|
2017-09-09 05:15:17 +03:00
|
|
|
/**
|
|
|
|
* Construct the fuse channel and session structures that are
|
|
|
|
* required by libfuse to communicate with the kernel using
|
|
|
|
* a pre-existing fuseDevice descriptor. The descriptor may
|
|
|
|
* have been obtained via privilegedFuseMount() or may have
|
|
|
|
* been passed to us as part of a graceful restart procedure.
|
2018-02-01 01:37:01 +03:00
|
|
|
*
|
|
|
|
* The caller is expected to follow up with a call to the
|
|
|
|
* initialize() method to perform the handshake with the
|
|
|
|
* kernel and set up the thread pool.
|
2017-09-09 05:15:17 +03:00
|
|
|
*/
|
2018-01-03 20:26:45 +03:00
|
|
|
FuseChannel(
|
|
|
|
folly::File&& fuseDevice,
|
|
|
|
AbsolutePathPiece mountPath,
|
|
|
|
size_t numThreads,
|
2018-09-10 23:42:11 +03:00
|
|
|
Dispatcher* const dispatcher,
|
|
|
|
std::shared_ptr<ProcessNameCache> processNameCache);
|
2018-02-01 01:37:01 +03:00
|
|
|
|
2018-03-12 21:43:28 +03:00
|
|
|
/**
|
|
|
|
* Destroy the FuseChannel.
|
|
|
|
*
|
2018-03-16 22:07:59 +03:00
|
|
|
* If the FUSE worker threads are still running, the destroy() will stop
|
2018-03-12 21:43:28 +03:00
|
|
|
* them and wait for them to exit.
|
|
|
|
*
|
2018-03-16 22:07:59 +03:00
|
|
|
* destroy() must not be invoked from inside one of the worker threads. For
|
|
|
|
* instance, do not invoke the destructor from inside a Dispatcher callback.
|
|
|
|
*
|
|
|
|
* The FuseChannel object itself may not have been immediately deleted by the
|
|
|
|
* time that destroy() returns. destroy() will wait for all outstanding FUSE
|
|
|
|
* requests to complete before it deletes the object. However, it may return
|
|
|
|
* before this happens if some FUSE requests are still pending and will
|
|
|
|
* complete in a non-FUSE-worker thread.
|
2018-03-12 21:43:28 +03:00
|
|
|
*/
|
2018-03-16 22:07:59 +03:00
|
|
|
void destroy();
|
2018-03-12 21:43:28 +03:00
|
|
|
|
2018-02-01 01:37:01 +03:00
|
|
|
/**
|
|
|
|
* Initialize the FuseChannel; until this completes successfully,
|
|
|
|
* FUSE requests will not be serviced.
|
|
|
|
*
|
2018-03-13 22:52:48 +03:00
|
|
|
* This will first start one worker thread to wait for the INIT request from
|
|
|
|
* the kernel and validate that we are compatible. Once we have successfully
|
|
|
|
* completed the INIT negotiation with the kernel we will start the remaining
|
|
|
|
* FUSE worker threads and indicate success via the returned Future object.
|
2018-02-01 01:37:01 +03:00
|
|
|
*
|
2018-03-16 22:07:49 +03:00
|
|
|
* Returns a folly::Future that will become ready once the mount point has
|
|
|
|
* been initialized and is ready for I/O. This Future will complete inside
|
|
|
|
* one of the FUSE worker threads.
|
|
|
|
*
|
|
|
|
* The initialization Future will return a new StopFuture object that will
|
|
|
|
* be fulfilled when the FuseChannel has stopped. This StopFuture can be
|
|
|
|
* used to detect if the FuseChannel has been unmounted or stopped because of
|
|
|
|
* an error or any other reason. The StopFuture may be fulfilled inside one
|
|
|
|
* of the FUSE worker threads that is in the process of shutting down.
|
|
|
|
* Callers should normally use via() to perform any additional work in
|
|
|
|
* another executor thread.
|
2018-02-01 01:37:01 +03:00
|
|
|
*/
|
2018-03-16 22:07:49 +03:00
|
|
|
FOLLY_NODISCARD folly::Future<StopFuture> initialize();
|
2017-09-09 05:15:17 +03:00
|
|
|
|
2018-03-13 07:24:09 +03:00
|
|
|
/**
|
|
|
|
* Initialize the FuseChannel when taking over an existing FuseDevice.
|
|
|
|
*
|
|
|
|
* This is used when performing a graceful restart of Eden, where we are
|
|
|
|
* taking over a FUSE connection that was already initialized by a previous
|
|
|
|
* process.
|
|
|
|
*
|
|
|
|
* The connInfo parameter specifies the connection data that was already
|
|
|
|
* negotiated by the previous owner of the FuseDevice.
|
|
|
|
*
|
|
|
|
* This function will immediately set up the thread pool used to service
|
|
|
|
* incoming fuse requests.
|
2018-03-16 22:07:49 +03:00
|
|
|
*
|
|
|
|
* Returns a StopFuture that will be fulfilled when the FuseChannel has
|
|
|
|
* stopped. This future can be used to detect if the FuseChannel has been
|
|
|
|
* unmounted or stopped because of an error or any other reason.
|
2018-03-13 07:24:09 +03:00
|
|
|
*/
|
2018-03-16 22:07:49 +03:00
|
|
|
StopFuture initializeFromTakeover(fuse_init_out connInfo);
|
2018-03-13 07:24:09 +03:00
|
|
|
|
2017-09-09 05:15:17 +03:00
|
|
|
// Forbidden copy constructor and assignment operator
|
|
|
|
FuseChannel(FuseChannel const&) = delete;
|
|
|
|
FuseChannel& operator=(FuseChannel const&) = delete;
|
|
|
|
|
|
|
|
/**
|
2018-03-13 22:52:48 +03:00
|
|
|
* Request that the FuseChannel stop processing new requests, and prepare
|
|
|
|
* to hand over the FuseDevice to another process.
|
2017-09-09 05:15:17 +03:00
|
|
|
*/
|
2018-03-13 22:52:48 +03:00
|
|
|
void takeoverStop() {
|
2018-03-14 04:25:47 +03:00
|
|
|
requestSessionExit(StopReason::TAKEOVER);
|
2018-03-13 22:52:48 +03:00
|
|
|
}
|
2017-09-09 05:15:17 +03:00
|
|
|
|
|
|
|
/**
|
2018-03-27 21:12:59 +03:00
|
|
|
* Request that the kernel invalidate its cached data for the specified
|
|
|
|
* inode.
|
|
|
|
*
|
|
|
|
* This operation is performed asynchronously. flushInvalidations() can be
|
|
|
|
* called if you need to determine when this operation has completed.
|
2017-09-09 05:15:17 +03:00
|
|
|
*
|
|
|
|
* @param ino the inode number
|
|
|
|
* @param off the offset in the inode where to start invalidating
|
|
|
|
* or negative to invalidate attributes only
|
|
|
|
* @param len the amount of cache to invalidate or 0 for all
|
|
|
|
*/
|
2018-03-27 21:12:59 +03:00
|
|
|
void invalidateInode(InodeNumber ino, int64_t off, int64_t len);
|
2017-09-09 05:15:17 +03:00
|
|
|
|
|
|
|
/**
|
2018-03-27 21:12:59 +03:00
|
|
|
* Request that the kernel invalidate its cached data for the specified
|
|
|
|
* directory entry.
|
|
|
|
*
|
|
|
|
* This operation is performed asynchronously. flushInvalidations() can be
|
|
|
|
* called if you need to determine when this operation has completed.
|
2017-09-09 05:15:17 +03:00
|
|
|
*
|
|
|
|
* @param parent inode number
|
|
|
|
* @param name file name
|
|
|
|
*/
|
2018-03-20 03:01:15 +03:00
|
|
|
void invalidateEntry(InodeNumber parent, PathComponentPiece name);
|
2017-09-09 05:15:17 +03:00
|
|
|
|
2018-11-07 19:56:49 +03:00
|
|
|
/*
|
|
|
|
* Request that the kernel invalidate its cached data for the specified
|
|
|
|
* inodes.
|
|
|
|
*
|
|
|
|
* This operation is performed asynchronously. flushInvalidations() can be
|
|
|
|
* called if you need to determine when this operation has completed.
|
|
|
|
*
|
|
|
|
* @param range a range of inodes
|
|
|
|
*/
|
|
|
|
void invalidateInodes(folly::Range<InodeNumber*> range);
|
|
|
|
|
2018-03-27 21:12:59 +03:00
|
|
|
/**
|
|
|
|
* Wait for all currently scheduled invalidateInode() and invalidateEntry()
|
|
|
|
* operations to complete.
|
|
|
|
*
|
|
|
|
* The returned Future will complete once all invalidation operations
|
|
|
|
* scheduled before this flushInvalidations() call have finished. This
|
|
|
|
* future will normally be completed in the FuseChannel's invalidation
|
|
|
|
* thread.
|
|
|
|
*/
|
|
|
|
FOLLY_NODISCARD folly::Future<folly::Unit> flushInvalidations();
|
|
|
|
|
2018-01-03 03:25:03 +03:00
|
|
|
/**
|
|
|
|
* Sends a reply to a kernel request that consists only of the error
|
|
|
|
* status (no additional payload).
|
|
|
|
* `err` may be 0 (indicating success) or a positive errno value.
|
|
|
|
*
|
|
|
|
* throws system_error if the write fails. Writes can fail if the
|
|
|
|
* data we send to the kernel is invalid.
|
|
|
|
*/
|
|
|
|
void replyError(const fuse_in_header& request, int err);
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Sends a raw data packet to the kernel.
|
|
|
|
* The data may be scattered across a number of discrete buffers;
|
|
|
|
* this method uses writev to send them to the kernel as a single unit.
|
|
|
|
* The kernel, and thus this method, assumes that the start of this data
|
|
|
|
* is a fuse_out_header instance. This method will sum the iovec lengths
|
|
|
|
* to compute the correct value to store into fuse_out_header::len.
|
|
|
|
*
|
|
|
|
* throws system_error if the write fails. Writes can fail if the
|
|
|
|
* data we send to the kernel is invalid.
|
|
|
|
*/
|
|
|
|
void sendRawReply(const iovec iov[], size_t count) const;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Sends a range of contiguous bytes as a reply to the kernel.
|
|
|
|
* request holds the context of the request to which we are replying.
|
|
|
|
* `bytes` is the payload to send in addition to the successful status
|
|
|
|
* header generated by this method.
|
2017-09-09 05:15:17 +03:00
|
|
|
*
|
2018-01-03 03:25:03 +03:00
|
|
|
* throws system_error if the write fails. Writes can fail if the
|
|
|
|
* data we send to the kernel is invalid.
|
2017-09-09 05:15:17 +03:00
|
|
|
*/
|
2018-01-03 03:25:03 +03:00
|
|
|
void sendReply(const fuse_in_header& request, folly::ByteRange bytes) const;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Sends a reply to a kernel request, consisting of multiple parts.
|
|
|
|
* The `vec` parameter holds an array of payload components and is moved
|
|
|
|
* in to this method which then prepends a fuse_out_header and passes
|
|
|
|
* control along to sendRawReply().
|
|
|
|
*
|
|
|
|
* throws system_error if the write fails. Writes can fail if the
|
|
|
|
* data we send to the kernel is invalid.
|
|
|
|
*/
|
|
|
|
void sendReply(const fuse_in_header& request, folly::fbvector<iovec>&& vec)
|
|
|
|
const;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Sends a reply to the kernel.
|
|
|
|
* The payload parameter is typically a fuse_out_XXX struct as defined
|
|
|
|
* in the appropriate fuse_kernel_XXX.h header file.
|
|
|
|
*
|
|
|
|
* throws system_error if the write fails. Writes can fail if the
|
|
|
|
* data we send to the kernel is invalid.
|
|
|
|
*/
|
|
|
|
template <typename T>
|
|
|
|
void sendReply(const fuse_in_header& request, const T& payload) const {
|
|
|
|
sendReply(
|
|
|
|
request,
|
|
|
|
folly::ByteRange{reinterpret_cast<const uint8_t*>(&payload),
|
|
|
|
sizeof(T)});
|
|
|
|
}
|
|
|
|
|
2018-03-20 20:18:29 +03:00
|
|
|
/**
|
|
|
|
* Function to get outstanding fuse requests.
|
|
|
|
*/
|
|
|
|
std::vector<fuse_in_header> getOutstandingRequests();
|
|
|
|
|
2018-09-10 23:42:12 +03:00
|
|
|
ProcessAccessLog& getProcessAccessLog() {
|
|
|
|
return processAccessLog_;
|
|
|
|
}
|
|
|
|
|
2018-03-10 03:24:40 +03:00
|
|
|
private:
|
|
|
|
struct HandlerEntry;
|
|
|
|
using HandlerMap = std::unordered_map<uint32_t, HandlerEntry>;
|
|
|
|
|
2018-03-13 22:52:48 +03:00
|
|
|
/**
|
|
|
|
* All of our mutable state that may be accessed from the worker threads,
|
|
|
|
* and therefore requires synchronization.
|
|
|
|
*/
|
|
|
|
struct State {
|
2019-01-17 01:17:09 +03:00
|
|
|
uint64_t nextRequestId{1};
|
2018-03-13 22:52:48 +03:00
|
|
|
std::unordered_map<uint64_t, std::weak_ptr<folly::RequestContext>> requests;
|
|
|
|
std::vector<std::thread> workerThreads;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* We track the number of stopped threads, to know when we are done and can
|
|
|
|
* signal sessionCompletePromise_. We only want to signal
|
|
|
|
* sessionCompletePromise_ after initialization is successful and then all
|
|
|
|
* threads have stopped.
|
|
|
|
*
|
|
|
|
* If an error occurs during initialization we may have started some but
|
|
|
|
* not all of the worker threads. We do not want to signal
|
|
|
|
* sessionCompletePromise_ in this case--we will return the error from
|
|
|
|
* initialize() or takeoverInitialize() instead.
|
|
|
|
*/
|
|
|
|
size_t stoppedThreads{0};
|
2018-03-16 22:07:59 +03:00
|
|
|
|
|
|
|
/**
|
|
|
|
* If destroyPending is true, the FuseChannel object should be
|
|
|
|
* automatically destroyed when the last outstanding request finishes.
|
|
|
|
*/
|
|
|
|
bool destroyPending{false};
|
2018-03-17 03:30:49 +03:00
|
|
|
|
|
|
|
/**
|
|
|
|
* If the FuseChannel is stopped or stopping, the reason why it is
|
|
|
|
* stopping. This is set to RUNNING while the FuseDevice is initializing
|
|
|
|
* or running.
|
|
|
|
*/
|
|
|
|
StopReason stopReason{StopReason::RUNNING};
|
2018-03-13 22:52:48 +03:00
|
|
|
};
|
|
|
|
|
2018-03-27 21:12:59 +03:00
|
|
|
struct DataRange {
|
|
|
|
DataRange(int64_t offset, int64_t length);
|
|
|
|
|
|
|
|
int64_t offset;
|
|
|
|
int64_t length;
|
|
|
|
};
|
|
|
|
enum class InvalidationType : uint32_t {
|
|
|
|
INODE,
|
|
|
|
DIR_ENTRY,
|
|
|
|
FLUSH,
|
|
|
|
};
|
|
|
|
struct InvalidationEntry {
|
|
|
|
InvalidationEntry(InodeNumber inode, int64_t offset, int64_t length);
|
|
|
|
InvalidationEntry(InodeNumber inode, PathComponentPiece name);
|
|
|
|
explicit InvalidationEntry(folly::Promise<folly::Unit> promise);
|
2018-03-31 00:58:47 +03:00
|
|
|
InvalidationEntry(InvalidationEntry&& other) noexcept;
|
2018-03-27 21:12:59 +03:00
|
|
|
~InvalidationEntry();
|
|
|
|
|
|
|
|
InvalidationType type;
|
|
|
|
InodeNumber inode;
|
|
|
|
union {
|
|
|
|
PathComponent name;
|
|
|
|
DataRange range;
|
|
|
|
folly::Promise<folly::Unit> promise;
|
|
|
|
};
|
|
|
|
};
|
|
|
|
struct InvalidationQueue {
|
|
|
|
std::vector<InvalidationEntry> queue;
|
|
|
|
bool stop{false};
|
|
|
|
};
|
|
|
|
friend std::ostream& operator<<(
|
|
|
|
std::ostream& os,
|
|
|
|
const InvalidationEntry& entry);
|
|
|
|
|
2018-03-16 22:07:59 +03:00
|
|
|
/**
|
|
|
|
* Private destructor.
|
|
|
|
*
|
|
|
|
* FuseChannel objects must always be allocated on the heap, and destroyed by
|
|
|
|
* calling FuseChannel::destroy(). Users are not allowed to destroy
|
|
|
|
* FuseChannel objects directly.
|
|
|
|
*
|
|
|
|
* FuseChannel::destroy() will delete the FuseChannel once all outstanding
|
|
|
|
* requests have completed.
|
|
|
|
*/
|
|
|
|
~FuseChannel();
|
2018-03-10 03:24:40 +03:00
|
|
|
|
2019-07-26 20:03:50 +03:00
|
|
|
bool isReadOperation(FuseOpcode opcode);
|
|
|
|
bool isWriteOperation(FuseOpcode opcode);
|
|
|
|
|
2018-01-03 03:25:05 +03:00
|
|
|
folly::Future<folly::Unit> fuseRead(
|
|
|
|
const fuse_in_header* header,
|
|
|
|
const uint8_t* arg);
|
|
|
|
folly::Future<folly::Unit> fuseWrite(
|
|
|
|
const fuse_in_header* header,
|
|
|
|
const uint8_t* arg);
|
|
|
|
folly::Future<folly::Unit> fuseLookup(
|
|
|
|
const fuse_in_header* header,
|
|
|
|
const uint8_t* arg);
|
|
|
|
folly::Future<folly::Unit> fuseForget(
|
|
|
|
const fuse_in_header* header,
|
|
|
|
const uint8_t* arg);
|
|
|
|
folly::Future<folly::Unit> fuseGetAttr(
|
|
|
|
const fuse_in_header* header,
|
|
|
|
const uint8_t* arg);
|
|
|
|
folly::Future<folly::Unit> fuseSetAttr(
|
|
|
|
const fuse_in_header* header,
|
|
|
|
const uint8_t* arg);
|
|
|
|
folly::Future<folly::Unit> fuseReadLink(
|
|
|
|
const fuse_in_header* header,
|
|
|
|
const uint8_t* arg);
|
|
|
|
folly::Future<folly::Unit> fuseSymlink(
|
|
|
|
const fuse_in_header* header,
|
|
|
|
const uint8_t* arg);
|
|
|
|
folly::Future<folly::Unit> fuseMknod(
|
|
|
|
const fuse_in_header* header,
|
|
|
|
const uint8_t* arg);
|
|
|
|
folly::Future<folly::Unit> fuseMkdir(
|
|
|
|
const fuse_in_header* header,
|
|
|
|
const uint8_t* arg);
|
|
|
|
folly::Future<folly::Unit> fuseUnlink(
|
|
|
|
const fuse_in_header* header,
|
|
|
|
const uint8_t* arg);
|
|
|
|
folly::Future<folly::Unit> fuseRmdir(
|
|
|
|
const fuse_in_header* header,
|
|
|
|
const uint8_t* arg);
|
|
|
|
folly::Future<folly::Unit> fuseRename(
|
|
|
|
const fuse_in_header* header,
|
|
|
|
const uint8_t* arg);
|
|
|
|
folly::Future<folly::Unit> fuseLink(
|
|
|
|
const fuse_in_header* header,
|
|
|
|
const uint8_t* arg);
|
|
|
|
folly::Future<folly::Unit> fuseOpen(
|
|
|
|
const fuse_in_header* header,
|
|
|
|
const uint8_t* arg);
|
|
|
|
folly::Future<folly::Unit> fuseStatFs(
|
|
|
|
const fuse_in_header* header,
|
|
|
|
const uint8_t* arg);
|
|
|
|
folly::Future<folly::Unit> fuseRelease(
|
|
|
|
const fuse_in_header* header,
|
|
|
|
const uint8_t* arg);
|
|
|
|
folly::Future<folly::Unit> fuseFsync(
|
|
|
|
const fuse_in_header* header,
|
|
|
|
const uint8_t* arg);
|
|
|
|
folly::Future<folly::Unit> fuseSetXAttr(
|
|
|
|
const fuse_in_header* header,
|
|
|
|
const uint8_t* arg);
|
|
|
|
folly::Future<folly::Unit> fuseGetXAttr(
|
|
|
|
const fuse_in_header* header,
|
|
|
|
const uint8_t* arg);
|
|
|
|
folly::Future<folly::Unit> fuseListXAttr(
|
|
|
|
const fuse_in_header* header,
|
|
|
|
const uint8_t* arg);
|
|
|
|
folly::Future<folly::Unit> fuseRemoveXAttr(
|
|
|
|
const fuse_in_header* header,
|
|
|
|
const uint8_t* arg);
|
|
|
|
folly::Future<folly::Unit> fuseFlush(
|
|
|
|
const fuse_in_header* header,
|
|
|
|
const uint8_t* arg);
|
|
|
|
folly::Future<folly::Unit> fuseOpenDir(
|
|
|
|
const fuse_in_header* header,
|
|
|
|
const uint8_t* arg);
|
|
|
|
folly::Future<folly::Unit> fuseReadDir(
|
|
|
|
const fuse_in_header* header,
|
|
|
|
const uint8_t* arg);
|
|
|
|
folly::Future<folly::Unit> fuseReleaseDir(
|
|
|
|
const fuse_in_header* header,
|
|
|
|
const uint8_t* arg);
|
|
|
|
folly::Future<folly::Unit> fuseFsyncDir(
|
|
|
|
const fuse_in_header* header,
|
|
|
|
const uint8_t* arg);
|
|
|
|
folly::Future<folly::Unit> fuseAccess(
|
|
|
|
const fuse_in_header* header,
|
|
|
|
const uint8_t* arg);
|
|
|
|
folly::Future<folly::Unit> fuseCreate(
|
|
|
|
const fuse_in_header* header,
|
|
|
|
const uint8_t* arg);
|
|
|
|
folly::Future<folly::Unit> fuseBmap(
|
|
|
|
const fuse_in_header* header,
|
|
|
|
const uint8_t* arg);
|
|
|
|
folly::Future<folly::Unit> fuseBatchForget(
|
|
|
|
const fuse_in_header* header,
|
|
|
|
const uint8_t* arg);
|
|
|
|
|
2018-03-15 23:33:55 +03:00
|
|
|
void setThreadSigmask();
|
2018-03-13 22:52:50 +03:00
|
|
|
void initWorkerThread() noexcept;
|
|
|
|
void fuseWorkerThread() noexcept;
|
2018-03-27 21:12:59 +03:00
|
|
|
void invalidationThread() noexcept;
|
|
|
|
void stopInvalidationThread();
|
|
|
|
void sendInvalidation(InvalidationEntry& entry);
|
|
|
|
void sendInvalidateInode(InodeNumber ino, int64_t off, int64_t len);
|
|
|
|
void sendInvalidateEntry(InodeNumber parent, PathComponentPiece name);
|
2018-02-01 01:37:01 +03:00
|
|
|
void readInitPacket();
|
|
|
|
void startWorkerThreads();
|
2017-09-09 05:15:17 +03:00
|
|
|
|
2018-03-17 03:30:49 +03:00
|
|
|
/**
|
|
|
|
* sessionComplete() will fulfill the sessionCompletePromise_.
|
|
|
|
*
|
|
|
|
* Beware: calling sessionComplete() should be the very last statement in any
|
|
|
|
* method that calls it. It may destroy the FuseChannel object before it
|
|
|
|
* returns.
|
|
|
|
*/
|
|
|
|
void sessionComplete(folly::Synchronized<State>::LockedPtr state);
|
|
|
|
|
|
|
|
static bool isFuseDeviceValid(StopReason reason) {
|
|
|
|
// The FuseDevice may still be used if the FuseChannel was stopped due to a
|
|
|
|
// takeover request or because the FuseChannel object was destroyed without
|
|
|
|
// ever being unmounted. It is also still valid if the FuseChannel is
|
|
|
|
// still running.
|
|
|
|
//
|
|
|
|
// In all other cases the FuseDevice should no longer be used.
|
|
|
|
return (
|
|
|
|
reason == StopReason::RUNNING || reason == StopReason::TAKEOVER ||
|
|
|
|
reason == StopReason::DESTRUCTOR);
|
|
|
|
}
|
|
|
|
|
2018-03-13 22:52:48 +03:00
|
|
|
/**
|
|
|
|
* Dispatches fuse requests until the session is torn down.
|
|
|
|
* This function blocks until the fuse session is stopped.
|
|
|
|
* The intent is that this is called from each of the
|
|
|
|
* fuse worker threads provided by the MountPoint.
|
|
|
|
*/
|
|
|
|
void processSession();
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Requests that the worker threads terminate their processing loop.
|
|
|
|
*/
|
2018-03-14 04:25:47 +03:00
|
|
|
void requestSessionExit(StopReason reason);
|
|
|
|
void requestSessionExit(
|
|
|
|
const folly::Synchronized<State>::LockedPtr& state,
|
|
|
|
StopReason reason);
|
2018-03-13 22:52:48 +03:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Constant state that does not change for the lifetime of the FuseChannel
|
|
|
|
*/
|
|
|
|
const size_t bufferSize_{0};
|
|
|
|
const size_t numThreads_;
|
2017-09-09 05:15:17 +03:00
|
|
|
Dispatcher* const dispatcher_{nullptr};
|
2018-03-13 22:52:48 +03:00
|
|
|
const AbsolutePath mountPath_;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* connInfo_ is modified during the initialization process,
|
|
|
|
* but constant once initialization is complete.
|
|
|
|
*/
|
2018-10-24 04:48:38 +03:00
|
|
|
std::optional<fuse_init_out> connInfo_;
|
2018-03-13 22:52:48 +03:00
|
|
|
|
|
|
|
/*
|
2018-03-17 03:30:49 +03:00
|
|
|
* fuseDevice_ is constant while the worker threads are running.
|
2018-03-13 22:52:48 +03:00
|
|
|
*
|
2018-03-17 03:30:49 +03:00
|
|
|
* This is constant for most of the lifetime of the FuseChannel object.
|
|
|
|
* It can be modified during shutdown so that it can be returned as part of
|
|
|
|
* the StopData. However, there is guaranteed to be external synchronization
|
|
|
|
* around this event:
|
|
|
|
* - If the stop can occur as the last FUSE worker thread shuts down.
|
|
|
|
* No other FUSE worker threads can access fuseDevice_ after this point,
|
|
|
|
* and the FuseChannel destructor will join the threads before destryoing
|
|
|
|
* fuseDevice_.
|
|
|
|
* - If the stop can occur as when the last outstanding FUSE request
|
|
|
|
* completes, after all FUSE worker threads have stopped. In this case no
|
|
|
|
* other threads are accessing the FuseChannel and it will be immediately
|
|
|
|
* destroyed in the same thread that creates the StopData object.
|
2018-03-13 22:52:48 +03:00
|
|
|
*/
|
|
|
|
folly::File fuseDevice_;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Mutable state that is accessed from the worker threads.
|
|
|
|
* All of this state uses locking or other synchronization.
|
|
|
|
*/
|
2018-03-17 03:30:49 +03:00
|
|
|
std::atomic<bool> stop_{false};
|
2019-01-12 02:08:35 +03:00
|
|
|
folly::once_flag unmountLogFlag_;
|
2018-03-13 22:52:48 +03:00
|
|
|
folly::Synchronized<State> state_;
|
2018-03-17 03:30:49 +03:00
|
|
|
folly::Promise<StopFuture> initPromise_;
|
|
|
|
folly::Promise<StopData> sessionCompletePromise_;
|
2018-01-05 21:18:53 +03:00
|
|
|
|
|
|
|
// To prevent logging unsupported opcodes twice.
|
2018-01-05 21:18:55 +03:00
|
|
|
folly::Synchronized<std::unordered_set<FuseOpcode>> unhandledOpcodes_;
|
2018-03-16 22:07:59 +03:00
|
|
|
|
2018-03-27 21:12:59 +03:00
|
|
|
// State for sending inode invalidation requests to the kernel
|
|
|
|
// These are processed in their own dedicated thread.
|
|
|
|
folly::Synchronized<InvalidationQueue, std::mutex> invalidationQueue_;
|
|
|
|
std::condition_variable invalidationCV_;
|
|
|
|
std::thread invalidationThread_;
|
|
|
|
|
2018-09-10 23:42:11 +03:00
|
|
|
ProcessAccessLog processAccessLog_;
|
|
|
|
|
2018-03-16 22:07:59 +03:00
|
|
|
static const HandlerMap handlerMap_;
|
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* FuseChannelDeleter acts as a deleter argument for std::shared_ptr or
|
|
|
|
* std::unique_ptr.
|
|
|
|
*/
|
|
|
|
class FuseChannelDeleter {
|
|
|
|
public:
|
|
|
|
void operator()(FuseChannel* channel) {
|
|
|
|
channel->destroy();
|
|
|
|
}
|
2017-09-09 05:15:17 +03:00
|
|
|
};
|
2018-03-20 03:01:15 +03:00
|
|
|
|
2019-03-12 06:45:20 +03:00
|
|
|
class FuseDeviceUnmountedDuringInitialization : public std::runtime_error {
|
|
|
|
public:
|
|
|
|
explicit FuseDeviceUnmountedDuringInitialization(AbsolutePathPiece mountPath);
|
|
|
|
};
|
|
|
|
|
2017-11-04 01:58:04 +03:00
|
|
|
} // namespace eden
|
|
|
|
} // namespace facebook
|