blob: dfd8141159dbcc521c2ba141a10288a33c51abbb [file] [log] [blame]
Narayan Kamathaef84a12020-01-02 15:20:13 +00001/*
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
Nikita Ioffe6d9d9dc2020-06-18 17:35:08 +010022#include <cstdint>
23#include <limits>
Narayan Kamathaef84a12020-01-02 15:20:13 +000024#include <list>
25#include <memory>
26#include <mutex>
Nikita Ioffe6d9d9dc2020-06-18 17:35:08 +010027#include <set>
Zimuzo Ezeozue67db40c2020-02-21 19:41:33 +000028#include <sstream>
Narayan Kamathaef84a12020-01-02 15:20:13 +000029#include <string>
Narayan Kamath568f17a2020-02-19 13:45:29 +000030#include <unordered_set>
Nikita Ioffe6d9d9dc2020-06-18 17:35:08 +010031#include <utility>
Narayan Kamathaef84a12020-01-02 15:20:13 +000032#include <vector>
33
34#include "libfuse_jni/ReaddirHelper.h"
35#include "libfuse_jni/RedactionInfo.h"
36
37class NodeTest;
38
39namespace mediaprovider {
40namespace fuse {
41
42struct handle {
Zim7e35c332020-04-03 19:25:00 +010043 explicit handle(int fd, const RedactionInfo* ri, bool cached) : fd(fd), ri(ri), cached(cached) {
Narayan Kamathbd22bb02020-01-08 16:02:50 +000044 CHECK(ri != nullptr);
45 }
46
Narayan Kamathbd22bb02020-01-08 16:02:50 +000047 const int fd;
48 const std::unique_ptr<const RedactionInfo> ri;
49 const bool cached;
Narayan Kamathaef84a12020-01-02 15:20:13 +000050
51 ~handle() { close(fd); }
52};
53
54struct dirhandle {
Narayan Kamathbd22bb02020-01-08 16:02:50 +000055 explicit dirhandle(DIR* dir) : d(dir), next_off(0) { CHECK(dir != nullptr); }
Narayan Kamathaef84a12020-01-02 15:20:13 +000056
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
65 ~dirhandle() { closedir(d); }
66};
67
Narayan Kamath568f17a2020-02-19 13:45:29 +000068// 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.
71static constexpr bool kEnableInodeTracking = true;
72
73class 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.
77class NodeTracker {
78 public:
Nandana Duttaa76d1e2020-04-29 09:42:42 +010079 explicit NodeTracker(std::recursive_mutex* lock) : lock_(lock) {}
Narayan Kamath568f17a2020-02-19 13:45:29 +000080
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
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
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
Narayan Kamathaef84a12020-01-02 15:20:13 +0000114class node {
115 public:
116 // Creates a new node with the specified parent, name and lock.
Narayan Kamath568f17a2020-02-19 13:45:29 +0000117 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);
Narayan Kamathaef84a12020-01-02 15:20:13 +0000124 }
125
126 // Creates a new root node. Root nodes have no parents by definition
127 // and their "name" must signify an absolute path.
Narayan Kamath568f17a2020-02-19 13:45:29 +0000128 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);
Narayan Kamathaef84a12020-01-02 15:20:13 +0000132
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.
Narayan Kamath568f17a2020-02-19 13:45:29 +0000140 static inline node* FromInode(__u64 ino, const NodeTracker* tracker) {
141 tracker->CheckTracked(ino);
Narayan Kamathaef84a12020-01-02 15:20:13 +0000142 return reinterpret_cast<node*>(static_cast<uintptr_t>(ino));
143 }
144
145 // Maps a node to its associated inode.
146 static __u64 ToInode(node* node) {
147 return static_cast<__u64>(reinterpret_cast<uintptr_t>(node));
148 }
149
Narayan Kamathaef84a12020-01-02 15:20:13 +0000150 // 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.
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
Zimuzo Ezeozue67db40c2020-02-21 19:41:33 +0000173 // 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
Narayan Kamatheca34252020-02-11 13:08:37 +0000177 // 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.
179 node* LookupChildByName(const std::string& name, bool acquire) const {
Narayan Kamathaef84a12020-01-02 15:20:13 +0000180 std::lock_guard<std::recursive_mutex> guard(*lock_);
181
Nikita Ioffe6d9d9dc2020-06-18 17:35:08 +0100182 // 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_) {
Narayan Kamatheca34252020-02-11 13:08:37 +0000191 if (acquire) {
192 child->Acquire();
193 }
Narayan Kamathaef84a12020-01-02 15:20:13 +0000194 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.
203 void SetDeleted() {
204 std::lock_guard<std::recursive_mutex> guard(*lock_);
205
206 deleted_ = true;
207 }
208
209 void Rename(const std::string& name, node* new_parent) {
Narayan Kamathaef84a12020-01-02 15:20:13 +0000210 std::lock_guard<std::recursive_mutex> guard(*lock_);
211
Zim87c7bf82020-01-07 18:11:27 +0000212 if (new_parent != parent_) {
213 RemoveFromParent();
Nikita Ioffe6d9d9dc2020-06-18 17:35:08 +0100214 name_ = name;
Zim87c7bf82020-01-07 18:11:27 +0000215 AddToParent(new_parent);
Nikita Ioffe6d9d9dc2020-06-18 17:35:08 +0100216 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);
Zim87c7bf82020-01-07 18:11:27 +0000247 }
Narayan Kamathaef84a12020-01-02 15:20:13 +0000248 }
249
250 const std::string& GetName() const {
251 std::lock_guard<std::recursive_mutex> guard(*lock_);
252 return name_;
253 }
254
Zima76c3492020-02-19 01:23:26 +0000255 node* GetParent() const {
256 std::lock_guard<std::recursive_mutex> guard(*lock_);
257 return parent_;
258 }
259
Narayan Kamathaef84a12020-01-02 15:20:13 +0000260 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
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
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
Zim62bd7172020-09-16 14:54:06 +0100285 bool HasCaseInsensitiveMatch() const {
286 std::lock_guard<std::recursive_mutex> guard(*lock_);
287 return has_case_insensitive_match_;
288 }
289
290 void SetCaseInsensitiveMatch() {
291 std::lock_guard<std::recursive_mutex> guard(*lock_);
292 has_case_insensitive_match_ = true;
293 }
294
Zim1e8804f2020-09-16 14:23:26 +0100295 bool HasRedactedCache() const {
296 std::lock_guard<std::recursive_mutex> guard(*lock_);
297 return has_redacted_cache_;
298 }
299
300 void SetRedactedCache(bool state) {
301 std::lock_guard<std::recursive_mutex> guard(*lock_);
302 has_redacted_cache_ = state;
303 }
304
Narayan Kamathaef84a12020-01-02 15:20:13 +0000305 inline void AddDirHandle(dirhandle* d) {
306 std::lock_guard<std::recursive_mutex> guard(*lock_);
307
308 dirhandles_.emplace_back(std::unique_ptr<dirhandle>(d));
309 }
310
311 void DestroyDirHandle(dirhandle* d) {
312 std::lock_guard<std::recursive_mutex> guard(*lock_);
313
314 auto comp = [d](const std::unique_ptr<dirhandle>& ptr) { return ptr.get() == d; };
315 auto it = std::find_if(dirhandles_.begin(), dirhandles_.end(), comp);
316 CHECK(it != dirhandles_.end());
317 dirhandles_.erase(it);
318 }
319
320 // Deletes the tree of nodes rooted at |tree|.
321 static void DeleteTree(node* tree);
322
323 // Looks up an absolute path rooted at |root|, or nullptr if no such path
324 // through the hierarchy exists.
325 static const node* LookupAbsolutePath(const node* root, const std::string& absolute_path);
326
327 private:
Narayan Kamath568f17a2020-02-19 13:45:29 +0000328 node(node* parent, const std::string& name, std::recursive_mutex* lock, NodeTracker* tracker)
329 : name_(name),
330 refcount_(0),
331 parent_(nullptr),
Zim1e8804f2020-09-16 14:23:26 +0100332 has_redacted_cache_(false),
Zim62bd7172020-09-16 14:54:06 +0100333 has_case_insensitive_match_(false),
Zim1e8804f2020-09-16 14:23:26 +0100334 deleted_(false),
Narayan Kamath568f17a2020-02-19 13:45:29 +0000335 lock_(lock),
336 tracker_(tracker) {
337 tracker_->NodeCreated(this);
Narayan Kamathaef84a12020-01-02 15:20:13 +0000338 Acquire();
339 // This is a special case for the root node. All other nodes will have a
340 // non-null parent.
341 if (parent != nullptr) {
342 AddToParent(parent);
343 }
344 }
345
Narayan Kamatheca34252020-02-11 13:08:37 +0000346 // Acquires a reference to a node. This maps to the "lookup count" specified
347 // by the FUSE documentation and must only happen under the circumstances
348 // documented in libfuse/include/fuse_lowlevel.h.
349 inline void Acquire() {
350 std::lock_guard<std::recursive_mutex> guard(*lock_);
351 refcount_++;
352 }
353
Narayan Kamathaef84a12020-01-02 15:20:13 +0000354 // Adds this node to a specified parent.
355 void AddToParent(node* parent) {
356 std::lock_guard<std::recursive_mutex> guard(*lock_);
357 // This method assumes this node is currently unparented.
358 CHECK(parent_ == nullptr);
359 // Check that the new parent isn't nullptr either.
360 CHECK(parent != nullptr);
361
362 parent_ = parent;
Nikita Ioffe6d9d9dc2020-06-18 17:35:08 +0100363 parent_->children_.insert(this);
Narayan Kamathaef84a12020-01-02 15:20:13 +0000364
365 // TODO(narayan, zezeozue): It's unclear why we need to call Acquire on the
366 // parent node when we're adding a child to it.
367 parent_->Acquire();
368 }
369
370 // Removes this node from its current parent, and set its parent to nullptr.
371 void RemoveFromParent() {
372 std::lock_guard<std::recursive_mutex> guard(*lock_);
373
374 if (parent_ != nullptr) {
Nikita Ioffe6d9d9dc2020-06-18 17:35:08 +0100375 auto it = parent_->children_.find(this);
376 CHECK(it != parent_->children_.end());
377 parent_->children_.erase(it);
Narayan Kamathaef84a12020-01-02 15:20:13 +0000378
Narayan Kamathaef84a12020-01-02 15:20:13 +0000379 parent_->Release(1);
380 parent_ = nullptr;
381 }
382 }
383
Nikita Ioffe6d9d9dc2020-06-18 17:35:08 +0100384 // A custom heterogeneous comparator used for set of this node's children_ to speed up child
385 // node by name lookups.
386 //
387 // This comparator treats node* as pair (node->name_, node): two nodes* are first
388 // compared by their name using case-insenstive comparison function. If their names are equal,
389 // then pointers are compared as integers.
390 //
391 // See LookupChildByName function to see how this comparator is used.
392 //
393 // Note that it's important to first compare by name_, since it will make all nodes with same
394 // name (compared using strcasecmp) together, which allows LookupChildByName function to find
395 // range of the candidate nodes by issuing two binary searches.
396 struct NodeCompare {
397 using is_transparent = void;
398
399 bool operator()(const node* lhs, const node* rhs) const {
400 int cmp = strcasecmp(lhs->name_.c_str(), rhs->name_.c_str());
401 if (cmp != 0) {
402 return cmp < 0;
403 }
404 return reinterpret_cast<uintptr_t>(lhs) < reinterpret_cast<uintptr_t>(rhs);
405 }
406
407 bool operator()(const node* lhs, const std::pair<std::string, uintptr_t>& rhs) const {
408 int cmp = strcasecmp(lhs->name_.c_str(), rhs.first.c_str());
409 if (cmp != 0) {
410 return cmp < 0;
411 }
412 return reinterpret_cast<uintptr_t>(lhs) < rhs.second;
413 }
414
415 bool operator()(const std::pair<std::string, uintptr_t>& lhs, const node* rhs) const {
416 int cmp = strcasecmp(lhs.first.c_str(), rhs->name_.c_str());
417 if (cmp != 0) {
418 return cmp < 0;
419 }
420 return lhs.second < reinterpret_cast<uintptr_t>(rhs);
421 }
422 };
423
Zimuzo Ezeozue67db40c2020-02-21 19:41:33 +0000424 // A helper function to recursively construct the absolute path of a given node.
425 // If |safe| is true, builds a PII safe path instead
426 void BuildPathForNodeRecursive(bool safe, const node* node, std::stringstream* path) const;
Narayan Kamathaef84a12020-01-02 15:20:13 +0000427
428 // The name of this node. Non-const because it can change during renames.
429 std::string name_;
430 // The reference count for this node. Guarded by |lock_|.
431 uint32_t refcount_;
Nikita Ioffe6d9d9dc2020-06-18 17:35:08 +0100432 // Set of children of this node. All of them contain a back reference
Narayan Kamathaef84a12020-01-02 15:20:13 +0000433 // to their parent. Guarded by |lock_|.
Nikita Ioffe6d9d9dc2020-06-18 17:35:08 +0100434 std::set<node*, NodeCompare> children_;
Narayan Kamathaef84a12020-01-02 15:20:13 +0000435 // Containing directory for this node. Guarded by |lock_|.
436 node* parent_;
437 // List of file handles associated with this node. Guarded by |lock_|.
438 std::vector<std::unique_ptr<handle>> handles_;
439 // List of directory handles associated with this node. Guarded by |lock_|.
440 std::vector<std::unique_ptr<dirhandle>> dirhandles_;
Zim1e8804f2020-09-16 14:23:26 +0100441 bool has_redacted_cache_;
Zim62bd7172020-09-16 14:54:06 +0100442 bool has_case_insensitive_match_;
Zim1e8804f2020-09-16 14:23:26 +0100443 bool deleted_;
Narayan Kamathaef84a12020-01-02 15:20:13 +0000444 std::recursive_mutex* lock_;
445
Narayan Kamath568f17a2020-02-19 13:45:29 +0000446 NodeTracker* const tracker_;
447
Narayan Kamathaef84a12020-01-02 15:20:13 +0000448 ~node() {
449 RemoveFromParent();
450
451 handles_.clear();
452 dirhandles_.clear();
Narayan Kamath568f17a2020-02-19 13:45:29 +0000453
454 tracker_->NodeDeleted(this);
Narayan Kamathaef84a12020-01-02 15:20:13 +0000455 }
456
457 friend class ::NodeTest;
458};
459
460} // namespace fuse
461} // namespace mediaprovider
462
463#endif // MEDIA_PROVIDER_JNI_MODE_INL_H_