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