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 <sys/types.h> 23 #include <atomic> 24 #include <cstdint> 25 #include <limits> 26 #include <list> 27 #include <memory> 28 #include <mutex> 29 #include <set> 30 #include <sstream> 31 #include <string> 32 #include <unordered_set> 33 #include <utility> 34 #include <vector> 35 36 #include "libfuse_jni/ReaddirHelper.h" 37 #include "libfuse_jni/RedactionInfo.h" 38 39 class NodeTest; 40 41 namespace mediaprovider { 42 namespace fuse { 43 44 struct handle { handlehandle45 explicit handle(int fd, const RedactionInfo* ri, bool cached, bool passthrough, uid_t uid, 46 uid_t transforms_uid) 47 : fd(fd), 48 ri(ri), 49 cached(cached), 50 passthrough(passthrough), 51 uid(uid), 52 transforms_uid(transforms_uid) { 53 CHECK(ri != nullptr); 54 } 55 56 const int fd; 57 const std::unique_ptr<const RedactionInfo> ri; 58 const bool cached; 59 const bool passthrough; 60 const uid_t uid; 61 const uid_t transforms_uid; 62 ~handlehandle63 ~handle() { close(fd); } 64 }; 65 66 struct dirhandle { dirhandledirhandle67 explicit dirhandle(DIR* dir) : d(dir), next_off(0) { CHECK(dir != nullptr); } 68 69 DIR* const d; 70 off_t next_off; 71 // Fuse readdir() is called multiple times based on the size of the buffer and 72 // number of directory entries in the given directory. 'de' holds the list 73 // of directory entries for the directory handle and this list is available 74 // across subsequent readdir() calls for the same directory handle. 75 std::vector<std::shared_ptr<DirectoryEntry>> de; 76 ~dirhandledirhandle77 ~dirhandle() { closedir(d); } 78 }; 79 80 /** Represents file open result from MediaProvider */ 81 struct FdAccessResult { FdAccessResultFdAccessResult82 FdAccessResult(const std::string& file_path, const bool should_redact) 83 : file_path(file_path), should_redact(should_redact) {} 84 85 const std::string file_path; 86 const bool should_redact; 87 }; 88 89 // Whether inode tracking is enabled or not. When enabled, we maintain a 90 // separate mapping from inode numbers to "live" nodes so we can detect when 91 // we receive a request to a node that has been deleted. 92 static constexpr bool kEnableInodeTracking = true; 93 94 class node; 95 96 // Tracks the set of active nodes associated with a FUSE instance so that we 97 // can assert that we only ever return an active node in response to a lookup. 98 class NodeTracker { 99 public: NodeTracker(std::recursive_mutex * lock)100 explicit NodeTracker(std::recursive_mutex* lock) : lock_(lock) {} 101 Exists(__u64 ino)102 bool Exists(__u64 ino) const { 103 if (kEnableInodeTracking) { 104 const node* node = reinterpret_cast<const class node*>(ino); 105 std::lock_guard<std::recursive_mutex> guard(*lock_); 106 return active_nodes_.find(node) != active_nodes_.end(); 107 } 108 } 109 CheckTracked(__u64 ino)110 void CheckTracked(__u64 ino) const { 111 if (kEnableInodeTracking) { 112 const node* node = reinterpret_cast<const class node*>(ino); 113 std::lock_guard<std::recursive_mutex> guard(*lock_); 114 CHECK(active_nodes_.find(node) != active_nodes_.end()); 115 } 116 } 117 NodeDeleted(const node * node)118 void NodeDeleted(const node* node) { 119 if (kEnableInodeTracking) { 120 std::lock_guard<std::recursive_mutex> guard(*lock_); 121 LOG(DEBUG) << "Node: " << reinterpret_cast<uintptr_t>(node) << " deleted."; 122 123 CHECK(active_nodes_.find(node) != active_nodes_.end()); 124 active_nodes_.erase(node); 125 } 126 } 127 NodeCreated(const node * node)128 void NodeCreated(const node* node) { 129 if (kEnableInodeTracking) { 130 std::lock_guard<std::recursive_mutex> guard(*lock_); 131 LOG(DEBUG) << "Node: " << reinterpret_cast<uintptr_t>(node) << " created."; 132 133 CHECK(active_nodes_.find(node) == active_nodes_.end()); 134 active_nodes_.insert(node); 135 } 136 } 137 138 private: 139 std::recursive_mutex* lock_; 140 std::unordered_set<const node*> active_nodes_; 141 }; 142 143 class node { 144 public: 145 // Creates a new node with the specified parent, name and lock. Create(node * parent,const std::string & name,const std::string & io_path,const bool transforms_complete,const int transforms,const int transforms_reason,std::recursive_mutex * lock,ino_t ino,NodeTracker * tracker)146 static node* Create(node* parent, const std::string& name, const std::string& io_path, 147 const bool transforms_complete, const int transforms, 148 const int transforms_reason, std::recursive_mutex* lock, ino_t ino, 149 NodeTracker* tracker) { 150 // Place the entire constructor under a critical section to make sure 151 // node creation, tracking (if enabled) and the addition to a parent are 152 // atomic. 153 std::lock_guard<std::recursive_mutex> guard(*lock); 154 return new node(parent, name, io_path, transforms_complete, transforms, transforms_reason, 155 lock, ino, tracker); 156 } 157 158 // Creates a new root node. Root nodes have no parents by definition 159 // and their "name" must signify an absolute path. CreateRoot(const std::string & path,std::recursive_mutex * lock,ino_t ino,NodeTracker * tracker)160 static node* CreateRoot(const std::string& path, std::recursive_mutex* lock, ino_t ino, 161 NodeTracker* tracker) { 162 std::lock_guard<std::recursive_mutex> guard(*lock); 163 node* root = new node(nullptr, path, path, true /* transforms_complete */, 164 0 /* transforms */, 0 /* transforms_reason */, lock, ino, tracker); 165 166 // The root always has one extra reference to avoid it being 167 // accidentally collected. 168 root->Acquire(); 169 return root; 170 } 171 172 // Maps an inode to its associated node. FromInode(__u64 ino,const NodeTracker * tracker)173 static inline node* FromInode(__u64 ino, const NodeTracker* tracker) { 174 tracker->CheckTracked(ino); 175 return reinterpret_cast<node*>(static_cast<uintptr_t>(ino)); 176 } 177 178 // TODO(b/215235604) FromInodeNoThrow(__u64 ino,const NodeTracker * tracker)179 static inline node* FromInodeNoThrow(__u64 ino, const NodeTracker* tracker) { 180 if (!tracker->Exists(ino)) return nullptr; 181 return reinterpret_cast<node*>(static_cast<uintptr_t>(ino)); 182 } 183 184 // Maps a node to its associated inode. ToInode(node * node)185 static __u64 ToInode(node* node) { 186 return static_cast<__u64>(reinterpret_cast<uintptr_t>(node)); 187 } 188 189 // Releases a reference to a node. Returns true iff the refcount dropped to 190 // zero as a result of this call to Release, meaning that it's no longer 191 // safe to perform any operations on references to this node. Release(uint32_t count)192 bool Release(uint32_t count) { 193 std::lock_guard<std::recursive_mutex> guard(*lock_); 194 if (refcount_ >= count) { 195 refcount_ -= count; 196 if (refcount_ == 0) { 197 delete this; 198 return true; 199 } 200 } else { 201 LOG(ERROR) << "Mismatched reference count: refcount_ = " << this->refcount_ 202 << " ,count = " << count; 203 } 204 205 return false; 206 } 207 208 // Builds the full path associated with this node, including all path segments 209 // associated with its descendants. 210 std::string BuildPath() const; 211 212 // Builds the full PII safe path associated with this node, including all path segments 213 // associated with its descendants. 214 std::string BuildSafePath() const; 215 216 // Looks up a direct descendant of this node by case-insensitive |name|. If |acquire| is true, 217 // also Acquire the node before returning a reference to it. 218 // |transforms| is an opaque flag that is used to distinguish multiple nodes sharing the same 219 // |name| but requiring different IO transformations as determined by the MediaProvider. 220 node* LookupChildByName(const std::string& name, bool acquire, const int transforms = 0) const { 221 return ForChild(name, [acquire, transforms](node* child) { 222 if (child->transforms_ == transforms) { 223 if (acquire) { 224 child->Acquire(); 225 } 226 return true; 227 } 228 return false; 229 }); 230 } 231 232 // Marks this node children as deleted. They are still associated with their parent, and 233 // all open handles etc. to the deleted nodes are preserved until their refcount goes 234 // to zero. SetDeletedForChild(const std::string & name)235 void SetDeletedForChild(const std::string& name) { 236 ForChild(name, [](node* child) { 237 child->SetDeleted(); 238 return false; 239 }); 240 } 241 SetDeleted()242 void SetDeleted() { 243 std::lock_guard<std::recursive_mutex> guard(*lock_); 244 deleted_ = true; 245 } 246 RenameChild(const std::string & old_name,const std::string & new_name,node * new_parent)247 void RenameChild(const std::string& old_name, const std::string& new_name, node* new_parent) { 248 ForChild(old_name, [=](node* child) { 249 child->Rename(new_name, new_parent); 250 return false; 251 }); 252 } 253 Rename(const std::string & name,node * new_parent)254 void Rename(const std::string& name, node* new_parent) { 255 std::lock_guard<std::recursive_mutex> guard(*lock_); 256 257 if (new_parent != parent_) { 258 RemoveFromParent(); 259 name_ = name; 260 AddToParent(new_parent); 261 return; 262 } 263 264 // Changing name_ will change the expected position of this node in parent's set of 265 // children. Consider following scenario: 266 // 1. This node: "b"; parent's set: {"a", "b", "c"} 267 // 2. Rename("b", "d") 268 // 269 // After rename, parent's set should become: {"a", "b", "d"}, but if we simply change the 270 // name it will be {"a", "d", "b"}, which violates properties of the set. 271 // 272 // To make sure that parent's set is always valid, changing name is 3 steps procedure: 273 // 1. Remove this node from parent's set. 274 // 2 Change the name. 275 // 3. Add it back to the set. 276 // Rename of node without changing its parent. Still need to remove and re-add it to make 277 // sure lookup index is correct. 278 if (name_ != name) { 279 // If this is a root node, simply rename it. 280 if (parent_ == nullptr) { 281 name_ = name; 282 return; 283 } 284 285 auto it = parent_->children_.find(this); 286 CHECK(it != parent_->children_.end()); 287 parent_->children_.erase(it); 288 289 name_ = name; 290 291 parent_->children_.insert(this); 292 } 293 } 294 GetName()295 const std::string& GetName() const { 296 std::lock_guard<std::recursive_mutex> guard(*lock_); 297 return name_; 298 } 299 GetIoPath()300 const std::string& GetIoPath() const { return io_path_; } 301 GetTransforms()302 int GetTransforms() const { return transforms_; } 303 GetTransformsReason()304 int GetTransformsReason() const { return transforms_reason_; } 305 IsTransformsComplete()306 bool IsTransformsComplete() const { 307 return transforms_complete_.load(std::memory_order_acquire); 308 } 309 SetTransformsComplete(bool complete)310 void SetTransformsComplete(bool complete) { 311 transforms_complete_.store(complete, std::memory_order_release); 312 } 313 GetParent()314 node* GetParent() const { 315 std::lock_guard<std::recursive_mutex> guard(*lock_); 316 return parent_; 317 } 318 AddHandle(handle * h)319 inline void AddHandle(handle* h) { 320 std::lock_guard<std::recursive_mutex> guard(*lock_); 321 handles_.emplace_back(std::unique_ptr<handle>(h)); 322 } 323 DestroyHandle(handle * h)324 void DestroyHandle(handle* h) { 325 // A deadlock occurs between FuseDaemon lock and inode lock in the photo picker scenario. 326 // In order to address this deadlock, we release the FuseDaemon lock before closing the 327 // backing file. 328 std::unique_ptr<handle> temp; 329 330 lock_->lock(); 331 332 auto comp = [h](const std::unique_ptr<handle>& ptr) { return ptr.get() == h; }; 333 auto it = std::find_if(handles_.begin(), handles_.end(), comp); 334 CHECK(it != handles_.end()); 335 temp = std::move(*it); 336 handles_.erase(it); 337 338 lock_->unlock(); 339 } 340 HasCachedHandle()341 bool HasCachedHandle() const { 342 std::lock_guard<std::recursive_mutex> guard(*lock_); 343 344 for (const auto& handle : handles_) { 345 if (handle->cached) { 346 return true; 347 } 348 } 349 return false; 350 } 351 CheckHandleForUid(const uid_t uid)352 std::unique_ptr<FdAccessResult> CheckHandleForUid(const uid_t uid) const { 353 std::lock_guard<std::recursive_mutex> guard(*lock_); 354 355 bool found_handle = false; 356 bool redaction_not_needed = false; 357 for (const auto& handle : handles_) { 358 if (handle->uid == uid) { 359 found_handle = true; 360 redaction_not_needed |= !handle->ri->isRedactionNeeded(); 361 } 362 } 363 364 if (found_handle) { 365 return std::make_unique<FdAccessResult>(BuildPath(), !redaction_not_needed); 366 } 367 368 return std::make_unique<FdAccessResult>(std::string(), false); 369 } 370 SetName(std::string name)371 void SetName(std::string name) { 372 std::lock_guard<std::recursive_mutex> guard(*lock_); 373 name_ = std::move(name); 374 } 375 HasRedactedCache()376 bool HasRedactedCache() const { 377 std::lock_guard<std::recursive_mutex> guard(*lock_); 378 return has_redacted_cache_; 379 } 380 SetRedactedCache(bool state)381 void SetRedactedCache(bool state) { 382 std::lock_guard<std::recursive_mutex> guard(*lock_); 383 has_redacted_cache_ = state; 384 } 385 AddDirHandle(dirhandle * d)386 inline void AddDirHandle(dirhandle* d) { 387 std::lock_guard<std::recursive_mutex> guard(*lock_); 388 389 dirhandles_.emplace_back(std::unique_ptr<dirhandle>(d)); 390 } 391 DestroyDirHandle(dirhandle * d)392 void DestroyDirHandle(dirhandle* d) { 393 std::lock_guard<std::recursive_mutex> guard(*lock_); 394 395 auto comp = [d](const std::unique_ptr<dirhandle>& ptr) { return ptr.get() == d; }; 396 auto it = std::find_if(dirhandles_.begin(), dirhandles_.end(), comp); 397 CHECK(it != dirhandles_.end()); 398 dirhandles_.erase(it); 399 } 400 401 // Deletes the tree of nodes rooted at |tree|. 402 static void DeleteTree(node* tree); 403 404 // Looks up an absolute path rooted at |root|, or nullptr if no such path 405 // through the hierarchy exists. 406 static const node* LookupAbsolutePath(const node* root, const std::string& absolute_path); 407 408 // Looks up for the node with the given ino rooted at |root|, or nullptr if no such node exists. 409 static const node* LookupInode(const node* root, ino_t ino); 410 411 private: node(node * parent,const std::string & name,const std::string & io_path,const bool transforms_complete,const int transforms,const int transforms_reason,std::recursive_mutex * lock,ino_t ino,NodeTracker * tracker)412 node(node* parent, const std::string& name, const std::string& io_path, 413 const bool transforms_complete, const int transforms, const int transforms_reason, 414 std::recursive_mutex* lock, ino_t ino, NodeTracker* tracker) 415 : name_(name), 416 io_path_(io_path), 417 transforms_complete_(transforms_complete), 418 transforms_(transforms), 419 transforms_reason_(transforms_reason), 420 refcount_(0), 421 parent_(nullptr), 422 has_redacted_cache_(false), 423 deleted_(false), 424 lock_(lock), 425 ino_(ino), 426 tracker_(tracker) { 427 tracker_->NodeCreated(this); 428 Acquire(); 429 // This is a special case for the root node. All other nodes will have a 430 // non-null parent. 431 if (parent != nullptr) { 432 AddToParent(parent); 433 } 434 } 435 436 // Acquires a reference to a node. This maps to the "lookup count" specified 437 // by the FUSE documentation and must only happen under the circumstances 438 // documented in libfuse/include/fuse_lowlevel.h. Acquire()439 inline void Acquire() { 440 std::lock_guard<std::recursive_mutex> guard(*lock_); 441 refcount_++; 442 } 443 444 // Adds this node to a specified parent. AddToParent(node * parent)445 void AddToParent(node* parent) { 446 std::lock_guard<std::recursive_mutex> guard(*lock_); 447 // This method assumes this node is currently unparented. 448 CHECK(parent_ == nullptr); 449 // Check that the new parent isn't nullptr either. 450 CHECK(parent != nullptr); 451 452 parent_ = parent; 453 parent_->children_.insert(this); 454 455 // TODO(narayan, zezeozue): It's unclear why we need to call Acquire on the 456 // parent node when we're adding a child to it. 457 parent_->Acquire(); 458 } 459 460 // Removes this node from its current parent, and set its parent to nullptr. RemoveFromParent()461 void RemoveFromParent() { 462 std::lock_guard<std::recursive_mutex> guard(*lock_); 463 464 if (parent_ != nullptr) { 465 auto it = parent_->children_.find(this); 466 CHECK(it != parent_->children_.end()); 467 parent_->children_.erase(it); 468 469 parent_->Release(1); 470 parent_ = nullptr; 471 } 472 } 473 474 // Finds *all* non-deleted nodes matching |name| and runs the function |callback| on each 475 // node until |callback| returns true. 476 // When |callback| returns true, the matched node is returned ForChild(const std::string & name,const std::function<bool (node *)> & callback)477 node* ForChild(const std::string& name, const std::function<bool(node*)>& callback) const { 478 std::lock_guard<std::recursive_mutex> guard(*lock_); 479 480 // lower_bound will give us the first child with strcasecmp(child->name, name) >=0. 481 // For more context see comment on the NodeCompare struct. 482 auto start = children_.lower_bound(std::make_pair(name, 0)); 483 // upper_bound will give us the first child with strcasecmp(child->name, name) > 0 484 auto end = 485 children_.upper_bound(std::make_pair(name, std::numeric_limits<uintptr_t>::max())); 486 487 // Make a copy of the matches because calling callback might modify the list which will 488 // cause issues while iterating over them. 489 std::vector<node*> children(start, end); 490 491 for (node* child : children) { 492 if (!child->deleted_ && callback(child)) { 493 return child; 494 } 495 } 496 497 return nullptr; 498 } 499 500 // A custom heterogeneous comparator used for set of this node's children_ to speed up child 501 // node by name lookups. 502 // 503 // This comparator treats node* as pair (node->name_, node): two nodes* are first 504 // compared by their name using case-insenstive comparison function. If their names are equal, 505 // then pointers are compared as integers. 506 // 507 // See LookupChildByName function to see how this comparator is used. 508 // 509 // Note that it's important to first compare by name_, since it will make all nodes with same 510 // name (compared using strcasecmp) together, which allows LookupChildByName function to find 511 // range of the candidate nodes by issuing two binary searches. 512 struct NodeCompare { 513 using is_transparent = void; 514 operatorNodeCompare515 bool operator()(const node* lhs, const node* rhs) const { 516 int cmp = strcasecmp(lhs->name_.c_str(), rhs->name_.c_str()); 517 if (cmp != 0) { 518 return cmp < 0; 519 } 520 return reinterpret_cast<uintptr_t>(lhs) < reinterpret_cast<uintptr_t>(rhs); 521 } 522 operatorNodeCompare523 bool operator()(const node* lhs, const std::pair<std::string, uintptr_t>& rhs) const { 524 int cmp = strcasecmp(lhs->name_.c_str(), rhs.first.c_str()); 525 if (cmp != 0) { 526 return cmp < 0; 527 } 528 return reinterpret_cast<uintptr_t>(lhs) < rhs.second; 529 } 530 operatorNodeCompare531 bool operator()(const std::pair<std::string, uintptr_t>& lhs, const node* rhs) const { 532 int cmp = strcasecmp(lhs.first.c_str(), rhs->name_.c_str()); 533 if (cmp != 0) { 534 return cmp < 0; 535 } 536 return lhs.second < reinterpret_cast<uintptr_t>(rhs); 537 } 538 }; 539 540 // A helper function to recursively construct the absolute path of a given node. 541 // If |safe| is true, builds a PII safe path instead 542 void BuildPathForNodeRecursive(bool safe, const node* node, std::stringstream* path) const; 543 544 // The name of this node. Non-const because it can change during renames. 545 std::string name_; 546 // Filesystem path that will be used for IO (if it is non-empty) instead of node->BuildPath 547 const std::string io_path_; 548 // Whether any transforms required on |io_path_| are complete. 549 // If false, might need to call a node transform function with |transforms| below 550 std::atomic_bool transforms_complete_; 551 // Opaque flags that determines the 'required' transforms to perform on node 552 // before IO. These flags should not be interpreted in native but should be passed to the 553 // MediaProvider as part of a transform function and if successful, |transforms_complete_| 554 // should be set to true 555 const int transforms_; 556 // Opaque value indicating the reason why transforms are required. 557 // This value should not be interpreted in native but should be passed to the MediaProvider 558 // as part of a transform function 559 const int transforms_reason_; 560 // The reference count for this node. Guarded by |lock_|. 561 uint32_t refcount_; 562 // Set of children of this node. All of them contain a back reference 563 // to their parent. Guarded by |lock_|. 564 std::set<node*, NodeCompare> children_; 565 // Containing directory for this node. Guarded by |lock_|. 566 node* parent_; 567 // List of file handles associated with this node. Guarded by |lock_|. 568 std::vector<std::unique_ptr<handle>> handles_; 569 // List of directory handles associated with this node. Guarded by |lock_|. 570 std::vector<std::unique_ptr<dirhandle>> dirhandles_; 571 bool has_redacted_cache_; 572 bool deleted_; 573 std::recursive_mutex* lock_; 574 // Inode number of the file represented by this node. 575 const ino_t ino_; 576 577 NodeTracker* const tracker_; 578 ~node()579 ~node() { 580 RemoveFromParent(); 581 582 handles_.clear(); 583 dirhandles_.clear(); 584 585 tracker_->NodeDeleted(this); 586 } 587 588 friend class ::NodeTest; 589 }; 590 591 } // namespace fuse 592 } // namespace mediaprovider 593 594 #endif // MEDIA_PROVIDER_JNI_MODE_INL_H_ 595