lin
2025-03-11 6f4f7a76e03a46fefb056a4b18197f1d9e8aa939
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
// Copyright (C) 2018 The Android Open Source Project
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
 
#ifndef IORAP_SRC_INODE2FILENAME_SEARCH_DIRECTORIES_H_
#define IORAP_SRC_INODE2FILENAME_SEARCH_DIRECTORIES_H_
 
#include "common/expected.h"
#include "inode2filename/inode.h"
#include "inode2filename/system_call.h"
 
#include <fruit/fruit.h>
 
#include <rxcpp/rx.hpp>
namespace iorap::inode2filename {
 
// Tuple of (Inode -> (Filename|Errno))
struct InodeResult {
  // We set this error when all root directories have been searched and
  // yet we still could not find a corresponding filename for the inode under search.
  static constexpr int kCouldNotFindFilename = -ENOENT;
 
  Inode inode;
  // Value: Contains the filename (with a root directory as a prefix).
  // Error: Contains the errno, usually -ENOENT or perhaps a security error.
  iorap::expected<std::string /*filename*/, int /*errno*/> data;
 
  static InodeResult makeSuccess(Inode inode, std::string filename) {
    return InodeResult{inode, std::move(filename)};
  }
 
  static InodeResult makeFailure(Inode inode, int err_no) {
    return InodeResult{inode, iorap::unexpected{err_no}};
  }
 
  constexpr operator bool() const {
    return data.has_value();
  }
};
 
enum class SearchMode {
  // Test modes:
  kInProcessDirect,  // Execute the code directly.
  kInProcessIpc,     // Execute code via IPC layer using multiple threads.
  // Shipping mode:
  kOutOfProcessIpc,  // Execute code via fork+exec with IPC.
 
  // Note: in-process system-wide stat(2)/readdir/etc is blocked by selinux.
  // Attempting to call the test modes will fail with -EPERM.
  //
  // Use fork+exec mode in shipping configurations, which spawns inode2filename
  // as a separate command.
};
 
struct SearchDirectories {
  // Type-erased subset of rxcpp::connectable_observable<?>
  struct RxAnyConnectable {
    // Connects to the underlying observable.
    //
    // This kicks off the graph, streams begin emitting items.
    // This method will block until all items have been fully emitted
    // and processed by any subscribers.
    virtual void connect() = 0;
 
    virtual ~RxAnyConnectable(){}
  };
 
 
  // Create a cold observable of inode results (a lazy stream) corresponding
  // to the inode search list.
  //
  // A depth-first search is done on each of the root directories (in order),
  // until all inodes have been found (or until all directories have been exhausted).
  //
  // Some internal errors may occur during emission that aren't part of an InodeResult;
  // these will be sent to the error logcat and dropped.
  //
  // Calling this function does not begin the search.
  // The returned observable will begin the search after subscribing to it.
  //
  // The emitted InodeResult stream has these guarantees:
  // - All inodes in inode_list will eventually be emitted exactly once in an InodeResult
  // - When all inodes are found, directory traversal is halted.
  // - The order of emission can be considered arbitrary.
  //
  // Lifetime rules:
  // - The observable must be fully consumed before deleting any of the SearchDirectory's
  //   borrowed constructor parameters (e.g. the SystemCall).
  // - SearchDirectory itself can be deleted at any time after creating an observable.
  rxcpp::observable<InodeResult>
      FindFilenamesFromInodes(std::vector<std::string> root_directories,
                              std::vector<Inode> inode_list,
                              SearchMode mode);
 
  // Create a cold observable of inode results (a lazy stream) corresponding
  // to the inode search list.
  //
  // A depth-first search is done on each of the root directories (in order),
  // until all inodes have been found (or until all directories have been exhausted).
  //
  // Some internal errors may occur during emission that aren't part of an InodeResult;
  // these will be sent to the error logcat and dropped.
  //
  // Calling this function does not begin the search.
  // The returned observable will begin the search after subscribing to it.
  //
  // The emitted InodeResult stream has these guarantees:
  // - All inodes in inode_list will eventually be emitted exactly once in an InodeResult
  // - When all inodes are found, directory traversal is halted.
  // - The order of emission can be considered arbitrary.
  //
  // Lifetime rules:
  // - The observable must be fully consumed before deleting any of the SearchDirectory's
  //   borrowed constructor parameters (e.g. the SystemCall).
  // - SearchDirectory itself can be deleted at any time after creating an observable.
  std::pair<rxcpp::observable<InodeResult>, std::unique_ptr<RxAnyConnectable>>
      FindFilenamesFromInodesPair(std::vector<std::string> root_directories,
                                  std::vector<Inode> inode_list,
                                  SearchMode mode);
 
  // Any borrowed parameters here can also be borrowed by the observables returned by the above
  // member functions.
  //
  // The observables must be fully consumed within the lifetime of the borrowed parameters.
  INJECT(SearchDirectories(borrowed<SystemCall*> system_call))
      : system_call_(system_call) {}
 
  // TODO: is there a way to get rid of this second RxAnyConnectable parameter?
 private:
  // This gets passed around to lazy lambdas, so we must finish consuming any observables
  // before the injected system call is deleted.
  borrowed<SystemCall*> system_call_;
};
 
}  // namespace iorap::inode2filename
 
#endif  // IORAP_SRC_INODE2FILENAME_SEARCH_DIRECTORIES_H_