1 /* 2 * Copyright (C) 2020 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specic language governing permissions and 14 * limitations under the License. 15 */ 16 17 #ifndef MEDIA_PROVIDER_JNI_NODE_INL_H_ 18 #define MEDIA_PROVIDER_JNI_NODE_INL_H_ 19 20 #include <android-base/logging.h> 21 22 #include <cstdint> 23 #include <limits> 24 #include <list> 25 #include <memory> 26 #include <mutex> 27 #include <set> 28 #include <sstream> 29 #include <string> 30 #include <unordered_set> 31 #include <utility> 32 #include <vector> 33 34 #include "libfuse_jni/ReaddirHelper.h" 35 #include "libfuse_jni/RedactionInfo.h" 36 37 class NodeTest; 38 39 namespace mediaprovider { 40 namespace fuse { 41 42 struct handle { handlehandle43 explicit handle(int fd, const RedactionInfo* ri, bool cached) : fd(fd), ri(ri), cached(cached) { 44 CHECK(ri != nullptr); 45 } 46 47 const int fd; 48 const std::unique_ptr<const RedactionInfo> ri; 49 const bool cached; 50 ~handlehandle51 ~handle() { close(fd); } 52 }; 53 54 struct dirhandle { dirhandledirhandle55 explicit dirhandle(DIR* dir) : d(dir), next_off(0) { CHECK(dir != nullptr); } 56 57 DIR* const d; 58 off_t next_off; 59 // Fuse readdir() is called multiple times based on the size of the buffer and 60 // number of directory entries in the given directory. 'de' holds the list 61 // of directory entries for the directory handle and this list is available 62 // across subsequent readdir() calls for the same directory handle. 63 std::vector<std::shared_ptr<DirectoryEntry>> de; 64 ~dirhandledirhandle65 ~dirhandle() { closedir(d); } 66 }; 67 68 // Whether inode tracking is enabled or not. When enabled, we maintain a 69 // separate mapping from inode numbers to "live" nodes so we can detect when 70 // we receive a request to a node that has been deleted. 71 static constexpr bool kEnableInodeTracking = true; 72 73 class node; 74 75 // Tracks the set of active nodes associated with a FUSE instance so that we 76 // can assert that we only ever return an active node in response to a lookup. 77 class NodeTracker { 78 public: NodeTracker(std::recursive_mutex * lock)79 explicit NodeTracker(std::recursive_mutex* lock) : lock_(lock) {} 80 CheckTracked(__u64 ino)81 void CheckTracked(__u64 ino) const { 82 if (kEnableInodeTracking) { 83 const node* node = reinterpret_cast<const class node*>(ino); 84 std::lock_guard<std::recursive_mutex> guard(*lock_); 85 CHECK(active_nodes_.find(node) != active_nodes_.end()); 86 } 87 } 88 NodeDeleted(const node * node)89 void NodeDeleted(const node* node) { 90 if (kEnableInodeTracking) { 91 std::lock_guard<std::recursive_mutex> guard(*lock_); 92 LOG(DEBUG) << "Node: " << reinterpret_cast<uintptr_t>(node) << " deleted."; 93 94 CHECK(active_nodes_.find(node) != active_nodes_.end()); 95 active_nodes_.erase(node); 96 } 97 } 98 NodeCreated(const node * node)99 void NodeCreated(const node* node) { 100 if (kEnableInodeTracking) { 101 std::lock_guard<std::recursive_mutex> guard(*lock_); 102 LOG(DEBUG) << "Node: " << reinterpret_cast<uintptr_t>(node) << " created."; 103 104 CHECK(active_nodes_.find(node) == active_nodes_.end()); 105 active_nodes_.insert(node); 106 } 107 } 108 109 private: 110 std::recursive_mutex* lock_; 111 std::unordered_set<const node*> active_nodes_; 112 }; 113 114 class node { 115 public: 116 // Creates a new node with the specified parent, name and lock. Create(node * parent,const std::string & name,std::recursive_mutex * lock,NodeTracker * tracker)117 static node* Create(node* parent, const std::string& name, std::recursive_mutex* lock, 118 NodeTracker* tracker) { 119 // Place the entire constructor under a critical section to make sure 120 // node creation, tracking (if enabled) and the addition to a parent are 121 // atomic. 122 std::lock_guard<std::recursive_mutex> guard(*lock); 123 return new node(parent, name, lock, tracker); 124 } 125 126 // Creates a new root node. Root nodes have no parents by definition 127 // and their "name" must signify an absolute path. CreateRoot(const std::string & path,std::recursive_mutex * lock,NodeTracker * tracker)128 static node* CreateRoot(const std::string& path, std::recursive_mutex* lock, 129 NodeTracker* tracker) { 130 std::lock_guard<std::recursive_mutex> guard(*lock); 131 node* root = new node(nullptr, path, lock, tracker); 132 133 // The root always has one extra reference to avoid it being 134 // accidentally collected. 135 root->Acquire(); 136 return root; 137 } 138 139 // Maps an inode to its associated node. FromInode(__u64 ino,const NodeTracker * tracker)140 static inline node* FromInode(__u64 ino, const NodeTracker* tracker) { 141 tracker->CheckTracked(ino); 142 return reinterpret_cast<node*>(static_cast<uintptr_t>(ino)); 143 } 144 145 // Maps a node to its associated inode. ToInode(node * node)146 static __u64 ToInode(node* node) { 147 return static_cast<__u64>(reinterpret_cast<uintptr_t>(node)); 148 } 149 150 // Releases a reference to a node. Returns true iff the refcount dropped to 151 // zero as a result of this call to Release, meaning that it's no longer 152 // safe to perform any operations on references to this node. Release(uint32_t count)153 bool Release(uint32_t count) { 154 std::lock_guard<std::recursive_mutex> guard(*lock_); 155 if (refcount_ >= count) { 156 refcount_ -= count; 157 if (refcount_ == 0) { 158 delete this; 159 return true; 160 } 161 } else { 162 LOG(ERROR) << "Mismatched reference count: refcount_ = " << this->refcount_ 163 << " ,count = " << count; 164 } 165 166 return false; 167 } 168 169 // Builds the full path associated with this node, including all path segments 170 // associated with its descendants. 171 std::string BuildPath() const; 172 173 // Builds the full PII safe path associated with this node, including all path segments 174 // associated with its descendants. 175 std::string BuildSafePath() const; 176 177 // Looks up a direct descendant of this node by name. If |acquire| is true, 178 // also Acquire the node before returning a reference to it. LookupChildByName(const std::string & name,bool acquire)179 node* LookupChildByName(const std::string& name, bool acquire) const { 180 std::lock_guard<std::recursive_mutex> guard(*lock_); 181 182 // lower_bound will give us the first child with strcasecmp(child->name, name) >=0. 183 // For more context see comment on the NodeCompare struct. 184 auto start = children_.lower_bound(std::make_pair(name, 0)); 185 // upper_bound will give us the first child with strcasecmp(child->name, name) > 0 186 auto end = 187 children_.upper_bound(std::make_pair(name, std::numeric_limits<uintptr_t>::max())); 188 for (auto it = start; it != end; it++) { 189 node* child = *it; 190 if (!child->deleted_) { 191 if (acquire) { 192 child->Acquire(); 193 } 194 return child; 195 } 196 } 197 return nullptr; 198 } 199 200 // Marks this node as deleted. It is still associated with its parent, and 201 // all open handles etc. to this node are preserved until its refcount goes 202 // to zero. SetDeleted()203 void SetDeleted() { 204 std::lock_guard<std::recursive_mutex> guard(*lock_); 205 206 deleted_ = true; 207 } 208 Rename(const std::string & name,node * new_parent)209 void Rename(const std::string& name, node* new_parent) { 210 std::lock_guard<std::recursive_mutex> guard(*lock_); 211 212 if (new_parent != parent_) { 213 RemoveFromParent(); 214 name_ = name; 215 AddToParent(new_parent); 216 return; 217 } 218 219 // Changing name_ will change the expected position of this node in parent's set of 220 // children. Consider following scenario: 221 // 1. This node: "b"; parent's set: {"a", "b", "c"} 222 // 2. Rename("b", "d") 223 // 224 // After rename, parent's set should become: {"a", "b", "d"}, but if we simply change the 225 // name it will be {"a", "d", "b"}, which violates properties of the set. 226 // 227 // To make sure that parent's set is always valid, changing name is 3 steps procedure: 228 // 1. Remove this node from parent's set. 229 // 2 Change the name. 230 // 3. Add it back to the set. 231 // Rename of node without changing its parent. Still need to remove and re-add it to make 232 // sure lookup index is correct. 233 if (name_ != name) { 234 // If this is a root node, simply rename it. 235 if (parent_ == nullptr) { 236 name_ = name; 237 return; 238 } 239 240 auto it = parent_->children_.find(this); 241 CHECK(it != parent_->children_.end()); 242 parent_->children_.erase(it); 243 244 name_ = name; 245 246 parent_->children_.insert(this); 247 } 248 } 249 GetName()250 const std::string& GetName() const { 251 std::lock_guard<std::recursive_mutex> guard(*lock_); 252 return name_; 253 } 254 GetParent()255 node* GetParent() const { 256 std::lock_guard<std::recursive_mutex> guard(*lock_); 257 return parent_; 258 } 259 AddHandle(handle * h)260 inline void AddHandle(handle* h) { 261 std::lock_guard<std::recursive_mutex> guard(*lock_); 262 handles_.emplace_back(std::unique_ptr<handle>(h)); 263 } 264 DestroyHandle(handle * h)265 void DestroyHandle(handle* h) { 266 std::lock_guard<std::recursive_mutex> guard(*lock_); 267 268 auto comp = [h](const std::unique_ptr<handle>& ptr) { return ptr.get() == h; }; 269 auto it = std::find_if(handles_.begin(), handles_.end(), comp); 270 CHECK(it != handles_.end()); 271 handles_.erase(it); 272 } 273 HasCachedHandle()274 bool HasCachedHandle() const { 275 std::lock_guard<std::recursive_mutex> guard(*lock_); 276 277 for (const auto& handle : handles_) { 278 if (handle->cached) { 279 return true; 280 } 281 } 282 return false; 283 } 284 AddDirHandle(dirhandle * d)285 inline void AddDirHandle(dirhandle* d) { 286 std::lock_guard<std::recursive_mutex> guard(*lock_); 287 288 dirhandles_.emplace_back(std::unique_ptr<dirhandle>(d)); 289 } 290 DestroyDirHandle(dirhandle * d)291 void DestroyDirHandle(dirhandle* d) { 292 std::lock_guard<std::recursive_mutex> guard(*lock_); 293 294 auto comp = [d](const std::unique_ptr<dirhandle>& ptr) { return ptr.get() == d; }; 295 auto it = std::find_if(dirhandles_.begin(), dirhandles_.end(), comp); 296 CHECK(it != dirhandles_.end()); 297 dirhandles_.erase(it); 298 } 299 300 // Deletes the tree of nodes rooted at |tree|. 301 static void DeleteTree(node* tree); 302 303 // Looks up an absolute path rooted at |root|, or nullptr if no such path 304 // through the hierarchy exists. 305 static const node* LookupAbsolutePath(const node* root, const std::string& absolute_path); 306 307 private: node(node * parent,const std::string & name,std::recursive_mutex * lock,NodeTracker * tracker)308 node(node* parent, const std::string& name, std::recursive_mutex* lock, NodeTracker* tracker) 309 : name_(name), 310 refcount_(0), 311 parent_(nullptr), 312 deleted_(false), 313 lock_(lock), 314 tracker_(tracker) { 315 tracker_->NodeCreated(this); 316 Acquire(); 317 // This is a special case for the root node. All other nodes will have a 318 // non-null parent. 319 if (parent != nullptr) { 320 AddToParent(parent); 321 } 322 } 323 324 // Acquires a reference to a node. This maps to the "lookup count" specified 325 // by the FUSE documentation and must only happen under the circumstances 326 // documented in libfuse/include/fuse_lowlevel.h. Acquire()327 inline void Acquire() { 328 std::lock_guard<std::recursive_mutex> guard(*lock_); 329 refcount_++; 330 } 331 332 // Adds this node to a specified parent. AddToParent(node * parent)333 void AddToParent(node* parent) { 334 std::lock_guard<std::recursive_mutex> guard(*lock_); 335 // This method assumes this node is currently unparented. 336 CHECK(parent_ == nullptr); 337 // Check that the new parent isn't nullptr either. 338 CHECK(parent != nullptr); 339 340 parent_ = parent; 341 parent_->children_.insert(this); 342 343 // TODO(narayan, zezeozue): It's unclear why we need to call Acquire on the 344 // parent node when we're adding a child to it. 345 parent_->Acquire(); 346 } 347 348 // Removes this node from its current parent, and set its parent to nullptr. RemoveFromParent()349 void RemoveFromParent() { 350 std::lock_guard<std::recursive_mutex> guard(*lock_); 351 352 if (parent_ != nullptr) { 353 auto it = parent_->children_.find(this); 354 CHECK(it != parent_->children_.end()); 355 parent_->children_.erase(it); 356 357 parent_->Release(1); 358 parent_ = nullptr; 359 } 360 } 361 362 // A custom heterogeneous comparator used for set of this node's children_ to speed up child 363 // node by name lookups. 364 // 365 // This comparator treats node* as pair (node->name_, node): two nodes* are first 366 // compared by their name using case-insenstive comparison function. If their names are equal, 367 // then pointers are compared as integers. 368 // 369 // See LookupChildByName function to see how this comparator is used. 370 // 371 // Note that it's important to first compare by name_, since it will make all nodes with same 372 // name (compared using strcasecmp) together, which allows LookupChildByName function to find 373 // range of the candidate nodes by issuing two binary searches. 374 struct NodeCompare { 375 using is_transparent = void; 376 operatorNodeCompare377 bool operator()(const node* lhs, const node* rhs) const { 378 int cmp = strcasecmp(lhs->name_.c_str(), rhs->name_.c_str()); 379 if (cmp != 0) { 380 return cmp < 0; 381 } 382 return reinterpret_cast<uintptr_t>(lhs) < reinterpret_cast<uintptr_t>(rhs); 383 } 384 operatorNodeCompare385 bool operator()(const node* lhs, const std::pair<std::string, uintptr_t>& rhs) const { 386 int cmp = strcasecmp(lhs->name_.c_str(), rhs.first.c_str()); 387 if (cmp != 0) { 388 return cmp < 0; 389 } 390 return reinterpret_cast<uintptr_t>(lhs) < rhs.second; 391 } 392 operatorNodeCompare393 bool operator()(const std::pair<std::string, uintptr_t>& lhs, const node* rhs) const { 394 int cmp = strcasecmp(lhs.first.c_str(), rhs->name_.c_str()); 395 if (cmp != 0) { 396 return cmp < 0; 397 } 398 return lhs.second < reinterpret_cast<uintptr_t>(rhs); 399 } 400 }; 401 402 // A helper function to recursively construct the absolute path of a given node. 403 // If |safe| is true, builds a PII safe path instead 404 void BuildPathForNodeRecursive(bool safe, const node* node, std::stringstream* path) const; 405 406 // The name of this node. Non-const because it can change during renames. 407 std::string name_; 408 // The reference count for this node. Guarded by |lock_|. 409 uint32_t refcount_; 410 // Set of children of this node. All of them contain a back reference 411 // to their parent. Guarded by |lock_|. 412 std::set<node*, NodeCompare> children_; 413 // Containing directory for this node. Guarded by |lock_|. 414 node* parent_; 415 // List of file handles associated with this node. Guarded by |lock_|. 416 std::vector<std::unique_ptr<handle>> handles_; 417 // List of directory handles associated with this node. Guarded by |lock_|. 418 std::vector<std::unique_ptr<dirhandle>> dirhandles_; 419 bool deleted_; 420 std::recursive_mutex* lock_; 421 422 NodeTracker* const tracker_; 423 ~node()424 ~node() { 425 RemoveFromParent(); 426 427 handles_.clear(); 428 dirhandles_.clear(); 429 430 tracker_->NodeDeleted(this); 431 } 432 433 friend class ::NodeTest; 434 }; 435 436 } // namespace fuse 437 } // namespace mediaprovider 438 439 #endif // MEDIA_PROVIDER_JNI_MODE_INL_H_ 440