From e3a6f0d1940c3103da7d31ce9eb4d8c96d7c3390 Mon Sep 17 00:00:00 2001 From: Daniel Rosenberg Date: Fri, 24 May 2024 17:10:41 -0700 Subject: Use Upstream Passthrough if available The upstream kernel has merged in a version of Passthrough with a differing interface. If we detect that, use it, as Android's version of Passthrough will be unavailable. Change-Id: Ib6b0da7a8b4a7ddeadc4dcae0e26801947670a11 Test: atest android.scopedstorage.cts.general.ScopedStorageDeviceTest using mainline kernel Flag: EXEMPT (Controlled by flag supplied by fuse kernel module) Bug: 333497409 --- jni/FuseDaemon.cpp | 48 ++++++++++++++++++++++++++++++++++++++---------- jni/node-inl.h | 16 ++++++++++++++++ 2 files changed, 54 insertions(+), 10 deletions(-) (limited to 'jni') diff --git a/jni/FuseDaemon.cpp b/jni/FuseDaemon.cpp index d82886edc..e85107bb0 100644 --- a/jni/FuseDaemon.cpp +++ b/jni/FuseDaemon.cpp @@ -274,6 +274,7 @@ struct fuse { zero_addr(0), disable_dentry_cache(false), passthrough(false), + upstream_passthrough(false), bpf(_bpf), bpf_fd(std::move(_bpf_fd)), supported_transcoding_relative_paths(_supported_transcoding_relative_paths), @@ -396,6 +397,7 @@ struct fuse { std::atomic_bool* active; std::atomic_bool disable_dentry_cache; std::atomic_bool passthrough; + std::atomic_bool upstream_passthrough; std::atomic_bool bpf; const android::base::unique_fd bpf_fd; @@ -755,6 +757,10 @@ static void pf_init(void* userdata, struct fuse_conn_info* conn) { // b. Files requiring redaction are still faster than no-passthrough devices that use // direct_io disable_splice_write = true; + } else if (conn->capable & FUSE_CAP_PASSTHROUGH_UPSTREAM) { + mask |= FUSE_CAP_PASSTHROUGH_UPSTREAM; + disable_splice_write = true; + fuse->upstream_passthrough = true; } else { LOG(WARNING) << "Passthrough feature not supported by the kernel"; fuse->passthrough = false; @@ -999,7 +1005,10 @@ static void do_forget(fuse_req_t req, struct fuse* fuse, fuse_ino_t ino, uint64_ // This is a narrowing conversion from an unsigned 64bit to a 32bit value. For // some reason we only keep 32 bit refcounts but the kernel issues // forget requests with a 64 bit counter. - node->Release(static_cast(nlookup)); + int backing_id = node->GetBackingId(); + if (node->Release(static_cast(nlookup))) { + if (backing_id) fuse_passthrough_close(req, backing_id); + } } } @@ -1447,7 +1456,7 @@ static handle* create_handle_for_node(struct fuse* fuse, const string& path, int } if (fuse->passthrough && allow_passthrough) { - *keep_cache = transforms_complete; + *keep_cache = transforms_complete && !fuse->upstream_passthrough; // We only enabled passthrough iff these 2 conditions hold // 1. Redaction is not needed // 2. Node transforms are completed, e.g transcoding. @@ -1495,14 +1504,33 @@ static handle* create_handle_for_node(struct fuse* fuse, const string& path, int return handle; } -static bool do_passthrough_enable(fuse_req_t req, struct fuse_file_info* fi, unsigned int fd) { - int passthrough_fh = fuse_passthrough_enable(req, fd); +static bool do_passthrough_enable(fuse_req_t req, struct fuse_file_info* fi, unsigned int fd, + node* node) { + struct fuse* fuse = get_fuse(req); - if (passthrough_fh <= 0) { - return false; - } + if (fuse->upstream_passthrough) { + int backing_id = node->GetBackingId(); + if (!backing_id) { + backing_id = fuse_passthrough_open(req, fd); + if (!backing_id) return false; + // We only ever want one backing id per backed file + if (!node->SetBackingId(backing_id)) { + fuse_passthrough_close(req, backing_id); + backing_id = node->GetBackingId(); + if (!backing_id) return false; + } + } - fi->passthrough_fh = passthrough_fh; + fi->backing_id = backing_id; + } else { + int passthrough_fh = fuse_passthrough_enable(req, fd); + + if (passthrough_fh <= 0) { + return false; + } + + fi->passthrough_fh = passthrough_fh; + } return true; } @@ -1611,7 +1639,7 @@ static void pf_open(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info* fi) { // TODO(b/173190192) ensuring that h->cached must be enabled in order to // user FUSE passthrough is a conservative rule and might be dropped as // soon as demonstrated its correctness. - if (h->passthrough && !do_passthrough_enable(req, fi, fd)) { + if (h->passthrough && !do_passthrough_enable(req, fi, fd, node)) { // TODO: Should we crash here so we can find errors easily? PLOG(ERROR) << "Passthrough OPEN failed for " << io_path; fuse_reply_err(req, EFAULT); @@ -2258,7 +2286,7 @@ static void pf_create(fuse_req_t req, // TODO(b/173190192) ensuring that h->cached must be enabled in order to // user FUSE passthrough is a conservative rule and might be dropped as // soon as demonstrated its correctness. - if (h->passthrough && !do_passthrough_enable(req, fi, fd)) { + if (h->passthrough && !do_passthrough_enable(req, fi, fd, node)) { PLOG(ERROR) << "Passthrough CREATE failed for " << child_path; fuse_reply_err(req, EFAULT); return; diff --git a/jni/node-inl.h b/jni/node-inl.h index ba3d9268f..e6b67b0a4 100644 --- a/jni/node-inl.h +++ b/jni/node-inl.h @@ -408,6 +408,19 @@ class node { // Looks up for the node with the given ino rooted at |root|, or nullptr if no such node exists. static const node* LookupInode(const node* root, ino_t ino); + int GetBackingId() { + std::lock_guard guard(*lock_); + return backing_id_; + } + + // A Node should only have one backing id. + bool SetBackingId(int new_id) { + std::lock_guard guard(*lock_); + if (backing_id_) return false; + backing_id_ = new_id; + return true; + } + private: node(node* parent, const std::string& name, const std::string& io_path, const bool transforms_complete, const int transforms, const int transforms_reason, @@ -423,6 +436,7 @@ class node { deleted_(false), lock_(lock), ino_(ino), + backing_id_(0), tracker_(tracker) { tracker_->NodeCreated(this); Acquire(); @@ -573,6 +587,8 @@ class node { std::recursive_mutex* lock_; // Inode number of the file represented by this node. const ino_t ino_; + // Backing identifier for upstream passthrough + int backing_id_; NodeTracker* const tracker_; -- cgit v1.2.3-59-g8ed1b