// 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_
|