Add FUSE node fields to support multiple nodes with same name
The following new fields are added on FUSE node:
1. transforms: Flags determining what, if any transformations should
be done to the node
2. io_path: Allows re-directing path to use for actual IO different
from node->BuildPath()
3. io_ready: Allows lazyily transforming a node before accessing the
new io_path
These fields have default values and change should be a no-op
Test: Manual && fsstress for ~24hrs
Bug: 158466177
Change-Id: I93417795b4b3d8b97e61385e4a24e45e562d3382
diff --git a/jni/FuseDaemon.cpp b/jni/FuseDaemon.cpp
index d40e0a6..c332bc505 100644
--- a/jni/FuseDaemon.cpp
+++ b/jni/FuseDaemon.cpp
@@ -412,9 +412,13 @@
}
bool should_inval = false;
+ bool transforms_complete = true;
+ int transforms = 0;
+ string io_path;
node = parent->LookupChildByName(name, true /* acquire */);
if (!node) {
- node = ::node::Create(parent, name, &fuse->lock, &fuse->tracker);
+ node = ::node::Create(parent, name, io_path, transforms_complete, transforms, &fuse->lock,
+ &fuse->tracker);
} else if (!mediaprovider::fuse::containsMount(path, std::to_string(getuid() / PER_USER_RANGE))) {
should_inval = node->HasCaseInsensitiveMatch();
// Only invalidate a path if it does not contain mount.
diff --git a/jni/node-inl.h b/jni/node-inl.h
index 13df5a3..e5344cc 100644
--- a/jni/node-inl.h
+++ b/jni/node-inl.h
@@ -19,6 +19,7 @@
#include <android-base/logging.h>
+#include <atomic>
#include <cstdint>
#include <limits>
#include <list>
@@ -114,13 +115,14 @@
class node {
public:
// Creates a new node with the specified parent, name and lock.
- static node* Create(node* parent, const std::string& name, std::recursive_mutex* lock,
+ static node* Create(node* parent, const std::string& name, const std::string& io_path,
+ bool transforms_complete, const int transforms, std::recursive_mutex* lock,
NodeTracker* tracker) {
// Place the entire constructor under a critical section to make sure
// node creation, tracking (if enabled) and the addition to a parent are
// atomic.
std::lock_guard<std::recursive_mutex> guard(*lock);
- return new node(parent, name, lock, tracker);
+ return new node(parent, name, io_path, transforms_complete, transforms, lock, tracker);
}
// Creates a new root node. Root nodes have no parents by definition
@@ -128,7 +130,7 @@
static node* CreateRoot(const std::string& path, std::recursive_mutex* lock,
NodeTracker* tracker) {
std::lock_guard<std::recursive_mutex> guard(*lock);
- node* root = new node(nullptr, path, lock, tracker);
+ node* root = new node(nullptr, path, path, true, 0, lock, tracker);
// The root always has one extra reference to avoid it being
// accidentally collected.
@@ -176,12 +178,15 @@
// Looks up a direct descendant of this node by name. If |acquire| is true,
// also Acquire the node before returning a reference to it.
- node* LookupChildByName(const std::string& name, bool acquire) const {
- return ForChild(name, [acquire](node* child) {
- if (acquire) {
- child->Acquire();
+ node* LookupChildByName(const std::string& name, bool acquire, const int transforms = 0) const {
+ return ForChild(name, [acquire, transforms](node* child) {
+ if (child->transforms_ == transforms) {
+ if (acquire) {
+ child->Acquire();
+ }
+ return true;
}
- return true;
+ return false;
});
}
@@ -253,6 +258,16 @@
return name_;
}
+ const std::string& GetIoPath() const { return io_path_; }
+
+ int GetTransforms() const { return transforms_; }
+
+ bool IsTransformsComplete() const {
+ return transforms_complete_.load(std::memory_order_acquire);
+ }
+
+ void SetTransformsComplete() { transforms_complete_.store(true, std::memory_order_release); }
+
node* GetParent() const {
std::lock_guard<std::recursive_mutex> guard(*lock_);
return parent_;
@@ -326,8 +341,12 @@
static const node* LookupAbsolutePath(const node* root, const std::string& absolute_path);
private:
- node(node* parent, const std::string& name, std::recursive_mutex* lock, NodeTracker* tracker)
+ node(node* parent, const std::string& name, const std::string& io_path, bool transforms_complete,
+ const int transforms, std::recursive_mutex* lock, NodeTracker* tracker)
: name_(name),
+ io_path_(io_path),
+ transforms_complete_(transforms_complete),
+ transforms_(transforms),
refcount_(0),
parent_(nullptr),
has_redacted_cache_(false),
@@ -454,6 +473,15 @@
// The name of this node. Non-const because it can change during renames.
std::string name_;
+ // Filesystem path that will be used for IO (if it is non-empty) instead of node->BuildPath
+ const std::string io_path_;
+ // Whether any transforms required on |io_path_| are complete.
+ // If false, might need to call a node transform function with |transforms| below
+ std::atomic_bool transforms_complete_;
+ // Opaque flags that determine the 'supported' and 'required' transforms to perform on node
+ // before IO. These flags should not be interpreted in native but should be passed as part
+ // of a transform function and if successful, |transforms_complete_| should be set to true
+ const int transforms_;
// The reference count for this node. Guarded by |lock_|.
uint32_t refcount_;
// Set of children of this node. All of them contain a back reference
diff --git a/jni/node_test.cpp b/jni/node_test.cpp
index 90f0894..d2ba399 100644
--- a/jni/node_test.cpp
+++ b/jni/node_test.cpp
@@ -31,8 +31,9 @@
typedef std::unique_ptr<node, decltype(&NodeTest::destroy)> unique_node_ptr;
- unique_node_ptr CreateNode(node* parent, const std::string& path) {
- return unique_node_ptr(node::Create(parent, path, &lock_, &tracker_), &NodeTest::destroy);
+ unique_node_ptr CreateNode(node* parent, const std::string& path, const int transforms = 0) {
+ return unique_node_ptr(node::Create(parent, path, "", true, transforms, &lock_, &tracker_),
+ &NodeTest::destroy);
}
static class node* ForChild(class node* node, const std::string& name,
@@ -66,7 +67,7 @@
}
TEST_F(NodeTest, TestRelease) {
- node* node = node::Create(nullptr, "/path", &lock_, &tracker_);
+ node* node = node::Create(nullptr, "/path", "", true, 0, &lock_, &tracker_);
acquire(node);
acquire(node);
ASSERT_EQ(3, GetRefCount(node));
@@ -141,57 +142,96 @@
TEST_F(NodeTest, TestRenameNameForChild) {
unique_node_ptr parent = CreateNode(nullptr, "/path");
- unique_node_ptr child = CreateNode(parent.get(), "subdir");
- ASSERT_EQ(2, GetRefCount(parent.get()));
- ASSERT_EQ(child.get(), parent->LookupChildByName("subdir", false /* acquire */));
+ unique_node_ptr child0 = CreateNode(parent.get(), "subdir", 0 /* transforms */);
+ unique_node_ptr child1 = CreateNode(parent.get(), "subdir", 1 /* transforms */);
+ ASSERT_EQ(3, GetRefCount(parent.get()));
+ ASSERT_EQ(child0.get(),
+ parent->LookupChildByName("subdir", false /* acquire */, 0 /* transforms */));
+ ASSERT_EQ(child1.get(),
+ parent->LookupChildByName("subdir", false /* acquire */, 1 /* transforms */));
parent->RenameChild("subdir", "subdir_new", parent.get());
- ASSERT_EQ(2, GetRefCount(parent.get()));
- ASSERT_EQ(nullptr, parent->LookupChildByName("subdir", false /* acquire */));
- ASSERT_EQ(child.get(), parent->LookupChildByName("subdir_new", false /* acquire */));
+ ASSERT_EQ(3, GetRefCount(parent.get()));
+ ASSERT_EQ(nullptr,
+ parent->LookupChildByName("subdir", false /* acquire */, 0 /* transforms */));
+ ASSERT_EQ(nullptr,
+ parent->LookupChildByName("subdir", false /* acquire */, 1 /* transforms */));
+ ASSERT_EQ(child0.get(),
+ parent->LookupChildByName("subdir_new", false /* acquire */, 0 /* transforms */));
+ ASSERT_EQ(child1.get(),
+ parent->LookupChildByName("subdir_new", false /* acquire */, 1 /* transforms */));
- ASSERT_EQ("/path/subdir_new", child->BuildPath());
- ASSERT_EQ(1, GetRefCount(child.get()));
+ ASSERT_EQ("/path/subdir_new", child0->BuildPath());
+ ASSERT_EQ("/path/subdir_new", child1->BuildPath());
+ ASSERT_EQ(1, GetRefCount(child0.get()));
+ ASSERT_EQ(1, GetRefCount(child1.get()));
}
TEST_F(NodeTest, TestRenameParentForChild) {
unique_node_ptr parent1 = CreateNode(nullptr, "/path1");
unique_node_ptr parent2 = CreateNode(nullptr, "/path2");
- unique_node_ptr child = CreateNode(parent1.get(), "subdir");
- ASSERT_EQ(2, GetRefCount(parent1.get()));
- ASSERT_EQ(child.get(), parent1->LookupChildByName("subdir", false /* acquire */));
+ unique_node_ptr child0 = CreateNode(parent1.get(), "subdir", 0 /* transforms */);
+ unique_node_ptr child1 = CreateNode(parent1.get(), "subdir", 1 /* transforms */);
+ ASSERT_EQ(3, GetRefCount(parent1.get()));
+ ASSERT_EQ(child0.get(),
+ parent1->LookupChildByName("subdir", false /* acquire */, 0 /* transforms */));
+ ASSERT_EQ(child1.get(),
+ parent1->LookupChildByName("subdir", false /* acquire */, 1 /* transforms */));
parent1->RenameChild("subdir", "subdir", parent2.get());
ASSERT_EQ(1, GetRefCount(parent1.get()));
- ASSERT_EQ(nullptr, parent1->LookupChildByName("subdir", false /* acquire */));
+ ASSERT_EQ(nullptr,
+ parent1->LookupChildByName("subdir", false /* acquire */, 0 /* transforms */));
+ ASSERT_EQ(nullptr,
+ parent1->LookupChildByName("subdir", false /* acquire */, 1 /* transforms */));
- ASSERT_EQ(2, GetRefCount(parent2.get()));
- ASSERT_EQ(child.get(), parent2->LookupChildByName("subdir", false /* acquire */));
+ ASSERT_EQ(3, GetRefCount(parent2.get()));
+ ASSERT_EQ(child0.get(),
+ parent2->LookupChildByName("subdir", false /* acquire */, 0 /* transforms */));
+ ASSERT_EQ(child1.get(),
+ parent2->LookupChildByName("subdir", false /* acquire */, 1 /* transforms */));
- ASSERT_EQ("/path2/subdir", child->BuildPath());
- ASSERT_EQ(1, GetRefCount(child.get()));
+ ASSERT_EQ("/path2/subdir", child0->BuildPath());
+ ASSERT_EQ("/path2/subdir", child1->BuildPath());
+ ASSERT_EQ(1, GetRefCount(child0.get()));
+ ASSERT_EQ(1, GetRefCount(child1.get()));
}
TEST_F(NodeTest, TestRenameNameAndParentForChild) {
unique_node_ptr parent1 = CreateNode(nullptr, "/path1");
unique_node_ptr parent2 = CreateNode(nullptr, "/path2");
- unique_node_ptr child = CreateNode(parent1.get(), "subdir");
- ASSERT_EQ(2, GetRefCount(parent1.get()));
- ASSERT_EQ(child.get(), parent1->LookupChildByName("subdir", false /* acquire */));
+ unique_node_ptr child0 = CreateNode(parent1.get(), "subdir", 0 /* transforms */);
+ unique_node_ptr child1 = CreateNode(parent1.get(), "subdir", 1 /* transforms */);
+ ASSERT_EQ(3, GetRefCount(parent1.get()));
+ ASSERT_EQ(child0.get(),
+ parent1->LookupChildByName("subdir", false /* acquire */, 0 /* transforms */));
+ ASSERT_EQ(child1.get(),
+ parent1->LookupChildByName("subdir", false /* acquire */, 1 /* transforms */));
parent1->RenameChild("subdir", "subdir_new", parent2.get());
ASSERT_EQ(1, GetRefCount(parent1.get()));
- ASSERT_EQ(nullptr, parent1->LookupChildByName("subdir", false /* acquire */));
- ASSERT_EQ(nullptr, parent1->LookupChildByName("subdir_new", false /* acquire */));
+ ASSERT_EQ(nullptr,
+ parent1->LookupChildByName("subdir", false /* acquire */, 0 /* transforms */));
+ ASSERT_EQ(nullptr,
+ parent1->LookupChildByName("subdir_new", false /* acquire */, 0 /* transforms */));
+ ASSERT_EQ(nullptr,
+ parent1->LookupChildByName("subdir", false /* acquire */, 1 /* transforms */));
+ ASSERT_EQ(nullptr,
+ parent1->LookupChildByName("subdir_new", false /* acquire */, 1 /* transforms */));
- ASSERT_EQ(2, GetRefCount(parent2.get()));
- ASSERT_EQ(child.get(), parent2->LookupChildByName("subdir_new", false /* acquire */));
+ ASSERT_EQ(3, GetRefCount(parent2.get()));
+ ASSERT_EQ(nullptr,
+ parent1->LookupChildByName("subdir_new", false /* acquire */, 0 /* transforms */));
+ ASSERT_EQ(nullptr,
+ parent1->LookupChildByName("subdir_new", false /* acquire */, 1 /* transforms */));
- ASSERT_EQ("/path2/subdir_new", child->BuildPath());
- ASSERT_EQ(1, GetRefCount(child.get()));
+ ASSERT_EQ("/path2/subdir_new", child0->BuildPath());
+ ASSERT_EQ("/path2/subdir_new", child1->BuildPath());
+ ASSERT_EQ(1, GetRefCount(child0.get()));
+ ASSERT_EQ(1, GetRefCount(child1.get()));
}
TEST_F(NodeTest, TestBuildPath) {
@@ -219,21 +259,28 @@
TEST_F(NodeTest, TestSetDeletedForChild) {
unique_node_ptr parent = CreateNode(nullptr, "/path");
- unique_node_ptr child = CreateNode(parent.get(), "subdir");
+ unique_node_ptr child0 = CreateNode(parent.get(), "subdir", 0 /* transforms */);
+ unique_node_ptr child1 = CreateNode(parent.get(), "subdir", 1 /* transforms */);
- ASSERT_EQ(child.get(), parent->LookupChildByName("subdir", false /* acquire */));
+ ASSERT_EQ(child0.get(),
+ parent->LookupChildByName("subdir", false /* acquire */, 0 /* transforms */));
+ ASSERT_EQ(child1.get(),
+ parent->LookupChildByName("subdir", false /* acquire */, 1 /* transforms */));
parent->SetDeletedForChild("subdir");
- ASSERT_EQ(nullptr, parent->LookupChildByName("subdir", false /* acquire */));
+ ASSERT_EQ(nullptr,
+ parent->LookupChildByName("subdir", false /* acquire */, 0 /* transforms */));
+ ASSERT_EQ(nullptr,
+ parent->LookupChildByName("subdir", false /* acquire */, 1 /* transforms */));
}
TEST_F(NodeTest, DeleteTree) {
unique_node_ptr parent = CreateNode(nullptr, "/path");
// This is the tree that we intend to delete.
- node* child = node::Create(parent.get(), "subdir", &lock_, &tracker_);
- node::Create(child, "s1", &lock_, &tracker_);
- node* subchild2 = node::Create(child, "s2", &lock_, &tracker_);
- node::Create(subchild2, "sc2", &lock_, &tracker_);
+ node* child = node::Create(parent.get(), "subdir", "", true, 0, &lock_, &tracker_);
+ node::Create(child, "s1", "", true, 0, &lock_, &tracker_);
+ node* subchild2 = node::Create(child, "s2", "", true, 0, &lock_, &tracker_);
+ node::Create(subchild2, "sc2", "", true, 0, &lock_, &tracker_);
ASSERT_EQ(child, parent->LookupChildByName("subdir", false /* acquire */));
node::DeleteTree(child);
@@ -248,6 +295,20 @@
ASSERT_EQ(nullptr, parent->LookupChildByName("", false /* acquire */));
}
+TEST_F(NodeTest, LookupChildByName_transforms) {
+ unique_node_ptr parent = CreateNode(nullptr, "/path");
+ unique_node_ptr child0 = CreateNode(parent.get(), "subdir", 0 /* transforms */);
+ unique_node_ptr child1 = CreateNode(parent.get(), "subdir", 1 /* transforms */);
+
+ ASSERT_EQ(child0.get(), parent->LookupChildByName("subdir", false /* acquire */));
+ ASSERT_EQ(child0.get(),
+ parent->LookupChildByName("subdir", false /* acquire */, 0 /* transforms */));
+ ASSERT_EQ(child1.get(),
+ parent->LookupChildByName("subdir", false /* acquire */, 1 /* transforms */));
+ ASSERT_EQ(nullptr,
+ parent->LookupChildByName("subdir", false /* acquire */, 2 /* transforms */));
+}
+
TEST_F(NodeTest, LookupChildByName_refcounts) {
unique_node_ptr parent = CreateNode(nullptr, "/path");
unique_node_ptr child = CreateNode(parent.get(), "subdir");