• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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