Snap for 8426163 from 6c1d6414fd3b155cd2be344693f61f414bff20ea to mainline-tzdata2-release
Change-Id: I29fc51ed48fa0d69d79d61cd480b0fc1e9abb6a2
diff --git a/.gitignore b/.gitignore
deleted file mode 100644
index 722d5e7..0000000
--- a/.gitignore
+++ /dev/null
@@ -1 +0,0 @@
-.vscode
diff --git a/METADATA b/METADATA
deleted file mode 100644
index 20b75c4..0000000
--- a/METADATA
+++ /dev/null
@@ -1,5 +0,0 @@
-third_party {
- # would be NOTICE save for:
- # incfs/kernel-headers/linux/incrementalfs.h
- license_type: RESTRICTED
-}
diff --git a/incfs/Android.bp b/incfs/Android.bp
index 88b3abb..a64821a 100644
--- a/incfs/Android.bp
+++ b/incfs/Android.bp
@@ -1,51 +1,28 @@
-// Copyright (C) 2020 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.
-
-package {
- default_applicable_licenses: ["system_incremental_delivery_incfs_license"],
-}
-
-// Added automatically by a large-scale-change that took the approach of
-// 'apply every license found to every target'. While this makes sure we respect
-// every license restriction, it may not be entirely correct.
-//
-// e.g. GPL in an MIT project might only apply to the contrib/ directory.
-//
-// Please consider splitting the single license below into multiple licenses,
-// taking care not to lose any license_kind information, and overriding the
-// default license using the 'licenses: [...]' property on targets as needed.
-//
-// For unused files, consider creating a 'fileGroup' with "//visibility:private"
-// to attach the license to, and including a comment whether the files may be
-// used in the current project.
-// See: http://go/android-license-faq
-license {
- name: "system_incremental_delivery_incfs_license",
- visibility: [":__subpackages__"],
- license_kinds: [
- "SPDX-license-identifier-Apache-2.0",
- "SPDX-license-identifier-GPL-2.0",
- ],
- license_text: [
- "NOTICE",
- ],
-}
-
cc_defaults {
- name: "libincfs_defaults_common",
+ name: "libincfs_defaults",
cpp_std: "c++2a",
cflags: ["-Werror", "-Wall", "-Wextra"],
+ defaults: ["linux_bionic_supported"],
+ export_include_dirs: ["include/", "kernel-headers",],
+ local_include_dirs: ["include/"],
+ header_libs: [
+ "libincfs_headers",
+ ],
+ export_header_lib_headers: ["libincfs_headers"],
+ static_libs: [
+ "libbase",
+ "com.android.sysprop.incremental",
+ ],
+ shared_libs: [
+ "libcrypto",
+ "liblog",
+ "libselinux",
+ ],
+ target: {
+ linux_bionic: {
+ enabled: true,
+ }
+ },
tidy: true,
tidy_checks: [
"android-*",
@@ -59,36 +36,6 @@
// operator=() does not handle self-assignment properly - all protobuf-generated classes
"-cert-oop54-cpp",
],
- target: {
- linux_bionic: {
- enabled: true,
- },
- },
-}
-
-cc_defaults {
- name: "libincfs_defaults",
- defaults: [
- "libincfs_defaults_common",
- "linux_bionic_supported",
- ],
- header_libs: ["libincfs_headers"],
- export_header_lib_headers: ["libincfs_headers"],
- static_libs: [
- "libbase",
- ],
- shared_libs: [
- "libcrypto",
- "liblog",
- "libselinux",
- ],
- target: {
- android: {
- static_libs: [
- "com.android.sysprop.incremental",
- ],
- },
- },
}
cc_library {
@@ -102,36 +49,6 @@
],
}
-cc_library_static {
- name: "libincfs-utils",
- defaults: ["libincfs_defaults_common"],
- local_include_dirs: ["util/include"],
- export_include_dirs: ["util/include"],
- host_supported: true,
- srcs: [
- "util/map_ptr.cpp",
- ],
- target: {
- android: {
- header_libs: ["libincfs_headers"],
- shared_libs: [
- "libbase",
- "libincfs",
- "libutils",
- ],
- },
- host: {
- static_libs: [
- "libbase",
- "libutils",
- ],
- },
- windows: {
- enabled: true,
- },
- },
-}
-
cc_library_headers {
name: "libincfs_headers",
export_include_dirs: ["include/", "kernel-headers",],
@@ -146,38 +63,19 @@
cc_test {
name: "libincfs-test",
defaults: ["libincfs_defaults"],
- local_include_dirs: ["tests/include"],
static_libs: [
"libincfs",
- "libincfs-utils",
],
shared_libs: [
"libbase",
- "libutils",
],
srcs: [
- "tests/util/map_ptr_test.cpp",
"tests/incfs_test.cpp",
"tests/MountRegistry_test.cpp",
],
require_root: true,
}
-cc_benchmark {
- name: "hardening-benchmark",
- defaults: ["libincfs_defaults"],
-
- srcs: [
- "tests/hardening_benchmark.cpp",
- ],
- static_libs: [
- "libziparchive_for_incfs",
- "libutils",
- "libincfs",
- "libincfs-utils",
- ],
-}
-
cc_binary {
name: "incfsdump",
defaults: ["libincfs_defaults"],
diff --git a/incfs/MountRegistry.cpp b/incfs/MountRegistry.cpp
index b7b53c8..c4752f3 100644
--- a/incfs/MountRegistry.cpp
+++ b/incfs/MountRegistry.cpp
@@ -18,18 +18,18 @@
#include "MountRegistry.h"
-#include <android-base/logging.h>
-#include <poll.h>
-#include <stdlib.h>
-
-#include <charconv>
-#include <set>
-#include <unordered_map>
-
#include "incfs.h"
#include "path.h"
#include "split.h"
+#include <android-base/logging.h>
+
+#include <charconv>
+#include <unordered_map>
+
+#include <poll.h>
+#include <stdlib.h>
+
using namespace std::literals;
namespace android::incfs {
@@ -69,7 +69,7 @@
std::vector<std::pair<std::string_view, std::string_view>> result;
result.reserve(mBase->binds.size());
for (auto it : mBase->binds) {
- result.emplace_back(it->second.subdir, it->first);
+ result.emplace_back(it->second.first, it->first);
}
return result;
}
@@ -88,12 +88,12 @@
std::string_view path) const {
auto it = rootByBindPoint.lower_bound(path);
if (it != rootByBindPoint.end() && it->first == path) {
- return {it->second.rootIndex, it};
+ return {it->second.second, it};
}
if (it != rootByBindPoint.begin()) {
--it;
if (path::startsWith(path, it->first) && path.size() > it->first.size()) {
- const auto index = it->second.rootIndex;
+ const auto index = it->second.second;
if (index >= int(roots.size()) || roots[index].empty()) {
LOG(ERROR) << "[incfs] Root for path '" << path << "' #" << index
<< " is not valid";
@@ -113,24 +113,24 @@
return roots[index].path;
}
-auto MountRegistry::Mounts::rootAndSubpathFor(std::string_view path) const
- -> std::pair<const Root*, std::string> {
+std::pair<std::string_view, std::string> MountRegistry::Mounts::rootAndSubpathFor(
+ std::string_view path) const {
auto normalPath = path::normalize(path);
auto [index, bindIt] = rootIndex(normalPath);
if (index < 0) {
return {};
}
- const auto& bindSubdir = bindIt->second.subdir;
+ const auto& bindSubdir = bindIt->second.first;
const auto pastBindSubdir = path::relativize(bindIt->first, normalPath);
const auto& root = roots[index];
- return {&root, path::join(bindSubdir, pastBindSubdir)};
+ return {root.path, path::join(bindSubdir, pastBindSubdir)};
}
void MountRegistry::Mounts::addRoot(std::string_view root, std::string_view backingDir) {
const auto index = roots.size();
auto absolute = path::normalize(root);
- auto it = rootByBindPoint.insert_or_assign(absolute, Bind{std::string(), int(index)}).first;
+ auto it = rootByBindPoint.insert_or_assign(absolute, std::pair{std::string(), index}).first;
roots.push_back({std::move(absolute), path::normalize(backingDir), {it}});
}
@@ -141,17 +141,13 @@
LOG(WARNING) << "[incfs] Trying to remove non-existent root '" << root << '\'';
return;
}
- const auto index = it->second.rootIndex;
+ const auto index = it->second.second;
if (index >= int(roots.size())) {
LOG(ERROR) << "[incfs] Root '" << root << "' has index " << index
<< " out of bounds (total roots count is " << roots.size();
return;
}
- for (auto bindIt : roots[index].binds) {
- rootByBindPoint.erase(bindIt);
- }
-
if (index + 1 == int(roots.size())) {
roots.pop_back();
// Run a small GC job here as we may be able to remove some obsolete
@@ -162,6 +158,37 @@
} else {
roots[index].clear();
}
+ rootByBindPoint.erase(it);
+}
+
+void MountRegistry::Mounts::moveBind(std::string_view src, std::string_view dest) {
+ auto srcAbsolute = path::normalize(src);
+ auto destAbsolute = path::normalize(dest);
+ if (srcAbsolute == destAbsolute) {
+ return;
+ }
+
+ auto [root, rootIt] = rootIndex(srcAbsolute);
+ if (root < 0) {
+ LOG(ERROR) << "[incfs] No root found for bind move from " << src << " to " << dest;
+ return;
+ }
+
+ if (roots[root].path == srcAbsolute) {
+ // moving the whole root
+ roots[root].path = destAbsolute;
+ }
+
+ // const_cast<> here is safe as we're erasing that element on the next line.
+ const auto newRootIt = rootByBindPoint
+ .insert_or_assign(std::move(destAbsolute),
+ std::pair{std::move(const_cast<std::string&>(
+ rootIt->second.first)),
+ root})
+ .first;
+ rootByBindPoint.erase(rootIt);
+ const auto bindIt = std::find(roots[root].binds.begin(), roots[root].binds.end(), rootIt);
+ *bindIt = newRootIt;
}
void MountRegistry::Mounts::addBind(std::string_view what, std::string_view where) {
@@ -174,10 +201,11 @@
const auto& currentBind = rootIt->first;
auto whatSubpath = path::relativize(currentBind, whatAbsolute);
- const auto& subdir = rootIt->second.subdir;
+ const auto& subdir = rootIt->second.first;
auto realSubdir = path::join(subdir, whatSubpath);
auto it = rootByBindPoint
- .insert_or_assign(path::normalize(where), Bind{std::move(realSubdir), root})
+ .insert_or_assign(path::normalize(where),
+ std::pair{std::move(realSubdir), root})
.first;
roots[root].binds.push_back(it);
}
@@ -216,23 +244,10 @@
auto lock = ensureUpToDate();
return std::string(mMounts.rootFor(path));
}
-
-auto MountRegistry::detailsFor(std::string_view path) -> Details {
- auto lock = ensureUpToDate();
- auto [root, subpath] = mMounts.rootAndSubpathFor(path);
- if (!root) {
- return {};
- }
- return {root->path, root->backing, subpath};
-}
-
std::pair<std::string, std::string> MountRegistry::rootAndSubpathFor(std::string_view path) {
auto lock = ensureUpToDate();
auto [root, subpath] = mMounts.rootAndSubpathFor(path);
- if (!root) {
- return {};
- }
- return {std::string(root->path), std::move(subpath)};
+ return {std::string(root), std::move(subpath)};
}
MountRegistry::Mounts MountRegistry::copyMounts() {
@@ -304,8 +319,8 @@
bool MountRegistry::Mounts::loadFrom(base::borrowed_fd fd, std::string_view filesystem) {
struct MountInfo {
+ std::string root;
std::string backing;
- std::set<std::string, std::less<>> roots;
std::vector<std::pair<std::string, std::string>> bindPoints;
};
std::unordered_map<std::string, MountInfo> mountsByGroup(16);
@@ -327,22 +342,20 @@
}
const auto groupId = items[2];
auto subdir = items[3];
- auto backingDir = items.rbegin()[1];
auto mountPoint = std::string(items[4]);
fixProcPath(mountPoint);
mountPoint = path::normalize(mountPoint);
auto& mount = mountsByGroup[std::string(groupId)];
- if (mount.backing.empty()) {
- mount.backing.assign(backingDir);
- } else if (mount.backing != backingDir) {
- LOG(WARNING) << "[incfs] root '" << *mount.roots.begin()
- << "' mounted in multiple places with different backing dirs, '"
- << mount.backing << "' vs new '" << backingDir
- << "'; updating to the new one";
- mount.backing.assign(backingDir);
- }
if (subdir == "/"sv) {
- mount.roots.emplace(mountPoint);
+ if (mount.root.empty()) {
+ mount.root.assign(mountPoint);
+ mount.backing.assign(items.rbegin()[1]);
+ fixProcPath(mount.backing);
+ } else {
+ LOG(WARNING) << "[incfs] incfs root '" << mount.root
+ << "' mounted in multiple places, ignoring later mount '" << mountPoint
+ << '\'';
+ }
subdir = ""sv;
}
mount.bindPoints.emplace_back(std::string(subdir), std::move(mountPoint));
@@ -353,7 +366,7 @@
}
rootByBindPoint.clear();
- // preserve the allocated capacity, but clear existing data
+ // preserve the allocated capacity, but clean existing data
roots.resize(mountsByGroup.size());
for (auto& root : roots) {
root.binds.clear();
@@ -361,32 +374,20 @@
int index = 0;
for (auto& [_, mount] : mountsByGroup) {
- if (mount.roots.empty()) {
- // the mount has no root, and without root we have no good way of accessing the
- // control files - so the only valid reaction here is to ignore it
- LOG(WARNING) << "[incfs] mount '" << mount.backing << "' has no root, but "
- << mount.bindPoints.size() << " bind(s), ignoring";
- continue;
- }
-
Root& root = roots[index];
auto& binds = root.binds;
binds.reserve(mount.bindPoints.size());
for (auto& [subdir, bind] : mount.bindPoints) {
- auto it = rootByBindPoint
- .insert_or_assign(std::move(bind), Bind{std::move(subdir), index})
- .first;
+ auto it =
+ rootByBindPoint
+ .insert_or_assign(std::move(bind), std::pair(std::move(subdir), index))
+ .first;
binds.push_back(it);
}
+ root.path = std::move(mount.root);
root.backing = std::move(mount.backing);
- fixProcPath(root.backing);
-
- // a trick here: given that as of now we either have exactly one root, or the preferred one
- // is always at the front, let's pick that one here.
- root.path = std::move(mount.roots.extract(mount.roots.begin()).value());
++index;
}
- roots.resize(index);
LOG(INFO) << "[incfs] Loaded " << filesystem << " mount info: " << roots.size()
<< " instances, " << rootByBindPoint.size() << " mount points";
@@ -395,7 +396,7 @@
LOG(INFO) << "[incfs] '" << root << '\'';
LOG(INFO) << "[incfs] backing: '" << backing << '\'';
for (auto&& bind : binds) {
- LOG(INFO) << "[incfs] bind : '" << bind->second.subdir << "'->'" << bind->first
+ LOG(INFO) << "[incfs] bind : '" << bind->second.first << "'->'" << bind->first
<< '\'';
}
}
diff --git a/incfs/incfs.cpp b/incfs/incfs.cpp
index 842e381..7994c54 100644
--- a/incfs/incfs.cpp
+++ b/incfs/incfs.cpp
@@ -23,7 +23,6 @@
#include <android-base/logging.h>
#include <android-base/no_destructor.h>
#include <android-base/parsebool.h>
-#include <android-base/properties.h>
#include <android-base/stringprintf.h>
#include <android-base/strings.h>
#include <android-base/unique_fd.h>
@@ -34,7 +33,6 @@
#include <openssl/sha.h>
#include <selinux/android.h>
#include <selinux/selinux.h>
-#include <sys/inotify.h>
#include <sys/mount.h>
#include <sys/poll.h>
#include <sys/stat.h>
@@ -44,8 +42,8 @@
#include <sys/xattr.h>
#include <unistd.h>
-#include <charconv>
#include <chrono>
+#include <fstream>
#include <iterator>
#include <mutex>
#include <optional>
@@ -63,9 +61,8 @@
IncFsFd cmd;
IncFsFd pendingReads;
IncFsFd logs;
- IncFsFd blocksWritten;
- constexpr IncFsControl(IncFsFd cmd, IncFsFd pendingReads, IncFsFd logs, IncFsFd blocksWritten)
- : cmd(cmd), pendingReads(pendingReads), logs(logs), blocksWritten(blocksWritten) {}
+ constexpr IncFsControl(IncFsFd cmd, IncFsFd pendingReads, IncFsFd logs)
+ : cmd(cmd), pendingReads(pendingReads), logs(logs) {}
};
static MountRegistry& registry() {
@@ -85,10 +82,6 @@
return openRaw(path::join(dir, name));
}
-static std::string indexPath(std::string_view root, IncFsFileId fileId) {
- return path::join(root, INCFS_INDEX_NAME, toString(fileId));
-}
-
static std::string rootForCmd(int fd) {
auto cmdFile = path::fromFd(fd);
if (cmdFile.empty()) {
@@ -111,29 +104,41 @@
return std::string(res);
}
+static Features readIncFsFeatures() {
+ static const char kSysfsFeaturesDir[] = "/sys/fs/" INCFS_NAME "/features";
+ const auto dir = path::openDir(kSysfsFeaturesDir);
+ if (!dir) {
+ return Features::none;
+ }
+
+ int res = Features::none;
+ while (auto entry = ::readdir(dir.get())) {
+ if (entry->d_type != DT_REG) {
+ continue;
+ }
+ if (entry->d_name == "corefs"sv) {
+ res |= Features::core;
+ }
+ }
+
+ return Features(res);
+}
+
+IncFsFeatures IncFs_Features() {
+ return IncFsFeatures(readIncFsFeatures());
+}
+
static bool isFsAvailable() {
static const char kProcFilesystems[] = "/proc/filesystems";
std::string filesystems;
if (!ab::ReadFileToString(kProcFilesystems, &filesystems)) {
return false;
}
- const auto result = filesystems.find("\t" INCFS_NAME "\n") != std::string::npos;
- LOG(INFO) << "isFsAvailable: " << (result ? "true" : "false");
- return result;
-}
-
-static int getFirstApiLevel() {
- uint64_t api_level = android::base::GetUintProperty<uint64_t>("ro.product.first_api_level", 0);
- LOG(INFO) << "Initial API level of the device: " << api_level;
- return api_level;
+ return filesystems.find("\t" INCFS_NAME "\n") != std::string::npos;
}
static std::string_view incFsPropertyValue() {
- constexpr const int R_API = 30;
- static const auto kDefaultValue{getFirstApiLevel() > R_API ? "on" : ""};
- static const ab::NoDestructor<std::string> kValue{
- IncrementalProperties::enable().value_or(kDefaultValue)};
- LOG(INFO) << "ro.incremental.enable: " << *kValue;
+ static const ab::NoDestructor<std::string> kValue{IncrementalProperties::enable().value_or("")};
return *kValue;
}
@@ -155,29 +160,6 @@
return {false, {}};
}
-template <class Callback>
-static IncFsErrorCode forEachFileIn(std::string_view dirPath, Callback cb) {
- auto dir = path::openDir(details::c_str(dirPath));
- if (!dir) {
- return -EINVAL;
- }
-
- int res = 0;
- while (auto entry = (errno = 0, ::readdir(dir.get()))) {
- if (entry->d_type != DT_REG) {
- continue;
- }
- ++res;
- if (!cb(entry->d_name)) {
- break;
- }
- }
- if (errno) {
- return -errno;
- }
- return res;
-}
-
namespace {
class IncFsInit {
@@ -203,12 +185,6 @@
return true;
}
std::call_once(loadedFlag_, [this] {
- if (isFsAvailable()) {
- // Loaded from a different process, I suppose.
- loaded_ = true;
- LOG(INFO) << "IncFS is already available, skipped loading";
- return;
- }
const ab::unique_fd fd(TEMP_FAILURE_RETRY(
::open(details::c_str(moduleName_), O_RDONLY | O_NOFOLLOW | O_CLOEXEC)));
if (fd < 0) {
@@ -249,50 +225,7 @@
return init().enabled();
}
-static Features readIncFsFeatures() {
- init().enabledAndReady();
-
- int res = Features::none | Features::mappingFilesProgressFixed;
-
- static const char kSysfsFeaturesDir[] = "/sys/fs/" INCFS_NAME "/features";
- const auto dir = path::openDir(kSysfsFeaturesDir);
- if (!dir) {
- PLOG(ERROR) << "IncFs_Features: failed to open features dir, assuming v1/none.";
- return Features(res);
- }
-
- while (auto entry = ::readdir(dir.get())) {
- if (entry->d_type != DT_REG) {
- continue;
- }
- if (entry->d_name == "corefs"sv) {
- res |= Features::core;
- } else if (entry->d_name == "v2"sv || entry->d_name == "report_uid"sv) {
- res |= Features::v2;
- }
- }
-
- LOG(INFO) << "IncFs_Features: " << ((res & Features::v2) ? "v2" : "v1");
-
- return Features(res);
-}
-
-IncFsFeatures IncFs_Features() {
- static const auto features = IncFsFeatures(readIncFsFeatures());
- return features;
-}
-
-bool isIncFsFdImpl(int fd) {
- struct statfs fs = {};
- if (::fstatfs(fd, &fs) != 0) {
- PLOG(WARNING) << __func__ << "(): could not fstatfs fd " << fd;
- return false;
- }
-
- return fs.f_type == (decltype(fs.f_type))INCFS_MAGIC_NUMBER;
-}
-
-bool isIncFsPathImpl(const char* path) {
+bool isIncFsPath(const char* path) {
struct statfs fs = {};
if (::statfs(path, &fs) != 0) {
PLOG(WARNING) << __func__ << "(): could not statfs " << path;
@@ -361,18 +294,11 @@
}
static std::string makeMountOptionsString(IncFsMountOptions options) {
- auto opts = ab::StringPrintf("read_timeout_ms=%u,readahead=0,rlog_pages=%u,rlog_wakeup_cnt=1,",
- unsigned(options.defaultReadTimeoutMs),
- unsigned(options.readLogBufferPages < 0
- ? INCFS_DEFAULT_PAGE_READ_BUFFER_PAGES
- : options.readLogBufferPages));
- if (features() & Features::v2) {
- ab::StringAppendF(&opts, "report_uid,");
- if (options.sysfsName && *options.sysfsName) {
- ab::StringAppendF(&opts, "sysfs_name=%s,", options.sysfsName);
- }
- }
- return opts;
+ return ab::StringPrintf("read_timeout_ms=%u,readahead=0,rlog_pages=%u,rlog_wakeup_cnt=1",
+ unsigned(options.defaultReadTimeoutMs),
+ unsigned(options.readLogBufferPages < 0
+ ? INCFS_DEFAULT_PAGE_READ_BUFFER_PAGES
+ : options.readLogBufferPages));
}
static IncFsControl* makeControl(const char* root) {
@@ -385,25 +311,12 @@
return nullptr;
}
auto logs = openRaw(root, INCFS_LOG_FILENAME);
- if (!logs.ok()) {
- return nullptr;
- }
- ab::unique_fd blocksWritten;
- if (features() & Features::v2) {
- blocksWritten = openRaw(root, INCFS_BLOCKS_WRITTEN_FILENAME);
- if (!blocksWritten.ok()) {
- return nullptr;
- }
- }
- auto control =
- IncFs_CreateControl(cmd.get(), pendingReads.get(), logs.get(), blocksWritten.get());
+ // logs may be absent, that's fine
+ auto control = IncFs_CreateControl(cmd.get(), pendingReads.get(), logs.get());
if (control) {
(void)cmd.release();
(void)pendingReads.release();
(void)logs.release();
- (void)blocksWritten.release();
- } else {
- errno = ENOMEM;
}
return control;
}
@@ -441,7 +354,7 @@
IncFsFileId res;
auto out = (char*)&res;
for (auto it = str.begin(); it != str.end(); it += 2, ++out) {
- static const auto fromChar = [](char src) -> int {
+ static const auto fromChar = [](char src) -> char {
if (src >= '0' && src <= '9') {
return src - '0';
}
@@ -451,7 +364,7 @@
return -1;
};
- const int c[2] = {fromChar(it[0]), fromChar(it[1])};
+ const char c[2] = {fromChar(it[0]), fromChar(it[1])};
if (c[0] == -1 || c[1] == -1) {
errno = EINVAL;
return kIncFsInvalidFileId;
@@ -491,24 +404,17 @@
}
static bool restoreconControlFiles(std::string_view targetDir) {
- static constexpr auto restorecon = [](const char* name) {
- if (const auto err = selinux_android_restorecon(name, SELINUX_ANDROID_RESTORECON_FORCE);
+ const std::string controlFilePaths[] = {path::join(targetDir, INCFS_PENDING_READS_FILENAME),
+ path::join(targetDir, INCFS_LOG_FILENAME)};
+ for (size_t i = 0; i < std::size(controlFilePaths); i++) {
+ if (const auto err = selinux_android_restorecon(controlFilePaths[i].c_str(),
+ SELINUX_ANDROID_RESTORECON_FORCE);
err != 0) {
+ PLOG(ERROR) << "[incfs] Failed to restorecon: " << controlFilePaths[i]
+ << " error code: " << err;
errno = -err;
- PLOG(ERROR) << "[incfs] Failed to restorecon: " << name;
return false;
}
- return true;
- };
- if (!restorecon(path::join(targetDir, INCFS_PENDING_READS_FILENAME).c_str())) {
- return false;
- }
- if (!restorecon(path::join(targetDir, INCFS_LOG_FILENAME).c_str())) {
- return false;
- }
- if ((features() & Features::v2) &&
- !restorecon(path::join(targetDir, INCFS_BLOCKS_WRITTEN_FILENAME).c_str())) {
- return false;
}
return true;
}
@@ -545,18 +451,17 @@
const auto opts = makeMountOptionsString(options);
if (::mount(backingPath, targetDir, INCFS_NAME, MS_NOSUID | MS_NODEV | MS_NOATIME,
opts.c_str())) {
- PLOG(ERROR) << "[incfs] Failed to mount IncFS filesystem: " << targetDir;
+ PLOG(ERROR) << "[incfs] Failed to mount IncFS filesystem: " << targetDir
+ << " errno: " << errno;
return nullptr;
}
if (!restoreconControlFiles(targetDir)) {
- (void)IncFs_Unmount(targetDir);
return nullptr;
}
auto control = makeControl(targetDir);
if (control == nullptr) {
- (void)IncFs_Unmount(targetDir);
return nullptr;
}
return control;
@@ -582,8 +487,6 @@
return control->pendingReads;
case LOGS:
return control->logs;
- case BLOCKS_WRITTEN:
- return control->blocksWritten;
default:
return -EINVAL;
}
@@ -599,13 +502,11 @@
out[CMD] = std::exchange(control->cmd, -1);
out[PENDING_READS] = std::exchange(control->pendingReads, -1);
out[LOGS] = std::exchange(control->logs, -1);
- out[BLOCKS_WRITTEN] = std::exchange(control->blocksWritten, -1);
return IncFsFdType::FDS_COUNT;
}
-IncFsControl* IncFs_CreateControl(IncFsFd cmd, IncFsFd pendingReads, IncFsFd logs,
- IncFsFd blocksWritten) {
- return new IncFsControl(cmd, pendingReads, logs, blocksWritten);
+IncFsControl* IncFs_CreateControl(IncFsFd cmd, IncFsFd pendingReads, IncFsFd logs) {
+ return new IncFsControl(cmd, pendingReads, logs);
}
void IncFs_DeleteControl(IncFsControl* control) {
@@ -619,9 +520,6 @@
if (control->logs >= 0) {
close(control->logs);
}
- if (control->blocksWritten >= 0) {
- close(control->blocksWritten);
- }
delete control;
}
}
@@ -796,48 +694,6 @@
return 0;
}
-IncFsErrorCode IncFs_MakeMappedFile(const IncFsControl* control, const char* path, int32_t mode,
- IncFsNewMappedFileParams params) {
- if (!control) {
- return -EINVAL;
- }
-
- auto [root, subpath] = registry().rootAndSubpathFor(path);
- if (root.empty()) {
- PLOG(WARNING) << "[incfs] makeMappedFile failed for path " << path << ", root is empty.";
- return -EINVAL;
- }
- if (params.size < 0) {
- LOG(WARNING) << "[incfs] makeMappedFile failed for path " << path
- << ", size is invalid: " << params.size;
- return -ERANGE;
- }
-
- const auto [subdir, name] = path::splitDirBase(subpath);
- incfs_create_mapped_file_args args = {
- .size = (uint64_t)params.size,
- .mode = (uint16_t)mode,
- .directory_path = (uint64_t)subdir.data(),
- .file_name = (uint64_t)name.data(),
- .source_offset = (uint64_t)params.sourceOffset,
- };
- static_assert(sizeof(args.source_file_id.bytes) == sizeof(params.sourceId.data));
- memcpy(args.source_file_id.bytes, params.sourceId.data, sizeof(args.source_file_id.bytes));
-
- if (::ioctl(control->cmd, INCFS_IOC_CREATE_MAPPED_FILE, &args)) {
- PLOG(WARNING) << "[incfs] makeMappedFile failed for " << root << " / " << subdir << " / "
- << name << " of " << params.size << " bytes starting at "
- << params.sourceOffset;
- return -errno;
- }
- if (::chmod(path::join(root, subpath).c_str(), mode)) {
- PLOG(WARNING) << "[incfs] makeMappedFile error: couldn't change file mode to 0" << std::oct
- << mode;
- }
-
- return 0;
-}
-
static IncFsErrorCode makeDir(const char* commandPath, int32_t mode, bool allowExisting) {
if (!::mkdir(commandPath, mode)) {
if (::chmod(commandPath, mode)) {
@@ -937,7 +793,7 @@
if (root.empty()) {
return -EINVAL;
}
- auto name = indexPath(root, fileId);
+ auto name = path::join(root, kIndexDir, toStringImpl(fileId));
return getMetadata(details::c_str(name), buffer, bufferSize);
}
@@ -955,16 +811,6 @@
return getMetadata(path, buffer, bufferSize);
}
-template <class GetterFunc, class Param>
-static IncFsFileId getId(GetterFunc getter, Param param) {
- char buffer[kIncFsFileIdStringLength];
- const auto res = getter(param, kIdAttrName, buffer, sizeof(buffer));
- if (res != sizeof(buffer)) {
- return kIncFsInvalidFileId;
- }
- return toFileIdImpl({buffer, std::size(buffer)});
-}
-
IncFsFileId IncFs_GetId(const IncFsControl* control, const char* path) {
if (!control) {
return kIncFsInvalidFileId;
@@ -975,7 +821,12 @@
errno = EINVAL;
return kIncFsInvalidFileId;
}
- return getId(::getxattr, path);
+ char buffer[kIncFsFileIdStringLength];
+ const auto res = ::getxattr(path, kIdAttrName, buffer, sizeof(buffer));
+ if (res != sizeof(buffer)) {
+ return kIncFsInvalidFileId;
+ }
+ return toFileIdImpl({buffer, std::size(buffer)});
}
static IncFsErrorCode getSignature(int fd, char buffer[], size_t* bufferSize) {
@@ -1005,7 +856,7 @@
if (root.empty()) {
return -EINVAL;
}
- auto file = indexPath(root, fileId);
+ auto file = path::join(root, kIndexDir, toStringImpl(fileId));
auto fd = openRaw(file);
if (fd < 0) {
return fd.get();
@@ -1086,9 +937,8 @@
return 0;
}
-template <class RawPendingRead>
-static int waitForReadsImpl(int fd, int32_t timeoutMs, RawPendingRead pendingReadsBuffer[],
- size_t* pendingReadsBufferSize) {
+static int waitForReads(int fd, int32_t timeoutMs, incfs_pending_read_info pendingReadsBuffer[],
+ size_t* pendingReadsBufferSize) {
using namespace std::chrono;
auto hrTimeout = steady_clock::duration(milliseconds(timeoutMs));
@@ -1136,81 +986,53 @@
return 0;
}
-template <class PublicPendingRead, class RawPendingRead>
-PublicPendingRead convertRead(RawPendingRead rawRead) {
- PublicPendingRead res = {
- .bootClockTsUs = rawRead.timestamp_us,
- .block = (IncFsBlockIndex)rawRead.block_index,
- .serialNo = rawRead.serial_number,
- };
- memcpy(&res.id.data, rawRead.file_id.bytes, sizeof(res.id.data));
-
- if constexpr (std::is_same_v<PublicPendingRead, IncFsReadInfoWithUid>) {
- if constexpr (std::is_same_v<RawPendingRead, incfs_pending_read_info2>) {
- res.uid = rawRead.uid;
- } else {
- res.uid = kIncFsNoUid;
- }
- }
- return res;
-}
-
-template <class RawPendingRead, class PublicPendingRead>
-static int waitForReads(IncFsFd readFd, int32_t timeoutMs, PublicPendingRead buffer[],
- size_t* bufferSize) {
- std::vector<RawPendingRead> pendingReads(*bufferSize);
- if (const auto res = waitForReadsImpl(readFd, timeoutMs, pendingReads.data(), bufferSize)) {
- return res;
- }
- for (size_t i = 0; i != *bufferSize; ++i) {
- buffer[i] = convertRead<PublicPendingRead>(pendingReads[i]);
- }
- return 0;
-}
-
-template <class PublicPendingRead>
-static int waitForReads(IncFsFd readFd, int32_t timeoutMs, PublicPendingRead buffer[],
- size_t* bufferSize) {
- if (features() & Features::v2) {
- return waitForReads<incfs_pending_read_info2>(readFd, timeoutMs, buffer, bufferSize);
- }
- return waitForReads<incfs_pending_read_info>(readFd, timeoutMs, buffer, bufferSize);
-}
-
IncFsErrorCode IncFs_WaitForPendingReads(const IncFsControl* control, int32_t timeoutMs,
IncFsReadInfo buffer[], size_t* bufferSize) {
if (!control || control->pendingReads < 0) {
return -EINVAL;
}
- return waitForReads(control->pendingReads, timeoutMs, buffer, bufferSize);
-}
-
-IncFsErrorCode IncFs_WaitForPendingReadsWithUid(const IncFsControl* control, int32_t timeoutMs,
- IncFsReadInfoWithUid buffer[], size_t* bufferSize) {
- if (!control || control->pendingReads < 0) {
- return -EINVAL;
+ std::vector<incfs_pending_read_info> pendingReads;
+ pendingReads.resize(*bufferSize);
+ if (const auto res =
+ waitForReads(control->pendingReads, timeoutMs, pendingReads.data(), bufferSize)) {
+ return res;
}
-
- return waitForReads(control->pendingReads, timeoutMs, buffer, bufferSize);
+ for (size_t i = 0; i != *bufferSize; ++i) {
+ buffer[i] = IncFsReadInfo{
+ .bootClockTsUs = pendingReads[i].timestamp_us,
+ .block = (IncFsBlockIndex)pendingReads[i].block_index,
+ .serialNo = pendingReads[i].serial_number,
+ };
+ memcpy(&buffer[i].id.data, pendingReads[i].file_id.bytes, sizeof(buffer[i].id.data));
+ }
+ return 0;
}
IncFsErrorCode IncFs_WaitForPageReads(const IncFsControl* control, int32_t timeoutMs,
IncFsReadInfo buffer[], size_t* bufferSize) {
- if (!control || control->logs < 0) {
+ if (!control) {
return -EINVAL;
}
- return waitForReads(control->logs, timeoutMs, buffer, bufferSize);
-}
-
-IncFsErrorCode IncFs_WaitForPageReadsWithUid(const IncFsControl* control, int32_t timeoutMs,
- IncFsReadInfoWithUid buffer[], size_t* bufferSize) {
- if (!control || control->logs < 0) {
+ auto logsFd = control->logs;
+ if (logsFd < 0) {
return -EINVAL;
}
-
- return waitForReads(control->logs, timeoutMs, buffer, bufferSize);
+ std::vector<incfs_pending_read_info> pendingReads;
+ pendingReads.resize(*bufferSize);
+ if (const auto res = waitForReads(logsFd, timeoutMs, pendingReads.data(), bufferSize)) {
+ return res;
+ }
+ for (size_t i = 0; i != *bufferSize; ++i) {
+ buffer[i] = IncFsReadInfo{
+ .bootClockTsUs = pendingReads[i].timestamp_us,
+ .block = (IncFsBlockIndex)pendingReads[i].block_index,
+ .serialNo = pendingReads[i].serial_number,
+ };
+ memcpy(&buffer[i].id.data, pendingReads[i].file_id.bytes, sizeof(buffer[i].id.data));
+ }
+ return 0;
}
static IncFsFd openForSpecialOps(int cmd, const char* path) {
@@ -1250,7 +1072,7 @@
if (root.empty()) {
return -EINVAL;
}
- auto name = indexPath(root, id);
+ auto name = path::join(root, kIndexDir, toStringImpl(id));
return openForSpecialOps(cmd, makeCommandPath(root, name).c_str());
}
@@ -1365,12 +1187,8 @@
return 0;
}
-bool IncFs_IsIncFsFd(int fd) {
- return isIncFsFdImpl(fd);
-}
-
bool IncFs_IsIncFsPath(const char* path) {
- return isIncFsPathImpl(path);
+ return isIncFsPath(path);
}
IncFsErrorCode IncFs_GetFilledRanges(int fd, IncFsSpan outBuffer, IncFsFilledRanges* filledRanges) {
@@ -1479,17 +1297,7 @@
return -error;
}
-static IncFsErrorCode isFullyLoadedV2(std::string_view root, IncFsFileId id) {
- if (::access(path::join(root, INCFS_INCOMPLETE_NAME, toStringImpl(id)).c_str(), F_OK)) {
- if (errno == ENOENT) {
- return 0; // no such incomplete file -> it's fully loaded.
- }
- return -errno;
- }
- return -ENODATA;
-}
-
-static IncFsErrorCode isFullyLoadedSlow(int fd) {
+IncFsErrorCode IncFs_IsFullyLoaded(int fd) {
char buffer[2 * sizeof(IncFsBlockRange)];
IncFsFilledRanges ranges;
auto res = IncFs_GetFilledRanges(fd, IncFsSpan{.data = buffer, .size = std::size(buffer)},
@@ -1527,567 +1335,6 @@
return -ENODATA;
}
-IncFsErrorCode IncFs_IsFullyLoaded(int fd) {
- if (features() & Features::v2) {
- const auto fdPath = path::fromFd(fd);
- if (fdPath.empty()) {
- return errno ? -errno : -EINVAL;
- }
- const auto id = getId(::fgetxattr, fd);
- if (id == kIncFsInvalidFileId) {
- return -errno;
- }
- return isFullyLoadedV2(registry().rootFor(fdPath), id);
- }
- return isFullyLoadedSlow(fd);
-}
-IncFsErrorCode IncFs_IsFullyLoadedByPath(const IncFsControl* control, const char* path) {
- if (!control || !path) {
- return -EINVAL;
- }
- const auto root = rootForCmd(control->cmd);
- if (root.empty()) {
- return -EINVAL;
- }
- const auto pathRoot = registry().rootFor(path);
- if (pathRoot != root) {
- return -EINVAL;
- }
- if (features() & Features::v2) {
- const auto id = getId(::getxattr, path);
- if (id == kIncFsInvalidFileId) {
- return -ENOTSUP;
- }
- return isFullyLoadedV2(root, id);
- }
- return isFullyLoadedSlow(openForSpecialOps(control->cmd, makeCommandPath(root, path).c_str()));
-}
-IncFsErrorCode IncFs_IsFullyLoadedById(const IncFsControl* control, IncFsFileId fileId) {
- if (!control) {
- return -EINVAL;
- }
- const auto root = rootForCmd(control->cmd);
- if (root.empty()) {
- return -EINVAL;
- }
- if (features() & Features::v2) {
- return isFullyLoadedV2(root, fileId);
- }
- return isFullyLoadedSlow(
- openForSpecialOps(control->cmd,
- makeCommandPath(root, indexPath(root, fileId)).c_str()));
-}
-
-static IncFsErrorCode isEverythingLoadedV2(const IncFsControl* control) {
- const auto root = rootForCmd(control->cmd);
- if (root.empty()) {
- return -EINVAL;
- }
- auto res = forEachFileIn(path::join(root, INCFS_INCOMPLETE_NAME), [](auto) { return false; });
- return res < 0 ? res : res > 0 ? -ENODATA : 0;
-}
-
-static IncFsErrorCode isEverythingLoadedSlow(const IncFsControl* control) {
- const auto root = rootForCmd(control->cmd);
- if (root.empty()) {
- return -EINVAL;
- }
- // No special API for this version of the driver, need to recurse and check each file
- // separately. Can at least speed it up by iterating over the .index/ dir and not dealing with
- // the directory tree.
- const auto indexPath = path::join(root, INCFS_INDEX_NAME);
- const auto dir = path::openDir(indexPath.c_str());
- if (!dir) {
- return -EINVAL;
- }
- while (const auto entry = ::readdir(dir.get())) {
- if (entry->d_type != DT_REG) {
- continue;
- }
- const auto name = path::join(indexPath, entry->d_name);
- auto fd =
- ab::unique_fd(openForSpecialOps(control->cmd, makeCommandPath(root, name).c_str()));
- if (fd.get() < 0) {
- PLOG(WARNING) << __func__ << "(): can't open " << entry->d_name << " for special ops";
- return fd.release();
- }
- const auto checkFullyLoaded = IncFs_IsFullyLoaded(fd.get());
- if (checkFullyLoaded == 0 || checkFullyLoaded == -EOPNOTSUPP ||
- checkFullyLoaded == -ENOTSUP || checkFullyLoaded == -ENOENT) {
- // special kinds of files may return an error here, but it still means
- // _this_ file is OK - you simply need to check the rest. E.g. can't query
- // a mapped file, instead need to check its parent.
- continue;
- }
- return checkFullyLoaded;
- }
- return 0;
-}
-
-IncFsErrorCode IncFs_IsEverythingFullyLoaded(const IncFsControl* control) {
- if (!control) {
- return -EINVAL;
- }
- if (features() & Features::v2) {
- return isEverythingLoadedV2(control);
- }
- return isEverythingLoadedSlow(control);
-}
-
-IncFsErrorCode IncFs_SetUidReadTimeouts(const IncFsControl* control,
- const IncFsUidReadTimeouts timeouts[], size_t count) {
- if (!control) {
- return -EINVAL;
- }
- if (!(features() & Features::v2)) {
- return -ENOTSUP;
- }
-
- std::vector<incfs_per_uid_read_timeouts> argTimeouts(count);
- for (size_t i = 0; i != count; ++i) {
- argTimeouts[i] = incfs_per_uid_read_timeouts{
- .uid = (uint32_t)timeouts[i].uid,
- .min_time_us = timeouts[i].minTimeUs,
- .min_pending_time_us = timeouts[i].minPendingTimeUs,
- .max_pending_time_us = timeouts[i].maxPendingTimeUs,
- };
- }
- incfs_set_read_timeouts_args args = {.timeouts_array = (uint64_t)(uintptr_t)argTimeouts.data(),
- .timeouts_array_size = uint32_t(
- argTimeouts.size() * sizeof(*argTimeouts.data()))};
- if (::ioctl(control->cmd, INCFS_IOC_SET_READ_TIMEOUTS, &args)) {
- PLOG(WARNING) << "[incfs] setUidReadTimeouts failed";
- return -errno;
- }
- return 0;
-}
-
-IncFsErrorCode IncFs_GetUidReadTimeouts(const IncFsControl* control,
- IncFsUidReadTimeouts timeouts[], size_t* bufferSize) {
- if (!control || !bufferSize) {
- return -EINVAL;
- }
- if (!(features() & Features::v2)) {
- return -ENOTSUP;
- }
-
- std::vector<incfs_per_uid_read_timeouts> argTimeouts(*bufferSize);
- incfs_get_read_timeouts_args args = {.timeouts_array = (uint64_t)(uintptr_t)argTimeouts.data(),
- .timeouts_array_size = uint32_t(
- argTimeouts.size() * sizeof(*argTimeouts.data())),
- .timeouts_array_size_out = args.timeouts_array_size};
- if (::ioctl(control->cmd, INCFS_IOC_GET_READ_TIMEOUTS, &args)) {
- if (errno == E2BIG) {
- *bufferSize = args.timeouts_array_size_out / sizeof(*argTimeouts.data());
- }
- return -errno;
- }
-
- *bufferSize = args.timeouts_array_size_out / sizeof(*argTimeouts.data());
- for (size_t i = 0; i != *bufferSize; ++i) {
- timeouts[i].uid = argTimeouts[i].uid;
- timeouts[i].minTimeUs = argTimeouts[i].min_time_us;
- timeouts[i].minPendingTimeUs = argTimeouts[i].min_pending_time_us;
- timeouts[i].maxPendingTimeUs = argTimeouts[i].max_pending_time_us;
- }
- return 0;
-}
-
-// Trying to detect if this is a mapped file.
-// Not the best way as it might return true for other system files.
-// TODO: remove after IncFS returns ENOTSUP for such files.
-static bool isMapped(int fd) {
- char buffer[kIncFsFileIdStringLength];
- const auto res = ::fgetxattr(fd, kIdAttrName, buffer, sizeof(buffer));
- return res != sizeof(buffer);
-}
-
-static IncFsErrorCode getFileBlockCount(int fd, IncFsBlockCounts* blockCount) {
- if (isMapped(fd)) {
- return -ENOTSUP;
- }
-
- incfs_get_block_count_args args = {};
- auto res = ::ioctl(fd, INCFS_IOC_GET_BLOCK_COUNT, &args);
- if (res < 0) {
- return -errno;
- }
- *blockCount = IncFsBlockCounts{
- .totalDataBlocks = args.total_data_blocks_out,
- .filledDataBlocks = args.filled_data_blocks_out,
- .totalHashBlocks = args.total_hash_blocks_out,
- .filledHashBlocks = args.filled_hash_blocks_out,
- };
- return 0;
-}
-
-IncFsErrorCode IncFs_GetFileBlockCountById(const IncFsControl* control, IncFsFileId id,
- IncFsBlockCounts* blockCount) {
- if (!control) {
- return -EINVAL;
- }
- if (!(features() & Features::v2)) {
- return -ENOTSUP;
- }
- const auto root = rootForCmd(control->cmd);
- if (root.empty()) {
- return -EINVAL;
- }
- auto name = indexPath(root, id);
- auto fd = openRaw(name);
- if (fd < 0) {
- return fd.get();
- }
- return getFileBlockCount(fd, blockCount);
-}
-
-IncFsErrorCode IncFs_GetFileBlockCountByPath(const IncFsControl* control, const char* path,
- IncFsBlockCounts* blockCount) {
- if (!control) {
- return -EINVAL;
- }
- if (!(features() & Features::v2)) {
- return -ENOTSUP;
- }
- const auto pathRoot = registry().rootFor(path);
- const auto root = rootForCmd(control->cmd);
- if (root.empty() || root != pathRoot) {
- return -EINVAL;
- }
- auto fd = openRaw(path);
- if (fd < 0) {
- return fd.get();
- }
- return getFileBlockCount(fd, blockCount);
-}
-
-IncFsErrorCode IncFs_ListIncompleteFiles(const IncFsControl* control, IncFsFileId ids[],
- size_t* bufferSize) {
- if (!control || !bufferSize) {
- return -EINVAL;
- }
- if (!(features() & Features::v2)) {
- return -ENOTSUP;
- }
- const auto root = rootForCmd(control->cmd);
- if (root.empty()) {
- return -EINVAL;
- }
- size_t index = 0;
- int error = 0;
- const auto res = forEachFileIn(path::join(root, INCFS_INCOMPLETE_NAME), [&](const char* name) {
- if (index >= *bufferSize) {
- error = -E2BIG;
- } else {
- ids[index] = IncFs_FileIdFromString(name);
- }
- ++index;
- return true;
- });
- if (res < 0) {
- return res;
- }
- *bufferSize = index;
- return error ? error : 0;
-}
-
-IncFsErrorCode IncFs_ForEachFile(const IncFsControl* control, void* context, FileCallback cb) {
- if (!control || !cb) {
- return -EINVAL;
- }
- const auto root = rootForCmd(control->cmd);
- if (root.empty()) {
- return -EINVAL;
- }
- return forEachFileIn(path::join(root, INCFS_INDEX_NAME), [&](const char* name) {
- return cb(context, control, IncFs_FileIdFromString(name));
- });
-}
-
-IncFsErrorCode IncFs_ForEachIncompleteFile(const IncFsControl* control, void* context,
- FileCallback cb) {
- if (!control || !cb) {
- return -EINVAL;
- }
- if (!(features() & Features::v2)) {
- return -ENOTSUP;
- }
- const auto root = rootForCmd(control->cmd);
- if (root.empty()) {
- return -EINVAL;
- }
- return forEachFileIn(path::join(root, INCFS_INCOMPLETE_NAME), [&](const char* name) {
- return cb(context, control, IncFs_FileIdFromString(name));
- });
-}
-
-IncFsErrorCode IncFs_WaitForLoadingComplete(const IncFsControl* control, int32_t timeoutMs) {
- if (!control) {
- return -EINVAL;
- }
- if (!(features() & Features::v2)) {
- return -ENOTSUP;
- }
-
- using namespace std::chrono;
- auto hrTimeout = steady_clock::duration(milliseconds(timeoutMs));
-
- const auto root = rootForCmd(control->cmd);
- if (root.empty()) {
- return -EINVAL;
- }
-
- ab::unique_fd fd(inotify_init1(IN_NONBLOCK | IN_CLOEXEC));
- if (!fd.ok()) {
- return -EFAULT;
- }
-
- // first create all the watches, and only then list existing files to prevent races
- auto dirPath = path::join(root, INCFS_INCOMPLETE_NAME);
- int watchFd = inotify_add_watch(fd.get(), dirPath.c_str(), IN_DELETE);
- if (watchFd < 0) {
- return -errno;
- }
-
- size_t count = 0;
- auto res = IncFs_ListIncompleteFiles(control, nullptr, &count);
- if (!res) {
- return 0;
- }
- if (res != -E2BIG) {
- return res;
- }
-
- while (hrTimeout > hrTimeout.zero()) {
- const auto startTs = steady_clock::now();
-
- pollfd pfd = {fd.get(), POLLIN, 0};
- const auto res = ::poll(&pfd, 1, duration_cast<milliseconds>(hrTimeout).count());
- if (res == 0) {
- return -ETIMEDOUT;
- }
- if (res < 0) {
- const auto error = errno;
- if (error != EINTR) {
- PLOG(ERROR) << "poll() failed";
- return -error;
- }
- } else {
- // empty the inotify fd first to not miss any new deletions,
- // then check if the directory is empty.
- char buffer[sizeof(inotify_event) + NAME_MAX + 1];
- for (;;) {
- auto err = TEMP_FAILURE_RETRY(::read(fd.get(), buffer, sizeof(buffer)));
- if (err < 0) {
- if (errno == EAGAIN) { // no new events
- break;
- }
- return -errno;
- }
- }
-
- size_t count = 0;
- auto res = IncFs_ListIncompleteFiles(control, nullptr, &count);
- if (!res) {
- return 0;
- }
- if (res != -E2BIG) {
- return res;
- }
- }
- hrTimeout -= steady_clock::now() - startTs;
- }
-
- return -ETIMEDOUT;
-}
-
-IncFsErrorCode IncFs_WaitForFsWrittenBlocksChange(const IncFsControl* control, int32_t timeoutMs,
- IncFsSize* count) {
- if (!control || !count) {
- return -EINVAL;
- }
- if (!(features() & Features::v2)) {
- return -ENOTSUP;
- }
-
- using namespace std::chrono;
- auto hrTimeout = steady_clock::duration(milliseconds(timeoutMs));
-
- while (hrTimeout > hrTimeout.zero()) {
- const auto startTs = steady_clock::now();
-
- pollfd pfd = {control->blocksWritten, POLLIN, 0};
- const auto res = ::poll(&pfd, 1, duration_cast<milliseconds>(hrTimeout).count());
- if (res > 0) {
- break;
- }
- if (res == 0) {
- return -ETIMEDOUT;
- }
- const auto error = errno;
- if (error != EINTR) {
- PLOG(ERROR) << "poll() failed";
- return -error;
- }
- hrTimeout -= steady_clock::now() - startTs;
- }
-
- char str[32];
- auto size = ::read(control->blocksWritten, str, sizeof(str));
- if (size < 0) {
- const auto error = errno;
- PLOG(ERROR) << "read() failed";
- return -error;
- }
- const auto res = std::from_chars(str, str + size, *count);
- if (res.ec != std::errc{}) {
- return res.ec == std::errc::invalid_argument ? -EINVAL : -ERANGE;
- }
-
- return 0;
-}
-
-static IncFsErrorCode reserveSpace(const char* backingPath, IncFsSize size) {
- auto fd = ab::unique_fd(::open(backingPath, O_WRONLY | O_CLOEXEC));
- if (fd < 0) {
- return -errno;
- }
- struct stat st = {};
- if (::fstat(fd.get(), &st)) {
- return -errno;
- }
- if (size == kIncFsTrimReservedSpace) {
- if (::ftruncate(fd.get(), st.st_size)) {
- return -errno;
- }
- } else {
- // Add 1.5% of the size for the hash tree and the blockmap, and some more blocks
- // for fixed overhead.
- // hash tree is ~33 bytes / page, and blockmap is 10 bytes / page
- // no need to round to a page size as filesystems already do that.
- const auto backingSize = IncFsSize(size * 1.015) + INCFS_DATA_FILE_BLOCK_SIZE * 4;
- if (backingSize < st.st_size) {
- return -EPERM;
- }
- if (::fallocate(fd.get(), FALLOC_FL_KEEP_SIZE, 0, backingSize)) {
- return -errno;
- }
- }
- return 0;
-}
-
-IncFsErrorCode IncFs_ReserveSpaceByPath(const IncFsControl* control, const char* path,
- IncFsSize size) {
- if (!control || (size != kIncFsTrimReservedSpace && size < 0)) {
- return -EINVAL;
- }
- const auto [pathRoot, backingRoot, subpath] = registry().detailsFor(path);
- const auto root = rootForCmd(control->cmd);
- if (root.empty() || root != pathRoot) {
- return -EINVAL;
- }
- return reserveSpace(path::join(backingRoot, subpath).c_str(), size);
-}
-
-IncFsErrorCode IncFs_ReserveSpaceById(const IncFsControl* control, IncFsFileId id, IncFsSize size) {
- if (!control || (size != kIncFsTrimReservedSpace && size < 0)) {
- return -EINVAL;
- }
- const auto root = rootForCmd(control->cmd);
- if (root.empty()) {
- return -EINVAL;
- }
- auto path = indexPath(root, id);
- const auto [pathRoot, backingRoot, subpath] = registry().detailsFor(path);
- if (root != pathRoot) {
- return -EINVAL;
- }
- return reserveSpace(path::join(backingRoot, subpath).c_str(), size);
-}
-
-template <class IntType>
-static int readIntFromFile(std::string_view rootDir, std::string_view subPath, IntType& result) {
- std::string content;
- if (!ab::ReadFileToString(path::join(rootDir, subPath), &content)) {
- PLOG(ERROR) << "IncFs_GetMetrics: failed to read file: " << rootDir << "/" << subPath;
- return -errno;
- }
- const auto res = std::from_chars(content.data(), content.data() + content.size(), result);
- if (res.ec != std::errc()) {
- return -static_cast<int>(res.ec);
- }
- return 0;
-}
-
-IncFsErrorCode IncFs_GetMetrics(const char* sysfsName, IncFsMetrics* metrics) {
- if (!sysfsName || !*sysfsName) {
- return -EINVAL;
- }
-
- const auto kSysfsMetricsDir =
- ab::StringPrintf("/sys/fs/%s/instances/%s", INCFS_NAME, sysfsName);
-
- int err;
- if (err = readIntFromFile(kSysfsMetricsDir, "reads_delayed_min", metrics->readsDelayedMin);
- err != 0) {
- return err;
- }
- if (err = readIntFromFile(kSysfsMetricsDir, "reads_delayed_min_us", metrics->readsDelayedMinUs);
- err != 0) {
- return err;
- }
- if (err = readIntFromFile(kSysfsMetricsDir, "reads_delayed_pending",
- metrics->readsDelayedPending);
- err != 0) {
- return err;
- }
- if (err = readIntFromFile(kSysfsMetricsDir, "reads_delayed_pending_us",
- metrics->readsDelayedPendingUs);
- err != 0) {
- return err;
- }
- if (err = readIntFromFile(kSysfsMetricsDir, "reads_failed_hash_verification",
- metrics->readsFailedHashVerification);
- err != 0) {
- return err;
- }
- if (err = readIntFromFile(kSysfsMetricsDir, "reads_failed_other", metrics->readsFailedOther);
- err != 0) {
- return err;
- }
- if (err = readIntFromFile(kSysfsMetricsDir, "reads_failed_timed_out",
- metrics->readsFailedTimedOut);
- err != 0) {
- return err;
- }
- return 0;
-}
-
-IncFsErrorCode IncFs_GetLastReadError(const IncFsControl* control,
- IncFsLastReadError* lastReadError) {
- if (!control) {
- return -EINVAL;
- }
- if (!(features() & Features::v2)) {
- return -ENOTSUP;
- }
- incfs_get_last_read_error_args args = {};
- auto res = ::ioctl(control->cmd, INCFS_IOC_GET_LAST_READ_ERROR, &args);
- if (res < 0) {
- PLOG(ERROR) << "[incfs] IncFs_GetLastReadError failed.";
- return -errno;
- }
- *lastReadError = IncFsLastReadError{
- .timestampUs = args.time_us_out,
- .block = static_cast<IncFsBlockIndex>(args.page_out),
- .errorNo = args.errno_out,
- .uid = static_cast<IncFsUid>(args.uid_out),
- };
- static_assert(sizeof(args.file_id_out.bytes) == sizeof(lastReadError->id.data));
- memcpy(lastReadError->id.data, args.file_id_out.bytes, sizeof(args.file_id_out.bytes));
- return 0;
-}
-
MountRegistry& android::incfs::defaultMountRegistry() {
return registry();
}
diff --git a/incfs/incfsdump/dump.cpp b/incfs/incfsdump/dump.cpp
index 4959062..e6c49b6 100644
--- a/incfs/incfsdump/dump.cpp
+++ b/incfs/incfsdump/dump.cpp
@@ -47,6 +47,7 @@
#include <string_view>
using namespace std::literals;
+namespace ab = android::base;
namespace {
@@ -198,7 +199,7 @@
class Dump {
public:
Dump(std::string_view backingFile)
- : mBackingFile(android::base::Basename(std::string(backingFile))), mIn(backingFile) {}
+ : mBackingFile(ab::Basename(std::string(backingFile))), mIn(backingFile) {}
void run() {
if (!mIn) {
diff --git a/incfs/include/MountRegistry.h b/incfs/include/MountRegistry.h
index 1ff320f..f8cfe4d 100644
--- a/incfs/include/MountRegistry.h
+++ b/incfs/include/MountRegistry.h
@@ -35,12 +35,8 @@
class MountRegistry final {
public:
- struct Bind {
- std::string subdir;
- int rootIndex;
- };
// std::less<> enables heterogeneous lookups, e.g. by a string_view
- using BindMap = std::map<std::string, Bind, std::less<>>;
+ using BindMap = std::map<std::string, std::pair<std::string, int>, std::less<>>;
class Mounts final {
struct Root {
@@ -84,7 +80,7 @@
bool empty() const { return roots.empty(); }
std::string_view rootFor(std::string_view path) const;
- std::pair<const Root*, std::string> rootAndSubpathFor(std::string_view path) const;
+ std::pair<std::string_view, std::string> rootAndSubpathFor(std::string_view path) const;
void swap(Mounts& other);
void clear();
@@ -92,6 +88,7 @@
void addRoot(std::string_view root, std::string_view backingDir);
void removeRoot(std::string_view root);
void addBind(std::string_view what, std::string_view where);
+ void moveBind(std::string_view src, std::string_view dest);
void removeBind(std::string_view what);
private:
@@ -106,14 +103,6 @@
std::string rootFor(std::string_view path);
std::pair<std::string, std::string> rootAndSubpathFor(std::string_view path);
-
- struct Details {
- std::string root;
- std::string backing;
- std::string subpath;
- };
- Details detailsFor(std::string_view path);
-
Mounts copyMounts();
void reload();
diff --git a/incfs/include/incfs.h b/incfs/include/incfs.h
index 5dfe060..79016d8 100644
--- a/incfs/include/incfs.h
+++ b/incfs/include/incfs.h
@@ -20,7 +20,6 @@
#include <array>
#include <chrono>
#include <functional>
-#include <optional>
#include <string>
#include <string_view>
#include <utility>
@@ -40,8 +39,6 @@
enum Features {
none = INCFS_FEATURE_NONE,
core = INCFS_FEATURE_CORE,
- v2 = INCFS_FEATURE_V2,
- mappingFilesProgressFixed = INCFS_FEATURE_MAPPING_FILES_PROGRESS_FIXED,
};
enum class HashAlgorithm {
@@ -52,7 +49,6 @@
enum class CompressionKind {
none = INCFS_COMPRESSION_KIND_NONE,
lz4 = INCFS_COMPRESSION_KIND_LZ4,
- zstd = INCFS_COMPRESSION_KIND_ZSTD,
};
enum class BlockKind {
@@ -101,12 +97,9 @@
IncFsFd cmd() const;
IncFsFd pendingReads() const;
IncFsFd logs() const;
- IncFsFd blocksWritten() const;
-
+ operator IncFsControl*() const { return mControl; };
void close();
- operator IncFsControl*() const { return mControl; }
-
using Fds = std::array<UniqueFd, IncFsFdType::FDS_COUNT>;
[[nodiscard]] Fds releaseFds();
@@ -182,37 +175,28 @@
using BlockIndex = IncFsBlockIndex;
using ErrorCode = IncFsErrorCode;
using Fd = IncFsFd;
-using Uid = IncFsUid;
using ReadInfo = IncFsReadInfo;
-using ReadInfoWithUid = IncFsReadInfoWithUid;
using RawMetadata = ByteBuffer;
using RawSignature = ByteBuffer;
using MountOptions = IncFsMountOptions;
using DataBlock = IncFsDataBlock;
using NewFileParams = IncFsNewFileParams;
-using NewMappedFileParams = IncFsNewMappedFileParams;
-using BlockCounts = IncFsBlockCounts;
-using UidReadTimeouts = IncFsUidReadTimeouts;
-using Metrics = IncFsMetrics;
-using LastReadError = IncFsLastReadError;
constexpr auto kDefaultReadTimeout = std::chrono::milliseconds(INCFS_DEFAULT_READ_TIMEOUT_MS);
constexpr int kBlockSize = INCFS_DATA_FILE_BLOCK_SIZE;
const auto kInvalidFileId = kIncFsInvalidFileId;
-const auto kNoUid = kIncFsNoUid;
bool enabled();
Features features();
bool isValidFileId(FileId fileId);
std::string toString(FileId fileId);
IncFsFileId toFileId(std::string_view str);
-bool isIncFsFd(int fd);
bool isIncFsPath(std::string_view path);
UniqueControl mount(std::string_view backingPath, std::string_view targetDir,
IncFsMountOptions options);
UniqueControl open(std::string_view dir);
-UniqueControl createControl(IncFsFd cmd, IncFsFd pendingReads, IncFsFd logs, IncFsFd blocksWritten);
+UniqueControl createControl(IncFsFd cmd, IncFsFd pendingReads, IncFsFd logs);
ErrorCode setOptions(const Control& control, MountOptions newOptions);
@@ -223,8 +207,6 @@
ErrorCode makeFile(const Control& control, std::string_view path, int mode, FileId fileId,
NewFileParams params);
-ErrorCode makeMappedFile(const Control& control, std::string_view path, int mode,
- NewMappedFileParams params);
ErrorCode makeDir(const Control& control, std::string_view path, int mode = 0555);
ErrorCode makeDirs(const Control& control, std::string_view path, int mode = 0555);
@@ -244,10 +226,6 @@
std::vector<ReadInfo>* pendingReadsBuffer);
WaitResult waitForPageReads(const Control& control, std::chrono::milliseconds timeout,
std::vector<ReadInfo>* pageReadsBuffer);
-WaitResult waitForPendingReads(const Control& control, std::chrono::milliseconds timeout,
- std::vector<ReadInfoWithUid>* pendingReadsBuffer);
-WaitResult waitForPageReads(const Control& control, std::chrono::milliseconds timeout,
- std::vector<ReadInfoWithUid>* pageReadsBuffer);
UniqueFd openForSpecialOps(const Control& control, FileId fileId);
UniqueFd openForSpecialOps(const Control& control, std::string_view path);
@@ -257,33 +235,8 @@
std::pair<ErrorCode, FilledRanges> getFilledRanges(int fd, FilledRanges::RangeBuffer&& buffer);
std::pair<ErrorCode, FilledRanges> getFilledRanges(int fd, FilledRanges&& resumeFrom);
-ErrorCode setUidReadTimeouts(const Control& control, Span<const UidReadTimeouts> timeouts);
-std::optional<std::vector<UidReadTimeouts>> getUidReadTimeouts(const Control& control);
-
-std::optional<BlockCounts> getBlockCount(const Control& control, FileId fileId);
-std::optional<BlockCounts> getBlockCount(const Control& control, std::string_view path);
-
-std::optional<std::vector<FileId>> listIncompleteFiles(const Control& control);
-
-template <class Callback>
-ErrorCode forEachFile(const Control& control, Callback&& cb);
-template <class Callback>
-ErrorCode forEachIncompleteFile(const Control& control, Callback&& cb);
-
-WaitResult waitForLoadingComplete(const Control& control, std::chrono::milliseconds timeout);
-
enum class LoadingState { Full, MissingBlocks };
LoadingState isFullyLoaded(int fd);
-LoadingState isFullyLoaded(const Control& control, std::string_view path);
-LoadingState isFullyLoaded(const Control& control, FileId fileId);
-LoadingState isEverythingFullyLoaded(const Control& control);
-
-static const auto kTrimReservedSpace = kIncFsTrimReservedSpace;
-ErrorCode reserveSpace(const Control& control, std::string_view path, Size size);
-ErrorCode reserveSpace(const Control& control, FileId id, Size size);
-
-std::optional<Metrics> getMetrics(std::string_view sysfsName);
-std::optional<LastReadError> getLastReadError(const Control& control);
// Some internal secret API as well that's not backed by C API yet.
class MountRegistry;
diff --git a/incfs/include/incfs_inline.h b/incfs/include/incfs_inline.h
index f3d1e89..bcb7f15 100644
--- a/incfs/include/incfs_inline.h
+++ b/incfs/include/incfs_inline.h
@@ -28,14 +28,14 @@
constexpr char kSizeAttrName[] = INCFS_XATTR_SIZE_NAME;
constexpr char kMetadataAttrName[] = INCFS_XATTR_METADATA_NAME;
+constexpr char kIndexDir[] = ".index";
+
namespace details {
class CStrWrapper {
public:
CStrWrapper(std::string_view sv) {
- if (!sv.data()) {
- mCstr = "";
- } else if (sv[sv.size()] == '\0') {
+ if (sv[sv.size()] == '\0') {
mCstr = sv.data();
} else {
mCopy.emplace(sv);
@@ -70,10 +70,6 @@
return Features(IncFs_Features());
}
-inline bool isIncFsFd(int fd) {
- return IncFs_IsIncFsFd(fd);
-}
-
inline bool isIncFsPath(std::string_view path) {
return IncFs_IsIncFsPath(details::c_str(path));
}
@@ -116,10 +112,6 @@
return IncFs_GetControlFd(mControl, LOGS);
}
-inline IncFsFd UniqueControl::blocksWritten() const {
- return IncFs_GetControlFd(mControl, BLOCKS_WRITTEN);
-}
-
inline UniqueControl::Fds UniqueControl::releaseFds() {
Fds result;
IncFsFd fds[result.size()];
@@ -141,9 +133,8 @@
return UniqueControl(control);
}
-inline UniqueControl createControl(IncFsFd cmd, IncFsFd pendingReads, IncFsFd logs,
- IncFsFd blocksWritten) {
- return UniqueControl(IncFs_CreateControl(cmd, pendingReads, logs, blocksWritten));
+inline UniqueControl createControl(IncFsFd cmd, IncFsFd pendingReads, IncFsFd logs) {
+ return UniqueControl(IncFs_CreateControl(cmd, pendingReads, logs));
}
inline ErrorCode setOptions(const Control& control, MountOptions newOptions) {
@@ -174,10 +165,6 @@
NewFileParams params) {
return IncFs_MakeFile(control, details::c_str(path), mode, fileId, params);
}
-inline ErrorCode makeMappedFile(const Control& control, std::string_view path, int mode,
- NewMappedFileParams params) {
- return IncFs_MakeMappedFile(control, details::c_str(path), mode, params);
-}
inline ErrorCode makeDir(const Control& control, std::string_view path, int mode) {
return IncFs_MakeDir(control, details::c_str(path), mode);
}
@@ -238,15 +225,15 @@
return IncFs_Unlink(control, details::c_str(path));
}
-template <class ReadInfoStruct, class Impl>
-WaitResult waitForReads(const Control& control, std::chrono::milliseconds timeout,
- std::vector<ReadInfoStruct>* pendingReadsBuffer, size_t defaultBufferSize,
- Impl impl) {
+inline WaitResult waitForPendingReads(const Control& control, std::chrono::milliseconds timeout,
+ std::vector<ReadInfo>* pendingReadsBuffer) {
+ static constexpr auto kDefaultBufferSize = INCFS_DEFAULT_PENDING_READ_BUFFER_SIZE;
if (pendingReadsBuffer->empty()) {
- pendingReadsBuffer->resize(defaultBufferSize);
+ pendingReadsBuffer->resize(kDefaultBufferSize);
}
size_t size = pendingReadsBuffer->size();
- IncFsErrorCode err = impl(control, timeout.count(), pendingReadsBuffer->data(), &size);
+ IncFsErrorCode err =
+ IncFs_WaitForPendingReads(control, timeout.count(), pendingReadsBuffer->data(), &size);
pendingReadsBuffer->resize(size);
switch (err) {
case 0:
@@ -257,32 +244,24 @@
return WaitResult(err);
}
-inline WaitResult waitForPendingReads(const Control& control, std::chrono::milliseconds timeout,
- std::vector<ReadInfo>* pendingReadsBuffer) {
- return waitForReads(control, timeout, pendingReadsBuffer,
- INCFS_DEFAULT_PENDING_READ_BUFFER_SIZE, IncFs_WaitForPendingReads);
-}
-
-inline WaitResult waitForPendingReads(const Control& control, std::chrono::milliseconds timeout,
- std::vector<ReadInfoWithUid>* pendingReadsBuffer) {
- return waitForReads(control, timeout, pendingReadsBuffer,
- INCFS_DEFAULT_PENDING_READ_BUFFER_SIZE, IncFs_WaitForPendingReadsWithUid);
-}
-
inline WaitResult waitForPageReads(const Control& control, std::chrono::milliseconds timeout,
std::vector<ReadInfo>* pageReadsBuffer) {
static constexpr auto kDefaultBufferSize =
INCFS_DEFAULT_PAGE_READ_BUFFER_PAGES * PAGE_SIZE / sizeof(ReadInfo);
- return waitForReads(control, timeout, pageReadsBuffer, kDefaultBufferSize,
- IncFs_WaitForPageReads);
-}
-
-inline WaitResult waitForPageReads(const Control& control, std::chrono::milliseconds timeout,
- std::vector<ReadInfoWithUid>* pageReadsBuffer) {
- static constexpr auto kDefaultBufferSize =
- INCFS_DEFAULT_PAGE_READ_BUFFER_PAGES * PAGE_SIZE / sizeof(ReadInfoWithUid);
- return waitForReads(control, timeout, pageReadsBuffer, kDefaultBufferSize,
- IncFs_WaitForPageReadsWithUid);
+ if (pageReadsBuffer->empty()) {
+ pageReadsBuffer->resize(kDefaultBufferSize);
+ }
+ size_t size = pageReadsBuffer->size();
+ IncFsErrorCode err =
+ IncFs_WaitForPageReads(control, timeout.count(), pageReadsBuffer->data(), &size);
+ pageReadsBuffer->resize(size);
+ switch (err) {
+ case 0:
+ return WaitResult::HaveData;
+ case -ETIMEDOUT:
+ return WaitResult::Timeout;
+ }
+ return WaitResult(err);
}
inline UniqueFd openForSpecialOps(const Control& control, FileId fileId) {
@@ -337,7 +316,8 @@
return {res, FilledRanges(std::move(buffer), rawRanges)};
}
-inline LoadingState toLoadingState(IncFsErrorCode res) {
+inline LoadingState isFullyLoaded(int fd) {
+ auto res = IncFs_IsFullyLoaded(fd);
switch (res) {
case 0:
return LoadingState::Full;
@@ -348,138 +328,6 @@
}
}
-inline LoadingState isFullyLoaded(int fd) {
- return toLoadingState(IncFs_IsFullyLoaded(fd));
-}
-inline LoadingState isFullyLoaded(const Control& control, std::string_view path) {
- return toLoadingState(IncFs_IsFullyLoadedByPath(control, details::c_str(path)));
-}
-inline LoadingState isFullyLoaded(const Control& control, FileId fileId) {
- return toLoadingState(IncFs_IsFullyLoadedById(control, fileId));
-}
-
-inline LoadingState isEverythingFullyLoaded(const Control& control) {
- return toLoadingState(IncFs_IsEverythingFullyLoaded(control));
-}
-
-inline std::optional<std::vector<FileId>> listIncompleteFiles(const Control& control) {
- std::vector<FileId> ids(32);
- size_t count = ids.size();
- auto err = IncFs_ListIncompleteFiles(control, ids.data(), &count);
- if (err == -E2BIG) {
- ids.resize(count);
- err = IncFs_ListIncompleteFiles(control, ids.data(), &count);
- }
- if (err) {
- errno = -err;
- return {};
- }
- ids.resize(count);
- return std::move(ids);
-}
-
-template <class Callback>
-inline ErrorCode forEachFile(const Control& control, Callback&& cb) {
- struct Context {
- const Control& c;
- const Callback& cb;
- } context = {control, cb};
- return IncFs_ForEachFile(control, &context, [](void* pcontext, const IncFsControl*, FileId id) {
- const auto context = (Context*)pcontext;
- return context->cb(context->c, id);
- });
-}
-template <class Callback>
-inline ErrorCode forEachIncompleteFile(const Control& control, Callback&& cb) {
- struct Context {
- const Control& c;
- const Callback& cb;
- } context = {control, cb};
- return IncFs_ForEachIncompleteFile(control, &context,
- [](void* pcontext, const IncFsControl*, FileId id) {
- const auto context = (Context*)pcontext;
- return context->cb(context->c, id);
- });
-}
-
-inline WaitResult waitForLoadingComplete(const Control& control,
- std::chrono::milliseconds timeout) {
- const auto res = IncFs_WaitForLoadingComplete(control, timeout.count());
- switch (res) {
- case 0:
- return WaitResult::HaveData;
- case -ETIMEDOUT:
- return WaitResult::Timeout;
- default:
- return WaitResult(res);
- }
-}
-
-inline std::optional<BlockCounts> getBlockCount(const Control& control, FileId fileId) {
- BlockCounts counts;
- auto res = IncFs_GetFileBlockCountById(control, fileId, &counts);
- if (res) {
- errno = -res;
- return {};
- }
- return counts;
-}
-
-inline std::optional<BlockCounts> getBlockCount(const Control& control, std::string_view path) {
- BlockCounts counts;
- auto res = IncFs_GetFileBlockCountByPath(control, details::c_str(path), &counts);
- if (res) {
- errno = -res;
- return {};
- }
- return counts;
-}
-
-inline ErrorCode setUidReadTimeouts(const Control& control, Span<const UidReadTimeouts> timeouts) {
- return IncFs_SetUidReadTimeouts(control, timeouts.data(), timeouts.size());
-}
-
-inline std::optional<std::vector<UidReadTimeouts>> getUidReadTimeouts(const Control& control) {
- std::vector<UidReadTimeouts> timeouts(32);
- size_t count = timeouts.size();
- auto res = IncFs_GetUidReadTimeouts(control, timeouts.data(), &count);
- if (res == -E2BIG) {
- timeouts.resize(count);
- res = IncFs_GetUidReadTimeouts(control, timeouts.data(), &count);
- }
- if (res) {
- errno = -res;
- return {};
- }
- timeouts.resize(count);
- return std::move(timeouts);
-}
-
-inline ErrorCode reserveSpace(const Control& control, std::string_view path, Size size) {
- return IncFs_ReserveSpaceByPath(control, details::c_str(path), size);
-}
-inline ErrorCode reserveSpace(const Control& control, FileId id, Size size) {
- return IncFs_ReserveSpaceById(control, id, size);
-}
-
-inline std::optional<Metrics> getMetrics(std::string_view sysfsName) {
- Metrics metrics;
- if (const auto res = IncFs_GetMetrics(details::c_str(sysfsName), &metrics); res < 0) {
- errno = -res;
- return {};
- }
- return metrics;
-}
-
-inline std::optional<LastReadError> getLastReadError(const Control& control) {
- LastReadError lastReadError;
- if (const auto res = IncFs_GetLastReadError(control, &lastReadError); res < 0) {
- errno = -res;
- return {};
- }
- return lastReadError;
-}
-
} // namespace android::incfs
inline bool operator==(const IncFsFileId& l, const IncFsFileId& r) {
diff --git a/incfs/include/incfs_ndk.h b/incfs/include/incfs_ndk.h
index 4862f39..0d9e8a6 100644
--- a/incfs/include/incfs_ndk.h
+++ b/incfs/include/incfs_ndk.h
@@ -43,10 +43,7 @@
typedef enum {
INCFS_FEATURE_NONE = 0,
- INCFS_FEATURE_CORE = 1 << 0,
- INCFS_FEATURE_V2 = 1 << 1,
- INCFS_FEATURE_MAPPING_FILES_PROGRESS_FIXED = 1 << 2,
-
+ INCFS_FEATURE_CORE = 1,
} IncFsFeatures;
typedef int IncFsErrorCode;
@@ -54,9 +51,6 @@
typedef int32_t IncFsBlockIndex;
typedef int IncFsFd;
typedef struct IncFsControl IncFsControl;
-typedef int IncFsUid;
-
-static const IncFsUid kIncFsNoUid = -1;
typedef struct {
const char* data;
@@ -67,7 +61,6 @@
CMD,
PENDING_READS,
LOGS,
- BLOCKS_WRITTEN,
FDS_COUNT,
} IncFsFdType;
@@ -92,13 +85,11 @@
int32_t defaultReadTimeoutMs;
int32_t readLogBufferPages;
int32_t readLogDisableAfterTimeoutMs;
- const char* sysfsName;
} IncFsMountOptions;
typedef enum {
INCFS_COMPRESSION_KIND_NONE,
INCFS_COMPRESSION_KIND_LZ4,
- INCFS_COMPRESSION_KIND_ZSTD,
} IncFsCompressionKind;
typedef enum {
@@ -122,12 +113,6 @@
} IncFsNewFileParams;
typedef struct {
- IncFsFileId sourceId;
- IncFsSize sourceOffset;
- IncFsSize size;
-} IncFsNewMappedFileParams;
-
-typedef struct {
IncFsFileId id;
uint64_t bootClockTsUs;
IncFsBlockIndex block;
@@ -135,14 +120,6 @@
} IncFsReadInfo;
typedef struct {
- IncFsFileId id;
- uint64_t bootClockTsUs;
- IncFsBlockIndex block;
- uint32_t serialNo;
- IncFsUid uid;
-} IncFsReadInfoWithUid;
-
-typedef struct {
IncFsBlockIndex begin;
IncFsBlockIndex end;
} IncFsBlockRange;
@@ -155,40 +132,6 @@
IncFsBlockIndex endIndex;
} IncFsFilledRanges;
-typedef struct {
- IncFsSize totalDataBlocks;
- IncFsSize filledDataBlocks;
- IncFsSize totalHashBlocks;
- IncFsSize filledHashBlocks;
-} IncFsBlockCounts;
-
-typedef struct {
- IncFsUid uid;
- uint32_t minTimeUs;
- uint32_t minPendingTimeUs;
- uint32_t maxPendingTimeUs;
-} IncFsUidReadTimeouts;
-
-typedef struct {
- uint32_t readsDelayedMin;
- uint64_t readsDelayedMinUs;
- uint32_t readsDelayedPending;
- uint64_t readsDelayedPendingUs;
- uint32_t readsFailedHashVerification;
- uint32_t readsFailedOther;
- uint32_t readsFailedTimedOut;
- uint64_t reserved;
- uint64_t reserved1;
-} IncFsMetrics;
-
-typedef struct {
- IncFsFileId id;
- uint64_t timestampUs;
- IncFsBlockIndex block;
- uint32_t errorNo;
- IncFsUid uid;
-} IncFsLastReadError;
-
// All functions return -errno in case of failure.
// All IncFsFd functions return >=0 in case of success.
// All IncFsFileId functions return invalid IncFsFileId on error.
@@ -197,7 +140,6 @@
bool IncFs_IsEnabled();
IncFsFeatures IncFs_Features();
-bool IncFs_IsIncFsFd(int fd);
bool IncFs_IsIncFsPath(const char* path);
static inline bool IncFs_IsValidFileId(IncFsFileId fileId) {
@@ -212,8 +154,7 @@
IncFsControl* IncFs_Mount(const char* backingPath, const char* targetDir,
IncFsMountOptions options);
IncFsControl* IncFs_Open(const char* dir);
-IncFsControl* IncFs_CreateControl(IncFsFd cmd, IncFsFd pendingReads, IncFsFd logs,
- IncFsFd blocksWritten);
+IncFsControl* IncFs_CreateControl(IncFsFd cmd, IncFsFd pendingReads, IncFsFd logs);
void IncFs_DeleteControl(IncFsControl* control);
IncFsFd IncFs_GetControlFd(const IncFsControl* control, IncFsFdType type);
IncFsSize IncFs_ReleaseControlFds(IncFsControl* control, IncFsFd out[], IncFsSize outSize);
@@ -227,9 +168,6 @@
IncFsErrorCode IncFs_MakeFile(const IncFsControl* control, const char* path, int32_t mode,
IncFsFileId id, IncFsNewFileParams params);
-IncFsErrorCode IncFs_MakeMappedFile(const IncFsControl* control, const char* path, int32_t mode,
- IncFsNewMappedFileParams params);
-
IncFsErrorCode IncFs_MakeDir(const IncFsControl* control, const char* path, int32_t mode);
IncFsErrorCode IncFs_MakeDirs(const IncFsControl* control, const char* path, int32_t mode);
@@ -255,45 +193,11 @@
IncFsErrorCode IncFs_WaitForPageReads(const IncFsControl* control, int32_t timeoutMs,
IncFsReadInfo buffer[], size_t* bufferSize);
-IncFsErrorCode IncFs_WaitForPendingReadsWithUid(const IncFsControl* control, int32_t timeoutMs,
- IncFsReadInfoWithUid buffer[], size_t* bufferSize);
-IncFsErrorCode IncFs_WaitForPageReadsWithUid(const IncFsControl* control, int32_t timeoutMs,
- IncFsReadInfoWithUid buffer[], size_t* bufferSize);
-
-IncFsErrorCode IncFs_WaitForFsWrittenBlocksChange(const IncFsControl* control, int32_t timeoutMs,
- IncFsSize* count);
-
IncFsFd IncFs_OpenForSpecialOpsByPath(const IncFsControl* control, const char* path);
IncFsFd IncFs_OpenForSpecialOpsById(const IncFsControl* control, IncFsFileId id);
IncFsErrorCode IncFs_WriteBlocks(const IncFsDataBlock blocks[], size_t blocksCount);
-IncFsErrorCode IncFs_SetUidReadTimeouts(const IncFsControl* control,
- const IncFsUidReadTimeouts timeouts[], size_t count);
-IncFsErrorCode IncFs_GetUidReadTimeouts(const IncFsControl* control,
- IncFsUidReadTimeouts timeoutsBuffer[], size_t* bufferSize);
-
-IncFsErrorCode IncFs_GetFileBlockCountByPath(const IncFsControl* control, const char* path,
- IncFsBlockCounts* blockCount);
-IncFsErrorCode IncFs_GetFileBlockCountById(const IncFsControl* control, IncFsFileId id,
- IncFsBlockCounts* blockCount);
-
-IncFsErrorCode IncFs_ListIncompleteFiles(const IncFsControl* control, IncFsFileId ids[],
- size_t* bufferSize);
-
-// Calls a passed callback for each file on the mounted filesystem, or, in the second case,
-// for each incomplete file (only for v2 IncFS).
-// Callback can stop the iteration early by returning |false|.
-// Return codes:
-// >=0 - number of files iterated,
-// <0 - -errno
-typedef bool (*FileCallback)(void* context, const IncFsControl* control, IncFsFileId fileId);
-IncFsErrorCode IncFs_ForEachFile(const IncFsControl* control, void* context, FileCallback cb);
-IncFsErrorCode IncFs_ForEachIncompleteFile(const IncFsControl* control, void* context,
- FileCallback cb);
-
-IncFsErrorCode IncFs_WaitForLoadingComplete(const IncFsControl* control, int32_t timeoutMs);
-
// Gets a collection of filled ranges in the file from IncFS. Uses the |outBuffer| memory, it has
// to be big enough to fit all the ranges the caller is expecting.
// Return codes:
@@ -309,38 +213,6 @@
// -ENODATA - some blocks are missing,
// <0 - error from the syscall.
IncFsErrorCode IncFs_IsFullyLoaded(int fd);
-IncFsErrorCode IncFs_IsFullyLoadedByPath(const IncFsControl* control, const char* path);
-IncFsErrorCode IncFs_IsFullyLoadedById(const IncFsControl* control, IncFsFileId fileId);
-
-// Check if all files on the mount are fully loaded. Return codes:
-// 0 - fully loaded,
-// -ENODATA - some blocks are missing,
-// <0 - error from the syscall.
-IncFsErrorCode IncFs_IsEverythingFullyLoaded(const IncFsControl* control);
-
-// Reserve |size| bytes for the file. Trims reserved space to the current file size when |size = -1|
-static const IncFsSize kIncFsTrimReservedSpace = -1;
-IncFsErrorCode IncFs_ReserveSpaceByPath(const IncFsControl* control, const char* path,
- IncFsSize size);
-IncFsErrorCode IncFs_ReserveSpaceById(const IncFsControl* control, IncFsFileId id, IncFsSize size);
-
-// Gets the metrics of a mount by specifying the corresponding sysfs subpath.
-// Return codes:
-// =0 - success
-// <0 - -errno
-IncFsErrorCode IncFs_GetMetrics(const char* sysfsName, IncFsMetrics* metrics);
-
-// Gets information about the last read error of a mount.
-// Return codes:
-// =0 - success
-// <0 - -errno
-// When there is no read error, still returns success. Fields in IncFsLastReadError will be all 0.
-// Possible values of IncFsLastReadError.errorNo:
-// -ETIME for read timeout;
-// -EBADMSG for hash verification failure;
-// Other negative values for other types of errors.
-IncFsErrorCode IncFs_GetLastReadError(const IncFsControl* control,
- IncFsLastReadError* lastReadError);
__END_DECLS
diff --git a/incfs/kernel-headers/linux/incrementalfs.h b/incfs/kernel-headers/linux/incrementalfs.h
index d164cd9..b9f4c7a 100644
--- a/incfs/kernel-headers/linux/incrementalfs.h
+++ b/incfs/kernel-headers/linux/incrementalfs.h
@@ -18,7 +18,7 @@
/* ===== constants ===== */
#define INCFS_NAME "incremental-fs"
-#define INCFS_MAGIC_NUMBER (unsigned long)(0x5346434e49ul)
+#define INCFS_MAGIC_NUMBER (0x5346434e49ul)
#define INCFS_DATA_FILE_BLOCK_SIZE 4096
#define INCFS_HEADER_VER 1
@@ -28,15 +28,11 @@
#define INCFS_MAX_HASH_SIZE 32
#define INCFS_MAX_FILE_ATTR_SIZE 512
-#define INCFS_INDEX_NAME ".index"
-#define INCFS_INCOMPLETE_NAME ".incomplete"
#define INCFS_PENDING_READS_FILENAME ".pending_reads"
#define INCFS_LOG_FILENAME ".log"
-#define INCFS_BLOCKS_WRITTEN_FILENAME ".blocks_written"
#define INCFS_XATTR_ID_NAME (XATTR_USER_PREFIX "incfs.id")
#define INCFS_XATTR_SIZE_NAME (XATTR_USER_PREFIX "incfs.size")
#define INCFS_XATTR_METADATA_NAME (XATTR_USER_PREFIX "incfs.metadata")
-#define INCFS_XATTR_VERITY_NAME (XATTR_USER_PREFIX "incfs.verity")
#define INCFS_MAX_SIGNATURE_SIZE 8096
#define INCFS_SIGNATURE_VERSION 2
@@ -46,10 +42,7 @@
/* ===== ioctl requests on the command dir ===== */
-/*
- * Create a new file
- * May only be called on .pending_reads file
- */
+/* Create a new file */
#define INCFS_IOC_CREATE_FILE \
_IOWR(INCFS_IOCTL_BASE_CODE, 30, struct incfs_new_file_args)
@@ -99,73 +92,9 @@
#define INCFS_IOC_GET_FILLED_BLOCKS \
_IOR(INCFS_IOCTL_BASE_CODE, 34, struct incfs_get_filled_blocks_args)
-/*
- * Creates a new mapped file
- * May only be called on .pending_reads file
- */
-#define INCFS_IOC_CREATE_MAPPED_FILE \
- _IOWR(INCFS_IOCTL_BASE_CODE, 35, struct incfs_create_mapped_file_args)
-
-/*
- * Get number of blocks, total and filled
- * May only be called on .pending_reads file
- */
-#define INCFS_IOC_GET_BLOCK_COUNT \
- _IOR(INCFS_IOCTL_BASE_CODE, 36, struct incfs_get_block_count_args)
-
-/*
- * Get per UID read timeouts
- * May only be called on .pending_reads file
- */
-#define INCFS_IOC_GET_READ_TIMEOUTS \
- _IOR(INCFS_IOCTL_BASE_CODE, 37, struct incfs_get_read_timeouts_args)
-
-/*
- * Set per UID read timeouts
- * May only be called on .pending_reads file
- */
-#define INCFS_IOC_SET_READ_TIMEOUTS \
- _IOW(INCFS_IOCTL_BASE_CODE, 38, struct incfs_set_read_timeouts_args)
-
-/*
- * Get last read error
- * May only be called on .pending_reads file
- */
-#define INCFS_IOC_GET_LAST_READ_ERROR \
- _IOW(INCFS_IOCTL_BASE_CODE, 39, struct incfs_get_last_read_error_args)
-
-/* ===== sysfs feature flags ===== */
-/*
- * Each flag is represented by a file in /sys/fs/incremental-fs/features
- * If the file exists the feature is supported
- * Also the file contents will be the line "supported"
- */
-
-/*
- * Basic flag stating that the core incfs file system is available
- */
-#define INCFS_FEATURE_FLAG_COREFS "corefs"
-
-/*
- * zstd compression support
- */
-#define INCFS_FEATURE_FLAG_ZSTD "zstd"
-
-/*
- * v2 feature set support. Covers:
- * INCFS_IOC_CREATE_MAPPED_FILE
- * INCFS_IOC_GET_BLOCK_COUNT
- * INCFS_IOC_GET_READ_TIMEOUTS/INCFS_IOC_SET_READ_TIMEOUTS
- * .blocks_written status file
- * .incomplete folder
- * report_uid mount option
- */
-#define INCFS_FEATURE_FLAG_V2 "v2"
-
enum incfs_compression_alg {
COMPRESSION_NONE = 0,
- COMPRESSION_LZ4 = 1,
- COMPRESSION_ZSTD = 2,
+ COMPRESSION_LZ4 = 1
};
enum incfs_block_flags {
@@ -180,8 +109,6 @@
/*
* Description of a pending read. A pending read - a read call by
* a userspace program for which the filesystem currently doesn't have data.
- *
- * Reads from .pending_reads and .log return an array of these structure
*/
struct incfs_pending_read_info {
/* Id of a file that is being read from. */
@@ -198,32 +125,6 @@
};
/*
- * Description of a pending read. A pending read - a read call by
- * a userspace program for which the filesystem currently doesn't have data.
- *
- * This version of incfs_pending_read_info is used whenever the file system is
- * mounted with the report_uid flag
- */
-struct incfs_pending_read_info2 {
- /* Id of a file that is being read from. */
- incfs_uuid_t file_id;
-
- /* A number of microseconds since system boot to the read. */
- __aligned_u64 timestamp_us;
-
- /* Index of a file block that is being read. */
- __u32 block_index;
-
- /* A serial number of this pending read. */
- __u32 serial_number;
-
- /* The UID of the reading process */
- __u32 uid;
-
- __u32 reserved;
-};
-
-/*
* Description of a data or hash block to add to a data file.
*/
struct incfs_fill_block {
@@ -420,7 +321,7 @@
/* Actual number of blocks in file */
__u32 total_blocks_out;
- /* The number of data blocks in file */
+ /* The number of data blocks in file */
__u32 data_blocks_out;
/* Number of bytes written to range buffer */
@@ -430,154 +331,4 @@
__u32 index_out;
};
-/*
- * Create a new mapped file
- * Argument for INCFS_IOC_CREATE_MAPPED_FILE
- */
-struct incfs_create_mapped_file_args {
- /*
- * Total size of the new file.
- */
- __aligned_u64 size;
-
- /*
- * File mode. Permissions and dir flag.
- */
- __u16 mode;
-
- __u16 reserved1;
-
- __u32 reserved2;
-
- /*
- * A pointer to a null-terminated relative path to the incfs mount
- * point
- * Max length: PATH_MAX
- *
- * Equivalent to: char *directory_path;
- */
- __aligned_u64 directory_path;
-
- /*
- * A pointer to a null-terminated file name.
- * Max length: PATH_MAX
- *
- * Equivalent to: char *file_name;
- */
- __aligned_u64 file_name;
-
- /* Id of source file to map. */
- incfs_uuid_t source_file_id;
-
- /*
- * Offset in source file to start mapping. Must be a multiple of
- * INCFS_DATA_FILE_BLOCK_SIZE
- */
- __aligned_u64 source_offset;
-};
-
-/*
- * Get information about the blocks in this file
- * Argument for INCFS_IOC_GET_BLOCK_COUNT
- */
-struct incfs_get_block_count_args {
- /* Total number of data blocks in the file */
- __u32 total_data_blocks_out;
-
- /* Number of filled data blocks in the file */
- __u32 filled_data_blocks_out;
-
- /* Total number of hash blocks in the file */
- __u32 total_hash_blocks_out;
-
- /* Number of filled hash blocks in the file */
- __u32 filled_hash_blocks_out;
-};
-
-/* Description of timeouts for one UID */
-struct incfs_per_uid_read_timeouts {
- /* UID to apply these timeouts to */
- __u32 uid;
-
- /*
- * Min time in microseconds to read any block. Note that this doesn't
- * apply to reads which are satisfied from the page cache.
- */
- __u32 min_time_us;
-
- /*
- * Min time in microseconds to satisfy a pending read. Any pending read
- * which is filled before this time will be delayed so that the total
- * read time >= this value.
- */
- __u32 min_pending_time_us;
-
- /*
- * Max time in microseconds to satisfy a pending read before the read
- * times out. If set to U32_MAX, defaults to mount options
- * read_timeout_ms * 1000. Must be >= min_pending_time_us
- */
- __u32 max_pending_time_us;
-};
-
-/*
- * Get the read timeouts array
- * Argument for INCFS_IOC_GET_READ_TIMEOUTS
- */
-struct incfs_get_read_timeouts_args {
- /*
- * A pointer to a buffer to fill with the current timeouts
- *
- * Equivalent to struct incfs_per_uid_read_timeouts *
- */
- __aligned_u64 timeouts_array;
-
- /* Size of above buffer in bytes */
- __u32 timeouts_array_size;
-
- /* Size used in bytes, or size needed if -ENOMEM returned */
- __u32 timeouts_array_size_out;
-};
-
-/*
- * Set the read timeouts array
- * Arguments for INCFS_IOC_SET_READ_TIMEOUTS
- */
-struct incfs_set_read_timeouts_args {
- /*
- * A pointer to an array containing the new timeouts
- * This will replace any existing timeouts
- *
- * Equivalent to struct incfs_per_uid_read_timeouts *
- */
- __aligned_u64 timeouts_array;
-
- /* Size of above array in bytes. Must be < 256 */
- __u32 timeouts_array_size;
-};
-
-/*
- * Get last read error struct
- * Arguments for INCFS_IOC_GET_LAST_READ_ERROR
- */
-struct incfs_get_last_read_error_args {
- /* File id of last file that had a read error */
- incfs_uuid_t file_id_out;
-
- /* Time of last read error, in us, from CLOCK_MONOTONIC */
- __u64 time_us_out;
-
- /* Index of page that was being read at last read error */
- __u32 page_out;
-
- /* errno of last read error */
- __u32 errno_out;
-
- /* uid of last read error */
- __u32 uid_out;
-
- __u32 reserved1;
- __u64 reserved2;
-};
-
-#endif /* _UAPI_LINUX_INCREMENTALFS_H */
\ No newline at end of file
+#endif /* _UAPI_LINUX_INCREMENTALFS_H */
diff --git a/incfs/path.cpp b/incfs/path.cpp
index f6f87ae..8781571 100644
--- a/incfs/path.cpp
+++ b/incfs/path.cpp
@@ -121,19 +121,23 @@
snprintf(fdNameBuffer, std::size(fdNameBuffer), fdNameFormat, fd);
std::string res;
- // We used to call lstat() here to preallocate the buffer to the exact required size; turns out
- // that call is significantly more expensive than anything else, so doing a couple extra
- // iterations is worth the savings.
- auto bufSize = 256;
+ // lstat() is supposed to return us exactly the needed buffer size, but
+ // somehow it may also return a smaller (but still >0) st_size field.
+ // That's why let's only use it for the initial estimate.
+ struct stat st = {};
+ if (::lstat(fdNameBuffer, &st) || st.st_size == 0) {
+ st.st_size = PATH_MAX;
+ }
+ auto bufSize = st.st_size;
for (;;) {
- res.resize(bufSize - 1, '\0');
+ res.resize(bufSize + 1, '\0');
auto size = ::readlink(fdNameBuffer, &res[0], res.size());
if (size < 0) {
PLOG(ERROR) << "readlink failed for " << fdNameBuffer;
return {};
}
- if (size >= ssize_t(res.size())) {
- // can't tell if the name is exactly that long, or got truncated - just repeat the call.
+ if (size > bufSize) {
+ // File got renamed in between lstat() and readlink() calls? Retry.
bufSize *= 2;
continue;
}
@@ -145,14 +149,13 @@
}
}
-static void preparePathComponent(std::string_view& path, bool trimAll) {
- // need to check for double front slash as a single one has a separate meaning in front
- while (!path.empty() && path.front() == '/' &&
- (trimAll || (path.size() > 1 && path[1] == '/'))) {
- path.remove_prefix(1);
+static void preparePathComponent(std::string_view& path, bool trimFront) {
+ if (trimFront) {
+ while (!path.empty() && path.front() == '/') {
+ path.remove_prefix(1);
+ }
}
- // for the back we don't care about double-vs-single slash difference
- while (path.size() > !trimAll && path.back() == '/') {
+ while (!path.empty() && path.back() == '/') {
path.remove_suffix(1);
}
}
@@ -175,7 +178,7 @@
}
void details::appendNextPath(std::string& res, std::string_view path) {
- preparePathComponent(path, !res.empty());
+ preparePathComponent(path, true);
if (path.empty()) {
return;
}
@@ -185,6 +188,41 @@
res += path;
}
+std::string_view baseName(std::string_view path) {
+ if (path.empty()) {
+ return {};
+ }
+ if (path == "/"sv) {
+ return "/"sv;
+ }
+ auto pos = path.rfind('/');
+ while (!path.empty() && pos == path.size() - 1) {
+ path.remove_suffix(1);
+ pos = path.rfind('/');
+ }
+ if (pos == path.npos) {
+ return path.empty() ? "/"sv : path;
+ }
+ return path.substr(pos + 1);
+}
+
+std::string_view dirName(std::string_view path) {
+ if (path.empty()) {
+ return {};
+ }
+ if (path == "/"sv) {
+ return "/"sv;
+ }
+ const auto pos = path.rfind('/');
+ if (pos == 0) {
+ return "/"sv;
+ }
+ if (pos == path.npos) {
+ return "."sv;
+ }
+ return path.substr(0, pos);
+}
+
std::pair<std::string_view, std::string_view> splitDirBase(std::string& full) {
auto res = std::pair(dirName(full), baseName(full));
if (res.first.data() == full.data()) {
diff --git a/incfs/include/path.h b/incfs/path.h
similarity index 62%
rename from incfs/include/path.h
rename to incfs/path.h
index 325eb1d..f312d17 100644
--- a/incfs/include/path.h
+++ b/incfs/path.h
@@ -48,43 +48,8 @@
std::string_view relativize(std::string_view parent, std::string&& nested) = delete;
// Note: some system headers #define 'dirname' and 'basename' as macros
-
-inline std::string_view baseName(std::string_view path) {
- using namespace std::literals;
- if (path.empty()) {
- return {};
- }
- if (path == "/"sv) {
- return "/"sv;
- }
- auto pos = path.rfind('/');
- while (!path.empty() && pos == path.size() - 1) {
- path.remove_suffix(1);
- pos = path.rfind('/');
- }
- if (pos == path.npos) {
- return path.empty() ? "/"sv : path;
- }
- return path.substr(pos + 1);
-}
-
-inline std::string_view dirName(std::string_view path) {
- using namespace std::literals;
- if (path.empty()) {
- return {};
- }
- if (path == "/"sv) {
- return "/"sv;
- }
- const auto pos = path.rfind('/');
- if (pos == 0) {
- return "/"sv;
- }
- if (pos == path.npos) {
- return "."sv;
- }
- return path.substr(0, pos);
-}
+std::string_view dirName(std::string_view path);
+std::string_view baseName(std::string_view path);
// Split the |full| path into its directory and basename components.
// This modifies the input string to null-terminate the output directory
@@ -95,32 +60,20 @@
bool endsWith(std::string_view path, std::string_view prefix);
inline auto openDir(const char* path) {
- struct closer {
- void operator()(DIR* d) const { ::closedir(d); }
- };
- auto dir = std::unique_ptr<DIR, closer>(::opendir(path));
+ auto dir = std::unique_ptr<DIR, decltype(&closedir)>(::opendir(path), &::closedir);
return dir;
}
template <class... Paths>
-std::string join(std::string&& first, std::string_view second, Paths&&... paths) {
- std::string& result = first;
+std::string join(std::string_view first, std::string_view second, Paths&&... paths) {
+ std::string result;
{
using std::size;
result.reserve(first.size() + second.size() + 1 + (sizeof...(paths) + ... + size(paths)));
}
- (details::appendNextPath(result, second), ...,
- details::appendNextPath(result, std::forward<Paths>(paths)));
+ result.assign(first);
+ (details::appendNextPath(result, second), ..., details::appendNextPath(result, paths));
return result;
}
-template <class... Paths>
-std::string join(std::string_view first, std::string_view second, Paths&&... paths) {
- return join(std::string(), first, second, std::forward<Paths>(paths)...);
-}
-template <class... Paths>
-std::string join(const char* first, std::string_view second, Paths&&... paths) {
- return path::join(std::string_view(first), second, std::forward<Paths>(paths)...);
-}
-
} // namespace android::incfs::path
diff --git a/incfs/tests/MountRegistry_test.cpp b/incfs/tests/MountRegistry_test.cpp
index bb3ba49..2b82eb0 100644
--- a/incfs/tests/MountRegistry_test.cpp
+++ b/incfs/tests/MountRegistry_test.cpp
@@ -14,8 +14,6 @@
* limitations under the License.
*/
-#include "MountRegistry.h"
-
#include <android-base/file.h>
#include <android-base/logging.h>
#include <android-base/unique_fd.h>
@@ -23,11 +21,10 @@
#include <sys/select.h>
#include <unistd.h>
-#include <iterator>
#include <optional>
#include <thread>
-#include "incfs.h"
+#include "MountRegistry.h"
#include "path.h"
using namespace android::incfs;
@@ -75,97 +72,9 @@
ASSERT_STREQ("/root", r().rootFor("/bind").data());
ASSERT_STREQ("/root", r().rootFor("/bind2").data());
ASSERT_STREQ("/root", r().rootFor("/other/bind/dir").data());
- ASSERT_EQ("/root"s, r().rootAndSubpathFor("/root").first->path);
- ASSERT_EQ(""s, r().rootAndSubpathFor("/root").second);
- ASSERT_EQ("/root"s, r().rootAndSubpathFor("/bind").first->path);
- ASSERT_EQ("1"s, r().rootAndSubpathFor("/bind").second);
- ASSERT_EQ("/root"s, r().rootAndSubpathFor("/bind2").first->path);
- ASSERT_EQ("2/3"s, r().rootAndSubpathFor("/bind2").second);
- ASSERT_EQ("/root"s, r().rootAndSubpathFor("/bind2/blah").first->path);
- ASSERT_EQ("2/3/blah"s, r().rootAndSubpathFor("/bind2/blah").second);
- ASSERT_EQ("/root"s, r().rootAndSubpathFor("/other/bind/blah").first->path);
- ASSERT_EQ("2/3/blah"s, r().rootAndSubpathFor("/other/bind/blah").second);
-}
-
-TEST_F(MountRegistryTest, MultiRoot) {
- r().addRoot("/root", "/backing");
- r().addBind("/root", "/bind");
- ASSERT_STREQ("/root", r().rootFor("/root").data());
- ASSERT_STREQ("/root", r().rootFor("/bind").data());
- ASSERT_STREQ("/root", r().rootFor("/bind/2").data());
-}
-
-static MountRegistry::Mounts makeFrom(std::string_view str) {
- TemporaryFile f;
- EXPECT_TRUE(android::base::WriteFully(f.fd, str.data(), str.size()));
- EXPECT_EQ(0, lseek(f.fd, 0, SEEK_SET)); // rewind
-
- MountRegistry::Mounts m;
- EXPECT_TRUE(m.loadFrom(f.fd, INCFS_NAME));
- return std::move(m);
-}
-
-TEST_F(MountRegistryTest, MultiRootLoad) {
- constexpr char mountsFile[] =
- R"(4605 34 0:154 / /mnt/installer/0/0000000000000000000000000000CAFEF00D2019 rw,nosuid,nodev,noexec,noatime shared:45 master:43 - fuse /dev/fuse rw,lazytime,user_id=0,group_id=0,allow_other
-4561 35 0:154 / /mnt/androidwritable/0/0000000000000000000000000000CAFEF00D2019 rw,nosuid,nodev,noexec,noatime shared:44 master:43 - fuse /dev/fuse rw,lazytime,user_id=0,group_id=0,allow_other
-4560 99 0:154 / /storage/0000000000000000000000000000CAFEF00D2019 rw,nosuid,nodev,noexec,noatime master:43 - fuse /dev/fuse rw,lazytime,user_id=0,group_id=0,allow_other
-4650 30 0:44 /MyFiles /mnt/pass_through/0/0000000000000000000000000000CAFEF00D2019 rw,nosuid,nodev,noexec,relatime shared:31 - 9p media rw,sync,dirsync,access=client,trans=virtio
-3181 79 0:146 / /data/incremental/MT_data_app_vmdl703/mount rw,nosuid,nodev,noatime shared:46 - incremental-fs /data/incremental/MT_data_app_vmdl703/backing_store rw,seclabel,read_timeout_ms=10000,readahead=0
-3182 77 0:146 / /var/run/mount/data/mount/data/incremental/MT_data_app_vmdl703/mount rw,nosuid,nodev,noatime shared:46 - incremental-fs /data/incremental/MT_data_app_vmdl703/backing_store rw,seclabel,read_timeout_ms=10000,readahead=0
-)";
-
- auto m = makeFrom(mountsFile);
-
- EXPECT_EQ(size_t(1), m.size());
- EXPECT_STREQ("/data/incremental/MT_data_app_vmdl703/mount",
- m.rootFor("/data/incremental/MT_data_app_vmdl703/mount/123/2").data());
- EXPECT_STREQ("/data/incremental/MT_data_app_vmdl703/mount",
- m.rootFor("/var/run/mount/data/mount/data/incremental/MT_data_app_vmdl703/mount/"
- "some/thing")
- .data());
-}
-
-TEST_F(MountRegistryTest, MultiRootLoadReversed) {
- constexpr char mountsFile[] =
- R"(4605 34 0:154 / /mnt/installer/0/0000000000000000000000000000CAFEF00D2019 rw,nosuid,nodev,noexec,noatime shared:45 master:43 - fuse /dev/fuse rw,lazytime,user_id=0,group_id=0,allow_other
-4561 35 0:154 / /mnt/androidwritable/0/0000000000000000000000000000CAFEF00D2019 rw,nosuid,nodev,noexec,noatime shared:44 master:43 - fuse /dev/fuse rw,lazytime,user_id=0,group_id=0,allow_other
-4560 99 0:154 / /storage/0000000000000000000000000000CAFEF00D2019 rw,nosuid,nodev,noexec,noatime master:43 - fuse /dev/fuse rw,lazytime,user_id=0,group_id=0,allow_other
-4650 30 0:44 /MyFiles /mnt/pass_through/0/0000000000000000000000000000CAFEF00D2019 rw,nosuid,nodev,noexec,relatime shared:31 - 9p media rw,sync,dirsync,access=client,trans=virtio
-3182 77 0:146 / /var/run/mount/data/mount/data/incremental/MT_data_app_vmdl703/mount rw,nosuid,nodev,noatime shared:46 - incremental-fs /data/incremental/MT_data_app_vmdl703/backing_store rw,seclabel,read_timeout_ms=10000,readahead=0
-3181 79 0:146 / /data/incremental/MT_data_app_vmdl703/mount rw,nosuid,nodev,noatime shared:46 - incremental-fs /data/incremental/MT_data_app_vmdl703/backing_store rw,seclabel,read_timeout_ms=10000,readahead=0
-)";
-
- auto m = makeFrom(mountsFile);
-
- EXPECT_EQ(size_t(1), m.size());
- EXPECT_STREQ("/data/incremental/MT_data_app_vmdl703/mount",
- m.rootFor("/data/incremental/MT_data_app_vmdl703/mount/123/2").data());
- EXPECT_STREQ("/data/incremental/MT_data_app_vmdl703/mount",
- m.rootFor("/var/run/mount/data/mount/data/incremental/MT_data_app_vmdl703/mount/"
- "some/thing")
- .data());
-}
-
-TEST_F(MountRegistryTest, LoadInvalid) {
- constexpr char mountsFile[] =
- R"(9465 93 0:281 // /data/incremental1 shared:56 - incremental-fs /data/incremental2 rw,seclabel,read_timeout_ms=10000,readahead=0,report_uid
-9529 93 0:281 /st_232_0 /data/app/vmdl1998506227.tmp rw,nosuid,nodev,noatime shared:56 - incremental-fs /data/incremental/MT_data_app_vmdl199/backing_store rw,seclabel,read_timeout_ms=10000,readahead=0,report_uid
-9657 93 0:282 /st_233_0 /data/app/vmdl2034419270.tmp rw,nosuid,nodev,noatime shared:57 - incremental-fs /data/incremental/MT_data_app_vmdl203/backing_store rw,seclabel,read_timeout_ms=10000,readahead=0,report_uid
-9721 93 0:283 / /data/incremental/MT_data_app_vmdl154/mount rw,nosuid,nodev,noatime shared:58 - incremental-fs /data/incremental/MT_data_app_vmdl154/backing_store rw,seclabel,read_timeout_ms=10000,readahead=0,report_uid
-9785 93 0:283 /st_234_0 /data/app/vmdl1545425783.tmp rw,nosuid,nodev,noatime shared:58 - incremental-fs /data/incremental/MT_data_app_vmdl154/backing_store rw,seclabel,read_timeout_ms=10000,readahead=0,report_uid
-9849 93 0:284 / /data/incremental/MT_data_app_vmdl209/mount rw,nosuid,nodev,noatime shared:59 - incremental-fs /data/incremental/MT_data_app_vmdl209/backing_store rw,seclabel,read_timeout_ms=10000,readahead=0,report_uid
-9913 93 0:284 /st_235_0 /data/app/vmdl2099748756.tmp rw,nosuid,nodev,noatime shared:59 - incremental-fs /data/incremental/MT_data_app_vmdl209/backing_store rw,seclabel,read_timeout_ms=10000,readahead=0,report_uid
-4007 93 0:269 /st_240_1 /data/app/~~I499PLubwcOVbJaEFqpHHQ== rw,nosuid,nodev,noatime shared:44 - incremental-fs /data/incremental/MT_data_app_vmdl158/backing_store rw,seclabel,read_timeout_ms=10000,readahead=0,report_uid
-5285 93 0:270 /st_241_1 /data/app/~~CvgMdGm9eNJpvdq-62Jktg== rw,nosuid,nodev,noatime shared:45 - incremental-fs /data/incremental/MT_data_app_vmdl943/backing_store rw,seclabel,read_timeout_ms=10000,readahead=0,report_uid
-8786 93 0:271 /st_242_1 /data/app/~~_oNudLuBvqtSE78VoMVY5Q== rw,nosuid,nodev,noatime shared:46 - incremental-fs /data/incremental/MT_data_app_vmdl144/backing_store rw,seclabel,read_timeout_ms=10000,readahead=0,report_uid
-10358 93 0:272 /st_243_1 /data/app/~~o5-RadxV4DUe-mgPyv9SkQ== rw,nosuid,nodev,noatime shared:47 - incremental-fs /data/incremental/MT_data_app_vmdl642/backing_store rw,seclabel,read_timeout_ms=10000,readahead=0,report_uid
-10422 93 0:131 /st_244_1 /data/app/~~rTnKswx1F427UguGO9nDRA== rw,nosuid,nodev,noatime shared:48 - incremental-fs /data/incremental/MT_data_app_vmdl336/backing_store rw,seclabel,read_timeout_ms=10000,readahead=0,report_uid
-10486 93 0:132 /st_245_1 /data/app/~~ZIoVqPDBjLBeajD4thHsYA== rw,nosuid,nodev,noatime shared:49 - incremental-fs /data/incremental/MT_data_app_vmdl993/backing_store rw,seclabel,read_timeout_ms=10000,readahead=0,report_uid
-10550 93 0:133 /st_246_1 /data/app/~~2qjtCtx5rqPW2Hwlrciu2w== rw,nosuid,nodev,noatime shared:50 - incremental-fs /data/incremental/MT_data_app_vmdl827/backing_store rw,seclabel,read_timeout_ms=10000,readahead=0,report_uid
-)";
-
- auto m = makeFrom(mountsFile);
- // only two of the mounts in this file are valid
- EXPECT_EQ(size_t(2), m.size());
+ ASSERT_EQ(std::pair("/root"sv, ""s), r().rootAndSubpathFor("/root"));
+ ASSERT_EQ(std::pair("/root"sv, "1"s), r().rootAndSubpathFor("/bind"));
+ ASSERT_EQ(std::pair("/root"sv, "2/3"s), r().rootAndSubpathFor("/bind2"));
+ ASSERT_EQ(std::pair("/root"sv, "2/3/blah"s), r().rootAndSubpathFor("/bind2/blah"));
+ ASSERT_EQ(std::pair("/root"sv, "2/3/blah"s), r().rootAndSubpathFor("/other/bind/blah"));
}
diff --git a/incfs/tests/hardening_benchmark.cpp b/incfs/tests/hardening_benchmark.cpp
deleted file mode 100644
index 8e4f047..0000000
--- a/incfs/tests/hardening_benchmark.cpp
+++ /dev/null
@@ -1,165 +0,0 @@
-/*
- * Copyright (C) 2020 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.
- */
-
-#include <android-base/file.h>
-#include <android-base/mapped_file.h>
-#include <benchmark/benchmark.h>
-#include <unistd.h>
-
-#include "incfs_support/access.h"
-#include "incfs_support/signal_handling.h"
-#include "util/map_ptr.h"
-
-static std::unique_ptr<TemporaryFile> makeFile() {
- auto tmp = std::unique_ptr<TemporaryFile>(new TemporaryFile());
- char c = 1;
- write(tmp->fd, &c, sizeof(c));
- return tmp;
-}
-
-static std::pair<std::unique_ptr<TemporaryFile>, std::unique_ptr<android::base::MappedFile>>
-makeEmptyFileMapping() {
- auto tmp = makeFile();
- // mmap() only works for non-empty files, but it's "ok" to resize it back to empty afterwards
- auto mapping = android::base::MappedFile::FromFd(tmp->fd, 0, 1, PROT_READ);
- ftruncate(tmp->fd, 0);
- return {std::move(tmp), std::move(mapping)};
-}
-
-static void TestEmpty(benchmark::State& state) {
- int val = 0;
- for (auto _ : state) {
- benchmark::DoNotOptimize(val += 1);
- }
-}
-BENCHMARK(TestEmpty);
-
-static void TestSignal(benchmark::State& state) {
- auto tmp = makeFile();
- auto mapping = android::base::MappedFile::FromFd(tmp->fd, 0, 1, PROT_READ);
-
- int val;
- for (auto _ : state) {
- SCOPED_SIGBUS_HANDLER({ break; });
- val += *mapping->data();
- }
-}
-BENCHMARK(TestSignal);
-
-static void TestRead(benchmark::State& state) {
- auto tmp = makeFile();
- int val = 0;
- for (auto _ : state) {
- char c;
- pread(tmp->fd, &c, sizeof(c), 0);
- val += c;
- }
-}
-BENCHMARK(TestRead);
-
-static void TestMapPtrRaw(benchmark::State& state) {
- auto tmp = makeFile();
- android::incfs::IncFsFileMap map;
- map.CreateForceVerification(tmp->fd, 0, 1, tmp->path, true);
- int val = 0;
- const uint8_t* prev_block = nullptr;
- for (auto _ : state) {
- auto start = static_cast<const uint8_t*>(map.unsafe_data());
- auto end = start + map.length();
- val += map.Verify(start, end, &prev_block);
- }
-}
-BENCHMARK(TestMapPtrRaw);
-
-static void TestMapPtr(benchmark::State& state) {
- auto tmp = makeFile();
- android::incfs::IncFsFileMap map;
- map.CreateForceVerification(tmp->fd, 0, 1, tmp->path, true);
- int val = 0;
- for (auto _ : state) {
- val += map.data<char>().verify();
- }
-}
-BENCHMARK(TestMapPtr);
-
-static void TestAccess(benchmark::State& state) {
- auto tmp = makeFile();
- auto mapping = android::base::MappedFile::FromFd(tmp->fd, 0, 1, PROT_READ);
- int val = 0;
- for (auto _ : state) {
- incfs::access(mapping->data(), [&](auto ptr) { val += *ptr; });
- }
-}
-BENCHMARK(TestAccess);
-
-static void TestAccessFast(benchmark::State& state) {
- auto tmp = makeFile();
- auto mapping = android::base::MappedFile::FromFd(tmp->fd, 0, 1, PROT_READ);
- int val = 0;
- incfs::access(mapping->data(), [&](auto ptr) {
- for (auto _ : state) {
- val += *ptr;
- }
- });
-}
-BENCHMARK(TestAccessFast);
-
-static void TestAccessVal(benchmark::State& state) {
- auto tmp = makeFile();
- auto mapping = android::base::MappedFile::FromFd(tmp->fd, 0, 1, PROT_READ);
- int val = 0;
- for (auto _ : state) {
- incfs::access(mapping->data(), [&](auto ptr) { return val += *ptr; });
- }
-}
-BENCHMARK(TestAccessVal);
-
-static void TestAccessNested(benchmark::State& state) {
- auto tmp = makeFile();
- auto mapping = android::base::MappedFile::FromFd(tmp->fd, 0, 1, PROT_READ);
- int val = 0;
- incfs::access(nullptr, [&](auto) {
- for (auto _ : state) {
- incfs::access(mapping->data(), [&](auto ptr) { val += *ptr; });
- }
- });
-}
-BENCHMARK(TestAccessNested);
-
-static void TestAccessDoubleNested(benchmark::State& state) {
- auto tmp = makeFile();
- auto mapping = android::base::MappedFile::FromFd(tmp->fd, 0, 1, PROT_READ);
- int val = 0;
- incfs::access(nullptr, [&](auto) {
- incfs::access(nullptr, [&](auto) {
- for (auto _ : state) {
- incfs::access(mapping->data(), [&](auto ptr) { val += *ptr; });
- }
- });
- });
-}
-BENCHMARK(TestAccessDoubleNested);
-
-static void TestAccessError(benchmark::State& state) {
- auto [tmp, mapping] = makeEmptyFileMapping();
- int val = 0;
- for (auto _ : state) {
- incfs::access(mapping->data(), [&](auto ptr) { val += *ptr; });
- }
-}
-BENCHMARK(TestAccessError);
-
-BENCHMARK_MAIN();
diff --git a/incfs/tests/incfs_test.cpp b/incfs/tests/incfs_test.cpp
index 94bbdcd..1dc419f 100644
--- a/incfs/tests/incfs_test.cpp
+++ b/incfs/tests/incfs_test.cpp
@@ -14,20 +14,27 @@
* limitations under the License.
*/
-#include <android-base/file.h>
-#include <android-base/stringprintf.h>
-#include <sys/select.h>
+#include "incfs.h"
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <android-base/unique_fd.h>
+#include <gtest/gtest.h>
+#include <selinux/selinux.h>
+#include <sys/select.h>
#include <unistd.h>
#include <optional>
#include <thread>
-#include "IncFsTestBase.h"
+#include "path.h"
using namespace android::incfs;
using namespace std::literals;
-namespace ab = android::base;
+
+static bool exists(std::string_view path) {
+ return access(path.data(), F_OK) == 0;
+}
struct ScopedUnmount {
std::string path_;
@@ -35,16 +42,90 @@
~ScopedUnmount() { unmount(path_); }
};
-class IncFsTest : public IncFsTestBase {
+class IncFsTest : public ::testing::Test {
protected:
- virtual int32_t getReadTimeout() {
- return std::chrono::duration_cast<std::chrono::milliseconds>(kDefaultReadTimeout).count();
+ virtual void SetUp() {
+ tmp_dir_for_mount_.emplace();
+ mount_dir_path_ = tmp_dir_for_mount_->path;
+ tmp_dir_for_image_.emplace();
+ image_dir_path_ = tmp_dir_for_image_->path;
+ ASSERT_TRUE(exists(image_dir_path_));
+ ASSERT_TRUE(exists(mount_dir_path_));
+ if (!enabled()) {
+ GTEST_SKIP() << "test not supported: IncFS is not enabled";
+ } else {
+ control_ =
+ mount(image_dir_path_, mount_dir_path_,
+ MountOptions{.readLogBufferPages = 4,
+ .defaultReadTimeoutMs = std::chrono::duration_cast<
+ std::chrono::milliseconds>(
+ kDefaultReadTimeout)
+ .count()});
+ ASSERT_TRUE(control_.cmd() >= 0) << "Expected >= 0 got " << control_.cmd();
+ ASSERT_TRUE(control_.pendingReads() >= 0);
+ ASSERT_TRUE(control_.logs() >= 0);
+ checkRestoreconResult(mountPath(INCFS_PENDING_READS_FILENAME));
+ checkRestoreconResult(mountPath(INCFS_LOG_FILENAME));
+ }
+ }
+
+ static void checkRestoreconResult(std::string_view path) {
+ char* ctx = nullptr;
+ ASSERT_NE(-1, getfilecon(path.data(), &ctx));
+ ASSERT_EQ("u:object_r:shell_data_file:s0", std::string(ctx));
+ freecon(ctx);
+ }
+
+ virtual void TearDown() {
+ unmount(mount_dir_path_);
+ tmp_dir_for_image_.reset();
+ tmp_dir_for_mount_.reset();
+ EXPECT_FALSE(exists(image_dir_path_));
+ EXPECT_FALSE(exists(mount_dir_path_));
+ }
+
+ template <class... Paths>
+ std::string mountPath(Paths&&... paths) const {
+ return path::join(mount_dir_path_, std::forward<Paths>(paths)...);
+ }
+
+ static IncFsFileId fileId(uint64_t i) {
+ IncFsFileId id = {};
+ static_assert(sizeof(id) >= sizeof(i));
+ memcpy(&id, &i, sizeof(i));
+ return id;
}
static IncFsSpan metadata(std::string_view sv) {
return {.data = sv.data(), .size = IncFsSize(sv.size())};
}
+ int makeFileWithHash(int id) {
+ // calculate the required size for two leaf hash blocks
+ constexpr auto size =
+ (INCFS_DATA_FILE_BLOCK_SIZE / INCFS_MAX_HASH_SIZE + 1) * INCFS_DATA_FILE_BLOCK_SIZE;
+
+ // assemble a signature/hashing data for it
+ struct __attribute__((packed)) Signature {
+ uint32_t version = INCFS_SIGNATURE_VERSION;
+ uint32_t hashingSize = sizeof(hashing);
+ struct __attribute__((packed)) Hashing {
+ uint32_t algo = INCFS_HASH_TREE_SHA256;
+ uint8_t log2Blocksize = 12;
+ uint32_t saltSize = 0;
+ uint32_t rootHashSize = INCFS_MAX_HASH_SIZE;
+ char rootHash[INCFS_MAX_HASH_SIZE] = {};
+ } hashing;
+ uint32_t signingSize = 0;
+ } signature;
+
+ int res = makeFile(control_, mountPath(test_file_name_), 0555, fileId(id),
+ {.size = size,
+ .signature = {.data = (char*)&signature, .size = sizeof(signature)}});
+ EXPECT_EQ(0, res);
+ return res ? -1 : size;
+ }
+
static int sizeToPages(int size) {
return (size + INCFS_DATA_FILE_BLOCK_SIZE - 1) / INCFS_DATA_FILE_BLOCK_SIZE;
}
@@ -105,100 +186,14 @@
ASSERT_EQ((int)std::size(blocks), writeBlocks({blocks, std::size(blocks)}));
}
- void writeBlock(int pageIndex) {
- auto fd = openForSpecialOps(control_, fileId(1));
- ASSERT_GE(fd.get(), 0);
-
- std::vector<char> data(INCFS_DATA_FILE_BLOCK_SIZE);
- auto block = DataBlock{
- .fileFd = fd.get(),
- .pageIndex = pageIndex,
- .compression = INCFS_COMPRESSION_KIND_NONE,
- .dataSize = (uint32_t)data.size(),
- .data = data.data(),
- };
- ASSERT_EQ(1, writeBlocks({&block, 1}));
- }
-
- template <class ReadStruct>
- void testWriteBlockAndPageRead() {
- const auto id = fileId(1);
- ASSERT_TRUE(control_.logs() >= 0);
- ASSERT_EQ(0,
- makeFile(control_, mountPath(test_file_name_), 0555, id,
- {.size = test_file_size_}));
- writeBlock(/*pageIndex=*/0);
-
- std::thread wait_page_read_thread([&]() {
- std::vector<ReadStruct> reads;
- auto res = waitForPageReads(control_, std::chrono::seconds(5), &reads);
- ASSERT_EQ(WaitResult::HaveData, res) << (int)res;
- ASSERT_FALSE(reads.empty());
- EXPECT_EQ(0, memcmp(&id, &reads[0].id, sizeof(id)));
- EXPECT_EQ(0, int(reads[0].block));
- if constexpr (std::is_same_v<ReadStruct, ReadInfoWithUid>) {
- if (features() & Features::v2) {
- EXPECT_NE(kIncFsNoUid, int(reads[0].uid));
- } else {
- EXPECT_EQ(kIncFsNoUid, int(reads[0].uid));
- }
- }
- });
-
- const auto file_path = mountPath(test_file_name_);
- const android::base::unique_fd readFd(
- open(file_path.c_str(), O_RDONLY | O_CLOEXEC | O_BINARY));
- ASSERT_TRUE(readFd >= 0);
- char buf[INCFS_DATA_FILE_BLOCK_SIZE];
- ASSERT_TRUE(android::base::ReadFully(readFd, buf, sizeof(buf)));
- wait_page_read_thread.join();
- }
-
- template <class PendingRead>
- void testWaitForPendingReads() {
- const auto id = fileId(1);
- ASSERT_EQ(0,
- makeFile(control_, mountPath(test_file_name_), 0555, id,
- {.size = test_file_size_}));
-
- std::thread wait_pending_read_thread([&]() {
- std::vector<PendingRead> pending_reads;
- ASSERT_EQ(WaitResult::HaveData,
- waitForPendingReads(control_, std::chrono::seconds(10), &pending_reads));
- ASSERT_GT(pending_reads.size(), 0u);
- EXPECT_EQ(0, memcmp(&id, &pending_reads[0].id, sizeof(id)));
- EXPECT_EQ(0, (int)pending_reads[0].block);
- if constexpr (std::is_same_v<PendingRead, ReadInfoWithUid>) {
- if (features() & Features::v2) {
- EXPECT_NE(kIncFsNoUid, int(pending_reads[0].uid));
- } else {
- EXPECT_EQ(kIncFsNoUid, int(pending_reads[0].uid));
- }
- }
-
- auto fd = openForSpecialOps(control_, fileId(1));
- ASSERT_GE(fd.get(), 0);
-
- std::vector<char> data(INCFS_DATA_FILE_BLOCK_SIZE);
- auto block = DataBlock{
- .fileFd = fd.get(),
- .pageIndex = 0,
- .compression = INCFS_COMPRESSION_KIND_NONE,
- .dataSize = (uint32_t)data.size(),
- .data = data.data(),
- };
- ASSERT_EQ(1, writeBlocks({&block, 1}));
- });
-
- const auto file_path = mountPath(test_file_name_);
- const android::base::unique_fd fd(open(file_path.c_str(), O_RDONLY | O_CLOEXEC | O_BINARY));
- ASSERT_GE(fd.get(), 0);
- char buf[INCFS_DATA_FILE_BLOCK_SIZE];
- ASSERT_TRUE(android::base::ReadFully(fd, buf, sizeof(buf)));
- wait_pending_read_thread.join();
- }
-
+ std::string mount_dir_path_;
+ std::optional<TemporaryDir> tmp_dir_for_mount_;
+ std::string image_dir_path_;
+ std::optional<TemporaryDir> tmp_dir_for_image_;
+ inline static const std::string_view test_file_name_ = "test.txt"sv;
+ inline static const std::string_view test_dir_name_ = "test_dir"sv;
inline static const int test_file_size_ = INCFS_DATA_FILE_BLOCK_SIZE;
+ Control control_;
};
TEST_F(IncFsTest, GetIncfsFeatures) {
@@ -222,32 +217,14 @@
ASSERT_TRUE(isIncFsPath(tmp_dir_to_bind.path));
}
-TEST_F(IncFsTest, FalseIncfsPathFile) {
- TemporaryFile test_file;
- ASSERT_FALSE(isIncFsFd(test_file.fd));
- ASSERT_FALSE(isIncFsPath(test_file.path));
-}
-
-TEST_F(IncFsTest, TrueIncfsPathForBindMountFile) {
- ASSERT_EQ(0,
- makeFile(control_, mountPath(test_file_name_), 0555, fileId(1),
- {.size = test_file_size_}));
- const auto file_path = mountPath(test_file_name_);
- const android::base::unique_fd fd(open(file_path.c_str(), O_RDONLY | O_CLOEXEC | O_BINARY));
- ASSERT_GE(fd.get(), 0);
- ASSERT_TRUE(isIncFsFd(fd.get()));
- ASSERT_TRUE(isIncFsPath(file_path));
-}
-
TEST_F(IncFsTest, Control) {
ASSERT_TRUE(control_);
EXPECT_GE(IncFs_GetControlFd(control_, CMD), 0);
EXPECT_GE(IncFs_GetControlFd(control_, PENDING_READS), 0);
EXPECT_GE(IncFs_GetControlFd(control_, LOGS), 0);
- EXPECT_EQ((features() & Features::v2) != 0, IncFs_GetControlFd(control_, BLOCKS_WRITTEN) >= 0);
auto fds = control_.releaseFds();
- EXPECT_GE(fds.size(), size_t(4));
+ EXPECT_GE(fds.size(), size_t(3));
EXPECT_GE(fds[0].get(), 0);
EXPECT_GE(fds[1].get(), 0);
EXPECT_GE(fds[2].get(), 0);
@@ -255,28 +232,26 @@
EXPECT_LT(IncFs_GetControlFd(control_, CMD), 0);
EXPECT_LT(IncFs_GetControlFd(control_, PENDING_READS), 0);
EXPECT_LT(IncFs_GetControlFd(control_, LOGS), 0);
- EXPECT_LT(IncFs_GetControlFd(control_, BLOCKS_WRITTEN), 0);
control_.close();
EXPECT_FALSE(control_);
- auto control = IncFs_CreateControl(fds[0].release(), fds[1].release(), fds[2].release(), -1);
+ auto control = IncFs_CreateControl(fds[0].release(), fds[1].release(), fds[2].release());
ASSERT_TRUE(control);
EXPECT_GE(IncFs_GetControlFd(control, CMD), 0);
EXPECT_GE(IncFs_GetControlFd(control, PENDING_READS), 0);
EXPECT_GE(IncFs_GetControlFd(control, LOGS), 0);
- IncFsFd rawFds[4];
+ IncFsFd rawFds[3];
EXPECT_EQ(-EINVAL, IncFs_ReleaseControlFds(nullptr, rawFds, 3));
EXPECT_EQ(-EINVAL, IncFs_ReleaseControlFds(control, nullptr, 3));
EXPECT_EQ(-ERANGE, IncFs_ReleaseControlFds(control, rawFds, 2));
- EXPECT_EQ(4, IncFs_ReleaseControlFds(control, rawFds, 4));
+ EXPECT_EQ(3, IncFs_ReleaseControlFds(control, rawFds, 3));
EXPECT_GE(rawFds[0], 0);
EXPECT_GE(rawFds[1], 0);
EXPECT_GE(rawFds[2], 0);
::close(rawFds[0]);
::close(rawFds[1]);
::close(rawFds[2]);
- if (rawFds[3] >= 0) ::close(rawFds[3]);
IncFs_DeleteControl(control);
}
@@ -328,7 +303,7 @@
TEST_F(IncFsTest, RootInvalidControl) {
const TemporaryFile tmp_file;
- auto control{createControl(tmp_file.fd, -1, -1, -1)};
+ auto control{createControl(tmp_file.fd, -1, -1)};
ASSERT_EQ("", root(control)) << "Error: " << errno;
}
@@ -371,39 +346,6 @@
ASSERT_EQ(0, (int)s.st_size);
}
-TEST_F(IncFsTest, MakeMappedFile) {
- ASSERT_EQ(0, makeDir(control_, mountPath(test_dir_name_)));
-
- constexpr auto file_size = INCFS_DATA_FILE_BLOCK_SIZE * 2;
- constexpr auto mapped_file_offset = file_size / 2;
- constexpr auto mapped_file_size = file_size / 3;
-
- const auto file_path = mountPath(test_dir_name_, test_file_name_);
- ASSERT_FALSE(exists(file_path));
- ASSERT_EQ(0,
- makeFile(control_, file_path, 0111, fileId(1),
- {.size = file_size, .metadata = metadata("md")}));
- struct stat s = {};
- ASSERT_EQ(0, stat(file_path.c_str(), &s));
- ASSERT_EQ(file_size, (int)s.st_size);
-
- const auto mapped_file_path = mountPath(test_dir_name_, test_mapped_file_name_);
- ASSERT_FALSE(exists(mapped_file_path));
- ASSERT_EQ(0,
- makeMappedFile(control_, mapped_file_path, 0111,
- {.sourceId = fileId(1),
- .sourceOffset = mapped_file_offset,
- .size = mapped_file_size}));
- s = {};
- ASSERT_EQ(0, stat(mapped_file_path.c_str(), &s));
- ASSERT_EQ(mapped_file_size, (int)s.st_size);
-
- // Check fileId for the source file.
- ASSERT_EQ(fileId(1), getFileId(control_, file_path));
- // Check that there is no fileId for the mapped file.
- ASSERT_EQ(kIncFsInvalidFileId, getFileId(control_, mapped_file_path));
-}
-
TEST_F(IncFsTest, GetFileId) {
auto id = fileId(1);
ASSERT_EQ(0,
@@ -447,19 +389,73 @@
}
TEST_F(IncFsTest, WriteBlocksAndPageRead) {
- ASSERT_NO_FATAL_FAILURE(testWriteBlockAndPageRead<ReadInfo>());
-}
+ const auto id = fileId(1);
+ ASSERT_TRUE(control_.logs() >= 0);
+ ASSERT_EQ(0,
+ makeFile(control_, mountPath(test_file_name_), 0555, id, {.size = test_file_size_}));
+ auto fd = openForSpecialOps(control_, fileId(1));
+ ASSERT_GE(fd.get(), 0);
-TEST_F(IncFsTest, WriteBlocksAndPageReadWithUid) {
- ASSERT_NO_FATAL_FAILURE(testWriteBlockAndPageRead<ReadInfoWithUid>());
+ std::vector<char> data(INCFS_DATA_FILE_BLOCK_SIZE);
+ auto block = DataBlock{
+ .fileFd = fd.get(),
+ .pageIndex = 0,
+ .compression = INCFS_COMPRESSION_KIND_NONE,
+ .dataSize = (uint32_t)data.size(),
+ .data = data.data(),
+ };
+ ASSERT_EQ(1, writeBlocks({&block, 1}));
+
+ std::thread wait_page_read_thread([&]() {
+ std::vector<ReadInfo> reads;
+ ASSERT_EQ(WaitResult::HaveData,
+ waitForPageReads(control_, std::chrono::seconds(5), &reads));
+ ASSERT_FALSE(reads.empty());
+ ASSERT_EQ(0, memcmp(&id, &reads[0].id, sizeof(id)));
+ ASSERT_EQ(0, int(reads[0].block));
+ });
+
+ const auto file_path = mountPath(test_file_name_);
+ const android::base::unique_fd readFd(open(file_path.c_str(), O_RDONLY | O_CLOEXEC | O_BINARY));
+ ASSERT_TRUE(readFd >= 0);
+ char buf[INCFS_DATA_FILE_BLOCK_SIZE];
+ ASSERT_TRUE(android::base::ReadFully(readFd, buf, sizeof(buf)));
+ wait_page_read_thread.join();
}
TEST_F(IncFsTest, WaitForPendingReads) {
- ASSERT_NO_FATAL_FAILURE(testWaitForPendingReads<ReadInfo>());
-}
+ const auto id = fileId(1);
+ ASSERT_EQ(0,
+ makeFile(control_, mountPath(test_file_name_), 0555, id, {.size = test_file_size_}));
-TEST_F(IncFsTest, WaitForPendingReadsWithUid) {
- ASSERT_NO_FATAL_FAILURE(testWaitForPendingReads<ReadInfoWithUid>());
+ std::thread wait_pending_read_thread([&]() {
+ std::vector<ReadInfo> pending_reads;
+ ASSERT_EQ(WaitResult::HaveData,
+ waitForPendingReads(control_, std::chrono::seconds(10), &pending_reads));
+ ASSERT_GT(pending_reads.size(), 0u);
+ ASSERT_EQ(0, memcmp(&id, &pending_reads[0].id, sizeof(id)));
+ ASSERT_EQ(0, (int)pending_reads[0].block);
+
+ auto fd = openForSpecialOps(control_, fileId(1));
+ ASSERT_GE(fd.get(), 0);
+
+ std::vector<char> data(INCFS_DATA_FILE_BLOCK_SIZE);
+ auto block = DataBlock{
+ .fileFd = fd.get(),
+ .pageIndex = 0,
+ .compression = INCFS_COMPRESSION_KIND_NONE,
+ .dataSize = (uint32_t)data.size(),
+ .data = data.data(),
+ };
+ ASSERT_EQ(1, writeBlocks({&block, 1}));
+ });
+
+ const auto file_path = mountPath(test_file_name_);
+ const android::base::unique_fd fd(open(file_path.c_str(), O_RDONLY | O_CLOEXEC | O_BINARY));
+ ASSERT_GE(fd.get(), 0);
+ char buf[INCFS_DATA_FILE_BLOCK_SIZE];
+ ASSERT_TRUE(android::base::ReadFully(fd, buf, sizeof(buf)));
+ wait_pending_read_thread.join();
}
TEST_F(IncFsTest, GetFilledRangesBad) {
@@ -509,7 +505,6 @@
EXPECT_EQ(0, filledRanges.hashRangesCount);
EXPECT_EQ(-ENODATA, IncFs_IsFullyLoaded(fd.get()));
- EXPECT_EQ(-ENODATA, IncFs_IsEverythingFullyLoaded(control_));
// write one block
std::vector<char> data(INCFS_DATA_FILE_BLOCK_SIZE);
@@ -543,7 +538,6 @@
EXPECT_EQ(0, filledRanges.hashRangesCount);
EXPECT_EQ(-ENODATA, IncFs_IsFullyLoaded(fd.get()));
- EXPECT_EQ(-ENODATA, IncFs_IsEverythingFullyLoaded(control_));
// append one more block next to the first one
block.pageIndex = 1;
@@ -572,7 +566,6 @@
EXPECT_EQ(0, filledRanges.hashRangesCount);
EXPECT_EQ(-ENODATA, IncFs_IsFullyLoaded(fd.get()));
- EXPECT_EQ(-ENODATA, IncFs_IsEverythingFullyLoaded(control_));
// now create a gap between filled blocks
block.pageIndex = 3;
@@ -613,7 +606,6 @@
EXPECT_EQ(0, filledRanges.hashRangesCount);
EXPECT_EQ(-ENODATA, IncFs_IsFullyLoaded(fd.get()));
- EXPECT_EQ(-ENODATA, IncFs_IsEverythingFullyLoaded(control_));
// at last fill the whole file and make sure we report it as having a single range
block.pageIndex = 2;
@@ -641,7 +633,6 @@
EXPECT_EQ(0, filledRanges.hashRangesCount);
EXPECT_EQ(0, IncFs_IsFullyLoaded(fd.get()));
- EXPECT_EQ(0, IncFs_IsEverythingFullyLoaded(control_));
}
TEST_F(IncFsTest, GetFilledRangesSmallBuffer) {
@@ -773,7 +764,6 @@
EXPECT_EQ(size_t(1), ranges3.hashRanges()[1].size());
EXPECT_EQ(LoadingState::MissingBlocks, isFullyLoaded(fd.get()));
- EXPECT_EQ(LoadingState::MissingBlocks, isEverythingFullyLoaded(control_));
{
std::vector<char> data(INCFS_DATA_FILE_BLOCK_SIZE);
@@ -793,705 +783,4 @@
}
}
EXPECT_EQ(LoadingState::Full, isFullyLoaded(fd.get()));
- EXPECT_EQ(LoadingState::Full, isEverythingFullyLoaded(control_));
-}
-
-TEST_F(IncFsTest, BlocksWritten) {
- if (!(features() & Features::v2)) {
- GTEST_SKIP() << "test not supported: IncFS is too old";
- return;
- }
- const auto id = fileId(1);
- ASSERT_EQ(0,
- makeFile(control_, mountPath(test_file_name_), 0555, id, {.size = test_file_size_}));
-
- IncFsSize blocksWritten = 0;
- ASSERT_EQ(0, IncFs_WaitForFsWrittenBlocksChange(control_, 0, &blocksWritten));
- EXPECT_EQ(0, blocksWritten);
-
- auto fd = openForSpecialOps(control_, fileId(1));
- ASSERT_GE(fd.get(), 0);
-
- std::vector<char> data(INCFS_DATA_FILE_BLOCK_SIZE);
- auto block = DataBlock{
- .fileFd = fd.get(),
- .pageIndex = 0,
- .compression = INCFS_COMPRESSION_KIND_NONE,
- .dataSize = (uint32_t)data.size(),
- .data = data.data(),
- };
- ASSERT_EQ(1, writeBlocks({&block, 1}));
-
- ASSERT_EQ(0, IncFs_WaitForFsWrittenBlocksChange(control_, 0, &blocksWritten));
- EXPECT_EQ(1, blocksWritten);
-}
-
-TEST_F(IncFsTest, Timeouts) {
- if (!(features() & Features::v2)) {
- GTEST_SKIP() << "test not supported: IncFS is too old";
- return;
- }
-
- IncFsUidReadTimeouts timeouts[2] = {{1, 1000, 2000, 3000}, {2, 1000, 3000, 4000}};
-
- EXPECT_EQ(0, IncFs_SetUidReadTimeouts(control_, timeouts, std::size(timeouts)));
-
- IncFsUidReadTimeouts outTimeouts[3];
-
- size_t outSize = 1;
- EXPECT_EQ(-E2BIG, IncFs_GetUidReadTimeouts(control_, outTimeouts, &outSize));
- EXPECT_EQ(size_t(2), outSize);
-
- outSize = 3;
- EXPECT_EQ(0, IncFs_GetUidReadTimeouts(control_, outTimeouts, &outSize));
- EXPECT_EQ(size_t(2), outSize);
-
- EXPECT_EQ(0, memcmp(timeouts, outTimeouts, 2 * sizeof(timeouts[0])));
-}
-
-TEST_F(IncFsTest, CompletionNoFiles) {
- if (!(features() & Features::v2)) {
- GTEST_SKIP() << "test not supported: IncFS is too old";
- return;
- }
-
- size_t count = 0;
- EXPECT_EQ(0, IncFs_ListIncompleteFiles(control_, nullptr, &count));
- EXPECT_EQ(size_t(0), count);
- EXPECT_EQ(0, IncFs_WaitForLoadingComplete(control_, 0));
-}
-
-TEST_F(IncFsTest, CompletionOneFile) {
- if (!(features() & Features::v2)) {
- GTEST_SKIP() << "test not supported: IncFS is too old";
- return;
- }
-
- const auto id = fileId(1);
- ASSERT_EQ(0,
- makeFile(control_, mountPath(test_file_name_), 0555, id, {.size = test_file_size_}));
-
- size_t count = 0;
- EXPECT_EQ(-E2BIG, IncFs_ListIncompleteFiles(control_, nullptr, &count));
- EXPECT_EQ(size_t(1), count);
- EXPECT_EQ(-ETIMEDOUT, IncFs_WaitForLoadingComplete(control_, 0));
-
- IncFsFileId ids[2];
- count = 2;
- EXPECT_EQ(0, IncFs_ListIncompleteFiles(control_, ids, &count));
- EXPECT_EQ(size_t(1), count);
- EXPECT_EQ(id, ids[0]);
-
- auto fd = openForSpecialOps(control_, id);
- ASSERT_GE(fd.get(), 0);
- std::vector<char> data(INCFS_DATA_FILE_BLOCK_SIZE);
- auto block = DataBlock{
- .fileFd = fd.get(),
- .pageIndex = 0,
- .compression = INCFS_COMPRESSION_KIND_NONE,
- .dataSize = (uint32_t)data.size(),
- .data = data.data(),
- };
- ASSERT_EQ(1, writeBlocks({&block, 1}));
-
- count = 2;
- EXPECT_EQ(0, IncFs_ListIncompleteFiles(control_, ids, &count));
- EXPECT_EQ(size_t(0), count);
- EXPECT_EQ(0, IncFs_WaitForLoadingComplete(control_, 0));
-}
-
-TEST_F(IncFsTest, CompletionMultiple) {
- if (!(features() & Features::v2)) {
- GTEST_SKIP() << "test not supported: IncFS is too old";
- return;
- }
-
- const auto id = fileId(1);
- ASSERT_EQ(0,
- makeFile(control_, mountPath(test_file_name_), 0555, id, {.size = test_file_size_}));
-
- size_t count = 0;
- EXPECT_EQ(-E2BIG, IncFs_ListIncompleteFiles(control_, nullptr, &count));
- EXPECT_EQ(size_t(1), count);
- EXPECT_EQ(-ETIMEDOUT, IncFs_WaitForLoadingComplete(control_, 0));
-
- // fill the existing file but add another one
- const auto id2 = fileId(2);
- ASSERT_EQ(0, makeFile(control_, mountPath("test2"), 0555, id2, {.size = test_file_size_}));
-
- IncFsFileId ids[2];
- count = 2;
- EXPECT_EQ(0, IncFs_ListIncompleteFiles(control_, ids, &count));
- EXPECT_EQ(size_t(2), count);
- EXPECT_EQ(id, ids[0]);
- EXPECT_EQ(id2, ids[1]);
-
- auto fd = openForSpecialOps(control_, id);
- ASSERT_GE(fd.get(), 0);
- std::vector<char> data(INCFS_DATA_FILE_BLOCK_SIZE);
- auto block = DataBlock{
- .fileFd = fd.get(),
- .pageIndex = 0,
- .compression = INCFS_COMPRESSION_KIND_NONE,
- .dataSize = (uint32_t)data.size(),
- .data = data.data(),
- };
- ASSERT_EQ(1, writeBlocks({&block, 1}));
-
- count = 2;
- EXPECT_EQ(0, IncFs_ListIncompleteFiles(control_, ids, &count));
- EXPECT_EQ(size_t(1), count);
- EXPECT_EQ(id2, ids[0]);
- EXPECT_EQ(-ETIMEDOUT, IncFs_WaitForLoadingComplete(control_, 0));
-}
-
-TEST_F(IncFsTest, CompletionWait) {
- if (!(features() & Features::v2)) {
- GTEST_SKIP() << "test not supported: IncFS is too old";
- return;
- }
-
- ASSERT_EQ(0,
- makeFile(control_, mountPath("test1"), 0555, fileId(1),
- {.size = INCFS_DATA_FILE_BLOCK_SIZE}));
- ASSERT_EQ(0,
- makeFile(control_, mountPath("test2"), 0555, fileId(2),
- {.size = INCFS_DATA_FILE_BLOCK_SIZE}));
- ASSERT_EQ(0,
- makeFile(control_, mountPath("test3"), 0555, fileId(3),
- {.size = INCFS_DATA_FILE_BLOCK_SIZE}));
-
- std::atomic<int> res = -1;
- auto waiter = std::thread([&] { res = IncFs_WaitForLoadingComplete(control_, 5 * 1000); });
-
- std::vector<char> data(INCFS_DATA_FILE_BLOCK_SIZE);
-
- {
- auto fd = openForSpecialOps(control_, fileId(1));
- ASSERT_GE(fd.get(), 0);
- auto block = DataBlock{
- .fileFd = fd.get(),
- .pageIndex = 0,
- .compression = INCFS_COMPRESSION_KIND_NONE,
- .dataSize = (uint32_t)data.size(),
- .data = data.data(),
- };
- ASSERT_EQ(1, writeBlocks({&block, 1}));
- }
- ASSERT_TRUE(res == -1);
-
- {
- auto fd = openForSpecialOps(control_, fileId(3));
- ASSERT_GE(fd.get(), 0);
- auto block = DataBlock{
- .fileFd = fd.get(),
- .pageIndex = 0,
- .compression = INCFS_COMPRESSION_KIND_NONE,
- .dataSize = (uint32_t)data.size(),
- .data = data.data(),
- };
- ASSERT_EQ(1, writeBlocks({&block, 1}));
- }
- ASSERT_TRUE(res == -1);
-
- {
- auto fd = openForSpecialOps(control_, fileId(2));
- ASSERT_GE(fd.get(), 0);
- auto block = DataBlock{
- .fileFd = fd.get(),
- .pageIndex = 0,
- .compression = INCFS_COMPRESSION_KIND_NONE,
- .dataSize = (uint32_t)data.size(),
- .data = data.data(),
- };
- ASSERT_EQ(1, writeBlocks({&block, 1}));
- }
-
- waiter.join();
-
- auto listIncomplete = [&] {
- IncFsFileId ids[3];
- size_t count = 3;
- if (IncFs_ListIncompleteFiles(control_, ids, &count) != 0) {
- return "error listing incomplete files"s;
- }
- auto res = ab::StringPrintf("[%d]", int(count));
- for (size_t i = 0; i < count; ++i) {
- ab::StringAppendF(&res, " %s", toString(ids[i]).c_str());
- }
- return res;
- };
- EXPECT_EQ(0, res) << "Incomplete files: " << listIncomplete();
-}
-
-TEST_F(IncFsTest, GetBlockCounts) {
- if (!(features() & Features::v2)) {
- GTEST_SKIP() << "test not supported: IncFS is too old";
- return;
- }
-
- const auto id = fileId(1);
- ASSERT_EQ(0,
- makeFile(control_, mountPath(test_file_name_), 0555, id,
- {.size = 20 * INCFS_DATA_FILE_BLOCK_SIZE + 3}));
-
- IncFsBlockCounts counts = {};
- EXPECT_EQ(0,
- IncFs_GetFileBlockCountByPath(control_, mountPath(test_file_name_).c_str(), &counts));
- EXPECT_EQ(21, counts.totalDataBlocks);
- EXPECT_EQ(0, counts.filledDataBlocks);
- EXPECT_EQ(0, counts.totalHashBlocks);
- EXPECT_EQ(0, counts.filledHashBlocks);
-
- EXPECT_EQ(0, IncFs_GetFileBlockCountById(control_, id, &counts));
- EXPECT_EQ(21, counts.totalDataBlocks);
- EXPECT_EQ(0, counts.filledDataBlocks);
- EXPECT_EQ(0, counts.totalHashBlocks);
- EXPECT_EQ(0, counts.filledHashBlocks);
-
- auto fd = openForSpecialOps(control_, id);
- ASSERT_GE(fd.get(), 0);
- std::vector<char> data(INCFS_DATA_FILE_BLOCK_SIZE);
- auto block = DataBlock{
- .fileFd = fd.get(),
- .pageIndex = 3,
- .compression = INCFS_COMPRESSION_KIND_NONE,
- .dataSize = (uint32_t)data.size(),
- .data = data.data(),
- };
- ASSERT_EQ(1, writeBlocks({&block, 1}));
-
- EXPECT_EQ(0,
- IncFs_GetFileBlockCountByPath(control_, mountPath(test_file_name_).c_str(), &counts));
- EXPECT_EQ(21, counts.totalDataBlocks);
- EXPECT_EQ(1, counts.filledDataBlocks);
- EXPECT_EQ(0, counts.totalHashBlocks);
- EXPECT_EQ(0, counts.filledHashBlocks);
-
- EXPECT_EQ(0, IncFs_GetFileBlockCountById(control_, id, &counts));
- EXPECT_EQ(21, counts.totalDataBlocks);
- EXPECT_EQ(1, counts.filledDataBlocks);
- EXPECT_EQ(0, counts.totalHashBlocks);
- EXPECT_EQ(0, counts.filledHashBlocks);
-}
-
-TEST_F(IncFsTest, GetBlockCountsHash) {
- if (!(features() & Features::v2)) {
- GTEST_SKIP() << "test not supported: IncFS is too old";
- return;
- }
-
- auto size = makeFileWithHash(1);
- ASSERT_GT(size, 0);
-
- IncFsBlockCounts counts = {};
- EXPECT_EQ(0,
- IncFs_GetFileBlockCountByPath(control_, mountPath(test_file_name_).c_str(), &counts));
- EXPECT_EQ(sizeToPages(size), counts.totalDataBlocks);
- EXPECT_EQ(0, counts.filledDataBlocks);
- EXPECT_EQ(3, counts.totalHashBlocks);
- EXPECT_EQ(0, counts.filledHashBlocks);
-
- ASSERT_NO_FATAL_FAILURE(writeTestRanges(1, size));
-
- EXPECT_EQ(0,
- IncFs_GetFileBlockCountByPath(control_, mountPath(test_file_name_).c_str(), &counts));
- EXPECT_EQ(sizeToPages(size), counts.totalDataBlocks);
- EXPECT_EQ(4, counts.filledDataBlocks);
- EXPECT_EQ(3, counts.totalHashBlocks);
- EXPECT_EQ(2, counts.filledHashBlocks);
-}
-
-TEST_F(IncFsTest, ReserveSpace) {
- auto size = makeFileWithHash(1);
- ASSERT_GT(size, 0);
-
- EXPECT_EQ(-ENOENT,
- IncFs_ReserveSpaceByPath(control_, mountPath("1"s += test_file_name_).c_str(), size));
- EXPECT_EQ(0, IncFs_ReserveSpaceByPath(control_, mountPath(test_file_name_).c_str(), size));
- EXPECT_EQ(0, IncFs_ReserveSpaceByPath(control_, mountPath(test_file_name_).c_str(), 2 * size));
- EXPECT_EQ(0, IncFs_ReserveSpaceByPath(control_, mountPath(test_file_name_).c_str(), 2 * size));
- EXPECT_EQ(0,
- IncFs_ReserveSpaceByPath(control_, mountPath(test_file_name_).c_str(),
- kTrimReservedSpace));
- EXPECT_EQ(0,
- IncFs_ReserveSpaceByPath(control_, mountPath(test_file_name_).c_str(),
- kTrimReservedSpace));
-
- EXPECT_EQ(-ENOENT, IncFs_ReserveSpaceById(control_, fileId(2), size));
- EXPECT_EQ(0, IncFs_ReserveSpaceById(control_, fileId(1), size));
- EXPECT_EQ(0, IncFs_ReserveSpaceById(control_, fileId(1), 2 * size));
- EXPECT_EQ(0, IncFs_ReserveSpaceById(control_, fileId(1), 2 * size));
- EXPECT_EQ(0, IncFs_ReserveSpaceById(control_, fileId(1), kTrimReservedSpace));
- EXPECT_EQ(0, IncFs_ReserveSpaceById(control_, fileId(1), kTrimReservedSpace));
-}
-
-TEST_F(IncFsTest, ForEachFile) {
- const auto incompleteSupported = (features() & Features::v2) != 0;
- EXPECT_EQ(-EINVAL, IncFs_ForEachFile(nullptr, nullptr, nullptr));
- EXPECT_EQ(-EINVAL, IncFs_ForEachIncompleteFile(nullptr, nullptr, nullptr));
- EXPECT_EQ(-EINVAL, IncFs_ForEachFile(control_, nullptr, nullptr));
- EXPECT_EQ(-EINVAL, IncFs_ForEachIncompleteFile(control_, nullptr, nullptr));
- EXPECT_EQ(0, IncFs_ForEachFile(control_, nullptr, [](auto, auto, auto) { return true; }));
- EXPECT_EQ(incompleteSupported ? 0 : -ENOTSUP,
- IncFs_ForEachIncompleteFile(control_, nullptr,
- [](auto, auto, auto) { return true; }));
- EXPECT_EQ(0, IncFs_ForEachFile(control_, this, [](auto, auto, auto) { return true; }));
- EXPECT_EQ(incompleteSupported ? 0 : -ENOTSUP,
- IncFs_ForEachIncompleteFile(control_, this, [](auto, auto, auto) { return true; }));
-
- int res = makeFile(control_, mountPath("incomplete.txt"), 0555, fileId(1),
- {.metadata = metadata("md")});
- ASSERT_EQ(res, 0);
-
- EXPECT_EQ(1, IncFs_ForEachFile(control_, this, [](auto, auto context, auto id) {
- auto self = (IncFsTest*)context;
- EXPECT_EQ(self->fileId(1), id);
- return true;
- }));
- EXPECT_EQ(incompleteSupported ? 0 : -ENOTSUP,
- IncFs_ForEachIncompleteFile(control_, this, [](auto, auto, auto) { return true; }));
-
- auto size = makeFileWithHash(2);
- ASSERT_GT(size, 0);
-
- EXPECT_EQ(1, IncFs_ForEachFile(control_, this, [](auto, auto context, auto id) {
- auto self = (IncFsTest*)context;
- EXPECT_TRUE(id == self->fileId(1) || id == self->fileId(2));
- return false;
- }));
- EXPECT_EQ(2, IncFs_ForEachFile(control_, this, [](auto, auto context, auto id) {
- auto self = (IncFsTest*)context;
- EXPECT_TRUE(id == self->fileId(1) || id == self->fileId(2));
- return true;
- }));
- EXPECT_EQ(incompleteSupported ? 1 : -ENOTSUP,
- IncFs_ForEachIncompleteFile(control_, this, [](auto, auto context, auto id) {
- auto self = (IncFsTest*)context;
- EXPECT_EQ(self->fileId(2), id);
- return true;
- }));
-}
-
-TEST(CStrWrapperTest, EmptyStringView) {
- ASSERT_STREQ("", details::c_str({}).get());
- ASSERT_STREQ("", details::c_str({nullptr, 0}).get());
-}
-
-class IncFsGetMetricsTest : public IncFsTestBase {
-protected:
- int32_t getReadTimeout() override { return 100 /* 0.1 second */; }
-};
-
-TEST_F(IncFsGetMetricsTest, MetricsWithNoEvents) {
- if (!(features() & Features::v2)) {
- GTEST_SKIP() << "test not supported: IncFS is too old";
- return;
- }
- IncFsLastReadError lastReadError = {.id = fileId(-1),
- .timestampUs = static_cast<uint64_t>(-1),
- .block = static_cast<IncFsBlockIndex>(-1),
- .errorNo = static_cast<uint32_t>(-1),
- .uid = static_cast<IncFsUid>(-1)};
- EXPECT_EQ(0, IncFs_GetLastReadError(control_, &lastReadError));
- // All fields should be zero
- EXPECT_EQ(FileId{}, lastReadError.id);
- EXPECT_EQ(0, (int)lastReadError.timestampUs);
- EXPECT_EQ(0, (int)lastReadError.block);
- EXPECT_EQ(0, (int)lastReadError.errorNo);
- EXPECT_EQ(0, (int)lastReadError.uid);
-
- IncFsMetrics incfsMetrics = {10, 10, 10, 10, 10, 10, 10, 10, 10};
- EXPECT_EQ(0, IncFs_GetMetrics(metrics_key_.c_str(), &incfsMetrics));
- EXPECT_EQ(0, (int)incfsMetrics.readsDelayedMin);
- EXPECT_EQ(0, (int)incfsMetrics.readsDelayedMinUs);
- EXPECT_EQ(0, (int)incfsMetrics.readsDelayedPending);
- EXPECT_EQ(0, (int)incfsMetrics.readsDelayedPendingUs);
- EXPECT_EQ(0, (int)incfsMetrics.readsFailedHashVerification);
- EXPECT_EQ(0, (int)incfsMetrics.readsFailedOther);
- EXPECT_EQ(0, (int)incfsMetrics.readsFailedTimedOut);
-}
-
-TEST_F(IncFsGetMetricsTest, MetricsWithReadsTimeOut) {
- if (!(features() & Features::v2)) {
- GTEST_SKIP() << "test not supported: IncFS is too old";
- return;
- }
- const auto id = fileId(1);
- ASSERT_EQ(0,
- makeFile(control_, mountPath(test_file_name_), 0555, id,
- {.size = INCFS_DATA_FILE_BLOCK_SIZE}));
-
- const auto file_path = mountPath(test_file_name_);
- const android::base::unique_fd fd(open(file_path.c_str(), O_RDONLY | O_CLOEXEC | O_BINARY));
- ASSERT_GE(fd.get(), 0);
- // Read should timeout immediately
- char buf[INCFS_DATA_FILE_BLOCK_SIZE];
- EXPECT_FALSE(android::base::ReadFully(fd, buf, sizeof(buf)));
- IncFsLastReadError lastReadError = {.id = fileId(-1),
- .timestampUs = static_cast<uint64_t>(-1),
- .block = static_cast<IncFsBlockIndex>(-1),
- .errorNo = static_cast<uint32_t>(-1),
- .uid = static_cast<IncFsUid>(-1)};
- EXPECT_EQ(0, IncFs_GetLastReadError(control_, &lastReadError));
- EXPECT_EQ(id, lastReadError.id);
- EXPECT_TRUE(lastReadError.timestampUs > 0);
- EXPECT_EQ(0, (int)lastReadError.block);
- EXPECT_EQ(-ETIME, (int)lastReadError.errorNo);
- EXPECT_EQ((int)getuid(), (int)lastReadError.uid);
-
- IncFsMetrics incfsMetrics = {10, 10, 10, 10, 10, 10, 10, 10, 10};
- EXPECT_EQ(0, IncFs_GetMetrics(metrics_key_.c_str(), &incfsMetrics));
- EXPECT_EQ(0, (int)incfsMetrics.readsDelayedMin);
- EXPECT_EQ(0, (int)incfsMetrics.readsDelayedMinUs);
- EXPECT_EQ(0, (int)incfsMetrics.readsDelayedPending);
- EXPECT_EQ(0, (int)incfsMetrics.readsDelayedPendingUs);
- EXPECT_EQ(0, (int)incfsMetrics.readsFailedHashVerification);
- EXPECT_EQ(0, (int)incfsMetrics.readsFailedOther);
- EXPECT_EQ(1, (int)incfsMetrics.readsFailedTimedOut);
-}
-
-TEST_F(IncFsGetMetricsTest, MetricsWithHashFailure) {
- if (!(features() & Features::v2)) {
- GTEST_SKIP() << "test not supported: IncFS is too old";
- return;
- }
- auto size = makeFileWithHash(1);
- ASSERT_GT(size, 0);
- // Make data and hash mismatch
- const auto id = fileId(1);
- char data[INCFS_DATA_FILE_BLOCK_SIZE]{static_cast<char>(-1)};
- char hashData[INCFS_DATA_FILE_BLOCK_SIZE]{};
- auto wfd = openForSpecialOps(control_, id);
- ASSERT_GE(wfd.get(), 0);
- DataBlock blocks[] = {{
- .fileFd = wfd.get(),
- .pageIndex = 0,
- .compression = INCFS_COMPRESSION_KIND_NONE,
- .dataSize = INCFS_DATA_FILE_BLOCK_SIZE,
- .data = data,
- },
- {
- .fileFd = wfd.get(),
- // first hash page
- .pageIndex = 0,
- .compression = INCFS_COMPRESSION_KIND_NONE,
- .dataSize = INCFS_DATA_FILE_BLOCK_SIZE,
- .kind = INCFS_BLOCK_KIND_HASH,
- .data = hashData,
- },
- {
- .fileFd = wfd.get(),
- .pageIndex = 2,
- .compression = INCFS_COMPRESSION_KIND_NONE,
- .dataSize = INCFS_DATA_FILE_BLOCK_SIZE,
- .kind = INCFS_BLOCK_KIND_HASH,
- .data = hashData,
- }};
- ASSERT_EQ((int)std::size(blocks), writeBlocks({blocks, std::size(blocks)}));
- const auto file_path = mountPath(test_file_name_);
- const android::base::unique_fd fd(open(file_path.c_str(), O_RDONLY | O_CLOEXEC | O_BINARY));
- ASSERT_GE(fd.get(), 0);
- // Read should fail at reading the first block due to hash failure
- char buf[INCFS_DATA_FILE_BLOCK_SIZE];
- EXPECT_FALSE(android::base::ReadFully(fd, buf, sizeof(buf)));
- IncFsLastReadError lastReadError = {.id = fileId(-1),
- .timestampUs = static_cast<uint64_t>(-1),
- .block = static_cast<IncFsBlockIndex>(-1),
- .errorNo = static_cast<uint32_t>(-1),
- .uid = static_cast<IncFsUid>(-1)};
- EXPECT_EQ(0, IncFs_GetLastReadError(control_, &lastReadError));
- EXPECT_EQ(0, std::strcmp(lastReadError.id.data, id.data));
- EXPECT_TRUE(lastReadError.timestampUs > 0);
- EXPECT_EQ(0, (int)lastReadError.block);
- EXPECT_EQ(-EBADMSG, (int)lastReadError.errorNo);
- EXPECT_EQ((int)getuid(), (int)lastReadError.uid);
-
- IncFsMetrics incfsMetrics = {10, 10, 10, 10, 10, 10, 10, 10, 10};
- EXPECT_EQ(0, IncFs_GetMetrics(metrics_key_.c_str(), &incfsMetrics));
- EXPECT_EQ(0, (int)incfsMetrics.readsDelayedMin);
- EXPECT_EQ(0, (int)incfsMetrics.readsDelayedMinUs);
- EXPECT_EQ(0, (int)incfsMetrics.readsDelayedPending);
- EXPECT_EQ(0, (int)incfsMetrics.readsDelayedPendingUs);
- EXPECT_EQ(1, (int)incfsMetrics.readsFailedHashVerification);
- EXPECT_EQ(0, (int)incfsMetrics.readsFailedOther);
- EXPECT_EQ(0, (int)incfsMetrics.readsFailedTimedOut);
-}
-
-TEST_F(IncFsGetMetricsTest, MetricsWithReadsDelayed) {
- if (!(features() & Features::v2)) {
- GTEST_SKIP() << "test not supported: IncFS is too old";
- return;
- }
- const auto id = fileId(1);
- int testFileSize = INCFS_DATA_FILE_BLOCK_SIZE;
- int waitBeforeWriteUs = 10000;
- ASSERT_EQ(0, makeFile(control_, mountPath(test_file_name_), 0555, id, {.size = testFileSize}));
- std::thread wait_before_write_thread([&]() {
- std::vector<ReadInfoWithUid> pending_reads;
- ASSERT_EQ(WaitResult::HaveData,
- waitForPendingReads(control_, std::chrono::seconds(1), &pending_reads));
- // Additional wait is needed for the kernel jiffies counter to increment
- usleep(waitBeforeWriteUs);
- auto fd = openForSpecialOps(control_, fileId(1));
- ASSERT_GE(fd.get(), 0);
- std::vector<char> data(INCFS_DATA_FILE_BLOCK_SIZE);
- auto block = DataBlock{
- .fileFd = fd.get(),
- .pageIndex = 0,
- .compression = INCFS_COMPRESSION_KIND_NONE,
- .dataSize = (uint32_t)data.size(),
- .data = data.data(),
- };
- ASSERT_EQ(1, writeBlocks({&block, 1}));
- });
-
- const auto file_path = mountPath(test_file_name_);
- const android::base::unique_fd fd(open(file_path.c_str(), O_RDONLY | O_CLOEXEC | O_BINARY));
- ASSERT_GE(fd.get(), 0);
- char buf[testFileSize];
- EXPECT_TRUE(android::base::ReadFully(fd, buf, sizeof(buf)));
- wait_before_write_thread.join();
-
- IncFsLastReadError lastReadError = {.id = fileId(-1), 1, 1, 1, 1};
- EXPECT_EQ(0, IncFs_GetLastReadError(control_, &lastReadError));
- EXPECT_EQ(FileId{}, lastReadError.id);
- EXPECT_EQ(0, (int)lastReadError.timestampUs);
- EXPECT_EQ(0, (int)lastReadError.block);
- EXPECT_EQ(0, (int)lastReadError.errorNo);
- EXPECT_EQ(0, (int)lastReadError.uid);
-
- IncFsMetrics incfsMetrics = {10, 10, 10, 10, 10, 10, 10, 10, 10};
- EXPECT_EQ(0, IncFs_GetMetrics(metrics_key_.c_str(), &incfsMetrics));
- EXPECT_EQ(0, (int)incfsMetrics.readsDelayedMin);
- EXPECT_EQ(0, (int)incfsMetrics.readsDelayedMinUs);
- EXPECT_EQ(1, (int)incfsMetrics.readsDelayedPending);
- EXPECT_TRUE((int)incfsMetrics.readsDelayedPendingUs > 0);
- EXPECT_EQ(0, (int)incfsMetrics.readsFailedHashVerification);
- EXPECT_EQ(0, (int)incfsMetrics.readsFailedOther);
- EXPECT_EQ(0, (int)incfsMetrics.readsFailedTimedOut);
-}
-
-TEST_F(IncFsGetMetricsTest, MetricsWithReadsDelayedPerUidTimeout) {
- if (!(features() & Features::v2)) {
- GTEST_SKIP() << "test not supported: IncFS is too old";
- return;
- }
- const auto id = fileId(1);
- int testFileSize = INCFS_DATA_FILE_BLOCK_SIZE;
- ASSERT_EQ(0, makeFile(control_, mountPath(test_file_name_), 0555, id, {.size = testFileSize}));
-
- auto fdToFill = openForSpecialOps(control_, fileId(1));
- ASSERT_GE(fdToFill.get(), 0);
- std::vector<char> data(INCFS_DATA_FILE_BLOCK_SIZE);
- auto block = DataBlock{
- .fileFd = fdToFill.get(),
- .pageIndex = 0,
- .compression = INCFS_COMPRESSION_KIND_NONE,
- .dataSize = (uint32_t)data.size(),
- .data = data.data(),
- };
- ASSERT_EQ(1, writeBlocks({&block, 1}));
-
- // Set per-uid read timeout then read
- uint32_t readTimeoutUs = 1000000;
- IncFsUidReadTimeouts timeouts[1] = {
- {static_cast<IncFsUid>(getuid()), readTimeoutUs, readTimeoutUs, readTimeoutUs}};
- ASSERT_EQ(0, IncFs_SetUidReadTimeouts(control_, timeouts, std::size(timeouts)));
- const auto file_path = mountPath(test_file_name_);
- const android::base::unique_fd fd(open(file_path.c_str(), O_RDONLY | O_CLOEXEC | O_BINARY));
- char buf[testFileSize];
- ASSERT_GE(fd.get(), 0);
- ASSERT_TRUE(android::base::ReadFully(fd, buf, sizeof(buf)));
-
- IncFsLastReadError lastReadError = {.id = fileId(-1), 1, 1, 1, 1};
- EXPECT_EQ(0, IncFs_GetLastReadError(control_, &lastReadError));
- EXPECT_EQ(FileId{}, lastReadError.id);
- EXPECT_EQ(0, (int)lastReadError.timestampUs);
- EXPECT_EQ(0, (int)lastReadError.block);
- EXPECT_EQ(0, (int)lastReadError.errorNo);
- EXPECT_EQ(0, (int)lastReadError.uid);
-
- IncFsMetrics incfsMetrics = {10, 10, 10, 10, 10, 10, 10, 10, 10};
- EXPECT_EQ(0, IncFs_GetMetrics(metrics_key_.c_str(), &incfsMetrics));
- EXPECT_EQ(1, (int)incfsMetrics.readsDelayedMin);
- EXPECT_EQ(readTimeoutUs, (uint32_t)incfsMetrics.readsDelayedMinUs);
- EXPECT_EQ(0, (int)incfsMetrics.readsDelayedPending);
- EXPECT_EQ(0, (int)incfsMetrics.readsDelayedPendingUs);
- EXPECT_EQ(0, (int)incfsMetrics.readsFailedHashVerification);
- EXPECT_EQ(0, (int)incfsMetrics.readsFailedOther);
- EXPECT_EQ(0, (int)incfsMetrics.readsFailedTimedOut);
-}
-
-inline bool operator==(const BlockCounts& lhs, const BlockCounts& rhs) {
- return lhs.totalDataBlocks == rhs.totalDataBlocks &&
- lhs.filledDataBlocks == rhs.filledDataBlocks &&
- lhs.totalHashBlocks == rhs.totalHashBlocks &&
- lhs.filledHashBlocks == rhs.filledHashBlocks;
-}
-
-TEST_F(IncFsTest, LoadingProgress) {
- ASSERT_EQ(0, makeDir(control_, mountPath(test_dir_name_)));
-
- constexpr auto file_size = INCFS_DATA_FILE_BLOCK_SIZE * 2;
- constexpr auto mapped_file_offset = file_size / 2;
- constexpr auto mapped_file_size = file_size / 3;
-
- const auto file_id = fileId(1);
-
- const auto file_path = mountPath(test_dir_name_, test_file_name_);
- ASSERT_FALSE(exists(file_path));
- ASSERT_EQ(0,
- makeFile(control_, file_path, 0111, file_id,
- {.size = file_size, .metadata = metadata("md")}));
- struct stat s = {};
- ASSERT_EQ(0, stat(file_path.c_str(), &s));
- ASSERT_EQ(file_size, (int)s.st_size);
-
- const auto mapped_file_path = mountPath(test_dir_name_, test_mapped_file_name_);
- ASSERT_FALSE(exists(mapped_file_path));
- ASSERT_EQ(0,
- makeMappedFile(control_, mapped_file_path, 0111,
- {.sourceId = file_id,
- .sourceOffset = mapped_file_offset,
- .size = mapped_file_size}));
- s = {};
- ASSERT_EQ(0, stat(mapped_file_path.c_str(), &s));
- ASSERT_EQ(mapped_file_size, (int)s.st_size);
-
- // Check fully loaded first.
- ASSERT_EQ(LoadingState::MissingBlocks, isFullyLoaded(control_, file_path));
- ASSERT_EQ(LoadingState::MissingBlocks, isFullyLoaded(control_, file_id));
- ASSERT_EQ((LoadingState)-ENOTSUP, isFullyLoaded(control_, mapped_file_path));
-
- // Next is loading progress.
- ASSERT_EQ(BlockCounts{.totalDataBlocks = 2}, *getBlockCount(control_, file_path));
- ASSERT_EQ(BlockCounts{.totalDataBlocks = 2}, *getBlockCount(control_, file_id));
- ASSERT_FALSE(getBlockCount(control_, mapped_file_path));
-
- // Now write a page #0.
- ASSERT_NO_FATAL_FAILURE(writeBlock(0));
-
- // Recheck everything.
- ASSERT_EQ(LoadingState::MissingBlocks, isFullyLoaded(control_, file_path));
- ASSERT_EQ(LoadingState::MissingBlocks, isFullyLoaded(control_, file_id));
- ASSERT_EQ((LoadingState)-ENOTSUP, isFullyLoaded(control_, mapped_file_path));
-
- BlockCounts onePage{.totalDataBlocks = 2, .filledDataBlocks = 1};
- ASSERT_EQ(onePage, *getBlockCount(control_, file_path));
- ASSERT_EQ(onePage, *getBlockCount(control_, file_id));
- ASSERT_FALSE(getBlockCount(control_, mapped_file_path));
-
- // Now write a page #1.
- ASSERT_NO_FATAL_FAILURE(writeBlock(1));
-
- // Check for fully loaded.
- ASSERT_EQ(LoadingState::Full, isFullyLoaded(control_, file_path));
- ASSERT_EQ(LoadingState::Full, isFullyLoaded(control_, file_id));
- ASSERT_EQ((LoadingState)-ENOTSUP, isFullyLoaded(control_, mapped_file_path));
-
- BlockCounts twoPages{.totalDataBlocks = 2, .filledDataBlocks = 2};
- ASSERT_EQ(twoPages, *getBlockCount(control_, file_path));
- ASSERT_EQ(twoPages, *getBlockCount(control_, file_id));
- ASSERT_FALSE(getBlockCount(control_, mapped_file_path));
}
diff --git a/incfs/tests/include/IncFsTestBase.h b/incfs/tests/include/IncFsTestBase.h
deleted file mode 100644
index 8c61d64..0000000
--- a/incfs/tests/include/IncFsTestBase.h
+++ /dev/null
@@ -1,126 +0,0 @@
-/*
- * Copyright (C) 2020 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.
- */
-
-#pragma once
-
-#include <android-base/logging.h>
-#include <android-base/unique_fd.h>
-#include <gtest/gtest.h>
-#include <selinux/selinux.h>
-
-#include "incfs.h"
-#include "path.h"
-
-namespace android::incfs {
-
-static bool exists(std::string_view path) {
- return access(path.data(), F_OK) == 0;
-}
-
-class IncFsTestBase : public ::testing::Test {
-protected:
- virtual void SetUp() {
- tmp_dir_for_mount_.emplace();
- mount_dir_path_ = tmp_dir_for_mount_->path;
- tmp_dir_for_image_.emplace();
- image_dir_path_ = tmp_dir_for_image_->path;
- ASSERT_TRUE(exists(image_dir_path_));
- ASSERT_TRUE(exists(mount_dir_path_));
- if (!enabled()) {
- GTEST_SKIP() << "test not supported: IncFS is not enabled";
- } else {
- metrics_key_ = path::baseName(image_dir_path_);
- control_ = mount(image_dir_path_, mount_dir_path_,
- MountOptions{.readLogBufferPages = 4,
- .defaultReadTimeoutMs = getReadTimeout(),
- .sysfsName = metrics_key_.c_str()});
- ASSERT_TRUE(control_.cmd() >= 0) << "Expected >= 0 got " << control_.cmd();
- ASSERT_TRUE(control_.pendingReads() >= 0);
- ASSERT_TRUE(control_.logs() >= 0);
- checkRestoreconResult(mountPath(INCFS_PENDING_READS_FILENAME));
- checkRestoreconResult(mountPath(INCFS_LOG_FILENAME));
- }
- }
-
- virtual void TearDown() {
- unmount(mount_dir_path_);
- tmp_dir_for_image_.reset();
- tmp_dir_for_mount_.reset();
- EXPECT_FALSE(exists(image_dir_path_));
- EXPECT_FALSE(exists(mount_dir_path_));
- }
-
- static void checkRestoreconResult(std::string_view path) {
- char* ctx = nullptr;
- ASSERT_NE(-1, getfilecon(path.data(), &ctx));
- ASSERT_EQ("u:object_r:shell_data_file:s0", std::string(ctx));
- freecon(ctx);
- }
-
- static IncFsFileId fileId(uint64_t i) {
- IncFsFileId id = {};
- static_assert(sizeof(id) >= sizeof(i));
- memcpy(&id, &i, sizeof(i));
- return id;
- }
-
- virtual int32_t getReadTimeout() {
- return std::chrono::duration_cast<std::chrono::milliseconds>(kDefaultReadTimeout).count();
- }
-
- template <class... Paths>
- std::string mountPath(Paths&&... paths) const {
- return path::join(mount_dir_path_, std::forward<Paths>(paths)...);
- }
-
- virtual int makeFileWithHash(int id) {
- // calculate the required size for two leaf hash blocks
- constexpr auto size =
- (INCFS_DATA_FILE_BLOCK_SIZE / INCFS_MAX_HASH_SIZE + 1) * INCFS_DATA_FILE_BLOCK_SIZE;
-
- // assemble a signature/hashing data for it
- struct __attribute__((packed)) Signature {
- uint32_t version = INCFS_SIGNATURE_VERSION;
- uint32_t hashingSize = sizeof(hashing);
- struct __attribute__((packed)) Hashing {
- uint32_t algo = INCFS_HASH_TREE_SHA256;
- uint8_t log2Blocksize = 12;
- uint32_t saltSize = 0;
- uint32_t rootHashSize = INCFS_MAX_HASH_SIZE;
- char rootHash[INCFS_MAX_HASH_SIZE] = {};
- } hashing;
- uint32_t signingSize = 0;
- } signature;
-
- int res = makeFile(control_, mountPath(test_file_name_), 0555, fileId(id),
- {.size = size,
- .signature = {.data = (char*)&signature, .size = sizeof(signature)}});
- EXPECT_EQ(0, res);
- return res ? -1 : size;
- }
-
- std::string mount_dir_path_;
- std::optional<TemporaryDir> tmp_dir_for_mount_;
- std::string image_dir_path_;
- std::optional<TemporaryDir> tmp_dir_for_image_;
- inline static const std::string_view test_file_name_ = "test.txt";
- inline static const std::string_view test_mapped_file_name_ = "mapped.txt";
- inline static const std::string_view test_dir_name_ = "test_dir";
- std::string metrics_key_;
- Control control_;
-};
-
-} // namespace android::incfs
diff --git a/incfs/tests/util/map_ptr_test.cpp b/incfs/tests/util/map_ptr_test.cpp
deleted file mode 100644
index ff9e466..0000000
--- a/incfs/tests/util/map_ptr_test.cpp
+++ /dev/null
@@ -1,328 +0,0 @@
-/*
- * Copyright (C) 2020 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.
- */
-
-#include <android-base/file.h>
-#include <sys/select.h>
-
-#include <setjmp.h>
-#include <unistd.h>
-
-#include "IncFsTestBase.h"
-
-#include "util/map_ptr.h"
-
-using namespace android::incfs;
-using namespace std::literals;
-
-constexpr int FILE_PAGES = 5U;
-constexpr int FILE_SIZE = INCFS_DATA_FILE_BLOCK_SIZE * FILE_PAGES;
-constexpr int FILE_MISSING_PAGE = 3U;
-
-class MapPtrTest : public IncFsTestBase {
-protected:
- virtual void SetUp() override {
- IncFsTestBase::SetUp();
-
- const auto id = fileId(1);
- ASSERT_TRUE(control_.logs() >= 0);
- ASSERT_EQ(0, makeFile(control_, mountPath(test_file_name_), 0555, id, {.size = FILE_SIZE}));
- auto fd = openForSpecialOps(control_, fileId(1));
- ASSERT_GE(fd.get(), 0);
-
- // Generate the file data.
- std::vector<uint32_t> data(INCFS_DATA_FILE_BLOCK_SIZE);
- for (int i = 0; i < FILE_SIZE; i++) {
- data[i] = i;
- }
-
- // Write the file, but leave one page missing.
- for (int p = 0; p < FILE_PAGES; p++) {
- if (p == FILE_MISSING_PAGE) {
- continue;
- }
- auto block = DataBlock{
- .fileFd = fd.get(),
- .pageIndex = p,
- .compression = INCFS_COMPRESSION_KIND_NONE,
- .dataSize = (uint32_t)INCFS_DATA_FILE_BLOCK_SIZE,
- .data = reinterpret_cast<const char *>(data.data()) +
- INCFS_DATA_FILE_BLOCK_SIZE * p,
- };
- ASSERT_EQ(1, writeBlocks({&block, 1}));
- }
-
- mount_path_ = mountPath(test_file_name_);
- }
-
- android::base::unique_fd GetFd() {
- return android::base::unique_fd(open(mount_path_.c_str(), O_RDONLY | O_CLOEXEC | O_BINARY));
- }
-
- int32_t getReadTimeout() override { return 1; }
-
- std::unique_ptr<IncFsFileMap> GetFileMap(int fd, off64_t offset, size_t length) {
- auto map = std::make_unique<IncFsFileMap>();
- return map->Create(fd, offset, length, nullptr) ? std::move(map) : nullptr;
- }
-
-private:
- std::string mount_path_;
-};
-
-struct TwoValues {
- uint32_t first;
- uint32_t second;
-};
-
-TEST_F(MapPtrTest, ReadAtStart) {
- auto fd = GetFd();
- auto map = GetFileMap(fd.get(), 0U /* offset */, FILE_SIZE);
- ASSERT_NE(nullptr, map);
-
- auto p1 = map->data<uint32_t>();
- ASSERT_TRUE(p1);
- ASSERT_EQ(0U, p1.value());
-
- auto p2 = map->data<TwoValues>();
- ASSERT_TRUE(p2);
- ASSERT_EQ(0U, p2->first);
- ASSERT_EQ(1U, p2->second);
-}
-
-TEST_F(MapPtrTest, ReadNull) {
- auto fd = GetFd();
- auto map = GetFileMap(fd.get(), 0U /* offset */, FILE_SIZE);
- ASSERT_NE(nullptr, map);
-
- auto p1 = map->data<uint32_t>();
- ASSERT_TRUE(p1);
- ASSERT_EQ(0U, p1.value());
-
- p1 = nullptr;
- ASSERT_FALSE(p1);
-
- p1 = map->data<uint32_t>();
- ASSERT_TRUE(p1);
- ASSERT_EQ(0U, p1.value());
-}
-
-TEST_F(MapPtrTest, ReadAtStartWithOffset) {
- auto fd = GetFd();
- auto map = GetFileMap(fd.get(), sizeof(uint32_t) * 4U /* offset */, FILE_SIZE);
- ASSERT_NE(nullptr, map);
-
- auto p1 = map->data<uint32_t>();
- ASSERT_TRUE(p1);
- ASSERT_EQ(4U, p1.value());
-
- auto p2 = map->data<TwoValues>();
- ASSERT_TRUE(p2);
- ASSERT_EQ(4U, p2->first);
- ASSERT_EQ(5U, p2->second);
-}
-
-TEST_F(MapPtrTest, PointerArithmetic) {
- auto fd = GetFd();
- auto map = GetFileMap(fd.get(), 0U /* offset */, FILE_SIZE);
- ASSERT_NE(nullptr, map);
-
- auto p1 = map->data<uint32_t>() + 11U;
- ASSERT_TRUE(p1);
- ASSERT_EQ(11U, p1.value());
-
- auto p2 = p1 - 5U;
- ASSERT_TRUE(p2);
- ASSERT_EQ(6U, p2.value());
-
- auto dis = p1 - p2;
- ASSERT_EQ((ptrdiff_t)5U, dis);
-}
-
-TEST_F(MapPtrTest, PointerIncrement) {
- auto fd = GetFd();
- auto map = GetFileMap(fd.get(), 0U /* offset */, FILE_SIZE);
- ASSERT_NE(nullptr, map);
-
- auto p1 = map->data<uint32_t>();
- ASSERT_TRUE(p1);
- ASSERT_EQ(0U, p1.value());
-
- auto p2 = p1++;
- ASSERT_TRUE(p1);
- ASSERT_TRUE(p2);
- ASSERT_EQ(1U, p1.value());
- ASSERT_EQ(0U, p2.value());
-
- auto p3 = ++p2;
- ASSERT_TRUE(p2);
- ASSERT_TRUE(p3);
- ASSERT_EQ(1U, p2.value());
- ASSERT_EQ(1U, p3.value());
-}
-
-TEST_F(MapPtrTest, PointerComparison) {
- auto fd = GetFd();
- auto map = GetFileMap(fd.get(), 0U /* offset */, FILE_SIZE);
- ASSERT_NE(nullptr, map);
-
- auto p1 = map->data<uint32_t>();
- ASSERT_TRUE(p1);
- ASSERT_EQ(0U, p1.value());
-
- auto p2 = p1;
- ASSERT_TRUE(p1 == p2);
- ASSERT_TRUE(p1 < p2 + 1U);
- ASSERT_TRUE(p2 != p2 + 1U);
-}
-
-TEST_F(MapPtrTest, PointerConvert) {
- auto fd = GetFd();
- auto map = GetFileMap(fd.get(), 0U /* offset */, FILE_SIZE);
- ASSERT_NE(nullptr, map);
-
- auto p1 = (map->data<uint32_t>() + 11U).convert<TwoValues>();
- ASSERT_TRUE(p1);
- ASSERT_EQ(11U, p1->first);
- ASSERT_EQ(12U, p1->second);
-}
-
-TEST_F(MapPtrTest, PointerOffset) {
- auto fd = GetFd();
- auto map = GetFileMap(fd.get(), 0U /* offset */, FILE_SIZE);
- ASSERT_NE(nullptr, map);
-
- auto p1 = map->data().offset(11U * sizeof(uint32_t)).convert<TwoValues>();
- ASSERT_TRUE(p1);
- ASSERT_EQ(11U, p1->first);
- ASSERT_EQ(12U, p1->second);
-}
-
-TEST_F(MapPtrTest, Iterator) {
- auto fd = GetFd();
- auto map = GetFileMap(fd.get(), 0U /* offset */, FILE_SIZE);
- ASSERT_NE(nullptr, map);
-
- auto it = map->data<uint32_t>().iterator();
- ASSERT_TRUE(*it);
- ASSERT_EQ(0U, (*it).value());
-
- auto it2 = it;
- ASSERT_EQ(it, it2);
-
- auto it3 = it++;
- ASSERT_TRUE(*it3);
- ASSERT_EQ(0U, (*it3).value());
-
- ASSERT_NE(it, it2);
- ASSERT_EQ(1, it - it2);
- ASSERT_EQ(-1, it2 - it);
-
- auto it4 = ++it;
- ASSERT_TRUE(*it4);
- ASSERT_EQ(2U, (*it4).value());
-
- it += 10;
- ASSERT_EQ(12U, (*it).value());
-}
-
-static jmp_buf buf;
-
-void sigbus_handler(int sig) {
- if (sig == SIGBUS) {
- siglongjmp(buf, 1);
- } else {
- FAIL();
- }
-}
-
-#define ASSERT_SIGBUS(test) \
- do { \
- signal(SIGBUS, &sigbus_handler); \
- if (sigsetjmp(buf, 1) == 0) { \
- ASSERT_EQ(0U, (test)); \
- FAIL() << "No signal raised"; \
- } \
- } while (0)
-
-TEST_F(MapPtrTest, VerifyMissingPageFails) {
- for (uint32_t off :
- std::vector<uint32_t>{0U, INCFS_DATA_FILE_BLOCK_SIZE / 2 - 1,
- INCFS_DATA_FILE_BLOCK_SIZE / 2 + 1, INCFS_DATA_FILE_BLOCK_SIZE,
- INCFS_DATA_FILE_BLOCK_SIZE * 3 / 2 - 1,
- INCFS_DATA_FILE_BLOCK_SIZE * 3 / 2 + 1}) {
- auto fd = GetFd();
- auto map = GetFileMap(fd.get(), off /* offset */, FILE_SIZE);
- ASSERT_NE(nullptr, map);
-
- auto missing_page_start = INCFS_DATA_FILE_BLOCK_SIZE * FILE_MISSING_PAGE;
- auto p1 = map->data().offset(missing_page_start - off).convert<uint32_t>();
- ASSERT_FALSE(p1);
- ASSERT_SIGBUS(p1.value());
-
- const auto p2 = p1;
- ASSERT_FALSE(p2);
- ASSERT_SIGBUS(p2.value());
-
- const auto p3 = p2 - 1U;
- ASSERT_TRUE(p3);
- ASSERT_EQ(3071U, p3.value());
-
- auto p4 = p3;
- ASSERT_TRUE(p4);
- ASSERT_EQ(3071U, p4.value());
-
- ASSERT_FALSE(p4 + 1U);
- ASSERT_SIGBUS((p4 + 1U).value());
-
- auto p5 = p4++;
- ASSERT_TRUE(p5);
- ASSERT_EQ(3071U, p5.value());
- ASSERT_FALSE(p4);
- ASSERT_SIGBUS(p4.value());
-
- auto p6 = p3;
- ASSERT_TRUE(p6);
- ASSERT_EQ(3071U, p6.value());
-
- auto p7 = ++p6;
- ASSERT_FALSE(p7);
- ASSERT_SIGBUS(p7.value());
- ASSERT_FALSE(p6);
- ASSERT_SIGBUS(p6.value());
-
- auto missing_page_end = INCFS_DATA_FILE_BLOCK_SIZE * (FILE_MISSING_PAGE + 1);
- auto p8 = map->data().offset(missing_page_end - off).convert<uint32_t>();
- ASSERT_TRUE(p8);
- ASSERT_EQ(4096U, p8.value());
-
- ASSERT_FALSE(p8 - 1U);
- ASSERT_SIGBUS((p8 - 1U).value());
- }
-}
-
-TEST_F(MapPtrTest, GetDataAfterClose) {
- std::unique_ptr<IncFsFileMap> map;
- {
- auto fd = GetFd();
- map = GetFileMap(fd.get(), 0U /* offset */, FILE_SIZE);
- ASSERT_NE(nullptr, map);
- }
-
- auto missing_page_start = INCFS_DATA_FILE_BLOCK_SIZE * FILE_MISSING_PAGE;
- auto p1 = map->data().offset(missing_page_start).convert<uint32_t>();
- ASSERT_FALSE(p1);
- ASSERT_SIGBUS(p1.value());
-}
\ No newline at end of file
diff --git a/incfs/util/include/util/map_ptr.h b/incfs/util/include/util/map_ptr.h
deleted file mode 100644
index 304540f..0000000
--- a/incfs/util/include/util/map_ptr.h
+++ /dev/null
@@ -1,412 +0,0 @@
-/*
- * Copyright (C) 2020 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.
- */
-
-#pragma once
-
-#include <android-base/logging.h>
-#include <android-base/off64_t.h>
-
-#include <atomic>
-#include <iterator>
-#include <memory>
-#include <shared_mutex>
-#include <type_traits>
-#include <vector>
-
-#ifdef __ANDROID__
-#include <linux/incrementalfs.h>
-#endif
-
-namespace android {
-
-class FileMap;
-
-namespace incfs {
-
-// Controls whether not verifying the presence of data before de-referencing the pointer aborts
-// program execution.
-#define LIBINCFS_MAP_PTR_DEBUG false
-#if LIBINCFS_MAP_PTR_DEBUG
-#define LIBINCFS_MAP_PTR_DEBUG_CODE(x) x
-#else
-#define LIBINCFS_MAP_PTR_DEBUG_CODE(x)
-#endif
-
-template <typename T, bool Verified = false>
-struct map_ptr;
-
-// This class represents a memory-mapped, read-only file that may exist on an IncFs file system.
-//
-// Files stored on IncFs may not be fully present. This class is able to return a smart pointer
-// (map_ptr<T>) that is able to verify whether the contents of the pointer are fully present on
-// IncFs.
-//
-// This always uses MAP_SHARED.
-class IncFsFileMap final {
-public:
- IncFsFileMap() noexcept;
- IncFsFileMap(IncFsFileMap&&) noexcept;
- IncFsFileMap& operator =(IncFsFileMap&&) noexcept;
- ~IncFsFileMap() noexcept;
-
- // Initializes the map. Does not take ownership of the file descriptor.
- // Returns whether or not the file was able to be memory-mapped.
- bool Create(int fd, off64_t offset, size_t length, const char* file_name);
-
- // Same thing, but allows verification to be disabled when `verify` is `false`, and enabled when
- // `verify` is true and the file resides on IncFs.
- bool Create(int fd, off64_t offset, size_t length, const char* file_name, bool verify);
-
- // Same thing, but allows verification to be disabled when `verify` is `false`, and enabled when
- // `verify` is true regardless of whether the file resides on IncFs (used for benchmarks and
- // testing).
- bool CreateForceVerification(int fd, off64_t offset, size_t length, const char* file_name,
- bool verify);
-
- template <typename T = void>
- map_ptr<T> data() const {
- return map_ptr<T>(verification_enabled_ ? this : nullptr,
- reinterpret_cast<const T*>(unsafe_data()));
- }
-
- const void* unsafe_data() const;
- size_t length() const;
- off64_t offset() const;
- const char* file_name() const;
-
-public:
- // Returns whether the data range is entirely present on IncFs.
- bool Verify(const uint8_t* const& data_start, const uint8_t* const& data_end,
- const uint8_t** prev_verified_block) const;
-
-private:
- DISALLOW_COPY_AND_ASSIGN(IncFsFileMap);
-
- using bucket_t = uint8_t;
- static constexpr size_t kBucketBits = sizeof(bucket_t) * 8U;
-
- // File descriptor of the memory-mapped file (not owned).
- int fd_ = -1;
- bool verification_enabled_ = false;
- size_t start_block_offset_ = 0;
- const uint8_t* start_block_ptr_ = nullptr;
-
- std::unique_ptr<android::FileMap> map_;
-
- // Bitwise cache for storing whether a block has already been verified. This cache relies on
- // IncFs not deleting blocks of a file that is currently memory mapped.
- mutable std::vector<std::atomic<bucket_t>> loaded_blocks_;
-
- template <typename, bool>
- friend struct map_ptr;
-};
-
-// Variant of map_ptr that statically guarantees that the pointed to data is fully present and
-// reading data will not result in IncFs raising a SIGBUS.
-template <typename T>
-using verified_map_ptr = map_ptr<T, true>;
-
-// Smart pointer that is able to verify whether the contents of the pointer are fully present on
-// the file system before using the pointer. Files residing on IncFs may not be fully present.
-//
-// Before attempting to use the data represented by the smart pointer, the caller should always use
-// the bool operator to verify the presence of the data. The bool operator is not thread-safe. If
-// this pointer must be used in multiple threads concurrently, use verified_map_ptr instead.
-//
-// map_ptr created from raw pointers have less overhead than when created from IncFsFileMap.
-template <typename T, bool Verified>
-struct map_ptr final {
-private:
- friend class IncFsFileMap;
-
- // To access internals of map_ptr with a different type
- template <typename, bool>
- friend struct map_ptr;
-
- template <typename T1>
- using IsVoid = typename std::enable_if_t<std::is_void<T1>::value, int>;
-
- template <typename T1>
- using NotVoid = typename std::enable_if_t<!std::is_void<T1>::value, int>;
-
- template <bool V>
- using IsVerified = typename std::enable_if_t<V, int>;
-
- template <bool V>
- using IsUnverified = typename std::enable_if_t<!V, int>;
-
-public:
- class const_iterator final {
- public:
- friend struct map_ptr<T, Verified>;
- using iterator_category = std::random_access_iterator_tag;
- using value_type = const map_ptr<T>;
- using difference_type = std::ptrdiff_t;
- using pointer = void;
- using reference = value_type;
-
- const_iterator() = default;
- const_iterator(const const_iterator& it) = default;
-
- bool operator==(const const_iterator& other) const { return safe_ptr_ == other.safe_ptr_; }
- bool operator!=(const const_iterator& other) const { return safe_ptr_ != other.safe_ptr_; }
- std::ptrdiff_t operator-(const const_iterator& other) const {
- return safe_ptr_ - other.safe_ptr_;
- }
-
- const_iterator operator+(int n) const {
- const_iterator other = *this;
- other += n;
- return other;
- }
-
- reference operator*() const { return safe_ptr_; }
-
- const const_iterator& operator++() {
- safe_ptr_++;
- return *this;
- }
-
- const_iterator& operator+=(int n) {
- safe_ptr_ = safe_ptr_ + n;
- return *this;
- }
-
- const const_iterator operator++(int) {
- const_iterator temp(*this);
- safe_ptr_++;
- return temp;
- }
-
- private:
- explicit const_iterator(const map_ptr<T>& ptr) : safe_ptr_(ptr) {}
- map_ptr<T> safe_ptr_;
- };
-
- // Default constructor
- map_ptr() = default;
-
- // Implicit conversion from raw pointer
- map_ptr(const T* ptr) : map_ptr(nullptr, ptr, nullptr) {}
-
- // Copy constructor
- map_ptr(const map_ptr& other) = default;
-
- // Implicit copy conversion from verified to unverified map_ptr<T>
- template <bool V2, bool V1 = Verified, IsUnverified<V1> = 0, IsVerified<V2> = 0>
- map_ptr(const map_ptr<T, V2>& other) : map_ptr(other.map_, other.ptr_, other.verified_block_) {}
-
- // Move constructor
- map_ptr(map_ptr&& other) noexcept = default;
-
- // Implicit move conversion from verified to unverified map_ptr<T>
- template <bool V2, bool V1 = Verified, IsUnverified<V1> = 0, IsVerified<V2> = 0>
- map_ptr(map_ptr&& other) : map_ptr(other.map_, other.ptr_, other.verified_block_) {}
-
- // Implicit conversion to unverified map_ptr<void>
- template <typename U, bool V2, typename T1 = T, bool V1 = Verified, IsVoid<T1> = 0,
- NotVoid<U> = 0, IsUnverified<V1> = 0>
- map_ptr(const map_ptr<U, V2>& other)
- : map_ptr(other.map_, reinterpret_cast<const void*>(other.ptr_), other.verified_block_) {}
-
- // Implicit conversion from regular raw pointer
- map_ptr& operator=(const T* ptr) {
- ptr_ = ptr;
- map_ = nullptr;
- verified_block_ = nullptr;
- LIBINCFS_MAP_PTR_DEBUG_CODE(verified_ = Verified);
- return *this;
- }
-
- // Copy assignment operator
- map_ptr& operator=(const map_ptr& other) = default;
-
- // Copy assignment operator
- template <bool V2, bool V1 = Verified, IsUnverified<V1> = 0, IsVerified<V2> = 0>
- map_ptr& operator=(const map_ptr<T, V2>& other) {
- ptr_ = other.ptr_;
- map_ = other.map_;
- verified_block_ = other.verified_block_;
- LIBINCFS_MAP_PTR_DEBUG_CODE(verified_ = other.verified_);
- return *this;
- }
-
- template <bool V2>
- bool operator==(const map_ptr<T, V2>& other) const {
- return ptr_ == other.ptr_;
- }
-
- template <bool V2>
- bool operator!=(const map_ptr<T, V2>& other) const {
- return ptr_ != other.ptr_;
- }
-
- template <bool V2>
- bool operator<(const map_ptr<T, V2>& other) const {
- return ptr_ < other.ptr_;
- }
-
- template <bool V2>
- std::ptrdiff_t operator-(const map_ptr<T, V2>& other) const {
- return ptr_ - other.ptr_;
- }
-
- template <typename U>
- map_ptr<U> convert() const {
- return map_ptr<U>(map_, reinterpret_cast<const U*>(ptr_), verified_block_);
- }
-
- // Retrieves a map_ptr<T> offset from an original map_ptr<U> by the specified number of `offset`
- // bytes.
- map_ptr<T> offset(std::ptrdiff_t offset) const {
- return map_ptr<T>(map_,
- reinterpret_cast<const T*>(reinterpret_cast<const uint8_t*>(ptr_) +
- offset),
- verified_block_);
- }
-
- // Returns a raw pointer to the value of this pointer.
- const T* unsafe_ptr() const { return ptr_; }
-
- // Start T == void methods
-
- template <typename T1 = T, IsVoid<T1> = 0>
- operator bool() const {
- return ptr_ != nullptr;
- }
-
- // End T == void methods
- // Start T != void methods
-
- template <typename T1 = T, NotVoid<T1> = 0, bool V1 = Verified, IsUnverified<V1> = 0>
- operator bool() const {
- return verify();
- }
-
- template <typename T1 = T, NotVoid<T1> = 0, bool V1 = Verified, IsVerified<V1> = 0>
- operator bool() const {
- return ptr_ != nullptr;
- }
-
- template <typename T1 = T, NotVoid<T1> = 0>
- const_iterator iterator() const {
- return const_iterator(*this);
- }
-
- template <typename T1 = T, NotVoid<T1> = 0>
- const map_ptr<T1>& operator++() {
- LIBINCFS_MAP_PTR_DEBUG_CODE(verified_ = false);
- ++ptr_;
- return *this;
- }
-
- template <typename T1 = T, NotVoid<T1> = 0>
- const map_ptr<T1> operator++(int) {
- map_ptr<T1> temp = *this;
- LIBINCFS_MAP_PTR_DEBUG_CODE(verified_ = false);
- ++ptr_;
- return temp;
- }
-
- template <typename S, typename T1 = T, NotVoid<T1> = 0>
- map_ptr<T1> operator+(const S n) const {
- return map_ptr<T1>(map_, ptr_ + n, verified_block_);
- }
-
- template <typename S, typename T1 = T, NotVoid<T1> = 0>
- map_ptr<T1> operator-(const S n) const {
- return map_ptr<T1>(map_, ptr_ - n, verified_block_);
- }
-
- // Returns the value of the pointer.
- // The caller should verify the presence of the pointer data before calling this method.
- template <typename T1 = T, NotVoid<T1> = 0>
- const T1& value() const {
- LIBINCFS_MAP_PTR_DEBUG_CODE(
- CHECK(verified_) << "Did not verify presence before de-referencing safe pointer");
- return *ptr_;
- }
-
- // Returns a raw pointer to the value this pointer.
- // The caller should verify the presence of the pointer data before calling this method.
- template <typename T1 = T, NotVoid<T1> = 0>
- const T1* operator->() const {
- LIBINCFS_MAP_PTR_DEBUG_CODE(
- CHECK(verified_) << "Did not verify presence before de-referencing safe pointer");
- return ptr_;
- }
-
- // Verifies the presence of `n` elements of `T`.
- //
- // Returns true if the elements are completely present; otherwise, returns false.
- template <typename T1 = T, NotVoid<T1> = 0, bool V1 = Verified, IsUnverified<V1> = 0>
- bool verify(size_t n = 1) const {
- if (ptr_ == nullptr) {
- return false;
- }
-
-#ifdef __ANDROID__
- if (LIKELY(map_ == nullptr)) {
- return ptr_ != nullptr;
- }
-
- const size_t verify_size = sizeof(T) * n;
- LIBINCFS_MAP_PTR_DEBUG_CODE(if (sizeof(T) <= verify_size) verified_ = true;);
-
- const auto data_start = reinterpret_cast<const uint8_t*>(ptr_);
- const auto data_end = reinterpret_cast<const uint8_t*>(ptr_) + verify_size;
-
- // If the data is entirely within the block beginning at the previous verified block
- // pointer, then the data can safely be used.
- if (LIKELY(data_start >= verified_block_ &&
- data_end <= verified_block_ + INCFS_DATA_FILE_BLOCK_SIZE)) {
- return true;
- }
-
- if (LIKELY(map_->Verify(data_start, data_end, &verified_block_))) {
- return true;
- }
-
- LIBINCFS_MAP_PTR_DEBUG_CODE(verified_ = false);
- return false;
-#else
- (void)n;
- return true;
-#endif
- }
-
- // Returns a verified version of this pointer.
- // The caller should verify the presence of the pointer data before calling this method.
- template <typename T1 = T, NotVoid<T1> = 0>
- verified_map_ptr<T1> verified() const {
- return verified_map_ptr<T1>(map_, ptr_, verified_block_);
- }
-
-private:
- map_ptr(const IncFsFileMap* map, const T* ptr)
- : ptr_(ptr), map_(map), verified_block_(nullptr) {}
- map_ptr(const IncFsFileMap* map, const T* ptr, const uint8_t* verified_block)
- : ptr_(ptr), map_(map), verified_block_(verified_block) {}
-
- const T* ptr_ = nullptr;
- mutable const IncFsFileMap* map_ = nullptr;
- mutable const uint8_t* verified_block_;
- LIBINCFS_MAP_PTR_DEBUG_CODE(mutable bool verified_ = Verified);
-};
-
-} // namespace incfs
-
-} // namespace android
\ No newline at end of file
diff --git a/incfs/util/map_ptr.cpp b/incfs/util/map_ptr.cpp
deleted file mode 100644
index f391506..0000000
--- a/incfs/util/map_ptr.cpp
+++ /dev/null
@@ -1,157 +0,0 @@
-/*
- * Copyright (C) 2020 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.
- */
-
-#include <android-base/file.h>
-#include <android-base/stringprintf.h>
-#include <utils/FileMap.h>
-
-#ifdef __ANDROID__
-#include "incfs_inline.h"
-#endif
-
-#include "util/map_ptr.h"
-
-namespace android::incfs {
-IncFsFileMap::IncFsFileMap() noexcept = default;
-IncFsFileMap::IncFsFileMap(IncFsFileMap&&) noexcept = default;
-IncFsFileMap& IncFsFileMap::operator =(IncFsFileMap&&) noexcept = default;
-IncFsFileMap::~IncFsFileMap() noexcept = default;
-
-const void* IncFsFileMap::unsafe_data() const {
- return map_->getDataPtr();
-}
-
-size_t IncFsFileMap::length() const {
- return map_->getDataLength();
-}
-
-off64_t IncFsFileMap::offset() const {
- return map_->getDataOffset();
-}
-
-const char* IncFsFileMap::file_name() const {
- return map_->getFileName();
-}
-
-bool IncFsFileMap::Create(int fd, off64_t offset, size_t length, const char* file_name) {
- return Create(fd, offset, length, file_name, true /* verify */);
-}
-
-#ifdef __ANDROID__
-static bool IsVerificationEnabled(int fd) {
- return isIncFsFd(fd) && isFullyLoaded(fd) != LoadingState::Full;
-}
-
-using data_block_index_t = uint32_t;
-
-static data_block_index_t get_block_index(const uint8_t* ptr, const uint8_t* start_block_ptr) {
- return (ptr - start_block_ptr) / INCFS_DATA_FILE_BLOCK_SIZE;
-}
-
-bool IncFsFileMap::Create(int fd, off64_t offset, size_t length, const char* file_name,
- bool verify) {
- return CreateForceVerification(fd, offset, length, file_name,
- verify && IsVerificationEnabled(fd));
-}
-
-bool IncFsFileMap::CreateForceVerification(int fd, off64_t offset, size_t length,
- const char* file_name, bool verify) {
- map_ = std::make_unique<android::FileMap>();
- if (!map_->create(file_name, fd, offset, length, true /* readOnly */)) {
- return false;
- }
-
- fd_ = fd;
- verification_enabled_ = verify;
- if (verification_enabled_) {
- // Initialize the block cache with enough buckets to hold all of the blocks within the
- // memory-mapped region.
- size_t offset_diff = offset % INCFS_DATA_FILE_BLOCK_SIZE;
- size_t base_length_ = length + offset_diff;
- start_block_offset_ = offset - offset_diff;
- start_block_ptr_ = reinterpret_cast<const uint8_t*>(map_->getDataPtr()) - offset_diff;
-
- const size_t bucket_count = (base_length_ / INCFS_DATA_FILE_BLOCK_SIZE) / kBucketBits;
- loaded_blocks_ = std::vector<std::atomic<bucket_t> >(bucket_count + 1U);
- }
- return true;
-}
-
-bool IncFsFileMap::Verify(const uint8_t* const& data_start, const uint8_t* const& data_end,
- const uint8_t** prev_verified_block) const {
- const data_block_index_t start_index = get_block_index(data_start, start_block_ptr_);
- const data_block_index_t end_index = get_block_index(data_end - 1U, start_block_ptr_);
-
- bool success = true;
- // Retrieve the set of the required blocks that must be present in order to read the data
- // safely.
- for (data_block_index_t curr_index = start_index; curr_index <= end_index; ++curr_index) {
- const size_t i = curr_index / kBucketBits;
- const bucket_t present_bit = 1U << (curr_index % kBucketBits);
- std::atomic<bucket_t>& bucket = loaded_blocks_[i];
- if (LIKELY(bucket.load(std::memory_order_relaxed) & present_bit)) {
- continue;
- }
-
- // Touch all of the blocks with pread to ensure that the region of data is fully present.
- uint8_t value;
- const off64_t read_offset = (curr_index * INCFS_DATA_FILE_BLOCK_SIZE) + start_block_offset_;
- if (UNLIKELY(TEMP_FAILURE_RETRY(pread64(fd_, &value, 1U, read_offset)) <= 0)) {
- success = false;
- break;
- }
-
- bucket.fetch_or(present_bit, std::memory_order_relaxed);
- }
-
- if (UNLIKELY(!success)) {
- // Log the region of the file that could not be fully loaded.
- size_t start_offset = (data_start - start_block_ptr_) + map_->getDataOffset();
- size_t end_offset = (data_end - start_block_ptr_) + map_->getDataOffset();
- std::string location = file_name() ? base::StringPrintf("path %s", file_name())
- : base::StringPrintf("fd %d", fd_);
- const std::string message =
- base::StringPrintf("region 0x%016zx - 0x%016zx of %s not fully loaded",
- start_offset, end_offset, location.c_str());
- LOG(WARNING) << message;
- return false;
- }
-
- // Update the previous verified block pointer to optimize repeated verifies on the same block.
- *prev_verified_block = start_block_ptr_ + (end_index * INCFS_DATA_FILE_BLOCK_SIZE);
- return true;
-}
-
-#else
-bool IncFsFileMap::Create(int fd, off64_t offset, size_t length, const char* file_name,
- bool verify) {
- return CreateForceVerification(fd, offset, length, file_name, verify);
-}
-
-bool IncFsFileMap::CreateForceVerification(int fd, off64_t offset, size_t length,
- const char* file_name, bool /* verify */) {
- map_ = std::make_unique<android::FileMap>();
- return map_->create(file_name, fd, offset, length, true /* readOnly */);
-}
-
-bool IncFsFileMap::Verify(const uint8_t* const& /* data_start */,
- const uint8_t* const& /* data_end */,
- const uint8_t** /* prev_verified_block */) const {
- return true;
-}
-#endif
-
-} // namespace android::incfs
\ No newline at end of file
diff --git a/libdataloader/Android.bp b/libdataloader/Android.bp
index eb9adf4..142a409 100644
--- a/libdataloader/Android.bp
+++ b/libdataloader/Android.bp
@@ -12,25 +12,6 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-package {
- default_applicable_licenses: [
- "system_incremental_delivery_libdataloader_license",
- ],
-}
-
-// Added automatically by a large-scale-change
-// See: http://go/android-license-faq
-license {
- name: "system_incremental_delivery_libdataloader_license",
- visibility: [":__subpackages__"],
- license_kinds: [
- "SPDX-license-identifier-Apache-2.0",
- ],
- license_text: [
- "NOTICE",
- ],
-}
-
cc_defaults {
name: "libdataloader_defaults",
cpp_std: "c++2a",
@@ -42,11 +23,9 @@
"libbase",
"libincfs",
"liblog",
+ "libnativehelper",
"libutils",
],
- static_libs: [
- "libnativehelper_lazy",
- ],
tidy: true,
tidy_checks: [
"android-*",
diff --git a/libdataloader/DataLoaderConnector.cpp b/libdataloader/DataLoaderConnector.cpp
index f5f82ea..5e41f4f 100644
--- a/libdataloader/DataLoaderConnector.cpp
+++ b/libdataloader/DataLoaderConnector.cpp
@@ -17,9 +17,7 @@
#include <android-base/logging.h>
#include <fcntl.h>
-#include <nativehelper/JNIPlatformHelp.h>
-#include <nativehelper/scoped_local_ref.h>
-#include <nativehelper/scoped_utf_chars.h>
+#include <nativehelper/JNIHelp.h>
#include <sys/stat.h>
#include <utils/Looper.h>
@@ -30,15 +28,12 @@
#include "ManagedDataLoader.h"
#include "dataloader.h"
#include "incfs.h"
-#include "path.h"
namespace {
+using namespace android::dataloader;
using namespace std::literals;
-using ReadInfo = android::dataloader::ReadInfo;
-using ReadInfoWithUid = android::dataloader::ReadInfoWithUid;
-
using FileId = android::incfs::FileId;
using RawMetadata = android::incfs::RawMetadata;
using UniqueControl = android::incfs::UniqueControl;
@@ -72,7 +67,6 @@
jfieldID controlCmd;
jfieldID controlPendingReads;
jfieldID controlLog;
- jfieldID controlBlocksWritten;
jfieldID paramsType;
jfieldID paramsPackageName;
@@ -117,6 +111,8 @@
constants.DATA_LOADER_UNRECOVERABLE =
GetStaticIntFieldValueOrDie(env, listener, "DATA_LOADER_UNRECOVERABLE");
+ CHECK(constants.DATA_LOADER_UNRECOVERABLE == DATA_LOADER_UNRECOVERABLE);
+
auto packageInstaller = (jclass)FindClassOrDie(env, "android/content/pm/PackageInstaller");
constants.DATA_LOADER_TYPE_NONE =
@@ -162,8 +158,6 @@
controlPendingReads = GetFieldIDOrDie(env, incControl, "pendingReads",
"Landroid/os/ParcelFileDescriptor;");
controlLog = GetFieldIDOrDie(env, incControl, "log", "Landroid/os/ParcelFileDescriptor;");
- controlBlocksWritten = GetFieldIDOrDie(env, incControl, "blocksWritten",
- "Landroid/os/ParcelFileDescriptor;");
auto params = FindClassOrDie(env, "android/content/pm/DataLoaderParamsParcel");
paramsType = GetFieldIDOrDie(env, params, "type", "I");
@@ -210,17 +204,6 @@
return ids;
}
-bool checkAndClearJavaException(JNIEnv* env, std::string_view method) {
- if (!env->ExceptionCheck()) {
- return false;
- }
-
- LOG(ERROR) << "Java exception during DataLoader::" << method;
- env->ExceptionDescribe();
- env->ExceptionClear();
- return true;
-}
-
bool reportStatusViaCallback(JNIEnv* env, jobject listener, jint storageId, jint status) {
if (listener == nullptr) {
ALOGE("No listener object to talk to IncrementalService. "
@@ -245,10 +228,12 @@
using DataLoaderConnectorsMap = std::unordered_map<int, DataLoaderConnectorPtr>;
struct Globals {
- Globals() { managedDataLoaderFactory = new android::dataloader::ManagedDataLoaderFactory(); }
+ Globals() {
+ managedDataLoaderFactory = new details::DataLoaderFactoryImpl(
+ [](auto jvm, auto) { return std::make_unique<ManagedDataLoader>(jvm); });
+ }
DataLoaderFactory* managedDataLoaderFactory = nullptr;
- DataLoaderFactory* legacyDataLoaderFactory = nullptr;
DataLoaderFactory* dataLoaderFactory = nullptr;
std::mutex dataLoaderConnectorsLock;
@@ -260,8 +245,6 @@
std::thread logLooperThread;
std::vector<ReadInfo> pendingReads;
std::vector<ReadInfo> pageReads;
- std::vector<ReadInfoWithUid> pendingReadsWithUid;
- std::vector<ReadInfoWithUid> pageReadsWithUid;
};
static Globals& globals() {
@@ -301,8 +284,7 @@
static constexpr auto kPendingReadsBufferSize = 256;
-class DataLoaderConnector : public android::dataloader::FilesystemConnector,
- public android::dataloader::StatusListener {
+class DataLoaderConnector : public FilesystemConnector, public StatusListener {
public:
DataLoaderConnector(JNIEnv* env, jobject service, jint storageId, UniqueControl control,
jobject serviceConnector, jobject callbackControl, jobject listener)
@@ -318,68 +300,36 @@
DataLoaderConnector(const DataLoaderConnector&) = delete;
DataLoaderConnector(const DataLoaderConnector&&) = delete;
virtual ~DataLoaderConnector() {
- if (mDataLoader && mDataLoader->onDestroy) {
- mDataLoader->onDestroy(mDataLoader);
- checkAndClearJavaException(__func__);
- }
- mDataLoader = nullptr;
-
JNIEnv* env = GetOrAttachJNIEnvironment(mJvm);
- const auto& jni = jniIds(env);
- reportStatusViaCallback(env, mListener, mStorageId, jni.constants.DATA_LOADER_DESTROYED);
-
env->DeleteGlobalRef(mService);
env->DeleteGlobalRef(mServiceConnector);
env->DeleteGlobalRef(mCallbackControl);
env->DeleteGlobalRef(mListener);
} // to avoid delete-non-virtual-dtor
- bool tryFactory(DataLoaderFactory* factory, bool withFeatures,
- const DataLoaderParamsPair& params, jobject managedParams) {
- if (!factory) {
- return true;
- }
-
- // Let's try the non-default first.
- mDataLoader = factory->onCreate(factory, ¶ms.ndkDataLoaderParams(), this, this, mJvm,
- mService, managedParams);
- if (checkAndClearJavaException(__func__)) {
- return false;
- }
- if (!mDataLoader) {
- return true;
- }
-
- mDataLoaderFeatures = withFeatures && mDataLoader->getFeatures
- ? mDataLoader->getFeatures(mDataLoader)
- : DATA_LOADER_FEATURE_NONE;
- if (mDataLoaderFeatures & DATA_LOADER_FEATURE_UID) {
- ALOGE("DataLoader supports UID");
- CHECK(mDataLoader->onPageReadsWithUid);
- CHECK(mDataLoader->onPendingReadsWithUid);
- }
- return true;
- }
-
bool onCreate(const DataLoaderParamsPair& params, jobject managedParams) {
CHECK(mDataLoader == nullptr);
- if (!mDataLoader &&
- !tryFactory(globals().dataLoaderFactory, /*withFeatures=*/true, params,
- managedParams)) {
- return false;
+ if (auto factory = globals().dataLoaderFactory) {
+ // Let's try the non-default first.
+ mDataLoader = factory->onCreate(factory, ¶ms.ndkDataLoaderParams(), this, this,
+ mJvm, mService, managedParams);
+ if (checkAndClearJavaException(__func__)) {
+ return false;
+ }
}
- if (!mDataLoader &&
- !tryFactory(globals().legacyDataLoaderFactory, /*withFeatures=*/false, params,
- managedParams)) {
- return false;
+
+ if (!mDataLoader) {
+ // Didn't work, let's try the default.
+ auto factory = globals().managedDataLoaderFactory;
+ mDataLoader = factory->onCreate(factory, ¶ms.ndkDataLoaderParams(), this, this,
+ mJvm, mService, managedParams);
+ if (checkAndClearJavaException(__func__)) {
+ return false;
+ }
}
- if (!mDataLoader &&
- !tryFactory(globals().managedDataLoaderFactory, /*withFeatures=*/false, params,
- managedParams)) {
- return false;
- }
+
if (!mDataLoader) {
return false;
}
@@ -388,7 +338,7 @@
}
bool onStart() {
CHECK(mDataLoader);
- bool result = !mDataLoader->onStart || mDataLoader->onStart(mDataLoader);
+ bool result = mDataLoader->onStart(mDataLoader);
if (checkAndClearJavaException(__func__)) {
result = false;
}
@@ -404,21 +354,26 @@
std::lock_guard{mPendingReadsLooperBusy}; // NOLINT
std::lock_guard{mLogLooperBusy}; // NOLINT
- if (mDataLoader->onStop) {
- mDataLoader->onStop(mDataLoader);
- }
+ mDataLoader->onStop(mDataLoader);
+ checkAndClearJavaException(__func__);
+ }
+ void onDestroy() {
+ CHECK(mDataLoader);
+ mDataLoader->onDestroy(mDataLoader);
checkAndClearJavaException(__func__);
}
- bool onPrepareImage(const android::dataloader::DataLoaderInstallationFiles& addedFiles) {
+ bool onPrepareImage(const DataLoaderInstallationFiles& addedFiles) {
CHECK(mDataLoader);
- bool result = !mDataLoader->onPrepareImage ||
+ bool result =
mDataLoader->onPrepareImage(mDataLoader, addedFiles.data(), addedFiles.size());
+ if (checkAndClearJavaException(__func__)) {
+ result = false;
+ }
return result;
}
- template <class ReadInfoType>
- int onPendingReadsLooperEvent(std::vector<ReadInfoType>& pendingReads) {
+ int onPendingReadsLooperEvent(std::vector<ReadInfo>& pendingReads) {
CHECK(mDataLoader);
std::lock_guard lock{mPendingReadsLooperBusy};
while (mRunning.load(std::memory_order_relaxed)) {
@@ -428,23 +383,11 @@
pendingReads.empty()) {
return 1;
}
- if constexpr (std::is_same_v<ReadInfoType, ReadInfo>) {
- if (mDataLoader->onPendingReads) {
- mDataLoader->onPendingReads(mDataLoader, pendingReads.data(),
- pendingReads.size());
- }
- } else {
- if (mDataLoader->onPendingReadsWithUid) {
- mDataLoader->onPendingReadsWithUid(mDataLoader, pendingReads.data(),
- pendingReads.size());
- }
- }
+ mDataLoader->onPendingReads(mDataLoader, pendingReads.data(), pendingReads.size());
}
return 1;
}
-
- template <class ReadInfoType>
- int onLogLooperEvent(std::vector<ReadInfoType>& pageReads) {
+ int onLogLooperEvent(std::vector<ReadInfo>& pageReads) {
CHECK(mDataLoader);
std::lock_guard lock{mLogLooperBusy};
while (mRunning.load(std::memory_order_relaxed)) {
@@ -454,40 +397,11 @@
pageReads.empty()) {
return 1;
}
- if constexpr (std::is_same_v<ReadInfoType, ReadInfo>) {
- if (mDataLoader->onPageReads) {
- mDataLoader->onPageReads(mDataLoader, pageReads.data(), pageReads.size());
- }
- } else {
- if (mDataLoader->onPageReadsWithUid) {
- mDataLoader->onPageReadsWithUid(mDataLoader, pageReads.data(),
- pageReads.size());
- }
- }
+ mDataLoader->onPageReads(mDataLoader, pageReads.data(), pageReads.size());
}
return 1;
}
- int onPendingReadsLooperEvent(std::vector<ReadInfo>& pendingReads,
- std::vector<ReadInfoWithUid>& pendingReadsWithUid) {
- CHECK(mDataLoader);
- if (mDataLoaderFeatures & DATA_LOADER_FEATURE_UID) {
- return this->onPendingReadsLooperEvent(pendingReadsWithUid);
- } else {
- return this->onPendingReadsLooperEvent(pendingReads);
- }
- }
-
- int onLogLooperEvent(std::vector<ReadInfo>& pageReads,
- std::vector<ReadInfoWithUid>& pageReadsWithUid) {
- CHECK(mDataLoader);
- if (mDataLoaderFeatures & DATA_LOADER_FEATURE_UID) {
- return this->onLogLooperEvent(pageReadsWithUid);
- } else {
- return this->onLogLooperEvent(pageReads);
- }
- }
-
void writeData(jstring name, jlong offsetBytes, jlong lengthBytes, jobject incomingFd) const {
CHECK(mCallbackControl);
JNIEnv* env = GetOrAttachJNIEnvironment(mJvm);
@@ -500,7 +414,7 @@
return android::incfs::openForSpecialOps(mControl, fid);
}
- int writeBlocks(android::dataloader::Span<const IncFsDataBlock> blocks) const {
+ int writeBlocks(Span<const IncFsDataBlock> blocks) const {
return android::incfs::writeBlocks(blocks);
}
@@ -525,28 +439,25 @@
}
bool reportStatus(DataLoaderStatus status) {
- JNIEnv* env = GetOrAttachJNIEnvironment(mJvm);
- const auto& jni = jniIds(env);
-
- jint osStatus;
- switch (status) {
- case DATA_LOADER_UNAVAILABLE:
- osStatus = jni.constants.DATA_LOADER_UNAVAILABLE;
- break;
- case DATA_LOADER_UNRECOVERABLE:
- osStatus = jni.constants.DATA_LOADER_UNRECOVERABLE;
- break;
- default: {
- ALOGE("Unable to report invalid status. status=%d", status);
- return false;
- }
+ if (status < DATA_LOADER_FIRST_STATUS || DATA_LOADER_LAST_STATUS < status) {
+ ALOGE("Unable to report invalid status. status=%d", status);
+ return false;
}
- return reportStatusViaCallback(env, mListener, mStorageId, osStatus);
+ JNIEnv* env = GetOrAttachJNIEnvironment(mJvm);
+ return reportStatusViaCallback(env, mListener, mStorageId, status);
}
bool checkAndClearJavaException(std::string_view method) const {
JNIEnv* env = GetOrAttachJNIEnvironment(mJvm);
- return ::checkAndClearJavaException(env, method);
+
+ if (!env->ExceptionCheck()) {
+ return false;
+ }
+
+ LOG(ERROR) << "Java exception during DataLoader::" << method;
+ env->ExceptionDescribe();
+ env->ExceptionClear();
+ return true;
}
const UniqueControl& control() const { return mControl; }
@@ -563,7 +474,6 @@
UniqueControl const mControl;
::DataLoader* mDataLoader = nullptr;
- DataLoaderFeatures mDataLoaderFeatures = DATA_LOADER_FEATURE_NONE;
std::mutex mPendingReadsLooperBusy;
std::mutex mLogLooperBusy;
@@ -576,8 +486,7 @@
return 0;
}
auto&& dataLoaderConnector = (DataLoaderConnector*)data;
- return dataLoaderConnector->onPendingReadsLooperEvent(globals().pendingReads,
- globals().pendingReadsWithUid);
+ return dataLoaderConnector->onPendingReadsLooperEvent(globals().pendingReads);
}
static int onLogLooperEvent(int fd, int events, void* data) {
@@ -586,7 +495,7 @@
return 0;
}
auto&& dataLoaderConnector = (DataLoaderConnector*)data;
- return dataLoaderConnector->onLogLooperEvent(globals().pageReads, globals().pageReadsWithUid);
+ return dataLoaderConnector->onLogLooperEvent(globals().pageReads);
}
static int createFdFromManaged(JNIEnv* env, jobject pfd) {
@@ -619,10 +528,7 @@
auto pr = createFdFromManaged(env,
env->GetObjectField(managedIncControl, jni.controlPendingReads));
auto log = createFdFromManaged(env, env->GetObjectField(managedIncControl, jni.controlLog));
- auto blocksWritten =
- createFdFromManaged(env,
- env->GetObjectField(managedIncControl, jni.controlBlocksWritten));
- return android::incfs::createControl(cmd, pr, log, blocksWritten);
+ return android::incfs::createControl(cmd, pr, log);
}
DataLoaderParamsPair::DataLoaderParamsPair(android::dataloader::DataLoaderParams&& dataLoaderParams)
@@ -638,32 +544,31 @@
const DataLoaderType type = (DataLoaderType)env->GetIntField(managedParams, jni.paramsType);
- ScopedLocalRef<jstring> paramsPackageName(env,
- GetStringField(env, managedParams,
- jni.paramsPackageName));
- ScopedLocalRef<jstring> paramsClassName(env,
- GetStringField(env, managedParams,
- jni.paramsClassName));
- ScopedLocalRef<jstring> paramsArguments(env,
- GetStringField(env, managedParams,
- jni.paramsArguments));
- ScopedUtfChars package(env, paramsPackageName.get());
- ScopedUtfChars className(env, paramsClassName.get());
- ScopedUtfChars arguments(env, paramsArguments.get());
- return DataLoaderParamsPair(android::dataloader::DataLoaderParams(type, package.c_str(),
- className.c_str(),
- arguments.c_str()));
+ std::string packageName(
+ env->GetStringUTFChars((jstring)env->GetObjectField(managedParams,
+ jni.paramsPackageName),
+ nullptr));
+ std::string className(
+ env->GetStringUTFChars((jstring)env->GetObjectField(managedParams, jni.paramsClassName),
+ nullptr));
+ std::string arguments(
+ env->GetStringUTFChars((jstring)env->GetObjectField(managedParams, jni.paramsArguments),
+ nullptr));
+
+ return DataLoaderParamsPair(android::dataloader::DataLoaderParams(type, std::move(packageName),
+ std::move(className),
+ std::move(arguments)));
}
static void pendingReadsLooperThread() {
- constexpr auto kTimeoutMsecs = -1;
+ constexpr auto kTimeoutMsecs = 60 * 1000;
while (!globals().stopped) {
pendingReadsLooper().pollAll(kTimeoutMsecs);
}
}
static void logLooperThread() {
- constexpr auto kTimeoutMsecs = -1;
+ constexpr auto kTimeoutMsecs = 60 * 1000;
while (!globals().stopped) {
logLooper().pollAll(kTimeoutMsecs);
}
@@ -703,11 +608,6 @@
void DataLoader_Initialize(struct ::DataLoaderFactory* factory) {
CHECK(factory) << "DataLoader factory is invalid.";
- globals().legacyDataLoaderFactory = factory;
-}
-
-void DataLoader_Initialize_WithFeatures(struct ::DataLoaderFactory* factory) {
- CHECK(factory) << "DataLoader factory is invalid.";
globals().dataLoaderFactory = factory;
}
@@ -760,23 +660,15 @@
}
}
auto nativeControl = createIncFsControlFromManaged(env, control);
- if (nativeControl) {
- using namespace android::incfs;
- ALOGI("DataLoader::create incremental fds: %d/%d/%d/%d", nativeControl.cmd(),
- nativeControl.pendingReads(), nativeControl.logs(), nativeControl.blocksWritten());
- auto cmdPath = pathFromFd(nativeControl.cmd());
- auto dir = path::dirName(cmdPath);
- ALOGI("DataLoader::create incremental dir: %s, files: %s/%s/%s/%s",
- details::c_str(dir).get(), details::c_str(path::baseName(cmdPath)).get(),
- details::c_str(path::baseName(pathFromFd(nativeControl.pendingReads()))).get(),
- details::c_str(path::baseName(pathFromFd(nativeControl.logs()))).get(),
- details::c_str(path::baseName(pathFromFd(nativeControl.blocksWritten()))).get());
- } else {
- ALOGI("DataLoader::create no incremental control");
- }
+ ALOGI("DataLoader::create1 cmd: %d|%s", nativeControl.cmd(),
+ pathFromFd(nativeControl.cmd()).c_str());
+ ALOGI("DataLoader::create1 pendingReads: %d|%s", nativeControl.pendingReads(),
+ pathFromFd(nativeControl.pendingReads()).c_str());
+ ALOGI("DataLoader::create1 log: %d|%s", nativeControl.logs(),
+ pathFromFd(nativeControl.logs()).c_str());
auto nativeParams = DataLoaderParamsPair::createFromManaged(env, params);
- ALOGI("DataLoader::create params: %d|%s|%s|%s", nativeParams.dataLoaderParams().type(),
+ ALOGI("DataLoader::create2: %d|%s|%s|%s", nativeParams.dataLoaderParams().type(),
nativeParams.dataLoaderParams().packageName().c_str(),
nativeParams.dataLoaderParams().className().c_str(),
nativeParams.dataLoaderParams().arguments().c_str());
@@ -795,7 +687,6 @@
auto dataLoaderConnector =
std::make_unique<DataLoaderConnector>(env, service, storageId, std::move(nativeControl),
serviceConnector, callbackControl, listener);
- bool created = dataLoaderConnector->onCreate(nativeParams, params);
{
std::lock_guard lock{globals().dataLoaderConnectorsLock};
auto [dlIt, dlInserted] =
@@ -805,8 +696,7 @@
ALOGE("id(%d): already exist, skipping creation.", storageId);
return false;
}
-
- if (!created) {
+ if (!dlIt->second->onCreate(nativeParams, params)) {
globals().dataLoaderConnectors.erase(dlIt);
// Enable the reporter.
reportUnavailableOnExit.reset(listener);
@@ -832,6 +722,8 @@
std::unique_ptr<_jobject, decltype(destroyAndReportUnavailable)>
destroyAndReportUnavailableOnExit(nullptr, destroyAndReportUnavailable);
+ const UniqueControl* control;
+ jobject listener;
DataLoaderConnectorPtr dataLoaderConnector;
{
std::lock_guard lock{globals().dataLoaderConnectorsLock};
@@ -840,44 +732,41 @@
ALOGE("Failed to start id(%d): not found", storageId);
return false;
}
- dataLoaderConnector = dlIt->second;
- }
- const UniqueControl* control = &(dataLoaderConnector->control());
- jobject listener = dataLoaderConnector->getListenerLocalRef(env);
- if (!dataLoaderConnector->onStart()) {
- ALOGE("Failed to start id(%d): onStart returned false", storageId);
- destroyAndReportUnavailableOnExit.reset(listener);
- return false;
+ listener = dlIt->second->getListenerLocalRef(env);
+
+ dataLoaderConnector = dlIt->second;
+ if (!dataLoaderConnector->onStart()) {
+ ALOGE("Failed to start id(%d): onStart returned false", storageId);
+ destroyAndReportUnavailableOnExit.reset(listener);
+ return false;
+ }
+
+ control = &(dataLoaderConnector->control());
+
+ // Create loopers while we are under lock.
+ if (control->pendingReads() >= 0 && !globals().pendingReadsLooperThread.joinable()) {
+ pendingReadsLooper();
+ globals().pendingReadsLooperThread = std::thread(&pendingReadsLooperThread);
+ }
+ if (control->logs() >= 0 && !globals().logLooperThread.joinable()) {
+ logLooper();
+ globals().logLooperThread = std::thread(&logLooperThread);
+ }
}
if (control->pendingReads() >= 0) {
- auto&& looper = pendingReadsLooper();
- if (!globals().pendingReadsLooperThread.joinable()) {
- std::lock_guard lock{globals().dataLoaderConnectorsLock};
- if (!globals().pendingReadsLooperThread.joinable()) {
- globals().pendingReadsLooperThread = std::thread(&pendingReadsLooperThread);
- }
- }
-
- looper.addFd(control->pendingReads(), android::Looper::POLL_CALLBACK,
- android::Looper::EVENT_INPUT, &onPendingReadsLooperEvent,
- dataLoaderConnector.get());
- looper.wake();
+ pendingReadsLooper().addFd(control->pendingReads(), android::Looper::POLL_CALLBACK,
+ android::Looper::EVENT_INPUT, &onPendingReadsLooperEvent,
+ dataLoaderConnector.get());
+ pendingReadsLooper().wake();
}
if (control->logs() >= 0) {
- auto&& looper = logLooper();
- if (!globals().logLooperThread.joinable()) {
- std::lock_guard lock{globals().dataLoaderConnectorsLock};
- if (!globals().logLooperThread.joinable()) {
- globals().logLooperThread = std::thread(&logLooperThread);
- }
- }
-
- looper.addFd(control->logs(), android::Looper::POLL_CALLBACK, android::Looper::EVENT_INPUT,
- &onLogLooperEvent, dataLoaderConnector.get());
- looper.wake();
+ logLooper().addFd(control->logs(), android::Looper::POLL_CALLBACK,
+ android::Looper::EVENT_INPUT, &onLogLooperEvent,
+ dataLoaderConnector.get());
+ logLooper().wake();
}
const auto& jni = jniIds(env);
@@ -886,8 +775,17 @@
return true;
}
-static void DataLoaderService_OnStop_NoStatus(const UniqueControl* control,
- const DataLoaderConnectorPtr& dataLoaderConnector) {
+jobject DataLoaderService_OnStop_NoStatus(JNIEnv* env, jint storageId) {
+ const UniqueControl* control;
+ {
+ std::lock_guard lock{globals().dataLoaderConnectorsLock};
+ auto dlIt = globals().dataLoaderConnectors.find(storageId);
+ if (dlIt == globals().dataLoaderConnectors.end()) {
+ return nullptr;
+ }
+ control = &(dlIt->second->control());
+ }
+
if (control->pendingReads() >= 0) {
pendingReadsLooper().removeFd(control->pendingReads());
pendingReadsLooper().wake();
@@ -896,25 +794,30 @@
logLooper().removeFd(control->logs());
logLooper().wake();
}
- dataLoaderConnector->onStop();
-}
-bool DataLoaderService_OnStop(JNIEnv* env, jint storageId) {
- DataLoaderConnectorPtr dataLoaderConnector;
+ jobject listener = nullptr;
{
std::lock_guard lock{globals().dataLoaderConnectorsLock};
auto dlIt = globals().dataLoaderConnectors.find(storageId);
if (dlIt == globals().dataLoaderConnectors.end()) {
ALOGI("Failed to stop id(%d): not found", storageId);
- return true;
+ return nullptr;
}
- dataLoaderConnector = dlIt->second;
- }
- const UniqueControl* control = &(dataLoaderConnector->control());
- jobject listener = dataLoaderConnector->getListenerLocalRef(env);
- // Just stop.
- DataLoaderService_OnStop_NoStatus(control, dataLoaderConnector);
+ listener = dlIt->second->getListenerLocalRef(env);
+
+ auto&& dataLoaderConnector = dlIt->second;
+ dataLoaderConnector->onStop();
+ }
+ return listener;
+}
+
+bool DataLoaderService_OnStop(JNIEnv* env, jint storageId) {
+ auto listener = DataLoaderService_OnStop_NoStatus(env, storageId);
+ if (listener == nullptr) {
+ ALOGI("Failed to stop id(%d): not found", storageId);
+ return true;
+ }
const auto& jni = jniIds(env);
reportStatusViaCallback(env, listener, storageId, jni.constants.DATA_LOADER_STOPPED);
@@ -922,26 +825,36 @@
return true;
}
-bool DataLoaderService_OnDestroy(JNIEnv* env, jint storageId) {
- DataLoaderConnectorPtr dataLoaderConnector;
+jobject DataLoaderService_OnDestroy_NoStatus(JNIEnv* env, jint storageId) {
+ jobject listener = DataLoaderService_OnStop_NoStatus(env, storageId);
+ if (!listener) {
+ return nullptr;
+ }
+
{
std::lock_guard lock{globals().dataLoaderConnectorsLock};
auto dlIt = globals().dataLoaderConnectors.find(storageId);
if (dlIt == globals().dataLoaderConnectors.end()) {
- ALOGI("Failed to destroy id(%d): not found", storageId);
- return true;
+ return nullptr;
}
- dataLoaderConnector = std::move(dlIt->second);
+
+ auto&& dataLoaderConnector = dlIt->second;
+ dataLoaderConnector->onDestroy();
globals().dataLoaderConnectors.erase(dlIt);
}
- const UniqueControl* control = &(dataLoaderConnector->control());
- // Stop/destroy.
- DataLoaderService_OnStop_NoStatus(control, dataLoaderConnector);
- // This will destroy the last instance of the DataLoaderConnectorPtr and should trigger the
- // destruction of the DataLoader. However if there are any hanging instances, the destruction
- // will be postponed. E.g. OnPrepareImage in progress at the same time we call OnDestroy.
- dataLoaderConnector = {};
+ return listener;
+}
+
+bool DataLoaderService_OnDestroy(JNIEnv* env, jint storageId) {
+ jobject listener = DataLoaderService_OnDestroy_NoStatus(env, storageId);
+ if (!listener) {
+ ALOGI("Failed to remove id(%d): not found", storageId);
+ return true;
+ }
+
+ const auto& jni = jniIds(env);
+ reportStatusViaCallback(env, listener, storageId, jni.constants.DATA_LOADER_DESTROYED);
return true;
}
@@ -972,22 +885,23 @@
files.reserve(size);
for (int i = 0; i < size; ++i) {
- ScopedLocalRef<jobject> jfile(env, env->GetObjectArrayElement(jfiles, i));
+ jobject jfile = env->GetObjectArrayElement(jfiles, i);
DataLoaderLocation location =
- (DataLoaderLocation)env->GetIntField(jfile.get(), jni.installationFileLocation);
- ScopedUtfChars name(env, GetStringField(env, jfile.get(), jni.installationFileName));
- IncFsSize size = env->GetLongField(jfile.get(), jni.installationFileLengthBytes);
+ (DataLoaderLocation)env->GetIntField(jfile, jni.installationFileLocation);
+ std::string name =
+ env->GetStringUTFChars((jstring)env->GetObjectField(jfile,
+ jni.installationFileName),
+ nullptr);
+ IncFsSize size = env->GetLongField(jfile, jni.installationFileLengthBytes);
- ScopedLocalRef<jbyteArray> jmetadataBytes(env,
- GetByteArrayField(env, jfile.get(),
- jni.installationFileMetadata));
- auto metadataElements = env->GetByteArrayElements(jmetadataBytes.get(), nullptr);
- auto metadataLength = env->GetArrayLength(jmetadataBytes.get());
+ auto jmetadataBytes = (jbyteArray)env->GetObjectField(jfile, jni.installationFileMetadata);
+ auto metadataElements = env->GetByteArrayElements(jmetadataBytes, nullptr);
+ auto metadataLength = env->GetArrayLength(jmetadataBytes);
RawMetadata metadata(metadataElements, metadataElements + metadataLength);
- env->ReleaseByteArrayElements(jmetadataBytes.get(), metadataElements, 0);
+ env->ReleaseByteArrayElements(jmetadataBytes, metadataElements, 0);
- files.emplace_back(location, name.c_str(), size, std::move(metadata));
+ files.emplace_back(location, std::move(name), size, std::move(metadata));
}
return DataLoaderInstallationFilesPair(std::move(files));
@@ -1011,6 +925,7 @@
bool DataLoaderService_OnPrepareImage(JNIEnv* env, jint storageId, jobjectArray addedFiles,
jobjectArray removedFiles) {
+ jobject listener;
DataLoaderConnectorPtr dataLoaderConnector;
{
std::lock_guard lock{globals().dataLoaderConnectorsLock};
@@ -1019,20 +934,14 @@
ALOGE("Failed to handle onPrepareImage for id(%d): not found", storageId);
return false;
}
+ listener = dlIt->second->getListenerLocalRef(env);
dataLoaderConnector = dlIt->second;
}
- jobject listener = dataLoaderConnector->getListenerLocalRef(env);
auto addedFilesPair = DataLoaderInstallationFilesPair::createFromManaged(env, addedFiles);
bool result = dataLoaderConnector->onPrepareImage(addedFilesPair.ndkFiles());
const auto& jni = jniIds(env);
-
- if (checkAndClearJavaException(env, "onPrepareImage")) {
- reportStatusViaCallback(env, listener, storageId, jni.constants.DATA_LOADER_UNAVAILABLE);
- return false;
- }
-
reportStatusViaCallback(env, listener, storageId,
result ? jni.constants.DATA_LOADER_IMAGE_READY
: jni.constants.DATA_LOADER_IMAGE_NOT_READY);
diff --git a/libdataloader/JNIHelpers.h b/libdataloader/JNIHelpers.h
index c35a669..f884eca 100644
--- a/libdataloader/JNIHelpers.h
+++ b/libdataloader/JNIHelpers.h
@@ -59,14 +59,6 @@
return env->GetStaticIntField(clazz, res);
}
-static inline jstring GetStringField(JNIEnv* env, jobject obj, jfieldID field) {
- return reinterpret_cast<jstring>(env->GetObjectField(obj, field));
-}
-
-static inline jbyteArray GetByteArrayField(JNIEnv* env, jobject obj, jfieldID field) {
- return reinterpret_cast<jbyteArray>(env->GetObjectField(obj, field));
-}
-
static inline JNIEnv* GetJNIEnvironment(JavaVM* vm) {
JNIEnv* env;
if (vm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6) != JNI_OK) {
diff --git a/libdataloader/ManagedDataLoader.cpp b/libdataloader/ManagedDataLoader.cpp
index c277d45..8f3e888 100644
--- a/libdataloader/ManagedDataLoader.cpp
+++ b/libdataloader/ManagedDataLoader.cpp
@@ -91,32 +91,18 @@
} // namespace
-ManagedDataLoader::ManagedDataLoader(JavaVM* jvm, jobject dataLoader)
- : mJvm(jvm), mDataLoader(dataLoader) {
+ManagedDataLoader::ManagedDataLoader(JavaVM* jvm) : mJvm(jvm) {
CHECK(mJvm);
-
- LegacyDataLoader::onStart = [](auto) -> bool { return true; };
- LegacyDataLoader::onStop = [](auto) {};
- LegacyDataLoader::onDestroy = [](LegacyDataLoader* self) {
- auto me = static_cast<ManagedDataLoader*>(self);
- me->onDestroy();
- delete me;
- };
- LegacyDataLoader::onPrepareImage = [](auto* self, const auto addedFiles[],
- int addedFilesCount) -> bool {
- return static_cast<ManagedDataLoader*>(self)->onPrepareImage(
- DataLoaderInstallationFiles(addedFiles, addedFilesCount));
- };
- LegacyDataLoader::onPendingReads = [](auto, auto, auto) {};
- LegacyDataLoader::onPageReads = [](auto, auto, auto) {};
}
-LegacyDataLoader* ManagedDataLoader::create(JavaVM* jvm,
- android::dataloader::FilesystemConnectorPtr ifs,
- android::dataloader::StatusListenerPtr listener,
- android::dataloader::ServiceConnectorPtr service,
- android::dataloader::ServiceParamsPtr params) {
- JNIEnv* env = GetJNIEnvironment(jvm);
+bool ManagedDataLoader::onCreate(const android::dataloader::DataLoaderParams&,
+ android::dataloader::FilesystemConnectorPtr ifs,
+ android::dataloader::StatusListenerPtr listener,
+ android::dataloader::ServiceConnectorPtr service,
+ android::dataloader::ServiceParamsPtr params) {
+ CHECK(!mDataLoader);
+
+ JNIEnv* env = GetJNIEnvironment(mJvm);
const auto& jni = jniIds(env);
jobject dlp = env->NewObject(jni.dataLoaderParams, jni.dataLoaderParamsConstruct, params);
@@ -126,16 +112,14 @@
auto dataLoader = env->CallObjectMethod(service, jni.dataLoaderServiceOnCreateDataLoader, dlp);
if (!dataLoader) {
LOG(ERROR) << "Failed to create Java DataLoader.";
- return nullptr;
+ return false;
}
if (env->ExceptionCheck()) {
- return nullptr;
- }
- if (!env->CallBooleanMethod(dataLoader, jni.dataLoaderOnCreate, dlp, ifsc)) {
- return nullptr;
+ return false;
}
- return new ManagedDataLoader(jvm, env->NewGlobalRef(dataLoader));
+ mDataLoader = env->NewGlobalRef(dataLoader);
+ return env->CallBooleanMethod(mDataLoader, jni.dataLoaderOnCreate, dlp, ifsc);
}
void ManagedDataLoader::onDestroy() {
@@ -179,18 +163,4 @@
return env->CallBooleanMethod(mDataLoader, jni.dataLoaderOnPrepareImage, jaddedFiles, nullptr);
}
-ManagedDataLoaderFactory::ManagedDataLoaderFactory() {
- ::DataLoaderFactory::onCreate =
- [](::DataLoaderFactory* self, const ::DataLoaderParams* ndkParams,
- ::DataLoaderFilesystemConnectorPtr fsConnector,
- ::DataLoaderStatusListenerPtr statusListener, ::DataLoaderServiceVmPtr vm,
- ::DataLoaderServiceConnectorPtr serviceConnector,
- ::DataLoaderServiceParamsPtr serviceParams) -> ::DataLoader* {
- return reinterpret_cast<::DataLoader*>(
- ManagedDataLoader::create(vm, static_cast<FilesystemConnector*>(fsConnector),
- static_cast<StatusListener*>(statusListener),
- serviceConnector, serviceParams));
- };
-}
-
} // namespace android::dataloader
diff --git a/libdataloader/ManagedDataLoader.h b/libdataloader/ManagedDataLoader.h
index b9f714f..33d00c6 100644
--- a/libdataloader/ManagedDataLoader.h
+++ b/libdataloader/ManagedDataLoader.h
@@ -17,50 +17,30 @@
#include <dataloader.h>
-__BEGIN_DECLS
-
-// This simulates legacy dataloader (compiled with previous version of libincfs_dataloader).
-// We still need to be able to support them.
-struct LegacyDataLoader {
- bool (*onStart)(struct LegacyDataLoader* self);
- void (*onStop)(struct LegacyDataLoader* self);
- void (*onDestroy)(struct LegacyDataLoader* self);
-
- bool (*onPrepareImage)(struct LegacyDataLoader* self,
- const DataLoaderInstallationFile addedFiles[], int addedFilesCount);
-
- void (*onPendingReads)(struct LegacyDataLoader* self, const IncFsReadInfo pendingReads[],
- int pendingReadsCount);
- void (*onPageReads)(struct LegacyDataLoader* self, const IncFsReadInfo pageReads[],
- int pageReadsCount);
-};
-
-__END_DECLS
-
namespace android::dataloader {
// Default DataLoader redirects everything back to Java.
-struct ManagedDataLoader : private LegacyDataLoader {
- static LegacyDataLoader* create(JavaVM* jvm, android::dataloader::FilesystemConnectorPtr ifs,
- android::dataloader::StatusListenerPtr listener,
- android::dataloader::ServiceConnectorPtr service,
- android::dataloader::ServiceParamsPtr params);
+struct ManagedDataLoader : public DataLoader {
+ ManagedDataLoader(JavaVM* jvm);
private:
- ManagedDataLoader(JavaVM* jvm, jobject dataLoader);
-
// Lifecycle.
- void onDestroy();
+ bool onCreate(const android::dataloader::DataLoaderParams&,
+ android::dataloader::FilesystemConnectorPtr ifs,
+ android::dataloader::StatusListenerPtr listener,
+ android::dataloader::ServiceConnectorPtr service,
+ android::dataloader::ServiceParamsPtr params) final;
+ bool onStart() final { return true; }
+ void onStop() final {}
+ void onDestroy() final;
- // Installation.
- bool onPrepareImage(DataLoaderInstallationFiles addedFiles);
+ bool onPrepareImage(DataLoaderInstallationFiles addedFiles) final;
+
+ void onPendingReads(PendingReads pendingReads) final {}
+ void onPageReads(PageReads pageReads) final {}
JavaVM* const mJvm;
jobject mDataLoader = nullptr;
};
-struct ManagedDataLoaderFactory : public ::DataLoaderFactory {
- ManagedDataLoaderFactory();
-};
-
} // namespace android::dataloader
diff --git a/libdataloader/include/dataloader.h b/libdataloader/include/dataloader.h
index 2cfd9ff..a46167d 100644
--- a/libdataloader/include/dataloader.h
+++ b/libdataloader/include/dataloader.h
@@ -36,9 +36,7 @@
struct StatusListener;
using FileId = IncFsFileId;
-using Uid = IncFsUid;
using ReadInfo = IncFsReadInfo;
-using ReadInfoWithUid = IncFsReadInfoWithUid;
using DataBlock = IncFsDataBlock;
using FilesystemConnectorPtr = FilesystemConnector*;
@@ -49,9 +47,7 @@
using DataLoaderPtr = std::unique_ptr<DataLoader>;
using DataLoaderInstallationFiles = Span<const ::DataLoaderInstallationFile>;
using PendingReads = Span<const ReadInfo>;
-using PendingReadsWithUid = Span<const ReadInfoWithUid>;
using PageReads = Span<const ReadInfo>;
-using PageReadsWithUid = Span<const ReadInfoWithUid>;
using RawMetadata = std::vector<char>;
using DataBlocks = Span<const DataBlock>;
@@ -63,9 +59,6 @@
virtual ~DataLoader() {}
- // Bitmask of supported features.
- virtual DataLoaderFeatures getFeatures() const = 0;
-
// Lifecycle.
virtual bool onCreate(const DataLoaderParams&, FilesystemConnectorPtr, StatusListenerPtr,
ServiceConnectorPtr, ServiceParamsPtr) = 0;
@@ -79,9 +72,6 @@
// IFS callbacks.
virtual void onPendingReads(PendingReads pendingReads) = 0;
virtual void onPageReads(PageReads pageReads) = 0;
-
- virtual void onPendingReadsWithUid(PageReadsWithUid pendingReads) = 0;
- virtual void onPageReadsWithUid(PendingReadsWithUid pageReads) = 0;
};
struct DataLoaderParams {
diff --git a/libdataloader/include/dataloader_inline.h b/libdataloader/include/dataloader_inline.h
index 10a5c46..26da530 100644
--- a/libdataloader/include/dataloader_inline.h
+++ b/libdataloader/include/dataloader_inline.h
@@ -22,9 +22,6 @@
struct DataLoaderImpl : public ::DataLoader {
DataLoaderImpl(DataLoaderPtr&& dataLoader) : mDataLoader(std::move(dataLoader)) {
- getFeatures = [](DataLoader* self) -> DataLoaderFeatures {
- return static_cast<DataLoaderImpl*>(self)->mDataLoader->getFeatures();
- };
onStart = [](DataLoader* self) -> bool {
return static_cast<DataLoaderImpl*>(self)->mDataLoader->onStart();
};
@@ -50,16 +47,6 @@
return static_cast<DataLoaderImpl*>(self)->mDataLoader->onPageReads(
PageReads(pageReads, pageReadsCount));
};
- onPendingReadsWithUid = [](DataLoader* self, const IncFsReadInfoWithUid pendingReads[],
- int pendingReadsCount) {
- return static_cast<DataLoaderImpl*>(self)->mDataLoader->onPendingReadsWithUid(
- PendingReadsWithUid(pendingReads, pendingReadsCount));
- };
- onPageReadsWithUid = [](DataLoader* self, const IncFsReadInfoWithUid pageReads[],
- int pageReadsCount) {
- return static_cast<DataLoaderImpl*>(self)->mDataLoader->onPageReadsWithUid(
- PageReadsWithUid(pageReads, pageReadsCount));
- };
}
private:
@@ -112,7 +99,7 @@
} // namespace details
inline void DataLoader::initialize(DataLoader::Factory&& factory) {
- DataLoader_Initialize_WithFeatures(new details::DataLoaderFactoryImpl(std::move(factory)));
+ DataLoader_Initialize(new details::DataLoaderFactoryImpl(std::move(factory)));
}
inline DataLoaderParams::DataLoaderParams(DataLoaderType type, std::string&& packageName,
diff --git a/libdataloader/include/dataloader_ndk.h b/libdataloader/include/dataloader_ndk.h
index 14dc3df..67013ec 100644
--- a/libdataloader/include/dataloader_ndk.h
+++ b/libdataloader/include/dataloader_ndk.h
@@ -24,9 +24,12 @@
#define DATALOADER_LIBRARY_NAME "libdataloader.so"
+// Keep in sync with IDataLoaderStatusListener.aidl
typedef enum {
- DATA_LOADER_UNAVAILABLE = 7,
DATA_LOADER_UNRECOVERABLE = 8,
+
+ DATA_LOADER_FIRST_STATUS = DATA_LOADER_UNRECOVERABLE,
+ DATA_LOADER_LAST_STATUS = DATA_LOADER_UNRECOVERABLE,
} DataLoaderStatus;
typedef enum {
@@ -41,11 +44,6 @@
DATA_LOADER_LOCATION_MEDIA_DATA = 2,
} DataLoaderLocation;
-typedef enum {
- DATA_LOADER_FEATURE_NONE = 0,
- DATA_LOADER_FEATURE_UID = 1 << 0,
-} DataLoaderFeatures;
-
struct DataLoaderParams {
int type;
const char* packageName;
@@ -83,7 +81,6 @@
typedef jobject DataLoaderServiceParamsPtr;
struct DataLoader {
- // DataLoader v1.
bool (*onStart)(struct DataLoader* self);
void (*onStop)(struct DataLoader* self);
void (*onDestroy)(struct DataLoader* self);
@@ -95,15 +92,6 @@
int pendingReadsCount);
void (*onPageReads)(struct DataLoader* self, const IncFsReadInfo pageReads[],
int pageReadsCount);
-
- // DataLoader v2, with features.
- // Use DataLoader_Initialize_WithFeatures to set a factory for v2 DataLoader.
- DataLoaderFeatures (*getFeatures)(struct DataLoader* self);
-
- void (*onPendingReadsWithUid)(struct DataLoader* self,
- const IncFsReadInfoWithUid pendingReads[], int pendingReadsCount);
- void (*onPageReadsWithUid)(struct DataLoader* self, const IncFsReadInfoWithUid pageReads[],
- int pageReadsCount);
};
struct DataLoaderFactory {
@@ -113,7 +101,6 @@
DataLoaderServiceParamsPtr);
};
void DataLoader_Initialize(struct DataLoaderFactory*);
-void DataLoader_Initialize_WithFeatures(struct DataLoaderFactory*);
void DataLoader_FilesystemConnector_writeData(DataLoaderFilesystemConnectorPtr, jstring name,
jlong offsetBytes, jlong lengthBytes,
diff --git a/sysprop/Android.bp b/sysprop/Android.bp
index 59ba604..5d7c86b 100644
--- a/sysprop/Android.bp
+++ b/sysprop/Android.bp
@@ -1,7 +1,3 @@
-package {
- default_applicable_licenses: ["Android-Apache-2.0"],
-}
-
sysprop_library {
name: "com.android.sysprop.incremental",
srcs: ["IncrementalProperties.sysprop"],
diff --git a/sysprop/api/com.android.sysprop.incremental-current.txt b/sysprop/api/com.android.sysprop.incremental-current.txt
index e69de29..998da9e 100644
--- a/sysprop/api/com.android.sysprop.incremental-current.txt
+++ b/sysprop/api/com.android.sysprop.incremental-current.txt
@@ -0,0 +1,9 @@
+props {
+ module: "android.sysprop.IncrementalProperties"
+ prop {
+ api_name: "enable"
+ type: String
+ scope: Internal
+ prop_name: "ro.incremental.enable"
+ }
+}