Track inodes by hand when kEnableInodeTracking is true.

This helps us build confidence that our assumptions around node lifecycles
are correct.

Test: atest FuseDaemonHostTest
Change-Id: I81d793a95db732b4b22b6e20c6c11717290359f7
diff --git a/jni/FuseDaemon.cpp b/jni/FuseDaemon.cpp
index 523226b..3e2569a 100644
--- a/jni/FuseDaemon.cpp
+++ b/jni/FuseDaemon.cpp
@@ -55,6 +55,7 @@
 #include <regex>
 #include <thread>
 #include <unordered_map>
+#include <unordered_set>
 #include <vector>
 
 #include "MediaProviderWrapper.h"
@@ -220,6 +221,11 @@
     const size_t target_ = 32 * 1024 * 1024;
 };
 
+// Whether inode tracking is enabled or not. When enabled, we maintain a
+// separate mapping from inode numbers to "live" nodes so we can detect when
+// we receive a request to a node that has been deleted.
+static constexpr bool kEnableInodeTracking = true;
+
 /* Single FUSE mount */
 struct fuse {
     explicit fuse(const std::string& _path)
@@ -231,12 +237,19 @@
     // because fuse_lowlevel_ops documents that the root inode is always one
     // (see FUSE_ROOT_ID in fuse_lowlevel.h). There are no particular requirements
     // on any of the other inodes in the FS.
-    inline node* FromInode(__u64 inode) const {
+    inline node* FromInode(__u64 inode) {
         if (inode == FUSE_ROOT_ID) {
             return root;
         }
 
-        return node::FromInode(inode);
+        node* node = node::FromInode(inode);
+
+        if (kEnableInodeTracking) {
+            std::lock_guard<std::recursive_mutex> guard(lock);
+            CHECK(inode_tracker_.find(node) != inode_tracker_.end());
+        }
+
+        return node;
     }
 
     inline __u64 ToInode(node* node) const {
@@ -247,6 +260,26 @@
         return node::ToInode(node);
     }
 
+    // Notify this FUSE instance that one of its nodes has been deleted.
+    void NodeDeleted(const node* node) {
+        if (kEnableInodeTracking) {
+            LOG(INFO) << "Node: " << node << " deleted.";
+
+            std::lock_guard<std::recursive_mutex> guard(lock);
+            inode_tracker_.erase(node);
+        }
+    }
+
+    // Notify this FUSE instance that a new nodes has been created.
+    void NodeCreated(const node* node) {
+        if (kEnableInodeTracking) {
+            LOG(INFO) << "Node: " << node << " created.";
+
+            std::lock_guard<std::recursive_mutex> guard(lock);
+            inode_tracker_.insert(node);
+        }
+    }
+
     std::recursive_mutex lock;
     const string path;
     node* const root;
@@ -265,6 +298,8 @@
     /* const */ char* zero_addr;
 
     FAdviser fadviser;
+
+    std::unordered_set<const node*> inode_tracker_;
 };
 
 static inline const char* safe_name(node* n) {
@@ -347,6 +382,7 @@
         node->Acquire();
     } else {
         node = ::node::Create(parent, name, &fuse->lock);
+        fuse->NodeCreated(node);
     }
 
     // Manipulate attr here if needed
@@ -452,7 +488,9 @@
         // 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<uint32_t>(nlookup));
+        if (node->Release(static_cast<uint32_t>(nlookup))) {
+            fuse->NodeDeleted(node);
+        }
     }
 }