Make fuse inode logic more testable + add tests.

The main purpose of this change is to abstract away all of the
internal book-keeping behind a "node" class, so that its logic
is well encapsulated and easier to test.

This refactor revealed a few bugs / issues which have been addressed:
- The fuse inode counter wasn't actually being used anywhere, so we
  can get rid of it.
- node->nid, node->ino don't store any useful information so this change
  gets rid of them.
- CloseAllOpenFds is supposed to close open FDs but doesn't actually do
  so, it only deletes the nodes associated with the FD.
- Removing the distinction between the root being stack allocated and
  every other node being heap allocated simplifies the code
  significantly.

Test: atest FuseDaemonHostTest
Test: atest node_test

Change-Id: Ia1800258efd236820406bdfb211882aa6ecaca46
diff --git a/jni/node.cpp b/jni/node.cpp
new file mode 100644
index 0000000..1974a91
--- /dev/null
+++ b/jni/node.cpp
@@ -0,0 +1,95 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specic language governing permissions and
+ * limitations under the License.
+ */
+
+#include "node-inl.h"
+
+static std::vector<std::string> GetPathSegments(int segment_start, const std::string& path) {
+    std::vector<std::string> segments;
+    int segment_end = path.find_first_of('/', segment_start);
+
+    while (segment_end != std::string::npos) {
+        if (segment_end == segment_start) {
+            // First character is '/' ignore
+            segment_end = path.find_first_of('/', ++segment_start);
+            continue;
+        }
+
+        segments.push_back(path.substr(segment_start, segment_end - segment_start));
+        segment_start = segment_end + 1;
+        segment_end = path.find_first_of('/', segment_start);
+    }
+    if (segment_start < path.size()) {
+        segments.push_back(path.substr(segment_start));
+    }
+    return segments;
+}
+
+namespace mediaprovider {
+namespace fuse {
+
+// Assumes that |node| has at least one child.
+void node::BuildPathForNodeRecursive(node* node, std::string* path) {
+    if (node->parent_) BuildPathForNodeRecursive(node->parent_, path);
+
+    (*path) += node->GetName() + "/";
+}
+
+std::string node::BuildPath() const {
+    std::lock_guard<std::recursive_mutex> guard(*lock_);
+    std::string path;
+
+    path.reserve(PATH_MAX);
+    if (parent_) BuildPathForNodeRecursive(parent_, &path);
+    path += name_;
+    return path;
+}
+
+const node* node::LookupAbsolutePath(const node* root, const std::string& absolute_path) {
+    if (absolute_path.find(root->GetName()) != 0) {
+        return nullptr;
+    }
+
+    std::vector<std::string> segments = GetPathSegments(root->GetName().size(), absolute_path);
+
+    std::lock_guard<std::recursive_mutex> guard(*root->lock_);
+
+    const node* node = root;
+    for (const std::string& segment : segments) {
+        node = node->LookupChildByName(segment);
+        if (!node) {
+            return nullptr;
+        }
+    }
+    return node;
+}
+
+void node::DeleteTree(node* tree) {
+    std::lock_guard<std::recursive_mutex> guard(*tree->lock_);
+
+    if (tree) {
+        for (node* child : tree->children_) {
+            DeleteTree(child);
+        }
+        tree->children_.clear();
+
+        LOG(DEBUG) << "DELETE node " << tree->GetName();
+        tree->RemoveFromParent();
+        delete tree;
+    }
+}
+
+}  // namespace fuse
+}  // namespace mediaprovider