1 // Copyright (C) 2018 The Android Open Source Project
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 //      http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
15 #ifndef IORAP_SRC_INODE2FILENAME_SEARCH_DIRECTORIES_H_
16 #define IORAP_SRC_INODE2FILENAME_SEARCH_DIRECTORIES_H_
17 
18 #include "inode2filename/inode.h"
19 #include "inode2filename/system_call.h"
20 #include "inode2filename/inode_resolver.h"
21 
22 #include <fruit/fruit.h>
23 
24 #include <rxcpp/rx.hpp>
25 namespace iorap::inode2filename {
26 
27 // TODO: rename.
28 using SearchMode = ProcessMode;
29 
30 struct SearchDirectories {
31   // Type-erased subset of rxcpp::connectable_observable<?>
32   struct RxAnyConnectable {
33     // Connects to the underlying observable.
34     //
35     // This kicks off the graph, streams begin emitting items.
36     // This method will block until all items have been fully emitted
37     // and processed by any subscribers.
38     virtual void connect() = 0;
39 
~RxAnyConnectableSearchDirectories::RxAnyConnectable40     virtual ~RxAnyConnectable(){}
41   };
42 
43 
44   // Create a cold observable of inode results (a lazy stream) corresponding
45   // to the inode search list.
46   //
47   // A depth-first search is done on each of the root directories (in order),
48   // until all inodes have been found (or until all directories have been exhausted).
49   //
50   // Some internal errors may occur during emission that aren't part of an InodeResult;
51   // these will be sent to the error logcat and dropped.
52   //
53   // Calling this function does not begin the search.
54   // The returned observable will begin the search after subscribing to it.
55   //
56   // The emitted InodeResult stream has these guarantees:
57   // - All inodes in inode_list will eventually be emitted exactly once in an InodeResult
58   // - When all inodes are found, directory traversal is halted.
59   // - The order of emission can be considered arbitrary.
60   //
61   // Lifetime rules:
62   // - The observable must be fully consumed before deleting any of the SearchDirectory's
63   //   borrowed constructor parameters (e.g. the SystemCall).
64   // - SearchDirectory itself can be deleted at any time after creating an observable.
65   rxcpp::observable<InodeResult>
66       FindFilenamesFromInodes(std::vector<std::string> root_directories,
67                               std::vector<Inode> inode_list,
68                               SearchMode mode) const;
69 
70   // Create a cold observable of inode results (a lazy stream) corresponding
71   // to the inode search list.
72   //
73   // A depth-first search is done on each of the root directories (in order),
74   // until all inodes have been found (or until all directories have been exhausted).
75   //
76   // Some internal errors may occur during emission that aren't part of an InodeResult;
77   // these will be sent to the error logcat and dropped.
78   //
79   // Calling this function does not begin the search.
80   // The returned observable will begin the search after subscribing to it.
81   //
82   // The emitted InodeResult stream has these guarantees:
83   // - All inodes in inode_list will eventually be emitted exactly once in an InodeResult
84   // - When all inodes are found, directory traversal is halted.
85   // - The order of emission can be considered arbitrary.
86   //
87   // Lifetime rules:
88   // - The observable must be fully consumed before deleting any of the SearchDirectory's
89   //   borrowed constructor parameters (e.g. the SystemCall).
90   // - SearchDirectory itself can be deleted at any time after creating an observable.
91   std::pair<rxcpp::observable<InodeResult>, std::unique_ptr<RxAnyConnectable>>
92       FindFilenamesFromInodesPair(std::vector<std::string> root_directories,
93                                   std::vector<Inode> inode_list,
94                                   SearchMode mode) const;
95 
96   // No items on the output stream will be emitted until 'inodes' completes.
97   //
98   // The current algorithm is a naive DFS, so if it began too early it would either
99   // miss the search items or require traversal restarts.
100   //
101   // See above for more details.
102   rxcpp::observable<InodeResult>
103       FindFilenamesFromInodes(std::vector<std::string> root_directories,
104                               rxcpp::observable<Inode> inodes,
105                               SearchMode mode) const;
106 
107   rxcpp::observable<InodeResult>
108       ListAllFilenames(std::vector<std::string> root_directories) const;
109 
110   rxcpp::observable<InodeResult> FilterFilenamesForSpecificInodes(
111       // haystack that will be subscribed to until all in inode_list are found.
112       rxcpp::observable<InodeResult> all_inodes,
113       // key list: traverse all_inodes until we emit all results from inode_list.
114       std::vector<Inode> inode_list,
115       // all_inodes have a missing device number: use stat(2) to fill it in.
116       bool missing_device_number,
117       bool needs_verification) const;
118 
119   rxcpp::observable<InodeResult> EmitAllFilenames(
120       // haystack that will be subscribed to until all in inode_list are found.
121       rxcpp::observable<InodeResult> all_inodes,
122       // all_inodes have a missing device number: use stat(2) to fill it in.
123       bool missing_device_number,
124       bool needs_verification) const;
125 
126   // Any borrowed parameters here can also be borrowed by the observables returned by the above
127   // member functions.
128   //
129   // The observables must be fully consumed within the lifetime of the borrowed parameters.
INJECTSearchDirectories130   INJECT(SearchDirectories(borrowed<SystemCall*> system_call))
131       : system_call_(system_call) {}
132 
133   // TODO: is there a way to get rid of this second RxAnyConnectable parameter?
134  private:
135   // This gets passed around to lazy lambdas, so we must finish consuming any observables
136   // before the injected system call is deleted.
137   borrowed<SystemCall*> system_call_;
138 };
139 
140 }  // namespace iorap::inode2filename
141 
142 #endif  // IORAP_SRC_INODE2FILENAME_SEARCH_DIRECTORIES_H_
143