[automerger skipped] storageproxyd: Start only a single binder thread am: 209890c07d am: 9ec3035667 -s ours
am skip reason: Merged-In I53a90eaa2aa69469fd3a00b6da0d7061318c8ba9 with SHA-1 b05b870671 is already in history
Original change: https://googleplex-android-review.googlesource.com/c/platform/system/core/+/24168425
Change-Id: Icb7dd5a47b8bbd28d17d6920658af943d003a2ef
Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
diff --git a/OWNERS b/OWNERS
index 682a067..96b4f54 100644
--- a/OWNERS
+++ b/OWNERS
@@ -1 +1,2 @@
+# Bug component: 128577
enh@google.com
diff --git a/bootstat/bootstat.cpp b/bootstat/bootstat.cpp
index 844357c..3b8866e 100644
--- a/bootstat/bootstat.cpp
+++ b/bootstat/bootstat.cpp
@@ -459,6 +459,8 @@
{"reboot,sys_ldo_ok,pmic,main", 227},
{"reboot,sys_ldo_ok,pmic,sub", 228},
{"reboot,smpl_timeout,pmic,main", 229},
+ {"reboot,ota,.*", 230},
+ {"reboot,periodic,.*", 231},
};
// Converts a string value representing the reason the system booted to an
diff --git a/debuggerd/client/debuggerd_client.cpp b/debuggerd/client/debuggerd_client.cpp
index c9e097e..bd1e91d 100644
--- a/debuggerd/client/debuggerd_client.cpp
+++ b/debuggerd/client/debuggerd_client.cpp
@@ -276,6 +276,13 @@
return false;
}
+ // WARNING: It's not possible to replace the below with a splice call.
+ // Due to the way debuggerd does many small writes across the pipe,
+ // this would cause splice to copy a page for each write. The second
+ // pipe fills up based on the number of pages being copied, even
+ // though there is not much data being transferred per page. When
+ // the second pipe is full, everything stops since there is nothing
+ // reading the second pipe to clear it.
char buf[1024];
rc = TEMP_FAILURE_RETRY(read(pipe_read.get(), buf, sizeof(buf)));
if (rc == 0) {
diff --git a/debuggerd/client/debuggerd_client_test.cpp b/debuggerd/client/debuggerd_client_test.cpp
index ebb8d86..33ff05f 100644
--- a/debuggerd/client/debuggerd_client_test.cpp
+++ b/debuggerd/client/debuggerd_client_test.cpp
@@ -18,6 +18,7 @@
#include <fcntl.h>
#include <stdio.h>
+#include <sys/eventfd.h>
#include <unistd.h>
#include <chrono>
@@ -51,23 +52,35 @@
TEST(debuggerd_client, race) {
static int THREAD_COUNT = getThreadCount();
+
+ // Semaphore incremented once per thread started.
+ unique_fd barrier(eventfd(0, EFD_SEMAPHORE));
+ ASSERT_NE(-1, barrier.get());
+
pid_t forkpid = fork();
-
ASSERT_NE(-1, forkpid);
-
if (forkpid == 0) {
// Spawn a bunch of threads, to make crash_dump take longer.
std::vector<std::thread> threads;
+ threads.reserve(THREAD_COUNT);
for (int i = 0; i < THREAD_COUNT; ++i) {
- threads.emplace_back([]() {
- while (true) {
- std::this_thread::sleep_for(60s);
+ threads.emplace_back([&barrier]() {
+ uint64_t count = 1;
+ ASSERT_NE(-1, write(barrier.get(), &count, sizeof(count)));
+ for (;;) {
+ pause();
}
});
}
+ for (;;) {
+ pause();
+ }
+ }
- std::this_thread::sleep_for(60s);
- exit(0);
+ // Wait for the child to spawn all of its threads.
+ for (int i = 0; i < THREAD_COUNT; ++i) {
+ uint64_t count;
+ ASSERT_NE(-1, read(barrier.get(), &count, sizeof(count)));
}
unique_fd pipe_read, pipe_write;
@@ -77,9 +90,6 @@
constexpr int PIPE_SIZE = 16 * 1024 * 1024;
ASSERT_EQ(PIPE_SIZE, fcntl(pipe_read.get(), F_SETPIPE_SZ, PIPE_SIZE));
- // Wait for a bit to let the child spawn all of its threads.
- std::this_thread::sleep_for(1s);
-
ASSERT_TRUE(
debuggerd_trigger_dump(forkpid, kDebuggerdNativeBacktrace, 60000, std::move(pipe_write)));
// Immediately kill the forked child, to make sure that the dump didn't return early.
diff --git a/debuggerd/crasher/crasher.cpp b/debuggerd/crasher/crasher.cpp
index 6a19878..12ba502 100644
--- a/debuggerd/crasher/crasher.cpp
+++ b/debuggerd/crasher/crasher.cpp
@@ -148,7 +148,7 @@
noinline void leak() {
while (true) {
void* mapping =
- mmap(nullptr, PAGE_SIZE, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
+ mmap(nullptr, getpagesize(), PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
static_cast<volatile char*>(mapping)[0] = 'a';
}
}
diff --git a/debuggerd/debuggerd.cpp b/debuggerd/debuggerd.cpp
index e20e8d9..26726cf 100644
--- a/debuggerd/debuggerd.cpp
+++ b/debuggerd/debuggerd.cpp
@@ -41,22 +41,6 @@
_exit(exit_code);
}
-static std::thread spawn_redirect_thread(unique_fd fd) {
- return std::thread([fd{ std::move(fd) }]() {
- while (true) {
- char buf[BUFSIZ];
- ssize_t rc = TEMP_FAILURE_RETRY(read(fd.get(), buf, sizeof(buf)));
- if (rc <= 0) {
- return;
- }
-
- if (!android::base::WriteFully(STDOUT_FILENO, buf, rc)) {
- return;
- }
- }
- });
-}
-
int main(int argc, char* argv[]) {
if (argc <= 1) usage(0);
if (argc > 3) usage(1);
@@ -107,14 +91,11 @@
}
}
- unique_fd piperead, pipewrite;
- if (!Pipe(&piperead, &pipewrite)) {
- err(1, "failed to create pipe");
+ unique_fd output_fd(fcntl(STDOUT_FILENO, F_DUPFD_CLOEXEC, 0));
+ if (output_fd.get() == -1) {
+ err(1, "failed to fcntl dup stdout");
}
-
- std::thread redirect_thread = spawn_redirect_thread(std::move(piperead));
- if (!debuggerd_trigger_dump(proc_info.pid, dump_type, 0, std::move(pipewrite))) {
- redirect_thread.join();
+ if (!debuggerd_trigger_dump(proc_info.pid, dump_type, 0, std::move(output_fd))) {
if (pid == proc_info.pid) {
errx(1, "failed to dump process %d", pid);
} else {
@@ -122,6 +103,5 @@
}
}
- redirect_thread.join();
return 0;
}
diff --git a/debuggerd/debuggerd_test.cpp b/debuggerd/debuggerd_test.cpp
index 4cd6193..52c1c25 100644
--- a/debuggerd/debuggerd_test.cpp
+++ b/debuggerd/debuggerd_test.cpp
@@ -300,24 +300,7 @@
}
static void ConsumeFd(unique_fd fd, std::string* output) {
- constexpr size_t read_length = PAGE_SIZE;
- std::string result;
-
- while (true) {
- size_t offset = result.size();
- result.resize(result.size() + PAGE_SIZE);
- ssize_t rc = TEMP_FAILURE_RETRY(read(fd.get(), &result[offset], read_length));
- if (rc == -1) {
- FAIL() << "read failed: " << strerror(errno);
- } else if (rc == 0) {
- result.resize(result.size() - PAGE_SIZE);
- break;
- }
-
- result.resize(result.size() - PAGE_SIZE + rc);
- }
-
- *output = std::move(result);
+ ASSERT_TRUE(android::base::ReadFdToString(fd, output));
}
class LogcatCollector {
diff --git a/debuggerd/handler/debuggerd_handler.cpp b/debuggerd/handler/debuggerd_handler.cpp
index c6a535a..1e5365d 100644
--- a/debuggerd/handler/debuggerd_handler.cpp
+++ b/debuggerd/handler/debuggerd_handler.cpp
@@ -721,19 +721,19 @@
}
size_t thread_stack_pages = 8;
- void* thread_stack_allocation = mmap(nullptr, PAGE_SIZE * (thread_stack_pages + 2), PROT_NONE,
+ void* thread_stack_allocation = mmap(nullptr, getpagesize() * (thread_stack_pages + 2), PROT_NONE,
MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
if (thread_stack_allocation == MAP_FAILED) {
fatal_errno("failed to allocate debuggerd thread stack");
}
- char* stack = static_cast<char*>(thread_stack_allocation) + PAGE_SIZE;
- if (mprotect(stack, PAGE_SIZE * thread_stack_pages, PROT_READ | PROT_WRITE) != 0) {
+ char* stack = static_cast<char*>(thread_stack_allocation) + getpagesize();
+ if (mprotect(stack, getpagesize() * thread_stack_pages, PROT_READ | PROT_WRITE) != 0) {
fatal_errno("failed to mprotect debuggerd thread stack");
}
// Stack grows negatively, set it to the last byte in the page...
- stack = (stack + thread_stack_pages * PAGE_SIZE - 1);
+ stack = (stack + thread_stack_pages * getpagesize() - 1);
// and align it.
stack -= 15;
pseudothread_stack = stack;
diff --git a/debuggerd/libdebuggerd/scudo.cpp b/debuggerd/libdebuggerd/scudo.cpp
index 5a62fe1..837f406 100644
--- a/debuggerd/libdebuggerd/scudo.cpp
+++ b/debuggerd/libdebuggerd/scudo.cpp
@@ -22,6 +22,7 @@
#include <android-base/macros.h>
#include <bionic/macros.h>
+#include <unistd.h>
#include "tombstone.pb.h"
@@ -54,21 +55,21 @@
}
untagged_fault_addr_ = process_info.untagged_fault_address;
- uintptr_t fault_page = untagged_fault_addr_ & ~(PAGE_SIZE - 1);
+ uintptr_t fault_page = untagged_fault_addr_ & ~(getpagesize() - 1);
- uintptr_t memory_begin = fault_page - PAGE_SIZE * 16;
+ uintptr_t memory_begin = fault_page - getpagesize() * 16;
if (memory_begin > fault_page) {
return;
}
- uintptr_t memory_end = fault_page + PAGE_SIZE * 16;
+ uintptr_t memory_end = fault_page + getpagesize() * 16;
if (memory_end < fault_page) {
return;
}
auto memory = std::make_unique<char[]>(memory_end - memory_begin);
- for (auto i = memory_begin; i != memory_end; i += PAGE_SIZE) {
- process_memory->ReadFully(i, memory.get() + i - memory_begin, PAGE_SIZE);
+ for (auto i = memory_begin; i != memory_end; i += getpagesize()) {
+ process_memory->ReadFully(i, memory.get() + i - memory_begin, getpagesize());
}
auto memory_tags = std::make_unique<char[]>((memory_end - memory_begin) / kTagGranuleSize);
diff --git a/debuggerd/rust/tombstoned_client/src/lib.rs b/debuggerd/rust/tombstoned_client/src/lib.rs
index 5c8abef..d1b5e69 100644
--- a/debuggerd/rust/tombstoned_client/src/lib.rs
+++ b/debuggerd/rust/tombstoned_client/src/lib.rs
@@ -39,20 +39,26 @@
}
impl TombstonedConnection {
+ /// # Safety
+ ///
+ /// The file descriptors must be valid and open.
unsafe fn from_raw_fds(
tombstoned_socket: RawFd,
text_output_fd: RawFd,
proto_output_fd: RawFd,
) -> Self {
Self {
- tombstoned_socket: File::from_raw_fd(tombstoned_socket),
+ // SAFETY: The caller guarantees that the file descriptor is valid and open.
+ tombstoned_socket: unsafe { File::from_raw_fd(tombstoned_socket) },
text_output: if text_output_fd >= 0 {
- Some(File::from_raw_fd(text_output_fd))
+ // SAFETY: The caller guarantees that the file descriptor is valid and open.
+ Some(unsafe { File::from_raw_fd(text_output_fd) })
} else {
None
},
proto_output: if proto_output_fd >= 0 {
- Some(File::from_raw_fd(proto_output_fd))
+ // SAFETY: The caller guarantees that the file descriptor is valid and open.
+ Some(unsafe { File::from_raw_fd(proto_output_fd) })
} else {
None
},
@@ -71,6 +77,8 @@
&mut proto_output_fd,
dump_type,
) {
+ // SAFETY: If tombstoned_connect_files returns successfully then they file descriptors
+ // are valid and open.
Ok(unsafe { Self::from_raw_fds(tombstoned_socket, text_output_fd, proto_output_fd) })
} else {
Err(Error)
@@ -146,8 +154,6 @@
.write_all(b"test data")
.expect("Failed to write to text output FD.");
- connection
- .notify_completion()
- .expect("Failed to notify completion.");
+ connection.notify_completion().expect("Failed to notify completion.");
}
}
diff --git a/debuggerd/seccomp_policy/crash_dump.riscv64.policy b/debuggerd/seccomp_policy/crash_dump.riscv64.policy
index 21887ab..281e231 100644
--- a/debuggerd/seccomp_policy/crash_dump.riscv64.policy
+++ b/debuggerd/seccomp_policy/crash_dump.riscv64.policy
@@ -19,12 +19,13 @@
faccessat: 1
recvmsg: 1
recvfrom: 1
+sysinfo: 1
process_vm_readv: 1
tgkill: 1
rt_sigprocmask: 1
rt_sigaction: 1
rt_tgsigqueueinfo: 1
-prctl: arg0 == PR_GET_NO_NEW_PRIVS || arg0 == 0x53564d41 || arg0 == PR_PAC_RESET_KEYS
+prctl: arg0 == PR_GET_NO_NEW_PRIVS || arg0 == 0x53564d41
madvise: 1
mprotect: arg2 in 0x1|0x2
munmap: 1
diff --git a/fastboot/Android.bp b/fastboot/Android.bp
index 7794c4b..56cac88 100644
--- a/fastboot/Android.bp
+++ b/fastboot/Android.bp
@@ -196,6 +196,7 @@
"libfastbootshim",
"libsnapshot_cow",
"liblz4",
+ "libzstd",
"libsnapshot_nobinder",
"update_metadata-protos",
"liburing",
diff --git a/fastboot/fastboot.cpp b/fastboot/fastboot.cpp
index cdcd036..0bd07ed 100644
--- a/fastboot/fastboot.cpp
+++ b/fastboot/fastboot.cpp
@@ -76,6 +76,7 @@
#include "constants.h"
#include "diagnose_usb.h"
#include "fastboot_driver.h"
+#include "fastboot_driver_interface.h"
#include "fs.h"
#include "storage.h"
#include "super_flash_helper.h"
@@ -95,6 +96,8 @@
using namespace std::string_literals;
using namespace std::placeholders;
+#define FASTBOOT_INFO_VERSION 1
+
static const char* serial = nullptr;
static bool g_long_listing = false;
@@ -102,7 +105,6 @@
// libsparse will support INT_MAX, but this results in large allocations, so
// let's keep it at 1GB to avoid memory pressure on the host.
static constexpr int64_t RESPARSE_LIMIT = 1 * 1024 * 1024 * 1024;
-static uint64_t sparse_limit = 0;
static int64_t target_sparse_limit = -1;
static unsigned g_base_addr = 0x10000000;
@@ -118,6 +120,9 @@
static std::vector<Image> images = {
// clang-format off
{ "boot", "boot.img", "boot.sig", "boot", false, ImageType::BootCritical },
+ { "bootloader",
+ "bootloader.img", "", "bootloader",
+ true, ImageType::Extra },
{ "init_boot",
"init_boot.img", "init_boot.sig",
"init_boot",
@@ -130,6 +135,7 @@
{ "odm_dlkm", "odm_dlkm.img", "odm_dlkm.sig", "odm_dlkm", true, ImageType::Normal },
{ "product", "product.img", "product.sig", "product", true, ImageType::Normal },
{ "pvmfw", "pvmfw.img", "pvmfw.sig", "pvmfw", true, ImageType::BootCritical },
+ { "radio", "radio.img", "", "radio", true, ImageType::Extra },
{ "recovery", "recovery.img", "recovery.sig", "recovery", true, ImageType::BootCritical },
{ "super", "super.img", "super.sig", "super", true, ImageType::Extra },
{ "system", "system.img", "system.sig", "system", false, ImageType::Normal },
@@ -172,7 +178,7 @@
// clang-format on
};
-static char* get_android_product_out() {
+char* get_android_product_out() {
char* dir = getenv("ANDROID_PRODUCT_OUT");
if (dir == nullptr || dir[0] == '\0') {
return nullptr;
@@ -394,7 +400,7 @@
ConnectedDevicesStorage storage;
std::set<std::string> devices;
- {
+ if (storage.Exists()) {
FileLock lock = storage.Lock();
devices = storage.ReadDevices(lock);
}
@@ -628,6 +634,9 @@
" --skip-reboot Don't reboot device after flashing.\n"
" --disable-verity Sets disable-verity when flashing vbmeta.\n"
" --disable-verification Sets disable-verification when flashing vbmeta.\n"
+ " --disable-super-optimization\n"
+ " Disables optimizations on flashing super partition.\n"
+ " --disable-fastboot-info Will collects tasks from image list rather than $OUT/fastboot-info.txt.\n"
" --fs-options=OPTION[,OPTION]\n"
" Enable filesystem features. OPTION supports casefold, projid, compress\n"
// TODO: remove --unbuffered?
@@ -773,7 +782,7 @@
#endif
-static unique_fd unzip_to_file(ZipArchiveHandle zip, const char* entry_name) {
+static unique_fd UnzipToFile(ZipArchiveHandle zip, const char* entry_name) {
unique_fd fd(make_temporary_fd(entry_name));
ZipEntry64 zip_entry;
@@ -995,7 +1004,7 @@
return resparse_file(s.get(), max_size);
}
-static uint64_t get_uint_var(const char* var_name) {
+static uint64_t get_uint_var(const char* var_name, fastboot::IFastBootDriver* fb) {
std::string value_str;
if (fb->GetVar(var_name, &value_str) != fastboot::SUCCESS || value_str.empty()) {
verbose("target didn't report %s", var_name);
@@ -1014,13 +1023,13 @@
return value;
}
-int64_t get_sparse_limit(int64_t size) {
- int64_t limit = sparse_limit;
+int64_t get_sparse_limit(int64_t size, const FlashingPlan* fp) {
+ int64_t limit = int64_t(fp->sparse_limit);
if (limit == 0) {
// Unlimited, so see what the target device's limit is.
// TODO: shouldn't we apply this limit even if you've used -S?
if (target_sparse_limit == -1) {
- target_sparse_limit = static_cast<int64_t>(get_uint_var("max-download-size"));
+ target_sparse_limit = static_cast<int64_t>(get_uint_var("max-download-size", fp->fb));
}
if (target_sparse_limit > 0) {
limit = target_sparse_limit;
@@ -1036,7 +1045,7 @@
return 0;
}
-static bool load_buf_fd(unique_fd fd, struct fastboot_buffer* buf) {
+static bool load_buf_fd(unique_fd fd, struct fastboot_buffer* buf, const FlashingPlan* fp) {
int64_t sz = get_file_size(fd);
if (sz == -1) {
return false;
@@ -1054,7 +1063,7 @@
}
lseek(fd.get(), 0, SEEK_SET);
- int64_t limit = get_sparse_limit(sz);
+ int64_t limit = get_sparse_limit(sz, fp);
buf->fd = std::move(fd);
if (limit) {
buf->files = load_sparse_files(buf->fd.get(), limit);
@@ -1070,13 +1079,11 @@
return true;
}
-static bool load_buf(const char* fname, struct fastboot_buffer* buf) {
+static bool load_buf(const char* fname, struct fastboot_buffer* buf, const FlashingPlan* fp) {
unique_fd fd(TEMP_FAILURE_RETRY(open(fname, O_RDONLY | O_BINARY)));
if (fd == -1) {
- auto path = find_item_given_name(fname);
- fd = unique_fd(TEMP_FAILURE_RETRY(open(path.c_str(), O_RDONLY | O_BINARY)));
- if (fd == -1) return false;
+ return false;
}
struct stat s;
@@ -1088,7 +1095,7 @@
return false;
}
- return load_buf_fd(std::move(fd), buf);
+ return load_buf_fd(std::move(fd), buf, fp);
}
static void rewrite_vbmeta_buffer(struct fastboot_buffer* buf, bool vbmeta_in_boot) {
@@ -1411,7 +1418,7 @@
}
}
-bool is_retrofit_device() {
+bool is_retrofit_device(fastboot::IFastBootDriver* fb) {
std::string value;
if (fb->GetVar("super-partition-name", &value) != fastboot::SUCCESS) {
return false;
@@ -1421,8 +1428,9 @@
// Fetch a partition from the device to a given fd. This is a wrapper over FetchToFd to fetch
// the full image.
-static uint64_t fetch_partition(const std::string& partition, borrowed_fd fd) {
- uint64_t fetch_size = get_uint_var(FB_VAR_MAX_FETCH_SIZE);
+static uint64_t fetch_partition(const std::string& partition, borrowed_fd fd,
+ fastboot::IFastBootDriver* fb) {
+ uint64_t fetch_size = get_uint_var(FB_VAR_MAX_FETCH_SIZE, fb);
if (fetch_size == 0) {
die("Unable to get %s. Device does not support fetch command.", FB_VAR_MAX_FETCH_SIZE);
}
@@ -1444,17 +1452,18 @@
}
static void do_fetch(const std::string& partition, const std::string& slot_override,
- const std::string& outfile) {
+ const std::string& outfile, fastboot::IFastBootDriver* fb) {
unique_fd fd(TEMP_FAILURE_RETRY(
open(outfile.c_str(), O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC | O_BINARY, 0644)));
- auto fetch = std::bind(fetch_partition, _1, borrowed_fd(fd));
+ auto fetch = std::bind(fetch_partition, _1, borrowed_fd(fd), fb);
do_for_partitions(partition, slot_override, fetch, false /* force slot */);
}
// Return immediately if not flashing a vendor boot image. If flashing a vendor boot image,
// repack vendor_boot image with an updated ramdisk. After execution, buf is set
// to the new image to flash, and return value is the real partition name to flash.
-static std::string repack_ramdisk(const char* pname, struct fastboot_buffer* buf) {
+static std::string repack_ramdisk(const char* pname, struct fastboot_buffer* buf,
+ fastboot::IFastBootDriver* fb) {
std::string_view pname_sv{pname};
if (!android::base::StartsWith(pname_sv, "vendor_boot:") &&
@@ -1472,7 +1481,7 @@
std::string ramdisk(pname_sv.substr(pname_sv.find(':') + 1));
unique_fd vendor_boot(make_temporary_fd("vendor boot repack"));
- uint64_t vendor_boot_size = fetch_partition(partition, vendor_boot);
+ uint64_t vendor_boot_size = fetch_partition(partition, vendor_boot, fb);
auto repack_res = replace_vendor_ramdisk(vendor_boot, vendor_boot_size, ramdisk, buf->fd,
static_cast<uint64_t>(buf->sz));
if (!repack_res.ok()) {
@@ -1485,17 +1494,34 @@
return partition;
}
-void do_flash(const char* pname, const char* fname, const bool apply_vbmeta) {
+void do_flash(const char* pname, const char* fname, const bool apply_vbmeta,
+ const FlashingPlan* fp) {
+ if (!fp) {
+ die("do flash was called without a valid flashing plan");
+ }
verbose("Do flash %s %s", pname, fname);
struct fastboot_buffer buf;
- if (!load_buf(fname, &buf)) {
+ if (fp->source) {
+ unique_fd fd = fp->source->OpenFile(fname);
+ if (fd < 0 || !load_buf_fd(std::move(fd), &buf, fp)) {
+ die("could not load '%s': %s", fname, strerror(errno));
+ }
+ std::vector<char> signature_data;
+ std::string file_string(fname);
+ if (fp->source->ReadFile(file_string.substr(0, file_string.find('.')) + ".sig",
+ &signature_data)) {
+ fb->Download("signature", signature_data);
+ fb->RawCommand("signature", "installing signature");
+ }
+ } else if (!load_buf(fname, &buf, fp)) {
die("cannot load '%s': %s", fname, strerror(errno));
}
+
if (is_logical(pname)) {
fb->ResizePartition(pname, std::to_string(buf.image_size));
}
- std::string flash_pname = repack_ramdisk(pname, &buf);
+ std::string flash_pname = repack_ramdisk(pname, &buf, fp->fb);
flash_buf(flash_pname, &buf, apply_vbmeta);
}
@@ -1590,7 +1616,7 @@
if (img_name.empty()) {
img_name = partition + ".img";
}
- return std::make_unique<FlashTask>(slot, partition, img_name, apply_vbmeta);
+ return std::make_unique<FlashTask>(slot, partition, img_name, apply_vbmeta, fp);
}
std::unique_ptr<RebootTask> ParseRebootCommand(const FlashingPlan* fp,
@@ -1637,14 +1663,22 @@
return task;
}
-void AddResizeTasks(const FlashingPlan* fp, std::vector<std::unique_ptr<Task>>* tasks) {
+bool AddResizeTasks(const FlashingPlan* fp, std::vector<std::unique_ptr<Task>>* tasks) {
// expands "resize-partitions" into individual commands : resize {os_partition_1}, resize
// {os_partition_2}, etc.
std::vector<std::unique_ptr<Task>> resize_tasks;
std::optional<size_t> loc;
+ std::vector<char> contents;
+ if (!fp->source->ReadFile("super_empty.img", &contents)) {
+ return false;
+ }
+ auto metadata = android::fs_mgr::ReadFromImageBlob(contents.data(), contents.size());
+ if (!metadata) {
+ return false;
+ }
for (size_t i = 0; i < tasks->size(); i++) {
if (auto flash_task = tasks->at(i)->AsFlashTask()) {
- if (should_flash_in_userspace(flash_task->GetPartitionAndSlot())) {
+ if (should_flash_in_userspace(*metadata.get(), flash_task->GetPartitionAndSlot())) {
if (!loc) {
loc = i;
}
@@ -1656,35 +1690,22 @@
// if no logical partitions (although should never happen since system will always need to be
// flashed)
if (!loc) {
- return;
+ return false;
}
tasks->insert(tasks->begin() + loc.value(), std::make_move_iterator(resize_tasks.begin()),
std::make_move_iterator(resize_tasks.end()));
- return;
-}
-
-static bool IsNumber(const std::string& s) {
- bool period = false;
- for (size_t i = 0; i < s.length(); i++) {
- if (!isdigit(s[i])) {
- if (!period && s[i] == '.' && i != 0 && i != s.length() - 1) {
- period = true;
- } else {
- return false;
- }
- }
- }
return true;
}
static bool IsIgnore(const std::vector<std::string>& command) {
- if (command[0][0] == '#') {
+ if (command.size() == 0 || command[0][0] == '#') {
return true;
}
return false;
}
-bool CheckFastbootInfoRequirements(const std::vector<std::string>& command) {
+bool CheckFastbootInfoRequirements(const std::vector<std::string>& command,
+ uint32_t host_tool_version) {
if (command.size() != 2) {
LOG(ERROR) << "unknown characters in version info in fastboot-info.txt -> "
<< android::base::Join(command, " ");
@@ -1696,18 +1717,20 @@
return false;
}
- if (!IsNumber(command[1])) {
- LOG(ERROR) << "version number contains non-numeric values in fastboot-info.txt -> "
+ uint32_t fastboot_info_version;
+ if (!android::base::ParseUint(command[1], &fastboot_info_version)) {
+ LOG(ERROR) << "version number contains non-numeric characters in fastboot-info.txt -> "
<< android::base::Join(command, " ");
return false;
}
LOG(VERBOSE) << "Checking 'fastboot-info.txt version'";
- if (command[1] < PLATFORM_TOOLS_VERSION) {
+ if (fastboot_info_version <= host_tool_version) {
return true;
}
+
LOG(ERROR) << "fasboot-info.txt version: " << command[1]
- << " not compatible with host tool version --> " << PLATFORM_TOOLS_VERSION;
+ << " not compatible with host tool version --> " << host_tool_version;
return false;
}
@@ -1721,7 +1744,7 @@
continue;
}
if (command.size() > 1 && command[0] == "version") {
- if (!CheckFastbootInfoRequirements(command)) {
+ if (!CheckFastbootInfoRequirements(command, FASTBOOT_INFO_VERSION)) {
return {};
}
continue;
@@ -1733,13 +1756,11 @@
}
auto task = ParseFastbootInfoLine(fp, command);
if (!task) {
- LOG(ERROR) << "Error when parsing fastboot-info.txt, falling back on Hardcoded list: "
- << text;
return {};
}
tasks.emplace_back(std::move(task));
}
- if (auto flash_super_task = FlashSuperLayoutTask::InitializeFromTasks(fp, tasks)) {
+ if (auto flash_super_task = OptimizedFlashSuperTask::InitializeFromTasks(fp, tasks)) {
auto it = tasks.begin();
for (size_t i = 0; i < tasks.size(); i++) {
if (auto flash_task = tasks[i]->AsFlashTask()) {
@@ -1754,23 +1775,13 @@
}
tasks.insert(it, std::move(flash_super_task));
} else {
- AddResizeTasks(fp, &tasks);
+ if (!AddResizeTasks(fp, &tasks)) {
+ LOG(WARNING) << "Failed to add resize tasks";
+ };
}
return tasks;
}
-std::vector<std::unique_ptr<Task>> ParseFastbootInfo(const FlashingPlan* fp, std::ifstream& fs) {
- if (!fs || fs.eof()) return {};
-
- std::string text;
- std::vector<std::string> file;
- // Get os_partitions that need to be resized
- while (std::getline(fs, text)) {
- file.emplace_back(text);
- }
- return ParseFastbootInfo(fp, file);
-}
-
FlashAllTool::FlashAllTool(FlashingPlan* fp) : fp_(fp) {}
void FlashAllTool::Flash() {
@@ -1786,27 +1797,26 @@
}
DetermineSlot();
- CollectImages();
CancelSnapshotIfNeeded();
- std::string path = find_item_given_name("fastboot-info.txt");
- std::ifstream stream(path);
- std::vector<std::unique_ptr<Task>> tasks = ParseFastbootInfo(fp_, stream);
- if (tasks.empty()) {
- LOG(VERBOSE) << "Flashing from hardcoded images. fastboot-info.txt is empty or does not "
- "exist";
- HardcodedFlash();
- return;
- }
- LOG(VERBOSE) << "Flashing from fastboot-info.txt";
- for (auto& task : tasks) {
+ tasks_ = CollectTasks();
+ for (auto& task : tasks_) {
task->Run();
}
- if (fp_->wants_wipe) {
- // avoid adding duplicate wipe tasks in fastboot main code.
- fp_->wants_wipe = false;
+ return;
+}
+
+std::vector<std::unique_ptr<Task>> FlashAllTool::CollectTasks() {
+ std::vector<std::unique_ptr<Task>> tasks;
+ if (fp_->should_use_fastboot_info) {
+ tasks = CollectTasksFromFastbootInfo();
+
+ } else {
+ tasks = CollectTasksFromImageList();
}
+
+ return tasks;
}
void FlashAllTool::CheckRequirements() {
@@ -1857,15 +1867,13 @@
}
}
-void FlashAllTool::HardcodedFlash() {
+std::vector<std::unique_ptr<Task>> FlashAllTool::CollectTasksFromImageList() {
CollectImages();
// First flash boot partitions. We allow this to happen either in userspace
// or in bootloader fastboot.
- FlashImages(boot_images_);
-
std::vector<std::unique_ptr<Task>> tasks;
-
- if (auto flash_super_task = FlashSuperLayoutTask::Initialize(fp_, os_images_)) {
+ AddFlashTasks(boot_images_, tasks);
+ if (auto flash_super_task = OptimizedFlashSuperTask::Initialize(fp_, os_images_)) {
tasks.emplace_back(std::move(flash_super_task));
} else {
// Sync the super partition. This will reboot to userspace fastboot if needed.
@@ -1877,7 +1885,7 @@
// On these devices, secondary slots must be flashed as physical
// partitions (otherwise they would not mount on first boot). To enforce
// this, we delete any logical partitions for the "other" slot.
- if (is_retrofit_device()) {
+ if (is_retrofit_device(fp_->fb)) {
std::string partition_name = image->part_name + "_"s + slot;
if (image->IsSecondary() && should_flash_in_userspace(partition_name)) {
fp_->fb->DeletePartition(partition_name);
@@ -1887,59 +1895,45 @@
tasks.emplace_back(std::make_unique<ResizeTask>(fp_, image->part_name, "0", slot));
}
}
- for (auto& i : tasks) {
- i->Run();
- }
- FlashImages(os_images_);
+
+ AddFlashTasks(os_images_, tasks);
+ return tasks;
}
-void FlashAllTool::FlashImages(const std::vector<std::pair<const Image*, std::string>>& images) {
+std::vector<std::unique_ptr<Task>> FlashAllTool::CollectTasksFromFastbootInfo() {
+ std::vector<std::unique_ptr<Task>> tasks;
+ std::vector<char> contents;
+ if (!fp_->source->ReadFile("fastboot-info.txt", &contents)) {
+ LOG(VERBOSE) << "Flashing from hardcoded images. fastboot-info.txt is empty or does not "
+ "exist";
+ return CollectTasksFromImageList();
+ }
+ tasks = ParseFastbootInfo(fp_, Split({contents.data(), contents.size()}, "\n"));
+ return tasks;
+}
+
+void FlashAllTool::AddFlashTasks(const std::vector<std::pair<const Image*, std::string>>& images,
+ std::vector<std::unique_ptr<Task>>& tasks) {
for (const auto& [image, slot] : images) {
fastboot_buffer buf;
unique_fd fd = fp_->source->OpenFile(image->img_name);
- if (fd < 0 || !load_buf_fd(std::move(fd), &buf)) {
+ if (fd < 0 || !load_buf_fd(std::move(fd), &buf, fp_)) {
if (image->optional_if_no_image) {
continue;
}
die("could not load '%s': %s", image->img_name.c_str(), strerror(errno));
}
- FlashImage(*image, slot, &buf);
+ tasks.emplace_back(std::make_unique<FlashTask>(slot, image->part_name, image->img_name,
+ is_vbmeta_partition(image->part_name), fp_));
}
}
-void FlashAllTool::FlashImage(const Image& image, const std::string& slot, fastboot_buffer* buf) {
- auto flash = [&, this](const std::string& partition_name) {
- std::vector<char> signature_data;
- if (fp_->source->ReadFile(image.sig_name, &signature_data)) {
- fb->Download("signature", signature_data);
- fb->RawCommand("signature", "installing signature");
- }
-
- if (is_logical(partition_name)) {
- fb->ResizePartition(partition_name, std::to_string(buf->image_size));
- }
-
- flash_buf(partition_name.c_str(), buf, is_vbmeta_partition(partition_name));
- };
- do_for_partitions(image.part_name, slot, flash, false);
-}
-
-class ZipImageSource final : public ImageSource {
- public:
- explicit ZipImageSource(ZipArchiveHandle zip) : zip_(zip) {}
- bool ReadFile(const std::string& name, std::vector<char>* out) const override;
- unique_fd OpenFile(const std::string& name) const override;
-
- private:
- ZipArchiveHandle zip_;
-};
-
bool ZipImageSource::ReadFile(const std::string& name, std::vector<char>* out) const {
return UnzipToMemory(zip_, name, out);
}
unique_fd ZipImageSource::OpenFile(const std::string& name) const {
- return unzip_to_file(zip_, name.c_str());
+ return UnzipToFile(zip_, name.c_str());
}
static void do_update(const char* filename, FlashingPlan* fp) {
@@ -1957,12 +1951,6 @@
CloseArchive(zip);
}
-class LocalImageSource final : public ImageSource {
- public:
- bool ReadFile(const std::string& name, std::vector<char>* out) const override;
- unique_fd OpenFile(const std::string& name) const override;
-};
-
bool LocalImageSource::ReadFile(const std::string& name, std::vector<char>* out) const {
auto path = find_item_given_name(name);
if (path.empty()) {
@@ -2022,7 +2010,7 @@
void fb_perform_format(const std::string& partition, int skip_if_not_supported,
const std::string& type_override, const std::string& size_override,
- const unsigned fs_options) {
+ const unsigned fs_options, const FlashingPlan* fp) {
std::string partition_type, partition_size;
struct fastboot_buffer buf;
@@ -2035,8 +2023,8 @@
if (target_sparse_limit > 0 && target_sparse_limit < limit) {
limit = target_sparse_limit;
}
- if (sparse_limit > 0 && sparse_limit < limit) {
- limit = sparse_limit;
+ if (fp->sparse_limit > 0 && fp->sparse_limit < limit) {
+ limit = fp->sparse_limit;
}
if (fb->GetVar("partition-type:" + partition, &partition_type) != fastboot::SUCCESS) {
@@ -2091,7 +2079,7 @@
if (fd == -1) {
die("Cannot open generated image: %s", strerror(errno));
}
- if (!load_buf_fd(std::move(fd), &buf)) {
+ if (!load_buf_fd(std::move(fd), &buf, fp)) {
die("Cannot read image: %s", strerror(errno));
}
flash_buf(partition, &buf, is_vbmeta_partition(partition));
@@ -2124,7 +2112,7 @@
}
static bool wipe_super(const android::fs_mgr::LpMetadata& metadata, const std::string& slot,
- std::string* message) {
+ std::string* message, const FlashingPlan* fp) {
auto super_device = GetMetadataSuperBlockDevice(metadata);
auto block_size = metadata.geometry.logical_block_size;
auto super_bdev_name = android::fs_mgr::GetBlockDevicePartitionName(*super_device);
@@ -2164,7 +2152,7 @@
auto image_path = temp_dir.path + "/"s + image_name;
auto flash = [&](const std::string& partition_name) {
- do_flash(partition_name.c_str(), image_path.c_str(), false);
+ do_flash(partition_name.c_str(), image_path.c_str(), false, fp);
};
do_for_partitions(partition, slot, flash, force_slot);
@@ -2173,7 +2161,8 @@
return true;
}
-static void do_wipe_super(const std::string& image, const std::string& slot_override) {
+static void do_wipe_super(const std::string& image, const std::string& slot_override,
+ const FlashingPlan* fp) {
if (access(image.c_str(), R_OK) != 0) {
die("Could not read image: %s", image.c_str());
}
@@ -2188,7 +2177,7 @@
}
std::string message;
- if (!wipe_super(*metadata.get(), slot, &message)) {
+ if (!wipe_super(*metadata.get(), slot, &message, fp)) {
die(message);
}
}
@@ -2230,6 +2219,8 @@
{"cmdline", required_argument, 0, 0},
{"disable-verification", no_argument, 0, 0},
{"disable-verity", no_argument, 0, 0},
+ {"disable-super-optimization", no_argument, 0, 0},
+ {"disable-fastboot-info", no_argument, 0, 0},
{"force", no_argument, 0, 0},
{"fs-options", required_argument, 0, 0},
{"header-version", required_argument, 0, 0},
@@ -2251,7 +2242,10 @@
{"version", no_argument, 0, 0},
{0, 0, 0, 0}};
- serial = getenv("ANDROID_SERIAL");
+ serial = getenv("FASTBOOT_DEVICE");
+ if (!serial) {
+ serial = getenv("ANDROID_SERIAL");
+ }
int c;
while ((c = getopt_long(argc, argv, "a::hls:S:vw", longopts, &longindex)) != -1) {
@@ -2265,6 +2259,10 @@
g_disable_verification = true;
} else if (name == "disable-verity") {
g_disable_verity = true;
+ } else if (name == "disable-super-optimization") {
+ fp->should_optimize_flash_super = false;
+ } else if (name == "disable-fastboot-info") {
+ fp->should_use_fastboot_info = false;
} else if (name == "force") {
fp->force_flash = true;
} else if (name == "fs-options") {
@@ -2320,7 +2318,7 @@
serial = optarg;
break;
case 'S':
- if (!android::base::ParseByteCount(optarg, &sparse_limit)) {
+ if (!android::base::ParseByteCount(optarg, &fp->sparse_limit)) {
die("invalid sparse limit %s", optarg);
}
break;
@@ -2438,7 +2436,8 @@
std::string partition = next_arg(&args);
auto format = [&](const std::string& partition) {
- fb_perform_format(partition, 0, type_override, size_override, fp->fs_options);
+ fb_perform_format(partition, 0, type_override, size_override, fp->fs_options,
+ fp.get());
};
do_for_partitions(partition, fp->slot_override, format, true);
} else if (command == "signature") {
@@ -2485,7 +2484,7 @@
}
if (fname.empty()) die("cannot determine image filename for '%s'", pname.c_str());
- FlashTask task(fp->slot_override, pname, fname, is_vbmeta_partition(pname));
+ FlashTask task(fp->slot_override, pname, fname, is_vbmeta_partition(pname), fp.get());
task.Run();
} else if (command == "flash:raw") {
std::string partition = next_arg(&args);
@@ -2532,7 +2531,7 @@
std::string filename = next_arg(&args);
struct fastboot_buffer buf;
- if (!load_buf(filename.c_str(), &buf) || buf.type != FB_BUFFER_FD) {
+ if (!load_buf(filename.c_str(), &buf, fp.get()) || buf.type != FB_BUFFER_FD) {
die("cannot load '%s'", filename.c_str());
}
fb->Download(filename, buf.fd.get(), buf.sz);
@@ -2580,7 +2579,7 @@
} else {
image = next_arg(&args);
}
- do_wipe_super(image, fp->slot_override);
+ do_wipe_super(image, fp->slot_override, fp.get());
} else if (command == "snapshot-update") {
std::string arg;
if (!args.empty()) {
@@ -2593,7 +2592,7 @@
} else if (command == FB_CMD_FETCH) {
std::string partition = next_arg(&args);
std::string outfile = next_arg(&args);
- do_fetch(partition, fp->slot_override, outfile);
+ do_fetch(partition, fp->slot_override, outfile, fp->fb);
} else {
syntax_error("unknown command %s", command.c_str());
}
@@ -2603,10 +2602,13 @@
if (fp->force_flash) {
CancelSnapshotIfNeeded();
}
+ std::vector<std::unique_ptr<Task>> wipe_tasks;
std::vector<std::string> partitions = {"userdata", "cache", "metadata"};
for (const auto& partition : partitions) {
- tasks.emplace_back(std::make_unique<WipeTask>(fp.get(), partition));
+ wipe_tasks.emplace_back(std::make_unique<WipeTask>(fp.get(), partition));
}
+ tasks.insert(tasks.begin(), std::make_move_iterator(wipe_tasks.begin()),
+ std::make_move_iterator(wipe_tasks.end()));
}
if (fp->wants_set_active) {
fb->SetActive(next_active);
diff --git a/fastboot/fastboot.h b/fastboot/fastboot.h
index d6afb9e..dc57149 100644
--- a/fastboot/fastboot.h
+++ b/fastboot/fastboot.h
@@ -27,6 +27,7 @@
*/
#pragma once
+#include <functional>
#include <string>
#include "fastboot_driver.h"
#include "fastboot_driver_interface.h"
@@ -40,6 +41,7 @@
#include "result.h"
#include "socket.h"
#include "util.h"
+#include "ziparchive/zip_archive.h"
class FastBootTool {
public:
@@ -95,6 +97,9 @@
bool wants_set_active = false;
bool skip_secondary = false;
bool force_flash = false;
+ bool should_optimize_flash_super = true;
+ bool should_use_fastboot_info = true;
+ uint64_t sparse_limit = 0;
std::string slot_override;
std::string current_slot;
@@ -108,23 +113,46 @@
FlashAllTool(FlashingPlan* fp);
void Flash();
+ std::vector<std::unique_ptr<Task>> CollectTasks();
private:
void CheckRequirements();
void DetermineSlot();
void CollectImages();
- void FlashImages(const std::vector<std::pair<const Image*, std::string>>& images);
- void FlashImage(const Image& image, const std::string& slot, fastboot_buffer* buf);
- void HardcodedFlash();
+ void AddFlashTasks(const std::vector<std::pair<const Image*, std::string>>& images,
+ std::vector<std::unique_ptr<Task>>& tasks);
+
+ std::vector<std::unique_ptr<Task>> CollectTasksFromFastbootInfo();
+ std::vector<std::unique_ptr<Task>> CollectTasksFromImageList();
std::vector<ImageEntry> boot_images_;
std::vector<ImageEntry> os_images_;
+ std::vector<std::unique_ptr<Task>> tasks_;
+
FlashingPlan* fp_;
};
+class ZipImageSource final : public ImageSource {
+ public:
+ explicit ZipImageSource(ZipArchiveHandle zip) : zip_(zip) {}
+ bool ReadFile(const std::string& name, std::vector<char>* out) const override;
+ unique_fd OpenFile(const std::string& name) const override;
+
+ private:
+ ZipArchiveHandle zip_;
+};
+
+class LocalImageSource final : public ImageSource {
+ public:
+ bool ReadFile(const std::string& name, std::vector<char>* out) const override;
+ unique_fd OpenFile(const std::string& name) const override;
+};
+
+char* get_android_product_out();
bool should_flash_in_userspace(const std::string& partition_name);
bool is_userspace_fastboot();
-void do_flash(const char* pname, const char* fname, const bool apply_vbmeta);
+void do_flash(const char* pname, const char* fname, const bool apply_vbmeta,
+ const FlashingPlan* fp);
void do_for_partitions(const std::string& part, const std::string& slot,
const std::function<void(const std::string&)>& func, bool force_slot);
std::string find_item(const std::string& item);
@@ -133,7 +161,8 @@
std::string get_current_slot();
// Code for Parsing fastboot-info.txt
-bool CheckFastbootInfoRequirements(const std::vector<std::string>& command);
+bool CheckFastbootInfoRequirements(const std::vector<std::string>& command,
+ uint32_t host_tool_version);
std::unique_ptr<FlashTask> ParseFlashCommand(const FlashingPlan* fp,
const std::vector<std::string>& parts);
std::unique_ptr<RebootTask> ParseRebootCommand(const FlashingPlan* fp,
@@ -142,7 +171,7 @@
const std::vector<std::string>& parts);
std::unique_ptr<Task> ParseFastbootInfoLine(const FlashingPlan* fp,
const std::vector<std::string>& command);
-void AddResizeTasks(const FlashingPlan* fp, std::vector<std::unique_ptr<Task>>& tasks);
+bool AddResizeTasks(const FlashingPlan* fp, std::vector<std::unique_ptr<Task>>& tasks);
std::vector<std::unique_ptr<Task>> ParseFastbootInfo(const FlashingPlan* fp,
const std::vector<std::string>& file);
@@ -156,11 +185,11 @@
bool supports_AB();
std::string GetPartitionName(const ImageEntry& entry, const std::string& current_slot_);
void flash_partition_files(const std::string& partition, const std::vector<SparsePtr>& files);
-int64_t get_sparse_limit(int64_t size);
+int64_t get_sparse_limit(int64_t size, const FlashingPlan* fp);
std::vector<SparsePtr> resparse_file(sparse_file* s, int64_t max_size);
-bool is_retrofit_device();
+bool is_retrofit_device(fastboot::IFastBootDriver* fb);
bool is_logical(const std::string& partition);
void fb_perform_format(const std::string& partition, int skip_if_not_supported,
const std::string& type_override, const std::string& size_override,
- const unsigned fs_options);
+ const unsigned fs_options, const FlashingPlan* fp);
diff --git a/fastboot/fastboot_driver.h b/fastboot/fastboot_driver.h
index 3d6c7b0..8774ead 100644
--- a/fastboot/fastboot_driver.h
+++ b/fastboot/fastboot_driver.h
@@ -28,6 +28,7 @@
#pragma once
#include <cstdlib>
#include <deque>
+#include <functional>
#include <limits>
#include <string>
#include <vector>
@@ -104,7 +105,7 @@
std::vector<std::string>* info = nullptr);
RetCode FetchToFd(const std::string& partition, android::base::borrowed_fd fd,
int64_t offset = -1, int64_t size = -1, std::string* response = nullptr,
- std::vector<std::string>* info = nullptr);
+ std::vector<std::string>* info = nullptr) override;
/* HIGHER LEVEL COMMANDS -- Composed of the commands above */
RetCode FlashPartition(const std::string& partition, const std::vector<char>& data);
diff --git a/fastboot/fastboot_driver_interface.h b/fastboot/fastboot_driver_interface.h
index 795938f..7cb8a6b 100644
--- a/fastboot/fastboot_driver_interface.h
+++ b/fastboot/fastboot_driver_interface.h
@@ -45,6 +45,10 @@
std::vector<std::string>* info = nullptr) = 0;
RetCode virtual GetVar(const std::string& key, std::string* val,
std::vector<std::string>* info = nullptr) = 0;
+ RetCode virtual FetchToFd(const std::string& partition, android::base::borrowed_fd fd,
+ int64_t offset = -1, int64_t size = -1,
+ std::string* response = nullptr,
+ std::vector<std::string>* info = nullptr) = 0;
RetCode virtual Download(const std::string& name, android::base::borrowed_fd fd, size_t size,
std::string* response = nullptr,
std::vector<std::string>* info = nullptr) = 0;
diff --git a/fastboot/fastboot_driver_mock.h b/fastboot/fastboot_driver_mock.h
index 62a5708..7c41d78 100644
--- a/fastboot/fastboot_driver_mock.h
+++ b/fastboot/fastboot_driver_mock.h
@@ -22,22 +22,22 @@
class MockFastbootDriver : public IFastBootDriver {
public:
- MOCK_METHOD(RetCode, FlashPartition,
- (const std::string& partition, android::base::borrowed_fd fd, uint32_t sz),
+ MOCK_METHOD(RetCode, FlashPartition, (const std::string&, android::base::borrowed_fd, uint32_t),
(override));
MOCK_METHOD(RetCode, DeletePartition, (const std::string&), (override));
MOCK_METHOD(RetCode, Reboot, (std::string*, std::vector<std::string>*), (override));
MOCK_METHOD(RetCode, RebootTo, (std::string, std::string*, std::vector<std::string>*),
(override));
-
MOCK_METHOD(RetCode, GetVar, (const std::string&, std::string*, std::vector<std::string>*),
(override));
-
+ MOCK_METHOD(RetCode, FetchToFd,
+ (const std::string&, android::base::borrowed_fd, int64_t offset, int64_t size,
+ std::string*, std::vector<std::string>*),
+ (override));
MOCK_METHOD(RetCode, Download,
(const std::string&, android::base::borrowed_fd, size_t, std::string*,
std::vector<std::string>*),
(override));
-
MOCK_METHOD(RetCode, RawCommand,
(const std::string&, const std::string&, std::string*, std::vector<std::string>*,
int*),
diff --git a/fastboot/filesystem.h b/fastboot/filesystem.h
index 5f41fbc..c5f9c94 100644
--- a/fastboot/filesystem.h
+++ b/fastboot/filesystem.h
@@ -31,6 +31,7 @@
#endif
std::string GetHomeDirPath();
+bool FileExists(const std::string& path);
bool EnsureDirectoryExists(const std::string& directory_path);
class FileLock {
diff --git a/fastboot/storage.cpp b/fastboot/storage.cpp
index d6e00cf..629ebc8 100644
--- a/fastboot/storage.cpp
+++ b/fastboot/storage.cpp
@@ -18,29 +18,25 @@
#include <android-base/logging.h>
#include <fstream>
+#include <iterator>
#include "storage.h"
#include "util.h"
ConnectedDevicesStorage::ConnectedDevicesStorage() {
- const std::string home_path = GetHomeDirPath();
- if (home_path.empty()) {
- return;
- }
-
- const std::string home_fastboot_path = home_path + kPathSeparator + ".fastboot";
-
- if (!EnsureDirectoryExists(home_fastboot_path)) {
- LOG(FATAL) << "Cannot create directory: " << home_fastboot_path;
- }
+ home_fastboot_path_ = GetHomeDirPath() + kPathSeparator + ".fastboot";
+ devices_path_ = home_fastboot_path_ + kPathSeparator + "devices";
// We're using a separate file for locking because the Windows LockFileEx does not
// permit opening a file stream for the locked file, even within the same process. So,
// we have to use fd or handle API to manipulate the storage files, which makes it
// nearly impossible to fully rewrite a file content without having to recreate it.
// Unfortunately, this is not an option during holding a lock.
- devices_path_ = home_fastboot_path + kPathSeparator + "devices";
- devices_lock_path_ = home_fastboot_path + kPathSeparator + "devices.lock";
+ devices_lock_path_ = home_fastboot_path_ + kPathSeparator + "devices.lock";
+}
+
+bool ConnectedDevicesStorage::Exists() const {
+ return FileExists(devices_path_);
}
void ConnectedDevicesStorage::WriteDevices(const FileLock&, const std::set<std::string>& devices) {
@@ -63,5 +59,8 @@
}
FileLock ConnectedDevicesStorage::Lock() const {
+ if (!EnsureDirectoryExists(home_fastboot_path_)) {
+ LOG(FATAL) << "Cannot create directory: " << home_fastboot_path_;
+ }
return FileLock(devices_lock_path_);
-}
\ No newline at end of file
+}
diff --git a/fastboot/storage.h b/fastboot/storage.h
index 0cc3d86..ae9d846 100644
--- a/fastboot/storage.h
+++ b/fastboot/storage.h
@@ -24,13 +24,15 @@
class ConnectedDevicesStorage {
public:
ConnectedDevicesStorage();
+
+ bool Exists() const;
void WriteDevices(const FileLock&, const std::set<std::string>& devices);
std::set<std::string> ReadDevices(const FileLock&);
void Clear(const FileLock&);
-
FileLock Lock() const;
private:
+ std::string home_fastboot_path_;
std::string devices_path_;
std::string devices_lock_path_;
};
\ No newline at end of file
diff --git a/fastboot/task.cpp b/fastboot/task.cpp
index 054c1ed..bf64f0e 100644
--- a/fastboot/task.cpp
+++ b/fastboot/task.cpp
@@ -26,9 +26,9 @@
#include "util.h"
using namespace std::string_literals;
-FlashTask::FlashTask(const std::string& _slot, const std::string& _pname, const std::string& _fname,
- const bool apply_vbmeta)
- : pname_(_pname), fname_(_fname), slot_(_slot), apply_vbmeta_(apply_vbmeta) {}
+FlashTask::FlashTask(const std::string& slot, const std::string& pname, const std::string& fname,
+ const bool apply_vbmeta, const FlashingPlan* fp)
+ : pname_(pname), fname_(fname), slot_(slot), apply_vbmeta_(apply_vbmeta), fp_(fp) {}
void FlashTask::Run() {
auto flash = [&](const std::string& partition) {
@@ -41,11 +41,19 @@
"And try again. If you are intentionally trying to "
"overwrite a fixed partition, use --force.");
}
- do_flash(partition.c_str(), fname_.c_str(), apply_vbmeta_);
+ do_flash(partition.c_str(), fname_.c_str(), apply_vbmeta_, fp_);
};
do_for_partitions(pname_, slot_, flash, true);
}
+std::string FlashTask::ToString() {
+ std::string apply_vbmeta_string = "";
+ if (apply_vbmeta_) {
+ apply_vbmeta_string = " --apply_vbmeta";
+ }
+ return "flash" + apply_vbmeta_string + " " + pname_ + " " + fname_;
+}
+
std::string FlashTask::GetPartitionAndSlot() {
auto slot = slot_;
if (slot.empty()) {
@@ -65,7 +73,7 @@
: reboot_target_(reboot_target), fp_(fp){};
void RebootTask::Run() {
- if ((reboot_target_ == "userspace" || reboot_target_ == "fastboot")) {
+ if (reboot_target_ == "fastboot") {
if (!is_userspace_fastboot()) {
reboot_to_userspace_fastboot();
fp_->fb->WaitForDisconnect();
@@ -84,20 +92,26 @@
}
}
-FlashSuperLayoutTask::FlashSuperLayoutTask(const std::string& super_name,
- std::unique_ptr<SuperFlashHelper> helper,
- SparsePtr sparse_layout, uint64_t super_size)
+std::string RebootTask::ToString() {
+ return "reboot " + reboot_target_;
+}
+
+OptimizedFlashSuperTask::OptimizedFlashSuperTask(const std::string& super_name,
+ std::unique_ptr<SuperFlashHelper> helper,
+ SparsePtr sparse_layout, uint64_t super_size,
+ const FlashingPlan* fp)
: super_name_(super_name),
helper_(std::move(helper)),
sparse_layout_(std::move(sparse_layout)),
- super_size_(super_size) {}
+ super_size_(super_size),
+ fp_(fp) {}
-void FlashSuperLayoutTask::Run() {
+void OptimizedFlashSuperTask::Run() {
// Use the reported super partition size as the upper limit, rather than
// sparse_file_len, which (1) can fail and (2) is kind of expensive, since
// it will map in all of the embedded fds.
std::vector<SparsePtr> files;
- if (int limit = get_sparse_limit(super_size_)) {
+ if (int limit = get_sparse_limit(super_size_, fp_)) {
files = resparse_file(sparse_layout_.get(), limit);
} else {
files.emplace_back(std::move(sparse_layout_));
@@ -106,9 +120,16 @@
// Send the data to the device.
flash_partition_files(super_name_, files);
}
+std::string OptimizedFlashSuperTask::ToString() {
+ return "optimized-flash-super";
+}
-std::unique_ptr<FlashSuperLayoutTask> FlashSuperLayoutTask::Initialize(
+std::unique_ptr<OptimizedFlashSuperTask> OptimizedFlashSuperTask::Initialize(
const FlashingPlan* fp, std::vector<ImageEntry>& os_images) {
+ if (!fp->should_optimize_flash_super) {
+ LOG(INFO) << "super optimization is disabled";
+ return nullptr;
+ }
if (!supports_AB()) {
LOG(VERBOSE) << "Cannot optimize flashing super on non-AB device";
return nullptr;
@@ -167,12 +188,16 @@
};
os_images.erase(std::remove_if(os_images.begin(), os_images.end(), remove_if_callback),
os_images.end());
- return std::make_unique<FlashSuperLayoutTask>(super_name, std::move(helper), std::move(s),
- partition_size);
+ return std::make_unique<OptimizedFlashSuperTask>(super_name, std::move(helper), std::move(s),
+ partition_size, fp);
}
-std::unique_ptr<FlashSuperLayoutTask> FlashSuperLayoutTask::InitializeFromTasks(
+std::unique_ptr<OptimizedFlashSuperTask> OptimizedFlashSuperTask::InitializeFromTasks(
const FlashingPlan* fp, std::vector<std::unique_ptr<Task>>& tasks) {
+ if (!fp->should_optimize_flash_super) {
+ LOG(INFO) << "super optimization is disabled";
+ return nullptr;
+ }
if (!supports_AB()) {
LOG(VERBOSE) << "Cannot optimize flashing super on non-AB device";
return nullptr;
@@ -214,11 +239,9 @@
for (const auto& task : tasks) {
if (auto flash_task = task->AsFlashTask()) {
- if (should_flash_in_userspace(flash_task->GetPartitionAndSlot())) {
- auto partition = flash_task->GetPartitionAndSlot();
- if (!helper->AddPartition(partition, flash_task->GetImageName(), false)) {
- return nullptr;
- }
+ auto partition = flash_task->GetPartitionAndSlot();
+ if (!helper->AddPartition(partition, flash_task->GetImageName(), false)) {
+ return nullptr;
}
}
}
@@ -238,8 +261,8 @@
};
tasks.erase(std::remove_if(tasks.begin(), tasks.end(), remove_if_callback), tasks.end());
- return std::make_unique<FlashSuperLayoutTask>(super_name, std::move(helper), std::move(s),
- partition_size);
+ return std::make_unique<OptimizedFlashSuperTask>(super_name, std::move(helper), std::move(s),
+ partition_size, fp);
}
UpdateSuperTask::UpdateSuperTask(const FlashingPlan* fp) : fp_(fp) {}
@@ -265,6 +288,9 @@
}
fp_->fb->RawCommand(command, "Updating super partition");
}
+std::string UpdateSuperTask::ToString() {
+ return "update-super";
+}
ResizeTask::ResizeTask(const FlashingPlan* fp, const std::string& pname, const std::string& size,
const std::string& slot)
@@ -279,12 +305,20 @@
do_for_partitions(pname_, slot_, resize_partition, false);
}
+std::string ResizeTask::ToString() {
+ return "resize " + pname_;
+}
+
DeleteTask::DeleteTask(const FlashingPlan* fp, const std::string& pname) : fp_(fp), pname_(pname){};
void DeleteTask::Run() {
fp_->fb->DeletePartition(pname_);
}
+std::string DeleteTask::ToString() {
+ return "delete " + pname_;
+}
+
WipeTask::WipeTask(const FlashingPlan* fp, const std::string& pname) : fp_(fp), pname_(pname){};
void WipeTask::Run() {
@@ -298,5 +332,9 @@
LOG(ERROR) << "wipe task erase failed with partition: " << pname_;
return;
}
- fb_perform_format(pname_, 1, partition_type, "", fp_->fs_options);
+ fb_perform_format(pname_, 1, partition_type, "", fp_->fs_options, fp_);
+}
+
+std::string WipeTask::ToString() {
+ return "erase " + pname_;
}
diff --git a/fastboot/task.h b/fastboot/task.h
index 34e3e92..f7c8801 100644
--- a/fastboot/task.h
+++ b/fastboot/task.h
@@ -35,6 +35,8 @@
public:
Task() = default;
virtual void Run() = 0;
+ virtual std::string ToString() = 0;
+
virtual FlashTask* AsFlashTask() { return nullptr; }
virtual RebootTask* AsRebootTask() { return nullptr; }
virtual UpdateSuperTask* AsUpdateSuperTask() { return nullptr; }
@@ -46,20 +48,22 @@
class FlashTask : public Task {
public:
FlashTask(const std::string& slot, const std::string& pname, const std::string& fname,
- const bool apply_vbmeta);
+ const bool apply_vbmeta, const FlashingPlan* fp);
virtual FlashTask* AsFlashTask() override { return this; }
+ void Run() override;
+ std::string ToString() override;
std::string GetPartition() { return pname_; }
std::string GetImageName() { return fname_; }
- std::string GetPartitionAndSlot();
std::string GetSlot() { return slot_; }
- void Run() override;
+ std::string GetPartitionAndSlot();
private:
const std::string pname_;
const std::string fname_;
const std::string slot_;
const bool apply_vbmeta_;
+ const FlashingPlan* fp_;
};
class RebootTask : public Task {
@@ -68,28 +72,31 @@
RebootTask(const FlashingPlan* fp, const std::string& reboot_target);
virtual RebootTask* AsRebootTask() override { return this; }
void Run() override;
+ std::string ToString() override;
private:
const std::string reboot_target_ = "";
const FlashingPlan* fp_;
};
-class FlashSuperLayoutTask : public Task {
+class OptimizedFlashSuperTask : public Task {
public:
- FlashSuperLayoutTask(const std::string& super_name, std::unique_ptr<SuperFlashHelper> helper,
- SparsePtr sparse_layout, uint64_t super_size);
- static std::unique_ptr<FlashSuperLayoutTask> Initialize(const FlashingPlan* fp,
- std::vector<ImageEntry>& os_images);
- static std::unique_ptr<FlashSuperLayoutTask> InitializeFromTasks(
+ OptimizedFlashSuperTask(const std::string& super_name, std::unique_ptr<SuperFlashHelper> helper,
+ SparsePtr sparse_layout, uint64_t super_size, const FlashingPlan* fp);
+ static std::unique_ptr<OptimizedFlashSuperTask> Initialize(const FlashingPlan* fp,
+ std::vector<ImageEntry>& os_images);
+ static std::unique_ptr<OptimizedFlashSuperTask> InitializeFromTasks(
const FlashingPlan* fp, std::vector<std::unique_ptr<Task>>& tasks);
using ImageEntry = std::pair<const Image*, std::string>;
void Run() override;
+ std::string ToString() override;
private:
const std::string super_name_;
std::unique_ptr<SuperFlashHelper> helper_;
SparsePtr sparse_layout_;
uint64_t super_size_;
+ const FlashingPlan* fp_;
};
class UpdateSuperTask : public Task {
@@ -98,6 +105,7 @@
virtual UpdateSuperTask* AsUpdateSuperTask() override { return this; }
void Run() override;
+ std::string ToString() override;
private:
const FlashingPlan* fp_;
@@ -108,6 +116,7 @@
ResizeTask(const FlashingPlan* fp, const std::string& pname, const std::string& size,
const std::string& slot);
void Run() override;
+ std::string ToString() override;
private:
const FlashingPlan* fp_;
@@ -118,8 +127,9 @@
class DeleteTask : public Task {
public:
- DeleteTask(const FlashingPlan* _fp, const std::string& _pname);
+ DeleteTask(const FlashingPlan* fp, const std::string& pname);
void Run() override;
+ std::string ToString() override;
private:
const FlashingPlan* fp_;
@@ -130,8 +140,8 @@
public:
WipeTask(const FlashingPlan* fp, const std::string& pname);
virtual WipeTask* AsWipeTask() override { return this; }
-
void Run() override;
+ std::string ToString() override;
private:
const FlashingPlan* fp_;
diff --git a/fastboot/task_test.cpp b/fastboot/task_test.cpp
index 145b4d7..1ba3f4a 100644
--- a/fastboot/task_test.cpp
+++ b/fastboot/task_test.cpp
@@ -16,6 +16,7 @@
#include "task.h"
#include "fastboot.h"
+#include "fastboot_driver_mock.h"
#include <gtest/gtest.h>
#include <fstream>
@@ -23,7 +24,9 @@
#include <memory>
#include <unordered_map>
#include "android-base/strings.h"
+
using android::base::Split;
+using testing::_;
class ParseTest : public ::testing ::Test {
protected:
@@ -58,7 +61,34 @@
return ParseFastbootInfoLine(fp, vec_command);
}
-TEST_F(ParseTest, CORRECT_FlASH_TASK_FORMED) {
+// tests if tasks_a is a superset of tasks_b. Used for checking to ensure all partitions flashed
+// from hardcoded image list is also flashed in new fastboot-info.txt
+static bool compareTaskList(std::vector<std::unique_ptr<Task>>& tasks_a,
+ std::vector<std::unique_ptr<Task>>& tasks_b) {
+ std::set<std::string> list;
+ for (auto& task : tasks_a) {
+ list.insert(task->ToString());
+ }
+ for (auto& task : tasks_b) {
+ if (list.find(task->ToString()) == list.end()) {
+ std::cout << "ERROR: " << task->ToString()
+ << " not found in task list created by fastboot-info.txt";
+ return false;
+ }
+ }
+ return true;
+}
+
+static std::string tasksToString(std::vector<std::unique_ptr<Task>>& tasks) {
+ std::string output;
+ for (auto& task : tasks) {
+ output.append(task->ToString());
+ output.append("\n");
+ }
+ return output;
+}
+
+TEST_F(ParseTest, CorrectFlashTaskFormed) {
std::vector<std::string> commands = {"flash dtbo", "flash --slot-other system system_other.img",
"flash system", "flash --apply-vbmeta vbmeta"};
@@ -86,25 +116,32 @@
}
}
-TEST_F(ParseTest, VERSION_CHECK_CORRRECT) {
- std::vector<std::string> correct_versions = {
- "version 1.0",
- "version 22.00",
- };
+TEST_F(ParseTest, VersionCheckCorrect) {
+ std::vector<std::string> correct_versions = {"version 1", "version 22", "version 5",
+ "version 17"};
- std::vector<std::string> bad_versions = {"version", "version .01", "version x1",
- "version 1.0.1", "version 1.", "s 1.0",
- "version 1.0 2.0"};
+ std::vector<std::string> bad_versions = {"version", "version .01", "version x1",
+ "version 1.0.1", "version 1.", "s 1.0",
+ "version 1.0 2.0", "version 100.00", "version 1 2"};
for (auto& version : correct_versions) {
- ASSERT_TRUE(CheckFastbootInfoRequirements(android::base::Split(version, " "))) << version;
+ ASSERT_TRUE(CheckFastbootInfoRequirements(android::base::Split(version, " "), 26))
+ << version;
}
+
+ // returning False for failing version check
+ for (auto& version : correct_versions) {
+ ASSERT_FALSE(CheckFastbootInfoRequirements(android::base::Split(version, " "), 0))
+ << version;
+ }
+ // returning False for bad format
for (auto& version : bad_versions) {
- ASSERT_FALSE(CheckFastbootInfoRequirements(android::base::Split(version, " "))) << version;
+ ASSERT_FALSE(CheckFastbootInfoRequirements(android::base::Split(version, " "), 100))
+ << version;
}
}
-TEST_F(ParseTest, BAD_FASTBOOT_INFO_INPUT) {
+TEST_F(ParseTest, BadFastbootInput) {
ASSERT_EQ(ParseCommand(fp.get(), "flash"), nullptr);
ASSERT_EQ(ParseCommand(fp.get(), "flash --slot-other --apply-vbmeta"), nullptr);
ASSERT_EQ(ParseCommand(fp.get(), "flash --apply-vbmeta"), nullptr);
@@ -121,3 +158,80 @@
ASSERT_EQ(ParseCommand(fp.get(), "erase dtbo dtbo"), nullptr);
ASSERT_EQ(ParseCommand(fp.get(), "wipe this"), nullptr);
}
+
+TEST_F(ParseTest, CorrectTaskFormed) {
+ std::vector<std::string> commands = {"flash dtbo", "flash --slot-other system system_other.img",
+ "reboot bootloader", "update-super", "erase cache"};
+ std::vector<std::unique_ptr<Task>> tasks = collectTasks(fp.get(), commands);
+
+ ASSERT_TRUE(tasks[0]->AsFlashTask());
+ ASSERT_TRUE(tasks[0]->AsFlashTask());
+ ASSERT_TRUE(tasks[1]->AsFlashTask());
+ ASSERT_TRUE(tasks[2]->AsRebootTask());
+ ASSERT_TRUE(tasks[3]->AsUpdateSuperTask());
+ ASSERT_TRUE(tasks[4]->AsWipeTask());
+}
+
+TEST_F(ParseTest, CorrectDriverCalls) {
+ fastboot::MockFastbootDriver fb;
+ fp->fb = &fb;
+
+ EXPECT_CALL(fb, RebootTo(_, _, _)).Times(1);
+ EXPECT_CALL(fb, Reboot(_, _)).Times(1);
+ EXPECT_CALL(fb, WaitForDisconnect()).Times(2);
+
+ std::vector<std::string> commands = {"reboot bootloader", "reboot"};
+ std::vector<std::unique_ptr<Task>> tasks = collectTasks(fp.get(), commands);
+
+ for (auto& task : tasks) {
+ task->Run();
+ }
+}
+
+TEST_F(ParseTest, CorrectTaskLists) {
+ if (!get_android_product_out()) {
+ GTEST_SKIP();
+ }
+
+ LocalImageSource s;
+ fp->source = &s;
+ fp->sparse_limit = std::numeric_limits<int64_t>::max();
+
+ fastboot::MockFastbootDriver fb;
+ fp->fb = &fb;
+ fp->should_optimize_flash_super = false;
+
+ ON_CALL(fb, GetVar("super-partition-name", _, _))
+ .WillByDefault(testing::Return(fastboot::BAD_ARG));
+
+ FlashAllTool tool(fp.get());
+
+ fp->should_use_fastboot_info = false;
+ auto hardcoded_tasks = tool.CollectTasks();
+ fp->should_use_fastboot_info = true;
+ auto fastboot_info_tasks = tool.CollectTasks();
+
+ auto is_non_flash_task = [](const auto& task) -> bool {
+ return task->AsFlashTask() == nullptr;
+ };
+
+ // remove non flash tasks for testing purposes
+ hardcoded_tasks.erase(
+ std::remove_if(hardcoded_tasks.begin(), hardcoded_tasks.end(), is_non_flash_task),
+ hardcoded_tasks.end());
+ fastboot_info_tasks.erase(std::remove_if(fastboot_info_tasks.begin(), fastboot_info_tasks.end(),
+ is_non_flash_task),
+ fastboot_info_tasks.end());
+
+ if (!compareTaskList(fastboot_info_tasks, hardcoded_tasks)) {
+ std::cout << "\n\n---Hardcoded Task List---\n"
+ << tasksToString(hardcoded_tasks) << "\n---Fastboot-Info Task List---\n"
+ << tasksToString(fastboot_info_tasks);
+ }
+
+ ASSERT_TRUE(compareTaskList(fastboot_info_tasks, hardcoded_tasks));
+
+ ASSERT_TRUE(fastboot_info_tasks.size() >= hardcoded_tasks.size())
+ << "size of fastboot-info task list: " << fastboot_info_tasks.size()
+ << " size of hardcoded task list: " << hardcoded_tasks.size();
+}
diff --git a/fastboot/usb_osx.cpp b/fastboot/usb_osx.cpp
index 5b9e5c8..8b852f5 100644
--- a/fastboot/usb_osx.cpp
+++ b/fastboot/usb_osx.cpp
@@ -436,12 +436,7 @@
for (;;) {
if (! IOIteratorIsValid(iterator)) {
- /*
- * Apple documentation advises resetting the iterator if
- * it should become invalid during iteration.
- */
- IOIteratorReset(iterator);
- continue;
+ break;
}
io_service_t device = IOIteratorNext(iterator);
diff --git a/fs_mgr/Android.bp b/fs_mgr/Android.bp
index dd61272..0a836e4 100644
--- a/fs_mgr/Android.bp
+++ b/fs_mgr/Android.bp
@@ -49,7 +49,6 @@
sanitize: {
misc_undefined: ["integer"],
},
- local_include_dirs: ["include/"],
cflags: [
"-Wall",
"-Werror",
@@ -60,6 +59,7 @@
name: "libfs_mgr_defaults",
defaults: ["fs_mgr_defaults"],
export_include_dirs: ["include"],
+ local_include_dirs: ["include/"],
include_dirs: ["system/vold"],
cflags: [
"-D_FILE_OFFSET_BITS=64",
@@ -70,8 +70,9 @@
"fs_mgr.cpp",
"fs_mgr_format.cpp",
"fs_mgr_dm_linear.cpp",
- "fs_mgr_overlayfs.cpp",
"fs_mgr_roots.cpp",
+ "fs_mgr_overlayfs_control.cpp",
+ "fs_mgr_overlayfs_mount.cpp",
"fs_mgr_vendor_overlay.cpp",
":libfiemap_srcs",
],
@@ -181,8 +182,13 @@
ramdisk_available: true,
vendor_ramdisk_available: true,
recovery_available: true,
+ apex_available: [
+ "//apex_available:anyapex",
+ "//apex_available:platform",
+ ],
host_supported: true,
defaults: ["fs_mgr_defaults"],
+ local_include_dirs: ["include/"],
srcs: [
"fs_mgr_fstab.cpp",
"fs_mgr_boot_config.cpp",
@@ -206,6 +212,7 @@
"libbase_headers",
"libgsi_headers",
],
+ min_sdk_version: "31",
}
cc_binary {
diff --git a/fs_mgr/fs_mgr.cpp b/fs_mgr/fs_mgr.cpp
index 742cdfa..e568a9b 100644
--- a/fs_mgr/fs_mgr.cpp
+++ b/fs_mgr/fs_mgr.cpp
@@ -613,7 +613,6 @@
// Read the primary superblock from an f2fs filesystem. On failure return
// false. If it's not an f2fs filesystem, also set FS_STAT_INVALID_MAGIC.
-#define F2FS_BLKSIZE 4096
#define F2FS_SUPER_OFFSET 1024
static bool read_f2fs_superblock(const std::string& blk_device, int* fs_stat) {
android::base::unique_fd fd(TEMP_FAILURE_RETRY(open(blk_device.c_str(), O_RDONLY | O_CLOEXEC)));
@@ -628,7 +627,9 @@
PERROR << "Can't read '" << blk_device << "' superblock1";
return false;
}
- if (TEMP_FAILURE_RETRY(pread(fd, &sb2, sizeof(sb2), F2FS_BLKSIZE + F2FS_SUPER_OFFSET)) !=
+ // F2FS only supports block_size=page_size case. So, it is safe to call
+ // `getpagesize()` and use that as size of super block.
+ if (TEMP_FAILURE_RETRY(pread(fd, &sb2, sizeof(sb2), getpagesize() + F2FS_SUPER_OFFSET)) !=
sizeof(sb2)) {
PERROR << "Can't read '" << blk_device << "' superblock2";
return false;
@@ -652,7 +653,7 @@
return false;
}
if (sb == cpu_to_le32(F2FS_SUPER_MAGIC)) return true;
- if (TEMP_FAILURE_RETRY(pread(fd, &sb, sizeof(sb), F2FS_BLKSIZE + F2FS_SUPER_OFFSET)) !=
+ if (TEMP_FAILURE_RETRY(pread(fd, &sb, sizeof(sb), getpagesize() + F2FS_SUPER_OFFSET)) !=
sizeof(sb)) {
return false;
}
@@ -1096,8 +1097,11 @@
class CheckpointManager {
public:
- CheckpointManager(int needs_checkpoint = -1, bool metadata_encrypted = false)
- : needs_checkpoint_(needs_checkpoint), metadata_encrypted_(metadata_encrypted) {}
+ CheckpointManager(int needs_checkpoint = -1, bool metadata_encrypted = false,
+ bool needs_encrypt = false)
+ : needs_checkpoint_(needs_checkpoint),
+ metadata_encrypted_(metadata_encrypted),
+ needs_encrypt_(needs_encrypt) {}
bool NeedsCheckpoint() {
if (needs_checkpoint_ != UNKNOWN) {
@@ -1160,7 +1164,7 @@
} else {
LERROR << entry->fs_type << " does not implement checkpoints.";
}
- } else if (entry->fs_mgr_flags.checkpoint_blk) {
+ } else if (entry->fs_mgr_flags.checkpoint_blk && !needs_encrypt_) {
auto actual_block_device = block_device.empty() ? entry->blk_device : block_device;
if (fs_mgr_find_bow_device(actual_block_device).empty()) {
unique_fd fd(
@@ -1228,6 +1232,7 @@
enum { UNKNOWN = -1, NO = 0, YES = 1 };
int needs_checkpoint_;
bool metadata_encrypted_;
+ bool needs_encrypt_;
std::map<std::string, std::string> device_map_;
};
@@ -1845,17 +1850,14 @@
return ret;
}
-// If tmp_mount_point is non-null, mount the filesystem there. This is for the
-// tmp mount we do to check the user password
// If multiple fstab entries are to be mounted on "n_name", it will try to mount each one
// in turn, and stop on 1st success, or no more match.
-static int fs_mgr_do_mount_helper(Fstab* fstab, const std::string& n_name,
- const std::string& n_blk_device, const char* tmp_mount_point,
- int needs_checkpoint, bool metadata_encrypted) {
+int fs_mgr_do_mount(Fstab* fstab, const std::string& n_name, const std::string& n_blk_device,
+ int needs_checkpoint, bool needs_encrypt) {
int mount_errors = 0;
int first_mount_errno = 0;
std::string mount_point;
- CheckpointManager checkpoint_manager(needs_checkpoint, metadata_encrypted);
+ CheckpointManager checkpoint_manager(needs_checkpoint, true, needs_encrypt);
AvbUniquePtr avb_handle(nullptr);
if (!fstab) {
@@ -1897,11 +1899,7 @@
}
// Now mount it where requested */
- if (tmp_mount_point) {
- mount_point = tmp_mount_point;
- } else {
- mount_point = fstab_entry.mount_point;
- }
+ mount_point = fstab_entry.mount_point;
int fs_stat = prepare_fs_for_mount(n_blk_device, fstab_entry, mount_point);
@@ -1958,35 +1956,6 @@
return FS_MGR_DOMNT_FAILED;
}
-int fs_mgr_do_mount(Fstab* fstab, const char* n_name, char* n_blk_device, char* tmp_mount_point) {
- return fs_mgr_do_mount_helper(fstab, n_name, n_blk_device, tmp_mount_point, -1, false);
-}
-
-int fs_mgr_do_mount(Fstab* fstab, const char* n_name, char* n_blk_device, char* tmp_mount_point,
- bool needs_checkpoint, bool metadata_encrypted) {
- return fs_mgr_do_mount_helper(fstab, n_name, n_blk_device, tmp_mount_point, needs_checkpoint,
- metadata_encrypted);
-}
-
-/*
- * mount a tmpfs filesystem at the given point.
- * return 0 on success, non-zero on failure.
- */
-int fs_mgr_do_tmpfs_mount(const char *n_name)
-{
- int ret;
-
- ret = mount("tmpfs", n_name, "tmpfs", MS_NOATIME | MS_NOSUID | MS_NODEV | MS_NOEXEC,
- CRYPTO_TMPFS_OPTIONS);
- if (ret < 0) {
- LERROR << "Cannot mount tmpfs filesystem at " << n_name;
- return -1;
- }
-
- /* Success */
- return 0;
-}
-
static bool ConfigureIoScheduler(const std::string& device_path) {
if (!StartsWith(device_path, "/dev/")) {
LERROR << __func__ << ": invalid argument " << device_path;
diff --git a/fs_mgr/fs_mgr_format.cpp b/fs_mgr/fs_mgr_format.cpp
index 7385f79..622f181 100644
--- a/fs_mgr/fs_mgr_format.cpp
+++ b/fs_mgr/fs_mgr_format.cpp
@@ -32,6 +32,7 @@
#include <selinux/android.h>
#include <selinux/label.h>
#include <selinux/selinux.h>
+#include <string>
#include "fs_mgr_priv.h"
@@ -68,6 +69,13 @@
/* Format the partition using the calculated length */
+ // EXT4 supports 4K block size on 16K page sizes. A 4K block
+ // size formatted EXT4 partition will mount fine on both 4K and 16K page
+ // size kernels.
+ // However, EXT4 does not support 16K block size on 4K systems.
+ // If we want the same userspace code to work on both 4k/16k kernels,
+ // using a hardcoded 4096 block size is a simple solution. Using
+ // getpagesize() here would work as well, but 4096 is simpler.
std::string size_str = std::to_string(dev_sz / 4096);
std::vector<const char*> mke2fs_args = {"/system/bin/mke2fs", "-t", "ext4", "-b", "4096"};
@@ -127,7 +135,7 @@
/* Format the partition using the calculated length */
- std::string size_str = std::to_string(dev_sz / 4096);
+ const auto size_str = std::to_string(dev_sz / getpagesize());
std::vector<const char*> args = {"/system/bin/make_f2fs", "-g", "android"};
if (needs_projid) {
diff --git a/fs_mgr/fs_mgr_fstab.cpp b/fs_mgr/fs_mgr_fstab.cpp
index 598a3d2..ca27034 100644
--- a/fs_mgr/fs_mgr_fstab.cpp
+++ b/fs_mgr/fs_mgr_fstab.cpp
@@ -51,6 +51,7 @@
namespace {
constexpr char kDefaultAndroidDtDir[] = "/proc/device-tree/firmware/android";
+constexpr char kProcMountsPath[] = "/proc/mounts";
struct FlagList {
const char *name;
@@ -242,7 +243,9 @@
LWARNING << "Warning: zramsize= flag malformed: " << arg;
}
}
- } else if (StartsWith(flag, "fileencryption=")) {
+ } else if (StartsWith(flag, "fileencryption=") || flag == "fileencryption") {
+ // "fileencryption" enables file-based encryption. It's normally followed by an = and
+ // then the encryption options. But that can be omitted to use the default options.
ParseFileEncryption(arg, entry);
} else if (StartsWith(flag, "max_comp_streams=")) {
if (!ParseInt(arg, &entry->max_comp_streams)) {
@@ -325,8 +328,7 @@
// some recovery fstabs still contain the FDE options since they didn't do
// anything in recovery mode anyway (except possibly to cause the
// reservation of a crypto footer) and thus never got removed.
- if (entry->fs_mgr_flags.crypt && !entry->fs_mgr_flags.vold_managed &&
- access("/system/bin/recovery", F_OK) != 0) {
+ if (entry->fs_mgr_flags.crypt && !entry->fs_mgr_flags.vold_managed && !InRecovery()) {
LERROR << "FDE is no longer supported; 'encryptable' can only be used for adoptable "
"storage";
return false;
@@ -517,6 +519,9 @@
// ramdisk's copy of the fstab had to be located in the root directory, but now
// the system/etc directory is supported too and is the preferred location.
std::string GetFstabPath() {
+ if (InRecovery()) {
+ return "/etc/recovery.fstab";
+ }
for (const char* prop : {"fstab_suffix", "hardware", "hardware.platform"}) {
std::string suffix;
@@ -697,9 +702,7 @@
}
}
-bool ReadFstabFromFile(const std::string& path, Fstab* fstab_out) {
- const bool is_proc_mounts = (path == "/proc/mounts");
-
+static bool ReadFstabFromFileCommon(const std::string& path, Fstab* fstab_out) {
std::string fstab_str;
if (!android::base::ReadFileToString(path, &fstab_str, /* follow_symlinks = */ true)) {
PERROR << __FUNCTION__ << "(): failed to read file: '" << path << "'";
@@ -707,11 +710,22 @@
}
Fstab fstab;
- if (!ParseFstabFromString(fstab_str, is_proc_mounts, &fstab)) {
+ if (!ParseFstabFromString(fstab_str, path == kProcMountsPath, &fstab)) {
LERROR << __FUNCTION__ << "(): failed to load fstab from : '" << path << "'";
return false;
}
- if (!is_proc_mounts) {
+
+ EnableMandatoryFlags(&fstab);
+
+ *fstab_out = std::move(fstab);
+ return true;
+}
+
+bool ReadFstabFromFile(const std::string& path, Fstab* fstab) {
+ if (!ReadFstabFromFileCommon(path, fstab)) {
+ return false;
+ }
+ if (path != kProcMountsPath) {
if (!access(android::gsi::kGsiBootedIndicatorFile, F_OK)) {
std::string dsu_slot;
if (!android::gsi::GetActiveDsu(&dsu_slot)) {
@@ -723,20 +737,23 @@
PERROR << __FUNCTION__ << "(): failed to read DSU LP names";
return false;
}
- TransformFstabForDsu(&fstab, dsu_slot, Split(lp_names, ","));
+ TransformFstabForDsu(fstab, dsu_slot, Split(lp_names, ","));
} else if (errno != ENOENT) {
PERROR << __FUNCTION__ << "(): failed to access() DSU booted indicator";
return false;
}
+
+ SkipMountingPartitions(fstab, false /* verbose */);
}
-
- SkipMountingPartitions(&fstab, false /* verbose */);
- EnableMandatoryFlags(&fstab);
-
- *fstab_out = std::move(fstab);
return true;
}
+bool ReadFstabFromProcMounts(Fstab* fstab) {
+ // Don't call `ReadFstabFromFile` because the code for `path != kProcMountsPath` has an extra
+ // code size cost, even if it's never executed.
+ return ReadFstabFromFileCommon(kProcMountsPath, fstab);
+}
+
// Returns fstab entries parsed from the device tree if they exist
bool ReadFstabFromDt(Fstab* fstab, bool verbose) {
std::string fstab_buf = ReadFstabFromDt();
@@ -820,15 +837,8 @@
fstab->clear();
ReadFstabFromDt(fstab, false /* verbose */);
- std::string default_fstab_path;
- // Use different fstab paths for normal boot and recovery boot, respectively
- if ((access("/sbin/recovery", F_OK) == 0) || (access("/system/bin/recovery", F_OK) == 0)) {
- default_fstab_path = "/etc/recovery.fstab";
- } else { // normal boot
- default_fstab_path = GetFstabPath();
- }
-
Fstab default_fstab;
+ const std::string default_fstab_path = GetFstabPath();
if (!default_fstab_path.empty() && ReadFstabFromFile(default_fstab_path, &default_fstab)) {
for (auto&& entry : default_fstab) {
fstab->emplace_back(std::move(entry));
@@ -921,6 +931,17 @@
return base_device + "-verity";
}
+bool InRecovery() {
+ // Check the existence of recovery binary instead of using the compile time
+ // __ANDROID_RECOVERY__ macro.
+ // If BOARD_USES_RECOVERY_AS_BOOT is true, both normal and recovery boot
+ // mode would use the same init binary, which would mean during normal boot
+ // the '/init' binary is actually a symlink pointing to
+ // init_second_stage.recovery, which would be compiled with
+ // __ANDROID_RECOVERY__ defined.
+ return access("/system/bin/recovery", F_OK) == 0 || access("/sbin/recovery", F_OK) == 0;
+}
+
} // namespace fs_mgr
} // namespace android
diff --git a/fs_mgr/fs_mgr_overlayfs.cpp b/fs_mgr/fs_mgr_overlayfs.cpp
deleted file mode 100644
index 6349c20..0000000
--- a/fs_mgr/fs_mgr_overlayfs.cpp
+++ /dev/null
@@ -1,1734 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <dirent.h>
-#include <errno.h>
-#include <fcntl.h>
-#include <linux/fs.h>
-#include <selinux/selinux.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/mount.h>
-#include <sys/param.h>
-#include <sys/stat.h>
-#include <sys/statvfs.h>
-#include <sys/types.h>
-#include <sys/utsname.h>
-#include <sys/vfs.h>
-#include <unistd.h>
-
-#include <algorithm>
-#include <memory>
-#include <optional>
-#include <string>
-#include <vector>
-
-#include <android-base/file.h>
-#include <android-base/macros.h>
-#include <android-base/properties.h>
-#include <android-base/strings.h>
-#include <android-base/unique_fd.h>
-#include <ext4_utils/ext4_utils.h>
-#include <fs_mgr.h>
-#include <fs_mgr/file_wait.h>
-#include <fs_mgr_dm_linear.h>
-#include <fs_mgr_overlayfs.h>
-#include <fstab/fstab.h>
-#include <libdm/dm.h>
-#include <libfiemap/image_manager.h>
-#include <libgsi/libgsi.h>
-#include <liblp/builder.h>
-#include <liblp/liblp.h>
-#include <storage_literals/storage_literals.h>
-
-#include "fs_mgr_priv.h"
-#include "fs_mgr_priv_overlayfs.h"
-#include "libfiemap/utility.h"
-
-using namespace std::literals;
-using namespace android::dm;
-using namespace android::fs_mgr;
-using namespace android::storage_literals;
-using android::fiemap::FilesystemHasReliablePinning;
-using android::fiemap::IImageManager;
-
-namespace {
-
-constexpr char kDataScratchSizeMbProp[] = "fs_mgr.overlayfs.data_scratch_size_mb";
-constexpr char kPreferCacheBackingStorageProp[] = "fs_mgr.overlayfs.prefer_cache_backing_storage";
-
-bool fs_mgr_access(const std::string& path) {
- return access(path.c_str(), F_OK) == 0;
-}
-
-const auto kLowerdirOption = "lowerdir="s;
-const auto kUpperdirOption = "upperdir="s;
-
-bool fs_mgr_in_recovery() {
- // Check the existence of recovery binary instead of using the compile time
- // __ANDROID_RECOVERY__ macro.
- // If BOARD_USES_RECOVERY_AS_BOOT is true, both normal and recovery boot
- // mode would use the same init binary, which would mean during normal boot
- // the '/init' binary is actually a symlink pointing to
- // init_second_stage.recovery, which would be compiled with
- // __ANDROID_RECOVERY__ defined.
- return fs_mgr_access("/system/bin/recovery");
-}
-
-bool fs_mgr_is_dsu_running() {
- // Since android::gsi::CanBootIntoGsi() or android::gsi::MarkSystemAsGsi() is
- // never called in recovery, the return value of android::gsi::IsGsiRunning()
- // is not well-defined. In this case, just return false as being in recovery
- // implies not running a DSU system.
- if (fs_mgr_in_recovery()) return false;
- return android::gsi::IsGsiRunning();
-}
-
-// list of acceptable overlayfs backing storage
-const auto kScratchMountPoint = "/mnt/scratch"s;
-const auto kCacheMountPoint = "/cache"s;
-
-bool IsABDevice() {
- return !android::base::GetProperty("ro.boot.slot_suffix", "").empty();
-}
-
-std::vector<const std::string> OverlayMountPoints() {
- // Never fallback to legacy cache mount point if within a DSU system,
- // because running a DSU system implies the device supports dynamic
- // partitions, which means legacy cache mustn't be used.
- if (fs_mgr_is_dsu_running()) {
- return {kScratchMountPoint};
- }
-
- // For non-A/B devices prefer cache backing storage if
- // kPreferCacheBackingStorageProp property set.
- if (!IsABDevice() && android::base::GetBoolProperty(kPreferCacheBackingStorageProp, false) &&
- android::base::GetIntProperty("ro.vendor.api_level", -1) < __ANDROID_API_T__) {
- return {kCacheMountPoint, kScratchMountPoint};
- }
-
- return {kScratchMountPoint, kCacheMountPoint};
-}
-
-// Return true if everything is mounted, but before adb is started. Right
-// after 'trigger load_persist_props_action' is done.
-bool fs_mgr_boot_completed() {
- return android::base::GetBoolProperty("ro.persistent_properties.ready", false);
-}
-
-bool fs_mgr_is_dir(const std::string& path) {
- struct stat st;
- return !stat(path.c_str(), &st) && S_ISDIR(st.st_mode);
-}
-
-bool fs_mgr_rw_access(const std::string& path) {
- if (path.empty()) return false;
- return access(path.c_str(), R_OK | W_OK) == 0;
-}
-
-// At less than 1% or 8MB of free space return value of false,
-// means we will try to wrap with overlayfs.
-bool fs_mgr_filesystem_has_space(const std::string& mount_point) {
- // If we have access issues to find out space remaining, return true
- // to prevent us trying to override with overlayfs.
- struct statvfs vst;
- if (statvfs(mount_point.c_str(), &vst)) {
- PLOG(ERROR) << "statvfs " << mount_point;
- return true;
- }
-
- static constexpr int kPercentThreshold = 1; // 1%
- static constexpr unsigned long kSizeThreshold = 8 * 1024 * 1024; // 8MB
-
- return (vst.f_bfree >= (vst.f_blocks * kPercentThreshold / 100)) &&
- (static_cast<uint64_t>(vst.f_bfree) * vst.f_frsize) >= kSizeThreshold;
-}
-
-const auto kPhysicalDevice = "/dev/block/by-name/"s;
-constexpr char kScratchImageMetadata[] = "/metadata/gsi/remount/lp_metadata";
-
-// Note: this is meant only for recovery/first-stage init.
-bool ScratchIsOnData() {
- // The scratch partition of DSU is managed by gsid.
- if (fs_mgr_is_dsu_running()) {
- return false;
- }
- return fs_mgr_access(kScratchImageMetadata);
-}
-
-bool fs_mgr_update_blk_device(FstabEntry* entry) {
- if (entry->fs_mgr_flags.logical) {
- fs_mgr_update_logical_partition(entry);
- }
- if (fs_mgr_access(entry->blk_device)) {
- return true;
- }
- if (entry->blk_device != "/dev/root") {
- return false;
- }
-
- // special case for system-as-root (taimen and others)
- auto blk_device = kPhysicalDevice + "system";
- if (!fs_mgr_access(blk_device)) {
- blk_device += fs_mgr_get_slot_suffix();
- if (!fs_mgr_access(blk_device)) {
- return false;
- }
- }
- entry->blk_device = blk_device;
- return true;
-}
-
-bool fs_mgr_has_shared_blocks(const std::string& mount_point, const std::string& dev) {
- struct statfs fs;
- if ((statfs((mount_point + "/lost+found").c_str(), &fs) == -1) ||
- (fs.f_type != EXT4_SUPER_MAGIC)) {
- return false;
- }
-
- android::base::unique_fd fd(open(dev.c_str(), O_RDONLY | O_CLOEXEC));
- if (fd < 0) return false;
-
- struct ext4_super_block sb;
- if ((TEMP_FAILURE_RETRY(lseek64(fd, 1024, SEEK_SET)) < 0) ||
- (TEMP_FAILURE_RETRY(read(fd, &sb, sizeof(sb))) < 0)) {
- return false;
- }
-
- struct fs_info info;
- if (ext4_parse_sb(&sb, &info) < 0) return false;
-
- return (info.feat_ro_compat & EXT4_FEATURE_RO_COMPAT_SHARED_BLOCKS) != 0;
-}
-
-#define F2FS_SUPER_OFFSET 1024
-#define F2FS_FEATURE_OFFSET 2180
-#define F2FS_FEATURE_RO 0x4000
-bool fs_mgr_is_read_only_f2fs(const std::string& dev) {
- if (!fs_mgr_is_f2fs(dev)) return false;
-
- android::base::unique_fd fd(open(dev.c_str(), O_RDONLY | O_CLOEXEC));
- if (fd < 0) return false;
-
- __le32 feat;
- if ((TEMP_FAILURE_RETRY(lseek64(fd, F2FS_SUPER_OFFSET + F2FS_FEATURE_OFFSET, SEEK_SET)) < 0) ||
- (TEMP_FAILURE_RETRY(read(fd, &feat, sizeof(feat))) < 0)) {
- return false;
- }
-
- return (feat & cpu_to_le32(F2FS_FEATURE_RO)) != 0;
-}
-
-bool fs_mgr_overlayfs_enabled(FstabEntry* entry) {
- // readonly filesystem, can not be mount -o remount,rw
- // for squashfs, erofs or if free space is (near) zero making such a remount
- // virtually useless, or if there are shared blocks that prevent remount,rw
- if (!fs_mgr_filesystem_has_space(entry->mount_point)) {
- return true;
- }
-
- // blk_device needs to be setup so we can check superblock.
- // If we fail here, because during init first stage and have doubts.
- if (!fs_mgr_update_blk_device(entry)) {
- return true;
- }
-
- // f2fs read-only mode doesn't support remount,rw
- if (fs_mgr_is_read_only_f2fs(entry->blk_device)) {
- return true;
- }
-
- // check if ext4 de-dupe
- auto has_shared_blocks = fs_mgr_has_shared_blocks(entry->mount_point, entry->blk_device);
- if (!has_shared_blocks && (entry->mount_point == "/system")) {
- has_shared_blocks = fs_mgr_has_shared_blocks("/", entry->blk_device);
- }
- return has_shared_blocks;
-}
-
-bool fs_mgr_rm_all(const std::string& path, bool* change = nullptr, int level = 0) {
- std::unique_ptr<DIR, decltype(&closedir)> dir(opendir(path.c_str()), closedir);
- if (!dir) {
- if (errno == ENOENT) {
- return true;
- }
- PERROR << "opendir " << path << " depth=" << level;
- if ((errno == EPERM) && (level != 0)) {
- return true;
- }
- return false;
- }
- dirent* entry;
- auto ret = true;
- while ((entry = readdir(dir.get()))) {
- if (("."s == entry->d_name) || (".."s == entry->d_name)) continue;
- auto file = path + "/" + entry->d_name;
- if (entry->d_type == DT_UNKNOWN) {
- struct stat st;
- if (!lstat(file.c_str(), &st) && (st.st_mode & S_IFDIR)) entry->d_type = DT_DIR;
- }
- if (entry->d_type == DT_DIR) {
- ret &= fs_mgr_rm_all(file, change, level + 1);
- if (!rmdir(file.c_str())) {
- if (change) *change = true;
- } else {
- if (errno != ENOENT) ret = false;
- PERROR << "rmdir " << file << " depth=" << level;
- }
- continue;
- }
- if (!unlink(file.c_str())) {
- if (change) *change = true;
- } else {
- if (errno != ENOENT) ret = false;
- PERROR << "rm " << file << " depth=" << level;
- }
- }
- return ret;
-}
-
-const auto kUpperName = "upper"s;
-const auto kWorkName = "work"s;
-const auto kOverlayTopDir = "/overlay"s;
-
-std::string fs_mgr_get_overlayfs_candidate(const std::string& mount_point) {
- if (!fs_mgr_is_dir(mount_point)) return "";
- const auto base = android::base::Basename(mount_point) + "/";
- for (const auto& overlay_mount_point : OverlayMountPoints()) {
- auto dir = overlay_mount_point + kOverlayTopDir + "/" + base;
- auto upper = dir + kUpperName;
- if (!fs_mgr_is_dir(upper)) continue;
- auto work = dir + kWorkName;
- if (!fs_mgr_is_dir(work)) continue;
- if (!fs_mgr_rw_access(work)) continue;
- return dir;
- }
- return "";
-}
-
-static inline bool KernelSupportsUserXattrs() {
- struct utsname uts;
- uname(&uts);
-
- int major, minor;
- if (sscanf(uts.release, "%d.%d", &major, &minor) != 2) {
- return false;
- }
- return major > 5 || (major == 5 && minor >= 15);
-}
-
-const std::string fs_mgr_mount_point(const std::string& mount_point) {
- if ("/"s != mount_point) return mount_point;
- return "/system";
-}
-
-// default options for mount_point, returns empty string for none available.
-std::string fs_mgr_get_overlayfs_options(const FstabEntry& entry) {
- const auto mount_point = fs_mgr_mount_point(entry.mount_point);
- auto candidate = fs_mgr_get_overlayfs_candidate(mount_point);
- if (candidate.empty()) return "";
- auto ret = kLowerdirOption + mount_point + "," + kUpperdirOption + candidate + kUpperName +
- ",workdir=" + candidate + kWorkName;
- if (fs_mgr_overlayfs_valid() == OverlayfsValidResult::kOverrideCredsRequired) {
- ret += ",override_creds=off";
- }
- if (KernelSupportsUserXattrs()) {
- ret += ",userxattr";
- }
- for (const auto& flag : android::base::Split(entry.fs_options, ",")) {
- if (android::base::StartsWith(flag, "context=")) {
- ret += "," + flag;
- }
- }
- return ret;
-}
-
-constexpr char kOverlayfsFileContext[] = "u:object_r:overlayfs_file:s0";
-
-class AutoSetFsCreateCon final {
- public:
- AutoSetFsCreateCon() {}
- AutoSetFsCreateCon(const std::string& context) { Set(context); }
- ~AutoSetFsCreateCon() { Restore(); }
-
- bool Ok() const { return ok_; }
- bool Set(const std::string& context) {
- if (setfscreatecon(context.c_str())) {
- PLOG(ERROR) << "setfscreatecon " << context;
- return false;
- }
- ok_ = true;
- return true;
- }
- bool Restore() {
- if (restored_ || !ok_) {
- return true;
- }
- if (setfscreatecon(nullptr)) {
- PLOG(ERROR) << "setfscreatecon null";
- return false;
- }
- restored_ = true;
- return true;
- }
-
- private:
- bool ok_ = false;
- bool restored_ = false;
-};
-
-std::string fs_mgr_overlayfs_setup_dir(const std::string& dir) {
- auto top = dir + kOverlayTopDir;
-
- AutoSetFsCreateCon createcon(kOverlayfsFileContext);
- if (!createcon.Ok()) {
- return {};
- }
- if (mkdir(top.c_str(), 0755) != 0 && errno != EEXIST) {
- PERROR << "mkdir " << top;
- return {};
- }
- if (!createcon.Restore()) {
- return {};
- }
- return top;
-}
-
-bool fs_mgr_overlayfs_setup_one(const std::string& overlay, const std::string& mount_point,
- bool* want_reboot) {
- if (fs_mgr_overlayfs_already_mounted(mount_point)) {
- return true;
- }
- auto fsrec_mount_point = overlay + "/" + android::base::Basename(mount_point) + "/";
-
- AutoSetFsCreateCon createcon(kOverlayfsFileContext);
- if (!createcon.Ok()) {
- return false;
- }
- if (mkdir(fsrec_mount_point.c_str(), 0755) != 0 && errno != EEXIST) {
- PERROR << "mkdir " << fsrec_mount_point;
- return false;
- }
- if (mkdir((fsrec_mount_point + kWorkName).c_str(), 0755) != 0 && errno != EEXIST) {
- PERROR << "mkdir " << fsrec_mount_point << kWorkName;
- return false;
- }
- if (!createcon.Restore()) {
- return false;
- }
-
- createcon = {};
-
- auto new_context = fs_mgr_get_context(mount_point);
- if (new_context.empty() || !createcon.Set(new_context)) {
- return false;
- }
-
- auto upper = fsrec_mount_point + kUpperName;
- if (mkdir(upper.c_str(), 0755) != 0 && errno != EEXIST) {
- PERROR << "mkdir " << upper;
- return false;
- }
- if (!createcon.Restore()) {
- return false;
- }
-
- if (want_reboot) *want_reboot = true;
-
- return true;
-}
-
-uint32_t fs_mgr_overlayfs_slot_number() {
- return SlotNumberForSlotSuffix(fs_mgr_get_slot_suffix());
-}
-
-std::string fs_mgr_overlayfs_super_device(uint32_t slot_number) {
- return kPhysicalDevice + fs_mgr_get_super_partition_name(slot_number);
-}
-
-bool fs_mgr_overlayfs_has_logical(const Fstab& fstab) {
- for (const auto& entry : fstab) {
- if (entry.fs_mgr_flags.logical) {
- return true;
- }
- }
- return false;
-}
-
-// Returns true if immediate unmount succeeded and the scratch mount point was
-// removed.
-bool fs_mgr_overlayfs_umount_scratch() {
- if (umount(kScratchMountPoint.c_str()) != 0) {
- return false;
- }
- if (rmdir(kScratchMountPoint.c_str()) != 0 && errno != ENOENT) {
- PLOG(ERROR) << "rmdir " << kScratchMountPoint;
- }
- return true;
-}
-
-OverlayfsTeardownResult TeardownDataScratch(IImageManager* images,
- const std::string& partition_name, bool was_mounted) {
- if (!images) {
- return OverlayfsTeardownResult::Error;
- }
- if (!images->DisableImage(partition_name)) {
- return OverlayfsTeardownResult::Error;
- }
- if (was_mounted) {
- // If overlayfs was mounted, don't bother trying to unmap since
- // it'll fail and create error spam.
- return OverlayfsTeardownResult::Busy;
- }
- if (!images->UnmapImageIfExists(partition_name)) {
- return OverlayfsTeardownResult::Busy;
- }
- if (!images->DeleteBackingImage(partition_name)) {
- return OverlayfsTeardownResult::Busy;
- }
- return OverlayfsTeardownResult::Ok;
-}
-
-OverlayfsTeardownResult fs_mgr_overlayfs_teardown_scratch(const std::string& overlay,
- bool* change) {
- // umount and delete kScratchMountPoint storage if we have logical partitions
- if (overlay != kScratchMountPoint) {
- return OverlayfsTeardownResult::Ok;
- }
-
- // Validation check.
- if (fs_mgr_is_dsu_running()) {
- LERROR << "Destroying DSU scratch is not allowed.";
- return OverlayfsTeardownResult::Error;
- }
-
- bool was_mounted = fs_mgr_overlayfs_already_mounted(kScratchMountPoint, false);
- if (was_mounted) {
- fs_mgr_overlayfs_umount_scratch();
- }
-
- const auto partition_name = android::base::Basename(kScratchMountPoint);
-
- auto images = IImageManager::Open("remount", 10s);
- if (images && images->BackingImageExists(partition_name)) {
- // No need to check super partition, if we knew we had a scratch device
- // in /data.
- return TeardownDataScratch(images.get(), partition_name, was_mounted);
- }
-
- auto slot_number = fs_mgr_overlayfs_slot_number();
- auto super_device = fs_mgr_overlayfs_super_device(slot_number);
- if (!fs_mgr_rw_access(super_device)) {
- return OverlayfsTeardownResult::Ok;
- }
-
- auto builder = MetadataBuilder::New(super_device, slot_number);
- if (!builder) {
- return OverlayfsTeardownResult::Ok;
- }
- if (builder->FindPartition(partition_name) == nullptr) {
- return OverlayfsTeardownResult::Ok;
- }
- builder->RemovePartition(partition_name);
- auto metadata = builder->Export();
- if (metadata && UpdatePartitionTable(super_device, *metadata.get(), slot_number)) {
- if (change) *change = true;
- if (!DestroyLogicalPartition(partition_name)) {
- return OverlayfsTeardownResult::Error;
- }
- } else {
- LERROR << "delete partition " << overlay;
- return OverlayfsTeardownResult::Error;
- }
-
- if (was_mounted) {
- return OverlayfsTeardownResult::Busy;
- }
- return OverlayfsTeardownResult::Ok;
-}
-
-bool fs_mgr_overlayfs_teardown_one(const std::string& overlay, const std::string& mount_point,
- bool* change, bool* should_destroy_scratch = nullptr) {
- const auto top = overlay + kOverlayTopDir;
-
- if (!fs_mgr_access(top)) {
- if (should_destroy_scratch) *should_destroy_scratch = true;
- return true;
- }
-
- auto cleanup_all = mount_point.empty();
- const auto partition_name = android::base::Basename(mount_point);
- const auto oldpath = top + (cleanup_all ? "" : ("/" + partition_name));
- const auto newpath = cleanup_all ? overlay + "/." + kOverlayTopDir.substr(1) + ".teardown"
- : top + "/." + partition_name + ".teardown";
- auto ret = fs_mgr_rm_all(newpath);
- if (!rename(oldpath.c_str(), newpath.c_str())) {
- if (change) *change = true;
- } else if (errno != ENOENT) {
- ret = false;
- PERROR << "mv " << oldpath << " " << newpath;
- }
- ret &= fs_mgr_rm_all(newpath, change);
- if (!rmdir(newpath.c_str())) {
- if (change) *change = true;
- } else if (errno != ENOENT) {
- ret = false;
- PERROR << "rmdir " << newpath;
- }
- if (!cleanup_all) {
- if (!rmdir(top.c_str())) {
- if (change) *change = true;
- cleanup_all = true;
- } else if (errno == ENOTEMPTY) {
- cleanup_all = true;
- // cleanup all if the content is all hidden (leading .)
- std::unique_ptr<DIR, decltype(&closedir)> dir(opendir(top.c_str()), closedir);
- if (!dir) {
- PERROR << "opendir " << top;
- } else {
- dirent* entry;
- while ((entry = readdir(dir.get()))) {
- if (entry->d_name[0] != '.') {
- cleanup_all = false;
- break;
- }
- }
- }
- } else if (errno == ENOENT) {
- cleanup_all = true;
- } else {
- ret = false;
- PERROR << "rmdir " << top;
- }
- }
- if (should_destroy_scratch) *should_destroy_scratch = cleanup_all;
- return ret;
-}
-
-bool fs_mgr_overlayfs_set_shared_mount(const std::string& mount_point, bool shared_flag) {
- auto ret = mount(nullptr, mount_point.c_str(), nullptr, shared_flag ? MS_SHARED : MS_PRIVATE,
- nullptr);
- if (ret) {
- PERROR << "__mount(target=" << mount_point
- << ",flag=" << (shared_flag ? "MS_SHARED" : "MS_PRIVATE") << ")=" << ret;
- // If "/system" doesn't look like a mountpoint, retry with "/".
- if (errno == EINVAL && mount_point == "/system") {
- return fs_mgr_overlayfs_set_shared_mount("/", shared_flag);
- }
- return false;
- }
- return true;
-}
-
-bool fs_mgr_overlayfs_move_mount(const std::string& source, const std::string& target) {
- auto ret = mount(source.c_str(), target.c_str(), nullptr, MS_MOVE, nullptr);
- if (ret) {
- PERROR << "__mount(source=" << source << ",target=" << target << ",flag=MS_MOVE)=" << ret;
- return false;
- }
- return true;
-}
-
-struct mount_info {
- std::string mount_point;
- bool shared_flag;
-};
-
-std::vector<mount_info> ReadMountinfoFromFile(const std::string& path) {
- std::vector<mount_info> info;
-
- auto file = std::unique_ptr<FILE, decltype(&fclose)>{fopen(path.c_str(), "re"), fclose};
- if (!file) {
- PERROR << __FUNCTION__ << "(): cannot open file: '" << path << "'";
- return info;
- }
-
- ssize_t len;
- size_t alloc_len = 0;
- char* line = nullptr;
- while ((len = getline(&line, &alloc_len, file.get())) != -1) {
- /* if the last character is a newline, shorten the string by 1 byte */
- if (line[len - 1] == '\n') {
- line[len - 1] = '\0';
- }
-
- static constexpr char delim[] = " \t";
- char* save_ptr;
- if (!strtok_r(line, delim, &save_ptr)) {
- LERROR << "Error parsing mount ID";
- break;
- }
- if (!strtok_r(nullptr, delim, &save_ptr)) {
- LERROR << "Error parsing parent ID";
- break;
- }
- if (!strtok_r(nullptr, delim, &save_ptr)) {
- LERROR << "Error parsing mount source";
- break;
- }
- if (!strtok_r(nullptr, delim, &save_ptr)) {
- LERROR << "Error parsing root";
- break;
- }
-
- char* p;
- if (!(p = strtok_r(nullptr, delim, &save_ptr))) {
- LERROR << "Error parsing mount_point";
- break;
- }
- mount_info entry = {p, false};
-
- if (!strtok_r(nullptr, delim, &save_ptr)) {
- LERROR << "Error parsing mount_flags";
- break;
- }
-
- while ((p = strtok_r(nullptr, delim, &save_ptr))) {
- if ((p[0] == '-') && (p[1] == '\0')) break;
- if (android::base::StartsWith(p, "shared:")) entry.shared_flag = true;
- }
- if (!p) {
- LERROR << "Error parsing fields";
- break;
- }
- info.emplace_back(std::move(entry));
- }
-
- free(line);
- if (info.empty()) {
- LERROR << __FUNCTION__ << "(): failed to load mountinfo from : '" << path << "'";
- }
- return info;
-}
-
-bool fs_mgr_overlayfs_mount(const FstabEntry& entry) {
- const auto mount_point = fs_mgr_mount_point(entry.mount_point);
- const auto options = fs_mgr_get_overlayfs_options(entry);
- if (options.empty()) return false;
-
- auto retval = true;
-
- struct move_entry {
- std::string mount_point;
- std::string dir;
- bool shared_flag;
- };
- std::vector<move_entry> move;
- auto parent_private = false;
- auto parent_made_private = false;
- auto dev_private = false;
- auto dev_made_private = false;
- for (auto& entry : ReadMountinfoFromFile("/proc/self/mountinfo")) {
- if ((entry.mount_point == mount_point) && !entry.shared_flag) {
- parent_private = true;
- }
- if ((entry.mount_point == "/dev") && !entry.shared_flag) {
- dev_private = true;
- }
-
- if (!android::base::StartsWith(entry.mount_point, mount_point + "/")) {
- continue;
- }
- if (std::find_if(move.begin(), move.end(), [&entry](const auto& it) {
- return android::base::StartsWith(entry.mount_point, it.mount_point + "/");
- }) != move.end()) {
- continue;
- }
-
- // use as the bound directory in /dev.
- AutoSetFsCreateCon createcon;
- auto new_context = fs_mgr_get_context(entry.mount_point);
- if (new_context.empty() || !createcon.Set(new_context)) {
- continue;
- }
- move_entry new_entry = {std::move(entry.mount_point), "/dev/TemporaryDir-XXXXXX",
- entry.shared_flag};
- const auto target = mkdtemp(new_entry.dir.data());
- if (!createcon.Restore()) {
- return false;
- }
- if (!target) {
- retval = false;
- PERROR << "temporary directory for MS_BIND";
- continue;
- }
-
- if (!parent_private && !parent_made_private) {
- parent_made_private = fs_mgr_overlayfs_set_shared_mount(mount_point, false);
- }
- if (new_entry.shared_flag) {
- new_entry.shared_flag = fs_mgr_overlayfs_set_shared_mount(new_entry.mount_point, false);
- }
- if (!fs_mgr_overlayfs_move_mount(new_entry.mount_point, new_entry.dir)) {
- retval = false;
- if (new_entry.shared_flag) {
- fs_mgr_overlayfs_set_shared_mount(new_entry.mount_point, true);
- }
- continue;
- }
- move.emplace_back(std::move(new_entry));
- }
-
- // hijack __mount() report format to help triage
- auto report = "__mount(source=overlay,target="s + mount_point + ",type=overlay";
- const auto opt_list = android::base::Split(options, ",");
- for (const auto& opt : opt_list) {
- if (android::base::StartsWith(opt, kUpperdirOption)) {
- report = report + "," + opt;
- break;
- }
- }
- report = report + ")=";
-
- auto ret = mount("overlay", mount_point.c_str(), "overlay", MS_RDONLY | MS_NOATIME,
- options.c_str());
- if (ret) {
- retval = false;
- PERROR << report << ret;
- } else {
- LINFO << report << ret;
- }
-
- // Move submounts back.
- for (const auto& entry : move) {
- if (!dev_private && !dev_made_private) {
- dev_made_private = fs_mgr_overlayfs_set_shared_mount("/dev", false);
- }
-
- if (!fs_mgr_overlayfs_move_mount(entry.dir, entry.mount_point)) {
- retval = false;
- } else if (entry.shared_flag &&
- !fs_mgr_overlayfs_set_shared_mount(entry.mount_point, true)) {
- retval = false;
- }
- rmdir(entry.dir.c_str());
- }
- if (dev_made_private) {
- fs_mgr_overlayfs_set_shared_mount("/dev", true);
- }
- if (parent_made_private) {
- fs_mgr_overlayfs_set_shared_mount(mount_point, true);
- }
-
- return retval;
-}
-
-// Mount kScratchMountPoint
-bool MountScratch(const std::string& device_path, bool readonly = false) {
- if (readonly) {
- if (!fs_mgr_access(device_path)) {
- LOG(ERROR) << "Path does not exist: " << device_path;
- return false;
- }
- } else if (!fs_mgr_rw_access(device_path)) {
- LOG(ERROR) << "Path does not exist or is not readwrite: " << device_path;
- return false;
- }
-
- std::vector<const char*> filesystem_candidates;
- if (fs_mgr_is_f2fs(device_path)) {
- filesystem_candidates = {"f2fs", "ext4"};
- } else if (fs_mgr_is_ext4(device_path)) {
- filesystem_candidates = {"ext4", "f2fs"};
- } else {
- LOG(ERROR) << "Scratch partition is not f2fs or ext4";
- return false;
- }
-
- AutoSetFsCreateCon createcon(kOverlayfsFileContext);
- if (!createcon.Ok()) {
- return false;
- }
- if (mkdir(kScratchMountPoint.c_str(), 0755) && (errno != EEXIST)) {
- PERROR << "create " << kScratchMountPoint;
- return false;
- }
-
- FstabEntry entry;
- entry.blk_device = device_path;
- entry.mount_point = kScratchMountPoint;
- entry.flags = MS_NOATIME | MS_RDONLY;
- if (!readonly) {
- entry.flags &= ~MS_RDONLY;
- entry.flags |= MS_SYNCHRONOUS;
- entry.fs_options = "nodiscard";
- fs_mgr_set_blk_ro(device_path, false);
- }
- // check_fs requires apex runtime library
- if (fs_mgr_overlayfs_already_mounted("/data", false)) {
- entry.fs_mgr_flags.check = true;
- }
- bool mounted = false;
- for (auto fs_type : filesystem_candidates) {
- entry.fs_type = fs_type;
- if (fs_mgr_do_mount_one(entry) == 0) {
- mounted = true;
- break;
- }
- }
- if (!createcon.Restore()) {
- return false;
- }
- if (!mounted) {
- rmdir(kScratchMountPoint.c_str());
- return false;
- }
- return true;
-}
-
-const std::string kMkF2fs("/system/bin/make_f2fs");
-const std::string kMkExt4("/system/bin/mke2fs");
-
-// Note: The scratch partition of DSU is managed by gsid, and should be initialized during
-// first-stage-mount. Just check if the DM device for DSU scratch partition is created or not.
-static std::string GetDsuScratchDevice() {
- auto& dm = DeviceMapper::Instance();
- std::string device;
- if (dm.GetState(android::gsi::kDsuScratch) != DmDeviceState::INVALID &&
- dm.GetDmDevicePathByName(android::gsi::kDsuScratch, &device)) {
- return device;
- }
- return "";
-}
-
-// This returns the scratch device that was detected during early boot (first-
-// stage init). If the device was created later, for example during setup for
-// the adb remount command, it can return an empty string since it does not
-// query ImageManager. (Note that ImageManager in first-stage init will always
-// use device-mapper, since /data is not available to use loop devices.)
-static std::string GetBootScratchDevice() {
- // Note: fs_mgr_is_dsu_running() always returns false in recovery or fastbootd.
- if (fs_mgr_is_dsu_running()) {
- return GetDsuScratchDevice();
- }
-
- auto& dm = DeviceMapper::Instance();
-
- // If there is a scratch partition allocated in /data or on super, we
- // automatically prioritize that over super_other or system_other.
- // Some devices, for example, have a write-protected eMMC and the
- // super partition cannot be used even if it exists.
- std::string device;
- auto partition_name = android::base::Basename(kScratchMountPoint);
- if (dm.GetState(partition_name) != DmDeviceState::INVALID &&
- dm.GetDmDevicePathByName(partition_name, &device)) {
- return device;
- }
-
- return "";
-}
-
-bool MakeScratchFilesystem(const std::string& scratch_device) {
- // Force mkfs by design for overlay support of adb remount, simplify and
- // thus do not rely on fsck to correct problems that could creep in.
- auto fs_type = ""s;
- auto command = ""s;
- if (!access(kMkF2fs.c_str(), X_OK) && fs_mgr_filesystem_available("f2fs")) {
- fs_type = "f2fs";
- command = kMkF2fs + " -w 4096 -f -d1 -l" + android::base::Basename(kScratchMountPoint);
- } else if (!access(kMkExt4.c_str(), X_OK) && fs_mgr_filesystem_available("ext4")) {
- fs_type = "ext4";
- command = kMkExt4 + " -F -b 4096 -t ext4 -m 0 -O has_journal -M " + kScratchMountPoint;
- } else {
- LERROR << "No supported mkfs command or filesystem driver available, supported filesystems "
- "are: f2fs, ext4";
- return false;
- }
- command += " " + scratch_device + " >/dev/null 2>/dev/null </dev/null";
- fs_mgr_set_blk_ro(scratch_device, false);
- auto ret = system(command.c_str());
- if (ret) {
- LERROR << "make " << fs_type << " filesystem on " << scratch_device << " return=" << ret;
- return false;
- }
- return true;
-}
-
-static void TruncatePartitionsWithSuffix(MetadataBuilder* builder, const std::string& suffix) {
- auto& dm = DeviceMapper::Instance();
-
- // Remove <other> partitions
- for (const auto& group : builder->ListGroups()) {
- for (const auto& part : builder->ListPartitionsInGroup(group)) {
- const auto& name = part->name();
- if (!android::base::EndsWith(name, suffix)) {
- continue;
- }
- if (dm.GetState(name) != DmDeviceState::INVALID && !DestroyLogicalPartition(name)) {
- continue;
- }
- builder->ResizePartition(builder->FindPartition(name), 0);
- }
- }
-}
-
-// Create or update a scratch partition within super.
-static bool CreateDynamicScratch(std::string* scratch_device, bool* partition_exists) {
- const auto partition_name = android::base::Basename(kScratchMountPoint);
-
- auto& dm = DeviceMapper::Instance();
- *partition_exists = dm.GetState(partition_name) != DmDeviceState::INVALID;
-
- auto partition_create = !*partition_exists;
- auto slot_number = fs_mgr_overlayfs_slot_number();
- auto super_device = fs_mgr_overlayfs_super_device(slot_number);
- auto builder = MetadataBuilder::New(super_device, slot_number);
- if (!builder) {
- LERROR << "open " << super_device << " metadata";
- return false;
- }
- auto partition = builder->FindPartition(partition_name);
- *partition_exists = partition != nullptr;
- auto changed = false;
- if (!*partition_exists) {
- partition = builder->AddPartition(partition_name, LP_PARTITION_ATTR_NONE);
- if (!partition) {
- LERROR << "create " << partition_name;
- return false;
- }
- changed = true;
- }
- // Take half of free space, minimum 512MB or maximum free - margin.
- static constexpr auto kMinimumSize = uint64_t(512 * 1024 * 1024);
- if (partition->size() < kMinimumSize) {
- auto partition_size =
- builder->AllocatableSpace() - builder->UsedSpace() + partition->size();
- if ((partition_size > kMinimumSize) || !partition->size()) {
- // Leave some space for free space jitter of a few erase
- // blocks, in case they are needed for any individual updates
- // to any other partition that needs to be flashed while
- // overlayfs is in force. Of course if margin_size is not
- // enough could normally get a flash failure, so
- // ResizePartition() will delete the scratch partition in
- // order to fulfill. Deleting scratch will destroy all of
- // the adb remount overrides :-( .
- auto margin_size = uint64_t(3 * 256 * 1024);
- BlockDeviceInfo info;
- if (builder->GetBlockDeviceInfo(fs_mgr_get_super_partition_name(slot_number), &info)) {
- margin_size = 3 * info.logical_block_size;
- }
- partition_size = std::max(std::min(kMinimumSize, partition_size - margin_size),
- partition_size / 2);
- if (partition_size > partition->size()) {
- if (!builder->ResizePartition(partition, partition_size)) {
- // Try to free up space by deallocating partitions in the other slot.
- TruncatePartitionsWithSuffix(builder.get(), fs_mgr_get_other_slot_suffix());
-
- partition_size =
- builder->AllocatableSpace() - builder->UsedSpace() + partition->size();
- partition_size = std::max(std::min(kMinimumSize, partition_size - margin_size),
- partition_size / 2);
- if (!builder->ResizePartition(partition, partition_size)) {
- LERROR << "resize " << partition_name;
- return false;
- }
- }
- if (!partition_create) DestroyLogicalPartition(partition_name);
- changed = true;
- *partition_exists = false;
- }
- }
- }
- // land the update back on to the partition
- if (changed) {
- auto metadata = builder->Export();
- if (!metadata || !UpdatePartitionTable(super_device, *metadata.get(), slot_number)) {
- LERROR << "add partition " << partition_name;
- return false;
- }
- }
-
- if (changed || partition_create) {
- CreateLogicalPartitionParams params = {
- .block_device = super_device,
- .metadata_slot = slot_number,
- .partition_name = partition_name,
- .force_writable = true,
- .timeout_ms = 10s,
- };
- if (!CreateLogicalPartition(params, scratch_device)) {
- return false;
- }
- } else if (scratch_device->empty()) {
- *scratch_device = GetBootScratchDevice();
- }
- return true;
-}
-
-static inline uint64_t GetIdealDataScratchSize() {
- BlockDeviceInfo super_info;
- PartitionOpener opener;
- if (!opener.GetInfo(fs_mgr_get_super_partition_name(), &super_info)) {
- LERROR << "could not get block device info for super";
- return 0;
- }
-
- struct statvfs s;
- if (statvfs("/data", &s) < 0) {
- PERROR << "could not statfs /data";
- return 0;
- }
-
- auto ideal_size = std::min(super_info.size, (uint64_t(s.f_frsize) * s.f_bfree) / 2);
-
- // Align up to the filesystem block size.
- if (auto remainder = ideal_size % s.f_bsize; remainder > 0) {
- ideal_size += s.f_bsize - remainder;
- }
- return ideal_size;
-}
-
-static bool CreateScratchOnData(std::string* scratch_device, bool* partition_exists) {
- *partition_exists = false;
-
- auto images = IImageManager::Open("remount", 10s);
- if (!images) {
- return false;
- }
-
- auto partition_name = android::base::Basename(kScratchMountPoint);
- if (images->GetMappedImageDevice(partition_name, scratch_device)) {
- *partition_exists = true;
- return true;
- }
-
- // Note: calling RemoveDisabledImages here ensures that we do not race with
- // clean_scratch_files and accidentally try to map an image that will be
- // deleted.
- if (!images->RemoveDisabledImages()) {
- return false;
- }
- if (!images->BackingImageExists(partition_name)) {
- auto size = android::base::GetUintProperty<uint64_t>(kDataScratchSizeMbProp, 0) * 1_MiB;
- if (!size) {
- size = GetIdealDataScratchSize();
- }
- if (!size) {
- size = 2_GiB;
- }
-
- auto flags = IImageManager::CREATE_IMAGE_DEFAULT;
-
- if (!images->CreateBackingImage(partition_name, size, flags)) {
- LERROR << "could not create scratch image of " << size << " bytes";
- return false;
- }
- }
- if (!images->MapImageDevice(partition_name, 10s, scratch_device)) {
- LERROR << "could not map scratch image";
- // If we cannot use this image, then remove it.
- TeardownDataScratch(images.get(), partition_name, false /* was_mounted */);
- return false;
- }
- return true;
-}
-
-static bool CanUseSuperPartition(const Fstab& fstab) {
- auto slot_number = fs_mgr_overlayfs_slot_number();
- auto super_device = fs_mgr_overlayfs_super_device(slot_number);
- if (!fs_mgr_rw_access(super_device) || !fs_mgr_overlayfs_has_logical(fstab)) {
- return false;
- }
- auto metadata = ReadMetadata(super_device, slot_number);
- if (!metadata) {
- return false;
- }
- return true;
-}
-
-bool fs_mgr_overlayfs_create_scratch(const Fstab& fstab, std::string* scratch_device,
- bool* partition_exists) {
- // Use the DSU scratch device managed by gsid if within a DSU system.
- if (fs_mgr_is_dsu_running()) {
- *scratch_device = GetDsuScratchDevice();
- *partition_exists = !scratch_device->empty();
- return *partition_exists;
- }
-
- // Try ImageManager on /data first.
- bool can_use_data = false;
- if (FilesystemHasReliablePinning("/data", &can_use_data) && can_use_data) {
- if (CreateScratchOnData(scratch_device, partition_exists)) {
- return true;
- }
- LOG(WARNING) << "Failed to allocate scratch on /data, fallback to use free space on super";
- }
- // If that fails, see if we can land on super.
- if (CanUseSuperPartition(fstab)) {
- return CreateDynamicScratch(scratch_device, partition_exists);
- }
- return false;
-}
-
-// Create and mount kScratchMountPoint storage if we have logical partitions
-bool fs_mgr_overlayfs_setup_scratch(const Fstab& fstab) {
- if (fs_mgr_overlayfs_already_mounted(kScratchMountPoint, false)) {
- return true;
- }
-
- std::string scratch_device;
- bool partition_exists;
- if (!fs_mgr_overlayfs_create_scratch(fstab, &scratch_device, &partition_exists)) {
- LOG(ERROR) << "Failed to create scratch partition";
- return false;
- }
-
- // If the partition exists, assume first that it can be mounted.
- if (partition_exists) {
- if (MountScratch(scratch_device)) {
- if (fs_mgr_access(kScratchMountPoint + kOverlayTopDir) ||
- fs_mgr_filesystem_has_space(kScratchMountPoint)) {
- return true;
- }
- // declare it useless, no overrides and no free space
- if (!fs_mgr_overlayfs_umount_scratch()) {
- LOG(ERROR) << "Unable to unmount scratch partition";
- return false;
- }
- }
- }
-
- if (!MakeScratchFilesystem(scratch_device)) {
- LOG(ERROR) << "Failed to format scratch partition";
- return false;
- }
-
- return MountScratch(scratch_device);
-}
-
-#if ALLOW_ADBD_DISABLE_VERITY
-constexpr bool kAllowOverlayfs = true;
-#else
-constexpr bool kAllowOverlayfs = false;
-#endif
-
-// NOTE: OverlayfsSetupAllowed() must be "stricter" than OverlayfsTeardownAllowed().
-// Setup is allowed only if teardown is also allowed.
-bool OverlayfsSetupAllowed(bool verbose = false) {
- if (!kAllowOverlayfs) {
- if (verbose) {
- LOG(ERROR) << "Overlayfs remounts can only be used in debuggable builds";
- }
- return false;
- }
- // Check mandatory kernel patches.
- if (fs_mgr_overlayfs_valid() == OverlayfsValidResult::kNotSupported) {
- if (verbose) {
- LOG(ERROR) << "Kernel does not support overlayfs";
- }
- return false;
- }
- // in recovery or fastbootd, not allowed!
- if (fs_mgr_in_recovery()) {
- if (verbose) {
- LOG(ERROR) << "Unsupported overlayfs setup from recovery";
- }
- return false;
- }
- return true;
-}
-
-constexpr bool OverlayfsTeardownAllowed() {
- // Never allow on non-debuggable build.
- return kAllowOverlayfs;
-}
-
-} // namespace
-
-bool fs_mgr_wants_overlayfs(FstabEntry* entry) {
- // Don't check entries that are managed by vold.
- if (entry->fs_mgr_flags.vold_managed || entry->fs_mgr_flags.recovery_only) return false;
-
- // *_other doesn't want overlayfs.
- if (entry->fs_mgr_flags.slot_select_other) return false;
-
- // Only concerned with readonly partitions.
- if (!(entry->flags & MS_RDONLY)) return false;
-
- // If unbindable, do not allow overlayfs as this could expose us to
- // security issues. On Android, this could also be used to turn off
- // the ability to overlay an otherwise acceptable filesystem since
- // /system and /vendor are never bound(sic) to.
- if (entry->flags & MS_UNBINDABLE) return false;
-
- if (!fs_mgr_overlayfs_enabled(entry)) return false;
-
- return true;
-}
-
-Fstab fs_mgr_overlayfs_candidate_list(const Fstab& fstab) {
- android::fs_mgr::Fstab mounts;
- if (!android::fs_mgr::ReadFstabFromFile("/proc/mounts", &mounts)) {
- PLOG(ERROR) << "Failed to read /proc/mounts";
- return {};
- }
-
- Fstab candidates;
- for (const auto& entry : fstab) {
- // Filter out partitions whose type doesn't match what's mounted.
- // This avoids spammy behavior on devices which can mount different
- // filesystems for each partition.
- auto proc_mount_point = (entry.mount_point == "/system") ? "/" : entry.mount_point;
- auto mounted = GetEntryForMountPoint(&mounts, proc_mount_point);
- if (!mounted || mounted->fs_type != entry.fs_type) {
- continue;
- }
-
- FstabEntry new_entry = entry;
- if (!fs_mgr_overlayfs_already_mounted(entry.mount_point) &&
- !fs_mgr_wants_overlayfs(&new_entry)) {
- continue;
- }
- auto new_mount_point = fs_mgr_mount_point(entry.mount_point);
- auto duplicate_or_more_specific = false;
- for (auto it = candidates.begin(); it != candidates.end();) {
- auto it_mount_point = fs_mgr_mount_point(it->mount_point);
- if ((it_mount_point == new_mount_point) ||
- (android::base::StartsWith(new_mount_point, it_mount_point + "/"))) {
- duplicate_or_more_specific = true;
- break;
- }
- if (android::base::StartsWith(it_mount_point, new_mount_point + "/")) {
- it = candidates.erase(it);
- } else {
- ++it;
- }
- }
- if (!duplicate_or_more_specific) candidates.emplace_back(std::move(new_entry));
- }
- return candidates;
-}
-
-static void TryMountScratch() {
- // Note we get the boot scratch device here, which means if scratch was
- // just created through ImageManager, this could fail. In practice this
- // should not happen because "remount" detects this scenario (by checking
- // if verity is still disabled, i.e. no reboot occurred), and skips calling
- // fs_mgr_overlayfs_mount_all().
- auto scratch_device = GetBootScratchDevice();
- if (!fs_mgr_rw_access(scratch_device)) {
- return;
- }
- if (!WaitForFile(scratch_device, 10s)) {
- return;
- }
- if (!MountScratch(scratch_device, true /* readonly */)) {
- return;
- }
- auto has_overlayfs_dir = fs_mgr_access(kScratchMountPoint + kOverlayTopDir);
- fs_mgr_overlayfs_umount_scratch();
- if (has_overlayfs_dir) {
- MountScratch(scratch_device);
- }
-}
-
-bool fs_mgr_overlayfs_mount_all(Fstab* fstab) {
- if (!OverlayfsSetupAllowed()) {
- return false;
- }
- auto ret = true;
- auto scratch_can_be_mounted = true;
- for (const auto& entry : fs_mgr_overlayfs_candidate_list(*fstab)) {
- if (fs_mgr_is_verity_enabled(entry)) continue;
- auto mount_point = fs_mgr_mount_point(entry.mount_point);
- if (fs_mgr_overlayfs_already_mounted(mount_point)) {
- continue;
- }
- if (scratch_can_be_mounted) {
- scratch_can_be_mounted = false;
- TryMountScratch();
- }
- ret &= fs_mgr_overlayfs_mount(entry);
- }
- return ret;
-}
-
-bool fs_mgr_overlayfs_setup(const Fstab& fstab, const char* mount_point, bool* want_reboot,
- bool just_disabled_verity) {
- if (!OverlayfsSetupAllowed(/*verbose=*/true)) {
- return false;
- }
-
- if (!fs_mgr_boot_completed()) {
- LOG(ERROR) << "Cannot setup overlayfs before persistent properties are ready";
- return false;
- }
-
- auto candidates = fs_mgr_overlayfs_candidate_list(fstab);
- for (auto it = candidates.begin(); it != candidates.end();) {
- if (mount_point &&
- (fs_mgr_mount_point(it->mount_point) != fs_mgr_mount_point(mount_point))) {
- it = candidates.erase(it);
- continue;
- }
-
- auto verity_enabled = !just_disabled_verity && fs_mgr_is_verity_enabled(*it);
- if (verity_enabled) {
- it = candidates.erase(it);
- continue;
- }
- ++it;
- }
-
- if (candidates.empty()) {
- if (mount_point) {
- LOG(ERROR) << "No overlayfs candidate was found for " << mount_point;
- return false;
- }
- return true;
- }
-
- std::string dir;
- for (const auto& overlay_mount_point : OverlayMountPoints()) {
- if (overlay_mount_point == kScratchMountPoint) {
- if (!fs_mgr_overlayfs_setup_scratch(fstab)) {
- continue;
- }
- } else {
- if (GetEntryForMountPoint(&fstab, overlay_mount_point) == nullptr) {
- continue;
- }
- }
- dir = overlay_mount_point;
- break;
- }
- if (dir.empty()) {
- LOG(ERROR) << "Could not allocate backing storage for overlays";
- return false;
- }
-
- const auto overlay = fs_mgr_overlayfs_setup_dir(dir);
- if (overlay.empty()) {
- return false;
- }
-
- bool ok = true;
- for (const auto& entry : candidates) {
- auto fstab_mount_point = fs_mgr_mount_point(entry.mount_point);
- ok &= fs_mgr_overlayfs_setup_one(overlay, fstab_mount_point, want_reboot);
- }
- return ok;
-}
-
-struct MapInfo {
- // If set, partition is owned by ImageManager.
- std::unique_ptr<IImageManager> images;
- // If set, and images is null, this is a DAP partition.
- std::string name;
- // If set, and images and name are empty, this is a non-dynamic partition.
- std::string device;
-
- MapInfo() = default;
- MapInfo(MapInfo&&) = default;
- ~MapInfo() {
- if (images) {
- images->UnmapImageDevice(name);
- } else if (!name.empty()) {
- DestroyLogicalPartition(name);
- }
- }
-};
-
-// Note: This function never returns the DSU scratch device in recovery or fastbootd,
-// because the DSU scratch is created in the first-stage-mount, which is not run in recovery.
-static std::optional<MapInfo> EnsureScratchMapped() {
- MapInfo info;
- info.device = GetBootScratchDevice();
- if (!info.device.empty()) {
- return {std::move(info)};
- }
- if (!fs_mgr_in_recovery()) {
- return {};
- }
-
- auto partition_name = android::base::Basename(kScratchMountPoint);
-
- // Check for scratch on /data first, before looking for a modified super
- // partition. We should only reach this code in recovery, because scratch
- // would otherwise always be mapped.
- auto images = IImageManager::Open("remount", 10s);
- if (images && images->BackingImageExists(partition_name)) {
- if (images->IsImageDisabled(partition_name)) {
- return {};
- }
- if (!images->MapImageDevice(partition_name, 10s, &info.device)) {
- return {};
- }
- info.name = partition_name;
- info.images = std::move(images);
- return {std::move(info)};
- }
-
- // Avoid uart spam by first checking for a scratch partition.
- auto metadata_slot = fs_mgr_overlayfs_slot_number();
- auto super_device = fs_mgr_overlayfs_super_device(metadata_slot);
- auto metadata = ReadCurrentMetadata(super_device);
- if (!metadata) {
- return {};
- }
-
- auto partition = FindPartition(*metadata.get(), partition_name);
- if (!partition) {
- return {};
- }
-
- CreateLogicalPartitionParams params = {
- .block_device = super_device,
- .metadata = metadata.get(),
- .partition = partition,
- .force_writable = true,
- .timeout_ms = 10s,
- };
- if (!CreateLogicalPartition(params, &info.device)) {
- return {};
- }
- info.name = partition_name;
- return {std::move(info)};
-}
-
-// This should only be reachable in recovery, where DSU scratch is not
-// automatically mapped.
-static bool MapDsuScratchDevice(std::string* device) {
- std::string dsu_slot;
- if (!android::gsi::IsGsiInstalled() || !android::gsi::GetActiveDsu(&dsu_slot) ||
- dsu_slot.empty()) {
- // Nothing to do if no DSU installation present.
- return false;
- }
-
- auto images = IImageManager::Open("dsu/" + dsu_slot, 10s);
- if (!images || !images->BackingImageExists(android::gsi::kDsuScratch)) {
- // Nothing to do if DSU scratch device doesn't exist.
- return false;
- }
-
- images->UnmapImageDevice(android::gsi::kDsuScratch);
- if (!images->MapImageDevice(android::gsi::kDsuScratch, 10s, device)) {
- return false;
- }
- return true;
-}
-
-static OverlayfsTeardownResult TeardownMountsAndScratch(const char* mount_point,
- bool* want_reboot) {
- bool should_destroy_scratch = false;
- auto rv = OverlayfsTeardownResult::Ok;
- for (const auto& overlay_mount_point : OverlayMountPoints()) {
- auto ok = fs_mgr_overlayfs_teardown_one(
- overlay_mount_point, mount_point ? fs_mgr_mount_point(mount_point) : "",
- want_reboot,
- overlay_mount_point == kScratchMountPoint ? &should_destroy_scratch : nullptr);
- if (!ok) {
- rv = OverlayfsTeardownResult::Error;
- }
- }
-
- // Do not attempt to destroy DSU scratch if within a DSU system,
- // because DSU scratch partition is managed by gsid.
- if (should_destroy_scratch && !fs_mgr_is_dsu_running()) {
- auto rv = fs_mgr_overlayfs_teardown_scratch(kScratchMountPoint, want_reboot);
- if (rv != OverlayfsTeardownResult::Ok) {
- return rv;
- }
- }
- // And now that we did what we could, lets inform
- // caller that there may still be more to do.
- if (!fs_mgr_boot_completed()) {
- LOG(ERROR) << "Cannot teardown overlayfs before persistent properties are ready";
- return OverlayfsTeardownResult::Error;
- }
- return rv;
-}
-
-// Returns false if teardown not permitted. If something is altered, set *want_reboot.
-OverlayfsTeardownResult fs_mgr_overlayfs_teardown(const char* mount_point, bool* want_reboot) {
- if (!OverlayfsTeardownAllowed()) {
- // Nothing to teardown.
- return OverlayfsTeardownResult::Ok;
- }
- // If scratch exists, but is not mounted, lets gain access to clean
- // specific override entries.
- auto mount_scratch = false;
- if ((mount_point != nullptr) && !fs_mgr_overlayfs_already_mounted(kScratchMountPoint, false)) {
- std::string scratch_device = GetBootScratchDevice();
- if (!scratch_device.empty()) {
- mount_scratch = MountScratch(scratch_device);
- }
- }
-
- auto rv = TeardownMountsAndScratch(mount_point, want_reboot);
-
- if (mount_scratch) {
- if (!fs_mgr_overlayfs_umount_scratch()) {
- return OverlayfsTeardownResult::Busy;
- }
- }
- return rv;
-}
-
-bool fs_mgr_overlayfs_is_setup() {
- if (!OverlayfsSetupAllowed()) {
- return false;
- }
- if (fs_mgr_overlayfs_already_mounted(kScratchMountPoint, false)) return true;
- Fstab fstab;
- if (!ReadDefaultFstab(&fstab)) {
- return false;
- }
- for (const auto& entry : fs_mgr_overlayfs_candidate_list(fstab)) {
- if (fs_mgr_is_verity_enabled(entry)) continue;
- if (fs_mgr_overlayfs_already_mounted(fs_mgr_mount_point(entry.mount_point))) return true;
- }
- return false;
-}
-
-namespace android {
-namespace fs_mgr {
-
-void MapScratchPartitionIfNeeded(Fstab* fstab,
- const std::function<bool(const std::set<std::string>&)>& init) {
- if (!OverlayfsSetupAllowed()) {
- return;
- }
- if (GetEntryForMountPoint(fstab, kScratchMountPoint) != nullptr) {
- return;
- }
-
- bool want_scratch = false;
- for (const auto& entry : fs_mgr_overlayfs_candidate_list(*fstab)) {
- if (fs_mgr_is_verity_enabled(entry)) {
- continue;
- }
- if (fs_mgr_overlayfs_already_mounted(fs_mgr_mount_point(entry.mount_point))) {
- continue;
- }
- want_scratch = true;
- break;
- }
- if (!want_scratch) {
- return;
- }
-
- if (ScratchIsOnData()) {
- if (auto images = IImageManager::Open("remount", 0ms)) {
- images->MapAllImages(init);
- }
- }
-
- // Physical or logical partitions will have already been mapped here,
- // so just ensure /dev/block symlinks exist.
- auto device = GetBootScratchDevice();
- if (!device.empty()) {
- init({android::base::Basename(device)});
- }
-}
-
-void CleanupOldScratchFiles() {
- if (!OverlayfsTeardownAllowed()) {
- return;
- }
- if (!ScratchIsOnData()) {
- return;
- }
- if (auto images = IImageManager::Open("remount", 0ms)) {
- images->RemoveDisabledImages();
- }
-}
-
-void TeardownAllOverlayForMountPoint(const std::string& mount_point) {
- if (!OverlayfsTeardownAllowed()) {
- return;
- }
- if (!fs_mgr_in_recovery()) {
- LERROR << __FUNCTION__ << "(): must be called within recovery.";
- return;
- }
-
- // Empty string means teardown everything.
- const std::string teardown_dir = mount_point.empty() ? "" : fs_mgr_mount_point(mount_point);
- constexpr bool* ignore_change = nullptr;
-
- // Teardown legacy overlay mount points that's not backed by a scratch device.
- for (const auto& overlay_mount_point : OverlayMountPoints()) {
- if (overlay_mount_point == kScratchMountPoint) {
- continue;
- }
- fs_mgr_overlayfs_teardown_one(overlay_mount_point, teardown_dir, ignore_change);
- }
-
- if (mount_point.empty()) {
- // Throw away the entire partition.
- auto partition_name = android::base::Basename(kScratchMountPoint);
- auto images = IImageManager::Open("remount", 10s);
- if (images && images->BackingImageExists(partition_name)) {
- if (images->DisableImage(partition_name)) {
- LOG(INFO) << "Disabled scratch partition for: " << kScratchMountPoint;
- } else {
- LOG(ERROR) << "Unable to disable scratch partition for " << kScratchMountPoint;
- }
- }
- }
-
- // Note if we just disabled scratch, this mount will fail.
- if (auto info = EnsureScratchMapped(); info.has_value()) {
- // Map scratch device, mount kScratchMountPoint and teardown kScratchMountPoint.
- fs_mgr_overlayfs_umount_scratch();
- if (MountScratch(info->device)) {
- bool should_destroy_scratch = false;
- fs_mgr_overlayfs_teardown_one(kScratchMountPoint, teardown_dir, ignore_change,
- &should_destroy_scratch);
- fs_mgr_overlayfs_umount_scratch();
- if (should_destroy_scratch) {
- fs_mgr_overlayfs_teardown_scratch(kScratchMountPoint, nullptr);
- }
- }
- }
-
- // Teardown DSU overlay if present.
- std::string scratch_device;
- if (MapDsuScratchDevice(&scratch_device)) {
- fs_mgr_overlayfs_umount_scratch();
- if (MountScratch(scratch_device)) {
- fs_mgr_overlayfs_teardown_one(kScratchMountPoint, teardown_dir, ignore_change);
- fs_mgr_overlayfs_umount_scratch();
- }
- DestroyLogicalPartition(android::gsi::kDsuScratch);
- }
-}
-
-} // namespace fs_mgr
-} // namespace android
-
-bool fs_mgr_overlayfs_already_mounted(const std::string& mount_point, bool overlay_only) {
- Fstab fstab;
- if (!ReadFstabFromFile("/proc/mounts", &fstab)) {
- return false;
- }
- const auto lowerdir = kLowerdirOption + mount_point;
- for (const auto& entry : fstab) {
- if (overlay_only && "overlay" != entry.fs_type && "overlayfs" != entry.fs_type) continue;
- if (mount_point != entry.mount_point) continue;
- if (!overlay_only) return true;
- const auto options = android::base::Split(entry.fs_options, ",");
- for (const auto& opt : options) {
- if (opt == lowerdir) {
- return true;
- }
- }
- }
- return false;
-}
diff --git a/fs_mgr/fs_mgr_overlayfs_control.cpp b/fs_mgr/fs_mgr_overlayfs_control.cpp
new file mode 100644
index 0000000..68576f2
--- /dev/null
+++ b/fs_mgr/fs_mgr_overlayfs_control.cpp
@@ -0,0 +1,986 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <dirent.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <linux/fs.h>
+#include <stdlib.h>
+#include <sys/stat.h>
+#include <sys/statvfs.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <algorithm>
+#include <memory>
+#include <optional>
+#include <string>
+#include <vector>
+
+#include <android-base/file.h>
+#include <android-base/properties.h>
+#include <android-base/strings.h>
+#include <fs_mgr.h>
+#include <fs_mgr_dm_linear.h>
+#include <fs_mgr_overlayfs.h>
+#include <fstab/fstab.h>
+#include <libdm/dm.h>
+#include <libfiemap/image_manager.h>
+#include <libgsi/libgsi.h>
+#include <liblp/builder.h>
+#include <liblp/liblp.h>
+#include <storage_literals/storage_literals.h>
+
+#include "fs_mgr_overlayfs_control.h"
+#include "fs_mgr_overlayfs_mount.h"
+#include "fs_mgr_priv.h"
+#include "libfiemap/utility.h"
+
+using namespace std::literals;
+using namespace android::dm;
+using namespace android::fs_mgr;
+using namespace android::storage_literals;
+using android::fiemap::FilesystemHasReliablePinning;
+using android::fiemap::IImageManager;
+
+namespace {
+
+constexpr char kDataScratchSizeMbProp[] = "fs_mgr.overlayfs.data_scratch_size_mb";
+
+constexpr char kPhysicalDevice[] = "/dev/block/by-name/";
+constexpr char kScratchImageMetadata[] = "/metadata/gsi/remount/lp_metadata";
+
+constexpr char kMkF2fs[] = "/system/bin/make_f2fs";
+constexpr char kMkExt4[] = "/system/bin/mke2fs";
+
+// Return true if everything is mounted, but before adb is started. Right
+// after 'trigger load_persist_props_action' is done.
+static bool fs_mgr_boot_completed() {
+ return android::base::GetBoolProperty("ro.persistent_properties.ready", false);
+}
+
+// Note: this is meant only for recovery/first-stage init.
+static bool ScratchIsOnData() {
+ // The scratch partition of DSU is managed by gsid.
+ if (fs_mgr_is_dsu_running()) {
+ return false;
+ }
+ return access(kScratchImageMetadata, F_OK) == 0;
+}
+
+static bool fs_mgr_rm_all(const std::string& path, bool* change = nullptr, int level = 0) {
+ std::unique_ptr<DIR, decltype(&closedir)> dir(opendir(path.c_str()), closedir);
+ if (!dir) {
+ if (errno == ENOENT) {
+ return true;
+ }
+ PERROR << "opendir " << path << " depth=" << level;
+ if ((errno == EPERM) && (level != 0)) {
+ return true;
+ }
+ return false;
+ }
+ dirent* entry;
+ auto ret = true;
+ while ((entry = readdir(dir.get()))) {
+ if (("."s == entry->d_name) || (".."s == entry->d_name)) continue;
+ auto file = path + "/" + entry->d_name;
+ if (entry->d_type == DT_UNKNOWN) {
+ struct stat st;
+ if (!lstat(file.c_str(), &st) && (st.st_mode & S_IFDIR)) entry->d_type = DT_DIR;
+ }
+ if (entry->d_type == DT_DIR) {
+ ret &= fs_mgr_rm_all(file, change, level + 1);
+ if (!rmdir(file.c_str())) {
+ if (change) *change = true;
+ } else {
+ if (errno != ENOENT) ret = false;
+ PERROR << "rmdir " << file << " depth=" << level;
+ }
+ continue;
+ }
+ if (!unlink(file.c_str())) {
+ if (change) *change = true;
+ } else {
+ if (errno != ENOENT) ret = false;
+ PERROR << "rm " << file << " depth=" << level;
+ }
+ }
+ return ret;
+}
+
+std::string fs_mgr_overlayfs_setup_dir(const std::string& dir) {
+ auto top = dir + "/" + kOverlayTopDir;
+
+ AutoSetFsCreateCon createcon(kOverlayfsFileContext);
+ if (!createcon.Ok()) {
+ return {};
+ }
+ if (mkdir(top.c_str(), 0755) != 0 && errno != EEXIST) {
+ PERROR << "mkdir " << top;
+ return {};
+ }
+ if (!createcon.Restore()) {
+ return {};
+ }
+ return top;
+}
+
+bool fs_mgr_overlayfs_setup_one(const std::string& overlay, const std::string& mount_point,
+ bool* want_reboot) {
+ if (fs_mgr_overlayfs_already_mounted(mount_point)) {
+ return true;
+ }
+ auto fsrec_mount_point = overlay + "/" + android::base::Basename(mount_point) + "/";
+
+ AutoSetFsCreateCon createcon(kOverlayfsFileContext);
+ if (!createcon.Ok()) {
+ return false;
+ }
+ if (mkdir(fsrec_mount_point.c_str(), 0755) != 0 && errno != EEXIST) {
+ PERROR << "mkdir " << fsrec_mount_point;
+ return false;
+ }
+ if (mkdir((fsrec_mount_point + kWorkName).c_str(), 0755) != 0 && errno != EEXIST) {
+ PERROR << "mkdir " << fsrec_mount_point << kWorkName;
+ return false;
+ }
+ if (!createcon.Restore()) {
+ return false;
+ }
+
+ createcon = {};
+
+ auto new_context = fs_mgr_get_context(mount_point);
+ if (new_context.empty() || !createcon.Set(new_context)) {
+ return false;
+ }
+
+ auto upper = fsrec_mount_point + kUpperName;
+ if (mkdir(upper.c_str(), 0755) != 0 && errno != EEXIST) {
+ PERROR << "mkdir " << upper;
+ return false;
+ }
+ if (!createcon.Restore()) {
+ return false;
+ }
+
+ if (want_reboot) *want_reboot = true;
+
+ return true;
+}
+
+static uint32_t fs_mgr_overlayfs_slot_number() {
+ return SlotNumberForSlotSuffix(fs_mgr_get_slot_suffix());
+}
+
+static bool fs_mgr_overlayfs_has_logical(const Fstab& fstab) {
+ for (const auto& entry : fstab) {
+ if (entry.fs_mgr_flags.logical) {
+ return true;
+ }
+ }
+ return false;
+}
+
+OverlayfsTeardownResult TeardownDataScratch(IImageManager* images,
+ const std::string& partition_name, bool was_mounted) {
+ if (!images) {
+ return OverlayfsTeardownResult::Error;
+ }
+ if (!images->DisableImage(partition_name)) {
+ return OverlayfsTeardownResult::Error;
+ }
+ if (was_mounted) {
+ // If overlayfs was mounted, don't bother trying to unmap since
+ // it'll fail and create error spam.
+ return OverlayfsTeardownResult::Busy;
+ }
+ if (!images->UnmapImageIfExists(partition_name)) {
+ return OverlayfsTeardownResult::Busy;
+ }
+ if (!images->DeleteBackingImage(partition_name)) {
+ return OverlayfsTeardownResult::Busy;
+ }
+ return OverlayfsTeardownResult::Ok;
+}
+
+OverlayfsTeardownResult fs_mgr_overlayfs_teardown_scratch(const std::string& overlay,
+ bool* change) {
+ // umount and delete kScratchMountPoint storage if we have logical partitions
+ if (overlay != kScratchMountPoint) {
+ return OverlayfsTeardownResult::Ok;
+ }
+
+ // Validation check.
+ if (fs_mgr_is_dsu_running()) {
+ LERROR << "Destroying DSU scratch is not allowed.";
+ return OverlayfsTeardownResult::Error;
+ }
+
+ bool was_mounted = fs_mgr_overlayfs_already_mounted(kScratchMountPoint, false);
+ if (was_mounted) {
+ fs_mgr_overlayfs_umount_scratch();
+ }
+
+ const auto partition_name = android::base::Basename(kScratchMountPoint);
+
+ auto images = IImageManager::Open("remount", 10s);
+ if (images && images->BackingImageExists(partition_name)) {
+ // No need to check super partition, if we knew we had a scratch device
+ // in /data.
+ return TeardownDataScratch(images.get(), partition_name, was_mounted);
+ }
+
+ auto slot_number = fs_mgr_overlayfs_slot_number();
+ const auto super_device = kPhysicalDevice + fs_mgr_get_super_partition_name();
+ if (access(super_device.c_str(), R_OK | W_OK)) {
+ return OverlayfsTeardownResult::Ok;
+ }
+
+ auto builder = MetadataBuilder::New(super_device, slot_number);
+ if (!builder) {
+ return OverlayfsTeardownResult::Ok;
+ }
+ if (builder->FindPartition(partition_name) == nullptr) {
+ return OverlayfsTeardownResult::Ok;
+ }
+ builder->RemovePartition(partition_name);
+ auto metadata = builder->Export();
+ if (metadata && UpdatePartitionTable(super_device, *metadata.get(), slot_number)) {
+ if (change) *change = true;
+ if (!DestroyLogicalPartition(partition_name)) {
+ return OverlayfsTeardownResult::Error;
+ }
+ } else {
+ LERROR << "delete partition " << overlay;
+ return OverlayfsTeardownResult::Error;
+ }
+
+ if (was_mounted) {
+ return OverlayfsTeardownResult::Busy;
+ }
+ return OverlayfsTeardownResult::Ok;
+}
+
+bool fs_mgr_overlayfs_teardown_one(const std::string& overlay, const std::string& mount_point,
+ bool* change, bool* should_destroy_scratch = nullptr) {
+ const auto top = overlay + "/" + kOverlayTopDir;
+
+ if (access(top.c_str(), F_OK)) {
+ if (should_destroy_scratch) *should_destroy_scratch = true;
+ return true;
+ }
+
+ auto cleanup_all = mount_point.empty();
+ const auto partition_name = android::base::Basename(mount_point);
+ const auto oldpath = top + (cleanup_all ? "" : ("/" + partition_name));
+ const auto newpath = cleanup_all ? overlay + "/." + kOverlayTopDir + ".teardown"
+ : top + "/." + partition_name + ".teardown";
+ auto ret = fs_mgr_rm_all(newpath);
+ if (!rename(oldpath.c_str(), newpath.c_str())) {
+ if (change) *change = true;
+ } else if (errno != ENOENT) {
+ ret = false;
+ PERROR << "mv " << oldpath << " " << newpath;
+ }
+ ret &= fs_mgr_rm_all(newpath, change);
+ if (!rmdir(newpath.c_str())) {
+ if (change) *change = true;
+ } else if (errno != ENOENT) {
+ ret = false;
+ PERROR << "rmdir " << newpath;
+ }
+ if (!cleanup_all) {
+ if (!rmdir(top.c_str())) {
+ if (change) *change = true;
+ cleanup_all = true;
+ } else if (errno == ENOTEMPTY) {
+ cleanup_all = true;
+ // cleanup all if the content is all hidden (leading .)
+ std::unique_ptr<DIR, decltype(&closedir)> dir(opendir(top.c_str()), closedir);
+ if (!dir) {
+ PERROR << "opendir " << top;
+ } else {
+ dirent* entry;
+ while ((entry = readdir(dir.get()))) {
+ if (entry->d_name[0] != '.') {
+ cleanup_all = false;
+ break;
+ }
+ }
+ }
+ } else if (errno == ENOENT) {
+ cleanup_all = true;
+ } else {
+ ret = false;
+ PERROR << "rmdir " << top;
+ }
+ }
+ if (should_destroy_scratch) *should_destroy_scratch = cleanup_all;
+ return ret;
+}
+
+// Note: The scratch partition of DSU is managed by gsid, and should be initialized during
+// first-stage-mount. Just check if the DM device for DSU scratch partition is created or not.
+static std::string GetDsuScratchDevice() {
+ auto& dm = DeviceMapper::Instance();
+ std::string device;
+ if (dm.GetState(android::gsi::kDsuScratch) != DmDeviceState::INVALID &&
+ dm.GetDmDevicePathByName(android::gsi::kDsuScratch, &device)) {
+ return device;
+ }
+ return "";
+}
+
+// This returns the scratch device that was detected during early boot (first-
+// stage init). If the device was created later, for example during setup for
+// the adb remount command, it can return an empty string since it does not
+// query ImageManager. (Note that ImageManager in first-stage init will always
+// use device-mapper, since /data is not available to use loop devices.)
+static std::string GetBootScratchDevice() {
+ // Note: fs_mgr_is_dsu_running() always returns false in recovery or fastbootd.
+ if (fs_mgr_is_dsu_running()) {
+ return GetDsuScratchDevice();
+ }
+
+ auto& dm = DeviceMapper::Instance();
+
+ // If there is a scratch partition allocated in /data or on super, we
+ // automatically prioritize that over super_other or system_other.
+ // Some devices, for example, have a write-protected eMMC and the
+ // super partition cannot be used even if it exists.
+ std::string device;
+ auto partition_name = android::base::Basename(kScratchMountPoint);
+ if (dm.GetState(partition_name) != DmDeviceState::INVALID &&
+ dm.GetDmDevicePathByName(partition_name, &device)) {
+ return device;
+ }
+
+ return "";
+}
+
+bool MakeScratchFilesystem(const std::string& scratch_device) {
+ // Force mkfs by design for overlay support of adb remount, simplify and
+ // thus do not rely on fsck to correct problems that could creep in.
+ auto fs_type = ""s;
+ auto command = ""s;
+ if (!access(kMkF2fs, X_OK) && fs_mgr_filesystem_available("f2fs")) {
+ fs_type = "f2fs";
+ command = kMkF2fs + " -w "s;
+ command += std::to_string(getpagesize());
+ command += " -f -d1 -l" + android::base::Basename(kScratchMountPoint);
+ } else if (!access(kMkExt4, X_OK) && fs_mgr_filesystem_available("ext4")) {
+ fs_type = "ext4";
+ command = kMkExt4 + " -F -b 4096 -t ext4 -m 0 -O has_journal -M "s + kScratchMountPoint;
+ } else {
+ LERROR << "No supported mkfs command or filesystem driver available, supported filesystems "
+ "are: f2fs, ext4";
+ return false;
+ }
+ command += " " + scratch_device + " >/dev/null 2>/dev/null </dev/null";
+ fs_mgr_set_blk_ro(scratch_device, false);
+ auto ret = system(command.c_str());
+ if (ret) {
+ LERROR << "make " << fs_type << " filesystem on " << scratch_device << " return=" << ret;
+ return false;
+ }
+ return true;
+}
+
+static void TruncatePartitionsWithSuffix(MetadataBuilder* builder, const std::string& suffix) {
+ auto& dm = DeviceMapper::Instance();
+
+ // Remove <other> partitions
+ for (const auto& group : builder->ListGroups()) {
+ for (const auto& part : builder->ListPartitionsInGroup(group)) {
+ const auto& name = part->name();
+ if (!android::base::EndsWith(name, suffix)) {
+ continue;
+ }
+ if (dm.GetState(name) != DmDeviceState::INVALID && !DestroyLogicalPartition(name)) {
+ continue;
+ }
+ builder->ResizePartition(builder->FindPartition(name), 0);
+ }
+ }
+}
+
+// Create or update a scratch partition within super.
+static bool CreateDynamicScratch(std::string* scratch_device, bool* partition_exists) {
+ const auto partition_name = android::base::Basename(kScratchMountPoint);
+
+ auto& dm = DeviceMapper::Instance();
+ *partition_exists = dm.GetState(partition_name) != DmDeviceState::INVALID;
+
+ auto partition_create = !*partition_exists;
+ auto slot_number = fs_mgr_overlayfs_slot_number();
+ const auto super_device = kPhysicalDevice + fs_mgr_get_super_partition_name();
+ auto builder = MetadataBuilder::New(super_device, slot_number);
+ if (!builder) {
+ LERROR << "open " << super_device << " metadata";
+ return false;
+ }
+ auto partition = builder->FindPartition(partition_name);
+ *partition_exists = partition != nullptr;
+ auto changed = false;
+ if (!*partition_exists) {
+ partition = builder->AddPartition(partition_name, LP_PARTITION_ATTR_NONE);
+ if (!partition) {
+ LERROR << "create " << partition_name;
+ return false;
+ }
+ changed = true;
+ }
+ // Take half of free space, minimum 512MB or maximum free - margin.
+ static constexpr auto kMinimumSize = uint64_t(512 * 1024 * 1024);
+ if (partition->size() < kMinimumSize) {
+ auto partition_size =
+ builder->AllocatableSpace() - builder->UsedSpace() + partition->size();
+ if ((partition_size > kMinimumSize) || !partition->size()) {
+ partition_size = std::max(std::min(kMinimumSize, partition_size), partition_size / 2);
+ if (partition_size > partition->size()) {
+ if (!builder->ResizePartition(partition, partition_size)) {
+ // Try to free up space by deallocating partitions in the other slot.
+ TruncatePartitionsWithSuffix(builder.get(), fs_mgr_get_other_slot_suffix());
+
+ partition_size =
+ builder->AllocatableSpace() - builder->UsedSpace() + partition->size();
+ partition_size =
+ std::max(std::min(kMinimumSize, partition_size), partition_size / 2);
+ if (!builder->ResizePartition(partition, partition_size)) {
+ LERROR << "resize " << partition_name;
+ return false;
+ }
+ }
+ if (!partition_create) DestroyLogicalPartition(partition_name);
+ changed = true;
+ *partition_exists = false;
+ }
+ }
+ }
+ // land the update back on to the partition
+ if (changed) {
+ auto metadata = builder->Export();
+ if (!metadata || !UpdatePartitionTable(super_device, *metadata.get(), slot_number)) {
+ LERROR << "add partition " << partition_name;
+ return false;
+ }
+ }
+
+ if (changed || partition_create) {
+ CreateLogicalPartitionParams params = {
+ .block_device = super_device,
+ .metadata_slot = slot_number,
+ .partition_name = partition_name,
+ .force_writable = true,
+ .timeout_ms = 10s,
+ };
+ if (!CreateLogicalPartition(params, scratch_device)) {
+ return false;
+ }
+ } else if (scratch_device->empty()) {
+ *scratch_device = GetBootScratchDevice();
+ }
+ return true;
+}
+
+static inline uint64_t GetIdealDataScratchSize() {
+ BlockDeviceInfo super_info;
+ PartitionOpener opener;
+ if (!opener.GetInfo(fs_mgr_get_super_partition_name(), &super_info)) {
+ LERROR << "could not get block device info for super";
+ return 0;
+ }
+
+ struct statvfs s;
+ if (statvfs("/data", &s) < 0) {
+ PERROR << "could not statfs /data";
+ return 0;
+ }
+
+ auto ideal_size = std::min(super_info.size, uint64_t(uint64_t(s.f_frsize) * s.f_bfree * 0.85));
+
+ // Align up to the filesystem block size.
+ if (auto remainder = ideal_size % s.f_bsize; remainder > 0) {
+ ideal_size += s.f_bsize - remainder;
+ }
+ return ideal_size;
+}
+
+static bool CreateScratchOnData(std::string* scratch_device, bool* partition_exists) {
+ *partition_exists = false;
+
+ auto images = IImageManager::Open("remount", 10s);
+ if (!images) {
+ return false;
+ }
+
+ auto partition_name = android::base::Basename(kScratchMountPoint);
+ if (images->GetMappedImageDevice(partition_name, scratch_device)) {
+ *partition_exists = true;
+ return true;
+ }
+
+ // Note: calling RemoveDisabledImages here ensures that we do not race with
+ // clean_scratch_files and accidentally try to map an image that will be
+ // deleted.
+ if (!images->RemoveDisabledImages()) {
+ return false;
+ }
+ if (!images->BackingImageExists(partition_name)) {
+ auto size = android::base::GetUintProperty<uint64_t>(kDataScratchSizeMbProp, 0) * 1_MiB;
+ if (!size) {
+ size = GetIdealDataScratchSize();
+ }
+ if (!size) {
+ size = 2_GiB;
+ }
+
+ auto flags = IImageManager::CREATE_IMAGE_DEFAULT;
+
+ if (!images->CreateBackingImage(partition_name, size, flags)) {
+ LERROR << "could not create scratch image of " << size << " bytes";
+ return false;
+ }
+ }
+ if (!images->MapImageDevice(partition_name, 10s, scratch_device)) {
+ LERROR << "could not map scratch image";
+ // If we cannot use this image, then remove it.
+ TeardownDataScratch(images.get(), partition_name, false /* was_mounted */);
+ return false;
+ }
+ return true;
+}
+
+static bool CanUseSuperPartition(const Fstab& fstab) {
+ auto slot_number = fs_mgr_overlayfs_slot_number();
+ const auto super_device = kPhysicalDevice + fs_mgr_get_super_partition_name();
+ if (access(super_device.c_str(), R_OK | W_OK) || !fs_mgr_overlayfs_has_logical(fstab)) {
+ return false;
+ }
+ auto metadata = ReadMetadata(super_device, slot_number);
+ if (!metadata) {
+ return false;
+ }
+ return true;
+}
+
+bool fs_mgr_overlayfs_create_scratch(const Fstab& fstab, std::string* scratch_device,
+ bool* partition_exists) {
+ // Use the DSU scratch device managed by gsid if within a DSU system.
+ if (fs_mgr_is_dsu_running()) {
+ *scratch_device = GetDsuScratchDevice();
+ *partition_exists = !scratch_device->empty();
+ return *partition_exists;
+ }
+
+ // Try ImageManager on /data first.
+ bool can_use_data = false;
+ if (FilesystemHasReliablePinning("/data", &can_use_data) && can_use_data) {
+ if (CreateScratchOnData(scratch_device, partition_exists)) {
+ return true;
+ }
+ LOG(WARNING) << "Failed to allocate scratch on /data, fallback to use free space on super";
+ }
+ // If that fails, see if we can land on super.
+ if (CanUseSuperPartition(fstab)) {
+ return CreateDynamicScratch(scratch_device, partition_exists);
+ }
+ return false;
+}
+
+// Create and mount kScratchMountPoint storage if we have logical partitions
+bool fs_mgr_overlayfs_setup_scratch(const Fstab& fstab) {
+ if (fs_mgr_overlayfs_already_mounted(kScratchMountPoint, false)) {
+ return true;
+ }
+
+ std::string scratch_device;
+ bool partition_exists;
+ if (!fs_mgr_overlayfs_create_scratch(fstab, &scratch_device, &partition_exists)) {
+ LOG(ERROR) << "Failed to create scratch partition";
+ return false;
+ }
+
+ // If the partition exists, assume first that it can be mounted.
+ if (partition_exists) {
+ if (MountScratch(scratch_device)) {
+ const auto top = kScratchMountPoint + "/"s + kOverlayTopDir;
+ if (access(top.c_str(), F_OK) == 0 || fs_mgr_filesystem_has_space(kScratchMountPoint)) {
+ return true;
+ }
+ // declare it useless, no overrides and no free space
+ if (!fs_mgr_overlayfs_umount_scratch()) {
+ LOG(ERROR) << "Unable to unmount scratch partition";
+ return false;
+ }
+ }
+ }
+
+ if (!MakeScratchFilesystem(scratch_device)) {
+ LOG(ERROR) << "Failed to format scratch partition";
+ return false;
+ }
+
+ return MountScratch(scratch_device);
+}
+
+constexpr bool OverlayfsTeardownAllowed() {
+ // Never allow on non-debuggable build.
+ return kAllowOverlayfs;
+}
+
+} // namespace
+
+bool fs_mgr_overlayfs_setup(const Fstab& fstab, const char* mount_point, bool* want_reboot,
+ bool just_disabled_verity) {
+ if (!OverlayfsSetupAllowed(/*verbose=*/true)) {
+ return false;
+ }
+
+ if (!fs_mgr_boot_completed()) {
+ LOG(ERROR) << "Cannot setup overlayfs before persistent properties are ready";
+ return false;
+ }
+
+ auto candidates = fs_mgr_overlayfs_candidate_list(fstab);
+ for (auto it = candidates.begin(); it != candidates.end();) {
+ if (mount_point &&
+ (fs_mgr_mount_point(it->mount_point) != fs_mgr_mount_point(mount_point))) {
+ it = candidates.erase(it);
+ continue;
+ }
+
+ auto verity_enabled = !just_disabled_verity && fs_mgr_is_verity_enabled(*it);
+ if (verity_enabled) {
+ it = candidates.erase(it);
+ continue;
+ }
+ ++it;
+ }
+
+ if (candidates.empty()) {
+ if (mount_point) {
+ LOG(ERROR) << "No overlayfs candidate was found for " << mount_point;
+ return false;
+ }
+ return true;
+ }
+
+ std::string dir;
+ for (const auto& overlay_mount_point : OverlayMountPoints()) {
+ if (overlay_mount_point == kScratchMountPoint) {
+ if (!fs_mgr_overlayfs_setup_scratch(fstab)) {
+ continue;
+ }
+ } else {
+ if (GetEntryForMountPoint(&fstab, overlay_mount_point) == nullptr) {
+ continue;
+ }
+ }
+ dir = overlay_mount_point;
+ break;
+ }
+ if (dir.empty()) {
+ LOG(ERROR) << "Could not allocate backing storage for overlays";
+ return false;
+ }
+
+ const auto overlay = fs_mgr_overlayfs_setup_dir(dir);
+ if (overlay.empty()) {
+ return false;
+ }
+
+ bool ok = true;
+ for (const auto& entry : candidates) {
+ auto fstab_mount_point = fs_mgr_mount_point(entry.mount_point);
+ ok &= fs_mgr_overlayfs_setup_one(overlay, fstab_mount_point, want_reboot);
+ }
+ return ok;
+}
+
+struct MapInfo {
+ // If set, partition is owned by ImageManager.
+ std::unique_ptr<IImageManager> images;
+ // If set, and images is null, this is a DAP partition.
+ std::string name;
+ // If set, and images and name are empty, this is a non-dynamic partition.
+ std::string device;
+
+ MapInfo() = default;
+ MapInfo(MapInfo&&) = default;
+ ~MapInfo() {
+ if (images) {
+ images->UnmapImageDevice(name);
+ } else if (!name.empty()) {
+ DestroyLogicalPartition(name);
+ }
+ }
+};
+
+// Note: This function never returns the DSU scratch device in recovery or fastbootd,
+// because the DSU scratch is created in the first-stage-mount, which is not run in recovery.
+static std::optional<MapInfo> EnsureScratchMapped() {
+ MapInfo info;
+ info.device = GetBootScratchDevice();
+ if (!info.device.empty()) {
+ return {std::move(info)};
+ }
+ if (!InRecovery()) {
+ return {};
+ }
+
+ auto partition_name = android::base::Basename(kScratchMountPoint);
+
+ // Check for scratch on /data first, before looking for a modified super
+ // partition. We should only reach this code in recovery, because scratch
+ // would otherwise always be mapped.
+ auto images = IImageManager::Open("remount", 10s);
+ if (images && images->BackingImageExists(partition_name)) {
+ if (images->IsImageDisabled(partition_name)) {
+ return {};
+ }
+ if (!images->MapImageDevice(partition_name, 10s, &info.device)) {
+ return {};
+ }
+ info.name = partition_name;
+ info.images = std::move(images);
+ return {std::move(info)};
+ }
+
+ // Avoid uart spam by first checking for a scratch partition.
+ const auto super_device = kPhysicalDevice + fs_mgr_get_super_partition_name();
+ auto metadata = ReadCurrentMetadata(super_device);
+ if (!metadata) {
+ return {};
+ }
+
+ auto partition = FindPartition(*metadata.get(), partition_name);
+ if (!partition) {
+ return {};
+ }
+
+ CreateLogicalPartitionParams params = {
+ .block_device = super_device,
+ .metadata = metadata.get(),
+ .partition = partition,
+ .force_writable = true,
+ .timeout_ms = 10s,
+ };
+ if (!CreateLogicalPartition(params, &info.device)) {
+ return {};
+ }
+ info.name = partition_name;
+ return {std::move(info)};
+}
+
+// This should only be reachable in recovery, where DSU scratch is not
+// automatically mapped.
+static bool MapDsuScratchDevice(std::string* device) {
+ std::string dsu_slot;
+ if (!android::gsi::IsGsiInstalled() || !android::gsi::GetActiveDsu(&dsu_slot) ||
+ dsu_slot.empty()) {
+ // Nothing to do if no DSU installation present.
+ return false;
+ }
+
+ auto images = IImageManager::Open("dsu/" + dsu_slot, 10s);
+ if (!images || !images->BackingImageExists(android::gsi::kDsuScratch)) {
+ // Nothing to do if DSU scratch device doesn't exist.
+ return false;
+ }
+
+ images->UnmapImageDevice(android::gsi::kDsuScratch);
+ if (!images->MapImageDevice(android::gsi::kDsuScratch, 10s, device)) {
+ return false;
+ }
+ return true;
+}
+
+static OverlayfsTeardownResult TeardownMountsAndScratch(const char* mount_point,
+ bool* want_reboot) {
+ bool should_destroy_scratch = false;
+ auto rv = OverlayfsTeardownResult::Ok;
+ for (const auto& overlay_mount_point : OverlayMountPoints()) {
+ auto ok = fs_mgr_overlayfs_teardown_one(
+ overlay_mount_point, mount_point ? fs_mgr_mount_point(mount_point) : "",
+ want_reboot,
+ overlay_mount_point == kScratchMountPoint ? &should_destroy_scratch : nullptr);
+ if (!ok) {
+ rv = OverlayfsTeardownResult::Error;
+ }
+ }
+
+ // Do not attempt to destroy DSU scratch if within a DSU system,
+ // because DSU scratch partition is managed by gsid.
+ if (should_destroy_scratch && !fs_mgr_is_dsu_running()) {
+ auto rv = fs_mgr_overlayfs_teardown_scratch(kScratchMountPoint, want_reboot);
+ if (rv != OverlayfsTeardownResult::Ok) {
+ return rv;
+ }
+ }
+ // And now that we did what we could, lets inform
+ // caller that there may still be more to do.
+ if (!fs_mgr_boot_completed()) {
+ LOG(ERROR) << "Cannot teardown overlayfs before persistent properties are ready";
+ return OverlayfsTeardownResult::Error;
+ }
+ return rv;
+}
+
+// Returns false if teardown not permitted. If something is altered, set *want_reboot.
+OverlayfsTeardownResult fs_mgr_overlayfs_teardown(const char* mount_point, bool* want_reboot) {
+ if (!OverlayfsTeardownAllowed()) {
+ // Nothing to teardown.
+ return OverlayfsTeardownResult::Ok;
+ }
+ // If scratch exists, but is not mounted, lets gain access to clean
+ // specific override entries.
+ auto mount_scratch = false;
+ if ((mount_point != nullptr) && !fs_mgr_overlayfs_already_mounted(kScratchMountPoint, false)) {
+ std::string scratch_device = GetBootScratchDevice();
+ if (!scratch_device.empty()) {
+ mount_scratch = MountScratch(scratch_device);
+ }
+ }
+
+ auto rv = TeardownMountsAndScratch(mount_point, want_reboot);
+
+ if (mount_scratch) {
+ if (!fs_mgr_overlayfs_umount_scratch()) {
+ return OverlayfsTeardownResult::Busy;
+ }
+ }
+ return rv;
+}
+
+namespace android {
+namespace fs_mgr {
+
+void MapScratchPartitionIfNeeded(Fstab* fstab,
+ const std::function<bool(const std::set<std::string>&)>& init) {
+ if (!OverlayfsSetupAllowed()) {
+ return;
+ }
+ if (GetEntryForMountPoint(fstab, kScratchMountPoint) != nullptr) {
+ return;
+ }
+
+ bool want_scratch = false;
+ for (const auto& entry : fs_mgr_overlayfs_candidate_list(*fstab)) {
+ if (fs_mgr_is_verity_enabled(entry)) {
+ continue;
+ }
+ if (fs_mgr_overlayfs_already_mounted(fs_mgr_mount_point(entry.mount_point))) {
+ continue;
+ }
+ want_scratch = true;
+ break;
+ }
+ if (!want_scratch) {
+ return;
+ }
+
+ if (ScratchIsOnData()) {
+ if (auto images = IImageManager::Open("remount", 0ms)) {
+ images->MapAllImages(init);
+ }
+ }
+
+ // Physical or logical partitions will have already been mapped here,
+ // so just ensure /dev/block symlinks exist.
+ auto device = GetBootScratchDevice();
+ if (!device.empty()) {
+ init({android::base::Basename(device)});
+ }
+}
+
+void CleanupOldScratchFiles() {
+ if (!OverlayfsTeardownAllowed()) {
+ return;
+ }
+ if (!ScratchIsOnData()) {
+ return;
+ }
+ if (auto images = IImageManager::Open("remount", 0ms)) {
+ images->RemoveDisabledImages();
+ }
+}
+
+void TeardownAllOverlayForMountPoint(const std::string& mount_point) {
+ if (!OverlayfsTeardownAllowed()) {
+ return;
+ }
+ if (!InRecovery()) {
+ LERROR << __FUNCTION__ << "(): must be called within recovery.";
+ return;
+ }
+
+ // Empty string means teardown everything.
+ const std::string teardown_dir = mount_point.empty() ? "" : fs_mgr_mount_point(mount_point);
+ constexpr bool* ignore_change = nullptr;
+
+ // Teardown legacy overlay mount points that's not backed by a scratch device.
+ for (const auto& overlay_mount_point : OverlayMountPoints()) {
+ if (overlay_mount_point == kScratchMountPoint) {
+ continue;
+ }
+ fs_mgr_overlayfs_teardown_one(overlay_mount_point, teardown_dir, ignore_change);
+ }
+
+ if (mount_point.empty()) {
+ // Throw away the entire partition.
+ auto partition_name = android::base::Basename(kScratchMountPoint);
+ auto images = IImageManager::Open("remount", 10s);
+ if (images && images->BackingImageExists(partition_name)) {
+ if (images->DisableImage(partition_name)) {
+ LOG(INFO) << "Disabled scratch partition for: " << kScratchMountPoint;
+ } else {
+ LOG(ERROR) << "Unable to disable scratch partition for " << kScratchMountPoint;
+ }
+ }
+ }
+
+ // Note if we just disabled scratch, this mount will fail.
+ if (auto info = EnsureScratchMapped(); info.has_value()) {
+ // Map scratch device, mount kScratchMountPoint and teardown kScratchMountPoint.
+ fs_mgr_overlayfs_umount_scratch();
+ if (MountScratch(info->device)) {
+ bool should_destroy_scratch = false;
+ fs_mgr_overlayfs_teardown_one(kScratchMountPoint, teardown_dir, ignore_change,
+ &should_destroy_scratch);
+ fs_mgr_overlayfs_umount_scratch();
+ if (should_destroy_scratch) {
+ fs_mgr_overlayfs_teardown_scratch(kScratchMountPoint, nullptr);
+ }
+ }
+ }
+
+ // Teardown DSU overlay if present.
+ std::string scratch_device;
+ if (MapDsuScratchDevice(&scratch_device)) {
+ fs_mgr_overlayfs_umount_scratch();
+ if (MountScratch(scratch_device)) {
+ fs_mgr_overlayfs_teardown_one(kScratchMountPoint, teardown_dir, ignore_change);
+ fs_mgr_overlayfs_umount_scratch();
+ }
+ DestroyLogicalPartition(android::gsi::kDsuScratch);
+ }
+}
+
+} // namespace fs_mgr
+} // namespace android
diff --git a/fs_mgr/fs_mgr_overlayfs_control.h b/fs_mgr/fs_mgr_overlayfs_control.h
new file mode 100644
index 0000000..b175101
--- /dev/null
+++ b/fs_mgr/fs_mgr_overlayfs_control.h
@@ -0,0 +1,42 @@
+// Copyright (C) 2023 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 <fstab/fstab.h>
+
+// If "mount_point" is non-null, set up exactly one overlay.
+//
+// If "mount_point" is null, setup any overlays.
+//
+// If |want_reboot| is non-null, and a reboot is needed to apply overlays, then
+// it will be true on return. The caller is responsible for initializing it.
+bool fs_mgr_overlayfs_setup(const android::fs_mgr::Fstab& fstab, const char* mount_point = nullptr,
+ bool* want_reboot = nullptr, bool just_disabled_verity = true);
+
+enum class OverlayfsTeardownResult {
+ Ok,
+ Busy, // Indicates that overlays are still in use.
+ Error
+};
+OverlayfsTeardownResult fs_mgr_overlayfs_teardown(const char* mount_point = nullptr,
+ bool* want_reboot = nullptr);
+
+namespace android {
+namespace fs_mgr {
+
+void CleanupOldScratchFiles();
+
+} // namespace fs_mgr
+} // namespace android
diff --git a/fs_mgr/fs_mgr_overlayfs_mount.cpp b/fs_mgr/fs_mgr_overlayfs_mount.cpp
new file mode 100644
index 0000000..37e3058
--- /dev/null
+++ b/fs_mgr/fs_mgr_overlayfs_mount.cpp
@@ -0,0 +1,770 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <errno.h>
+#include <fcntl.h>
+#include <linux/fs.h>
+#include <selinux/selinux.h>
+#include <stdlib.h>
+#include <sys/mount.h>
+#include <sys/stat.h>
+#include <sys/statvfs.h>
+#include <sys/types.h>
+#include <sys/utsname.h>
+#include <sys/vfs.h>
+#include <unistd.h>
+
+#include <algorithm>
+#include <memory>
+#include <string>
+#include <vector>
+
+#include <android-base/file.h>
+#include <android-base/macros.h>
+#include <android-base/properties.h>
+#include <android-base/strings.h>
+#include <android-base/unique_fd.h>
+#include <ext4_utils/ext4_utils.h>
+#include <fs_mgr.h>
+#include <fs_mgr/file_wait.h>
+#include <fs_mgr_overlayfs.h>
+#include <fstab/fstab.h>
+#include <libdm/dm.h>
+#include <libgsi/libgsi.h>
+#include <storage_literals/storage_literals.h>
+
+#include "fs_mgr_overlayfs_mount.h"
+#include "fs_mgr_priv.h"
+
+using namespace std::literals;
+using namespace android::dm;
+using namespace android::fs_mgr;
+using namespace android::storage_literals;
+
+constexpr char kPreferCacheBackingStorageProp[] = "fs_mgr.overlayfs.prefer_cache_backing_storage";
+
+constexpr char kCacheMountPoint[] = "/cache";
+constexpr char kPhysicalDevice[] = "/dev/block/by-name/";
+
+constexpr char kLowerdirOption[] = "lowerdir=";
+constexpr char kUpperdirOption[] = "upperdir=";
+
+bool fs_mgr_is_dsu_running() {
+ // Since android::gsi::CanBootIntoGsi() or android::gsi::MarkSystemAsGsi() is
+ // never called in recovery, the return value of android::gsi::IsGsiRunning()
+ // is not well-defined. In this case, just return false as being in recovery
+ // implies not running a DSU system.
+ if (InRecovery()) return false;
+ return android::gsi::IsGsiRunning();
+}
+
+std::vector<const std::string> OverlayMountPoints() {
+ // Never fallback to legacy cache mount point if within a DSU system,
+ // because running a DSU system implies the device supports dynamic
+ // partitions, which means legacy cache mustn't be used.
+ if (fs_mgr_is_dsu_running()) {
+ return {kScratchMountPoint};
+ }
+
+ // For non-A/B devices prefer cache backing storage if
+ // kPreferCacheBackingStorageProp property set.
+ if (fs_mgr_get_slot_suffix().empty() &&
+ android::base::GetBoolProperty(kPreferCacheBackingStorageProp, false) &&
+ android::base::GetIntProperty("ro.vendor.api_level", -1) < __ANDROID_API_T__) {
+ return {kCacheMountPoint, kScratchMountPoint};
+ }
+
+ return {kScratchMountPoint, kCacheMountPoint};
+}
+
+static bool fs_mgr_is_dir(const std::string& path) {
+ struct stat st;
+ return !stat(path.c_str(), &st) && S_ISDIR(st.st_mode);
+}
+
+// At less than 1% or 8MB of free space return value of false,
+// means we will try to wrap with overlayfs.
+bool fs_mgr_filesystem_has_space(const std::string& mount_point) {
+ // If we have access issues to find out space remaining, return true
+ // to prevent us trying to override with overlayfs.
+ struct statvfs vst;
+ if (statvfs(mount_point.c_str(), &vst)) {
+ PLOG(ERROR) << "statvfs " << mount_point;
+ return true;
+ }
+
+ static constexpr int kPercentThreshold = 1; // 1%
+ static constexpr unsigned long kSizeThreshold = 8 * 1024 * 1024; // 8MB
+
+ return (vst.f_bfree >= (vst.f_blocks * kPercentThreshold / 100)) &&
+ (static_cast<uint64_t>(vst.f_bfree) * vst.f_frsize) >= kSizeThreshold;
+}
+
+static bool fs_mgr_update_blk_device(FstabEntry* entry) {
+ if (entry->fs_mgr_flags.logical) {
+ fs_mgr_update_logical_partition(entry);
+ }
+ if (access(entry->blk_device.c_str(), F_OK) == 0) {
+ return true;
+ }
+ if (entry->blk_device != "/dev/root") {
+ return false;
+ }
+
+ // special case for system-as-root (taimen and others)
+ auto blk_device = kPhysicalDevice + "system"s;
+ if (access(blk_device.c_str(), F_OK)) {
+ blk_device += fs_mgr_get_slot_suffix();
+ if (access(blk_device.c_str(), F_OK)) {
+ return false;
+ }
+ }
+ entry->blk_device = blk_device;
+ return true;
+}
+
+static bool fs_mgr_has_shared_blocks(const std::string& mount_point, const std::string& dev) {
+ struct statfs fs;
+ if ((statfs((mount_point + "/lost+found").c_str(), &fs) == -1) ||
+ (fs.f_type != EXT4_SUPER_MAGIC)) {
+ return false;
+ }
+
+ android::base::unique_fd fd(open(dev.c_str(), O_RDONLY | O_CLOEXEC));
+ if (fd < 0) return false;
+
+ struct ext4_super_block sb;
+ if ((TEMP_FAILURE_RETRY(lseek64(fd, 1024, SEEK_SET)) < 0) ||
+ (TEMP_FAILURE_RETRY(read(fd, &sb, sizeof(sb))) < 0)) {
+ return false;
+ }
+
+ struct fs_info info;
+ if (ext4_parse_sb(&sb, &info) < 0) return false;
+
+ return (info.feat_ro_compat & EXT4_FEATURE_RO_COMPAT_SHARED_BLOCKS) != 0;
+}
+
+#define F2FS_SUPER_OFFSET 1024
+#define F2FS_FEATURE_OFFSET 2180
+#define F2FS_FEATURE_RO 0x4000
+static bool fs_mgr_is_read_only_f2fs(const std::string& dev) {
+ if (!fs_mgr_is_f2fs(dev)) return false;
+
+ android::base::unique_fd fd(open(dev.c_str(), O_RDONLY | O_CLOEXEC));
+ if (fd < 0) return false;
+
+ __le32 feat;
+ if ((TEMP_FAILURE_RETRY(lseek64(fd, F2FS_SUPER_OFFSET + F2FS_FEATURE_OFFSET, SEEK_SET)) < 0) ||
+ (TEMP_FAILURE_RETRY(read(fd, &feat, sizeof(feat))) < 0)) {
+ return false;
+ }
+
+ return (feat & cpu_to_le32(F2FS_FEATURE_RO)) != 0;
+}
+
+static bool fs_mgr_overlayfs_enabled(FstabEntry* entry) {
+ // readonly filesystem, can not be mount -o remount,rw
+ // for squashfs, erofs or if free space is (near) zero making such a remount
+ // virtually useless, or if there are shared blocks that prevent remount,rw
+ if (!fs_mgr_filesystem_has_space(entry->mount_point)) {
+ return true;
+ }
+
+ // blk_device needs to be setup so we can check superblock.
+ // If we fail here, because during init first stage and have doubts.
+ if (!fs_mgr_update_blk_device(entry)) {
+ return true;
+ }
+
+ // f2fs read-only mode doesn't support remount,rw
+ if (fs_mgr_is_read_only_f2fs(entry->blk_device)) {
+ return true;
+ }
+
+ // check if ext4 de-dupe
+ auto has_shared_blocks = fs_mgr_has_shared_blocks(entry->mount_point, entry->blk_device);
+ if (!has_shared_blocks && (entry->mount_point == "/system")) {
+ has_shared_blocks = fs_mgr_has_shared_blocks("/", entry->blk_device);
+ }
+ return has_shared_blocks;
+}
+
+static std::string fs_mgr_get_overlayfs_candidate(const std::string& mount_point) {
+ if (!fs_mgr_is_dir(mount_point)) return "";
+ const auto base = android::base::Basename(mount_point) + "/";
+ for (const auto& overlay_mount_point : OverlayMountPoints()) {
+ auto dir = overlay_mount_point + "/" + kOverlayTopDir + "/" + base;
+ auto upper = dir + kUpperName;
+ if (!fs_mgr_is_dir(upper)) continue;
+ auto work = dir + kWorkName;
+ if (!fs_mgr_is_dir(work)) continue;
+ if (access(work.c_str(), R_OK | W_OK)) continue;
+ return dir;
+ }
+ return "";
+}
+
+static inline bool KernelSupportsUserXattrs() {
+ struct utsname uts;
+ uname(&uts);
+
+ int major, minor;
+ if (sscanf(uts.release, "%d.%d", &major, &minor) != 2) {
+ return false;
+ }
+ return major > 5 || (major == 5 && minor >= 15);
+}
+
+const std::string fs_mgr_mount_point(const std::string& mount_point) {
+ if ("/"s != mount_point) return mount_point;
+ return "/system";
+}
+
+// default options for mount_point, returns empty string for none available.
+static std::string fs_mgr_get_overlayfs_options(const FstabEntry& entry) {
+ const auto mount_point = fs_mgr_mount_point(entry.mount_point);
+ auto candidate = fs_mgr_get_overlayfs_candidate(mount_point);
+ if (candidate.empty()) return "";
+ auto ret = kLowerdirOption + mount_point + "," + kUpperdirOption + candidate + kUpperName +
+ ",workdir=" + candidate + kWorkName;
+ if (fs_mgr_overlayfs_valid() == OverlayfsValidResult::kOverrideCredsRequired) {
+ ret += ",override_creds=off";
+ }
+ if (KernelSupportsUserXattrs()) {
+ ret += ",userxattr";
+ }
+ for (const auto& flag : android::base::Split(entry.fs_options, ",")) {
+ if (android::base::StartsWith(flag, "context=")) {
+ ret += "," + flag;
+ }
+ }
+ return ret;
+}
+
+bool AutoSetFsCreateCon::Set(const std::string& context) {
+ if (setfscreatecon(context.c_str())) {
+ PLOG(ERROR) << "setfscreatecon " << context;
+ return false;
+ }
+ ok_ = true;
+ return true;
+}
+
+bool AutoSetFsCreateCon::Restore() {
+ if (restored_ || !ok_) {
+ return true;
+ }
+ if (setfscreatecon(nullptr)) {
+ PLOG(ERROR) << "setfscreatecon null";
+ return false;
+ }
+ restored_ = true;
+ return true;
+}
+
+// Returns true if immediate unmount succeeded and the scratch mount point was
+// removed.
+bool fs_mgr_overlayfs_umount_scratch() {
+ if (umount(kScratchMountPoint) != 0) {
+ return false;
+ }
+ if (rmdir(kScratchMountPoint) != 0 && errno != ENOENT) {
+ PLOG(ERROR) << "rmdir " << kScratchMountPoint;
+ }
+ return true;
+}
+
+static bool fs_mgr_overlayfs_set_shared_mount(const std::string& mount_point, bool shared_flag) {
+ auto ret = mount(nullptr, mount_point.c_str(), nullptr, shared_flag ? MS_SHARED : MS_PRIVATE,
+ nullptr);
+ if (ret) {
+ PERROR << "__mount(target=" << mount_point
+ << ",flag=" << (shared_flag ? "MS_SHARED" : "MS_PRIVATE") << ")=" << ret;
+ // If "/system" doesn't look like a mountpoint, retry with "/".
+ if (errno == EINVAL && mount_point == "/system") {
+ return fs_mgr_overlayfs_set_shared_mount("/", shared_flag);
+ }
+ return false;
+ }
+ return true;
+}
+
+static bool fs_mgr_overlayfs_move_mount(const std::string& source, const std::string& target) {
+ auto ret = mount(source.c_str(), target.c_str(), nullptr, MS_MOVE, nullptr);
+ if (ret) {
+ PERROR << "__mount(source=" << source << ",target=" << target << ",flag=MS_MOVE)=" << ret;
+ return false;
+ }
+ return true;
+}
+
+struct mount_info {
+ std::string mount_point;
+ bool shared_flag;
+};
+
+static std::vector<mount_info> ReadMountinfoFromFile(const std::string& path) {
+ std::vector<mount_info> info;
+
+ auto file = std::unique_ptr<FILE, decltype(&fclose)>{fopen(path.c_str(), "re"), fclose};
+ if (!file) {
+ PERROR << __FUNCTION__ << "(): cannot open file: '" << path << "'";
+ return info;
+ }
+
+ ssize_t len;
+ size_t alloc_len = 0;
+ char* line = nullptr;
+ while ((len = getline(&line, &alloc_len, file.get())) != -1) {
+ /* if the last character is a newline, shorten the string by 1 byte */
+ if (line[len - 1] == '\n') {
+ line[len - 1] = '\0';
+ }
+
+ static constexpr char delim[] = " \t";
+ char* save_ptr;
+ if (!strtok_r(line, delim, &save_ptr)) {
+ LERROR << "Error parsing mount ID";
+ break;
+ }
+ if (!strtok_r(nullptr, delim, &save_ptr)) {
+ LERROR << "Error parsing parent ID";
+ break;
+ }
+ if (!strtok_r(nullptr, delim, &save_ptr)) {
+ LERROR << "Error parsing mount source";
+ break;
+ }
+ if (!strtok_r(nullptr, delim, &save_ptr)) {
+ LERROR << "Error parsing root";
+ break;
+ }
+
+ char* p;
+ if (!(p = strtok_r(nullptr, delim, &save_ptr))) {
+ LERROR << "Error parsing mount_point";
+ break;
+ }
+ mount_info entry = {p, false};
+
+ if (!strtok_r(nullptr, delim, &save_ptr)) {
+ LERROR << "Error parsing mount_flags";
+ break;
+ }
+
+ while ((p = strtok_r(nullptr, delim, &save_ptr))) {
+ if ((p[0] == '-') && (p[1] == '\0')) break;
+ if (android::base::StartsWith(p, "shared:")) entry.shared_flag = true;
+ }
+ if (!p) {
+ LERROR << "Error parsing fields";
+ break;
+ }
+ info.emplace_back(std::move(entry));
+ }
+
+ free(line);
+ if (info.empty()) {
+ LERROR << __FUNCTION__ << "(): failed to load mountinfo from : '" << path << "'";
+ }
+ return info;
+}
+
+static bool fs_mgr_overlayfs_mount(const FstabEntry& entry) {
+ const auto mount_point = fs_mgr_mount_point(entry.mount_point);
+ const auto options = fs_mgr_get_overlayfs_options(entry);
+ if (options.empty()) return false;
+
+ auto retval = true;
+
+ struct move_entry {
+ std::string mount_point;
+ std::string dir;
+ bool shared_flag;
+ };
+ std::vector<move_entry> move;
+ auto parent_private = false;
+ auto parent_made_private = false;
+ auto dev_private = false;
+ auto dev_made_private = false;
+ for (auto& entry : ReadMountinfoFromFile("/proc/self/mountinfo")) {
+ if ((entry.mount_point == mount_point) && !entry.shared_flag) {
+ parent_private = true;
+ }
+ if ((entry.mount_point == "/dev") && !entry.shared_flag) {
+ dev_private = true;
+ }
+
+ if (!android::base::StartsWith(entry.mount_point, mount_point + "/")) {
+ continue;
+ }
+ if (std::find_if(move.begin(), move.end(), [&entry](const auto& it) {
+ return android::base::StartsWith(entry.mount_point, it.mount_point + "/");
+ }) != move.end()) {
+ continue;
+ }
+
+ // use as the bound directory in /dev.
+ AutoSetFsCreateCon createcon;
+ auto new_context = fs_mgr_get_context(entry.mount_point);
+ if (new_context.empty() || !createcon.Set(new_context)) {
+ continue;
+ }
+ move_entry new_entry = {std::move(entry.mount_point), "/dev/TemporaryDir-XXXXXX",
+ entry.shared_flag};
+ const auto target = mkdtemp(new_entry.dir.data());
+ if (!createcon.Restore()) {
+ return false;
+ }
+ if (!target) {
+ retval = false;
+ PERROR << "temporary directory for MS_BIND";
+ continue;
+ }
+
+ if (!parent_private && !parent_made_private) {
+ parent_made_private = fs_mgr_overlayfs_set_shared_mount(mount_point, false);
+ }
+ if (new_entry.shared_flag) {
+ new_entry.shared_flag = fs_mgr_overlayfs_set_shared_mount(new_entry.mount_point, false);
+ }
+ if (!fs_mgr_overlayfs_move_mount(new_entry.mount_point, new_entry.dir)) {
+ retval = false;
+ if (new_entry.shared_flag) {
+ fs_mgr_overlayfs_set_shared_mount(new_entry.mount_point, true);
+ }
+ continue;
+ }
+ move.emplace_back(std::move(new_entry));
+ }
+
+ // hijack __mount() report format to help triage
+ auto report = "__mount(source=overlay,target="s + mount_point + ",type=overlay";
+ const auto opt_list = android::base::Split(options, ",");
+ for (const auto& opt : opt_list) {
+ if (android::base::StartsWith(opt, kUpperdirOption)) {
+ report = report + "," + opt;
+ break;
+ }
+ }
+ report = report + ")=";
+
+ auto ret = mount("overlay", mount_point.c_str(), "overlay", MS_RDONLY | MS_NOATIME,
+ options.c_str());
+ if (ret) {
+ retval = false;
+ PERROR << report << ret;
+ } else {
+ LINFO << report << ret;
+ }
+
+ // Move submounts back.
+ for (const auto& entry : move) {
+ if (!dev_private && !dev_made_private) {
+ dev_made_private = fs_mgr_overlayfs_set_shared_mount("/dev", false);
+ }
+
+ if (!fs_mgr_overlayfs_move_mount(entry.dir, entry.mount_point)) {
+ retval = false;
+ } else if (entry.shared_flag &&
+ !fs_mgr_overlayfs_set_shared_mount(entry.mount_point, true)) {
+ retval = false;
+ }
+ rmdir(entry.dir.c_str());
+ }
+ if (dev_made_private) {
+ fs_mgr_overlayfs_set_shared_mount("/dev", true);
+ }
+ if (parent_made_private) {
+ fs_mgr_overlayfs_set_shared_mount(mount_point, true);
+ }
+
+ return retval;
+}
+
+// Mount kScratchMountPoint
+bool MountScratch(const std::string& device_path, bool readonly) {
+ if (readonly) {
+ if (access(device_path.c_str(), F_OK)) {
+ LOG(ERROR) << "Path does not exist: " << device_path;
+ return false;
+ }
+ } else if (access(device_path.c_str(), R_OK | W_OK)) {
+ LOG(ERROR) << "Path does not exist or is not readwrite: " << device_path;
+ return false;
+ }
+
+ std::vector<const char*> filesystem_candidates;
+ if (fs_mgr_is_f2fs(device_path)) {
+ filesystem_candidates = {"f2fs", "ext4"};
+ } else if (fs_mgr_is_ext4(device_path)) {
+ filesystem_candidates = {"ext4", "f2fs"};
+ } else {
+ LOG(ERROR) << "Scratch partition is not f2fs or ext4";
+ return false;
+ }
+
+ AutoSetFsCreateCon createcon(kOverlayfsFileContext);
+ if (!createcon.Ok()) {
+ return false;
+ }
+ if (mkdir(kScratchMountPoint, 0755) && (errno != EEXIST)) {
+ PERROR << "create " << kScratchMountPoint;
+ return false;
+ }
+
+ FstabEntry entry;
+ entry.blk_device = device_path;
+ entry.mount_point = kScratchMountPoint;
+ entry.flags = MS_NOATIME | MS_RDONLY;
+ if (!readonly) {
+ entry.flags &= ~MS_RDONLY;
+ entry.flags |= MS_SYNCHRONOUS;
+ entry.fs_options = "nodiscard";
+ fs_mgr_set_blk_ro(device_path, false);
+ }
+ // check_fs requires apex runtime library
+ if (fs_mgr_overlayfs_already_mounted("/data", false)) {
+ entry.fs_mgr_flags.check = true;
+ }
+ bool mounted = false;
+ for (auto fs_type : filesystem_candidates) {
+ entry.fs_type = fs_type;
+ if (fs_mgr_do_mount_one(entry) == 0) {
+ mounted = true;
+ break;
+ }
+ }
+ if (!createcon.Restore()) {
+ return false;
+ }
+ if (!mounted) {
+ rmdir(kScratchMountPoint);
+ return false;
+ }
+ return true;
+}
+
+// Note: The scratch partition of DSU is managed by gsid, and should be initialized during
+// first-stage-mount. Just check if the DM device for DSU scratch partition is created or not.
+static std::string GetDsuScratchDevice() {
+ auto& dm = DeviceMapper::Instance();
+ std::string device;
+ if (dm.GetState(android::gsi::kDsuScratch) != DmDeviceState::INVALID &&
+ dm.GetDmDevicePathByName(android::gsi::kDsuScratch, &device)) {
+ return device;
+ }
+ return "";
+}
+
+// This returns the scratch device that was detected during early boot (first-
+// stage init). If the device was created later, for example during setup for
+// the adb remount command, it can return an empty string since it does not
+// query ImageManager. (Note that ImageManager in first-stage init will always
+// use device-mapper, since /data is not available to use loop devices.)
+static std::string GetBootScratchDevice() {
+ // Note: fs_mgr_is_dsu_running() always returns false in recovery or fastbootd.
+ if (fs_mgr_is_dsu_running()) {
+ return GetDsuScratchDevice();
+ }
+
+ auto& dm = DeviceMapper::Instance();
+
+ // If there is a scratch partition allocated in /data or on super, we
+ // automatically prioritize that over super_other or system_other.
+ // Some devices, for example, have a write-protected eMMC and the
+ // super partition cannot be used even if it exists.
+ std::string device;
+ auto partition_name = android::base::Basename(kScratchMountPoint);
+ if (dm.GetState(partition_name) != DmDeviceState::INVALID &&
+ dm.GetDmDevicePathByName(partition_name, &device)) {
+ return device;
+ }
+
+ return "";
+}
+
+// NOTE: OverlayfsSetupAllowed() must be "stricter" than OverlayfsTeardownAllowed().
+// Setup is allowed only if teardown is also allowed.
+bool OverlayfsSetupAllowed(bool verbose) {
+ if (!kAllowOverlayfs) {
+ if (verbose) {
+ LOG(ERROR) << "Overlayfs remounts can only be used in debuggable builds";
+ }
+ return false;
+ }
+ // Check mandatory kernel patches.
+ if (fs_mgr_overlayfs_valid() == OverlayfsValidResult::kNotSupported) {
+ if (verbose) {
+ LOG(ERROR) << "Kernel does not support overlayfs";
+ }
+ return false;
+ }
+ // in recovery or fastbootd, not allowed!
+ if (InRecovery()) {
+ if (verbose) {
+ LOG(ERROR) << "Unsupported overlayfs setup from recovery";
+ }
+ return false;
+ }
+ return true;
+}
+
+bool fs_mgr_wants_overlayfs(FstabEntry* entry) {
+ // Don't check entries that are managed by vold.
+ if (entry->fs_mgr_flags.vold_managed || entry->fs_mgr_flags.recovery_only) return false;
+
+ // *_other doesn't want overlayfs.
+ if (entry->fs_mgr_flags.slot_select_other) return false;
+
+ // Only concerned with readonly partitions.
+ if (!(entry->flags & MS_RDONLY)) return false;
+
+ // If unbindable, do not allow overlayfs as this could expose us to
+ // security issues. On Android, this could also be used to turn off
+ // the ability to overlay an otherwise acceptable filesystem since
+ // /system and /vendor are never bound(sic) to.
+ if (entry->flags & MS_UNBINDABLE) return false;
+
+ if (!fs_mgr_overlayfs_enabled(entry)) return false;
+
+ return true;
+}
+
+Fstab fs_mgr_overlayfs_candidate_list(const Fstab& fstab) {
+ android::fs_mgr::Fstab mounts;
+ if (!android::fs_mgr::ReadFstabFromFile("/proc/mounts", &mounts)) {
+ PLOG(ERROR) << "Failed to read /proc/mounts";
+ return {};
+ }
+
+ Fstab candidates;
+ for (const auto& entry : fstab) {
+ // Filter out partitions whose type doesn't match what's mounted.
+ // This avoids spammy behavior on devices which can mount different
+ // filesystems for each partition.
+ auto proc_mount_point = (entry.mount_point == "/system") ? "/" : entry.mount_point;
+ auto mounted = GetEntryForMountPoint(&mounts, proc_mount_point);
+ if (!mounted || mounted->fs_type != entry.fs_type) {
+ continue;
+ }
+
+ FstabEntry new_entry = entry;
+ if (!fs_mgr_overlayfs_already_mounted(entry.mount_point) &&
+ !fs_mgr_wants_overlayfs(&new_entry)) {
+ continue;
+ }
+ auto new_mount_point = fs_mgr_mount_point(entry.mount_point);
+ auto duplicate_or_more_specific = false;
+ for (auto it = candidates.begin(); it != candidates.end();) {
+ auto it_mount_point = fs_mgr_mount_point(it->mount_point);
+ if ((it_mount_point == new_mount_point) ||
+ (android::base::StartsWith(new_mount_point, it_mount_point + "/"))) {
+ duplicate_or_more_specific = true;
+ break;
+ }
+ if (android::base::StartsWith(it_mount_point, new_mount_point + "/")) {
+ it = candidates.erase(it);
+ } else {
+ ++it;
+ }
+ }
+ if (!duplicate_or_more_specific) candidates.emplace_back(std::move(new_entry));
+ }
+ return candidates;
+}
+
+static void TryMountScratch() {
+ // Note we get the boot scratch device here, which means if scratch was
+ // just created through ImageManager, this could fail. In practice this
+ // should not happen because "remount" detects this scenario (by checking
+ // if verity is still disabled, i.e. no reboot occurred), and skips calling
+ // fs_mgr_overlayfs_mount_all().
+ auto scratch_device = GetBootScratchDevice();
+ if (access(scratch_device.c_str(), R_OK | W_OK)) {
+ return;
+ }
+ if (!WaitForFile(scratch_device, 10s)) {
+ return;
+ }
+ if (!MountScratch(scratch_device, true /* readonly */)) {
+ return;
+ }
+ const auto top = kScratchMountPoint + "/"s + kOverlayTopDir;
+ const bool has_overlayfs_dir = access(top.c_str(), F_OK) == 0;
+ fs_mgr_overlayfs_umount_scratch();
+ if (has_overlayfs_dir) {
+ MountScratch(scratch_device);
+ }
+}
+
+bool fs_mgr_overlayfs_mount_all(Fstab* fstab) {
+ if (!OverlayfsSetupAllowed()) {
+ return false;
+ }
+ auto ret = true;
+ auto scratch_can_be_mounted = true;
+ for (const auto& entry : fs_mgr_overlayfs_candidate_list(*fstab)) {
+ if (fs_mgr_is_verity_enabled(entry)) continue;
+ auto mount_point = fs_mgr_mount_point(entry.mount_point);
+ if (fs_mgr_overlayfs_already_mounted(mount_point)) {
+ continue;
+ }
+ if (scratch_can_be_mounted) {
+ scratch_can_be_mounted = false;
+ TryMountScratch();
+ }
+ ret &= fs_mgr_overlayfs_mount(entry);
+ }
+ return ret;
+}
+
+bool fs_mgr_overlayfs_is_setup() {
+ if (!OverlayfsSetupAllowed()) {
+ return false;
+ }
+ if (fs_mgr_overlayfs_already_mounted(kScratchMountPoint, false)) return true;
+ Fstab fstab;
+ if (!ReadDefaultFstab(&fstab)) {
+ return false;
+ }
+ for (const auto& entry : fs_mgr_overlayfs_candidate_list(fstab)) {
+ if (fs_mgr_is_verity_enabled(entry)) continue;
+ if (fs_mgr_overlayfs_already_mounted(fs_mgr_mount_point(entry.mount_point))) return true;
+ }
+ return false;
+}
+
+bool fs_mgr_overlayfs_already_mounted(const std::string& mount_point, bool overlay_only) {
+ Fstab fstab;
+ if (!ReadFstabFromFile("/proc/mounts", &fstab)) {
+ return false;
+ }
+ const auto lowerdir = kLowerdirOption + mount_point;
+ for (const auto& entry : fstab) {
+ if (overlay_only && "overlay" != entry.fs_type && "overlayfs" != entry.fs_type) continue;
+ if (mount_point != entry.mount_point) continue;
+ if (!overlay_only) return true;
+ const auto options = android::base::Split(entry.fs_options, ",");
+ for (const auto& opt : options) {
+ if (opt == lowerdir) {
+ return true;
+ }
+ }
+ }
+ return false;
+}
diff --git a/fs_mgr/fs_mgr_overlayfs_mount.h b/fs_mgr/fs_mgr_overlayfs_mount.h
new file mode 100644
index 0000000..f0afac1
--- /dev/null
+++ b/fs_mgr/fs_mgr_overlayfs_mount.h
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2022 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 <string>
+
+#include <fstab/fstab.h>
+
+constexpr char kOverlayfsFileContext[] = "u:object_r:overlayfs_file:s0";
+
+constexpr char kScratchMountPoint[] = "/mnt/scratch";
+constexpr char kOverlayTopDir[] = "overlay";
+constexpr char kUpperName[] = "upper";
+constexpr char kWorkName[] = "work";
+
+#if ALLOW_ADBD_DISABLE_VERITY
+constexpr bool kAllowOverlayfs = true;
+#else
+constexpr bool kAllowOverlayfs = false;
+#endif
+
+class AutoSetFsCreateCon final {
+ public:
+ AutoSetFsCreateCon() {}
+ AutoSetFsCreateCon(const std::string& context) { Set(context); }
+ ~AutoSetFsCreateCon() { Restore(); }
+
+ bool Ok() const { return ok_; }
+ bool Set(const std::string& context);
+ bool Restore();
+
+ private:
+ bool ok_ = false;
+ bool restored_ = false;
+};
+
+bool fs_mgr_is_dsu_running();
+bool fs_mgr_filesystem_has_space(const std::string& mount_point);
+const std::string fs_mgr_mount_point(const std::string& mount_point);
+bool OverlayfsSetupAllowed(bool verbose = false);
+bool MountScratch(const std::string& device_path, bool readonly = false);
+bool fs_mgr_overlayfs_umount_scratch();
+std::vector<const std::string> OverlayMountPoints();
+bool fs_mgr_overlayfs_already_mounted(const std::string& mount_point, bool overlay_only = true);
+bool fs_mgr_wants_overlayfs(android::fs_mgr::FstabEntry* entry);
+android::fs_mgr::Fstab fs_mgr_overlayfs_candidate_list(const android::fs_mgr::Fstab& fstab);
diff --git a/fs_mgr/fs_mgr_priv.h b/fs_mgr/fs_mgr_priv.h
index 46cdb62..c3b18c8 100644
--- a/fs_mgr/fs_mgr_priv.h
+++ b/fs_mgr/fs_mgr_priv.h
@@ -97,8 +97,6 @@
bool fs_mgr_is_ext4(const std::string& blk_device);
bool fs_mgr_is_f2fs(const std::string& blk_device);
-bool fs_mgr_teardown_verity(android::fs_mgr::FstabEntry* fstab);
-
bool fs_mgr_filesystem_available(const std::string& filesystem);
std::string fs_mgr_get_context(const std::string& mount_point);
diff --git a/fs_mgr/fs_mgr_priv_overlayfs.h b/fs_mgr/fs_mgr_priv_overlayfs.h
deleted file mode 100644
index 2033701..0000000
--- a/fs_mgr/fs_mgr_priv_overlayfs.h
+++ /dev/null
@@ -1,49 +0,0 @@
-/*
- * Copyright (C) 2022 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 <string>
-
-#include <fstab/fstab.h>
-
-bool fs_mgr_overlayfs_already_mounted(const std::string& mount_point, bool overlay_only = true);
-bool fs_mgr_wants_overlayfs(android::fs_mgr::FstabEntry* entry);
-android::fs_mgr::Fstab fs_mgr_overlayfs_candidate_list(const android::fs_mgr::Fstab& fstab);
-
-// If "mount_point" is non-null, set up exactly one overlay.
-// If "mount_point" is null, setup any overlays.
-//
-// If |want_reboot| is non-null, and a reboot is needed to apply overlays, then
-// it will be true on return. The caller is responsible for initializing it.
-bool fs_mgr_overlayfs_setup(const android::fs_mgr::Fstab& fstab, const char* mount_point = nullptr,
- bool* want_reboot = nullptr, bool just_disabled_verity = true);
-
-enum class OverlayfsTeardownResult {
- Ok,
- Busy, // Indicates that overlays are still in use.
- Error
-};
-OverlayfsTeardownResult fs_mgr_overlayfs_teardown(const char* mount_point = nullptr,
- bool* want_reboot = nullptr);
-
-namespace android {
-namespace fs_mgr {
-
-void CleanupOldScratchFiles();
-
-} // namespace fs_mgr
-} // namespace android
diff --git a/fs_mgr/fs_mgr_remount.cpp b/fs_mgr/fs_mgr_remount.cpp
index 5a9f391..4c45828 100644
--- a/fs_mgr/fs_mgr_remount.cpp
+++ b/fs_mgr/fs_mgr_remount.cpp
@@ -43,7 +43,8 @@
#include <libavb_user/libavb_user.h>
#include <libgsi/libgsid.h>
-#include "fs_mgr_priv_overlayfs.h"
+#include "fs_mgr_overlayfs_control.h"
+#include "fs_mgr_overlayfs_mount.h"
using namespace std::literals;
using android::fs_mgr::Fstab;
diff --git a/fs_mgr/include/fs_mgr.h b/fs_mgr/include/fs_mgr.h
index 43de6d8..bc4a7a6 100644
--- a/fs_mgr/include/fs_mgr.h
+++ b/fs_mgr/include/fs_mgr.h
@@ -85,13 +85,10 @@
#define FS_MGR_DOMNT_FAILED (-1)
#define FS_MGR_DOMNT_BUSY (-2)
#define FS_MGR_DOMNT_SUCCESS 0
-int fs_mgr_do_mount(android::fs_mgr::Fstab* fstab, const char* n_name, char* n_blk_device,
- char* tmp_mount_point);
-int fs_mgr_do_mount(android::fs_mgr::Fstab* fstab, const char* n_name, char* n_blk_device,
- char* tmp_mount_point, bool need_cp, bool metadata_encrypted);
+int fs_mgr_do_mount(android::fs_mgr::Fstab* fstab, const std::string& n_name,
+ const std::string& n_blk_device, int needs_checkpoint, bool needs_encrypt);
int fs_mgr_do_mount_one(const android::fs_mgr::FstabEntry& entry,
const std::string& mount_point = "");
-int fs_mgr_do_tmpfs_mount(const char *n_name);
bool fs_mgr_load_verity_state(int* mode);
// Returns true if verity is enabled on this particular FstabEntry.
bool fs_mgr_is_verity_enabled(const android::fs_mgr::FstabEntry& entry);
diff --git a/fs_mgr/include_fstab/fstab/fstab.h b/fs_mgr/include_fstab/fstab/fstab.h
index a914b53..80b45ba 100644
--- a/fs_mgr/include_fstab/fstab/fstab.h
+++ b/fs_mgr/include_fstab/fstab/fstab.h
@@ -101,6 +101,7 @@
bool SkipMountWithConfig(const std::string& skip_config, Fstab* fstab, bool verbose);
bool ReadFstabFromFile(const std::string& path, Fstab* fstab);
+bool ReadFstabFromProcMounts(Fstab* fstab);
bool ReadFstabFromDt(Fstab* fstab, bool verbose = true);
bool ReadDefaultFstab(Fstab* fstab);
bool SkipMountingPartitions(Fstab* fstab, bool verbose = false);
@@ -130,5 +131,7 @@
// expected name.
std::string GetVerityDeviceName(const FstabEntry& entry);
+bool InRecovery();
+
} // namespace fs_mgr
} // namespace android
diff --git a/fs_mgr/libdm/dm.cpp b/fs_mgr/libdm/dm.cpp
index 1e8c14f..3a9ed9b 100644
--- a/fs_mgr/libdm/dm.cpp
+++ b/fs_mgr/libdm/dm.cpp
@@ -106,6 +106,10 @@
if (!GetDeviceUniquePath(name, &unique_path)) {
LOG(ERROR) << "Failed to get unique path for device " << name;
}
+ // Expect to have uevent generated if the unique path actually exists. This may not exist
+ // if the device was created but has never been activated before it gets deleted.
+ bool need_uevent = !unique_path.empty() && access(unique_path.c_str(), F_OK) == 0;
+
struct dm_ioctl io;
InitIo(&io, name);
@@ -116,7 +120,7 @@
// Check to make sure appropriate uevent is generated so ueventd will
// do the right thing and remove the corresponding device node and symlinks.
- if ((io.flags & DM_UEVENT_GENERATED_FLAG) == 0) {
+ if (need_uevent && (io.flags & DM_UEVENT_GENERATED_FLAG) == 0) {
LOG(ERROR) << "Didn't generate uevent for [" << name << "] removal";
return false;
}
diff --git a/fs_mgr/libdm/dm_table.cpp b/fs_mgr/libdm/dm_table.cpp
index efe03ab..b546995 100644
--- a/fs_mgr/libdm/dm_table.cpp
+++ b/fs_mgr/libdm/dm_table.cpp
@@ -38,11 +38,11 @@
bool DmTable::valid() const {
if (targets_.empty()) {
LOG(ERROR) << "Device-mapper table must have at least one target.";
- return "";
+ return false;
}
if (targets_[0]->start() != 0) {
LOG(ERROR) << "Device-mapper table must start at logical sector 0.";
- return "";
+ return false;
}
return true;
}
diff --git a/fs_mgr/libfiemap/Android.bp b/fs_mgr/libfiemap/Android.bp
index 5deba65..ddda648 100644
--- a/fs_mgr/libfiemap/Android.bp
+++ b/fs_mgr/libfiemap/Android.bp
@@ -93,6 +93,9 @@
test_options: {
min_shipping_api_level: 29,
},
+ header_libs: [
+ "libstorage_literals_headers",
+ ],
require_root: true,
}
diff --git a/fs_mgr/libfiemap/fiemap_writer.cpp b/fs_mgr/libfiemap/fiemap_writer.cpp
index 275388e..06e210e 100644
--- a/fs_mgr/libfiemap/fiemap_writer.cpp
+++ b/fs_mgr/libfiemap/fiemap_writer.cpp
@@ -458,9 +458,34 @@
return FiemapStatus::Error();
}
- if (fallocate(file_fd, 0, 0, file_size)) {
- PLOG(ERROR) << "Failed to allocate space for file: " << file_path << " size: " << file_size;
- return FiemapStatus::FromErrno(errno);
+ // F2FS can return EAGAIN and partially fallocate. Keep trying to fallocate,
+ // and if we don't make forward progress, return ENOSPC.
+ std::optional<off_t> prev_size;
+ while (true) {
+ if (fallocate(file_fd, 0, 0, file_size) == 0) {
+ break;
+ }
+ if (errno != EAGAIN) {
+ PLOG(ERROR) << "Failed to allocate space for file: " << file_path
+ << " size: " << file_size;
+ return FiemapStatus::FromErrno(errno);
+ }
+
+ struct stat s;
+ if (fstat(file_fd, &s) < 0) {
+ PLOG(ERROR) << "Failed to fstat after fallocate failure: " << file_path;
+ return FiemapStatus::FromErrno(errno);
+ }
+ if (!prev_size) {
+ prev_size = {s.st_size};
+ continue;
+ }
+ if (*prev_size >= s.st_size) {
+ LOG(ERROR) << "Fallocate retry failed, got " << s.st_size << ", asked for "
+ << file_size;
+ return FiemapStatus(FiemapStatus::ErrorCode::NO_SPACE);
+ }
+ LOG(INFO) << "Retrying fallocate, got " << s.st_size << ", asked for " << file_size;
}
if (need_explicit_writes) {
diff --git a/fs_mgr/libfiemap/fiemap_writer_test.cpp b/fs_mgr/libfiemap/fiemap_writer_test.cpp
index c65481b..bd97a78 100644
--- a/fs_mgr/libfiemap/fiemap_writer_test.cpp
+++ b/fs_mgr/libfiemap/fiemap_writer_test.cpp
@@ -22,21 +22,25 @@
#include <string.h>
#include <sys/mount.h>
#include <sys/stat.h>
+#include <sys/statvfs.h>
#include <sys/types.h>
#include <sys/vfs.h>
#include <unistd.h>
#include <string>
+#include <utility>
#include <android-base/file.h>
#include <android-base/logging.h>
#include <android-base/stringprintf.h>
#include <android-base/unique_fd.h>
+#include <fstab/fstab.h>
#include <gtest/gtest.h>
#include <libdm/loop_control.h>
#include <libfiemap/fiemap_writer.h>
#include <libfiemap/split_fiemap_writer.h>
#include <libgsi/libgsi.h>
+#include <storage_literals/storage_literals.h>
#include "utility.h"
@@ -46,6 +50,7 @@
using namespace std;
using namespace std::string_literals;
using namespace android::fiemap;
+using namespace android::storage_literals;
using unique_fd = android::base::unique_fd;
using LoopDevice = android::dm::LoopDevice;
@@ -427,90 +432,123 @@
ASSERT_FALSE(ptr->Write(buffer.get(), kSize));
}
-class VerifyBlockWritesExt4 : public ::testing::Test {
+// Get max file size and free space.
+std::pair<uint64_t, uint64_t> GetBigFileLimit(const std::string& mount_point) {
+ struct statvfs fs;
+ if (statvfs(mount_point.c_str(), &fs) < 0) {
+ PLOG(ERROR) << "statfs failed";
+ return {0, 0};
+ }
+
+ auto fs_limit = static_cast<uint64_t>(fs.f_blocks) * (fs.f_bsize - 1);
+ auto fs_free = static_cast<uint64_t>(fs.f_bfree) * fs.f_bsize;
+
+ LOG(INFO) << "Big file limit: " << fs_limit << ", free space: " << fs_free;
+
+ return {fs_limit, fs_free};
+}
+
+class FsTest : public ::testing::Test {
+ protected:
// 2GB Filesystem and 4k block size by default
static constexpr uint64_t block_size = 4096;
- static constexpr uint64_t fs_size = 2147483648;
+ static constexpr uint64_t fs_size = 64 * 1024 * 1024;
- protected:
- void SetUp() override {
- fs_path = std::string(getenv("TMPDIR")) + "/ext4_2G.img";
+ void SetUp() {
+ android::fs_mgr::Fstab fstab;
+ ASSERT_TRUE(android::fs_mgr::ReadFstabFromFile("/proc/mounts", &fstab));
+
+ ASSERT_EQ(access(tmpdir_.path, F_OK), 0);
+ fs_path_ = tmpdir_.path + "/fs_image"s;
+ mntpoint_ = tmpdir_.path + "/mnt_point"s;
+
+ auto entry = android::fs_mgr::GetEntryForMountPoint(&fstab, "/data");
+ ASSERT_NE(entry, nullptr);
+ if (entry->fs_type == "ext4") {
+ SetUpExt4();
+ } else if (entry->fs_type == "f2fs") {
+ SetUpF2fs();
+ } else {
+ FAIL() << "Unrecognized fs_type: " << entry->fs_type;
+ }
+ }
+
+ void SetUpExt4() {
uint64_t count = fs_size / block_size;
std::string dd_cmd =
::android::base::StringPrintf("/system/bin/dd if=/dev/zero of=%s bs=%" PRIu64
" count=%" PRIu64 " > /dev/null 2>&1",
- fs_path.c_str(), block_size, count);
+ fs_path_.c_str(), block_size, count);
std::string mkfs_cmd =
- ::android::base::StringPrintf("/system/bin/mkfs.ext4 -q %s", fs_path.c_str());
+ ::android::base::StringPrintf("/system/bin/mkfs.ext4 -q %s", fs_path_.c_str());
// create mount point
- mntpoint = std::string(getenv("TMPDIR")) + "/fiemap_mnt";
- ASSERT_EQ(mkdir(mntpoint.c_str(), S_IRWXU), 0);
+ ASSERT_EQ(mkdir(mntpoint_.c_str(), S_IRWXU), 0);
// create file for the file system
int ret = system(dd_cmd.c_str());
ASSERT_EQ(ret, 0);
// Get and attach a loop device to the filesystem we created
- LoopDevice loop_dev(fs_path, 10s);
+ LoopDevice loop_dev(fs_path_, 10s);
ASSERT_TRUE(loop_dev.valid());
// create file system
ret = system(mkfs_cmd.c_str());
ASSERT_EQ(ret, 0);
// mount the file system
- ASSERT_EQ(mount(loop_dev.device().c_str(), mntpoint.c_str(), "ext4", 0, nullptr), 0);
+ ASSERT_EQ(mount(loop_dev.device().c_str(), mntpoint_.c_str(), "ext4", 0, nullptr), 0);
}
- void TearDown() override {
- umount(mntpoint.c_str());
- rmdir(mntpoint.c_str());
- unlink(fs_path.c_str());
- }
-
- std::string mntpoint;
- std::string fs_path;
-};
-
-class VerifyBlockWritesF2fs : public ::testing::Test {
- // 2GB Filesystem and 4k block size by default
- static constexpr uint64_t block_size = 4096;
- static constexpr uint64_t fs_size = 2147483648;
-
- protected:
- void SetUp() override {
- fs_path = std::string(getenv("TMPDIR")) + "/f2fs_2G.img";
+ void SetUpF2fs() {
uint64_t count = fs_size / block_size;
std::string dd_cmd =
::android::base::StringPrintf("/system/bin/dd if=/dev/zero of=%s bs=%" PRIu64
" count=%" PRIu64 " > /dev/null 2>&1",
- fs_path.c_str(), block_size, count);
+ fs_path_.c_str(), block_size, count);
std::string mkfs_cmd =
- ::android::base::StringPrintf("/system/bin/make_f2fs -q %s", fs_path.c_str());
+ ::android::base::StringPrintf("/system/bin/make_f2fs -q %s", fs_path_.c_str());
// create mount point
- mntpoint = std::string(getenv("TMPDIR")) + "/fiemap_mnt";
- ASSERT_EQ(mkdir(mntpoint.c_str(), S_IRWXU), 0);
+ ASSERT_EQ(mkdir(mntpoint_.c_str(), S_IRWXU), 0);
// create file for the file system
int ret = system(dd_cmd.c_str());
ASSERT_EQ(ret, 0);
// Get and attach a loop device to the filesystem we created
- LoopDevice loop_dev(fs_path, 10s);
+ LoopDevice loop_dev(fs_path_, 10s);
ASSERT_TRUE(loop_dev.valid());
// create file system
ret = system(mkfs_cmd.c_str());
ASSERT_EQ(ret, 0);
// mount the file system
- ASSERT_EQ(mount(loop_dev.device().c_str(), mntpoint.c_str(), "f2fs", 0, nullptr), 0);
+ ASSERT_EQ(mount(loop_dev.device().c_str(), mntpoint_.c_str(), "f2fs", 0, nullptr), 0);
}
void TearDown() override {
- umount(mntpoint.c_str());
- rmdir(mntpoint.c_str());
- unlink(fs_path.c_str());
+ umount(mntpoint_.c_str());
+ rmdir(mntpoint_.c_str());
+ unlink(fs_path_.c_str());
}
- std::string mntpoint;
- std::string fs_path;
+ TemporaryDir tmpdir_;
+ std::string mntpoint_;
+ std::string fs_path_;
};
+TEST_F(FsTest, LowSpaceError) {
+ auto limits = GetBigFileLimit(mntpoint_);
+ ASSERT_GE(limits.first, 0);
+
+ FiemapUniquePtr ptr;
+
+ auto test_file = mntpoint_ + "/big_file";
+ auto status = FiemapWriter::Open(test_file, limits.first, &ptr);
+ ASSERT_FALSE(status.is_ok());
+ ASSERT_EQ(status.error_code(), FiemapStatus::ErrorCode::NO_SPACE);
+
+ // Also test for EFBIG.
+ status = FiemapWriter::Open(test_file, 16_TiB, &ptr);
+ ASSERT_FALSE(status.is_ok());
+ ASSERT_NE(status.error_code(), FiemapStatus::ErrorCode::NO_SPACE);
+}
+
bool DetermineBlockSize() {
struct statfs s;
if (statfs(gTestDir.c_str(), &s)) {
diff --git a/fs_mgr/libfiemap/include/libfiemap/fiemap_status.h b/fs_mgr/libfiemap/include/libfiemap/fiemap_status.h
index d7b2cf1..1365ba4 100644
--- a/fs_mgr/libfiemap/include/libfiemap/fiemap_status.h
+++ b/fs_mgr/libfiemap/include/libfiemap/fiemap_status.h
@@ -56,8 +56,7 @@
// For logging and debugging only.
std::string string() const;
- protected:
- FiemapStatus(ErrorCode code) : error_code_(code) {}
+ explicit FiemapStatus(ErrorCode code) : error_code_(code) {}
private:
ErrorCode error_code_;
diff --git a/fs_mgr/libsnapshot/Android.bp b/fs_mgr/libsnapshot/Android.bp
index 3dd1f1a..8f35381 100644
--- a/fs_mgr/libsnapshot/Android.bp
+++ b/fs_mgr/libsnapshot/Android.bp
@@ -85,11 +85,9 @@
"android/snapshot/snapshot.proto",
"device_info.cpp",
"snapshot.cpp",
- "snapshot_reader.cpp",
"snapshot_stats.cpp",
"snapshot_stub.cpp",
"snapshot_metadata_updater.cpp",
- "snapshot_writer.cpp",
"partition_cow_creator.cpp",
"return.cpp",
"utility.cpp",
@@ -163,8 +161,11 @@
"libbrotli",
"libz",
"liblz4",
+ "libzstd",
],
- export_include_dirs: ["include"],
+ header_libs: [
+ "libupdate_engine_headers",
+ ],
}
cc_library_static {
@@ -173,12 +174,16 @@
"libsnapshot_cow_defaults",
],
srcs: [
- "libsnapshot_cow/cow_decompress.cpp",
- "libsnapshot_cow/cow_reader.cpp",
- "libsnapshot_cow/cow_writer.cpp",
- "libsnapshot_cow/cow_format.cpp",
"libsnapshot_cow/cow_compress.cpp",
+ "libsnapshot_cow/cow_decompress.cpp",
+ "libsnapshot_cow/cow_format.cpp",
+ "libsnapshot_cow/cow_reader.cpp",
+ "libsnapshot_cow/parser_v2.cpp",
+ "libsnapshot_cow/snapshot_reader.cpp",
+ "libsnapshot_cow/writer_base.cpp",
+ "libsnapshot_cow/writer_v2.cpp",
],
+ export_include_dirs: ["include"],
host_supported: true,
recovery_available: true,
ramdisk_available: true,
@@ -221,9 +226,7 @@
srcs: [
"partition_cow_creator_test.cpp",
"snapshot_metadata_updater_test.cpp",
- "snapshot_reader_test.cpp",
"snapshot_test.cpp",
- "snapshot_writer_test.cpp",
],
shared_libs: [
"libbinder",
@@ -369,7 +372,8 @@
"libsnapshot_cow_defaults",
],
srcs: [
- "libsnapshot_cow/cow_api_test.cpp",
+ "libsnapshot_cow/snapshot_reader_test.cpp",
+ "libsnapshot_cow/test_v2.cpp",
],
cflags: [
"-D_FILE_OFFSET_BITS=64",
@@ -413,6 +417,7 @@
"libbrotli",
"libcrypto_static",
"liblog",
+ "libgflags",
"libsnapshot_cow",
"libz",
],
diff --git a/fs_mgr/libsnapshot/include/libsnapshot/cow_format.h b/fs_mgr/libsnapshot/include/libsnapshot/cow_format.h
index ba75a8d..3a81f63 100644
--- a/fs_mgr/libsnapshot/include/libsnapshot/cow_format.h
+++ b/fs_mgr/libsnapshot/include/libsnapshot/cow_format.h
@@ -15,7 +15,9 @@
#pragma once
#include <stdint.h>
-#include <string>
+
+#include <optional>
+#include <string_view>
namespace android {
namespace snapshot {
@@ -26,8 +28,12 @@
static constexpr uint32_t kCowVersionManifest = 2;
-static constexpr size_t BLOCK_SZ = 4096;
-static constexpr size_t BLOCK_SHIFT = (__builtin_ffs(BLOCK_SZ) - 1);
+static constexpr uint32_t kMinCowVersion = 1;
+static constexpr uint32_t kMaxCowVersion = 2;
+
+// Normally, this should be kMaxCowVersion. When a new version is under testing
+// it may be the previous value of kMaxCowVersion.
+static constexpr uint32_t kDefaultCowVersion = 2;
// This header appears as the first sequence of bytes in the COW. All fields
// in the layout are little-endian encoded. The on-disk layout is:
@@ -53,13 +59,15 @@
// between writing the last operation/data pair, or the footer itself. In this
// case, the safest way to proceed is to assume the last operation is faulty.
-struct CowHeader {
+struct CowHeaderPrefix {
uint64_t magic;
uint16_t major_version;
uint16_t minor_version;
+ uint16_t header_size; // size of CowHeader.
+} __attribute__((packed));
- // Size of this struct.
- uint16_t header_size;
+struct CowHeader {
+ CowHeaderPrefix prefix;
// Size of footer struct
uint16_t footer_size;
@@ -89,7 +97,7 @@
// the compression type of that data (see constants below).
uint8_t compression;
- // Length of Footer Data. Currently 64 for both checksums
+ // Length of Footer Data. Currently 64.
uint16_t data_length;
// The amount of file space used by Cow operations
@@ -99,14 +107,6 @@
uint64_t num_ops;
} __attribute__((packed));
-struct CowFooterData {
- // SHA256 checksums of Footer op
- uint8_t footer_checksum[32];
-
- // SHA256 of the operation sequence.
- uint8_t ops_checksum[32];
-} __attribute__((packed));
-
// Cow operations are currently fixed-size entries, but this may change if
// needed.
struct CowOperation {
@@ -158,16 +158,24 @@
kCowCompressNone = 0,
kCowCompressGz = 1,
kCowCompressBrotli = 2,
- kCowCompressLz4 = 3
+ kCowCompressLz4 = 3,
+ kCowCompressZstd = 4,
};
static constexpr uint8_t kCowReadAheadNotStarted = 0;
static constexpr uint8_t kCowReadAheadInProgress = 1;
static constexpr uint8_t kCowReadAheadDone = 2;
+static inline uint64_t GetCowOpSourceInfoData(const CowOperation* op) {
+ return op->source;
+}
+static inline bool GetCowOpSourceInfoCompression(const CowOperation* op) {
+ return op->compression != kCowCompressNone;
+}
+
struct CowFooter {
CowFooterOperation op;
- CowFooterData data;
+ uint8_t unused[64];
} __attribute__((packed));
struct ScratchMetadata {
@@ -196,5 +204,8 @@
// Ops that have dependencies on old blocks, and must take care in their merge order
bool IsOrderedOp(const CowOperation& op);
+// Convert compression name to internal value.
+std::optional<CowCompressionAlgorithm> CompressionAlgorithmFromString(std::string_view name);
+
} // namespace snapshot
} // namespace android
diff --git a/fs_mgr/libsnapshot/include/libsnapshot/cow_reader.h b/fs_mgr/libsnapshot/include/libsnapshot/cow_reader.h
index e8e4d72..f4ce52f 100644
--- a/fs_mgr/libsnapshot/include/libsnapshot/cow_reader.h
+++ b/fs_mgr/libsnapshot/include/libsnapshot/cow_reader.h
@@ -24,47 +24,24 @@
#include <android-base/unique_fd.h>
#include <libsnapshot/cow_format.h>
+namespace chromeos_update_engine {
+class FileDescriptor;
+} // namespace chromeos_update_engine
+
namespace android {
namespace snapshot {
class ICowOpIter;
-// A ByteSink object handles requests for a buffer of a specific size. It
-// always owns the underlying buffer. It's designed to minimize potential
-// copying as we parse or decompress the COW.
-class IByteSink {
- public:
- virtual ~IByteSink() {}
-
- // Called when the reader has data. The size of the request is given. The
- // sink must return a valid pointer (or null on failure), and return the
- // maximum number of bytes that can be written to the returned buffer.
- //
- // The returned buffer is owned by IByteSink, but must remain valid until
- // the read operation has completed (or the entire buffer has been
- // covered by calls to ReturnData).
- //
- // After calling GetBuffer(), all previous buffers returned are no longer
- // valid.
- //
- // GetBuffer() is intended to be sequential. A returned size of N indicates
- // that the output stream will advance by N bytes, and the ReturnData call
- // indicates that those bytes have been fulfilled. Therefore, it is
- // possible to have ReturnBuffer do nothing, if the implementation doesn't
- // care about incremental writes.
- virtual void* GetBuffer(size_t requested, size_t* actual) = 0;
-
- // Called when a section returned by |GetBuffer| has been filled with data.
- virtual bool ReturnData(void* buffer, size_t length) = 0;
-};
-
// Interface for reading from a snapuserd COW.
class ICowReader {
public:
+ using FileDescriptor = chromeos_update_engine::FileDescriptor;
+
virtual ~ICowReader() {}
// Return the file header.
- virtual bool GetHeader(CowHeader* header) = 0;
+ virtual CowHeader& GetHeader() = 0;
// Return the file footer.
virtual bool GetFooter(CowFooter* footer) = 0;
@@ -83,29 +60,55 @@
virtual std::unique_ptr<ICowOpIter> GetMergeOpIter(bool ignore_progress) = 0;
// Get decoded bytes from the data section, handling any decompression.
- // All retrieved data is passed to the sink.
- virtual bool ReadData(const CowOperation& op, IByteSink* sink) = 0;
+ //
+ // If ignore_bytes is non-zero, it specifies the initial number of bytes
+ // to skip writing to |buffer|.
+ //
+ // Returns the number of bytes written to |buffer|, or -1 on failure.
+ // errno is NOT set.
+ //
+ // Partial reads are not possible unless |buffer_size| is less than the
+ // operation block size.
+ //
+ // The operation pointer must derive from ICowOpIter::Get().
+ virtual ssize_t ReadData(const CowOperation* op, void* buffer, size_t buffer_size,
+ size_t ignore_bytes = 0) = 0;
+
+ // Get the absolute source offset, in bytes, of a CowOperation. Returns
+ // false if the operation does not read from source partitions.
+ virtual bool GetSourceOffset(const CowOperation* op, uint64_t* source_offset) = 0;
};
-// Iterate over a sequence of COW operations.
+static constexpr uint64_t GetBlockFromOffset(const CowHeader& header, uint64_t offset) {
+ return offset / header.block_size;
+}
+
+static constexpr uint64_t GetBlockRelativeOffset(const CowHeader& header, uint64_t offset) {
+ return offset % header.block_size;
+}
+
+// Iterate over a sequence of COW operations. The iterator is bidirectional.
class ICowOpIter {
public:
virtual ~ICowOpIter() {}
- // True if there are no more items to read forward, false otherwise.
- virtual bool Done() = 0;
+ // Returns true if the iterator is at the end of the operation list.
+ // If true, Get() and Next() must not be called.
+ virtual bool AtEnd() = 0;
// Read the current operation.
- virtual const CowOperation& Get() = 0;
+ virtual const CowOperation* Get() = 0;
// Advance to the next item.
virtual void Next() = 0;
+ // Returns true if the iterator is at the beginning of the operation list.
+ // If true, Prev() must not be called; Get() however will be valid if
+ // AtEnd() is not true.
+ virtual bool AtBegin() = 0;
+
// Advance to the previous item.
virtual void Prev() = 0;
-
- // True if there are no more items to read backwards, false otherwise
- virtual bool RDone() = 0;
};
class CowReader final : public ICowReader {
@@ -124,12 +127,11 @@
bool Parse(android::base::borrowed_fd fd, std::optional<uint64_t> label = {});
bool InitForMerge(android::base::unique_fd&& fd);
+
bool VerifyMergeOps() override;
-
- bool GetHeader(CowHeader* header) override;
bool GetFooter(CowFooter* footer) override;
-
bool GetLastLabel(uint64_t* label) override;
+ bool GetSourceOffset(const CowOperation* op, uint64_t* source_offset) override;
// Create a CowOpIter object which contains footer_.num_ops
// CowOperation objects. Get() returns a unique CowOperation object
@@ -139,8 +141,12 @@
std::unique_ptr<ICowOpIter> GetRevMergeOpIter(bool ignore_progress = false) override;
std::unique_ptr<ICowOpIter> GetMergeOpIter(bool ignore_progress = false) override;
- bool ReadData(const CowOperation& op, IByteSink* sink) override;
+ ssize_t ReadData(const CowOperation* op, void* buffer, size_t buffer_size,
+ size_t ignore_bytes = 0) override;
+ CowHeader& GetHeader() override { return header_; }
+
+ bool GetRawBytes(const CowOperation* op, void* buffer, size_t len, size_t* read);
bool GetRawBytes(uint64_t offset, void* buffer, size_t len, size_t* read);
// Returns the total number of data ops that should be merged. This is the
@@ -162,6 +168,7 @@
bool ParseOps(std::optional<uint64_t> label);
bool PrepMergeOps();
uint64_t FindNumCopyops();
+ uint8_t GetCompressionType(const CowOperation* op);
android::base::unique_fd owned_fd_;
android::base::borrowed_fd fd_;
diff --git a/fs_mgr/libsnapshot/include/libsnapshot/cow_writer.h b/fs_mgr/libsnapshot/include/libsnapshot/cow_writer.h
index c7b83a8..d6194eb 100644
--- a/fs_mgr/libsnapshot/include/libsnapshot/cow_writer.h
+++ b/fs_mgr/libsnapshot/include/libsnapshot/cow_writer.h
@@ -1,16 +1,16 @@
-// Copyright (C) 2019 The Android Open Source Project
+// copyright (c) 2019 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
+// 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
+// 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.
+// 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
@@ -61,30 +61,30 @@
// will occur in the sequence they were added to the COW.
class ICowWriter {
public:
- explicit ICowWriter(const CowOptions& options) : options_(options) {}
+ using FileDescriptor = chromeos_update_engine::FileDescriptor;
virtual ~ICowWriter() {}
// Encode an operation that copies the contents of |old_block| to the
// location of |new_block|. 'num_blocks' is the number of contiguous
// COPY operations from |old_block| to |new_block|.
- bool AddCopy(uint64_t new_block, uint64_t old_block, uint64_t num_blocks = 1);
+ virtual bool AddCopy(uint64_t new_block, uint64_t old_block, uint64_t num_blocks = 1) = 0;
// Encode a sequence of raw blocks. |size| must be a multiple of the block size.
- bool AddRawBlocks(uint64_t new_block_start, const void* data, size_t size);
+ virtual bool AddRawBlocks(uint64_t new_block_start, const void* data, size_t size) = 0;
// Add a sequence of xor'd blocks. |size| must be a multiple of the block size.
- bool AddXorBlocks(uint32_t new_block_start, const void* data, size_t size, uint32_t old_block,
- uint16_t offset);
+ virtual bool AddXorBlocks(uint32_t new_block_start, const void* data, size_t size,
+ uint32_t old_block, uint16_t offset) = 0;
// Encode a sequence of zeroed blocks. |size| must be a multiple of the block size.
- bool AddZeroBlocks(uint64_t new_block_start, uint64_t num_blocks);
+ virtual bool AddZeroBlocks(uint64_t new_block_start, uint64_t num_blocks) = 0;
// Add a label to the op sequence.
- bool AddLabel(uint64_t label);
+ virtual bool AddLabel(uint64_t label) = 0;
// Add sequence data for op merging. Data is a list of the destination block numbers.
- bool AddSequenceData(size_t num_ops, const uint32_t* data);
+ virtual bool AddSequenceData(size_t num_ops, const uint32_t* data) = 0;
// Flush all pending writes. This must be called before closing the writer
// to ensure that the correct headers and footers are written.
@@ -93,24 +93,19 @@
// Return number of bytes the cow image occupies on disk.
virtual uint64_t GetCowSize() = 0;
- // Returns true if AddCopy() operations are supported.
- virtual bool SupportsCopyOperation() const { return true; }
+ virtual uint32_t GetBlockSize() const = 0;
+ virtual std::optional<uint32_t> GetMaxBlocks() const = 0;
- const CowOptions& options() { return options_; }
+ // Open an ICowReader for this writer. The reader will be a snapshot of the
+ // current operations in the writer; new writes after OpenReader() will not
+ // be reflected.
+ virtual std::unique_ptr<ICowReader> OpenReader() = 0;
- protected:
- virtual bool EmitCopy(uint64_t new_block, uint64_t old_block, uint64_t num_blocks = 1) = 0;
- virtual bool EmitRawBlocks(uint64_t new_block_start, const void* data, size_t size) = 0;
- virtual bool EmitXorBlocks(uint32_t new_block_start, const void* data, size_t size,
- uint32_t old_block, uint16_t offset) = 0;
- virtual bool EmitZeroBlocks(uint64_t new_block_start, uint64_t num_blocks) = 0;
- virtual bool EmitLabel(uint64_t label) = 0;
- virtual bool EmitSequenceData(size_t num_ops, const uint32_t* data) = 0;
-
- bool ValidateNewBlock(uint64_t new_block);
-
- protected:
- CowOptions options_;
+ // Open a file descriptor. This allows reading and seeing through the cow
+ // as if it were a normal file. The optional source_device must be a valid
+ // path if the CowReader contains any copy or xor operations.
+ virtual std::unique_ptr<FileDescriptor> OpenFileDescriptor(
+ const std::optional<std::string>& source_device) = 0;
};
class CompressWorker {
@@ -149,98 +144,15 @@
std::vector<std::basic_string<uint8_t>>* compressed_data);
};
-class CowWriter : public ICowWriter {
- public:
- explicit CowWriter(const CowOptions& options);
- ~CowWriter();
+// Create an ICowWriter not backed by any file. This is useful for estimating
+// the final size of a cow file.
+std::unique_ptr<ICowWriter> CreateCowEstimator(uint32_t version, const CowOptions& options);
- // Set up the writer.
- // The file starts from the beginning.
- //
- // If fd is < 0, the CowWriter will be opened against /dev/null. This is for
- // computing COW sizes without using storage space.
- bool Initialize(android::base::unique_fd&& fd);
- bool Initialize(android::base::borrowed_fd fd);
- // Set up a writer, assuming that the given label is the last valid label.
- // This will result in dropping any labels that occur after the given on, and will fail
- // if the given label does not appear.
- bool InitializeAppend(android::base::unique_fd&&, uint64_t label);
- bool InitializeAppend(android::base::borrowed_fd fd, uint64_t label);
-
- bool Finalize() override;
-
- uint64_t GetCowSize() override;
-
- uint32_t GetCowVersion() { return header_.major_version; }
-
- protected:
- virtual bool EmitCopy(uint64_t new_block, uint64_t old_block, uint64_t num_blocks = 1) override;
- virtual bool EmitRawBlocks(uint64_t new_block_start, const void* data, size_t size) override;
- virtual bool EmitXorBlocks(uint32_t new_block_start, const void* data, size_t size,
- uint32_t old_block, uint16_t offset) override;
- virtual bool EmitZeroBlocks(uint64_t new_block_start, uint64_t num_blocks) override;
- virtual bool EmitLabel(uint64_t label) override;
- virtual bool EmitSequenceData(size_t num_ops, const uint32_t* data) override;
-
- private:
- bool EmitCluster();
- bool EmitClusterIfNeeded();
- bool EmitBlocks(uint64_t new_block_start, const void* data, size_t size, uint64_t old_block,
- uint16_t offset, uint8_t type);
- void SetupHeaders();
- void SetupWriteOptions();
- bool ParseOptions();
- bool OpenForWrite();
- bool OpenForAppend(uint64_t label);
- bool GetDataPos(uint64_t* pos);
- bool WriteRawData(const void* data, size_t size);
- bool WriteOperation(const CowOperation& op, const void* data = nullptr, size_t size = 0);
- void AddOperation(const CowOperation& op);
- void InitPos();
- void InitBatchWrites();
- void InitWorkers();
- bool FlushCluster();
-
- bool CompressBlocks(size_t num_blocks, const void* data);
- bool SetFd(android::base::borrowed_fd fd);
- bool Sync();
- bool Truncate(off_t length);
- bool EnsureSpaceAvailable(const uint64_t bytes_needed) const;
-
- private:
- android::base::unique_fd owned_fd_;
- android::base::borrowed_fd fd_;
- CowHeader header_{};
- CowFooter footer_{};
- CowCompressionAlgorithm compression_ = kCowCompressNone;
- uint64_t current_op_pos_ = 0;
- uint64_t next_op_pos_ = 0;
- uint64_t next_data_pos_ = 0;
- uint64_t current_data_pos_ = 0;
- ssize_t total_data_written_ = 0;
- uint32_t cluster_size_ = 0;
- uint32_t current_cluster_size_ = 0;
- uint64_t current_data_size_ = 0;
- bool is_dev_null_ = false;
- bool merge_in_progress_ = false;
- bool is_block_device_ = false;
- uint64_t cow_image_size_ = INT64_MAX;
-
- int num_compress_threads_ = 1;
- std::vector<std::unique_ptr<CompressWorker>> compress_threads_;
- std::vector<std::future<bool>> threads_;
- std::vector<std::basic_string<uint8_t>> compressed_buf_;
- std::vector<std::basic_string<uint8_t>>::iterator buf_iter_;
-
- std::vector<std::unique_ptr<CowOperation>> opbuffer_vec_;
- std::vector<std::unique_ptr<uint8_t[]>> databuffer_vec_;
- std::unique_ptr<struct iovec[]> cowop_vec_;
- int op_vec_index_ = 0;
-
- std::unique_ptr<struct iovec[]> data_vec_;
- int data_vec_index_ = 0;
- bool batch_write_ = false;
-};
+// Create an ICowWriter of the given version and options. If a label is given,
+// the writer is opened in append mode.
+std::unique_ptr<ICowWriter> CreateCowWriter(uint32_t version, const CowOptions& options,
+ android::base::unique_fd&& fd,
+ std::optional<uint64_t> label = {});
} // namespace snapshot
} // namespace android
diff --git a/fs_mgr/libsnapshot/include/libsnapshot/mock_cow_writer.h b/fs_mgr/libsnapshot/include/libsnapshot/mock_cow_writer.h
new file mode 100644
index 0000000..c58c654
--- /dev/null
+++ b/fs_mgr/libsnapshot/include/libsnapshot/mock_cow_writer.h
@@ -0,0 +1,45 @@
+//
+// Copyright (C) 2021 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 <gmock/gmock.h>
+#include <libsnapshot/cow_writer.h>
+
+namespace android::snapshot {
+
+class MockCowWriter : public ICowWriter {
+ public:
+ using FileDescriptor = chromeos_update_engine::FileDescriptor;
+
+ MOCK_METHOD(bool, Finalize, (), (override));
+
+ MOCK_METHOD(uint64_t, GetCowSize, (), (override));
+
+ MOCK_METHOD(bool, AddCopy, (uint64_t, uint64_t, uint64_t), (override));
+ MOCK_METHOD(bool, AddRawBlocks, (uint64_t, const void*, size_t), (override));
+ MOCK_METHOD(bool, AddXorBlocks, (uint32_t, const void*, size_t, uint32_t, uint16_t),
+ (override));
+ MOCK_METHOD(bool, AddZeroBlocks, (uint64_t, uint64_t), (override));
+ MOCK_METHOD(bool, AddLabel, (uint64_t), (override));
+ MOCK_METHOD(bool, AddSequenceData, (size_t, const uint32_t*), (override));
+ MOCK_METHOD(uint32_t, GetBlockSize, (), (override, const));
+ MOCK_METHOD(std::optional<uint32_t>, GetMaxBlocks, (), (override, const));
+
+ MOCK_METHOD(std::unique_ptr<ICowReader>, OpenReader, (), (override));
+ MOCK_METHOD(std::unique_ptr<FileDescriptor>, OpenFileDescriptor,
+ (const std::optional<std::string>&), (override));
+};
+
+} // namespace android::snapshot
diff --git a/fs_mgr/libsnapshot/include/libsnapshot/mock_snapshot.h b/fs_mgr/libsnapshot/include/libsnapshot/mock_snapshot.h
index d458b87..ca45d2f 100644
--- a/fs_mgr/libsnapshot/include/libsnapshot/mock_snapshot.h
+++ b/fs_mgr/libsnapshot/include/libsnapshot/mock_snapshot.h
@@ -42,9 +42,9 @@
(const android::fs_mgr::CreateLogicalPartitionParams& params,
std::string* snapshot_path),
(override));
- MOCK_METHOD(std::unique_ptr<ISnapshotWriter>, OpenSnapshotWriter,
+ MOCK_METHOD(std::unique_ptr<ICowWriter>, OpenSnapshotWriter,
(const android::fs_mgr::CreateLogicalPartitionParams& params,
- const std::optional<std::string>&),
+ std::optional<uint64_t>),
(override));
MOCK_METHOD(bool, UnmapUpdateSnapshot, (const std::string& target_partition_name), (override));
MOCK_METHOD(bool, NeedSnapshotsInFirstStageMount, (), (override));
diff --git a/fs_mgr/libsnapshot/include/libsnapshot/mock_snapshot_writer.h b/fs_mgr/libsnapshot/include/libsnapshot/mock_snapshot_writer.h
deleted file mode 100644
index 29828bc..0000000
--- a/fs_mgr/libsnapshot/include/libsnapshot/mock_snapshot_writer.h
+++ /dev/null
@@ -1,55 +0,0 @@
-//
-// Copyright (C) 2021 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 <gmock/gmock.h>
-#include <libsnapshot/snapshot_writer.h>
-
-namespace android::snapshot {
-
-class MockSnapshotWriter : public ISnapshotWriter {
- public:
- using FileDescriptor = ISnapshotWriter::FileDescriptor;
-
- explicit MockSnapshotWriter(const CowOptions& options) : ISnapshotWriter(options) {}
- MockSnapshotWriter() : ISnapshotWriter({}) {}
-
- MOCK_METHOD(bool, Finalize, (), (override));
-
- // Return number of bytes the cow image occupies on disk.
- MOCK_METHOD(uint64_t, GetCowSize, (), (override));
-
- // Returns true if AddCopy() operations are supported.
- MOCK_METHOD(bool, SupportsCopyOperation, (), (const override));
-
- MOCK_METHOD(bool, EmitCopy, (uint64_t, uint64_t, uint64_t), (override));
- MOCK_METHOD(bool, EmitRawBlocks, (uint64_t, const void*, size_t), (override));
- MOCK_METHOD(bool, EmitXorBlocks, (uint32_t, const void*, size_t, uint32_t, uint16_t),
- (override));
- MOCK_METHOD(bool, EmitZeroBlocks, (uint64_t, uint64_t), (override));
- MOCK_METHOD(bool, EmitLabel, (uint64_t), (override));
- MOCK_METHOD(bool, EmitSequenceData, (size_t, const uint32_t*), (override));
-
- // Open the writer in write mode (no append).
- MOCK_METHOD(bool, Initialize, (), (override));
- MOCK_METHOD(bool, VerifyMergeOps, (), (override, const, noexcept));
-
- // Open the writer in append mode, with the last label to resume
- // from. See CowWriter::InitializeAppend.
- MOCK_METHOD(bool, InitializeAppend, (uint64_t label), (override));
-
- MOCK_METHOD(std::unique_ptr<FileDescriptor>, OpenReader, (), (override));
-};
-} // namespace android::snapshot
diff --git a/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h b/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h
index 9eb89b6..df532ee 100644
--- a/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h
+++ b/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h
@@ -33,12 +33,11 @@
#include <libfiemap/image_manager.h>
#include <liblp/builder.h>
#include <liblp/liblp.h>
-#include <update_engine/update_metadata.pb.h>
-
#include <libsnapshot/auto_device.h>
+#include <libsnapshot/cow_writer.h>
#include <libsnapshot/return.h>
-#include <libsnapshot/snapshot_writer.h>
#include <snapuserd/snapuserd_client.h>
+#include <update_engine/update_metadata.pb.h>
#ifndef FRIEND_TEST
#define FRIEND_TEST(test_set_name, individual_test) \
@@ -211,16 +210,13 @@
virtual bool MapUpdateSnapshot(const android::fs_mgr::CreateLogicalPartitionParams& params,
std::string* snapshot_path) = 0;
- // Create an ISnapshotWriter to build a snapshot against a target partition. The partition name
+ // Create an ICowWriter to build a snapshot against a target partition. The partition name
// must be suffixed. If a source partition exists, it must be specified as well. The source
// partition will only be used if raw bytes are needed. The source partition should be an
// absolute path to the device, not a partition name.
- //
- // After calling OpenSnapshotWriter, the caller must invoke Initialize or InitializeForAppend
- // before invoking write operations.
- virtual std::unique_ptr<ISnapshotWriter> OpenSnapshotWriter(
+ virtual std::unique_ptr<ICowWriter> OpenSnapshotWriter(
const android::fs_mgr::CreateLogicalPartitionParams& params,
- const std::optional<std::string>& source_device) = 0;
+ std::optional<uint64_t> label = {}) = 0;
// Unmap a snapshot device or CowWriter that was previously opened with MapUpdateSnapshot,
// OpenSnapshotWriter. All outstanding open descriptors, writers, or
@@ -362,9 +358,9 @@
Return CreateUpdateSnapshots(const DeltaArchiveManifest& manifest) override;
bool MapUpdateSnapshot(const CreateLogicalPartitionParams& params,
std::string* snapshot_path) override;
- std::unique_ptr<ISnapshotWriter> OpenSnapshotWriter(
+ std::unique_ptr<ICowWriter> OpenSnapshotWriter(
const android::fs_mgr::CreateLogicalPartitionParams& params,
- const std::optional<std::string>& source_device) override;
+ std::optional<uint64_t> label) override;
bool UnmapUpdateSnapshot(const std::string& target_partition_name) override;
bool NeedSnapshotsInFirstStageMount() override;
bool CreateLogicalAndSnapshotPartitions(
@@ -693,14 +689,10 @@
};
// Helpers for OpenSnapshotWriter.
- std::unique_ptr<ISnapshotWriter> OpenCompressedSnapshotWriter(
- LockedFile* lock, const std::optional<std::string>& source_device,
- const std::string& partition_name, const SnapshotStatus& status,
- const SnapshotPaths& paths);
- std::unique_ptr<ISnapshotWriter> OpenKernelSnapshotWriter(
- LockedFile* lock, const std::optional<std::string>& source_device,
- const std::string& partition_name, const SnapshotStatus& status,
- const SnapshotPaths& paths);
+ std::unique_ptr<ICowWriter> OpenCompressedSnapshotWriter(LockedFile* lock,
+ const SnapshotStatus& status,
+ const SnapshotPaths& paths,
+ std::optional<uint64_t> label);
// Map the base device, COW devices, and snapshot device.
bool MapPartitionWithSnapshot(LockedFile* lock, CreateLogicalPartitionParams params,
@@ -747,7 +739,7 @@
// Initialize snapshots so that they can be mapped later.
// Map the COW partition and zero-initialize the header.
Return InitializeUpdateSnapshots(
- LockedFile* lock, MetadataBuilder* target_metadata,
+ LockedFile* lock, uint32_t cow_version, MetadataBuilder* target_metadata,
const LpMetadata* exported_target_metadata, const std::string& target_suffix,
const std::map<std::string, SnapshotStatus>& all_snapshot_status);
diff --git a/fs_mgr/libsnapshot/include/libsnapshot/snapshot_stub.h b/fs_mgr/libsnapshot/include/libsnapshot/snapshot_stub.h
index 171c7c6..1c9b403 100644
--- a/fs_mgr/libsnapshot/include/libsnapshot/snapshot_stub.h
+++ b/fs_mgr/libsnapshot/include/libsnapshot/snapshot_stub.h
@@ -40,9 +40,9 @@
const chromeos_update_engine::DeltaArchiveManifest& manifest) override;
bool MapUpdateSnapshot(const android::fs_mgr::CreateLogicalPartitionParams& params,
std::string* snapshot_path) override;
- std::unique_ptr<ISnapshotWriter> OpenSnapshotWriter(
+ std::unique_ptr<ICowWriter> OpenSnapshotWriter(
const android::fs_mgr::CreateLogicalPartitionParams& params,
- const std::optional<std::string>& source_device) override;
+ std::optional<uint64_t> label) override;
bool UnmapUpdateSnapshot(const std::string& target_partition_name) override;
bool NeedSnapshotsInFirstStageMount() override;
bool CreateLogicalAndSnapshotPartitions(
diff --git a/fs_mgr/libsnapshot/include/libsnapshot/snapshot_writer.h b/fs_mgr/libsnapshot/include/libsnapshot/snapshot_writer.h
deleted file mode 100644
index 0e3b1db..0000000
--- a/fs_mgr/libsnapshot/include/libsnapshot/snapshot_writer.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 <optional>
-
-#include <android-base/unique_fd.h>
-
-#include <libsnapshot/cow_writer.h>
-
-namespace chromeos_update_engine {
-class FileDescriptor;
-} // namespace chromeos_update_engine
-
-namespace android {
-namespace snapshot {
-
-class ISnapshotWriter : public ICowWriter {
- public:
- using FileDescriptor = chromeos_update_engine::FileDescriptor;
-
- explicit ISnapshotWriter(const CowOptions& options);
-
- // Set the source device. This is used for AddCopy() operations, if the
- // underlying writer needs the original bytes (for example if backed by
- // dm-snapshot or if writing directly to an unsnapshotted region). The
- // device is only opened on the first operation that requires it.
- void SetSourceDevice(const std::string& source_device);
-
- // Open the writer in write mode (no append).
- virtual bool Initialize() = 0;
-
- // Open the writer in append mode, with the last label to resume
- // from. See CowWriter::InitializeAppend.
- virtual bool InitializeAppend(uint64_t label) = 0;
-
- virtual std::unique_ptr<FileDescriptor> OpenReader() = 0;
- virtual bool VerifyMergeOps() const noexcept = 0;
-
- protected:
- android::base::borrowed_fd GetSourceFd();
-
- std::optional<std::string> source_device_;
-
- private:
- android::base::unique_fd source_fd_;
-};
-
-// Send writes to a COW or a raw device directly, based on a threshold.
-class CompressedSnapshotWriter final : public ISnapshotWriter {
- public:
- CompressedSnapshotWriter(const CowOptions& options);
-
- // Sets the COW device; this is required.
- bool SetCowDevice(android::base::unique_fd&& cow_device);
-
- bool Initialize() override;
- bool InitializeAppend(uint64_t label) override;
- bool Finalize() override;
- uint64_t GetCowSize() override;
- std::unique_ptr<FileDescriptor> OpenReader() override;
- bool VerifyMergeOps() const noexcept;
-
- protected:
- bool EmitCopy(uint64_t new_block, uint64_t old_block, uint64_t num_blocks = 1) override;
- bool EmitRawBlocks(uint64_t new_block_start, const void* data, size_t size) override;
- bool EmitXorBlocks(uint32_t new_block_start, const void* data, size_t size, uint32_t old_block,
- uint16_t offset) override;
- bool EmitZeroBlocks(uint64_t new_block_start, uint64_t num_blocks) override;
- bool EmitLabel(uint64_t label) override;
- bool EmitSequenceData(size_t num_ops, const uint32_t* data) override;
-
- private:
- std::unique_ptr<CowReader> OpenCowReader() const;
- android::base::unique_fd cow_device_;
-
- std::unique_ptr<CowWriter> cow_;
-};
-
-// Write directly to a dm-snapshot device.
-class OnlineKernelSnapshotWriter final : public ISnapshotWriter {
- public:
- OnlineKernelSnapshotWriter(const CowOptions& options);
-
- // Set the device used for all writes.
- void SetSnapshotDevice(android::base::unique_fd&& snapshot_fd, uint64_t cow_size);
-
- bool Initialize() override { return true; }
- bool InitializeAppend(uint64_t) override { return true; }
-
- bool Finalize() override;
- uint64_t GetCowSize() override { return cow_size_; }
- std::unique_ptr<FileDescriptor> OpenReader() override;
-
- // Online kernel snapshot writer doesn't care about merge sequences.
- // So ignore.
- bool VerifyMergeOps() const noexcept override { return true; }
-
- protected:
- bool EmitRawBlocks(uint64_t new_block_start, const void* data, size_t size) override;
- bool EmitZeroBlocks(uint64_t new_block_start, uint64_t num_blocks) override;
- bool EmitXorBlocks(uint32_t new_block_start, const void* data, size_t size, uint32_t old_block,
- uint16_t offset) override;
- bool EmitCopy(uint64_t new_block, uint64_t old_block, uint64_t num_blocks = 1) override;
- bool EmitLabel(uint64_t label) override;
- bool EmitSequenceData(size_t num_ops, const uint32_t* data) override;
-
- private:
- android::base::unique_fd snapshot_fd_;
- uint64_t cow_size_ = 0;
-};
-
-} // namespace snapshot
-} // namespace android
diff --git a/fs_mgr/libsnapshot/include_test/libsnapshot/test_helpers.h b/fs_mgr/libsnapshot/include_test/libsnapshot/test_helpers.h
index 24c91a8..5e9f049 100644
--- a/fs_mgr/libsnapshot/include_test/libsnapshot/test_helpers.h
+++ b/fs_mgr/libsnapshot/include_test/libsnapshot/test_helpers.h
@@ -198,7 +198,7 @@
// Expect space of |path| is multiple of 4K.
bool WriteRandomData(const std::string& path, std::optional<size_t> expect_size = std::nullopt,
std::string* hash = nullptr);
-std::string HashSnapshot(ISnapshotWriter* writer);
+std::string HashSnapshot(ICowWriter::FileDescriptor* writer);
std::string ToHexString(const uint8_t* buf, size_t len);
@@ -214,29 +214,6 @@
// Get partition size from update package metadata.
uint64_t GetSize(PartitionUpdate* partition_update);
-// Util class for test cases on low space scenario. These tests assumes image manager
-// uses /data as backup device.
-class LowSpaceUserdata {
- public:
- // Set the maximum free space allowed for this test. If /userdata has more space than the given
- // number, a file is allocated to consume space.
- AssertionResult Init(uint64_t max_free_space);
-
- uint64_t free_space() const;
- uint64_t available_space() const;
- uint64_t bsize() const;
-
- private:
- AssertionResult ReadUserdataStats();
-
- static constexpr const char* kUserDataDevice = "/data";
- std::unique_ptr<TemporaryFile> big_file_;
- bool initialized_ = false;
- uint64_t free_space_ = 0;
- uint64_t available_space_ = 0;
- uint64_t bsize_ = 0;
-};
-
bool IsVirtualAbEnabled();
#define SKIP_IF_NON_VIRTUAL_AB() \
diff --git a/fs_mgr/libsnapshot/libsnapshot_cow/cow_compress.cpp b/fs_mgr/libsnapshot/libsnapshot_cow/cow_compress.cpp
index 9b50986..a4a0ad6 100644
--- a/fs_mgr/libsnapshot/libsnapshot_cow/cow_compress.cpp
+++ b/fs_mgr/libsnapshot/libsnapshot_cow/cow_compress.cpp
@@ -29,9 +29,27 @@
#include <libsnapshot/cow_writer.h>
#include <lz4.h>
#include <zlib.h>
+#include <zstd.h>
namespace android {
namespace snapshot {
+
+std::optional<CowCompressionAlgorithm> CompressionAlgorithmFromString(std::string_view name) {
+ if (name == "gz") {
+ return {kCowCompressGz};
+ } else if (name == "brotli") {
+ return {kCowCompressBrotli};
+ } else if (name == "lz4") {
+ return {kCowCompressLz4};
+ } else if (name == "zstd") {
+ return {kCowCompressZstd};
+ } else if (name == "none" || name.empty()) {
+ return {kCowCompressNone};
+ } else {
+ return {};
+ }
+}
+
std::basic_string<uint8_t> CompressWorker::Compress(const void* data, size_t length) {
return Compress(compression_, data, length);
}
@@ -97,6 +115,23 @@
}
return buffer;
}
+ case kCowCompressZstd: {
+ std::basic_string<uint8_t> buffer(ZSTD_compressBound(length), '\0');
+ const auto compressed_size =
+ ZSTD_compress(buffer.data(), buffer.size(), data, length, 0);
+ if (compressed_size <= 0) {
+ LOG(ERROR) << "ZSTD compression failed " << compressed_size;
+ return {};
+ }
+ // Don't run compression if the compressed output is larger
+ if (compressed_size >= length) {
+ buffer.resize(length);
+ memcpy(buffer.data(), data, length);
+ } else {
+ buffer.resize(compressed_size);
+ }
+ return buffer;
+ }
default:
LOG(ERROR) << "unhandled compression type: " << compression;
break;
diff --git a/fs_mgr/libsnapshot/libsnapshot_cow/cow_decompress.cpp b/fs_mgr/libsnapshot/libsnapshot_cow/cow_decompress.cpp
index 139a29f..da90cc0 100644
--- a/fs_mgr/libsnapshot/libsnapshot_cow/cow_decompress.cpp
+++ b/fs_mgr/libsnapshot/libsnapshot_cow/cow_decompress.cpp
@@ -16,124 +16,122 @@
#include "cow_decompress.h"
+#include <array>
+#include <cstring>
#include <utility>
+#include <vector>
#include <android-base/logging.h>
#include <brotli/decode.h>
#include <lz4.h>
#include <zlib.h>
+#include <zstd.h>
namespace android {
namespace snapshot {
-class NoDecompressor final : public IDecompressor {
- public:
- bool Decompress(size_t) override;
-};
+ssize_t IByteStream::ReadFully(void* buffer, size_t buffer_size) {
+ size_t stream_remaining = Size();
-bool NoDecompressor::Decompress(size_t) {
- size_t stream_remaining = stream_->Size();
+ char* buffer_start = reinterpret_cast<char*>(buffer);
+ char* buffer_pos = buffer_start;
+ size_t buffer_remaining = buffer_size;
while (stream_remaining) {
- size_t buffer_size = stream_remaining;
- uint8_t* buffer = reinterpret_cast<uint8_t*>(sink_->GetBuffer(buffer_size, &buffer_size));
- if (!buffer) {
- LOG(ERROR) << "Could not acquire buffer from sink";
- return false;
+ const size_t to_read = std::min(buffer_remaining, stream_remaining);
+ const ssize_t actual_read = Read(buffer_pos, to_read);
+ if (actual_read < 0) {
+ return -1;
}
+ if (!actual_read) {
+ LOG(ERROR) << "Stream ended prematurely";
+ return -1;
+ }
+ CHECK_LE(actual_read, to_read);
- // Read until we can fill the buffer.
- uint8_t* buffer_pos = buffer;
- size_t bytes_to_read = std::min(buffer_size, stream_remaining);
- while (bytes_to_read) {
- size_t read;
- if (!stream_->Read(buffer_pos, bytes_to_read, &read)) {
- return false;
- }
- if (!read) {
- LOG(ERROR) << "Stream ended prematurely";
- return false;
- }
- if (!sink_->ReturnData(buffer_pos, read)) {
- LOG(ERROR) << "Could not return buffer to sink";
- return false;
- }
- buffer_pos += read;
- bytes_to_read -= read;
- stream_remaining -= read;
- }
+ stream_remaining -= actual_read;
+ buffer_pos += actual_read;
+ buffer_remaining -= actual_read;
}
- return true;
+ return buffer_pos - buffer_start;
}
-std::unique_ptr<IDecompressor> IDecompressor::Uncompressed() {
- return std::unique_ptr<IDecompressor>(new NoDecompressor());
+std::unique_ptr<IDecompressor> IDecompressor::FromString(std::string_view compressor) {
+ if (compressor == "lz4") {
+ return IDecompressor::Lz4();
+ } else if (compressor == "brotli") {
+ return IDecompressor::Brotli();
+ } else if (compressor == "gz") {
+ return IDecompressor::Gz();
+ } else {
+ return nullptr;
+ }
}
// Read chunks of the COW and incrementally stream them to the decoder.
class StreamDecompressor : public IDecompressor {
public:
- bool Decompress(size_t output_bytes) override;
+ ssize_t Decompress(void* buffer, size_t buffer_size, size_t decompressed_size,
+ size_t ignore_bytes) override;
virtual bool Init() = 0;
- virtual bool DecompressInput(const uint8_t* data, size_t length) = 0;
- virtual bool Done() = 0;
+ virtual bool PartialDecompress(const uint8_t* data, size_t length) = 0;
+ bool OutputFull() const { return !ignore_bytes_ && !output_buffer_remaining_; }
protected:
- bool GetFreshBuffer();
-
- size_t output_bytes_;
size_t stream_remaining_;
uint8_t* output_buffer_ = nullptr;
size_t output_buffer_remaining_ = 0;
+ size_t ignore_bytes_ = 0;
+ bool decompressor_ended_ = false;
};
static constexpr size_t kChunkSize = 4096;
-bool StreamDecompressor::Decompress(size_t output_bytes) {
+ssize_t StreamDecompressor::Decompress(void* buffer, size_t buffer_size, size_t,
+ size_t ignore_bytes) {
if (!Init()) {
return false;
}
stream_remaining_ = stream_->Size();
- output_bytes_ = output_bytes;
+ output_buffer_ = reinterpret_cast<uint8_t*>(buffer);
+ output_buffer_remaining_ = buffer_size;
+ ignore_bytes_ = ignore_bytes;
uint8_t chunk[kChunkSize];
- while (stream_remaining_) {
- size_t read = std::min(stream_remaining_, sizeof(chunk));
- if (!stream_->Read(chunk, read, &read)) {
- return false;
+ while (stream_remaining_ && output_buffer_remaining_ && !decompressor_ended_) {
+ size_t max_read = std::min(stream_remaining_, sizeof(chunk));
+ ssize_t read = stream_->Read(chunk, max_read);
+ if (read < 0) {
+ return -1;
}
if (!read) {
LOG(ERROR) << "Stream ended prematurely";
- return false;
+ return -1;
}
- if (!DecompressInput(chunk, read)) {
- return false;
+ if (!PartialDecompress(chunk, read)) {
+ return -1;
}
-
stream_remaining_ -= read;
+ }
- if (stream_remaining_ && Done()) {
+ if (stream_remaining_) {
+ if (decompressor_ended_ && !OutputFull()) {
+ // If there's more input in the stream, but we haven't finished
+ // consuming ignored bytes or available output space yet, then
+ // something weird happened. Report it and fail.
LOG(ERROR) << "Decompressor terminated early";
- return false;
+ return -1;
+ }
+ } else {
+ if (!decompressor_ended_ && !OutputFull()) {
+ // The stream ended, but the decoder doesn't think so, and there are
+ // more bytes in the output buffer.
+ LOG(ERROR) << "Decompressor expected more bytes";
+ return -1;
}
}
- if (!Done()) {
- LOG(ERROR) << "Decompressor expected more bytes";
- return false;
- }
- return true;
-}
-
-bool StreamDecompressor::GetFreshBuffer() {
- size_t request_size = std::min(output_bytes_, kChunkSize);
- output_buffer_ =
- reinterpret_cast<uint8_t*>(sink_->GetBuffer(request_size, &output_buffer_remaining_));
- if (!output_buffer_) {
- LOG(ERROR) << "Could not acquire buffer from sink";
- return false;
- }
- return true;
+ return buffer_size - output_buffer_remaining_;
}
class GzDecompressor final : public StreamDecompressor {
@@ -141,12 +139,10 @@
~GzDecompressor();
bool Init() override;
- bool DecompressInput(const uint8_t* data, size_t length) override;
- bool Done() override { return ended_; }
+ bool PartialDecompress(const uint8_t* data, size_t length) override;
private:
z_stream z_ = {};
- bool ended_ = false;
};
bool GzDecompressor::Init() {
@@ -161,23 +157,39 @@
inflateEnd(&z_);
}
-bool GzDecompressor::DecompressInput(const uint8_t* data, size_t length) {
+bool GzDecompressor::PartialDecompress(const uint8_t* data, size_t length) {
z_.next_in = reinterpret_cast<Bytef*>(const_cast<uint8_t*>(data));
z_.avail_in = length;
- while (z_.avail_in) {
- // If no more output buffer, grab a new buffer.
- if (z_.avail_out == 0) {
- if (!GetFreshBuffer()) {
- return false;
- }
- z_.next_out = reinterpret_cast<Bytef*>(output_buffer_);
- z_.avail_out = output_buffer_remaining_;
+ // If we're asked to ignore starting bytes, we sink those into the output
+ // repeatedly until there is nothing left to ignore.
+ while (ignore_bytes_ && z_.avail_in) {
+ std::array<Bytef, kChunkSize> ignore_buffer;
+ size_t max_ignore = std::min(ignore_bytes_, ignore_buffer.size());
+ z_.next_out = ignore_buffer.data();
+ z_.avail_out = max_ignore;
+
+ int rv = inflate(&z_, Z_NO_FLUSH);
+ if (rv != Z_OK && rv != Z_STREAM_END) {
+ LOG(ERROR) << "inflate returned error code " << rv;
+ return false;
}
- // Remember the position of the output buffer so we can call ReturnData.
- auto avail_out = z_.avail_out;
+ size_t returned = max_ignore - z_.avail_out;
+ CHECK_LE(returned, ignore_bytes_);
+ ignore_bytes_ -= returned;
+
+ if (rv == Z_STREAM_END) {
+ decompressor_ended_ = true;
+ return true;
+ }
+ }
+
+ z_.next_out = reinterpret_cast<Bytef*>(output_buffer_);
+ z_.avail_out = output_buffer_remaining_;
+
+ while (z_.avail_in && z_.avail_out) {
// Decompress.
int rv = inflate(&z_, Z_NO_FLUSH);
if (rv != Z_OK && rv != Z_STREAM_END) {
@@ -185,20 +197,14 @@
return false;
}
- size_t returned = avail_out - z_.avail_out;
- if (!sink_->ReturnData(output_buffer_, returned)) {
- LOG(ERROR) << "Could not return buffer to sink";
- return false;
- }
+ size_t returned = output_buffer_remaining_ - z_.avail_out;
+ CHECK_LE(returned, output_buffer_remaining_);
+
output_buffer_ += returned;
output_buffer_remaining_ -= returned;
if (rv == Z_STREAM_END) {
- if (z_.avail_in) {
- LOG(ERROR) << "Gz stream ended prematurely";
- return false;
- }
- ended_ = true;
+ decompressor_ended_ = true;
return true;
}
}
@@ -214,8 +220,7 @@
~BrotliDecompressor();
bool Init() override;
- bool DecompressInput(const uint8_t* data, size_t length) override;
- bool Done() override { return BrotliDecoderIsFinished(decoder_); }
+ bool PartialDecompress(const uint8_t* data, size_t length) override;
private:
BrotliDecoderState* decoder_ = nullptr;
@@ -232,28 +237,41 @@
}
}
-bool BrotliDecompressor::DecompressInput(const uint8_t* data, size_t length) {
+bool BrotliDecompressor::PartialDecompress(const uint8_t* data, size_t length) {
size_t available_in = length;
const uint8_t* next_in = data;
- bool needs_more_output = false;
- while (available_in || needs_more_output) {
- if (!output_buffer_remaining_ && !GetFreshBuffer()) {
+ while (available_in && ignore_bytes_ && !BrotliDecoderIsFinished(decoder_)) {
+ std::array<uint8_t, kChunkSize> ignore_buffer;
+ size_t max_ignore = std::min(ignore_bytes_, ignore_buffer.size());
+ size_t ignore_size = max_ignore;
+
+ uint8_t* ignore_buffer_ptr = ignore_buffer.data();
+ auto r = BrotliDecoderDecompressStream(decoder_, &available_in, &next_in, &ignore_size,
+ &ignore_buffer_ptr, nullptr);
+ if (r == BROTLI_DECODER_RESULT_ERROR) {
+ LOG(ERROR) << "brotli decode failed";
+ return false;
+ } else if (r == BROTLI_DECODER_RESULT_NEEDS_MORE_INPUT && available_in) {
+ LOG(ERROR) << "brotli unexpected needs more input";
return false;
}
+ ignore_bytes_ -= max_ignore - ignore_size;
+ }
- auto output_buffer = output_buffer_;
+ while (available_in && !BrotliDecoderIsFinished(decoder_)) {
auto r = BrotliDecoderDecompressStream(decoder_, &available_in, &next_in,
&output_buffer_remaining_, &output_buffer_, nullptr);
if (r == BROTLI_DECODER_RESULT_ERROR) {
LOG(ERROR) << "brotli decode failed";
return false;
- }
- if (!sink_->ReturnData(output_buffer, output_buffer_ - output_buffer)) {
+ } else if (r == BROTLI_DECODER_RESULT_NEEDS_MORE_INPUT && available_in) {
+ LOG(ERROR) << "brotli unexpected needs more input";
return false;
}
- needs_more_output = (r == BROTLI_DECODER_RESULT_NEEDS_MORE_OUTPUT);
}
+
+ decompressor_ended_ = BrotliDecoderIsFinished(decoder_);
return true;
}
@@ -265,44 +283,101 @@
public:
~Lz4Decompressor() override = default;
- bool Decompress(const size_t output_size) override {
- size_t actual_buffer_size = 0;
- auto&& output_buffer = sink_->GetBuffer(output_size, &actual_buffer_size);
- if (actual_buffer_size != output_size) {
- LOG(ERROR) << "Failed to allocate buffer of size " << output_size << " only got "
- << actual_buffer_size << " bytes";
- return false;
+ ssize_t Decompress(void* buffer, size_t buffer_size, size_t decompressed_size,
+ size_t ignore_bytes) override {
+ std::string input_buffer(stream_->Size(), '\0');
+ ssize_t streamed_in = stream_->ReadFully(input_buffer.data(), input_buffer.size());
+ if (streamed_in < 0) {
+ return -1;
}
- // If input size is same as output size, then input is uncompressed.
- if (stream_->Size() == output_size) {
- size_t bytes_read = 0;
- stream_->Read(output_buffer, output_size, &bytes_read);
- if (bytes_read != output_size) {
- LOG(ERROR) << "Failed to read all input at once. Expected: " << output_size
- << " actual: " << bytes_read;
- return false;
+ CHECK_EQ(streamed_in, stream_->Size());
+
+ char* decode_buffer = reinterpret_cast<char*>(buffer);
+ size_t decode_buffer_size = buffer_size;
+
+ // It's unclear if LZ4 can exactly satisfy a partial decode request, so
+ // if we get one, create a temporary buffer.
+ std::string temp;
+ if (buffer_size < decompressed_size) {
+ temp.resize(decompressed_size, '\0');
+ decode_buffer = temp.data();
+ decode_buffer_size = temp.size();
+ }
+
+ const int bytes_decompressed = LZ4_decompress_safe(input_buffer.data(), decode_buffer,
+ input_buffer.size(), decode_buffer_size);
+ if (bytes_decompressed < 0) {
+ LOG(ERROR) << "Failed to decompress LZ4 block, code: " << bytes_decompressed;
+ return -1;
+ }
+ if (bytes_decompressed != decompressed_size) {
+ LOG(ERROR) << "Failed to decompress LZ4 block, expected output size: "
+ << bytes_decompressed << ", actual: " << bytes_decompressed;
+ return -1;
+ }
+ CHECK_LE(bytes_decompressed, decode_buffer_size);
+
+ if (ignore_bytes > bytes_decompressed) {
+ LOG(ERROR) << "Ignoring more bytes than exist in stream (ignoring " << ignore_bytes
+ << ", got " << bytes_decompressed << ")";
+ return -1;
+ }
+
+ if (temp.empty()) {
+ // LZ4's API has no way to sink out the first N bytes of decoding,
+ // so we read them all in and memmove() to drop the partial read.
+ if (ignore_bytes) {
+ memmove(decode_buffer, decode_buffer + ignore_bytes,
+ bytes_decompressed - ignore_bytes);
}
- sink_->ReturnData(output_buffer, output_size);
- return true;
+ return bytes_decompressed - ignore_bytes;
}
+
+ size_t max_copy = std::min(bytes_decompressed - ignore_bytes, buffer_size);
+ memcpy(buffer, temp.data() + ignore_bytes, max_copy);
+ return max_copy;
+ }
+};
+
+class ZstdDecompressor final : public IDecompressor {
+ public:
+ ssize_t Decompress(void* buffer, size_t buffer_size, size_t decompressed_size,
+ size_t ignore_bytes = 0) override {
+ if (buffer_size < decompressed_size - ignore_bytes) {
+ LOG(INFO) << "buffer size " << buffer_size
+ << " is not large enough to hold decompressed data. Decompressed size "
+ << decompressed_size << ", ignore_bytes " << ignore_bytes;
+ return -1;
+ }
+ if (ignore_bytes == 0) {
+ if (!Decompress(buffer, decompressed_size)) {
+ return -1;
+ }
+ return decompressed_size;
+ }
+ std::vector<unsigned char> ignore_buf(decompressed_size);
+ if (!Decompress(buffer, decompressed_size)) {
+ return -1;
+ }
+ memcpy(buffer, ignore_buf.data() + ignore_bytes, buffer_size);
+ return decompressed_size;
+ }
+ bool Decompress(void* output_buffer, const size_t output_size) {
std::string input_buffer;
input_buffer.resize(stream_->Size());
- size_t bytes_read = 0;
- stream_->Read(input_buffer.data(), input_buffer.size(), &bytes_read);
+ size_t bytes_read = stream_->Read(input_buffer.data(), input_buffer.size());
if (bytes_read != input_buffer.size()) {
LOG(ERROR) << "Failed to read all input at once. Expected: " << input_buffer.size()
<< " actual: " << bytes_read;
return false;
}
- const int bytes_decompressed =
- LZ4_decompress_safe(input_buffer.data(), static_cast<char*>(output_buffer),
- input_buffer.size(), output_size);
+ const auto bytes_decompressed = ZSTD_decompress(output_buffer, output_size,
+ input_buffer.data(), input_buffer.size());
if (bytes_decompressed != output_size) {
- LOG(ERROR) << "Failed to decompress LZ4 block, expected output size: " << output_size
+ LOG(ERROR) << "Failed to decompress ZSTD block, expected output size: " << output_size
<< ", actual: " << bytes_decompressed;
return false;
}
- sink_->ReturnData(output_buffer, output_size);
return true;
}
};
@@ -311,5 +386,9 @@
return std::make_unique<Lz4Decompressor>();
}
+std::unique_ptr<IDecompressor> IDecompressor::Zstd() {
+ return std::make_unique<ZstdDecompressor>();
+}
+
} // namespace snapshot
} // namespace android
diff --git a/fs_mgr/libsnapshot/libsnapshot_cow/cow_decompress.h b/fs_mgr/libsnapshot/libsnapshot_cow/cow_decompress.h
index 7f74eda..52b3d76 100644
--- a/fs_mgr/libsnapshot/libsnapshot_cow/cow_decompress.h
+++ b/fs_mgr/libsnapshot/libsnapshot_cow/cow_decompress.h
@@ -26,11 +26,16 @@
virtual ~IByteStream() {}
// Read up to |length| bytes, storing the number of bytes read in the out-
- // parameter. If the end of the stream is reached, 0 is returned.
- virtual bool Read(void* buffer, size_t length, size_t* read) = 0;
+ // parameter. If the end of the stream is reached, 0 is returned. On error,
+ // -1 is returned. errno is NOT set.
+ virtual ssize_t Read(void* buffer, size_t length) = 0;
// Size of the stream.
virtual size_t Size() const = 0;
+
+ // Helper for Read(). Read the entire stream into |buffer|, up to |length|
+ // bytes.
+ ssize_t ReadFully(void* buffer, size_t length);
};
class IDecompressor {
@@ -42,16 +47,24 @@
static std::unique_ptr<IDecompressor> Gz();
static std::unique_ptr<IDecompressor> Brotli();
static std::unique_ptr<IDecompressor> Lz4();
+ static std::unique_ptr<IDecompressor> Zstd();
- // |output_bytes| is the expected total number of bytes to sink.
- virtual bool Decompress(size_t output_bytes) = 0;
+ static std::unique_ptr<IDecompressor> FromString(std::string_view compressor);
+
+ // Decompress at most |buffer_size| bytes, ignoring the first |ignore_bytes|
+ // of the decoded stream. |buffer_size| must be at least one byte.
+ // |decompressed_size| is the expected total size if the entire stream were
+ // decompressed.
+ //
+ // Returns the number of bytes written to |buffer|, or -1 on error. errno
+ // is NOT set.
+ virtual ssize_t Decompress(void* buffer, size_t buffer_size, size_t decompressed_size,
+ size_t ignore_bytes = 0) = 0;
void set_stream(IByteStream* stream) { stream_ = stream; }
- void set_sink(IByteSink* sink) { sink_ = sink; }
protected:
IByteStream* stream_ = nullptr;
- IByteSink* sink_ = nullptr;
};
} // namespace snapshot
diff --git a/fs_mgr/libsnapshot/libsnapshot_cow/cow_format.cpp b/fs_mgr/libsnapshot/libsnapshot_cow/cow_format.cpp
index 94c4109..ff3ccec 100644
--- a/fs_mgr/libsnapshot/libsnapshot_cow/cow_format.cpp
+++ b/fs_mgr/libsnapshot/libsnapshot_cow/cow_format.cpp
@@ -19,10 +19,13 @@
#include <unistd.h>
#include <android-base/logging.h>
+#include "writer_v2.h"
namespace android {
namespace snapshot {
+using android::base::unique_fd;
+
std::ostream& operator<<(std::ostream& os, CowOperation const& op) {
os << "CowOperation(type:";
if (op.type == kCowCopyOp)
@@ -57,8 +60,6 @@
os << "data_length:" << op.data_length << ",\t";
os << "new_block:" << op.new_block << ",\t";
os << "source:" << op.source;
- if (op.type == kCowXorOp)
- os << " (block:" << op.source / BLOCK_SZ << " offset:" << op.source % BLOCK_SZ << ")";
os << ")";
return os;
}
@@ -105,5 +106,27 @@
}
}
+std::unique_ptr<ICowWriter> CreateCowWriter(uint32_t version, const CowOptions& options,
+ unique_fd&& fd, std::optional<uint64_t> label) {
+ std::unique_ptr<CowWriterBase> base;
+ switch (version) {
+ case 1:
+ case 2:
+ base = std::make_unique<CowWriterV2>(options, std::move(fd));
+ break;
+ default:
+ LOG(ERROR) << "Cannot create unknown cow version: " << version;
+ return nullptr;
+ }
+ if (!base->Initialize(label)) {
+ return nullptr;
+ }
+ return base;
+}
+
+std::unique_ptr<ICowWriter> CreateCowEstimator(uint32_t version, const CowOptions& options) {
+ return CreateCowWriter(version, options, unique_fd{-1}, std::nullopt);
+}
+
} // namespace snapshot
} // namespace android
diff --git a/fs_mgr/libsnapshot/libsnapshot_cow/cow_reader.cpp b/fs_mgr/libsnapshot/libsnapshot_cow/cow_reader.cpp
index 45be191..f37aed1 100644
--- a/fs_mgr/libsnapshot/libsnapshot_cow/cow_reader.cpp
+++ b/fs_mgr/libsnapshot/libsnapshot_cow/cow_reader.cpp
@@ -30,6 +30,7 @@
#include <zlib.h>
#include "cow_decompress.h"
+#include "parser_v2.h"
namespace android {
namespace snapshot {
@@ -42,15 +43,6 @@
reader_flag_(reader_flag),
is_merge_(is_merge) {}
-static void SHA256(const void*, size_t, uint8_t[]) {
-#if 0
- SHA256_CTX c;
- SHA256_Init(&c);
- SHA256_Update(&c, data, length);
- SHA256_Final(out, &c);
-#endif
-}
-
std::unique_ptr<CowReader> CowReader::CloneCowReader() {
auto cow = std::make_unique<CowReader>();
cow->owned_fd_.reset();
@@ -62,7 +54,6 @@
cow->merge_op_start_ = merge_op_start_;
cow->num_total_data_ops_ = num_total_data_ops_;
cow->num_ordered_ops_to_merge_ = num_ordered_ops_to_merge_;
- cow->has_seq_ops_ = has_seq_ops_;
cow->data_loc_ = data_loc_;
cow->block_pos_index_ = block_pos_index_;
cow->is_merge_ = is_merge_;
@@ -100,217 +91,26 @@
bool CowReader::Parse(android::base::borrowed_fd fd, std::optional<uint64_t> label) {
fd_ = fd;
- auto pos = lseek(fd_.get(), 0, SEEK_END);
- if (pos < 0) {
- PLOG(ERROR) << "lseek end failed";
- return false;
- }
- fd_size_ = pos;
-
- if (lseek(fd_.get(), 0, SEEK_SET) < 0) {
- PLOG(ERROR) << "lseek header failed";
- return false;
- }
- if (!android::base::ReadFully(fd_, &header_, sizeof(header_))) {
- PLOG(ERROR) << "read header failed";
+ if (!ReadCowHeader(fd, &header_)) {
return false;
}
- if (header_.magic != kCowMagicNumber) {
- LOG(ERROR) << "Header Magic corrupted. Magic: " << header_.magic
- << "Expected: " << kCowMagicNumber;
- return false;
- }
- if (header_.footer_size != sizeof(CowFooter)) {
- LOG(ERROR) << "Footer size unknown, read " << header_.footer_size << ", expected "
- << sizeof(CowFooter);
- return false;
- }
- if (header_.op_size != sizeof(CowOperation)) {
- LOG(ERROR) << "Operation size unknown, read " << header_.op_size << ", expected "
- << sizeof(CowOperation);
- return false;
- }
- if (header_.cluster_ops == 1) {
- LOG(ERROR) << "Clusters must contain at least two operations to function.";
- return false;
- }
- if (header_.op_size != sizeof(CowOperation)) {
- LOG(ERROR) << "Operation size unknown, read " << header_.op_size << ", expected "
- << sizeof(CowOperation);
- return false;
- }
- if (header_.cluster_ops == 1) {
- LOG(ERROR) << "Clusters must contain at least two operations to function.";
+ CowParserV2 parser;
+ if (!parser.Parse(fd, header_, label)) {
return false;
}
- if ((header_.major_version > kCowVersionMajor) || (header_.minor_version != kCowVersionMinor)) {
- LOG(ERROR) << "Header version mismatch";
- LOG(ERROR) << "Major version: " << header_.major_version
- << "Expected: " << kCowVersionMajor;
- LOG(ERROR) << "Minor version: " << header_.minor_version
- << "Expected: " << kCowVersionMinor;
- return false;
- }
+ footer_ = parser.footer();
+ fd_size_ = parser.fd_size();
+ last_label_ = parser.last_label();
+ ops_ = std::move(parser.ops());
+ data_loc_ = parser.data_loc();
- if (!ParseOps(label)) {
- return false;
- }
// If we're resuming a write, we're not ready to merge
if (label.has_value()) return true;
return PrepMergeOps();
}
-bool CowReader::ParseOps(std::optional<uint64_t> label) {
- uint64_t pos;
- auto data_loc = std::make_shared<std::unordered_map<uint64_t, uint64_t>>();
-
- // Skip the scratch space
- if (header_.major_version >= 2 && (header_.buffer_size > 0)) {
- LOG(DEBUG) << " Scratch space found of size: " << header_.buffer_size;
- size_t init_offset = header_.header_size + header_.buffer_size;
- pos = lseek(fd_.get(), init_offset, SEEK_SET);
- if (pos != init_offset) {
- PLOG(ERROR) << "lseek ops failed";
- return false;
- }
- } else {
- pos = lseek(fd_.get(), header_.header_size, SEEK_SET);
- if (pos != header_.header_size) {
- PLOG(ERROR) << "lseek ops failed";
- return false;
- }
- // Reading a v1 version of COW which doesn't have buffer_size.
- header_.buffer_size = 0;
- }
- uint64_t data_pos = 0;
-
- if (header_.cluster_ops) {
- data_pos = pos + header_.cluster_ops * sizeof(CowOperation);
- } else {
- data_pos = pos + sizeof(CowOperation);
- }
-
- auto ops_buffer = std::make_shared<std::vector<CowOperation>>();
- uint64_t current_op_num = 0;
- uint64_t cluster_ops = header_.cluster_ops ?: 1;
- bool done = false;
-
- // Alternating op clusters and data
- while (!done) {
- uint64_t to_add = std::min(cluster_ops, (fd_size_ - pos) / sizeof(CowOperation));
- if (to_add == 0) break;
- ops_buffer->resize(current_op_num + to_add);
- if (!android::base::ReadFully(fd_, &ops_buffer->data()[current_op_num],
- to_add * sizeof(CowOperation))) {
- PLOG(ERROR) << "read op failed";
- return false;
- }
- // Parse current cluster to find start of next cluster
- while (current_op_num < ops_buffer->size()) {
- auto& current_op = ops_buffer->data()[current_op_num];
- current_op_num++;
- if (current_op.type == kCowXorOp) {
- data_loc->insert({current_op.new_block, data_pos});
- }
- pos += sizeof(CowOperation) + GetNextOpOffset(current_op, header_.cluster_ops);
- data_pos += current_op.data_length + GetNextDataOffset(current_op, header_.cluster_ops);
-
- if (current_op.type == kCowClusterOp) {
- break;
- } else if (current_op.type == kCowLabelOp) {
- last_label_ = {current_op.source};
-
- // If we reach the requested label, stop reading.
- if (label && label.value() == current_op.source) {
- done = true;
- break;
- }
- } else if (current_op.type == kCowFooterOp) {
- footer_.emplace();
- CowFooter* footer = &footer_.value();
- memcpy(&footer_->op, ¤t_op, sizeof(footer->op));
- off_t offs = lseek(fd_.get(), pos, SEEK_SET);
- if (offs < 0 || pos != static_cast<uint64_t>(offs)) {
- PLOG(ERROR) << "lseek next op failed " << offs;
- return false;
- }
- if (!android::base::ReadFully(fd_, &footer->data, sizeof(footer->data))) {
- LOG(ERROR) << "Could not read COW footer";
- return false;
- }
-
- // Drop the footer from the op stream.
- current_op_num--;
- done = true;
- break;
- } else if (current_op.type == kCowSequenceOp) {
- has_seq_ops_ = true;
- }
- }
-
- // Position for next cluster read
- off_t offs = lseek(fd_.get(), pos, SEEK_SET);
- if (offs < 0 || pos != static_cast<uint64_t>(offs)) {
- PLOG(ERROR) << "lseek next op failed " << offs;
- return false;
- }
- ops_buffer->resize(current_op_num);
- }
-
- LOG(DEBUG) << "COW file read complete. Total ops: " << ops_buffer->size();
- // To successfully parse a COW file, we need either:
- // (1) a label to read up to, and for that label to be found, or
- // (2) a valid footer.
- if (label) {
- if (!last_label_) {
- LOG(ERROR) << "Did not find label " << label.value()
- << " while reading COW (no labels found)";
- return false;
- }
- if (last_label_.value() != label.value()) {
- LOG(ERROR) << "Did not find label " << label.value()
- << ", last label=" << last_label_.value();
- return false;
- }
- } else if (!footer_) {
- LOG(ERROR) << "No COW footer found";
- return false;
- }
-
- uint8_t csum[32];
- memset(csum, 0, sizeof(uint8_t) * 32);
-
- if (footer_) {
- if (ops_buffer->size() != footer_->op.num_ops) {
- LOG(ERROR) << "num ops does not match, expected " << footer_->op.num_ops << ", found "
- << ops_buffer->size();
- return false;
- }
- if (ops_buffer->size() * sizeof(CowOperation) != footer_->op.ops_size) {
- LOG(ERROR) << "ops size does not match ";
- return false;
- }
- SHA256(&footer_->op, sizeof(footer_->op), footer_->data.footer_checksum);
- if (memcmp(csum, footer_->data.ops_checksum, sizeof(csum)) != 0) {
- LOG(ERROR) << "ops checksum does not match";
- return false;
- }
- SHA256(ops_buffer->data(), footer_->op.ops_size, csum);
- if (memcmp(csum, footer_->data.ops_checksum, sizeof(csum)) != 0) {
- LOG(ERROR) << "ops checksum does not match";
- return false;
- }
- }
-
- ops_ = ops_buffer;
- ops_->shrink_to_fit();
- data_loc_ = data_loc;
-
- return true;
-}
-
//
// This sets up the data needed for MergeOpIter. MergeOpIter presents
// data in the order we intend to merge in.
@@ -430,7 +230,7 @@
size_t seq_len = current_op.data_length / sizeof(uint32_t);
merge_op_blocks->resize(merge_op_blocks->size() + seq_len);
- if (!GetRawBytes(current_op.source, &merge_op_blocks->data()[num_seqs],
+ if (!GetRawBytes(¤t_op, &merge_op_blocks->data()[num_seqs],
current_op.data_length, &read)) {
PLOG(ERROR) << "Failed to read sequence op!";
return false;
@@ -445,7 +245,8 @@
continue;
}
- if (!has_seq_ops_ && IsOrderedOp(current_op)) {
+ // Sequence ops must be the first ops in the stream.
+ if (seq_ops_set.empty() && IsOrderedOp(current_op)) {
merge_op_blocks->emplace_back(current_op.new_block);
} else if (seq_ops_set.count(current_op.new_block) == 0) {
other_ops.push_back(current_op.new_block);
@@ -508,47 +309,63 @@
bool CowReader::VerifyMergeOps() {
auto itr = GetMergeOpIter(true);
- std::unordered_map<uint64_t, CowOperation> overwritten_blocks;
- while (!itr->Done()) {
- CowOperation op = itr->Get();
- uint64_t block;
- bool offset;
- if (op.type == kCowCopyOp) {
- block = op.source;
- offset = false;
- } else if (op.type == kCowXorOp) {
- block = op.source / BLOCK_SZ;
- offset = (op.source % BLOCK_SZ) != 0;
- } else {
+ std::unordered_map<uint64_t, const CowOperation*> overwritten_blocks;
+ bool non_ordered_op_found = false;
+
+ while (!itr->AtEnd()) {
+ const auto& op = itr->Get();
+ uint64_t offset;
+
+ // Op should not be a metadata
+ if (IsMetadataOp(*op)) {
+ LOG(ERROR) << "Metadata op: " << op << " found during merge sequence";
+ return false;
+ }
+
+ // Sequence ops should contain all the ordered ops followed
+ // by Replace and Zero ops. If we find the first op which
+ // is not ordered, that means all ordered ops processing
+ // has been completed.
+ if (!IsOrderedOp(*op)) {
+ non_ordered_op_found = true;
+ }
+
+ // Since, all ordered ops processing has been completed,
+ // check that the subsequent ops are not ordered.
+ if (non_ordered_op_found && IsOrderedOp(*op)) {
+ LOG(ERROR) << "Invalid sequence - non-ordered and ordered ops"
+ << " cannot be mixed during sequence generation";
+ return false;
+ }
+
+ if (!GetSourceOffset(op, &offset)) {
itr->Next();
continue;
}
- CowOperation* overwrite = nullptr;
+ uint64_t block = GetBlockFromOffset(header_, offset);
+ bool misaligned = (GetBlockRelativeOffset(header_, offset) != 0);
+
+ const CowOperation* overwrite = nullptr;
if (overwritten_blocks.count(block)) {
- overwrite = &overwritten_blocks[block];
+ overwrite = overwritten_blocks[block];
LOG(ERROR) << "Invalid Sequence! Block needed for op:\n"
<< op << "\noverwritten by previously merged op:\n"
<< *overwrite;
}
- if (offset && overwritten_blocks.count(block + 1)) {
- overwrite = &overwritten_blocks[block + 1];
+ if (misaligned && overwritten_blocks.count(block + 1)) {
+ overwrite = overwritten_blocks[block + 1];
LOG(ERROR) << "Invalid Sequence! Block needed for op:\n"
<< op << "\noverwritten by previously merged op:\n"
<< *overwrite;
}
if (overwrite != nullptr) return false;
- overwritten_blocks[op.new_block] = op;
+ overwritten_blocks[op->new_block] = op;
itr->Next();
}
return true;
}
-bool CowReader::GetHeader(CowHeader* header) {
- *header = header_;
- return true;
-}
-
bool CowReader::GetFooter(CowFooter* footer) {
if (!footer_) return false;
*footer = footer_.value();
@@ -565,12 +382,12 @@
public:
CowOpIter(std::shared_ptr<std::vector<CowOperation>>& ops, uint64_t start);
- bool Done() override;
- const CowOperation& Get() override;
+ bool AtEnd() override;
+ const CowOperation* Get() override;
void Next() override;
void Prev() override;
- bool RDone() override;
+ bool AtBegin() override;
private:
std::shared_ptr<std::vector<CowOperation>> ops_;
@@ -582,27 +399,27 @@
op_iter_ = ops_->begin() + start;
}
-bool CowOpIter::RDone() {
+bool CowOpIter::AtBegin() {
return op_iter_ == ops_->begin();
}
void CowOpIter::Prev() {
- CHECK(!RDone());
+ CHECK(!AtBegin());
op_iter_--;
}
-bool CowOpIter::Done() {
+bool CowOpIter::AtEnd() {
return op_iter_ == ops_->end();
}
void CowOpIter::Next() {
- CHECK(!Done());
+ CHECK(!AtEnd());
op_iter_++;
}
-const CowOperation& CowOpIter::Get() {
- CHECK(!Done());
- return (*op_iter_);
+const CowOperation* CowOpIter::Get() {
+ CHECK(!AtEnd());
+ return &(*op_iter_);
}
class CowRevMergeOpIter final : public ICowOpIter {
@@ -610,12 +427,12 @@
explicit CowRevMergeOpIter(std::shared_ptr<std::vector<CowOperation>> ops,
std::shared_ptr<std::vector<int>> block_pos_index, uint64_t start);
- bool Done() override;
- const CowOperation& Get() override;
+ bool AtEnd() override;
+ const CowOperation* Get() override;
void Next() override;
void Prev() override;
- bool RDone() override;
+ bool AtBegin() override;
private:
std::shared_ptr<std::vector<CowOperation>> ops_;
@@ -629,12 +446,12 @@
explicit CowMergeOpIter(std::shared_ptr<std::vector<CowOperation>> ops,
std::shared_ptr<std::vector<int>> block_pos_index, uint64_t start);
- bool Done() override;
- const CowOperation& Get() override;
+ bool AtEnd() override;
+ const CowOperation* Get() override;
void Next() override;
void Prev() override;
- bool RDone() override;
+ bool AtBegin() override;
private:
std::shared_ptr<std::vector<CowOperation>> ops_;
@@ -651,27 +468,27 @@
block_iter_ = cow_op_index_vec_->begin() + start;
}
-bool CowMergeOpIter::RDone() {
+bool CowMergeOpIter::AtBegin() {
return block_iter_ == cow_op_index_vec_->begin();
}
void CowMergeOpIter::Prev() {
- CHECK(!RDone());
+ CHECK(!AtBegin());
block_iter_--;
}
-bool CowMergeOpIter::Done() {
+bool CowMergeOpIter::AtEnd() {
return block_iter_ == cow_op_index_vec_->end();
}
void CowMergeOpIter::Next() {
- CHECK(!Done());
+ CHECK(!AtEnd());
block_iter_++;
}
-const CowOperation& CowMergeOpIter::Get() {
- CHECK(!Done());
- return ops_->data()[*block_iter_];
+const CowOperation* CowMergeOpIter::Get() {
+ CHECK(!AtEnd());
+ return &ops_->data()[*block_iter_];
}
CowRevMergeOpIter::CowRevMergeOpIter(std::shared_ptr<std::vector<CowOperation>> ops,
@@ -683,27 +500,27 @@
block_riter_ = cow_op_index_vec_->rbegin();
}
-bool CowRevMergeOpIter::RDone() {
+bool CowRevMergeOpIter::AtBegin() {
return block_riter_ == cow_op_index_vec_->rbegin();
}
void CowRevMergeOpIter::Prev() {
- CHECK(!RDone());
+ CHECK(!AtBegin());
block_riter_--;
}
-bool CowRevMergeOpIter::Done() {
+bool CowRevMergeOpIter::AtEnd() {
return block_riter_ == cow_op_index_vec_->rend() - start_;
}
void CowRevMergeOpIter::Next() {
- CHECK(!Done());
+ CHECK(!AtEnd());
block_riter_++;
}
-const CowOperation& CowRevMergeOpIter::Get() {
- CHECK(!Done());
- return ops_->data()[*block_riter_];
+const CowOperation* CowRevMergeOpIter::Get() {
+ CHECK(!AtEnd());
+ return &ops_->data()[*block_riter_];
}
std::unique_ptr<ICowOpIter> CowReader::GetOpIter(bool merge_progress) {
@@ -720,10 +537,22 @@
ignore_progress ? 0 : merge_op_start_);
}
+bool CowReader::GetRawBytes(const CowOperation* op, void* buffer, size_t len, size_t* read) {
+ switch (op->type) {
+ case kCowSequenceOp:
+ case kCowReplaceOp:
+ case kCowXorOp:
+ return GetRawBytes(GetCowOpSourceInfoData(op), buffer, len, read);
+ default:
+ LOG(ERROR) << "Cannot get raw bytes of non-data op: " << *op;
+ return false;
+ }
+}
+
bool CowReader::GetRawBytes(uint64_t offset, void* buffer, size_t len, size_t* read) {
// Validate the offset, taking care to acknowledge possible overflow of offset+len.
- if (offset < header_.header_size || offset >= fd_size_ - sizeof(CowFooter) || len >= fd_size_ ||
- offset + len > fd_size_ - sizeof(CowFooter)) {
+ if (offset < header_.prefix.header_size || offset >= fd_size_ - sizeof(CowFooter) ||
+ len >= fd_size_ || offset + len > fd_size_ - sizeof(CowFooter)) {
LOG(ERROR) << "invalid data offset: " << offset << ", " << len << " bytes";
return false;
}
@@ -747,18 +576,18 @@
remaining_ = data_length_;
}
- bool Read(void* buffer, size_t length, size_t* read) override {
+ ssize_t Read(void* buffer, size_t length) override {
size_t to_read = std::min(length, remaining_);
if (!to_read) {
- *read = 0;
- return true;
+ return 0;
}
- if (!reader_->GetRawBytes(offset_, buffer, to_read, read)) {
- return false;
+ size_t read;
+ if (!reader_->GetRawBytes(offset_, buffer, to_read, &read)) {
+ return -1;
}
- offset_ += *read;
- remaining_ -= *read;
- return true;
+ offset_ += read;
+ remaining_ -= read;
+ return read;
}
size_t Size() const override { return data_length_; }
@@ -770,11 +599,15 @@
size_t remaining_;
};
-bool CowReader::ReadData(const CowOperation& op, IByteSink* sink) {
+uint8_t CowReader::GetCompressionType(const CowOperation* op) {
+ return op->compression;
+}
+
+ssize_t CowReader::ReadData(const CowOperation* op, void* buffer, size_t buffer_size,
+ size_t ignore_bytes) {
std::unique_ptr<IDecompressor> decompressor;
- switch (op.compression) {
+ switch (GetCompressionType(op)) {
case kCowCompressNone:
- decompressor = IDecompressor::Uncompressed();
break;
case kCowCompressGz:
decompressor = IDecompressor::Gz();
@@ -782,24 +615,49 @@
case kCowCompressBrotli:
decompressor = IDecompressor::Brotli();
break;
+ case kCowCompressZstd:
+ if (header_.block_size != op->data_length) {
+ decompressor = IDecompressor::Zstd();
+ }
+ break;
case kCowCompressLz4:
- decompressor = IDecompressor::Lz4();
+ if (header_.block_size != op->data_length) {
+ decompressor = IDecompressor::Lz4();
+ }
break;
default:
- LOG(ERROR) << "Unknown compression type: " << op.compression;
- return false;
+ LOG(ERROR) << "Unknown compression type: " << GetCompressionType(op);
+ return -1;
}
uint64_t offset;
- if (op.type == kCowXorOp) {
- offset = data_loc_->at(op.new_block);
+ if (op->type == kCowXorOp) {
+ offset = data_loc_->at(op->new_block);
} else {
- offset = op.source;
+ offset = GetCowOpSourceInfoData(op);
}
- CowDataStream stream(this, offset, op.data_length);
+
+ if (!decompressor) {
+ CowDataStream stream(this, offset + ignore_bytes, op->data_length - ignore_bytes);
+ return stream.ReadFully(buffer, buffer_size);
+ }
+
+ CowDataStream stream(this, offset, op->data_length);
decompressor->set_stream(&stream);
- decompressor->set_sink(sink);
- return decompressor->Decompress(header_.block_size);
+ return decompressor->Decompress(buffer, buffer_size, header_.block_size, ignore_bytes);
+}
+
+bool CowReader::GetSourceOffset(const CowOperation* op, uint64_t* source_offset) {
+ switch (op->type) {
+ case kCowCopyOp:
+ *source_offset = GetCowOpSourceInfoData(op) * header_.block_size;
+ return true;
+ case kCowXorOp:
+ *source_offset = GetCowOpSourceInfoData(op);
+ return true;
+ default:
+ return false;
+ }
}
} // namespace snapshot
diff --git a/fs_mgr/libsnapshot/libsnapshot_cow/inspect_cow.cpp b/fs_mgr/libsnapshot/libsnapshot_cow/inspect_cow.cpp
index 167ff8c..a6dee4f 100644
--- a/fs_mgr/libsnapshot/libsnapshot_cow/inspect_cow.cpp
+++ b/fs_mgr/libsnapshot/libsnapshot_cow/inspect_cow.cpp
@@ -16,6 +16,7 @@
#include <stdio.h>
#include <unistd.h>
+#include <chrono>
#include <iomanip>
#include <iostream>
#include <string>
@@ -23,11 +24,26 @@
#include <android-base/logging.h>
#include <android-base/unique_fd.h>
+#include <gflags/gflags.h>
#include <libsnapshot/cow_reader.h>
+#include "parser_v2.h"
+
+DEFINE_bool(silent, false, "Run silently");
+DEFINE_bool(decompress, false, "Attempt to decompress data ops");
+DEFINE_bool(show_bad_data, false, "If an op fails to decompress, show its daw data");
+DEFINE_bool(show_ops, false, "Print all opcode information");
+DEFINE_string(order, "", "If show_ops is true, change the order (either merge or reverse-merge)");
+DEFINE_bool(show_merged, false,
+ "If show_ops is true, and order is merge or reverse-merge, include merged ops");
+DEFINE_bool(verify_merge_sequence, false, "Verify merge order sequencing");
+DEFINE_bool(show_merge_sequence, false, "Show merge order sequence");
+DEFINE_bool(show_raw_ops, false, "Show raw ops directly from the underlying parser");
namespace android {
namespace snapshot {
+using android::base::borrowed_fd;
+
void MyLogger(android::base::LogId, android::base::LogSeverity severity, const char*, const char*,
unsigned int, const char* message) {
if (severity == android::base::ERROR) {
@@ -37,69 +53,57 @@
}
}
-static void usage(void) {
- LOG(ERROR) << "Usage: inspect_cow [-sd] <COW_FILE>";
- LOG(ERROR) << "\t -s Run Silent";
- LOG(ERROR) << "\t -d Attempt to decompress";
- LOG(ERROR) << "\t -b Show data for failed decompress";
- LOG(ERROR) << "\t -l Show ops";
- LOG(ERROR) << "\t -m Show ops in reverse merge order";
- LOG(ERROR) << "\t -n Show ops in merge order";
- LOG(ERROR) << "\t -a Include merged ops in any merge order listing";
- LOG(ERROR) << "\t -o Shows sequence op block order";
- LOG(ERROR) << "\t -v Verifies merge order has no conflicts\n";
-}
-
-enum OpIter { Normal, RevMerge, Merge };
-
-struct Options {
- bool silent;
- bool decompress;
- bool show_ops;
- bool show_bad;
- bool show_seq;
- bool verify_sequence;
- OpIter iter_type;
- bool include_merged;
-};
-
-// Sink that always appends to the end of a string.
-class StringSink : public IByteSink {
- public:
- void* GetBuffer(size_t requested, size_t* actual) override {
- size_t old_size = stream_.size();
- stream_.resize(old_size + requested, '\0');
- *actual = requested;
- return stream_.data() + old_size;
- }
- bool ReturnData(void*, size_t) override { return true; }
- void Reset() { stream_.clear(); }
-
- std::string& stream() { return stream_; }
-
- private:
- std::string stream_;
-};
-
-static void ShowBad(CowReader& reader, const struct CowOperation& op) {
+static void ShowBad(CowReader& reader, const struct CowOperation* op) {
size_t count;
- auto buffer = std::make_unique<uint8_t[]>(op.data_length);
+ auto buffer = std::make_unique<uint8_t[]>(op->data_length);
- if (!reader.GetRawBytes(op.source, buffer.get(), op.data_length, &count)) {
+ if (!reader.GetRawBytes(op, buffer.get(), op->data_length, &count)) {
std::cerr << "Failed to read at all!\n";
} else {
std::cout << "The Block data is:\n";
- for (int i = 0; i < op.data_length; i++) {
+ for (int i = 0; i < op->data_length; i++) {
std::cout << std::hex << (int)buffer[i];
}
std::cout << std::dec << "\n\n";
- if (op.data_length >= sizeof(CowOperation)) {
+ if (op->data_length >= sizeof(CowOperation)) {
std::cout << "The start, as an op, would be " << *(CowOperation*)buffer.get() << "\n";
}
}
}
-static bool Inspect(const std::string& path, Options opt) {
+static bool ShowRawOpStreamV2(borrowed_fd fd, const CowHeader& header) {
+ CowParserV2 parser;
+ if (!parser.Parse(fd, header)) {
+ LOG(ERROR) << "v2 parser failed";
+ return false;
+ }
+ for (const auto& op : *parser.ops()) {
+ std::cout << op << "\n";
+ if (auto iter = parser.data_loc()->find(op.new_block); iter != parser.data_loc()->end()) {
+ std::cout << " data loc: " << iter->second << "\n";
+ }
+ }
+ return true;
+}
+
+static bool ShowRawOpStream(borrowed_fd fd) {
+ CowHeader header;
+ if (!ReadCowHeader(fd, &header)) {
+ LOG(ERROR) << "parse header failed";
+ return false;
+ }
+
+ switch (header.prefix.major_version) {
+ case 1:
+ case 2:
+ return ShowRawOpStreamV2(fd, header);
+ default:
+ LOG(ERROR) << "unknown COW version: " << header.prefix.major_version;
+ return false;
+ }
+}
+
+static bool Inspect(const std::string& path) {
android::base::unique_fd fd(open(path.c_str(), O_RDONLY));
if (fd < 0) {
PLOG(ERROR) << "open failed: " << path;
@@ -107,37 +111,41 @@
}
CowReader reader;
+
+ auto start_time = std::chrono::steady_clock::now();
if (!reader.Parse(fd)) {
LOG(ERROR) << "parse failed: " << path;
return false;
}
+ std::chrono::duration<double> parse_time = std::chrono::steady_clock::now() - start_time;
- CowHeader header;
- if (!reader.GetHeader(&header)) {
- LOG(ERROR) << "could not get header: " << path;
- return false;
- }
+ const CowHeader& header = reader.GetHeader();
CowFooter footer;
bool has_footer = false;
if (reader.GetFooter(&footer)) has_footer = true;
- if (!opt.silent) {
- std::cout << "Major version: " << header.major_version << "\n";
- std::cout << "Minor version: " << header.minor_version << "\n";
- std::cout << "Header size: " << header.header_size << "\n";
+ if (!FLAGS_silent) {
+ std::cout << "Version: " << header.prefix.major_version << "."
+ << header.prefix.minor_version << "\n";
+ std::cout << "Header size: " << header.prefix.header_size << "\n";
std::cout << "Footer size: " << header.footer_size << "\n";
std::cout << "Block size: " << header.block_size << "\n";
- std::cout << "Num merge ops: " << header.num_merge_ops << "\n";
- std::cout << "RA buffer size: " << header.buffer_size << "\n";
- std::cout << "\n";
+ std::cout << "Merge ops: " << header.num_merge_ops << "\n";
+ std::cout << "Readahead buffer: " << header.buffer_size << " bytes\n";
if (has_footer) {
- std::cout << "Total Ops size: " << footer.op.ops_size << "\n";
- std::cout << "Number of Ops: " << footer.op.num_ops << "\n";
- std::cout << "\n";
+ std::cout << "Footer: ops usage: " << footer.op.ops_size << " bytes\n";
+ std::cout << "Footer: op count: " << footer.op.num_ops << "\n";
+ } else {
+ std::cout << "Footer: none\n";
}
}
- if (opt.verify_sequence) {
+ if (!FLAGS_silent) {
+ std::cout << "Parse time: " << (parse_time.count() * 1000) << "ms\n";
+ }
+
+ if (FLAGS_verify_merge_sequence) {
+ std::cout << "\n";
if (reader.VerifyMergeOps()) {
std::cout << "\nMerge sequence is consistent.\n";
} else {
@@ -146,41 +154,57 @@
}
std::unique_ptr<ICowOpIter> iter;
- if (opt.iter_type == Normal) {
+ if (FLAGS_order.empty()) {
iter = reader.GetOpIter();
- } else if (opt.iter_type == RevMerge) {
- iter = reader.GetRevMergeOpIter(opt.include_merged);
- } else if (opt.iter_type == Merge) {
- iter = reader.GetMergeOpIter(opt.include_merged);
+ } else if (FLAGS_order == "reverse-merge") {
+ iter = reader.GetRevMergeOpIter(FLAGS_show_merged);
+ } else if (FLAGS_order == "merge") {
+ iter = reader.GetMergeOpIter(FLAGS_show_merged);
}
- StringSink sink;
+
+ std::string buffer(header.block_size, '\0');
+
+ if (!FLAGS_silent && FLAGS_show_raw_ops) {
+ std::cout << "\n";
+ std::cout << "Listing raw op stream:\n";
+ std::cout << "----------------------\n";
+ if (!ShowRawOpStream(fd)) {
+ return false;
+ }
+ }
+
+ if (!FLAGS_silent && FLAGS_show_ops) {
+ std::cout << "\n";
+ std::cout << "Listing op stream:\n";
+ std::cout << "------------------\n";
+ }
+
bool success = true;
uint64_t xor_ops = 0, copy_ops = 0, replace_ops = 0, zero_ops = 0;
- while (!iter->Done()) {
- const CowOperation& op = iter->Get();
+ while (!iter->AtEnd()) {
+ const CowOperation* op = iter->Get();
- if (!opt.silent && opt.show_ops) std::cout << op << "\n";
+ if (!FLAGS_silent && FLAGS_show_ops) std::cout << *op << "\n";
- if (opt.decompress && op.type == kCowReplaceOp && op.compression != kCowCompressNone) {
- if (!reader.ReadData(op, &sink)) {
- std::cerr << "Failed to decompress for :" << op << "\n";
+ if (FLAGS_decompress && op->type == kCowReplaceOp && op->compression != kCowCompressNone) {
+ if (reader.ReadData(op, buffer.data(), buffer.size()) < 0) {
+ std::cerr << "Failed to decompress for :" << *op << "\n";
success = false;
- if (opt.show_bad) ShowBad(reader, op);
+ if (FLAGS_show_bad_data) ShowBad(reader, op);
}
- sink.Reset();
}
- if (op.type == kCowSequenceOp && opt.show_seq) {
+ if (op->type == kCowSequenceOp && FLAGS_show_merge_sequence) {
size_t read;
std::vector<uint32_t> merge_op_blocks;
- size_t seq_len = op.data_length / sizeof(uint32_t);
+ size_t seq_len = op->data_length / sizeof(uint32_t);
merge_op_blocks.resize(seq_len);
- if (!reader.GetRawBytes(op.source, merge_op_blocks.data(), op.data_length, &read)) {
+ if (!reader.GetRawBytes(op, merge_op_blocks.data(), op->data_length, &read)) {
PLOG(ERROR) << "Failed to read sequence op!";
return false;
}
- if (!opt.silent) {
- std::cout << "Sequence for " << op << " is :\n";
+ if (!FLAGS_silent) {
+ std::cout << "Sequence for " << *op << " is :\n";
for (size_t i = 0; i < seq_len; i++) {
std::cout << std::setfill('0') << std::setw(6) << merge_op_blocks[i] << ", ";
if ((i + 1) % 10 == 0 || i + 1 == seq_len) std::cout << "\n";
@@ -188,24 +212,26 @@
}
}
- if (op.type == kCowCopyOp) {
+ if (op->type == kCowCopyOp) {
copy_ops++;
- } else if (op.type == kCowReplaceOp) {
+ } else if (op->type == kCowReplaceOp) {
replace_ops++;
- } else if (op.type == kCowZeroOp) {
+ } else if (op->type == kCowZeroOp) {
zero_ops++;
- } else if (op.type == kCowXorOp) {
+ } else if (op->type == kCowXorOp) {
xor_ops++;
}
iter->Next();
}
- if (!opt.silent) {
+ if (!FLAGS_silent) {
auto total_ops = replace_ops + zero_ops + copy_ops + xor_ops;
- std::cout << "Total-data-ops: " << total_ops << "Replace-ops: " << replace_ops
- << " Zero-ops: " << zero_ops << " Copy-ops: " << copy_ops
- << " Xor_ops: " << xor_ops << std::endl;
+ std::cout << "Data ops: " << total_ops << "\n";
+ std::cout << "Replace ops: " << replace_ops << "\n";
+ std::cout << "Zero ops: " << zero_ops << "\n";
+ std::cout << "Copy ops: " << copy_ops << "\n";
+ std::cout << "Xor ops: " << xor_ops << "\n";
}
return success;
@@ -215,55 +241,20 @@
} // namespace android
int main(int argc, char** argv) {
- int ch;
- struct android::snapshot::Options opt;
- opt.silent = false;
- opt.decompress = false;
- opt.show_bad = false;
- opt.iter_type = android::snapshot::Normal;
- opt.verify_sequence = false;
- opt.include_merged = false;
- while ((ch = getopt(argc, argv, "sdbmnolva")) != -1) {
- switch (ch) {
- case 's':
- opt.silent = true;
- break;
- case 'd':
- opt.decompress = true;
- break;
- case 'b':
- opt.show_bad = true;
- break;
- case 'm':
- opt.iter_type = android::snapshot::RevMerge;
- break;
- case 'n':
- opt.iter_type = android::snapshot::Merge;
- break;
- case 'o':
- opt.show_seq = true;
- break;
- case 'l':
- opt.show_ops = true;
- break;
- case 'v':
- opt.verify_sequence = true;
- break;
- case 'a':
- opt.include_merged = true;
- break;
- default:
- android::snapshot::usage();
- }
- }
- android::base::InitLogging(argv, android::snapshot::MyLogger);
+ gflags::ParseCommandLineFlags(&argc, &argv, true);
- if (argc < optind + 1) {
- android::snapshot::usage();
+ if (argc < 2) {
+ gflags::ShowUsageWithFlags(argv[0]);
+ return 1;
+ }
+ if (FLAGS_order != "" && FLAGS_order != "merge" && FLAGS_order != "reverse-merge") {
+ std::cerr << "Order must either be \"merge\" or \"reverse-merge\".\n";
return 1;
}
- if (!android::snapshot::Inspect(argv[optind], opt)) {
+ android::base::InitLogging(argv, android::snapshot::MyLogger);
+
+ if (!android::snapshot::Inspect(argv[1])) {
return 1;
}
return 0;
diff --git a/fs_mgr/libsnapshot/libsnapshot_cow/parser_v2.cpp b/fs_mgr/libsnapshot/libsnapshot_cow/parser_v2.cpp
new file mode 100644
index 0000000..fdb5c59
--- /dev/null
+++ b/fs_mgr/libsnapshot/libsnapshot_cow/parser_v2.cpp
@@ -0,0 +1,238 @@
+// Copyright (C) 2023 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 "parser_v2.h"
+
+#include <unistd.h>
+
+#include <android-base/file.h>
+#include <android-base/logging.h>
+
+namespace android {
+namespace snapshot {
+
+using android::base::borrowed_fd;
+
+bool ReadCowHeader(android::base::borrowed_fd fd, CowHeader* header) {
+ if (lseek(fd.get(), 0, SEEK_SET) < 0) {
+ PLOG(ERROR) << "lseek header failed";
+ return false;
+ }
+
+ memset(header, 0, sizeof(*header));
+
+ if (!android::base::ReadFully(fd, &header->prefix, sizeof(header->prefix))) {
+ return false;
+ }
+ if (header->prefix.magic != kCowMagicNumber) {
+ LOG(ERROR) << "Header Magic corrupted. Magic: " << header->prefix.magic
+ << "Expected: " << kCowMagicNumber;
+ return false;
+ }
+ if (header->prefix.header_size > sizeof(CowHeader)) {
+ LOG(ERROR) << "Unknown CowHeader size (got " << header->prefix.header_size
+ << " bytes, expected at most " << sizeof(CowHeader) << " bytes)";
+ return false;
+ }
+
+ if (lseek(fd.get(), 0, SEEK_SET) < 0) {
+ PLOG(ERROR) << "lseek header failed";
+ return false;
+ }
+ return android::base::ReadFully(fd, header, header->prefix.header_size);
+}
+
+bool CowParserV2::Parse(borrowed_fd fd, const CowHeader& header, std::optional<uint64_t> label) {
+ auto pos = lseek(fd.get(), 0, SEEK_END);
+ if (pos < 0) {
+ PLOG(ERROR) << "lseek end failed";
+ return false;
+ }
+ fd_size_ = pos;
+ header_ = header;
+
+ if (header_.footer_size != sizeof(CowFooter)) {
+ LOG(ERROR) << "Footer size unknown, read " << header_.footer_size << ", expected "
+ << sizeof(CowFooter);
+ return false;
+ }
+ if (header_.op_size != sizeof(CowOperation)) {
+ LOG(ERROR) << "Operation size unknown, read " << header_.op_size << ", expected "
+ << sizeof(CowOperation);
+ return false;
+ }
+ if (header_.cluster_ops == 1) {
+ LOG(ERROR) << "Clusters must contain at least two operations to function.";
+ return false;
+ }
+ if (header_.op_size != sizeof(CowOperation)) {
+ LOG(ERROR) << "Operation size unknown, read " << header_.op_size << ", expected "
+ << sizeof(CowOperation);
+ return false;
+ }
+ if (header_.cluster_ops == 1) {
+ LOG(ERROR) << "Clusters must contain at least two operations to function.";
+ return false;
+ }
+
+ if ((header_.prefix.major_version > kCowVersionMajor) ||
+ (header_.prefix.minor_version != kCowVersionMinor)) {
+ LOG(ERROR) << "Header version mismatch, "
+ << "major version: " << header_.prefix.major_version
+ << ", expected: " << kCowVersionMajor
+ << ", minor version: " << header_.prefix.minor_version
+ << ", expected: " << kCowVersionMinor;
+ return false;
+ }
+
+ return ParseOps(fd, label);
+}
+
+bool CowParserV2::ParseOps(borrowed_fd fd, std::optional<uint64_t> label) {
+ uint64_t pos;
+ auto data_loc = std::make_shared<std::unordered_map<uint64_t, uint64_t>>();
+
+ // Skip the scratch space
+ if (header_.prefix.major_version >= 2 && (header_.buffer_size > 0)) {
+ LOG(DEBUG) << " Scratch space found of size: " << header_.buffer_size;
+ size_t init_offset = header_.prefix.header_size + header_.buffer_size;
+ pos = lseek(fd.get(), init_offset, SEEK_SET);
+ if (pos != init_offset) {
+ PLOG(ERROR) << "lseek ops failed";
+ return false;
+ }
+ } else {
+ pos = lseek(fd.get(), header_.prefix.header_size, SEEK_SET);
+ if (pos != header_.prefix.header_size) {
+ PLOG(ERROR) << "lseek ops failed";
+ return false;
+ }
+ // Reading a v1 version of COW which doesn't have buffer_size.
+ header_.buffer_size = 0;
+ }
+ uint64_t data_pos = 0;
+
+ if (header_.cluster_ops) {
+ data_pos = pos + header_.cluster_ops * sizeof(CowOperation);
+ } else {
+ data_pos = pos + sizeof(CowOperation);
+ }
+
+ auto ops_buffer = std::make_shared<std::vector<CowOperation>>();
+ uint64_t current_op_num = 0;
+ uint64_t cluster_ops = header_.cluster_ops ?: 1;
+ bool done = false;
+
+ // Alternating op clusters and data
+ while (!done) {
+ uint64_t to_add = std::min(cluster_ops, (fd_size_ - pos) / sizeof(CowOperation));
+ if (to_add == 0) break;
+ ops_buffer->resize(current_op_num + to_add);
+ if (!android::base::ReadFully(fd, &ops_buffer->data()[current_op_num],
+ to_add * sizeof(CowOperation))) {
+ PLOG(ERROR) << "read op failed";
+ return false;
+ }
+ // Parse current cluster to find start of next cluster
+ while (current_op_num < ops_buffer->size()) {
+ auto& current_op = ops_buffer->data()[current_op_num];
+ current_op_num++;
+ if (current_op.type == kCowXorOp) {
+ data_loc->insert({current_op.new_block, data_pos});
+ }
+ pos += sizeof(CowOperation) + GetNextOpOffset(current_op, header_.cluster_ops);
+ data_pos += current_op.data_length + GetNextDataOffset(current_op, header_.cluster_ops);
+
+ if (current_op.type == kCowClusterOp) {
+ break;
+ } else if (current_op.type == kCowLabelOp) {
+ last_label_ = {current_op.source};
+
+ // If we reach the requested label, stop reading.
+ if (label && label.value() == current_op.source) {
+ done = true;
+ break;
+ }
+ } else if (current_op.type == kCowFooterOp) {
+ footer_.emplace();
+ CowFooter* footer = &footer_.value();
+ memcpy(&footer_->op, ¤t_op, sizeof(footer->op));
+ off_t offs = lseek(fd.get(), pos, SEEK_SET);
+ if (offs < 0 || pos != static_cast<uint64_t>(offs)) {
+ PLOG(ERROR) << "lseek next op failed " << offs;
+ return false;
+ }
+ if (!android::base::ReadFully(fd, &footer->unused, sizeof(footer->unused))) {
+ LOG(ERROR) << "Could not read COW footer";
+ return false;
+ }
+
+ // Drop the footer from the op stream.
+ current_op_num--;
+ done = true;
+ break;
+ }
+ }
+
+ // Position for next cluster read
+ off_t offs = lseek(fd.get(), pos, SEEK_SET);
+ if (offs < 0 || pos != static_cast<uint64_t>(offs)) {
+ PLOG(ERROR) << "lseek next op failed " << offs;
+ return false;
+ }
+ ops_buffer->resize(current_op_num);
+ }
+
+ LOG(DEBUG) << "COW file read complete. Total ops: " << ops_buffer->size();
+ // To successfully parse a COW file, we need either:
+ // (1) a label to read up to, and for that label to be found, or
+ // (2) a valid footer.
+ if (label) {
+ if (!last_label_) {
+ LOG(ERROR) << "Did not find label " << label.value()
+ << " while reading COW (no labels found)";
+ return false;
+ }
+ if (last_label_.value() != label.value()) {
+ LOG(ERROR) << "Did not find label " << label.value()
+ << ", last label=" << last_label_.value();
+ return false;
+ }
+ } else if (!footer_) {
+ LOG(ERROR) << "No COW footer found";
+ return false;
+ }
+
+ uint8_t csum[32];
+ memset(csum, 0, sizeof(uint8_t) * 32);
+
+ if (footer_) {
+ if (ops_buffer->size() != footer_->op.num_ops) {
+ LOG(ERROR) << "num ops does not match, expected " << footer_->op.num_ops << ", found "
+ << ops_buffer->size();
+ return false;
+ }
+ if (ops_buffer->size() * sizeof(CowOperation) != footer_->op.ops_size) {
+ LOG(ERROR) << "ops size does not match ";
+ return false;
+ }
+ }
+
+ ops_ = ops_buffer;
+ ops_->shrink_to_fit();
+ data_loc_ = data_loc;
+ return true;
+}
+
+} // namespace snapshot
+} // namespace android
diff --git a/fs_mgr/libsnapshot/libsnapshot_cow/parser_v2.h b/fs_mgr/libsnapshot/libsnapshot_cow/parser_v2.h
new file mode 100644
index 0000000..afcf687
--- /dev/null
+++ b/fs_mgr/libsnapshot/libsnapshot_cow/parser_v2.h
@@ -0,0 +1,55 @@
+// Copyright (C) 2023 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 <stdint.h>
+
+#include <memory>
+#include <optional>
+#include <unordered_map>
+#include <vector>
+
+#include <android-base/unique_fd.h>
+#include <libsnapshot/cow_format.h>
+
+namespace android {
+namespace snapshot {
+
+class CowParserV2 {
+ public:
+ bool Parse(android::base::borrowed_fd fd, const CowHeader& header,
+ std::optional<uint64_t> label = {});
+
+ const CowHeader& header() const { return header_; }
+ const std::optional<CowFooter>& footer() const { return footer_; }
+ std::shared_ptr<std::vector<CowOperation>> ops() { return ops_; }
+ std::shared_ptr<std::unordered_map<uint64_t, uint64_t>> data_loc() const { return data_loc_; }
+ uint64_t fd_size() const { return fd_size_; }
+ const std::optional<uint64_t>& last_label() const { return last_label_; }
+
+ private:
+ bool ParseOps(android::base::borrowed_fd fd, std::optional<uint64_t> label);
+
+ CowHeader header_ = {};
+ std::optional<CowFooter> footer_;
+ std::shared_ptr<std::vector<CowOperation>> ops_;
+ std::shared_ptr<std::unordered_map<uint64_t, uint64_t>> data_loc_;
+ uint64_t fd_size_;
+ std::optional<uint64_t> last_label_;
+};
+
+bool ReadCowHeader(android::base::borrowed_fd fd, CowHeader* header);
+
+} // namespace snapshot
+} // namespace android
diff --git a/fs_mgr/libsnapshot/libsnapshot_cow/snapshot_reader.cpp b/fs_mgr/libsnapshot/libsnapshot_cow/snapshot_reader.cpp
new file mode 100644
index 0000000..a3e40d9
--- /dev/null
+++ b/fs_mgr/libsnapshot/libsnapshot_cow/snapshot_reader.cpp
@@ -0,0 +1,262 @@
+//
+// 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 "snapshot_reader.h"
+
+#include <android-base/file.h>
+#include <android-base/logging.h>
+
+namespace android {
+namespace snapshot {
+
+using android::base::borrowed_fd;
+
+CompressedSnapshotReader::CompressedSnapshotReader(std::unique_ptr<ICowReader>&& cow,
+ const std::optional<std::string>& source_device,
+ std::optional<uint64_t> block_dev_size)
+ : cow_(std::move(cow)),
+ block_size_(cow_->GetHeader().block_size),
+ source_device_(source_device),
+ block_device_size_(block_dev_size.value_or(0)) {
+ const auto& header = cow_->GetHeader();
+ block_size_ = header.block_size;
+
+ // Populate the operation map.
+ op_iter_ = cow_->GetOpIter(false);
+ while (!op_iter_->AtEnd()) {
+ const CowOperation* op = op_iter_->Get();
+ if (IsMetadataOp(*op)) {
+ op_iter_->Next();
+ continue;
+ }
+ if (op->new_block >= ops_.size()) {
+ ops_.resize(op->new_block + 1, nullptr);
+ }
+ ops_[op->new_block] = op;
+ op_iter_->Next();
+ }
+}
+
+// Not supported.
+bool CompressedSnapshotReader::Open(const char*, int, mode_t) {
+ errno = EINVAL;
+ return false;
+}
+
+bool CompressedSnapshotReader::Open(const char*, int) {
+ errno = EINVAL;
+ return false;
+}
+
+ssize_t CompressedSnapshotReader::Write(const void*, size_t) {
+ errno = EINVAL;
+ return false;
+}
+
+bool CompressedSnapshotReader::BlkIoctl(int, uint64_t, uint64_t, int*) {
+ errno = EINVAL;
+ return false;
+}
+
+borrowed_fd CompressedSnapshotReader::GetSourceFd() {
+ if (source_fd_ < 0) {
+ if (!source_device_) {
+ LOG(ERROR) << "CompressedSnapshotReader needs source device, but none was set";
+ errno = EINVAL;
+ return {-1};
+ }
+ source_fd_.reset(open(source_device_->c_str(), O_RDONLY | O_CLOEXEC));
+ if (source_fd_ < 0) {
+ PLOG(ERROR) << "open " << *source_device_;
+ return {-1};
+ }
+ }
+ return source_fd_;
+}
+
+ssize_t CompressedSnapshotReader::Read(void* buf, size_t count) {
+ // Find the start and end chunks, inclusive.
+ uint64_t start_chunk = offset_ / block_size_;
+ uint64_t end_chunk = (offset_ + count - 1) / block_size_;
+
+ // Chop off the first N bytes if the position is not block-aligned.
+ size_t start_offset = offset_ % block_size_;
+
+ uint8_t* buf_pos = reinterpret_cast<uint8_t*>(buf);
+ size_t buf_remaining = count;
+
+ size_t initial_bytes = std::min(block_size_ - start_offset, buf_remaining);
+ ssize_t rv = ReadBlock(start_chunk, start_offset, buf_pos, initial_bytes);
+ if (rv < 0) {
+ return -1;
+ }
+ offset_ += rv;
+ buf_pos += rv;
+ buf_remaining -= rv;
+
+ for (uint64_t chunk = start_chunk + 1; chunk < end_chunk; chunk++) {
+ ssize_t rv = ReadBlock(chunk, 0, buf_pos, buf_remaining);
+ if (rv < 0) {
+ return -1;
+ }
+ offset_ += rv;
+ buf_pos += rv;
+ buf_remaining -= rv;
+ }
+
+ if (buf_remaining) {
+ ssize_t rv = ReadBlock(end_chunk, 0, buf_pos, buf_remaining);
+ if (rv < 0) {
+ return -1;
+ }
+ offset_ += rv;
+ buf_pos += rv;
+ buf_remaining -= rv;
+ }
+
+ CHECK_EQ(buf_pos - reinterpret_cast<uint8_t*>(buf), count);
+ CHECK_EQ(buf_remaining, 0);
+
+ errno = 0;
+ return count;
+}
+
+ssize_t CompressedSnapshotReader::ReadBlock(uint64_t chunk, size_t start_offset, void* buffer,
+ size_t buffer_size) {
+ size_t bytes_to_read = std::min(static_cast<size_t>(block_size_), buffer_size);
+
+ // The offset is relative to the chunk; we should be reading no more than
+ // one chunk.
+ CHECK(start_offset + bytes_to_read <= block_size_);
+
+ const CowOperation* op = nullptr;
+ if (chunk < ops_.size()) {
+ op = ops_[chunk];
+ }
+
+ if (!op || op->type == kCowCopyOp) {
+ borrowed_fd fd = GetSourceFd();
+ if (fd < 0) {
+ // GetSourceFd sets errno.
+ return -1;
+ }
+
+ if (op) {
+ uint64_t source_offset;
+ if (!cow_->GetSourceOffset(op, &source_offset)) {
+ LOG(ERROR) << "GetSourceOffset failed in CompressedSnapshotReader for op: " << *op;
+ return false;
+ }
+ chunk = GetBlockFromOffset(cow_->GetHeader(), source_offset);
+ }
+
+ off64_t offset = (chunk * block_size_) + start_offset;
+ if (!android::base::ReadFullyAtOffset(fd, buffer, bytes_to_read, offset)) {
+ PLOG(ERROR) << "read " << *source_device_;
+ // ReadFullyAtOffset sets errno.
+ return -1;
+ }
+ } else if (op->type == kCowZeroOp) {
+ memset(buffer, 0, bytes_to_read);
+ } else if (op->type == kCowReplaceOp) {
+ if (cow_->ReadData(op, buffer, bytes_to_read, start_offset) < bytes_to_read) {
+ LOG(ERROR) << "CompressedSnapshotReader failed to read replace op";
+ errno = EIO;
+ return -1;
+ }
+ } else if (op->type == kCowXorOp) {
+ borrowed_fd fd = GetSourceFd();
+ if (fd < 0) {
+ // GetSourceFd sets errno.
+ return -1;
+ }
+
+ uint64_t source_offset;
+ if (!cow_->GetSourceOffset(op, &source_offset)) {
+ LOG(ERROR) << "GetSourceOffset failed in CompressedSnapshotReader for op: " << *op;
+ return false;
+ }
+ off64_t offset = source_offset + start_offset;
+
+ std::string data(bytes_to_read, '\0');
+ if (!android::base::ReadFullyAtOffset(fd, data.data(), data.size(), offset)) {
+ PLOG(ERROR) << "read " << *source_device_;
+ // ReadFullyAtOffset sets errno.
+ return -1;
+ }
+
+ if (cow_->ReadData(op, buffer, bytes_to_read, start_offset) < bytes_to_read) {
+ LOG(ERROR) << "CompressedSnapshotReader failed to read xor op";
+ errno = EIO;
+ return -1;
+ }
+
+ for (size_t i = 0; i < bytes_to_read; i++) {
+ ((char*)buffer)[i] ^= data[i];
+ }
+ } else {
+ LOG(ERROR) << "CompressedSnapshotReader unknown op type: " << uint32_t(op->type);
+ errno = EINVAL;
+ return -1;
+ }
+
+ // MemoryByteSink doesn't do anything in ReturnBuffer, so don't bother calling it.
+ return bytes_to_read;
+}
+
+off64_t CompressedSnapshotReader::Seek(off64_t offset, int whence) {
+ switch (whence) {
+ case SEEK_SET:
+ offset_ = offset;
+ break;
+ case SEEK_END:
+ offset_ = static_cast<off64_t>(block_device_size_) + offset;
+ break;
+ case SEEK_CUR:
+ offset_ += offset;
+ break;
+ default:
+ LOG(ERROR) << "Unrecognized seek whence: " << whence;
+ errno = EINVAL;
+ return -1;
+ }
+ return offset_;
+}
+
+uint64_t CompressedSnapshotReader::BlockDevSize() {
+ return block_device_size_;
+}
+
+bool CompressedSnapshotReader::Close() {
+ cow_ = nullptr;
+ source_fd_ = {};
+ return true;
+}
+
+bool CompressedSnapshotReader::IsSettingErrno() {
+ return true;
+}
+
+bool CompressedSnapshotReader::IsOpen() {
+ return cow_ != nullptr;
+}
+
+bool CompressedSnapshotReader::Flush() {
+ return true;
+}
+
+} // namespace snapshot
+} // namespace android
diff --git a/fs_mgr/libsnapshot/snapshot_reader.h b/fs_mgr/libsnapshot/libsnapshot_cow/snapshot_reader.h
similarity index 64%
rename from fs_mgr/libsnapshot/snapshot_reader.h
rename to fs_mgr/libsnapshot/libsnapshot_cow/snapshot_reader.h
index a3e7e22..3de63ed 100644
--- a/fs_mgr/libsnapshot/snapshot_reader.h
+++ b/fs_mgr/libsnapshot/libsnapshot_cow/snapshot_reader.h
@@ -26,18 +26,16 @@
namespace android {
namespace snapshot {
-class ReadOnlyFileDescriptor : public chromeos_update_engine::FileDescriptor {
+class CompressedSnapshotReader : public chromeos_update_engine::FileDescriptor {
public:
+ CompressedSnapshotReader(std::unique_ptr<ICowReader>&& cow,
+ const std::optional<std::string>& source_device,
+ std::optional<uint64_t> block_dev_size);
+
bool Open(const char* path, int flags, mode_t mode) override;
bool Open(const char* path, int flags) override;
ssize_t Write(const void* buf, size_t count) override;
bool BlkIoctl(int request, uint64_t start, uint64_t length, int* result) override;
-};
-
-class ReadFdFileDescriptor : public ReadOnlyFileDescriptor {
- public:
- explicit ReadFdFileDescriptor(android::base::unique_fd&& fd);
-
ssize_t Read(void* buf, size_t count) override;
off64_t Seek(off64_t offset, int whence) override;
uint64_t BlockDevSize() override;
@@ -47,29 +45,10 @@
bool Flush() override;
private:
- android::base::unique_fd fd_;
-};
-
-class CompressedSnapshotReader : public ReadOnlyFileDescriptor {
- public:
- bool SetCow(std::unique_ptr<CowReader>&& cow);
- void SetSourceDevice(const std::string& source_device);
- void SetBlockDeviceSize(uint64_t block_device_size);
-
- ssize_t Read(void* buf, size_t count) override;
- off64_t Seek(off64_t offset, int whence) override;
- uint64_t BlockDevSize() override;
- bool Close() override;
- bool IsSettingErrno() override;
- bool IsOpen() override;
- bool Flush() override;
-
- private:
- ssize_t ReadBlock(uint64_t chunk, IByteSink* sink, size_t start_offset,
- const std::optional<uint64_t>& max_bytes = {});
+ ssize_t ReadBlock(uint64_t chunk, size_t start_offset, void* buffer, size_t size);
android::base::borrowed_fd GetSourceFd();
- std::unique_ptr<CowReader> cow_;
+ std::unique_ptr<ICowReader> cow_;
std::unique_ptr<ICowOpIter> op_iter_;
uint32_t block_size_ = 0;
diff --git a/fs_mgr/libsnapshot/snapshot_reader_test.cpp b/fs_mgr/libsnapshot/libsnapshot_cow/snapshot_reader_test.cpp
similarity index 88%
rename from fs_mgr/libsnapshot/snapshot_reader_test.cpp
rename to fs_mgr/libsnapshot/libsnapshot_cow/snapshot_reader_test.cpp
index 9adc655..10cb06d 100644
--- a/fs_mgr/libsnapshot/snapshot_reader_test.cpp
+++ b/fs_mgr/libsnapshot/libsnapshot_cow/snapshot_reader_test.cpp
@@ -12,8 +12,6 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-#include <libsnapshot/snapshot.h>
-
#include <unordered_set>
#include <android-base/file.h>
@@ -61,7 +59,7 @@
ASSERT_EQ(fsync(base_->fd), 0);
}
- void WriteCow(ISnapshotWriter* writer) {
+ void WriteCow(ICowWriter* writer) {
std::string new_block = MakeNewBlockString();
std::string xor_block = MakeXorBlockString();
@@ -72,8 +70,8 @@
ASSERT_TRUE(writer->Finalize());
}
- void TestBlockReads(ISnapshotWriter* writer) {
- auto reader = writer->OpenReader();
+ void TestBlockReads(ICowWriter* writer) {
+ auto reader = writer->OpenFileDescriptor(base_->path);
ASSERT_NE(reader, nullptr);
// Test that unchanged blocks are not modified.
@@ -117,8 +115,8 @@
ASSERT_EQ(two_blocks, zeroes);
}
- void TestByteReads(ISnapshotWriter* writer) {
- auto reader = writer->OpenReader();
+ void TestByteReads(ICowWriter* writer) {
+ auto reader = writer->OpenFileDescriptor(base_->path);
ASSERT_NE(reader, nullptr);
std::string blob(kBlockSize * 3, 'x');
@@ -140,9 +138,21 @@
ASSERT_EQ(reader->Seek(offset, SEEK_SET), offset);
ASSERT_EQ(reader->Read(&value, sizeof(value)), sizeof(value));
ASSERT_EQ(value, MakeNewBlockString()[1000]);
+
+ // Test a sequence of one byte reads.
+ offset = 5 * kBlockSize + 10;
+ std::string expected = MakeNewBlockString().substr(10, 20);
+ ASSERT_EQ(reader->Seek(offset, SEEK_SET), offset);
+
+ std::string got;
+ while (got.size() < expected.size()) {
+ ASSERT_EQ(reader->Read(&value, sizeof(value)), sizeof(value));
+ got.push_back(value);
+ }
+ ASSERT_EQ(got, expected);
}
- void TestReads(ISnapshotWriter* writer) {
+ void TestReads(ICowWriter* writer) {
ASSERT_NO_FATAL_FAILURE(TestBlockReads(writer));
ASSERT_NO_FATAL_FAILURE(TestByteReads(writer));
}
@@ -174,10 +184,7 @@
unique_fd cow_fd(dup(cow_->fd));
ASSERT_GE(cow_fd, 0);
- auto writer = std::make_unique<CompressedSnapshotWriter>(options);
- writer->SetSourceDevice(base_->path);
- ASSERT_TRUE(writer->SetCowDevice(std::move(cow_fd)));
- ASSERT_TRUE(writer->Initialize());
+ auto writer = CreateCowWriter(kDefaultCowVersion, options, std::move(cow_fd));
ASSERT_NO_FATAL_FAILURE(WriteCow(writer.get()));
ASSERT_NO_FATAL_FAILURE(TestReads(writer.get()));
}
diff --git a/fs_mgr/libsnapshot/libsnapshot_cow/cow_api_test.cpp b/fs_mgr/libsnapshot/libsnapshot_cow/test_v2.cpp
similarity index 68%
rename from fs_mgr/libsnapshot/libsnapshot_cow/cow_api_test.cpp
rename to fs_mgr/libsnapshot/libsnapshot_cow/test_v2.cpp
index 862ce55..31b9a58 100644
--- a/fs_mgr/libsnapshot/libsnapshot_cow/cow_api_test.cpp
+++ b/fs_mgr/libsnapshot/libsnapshot_cow/test_v2.cpp
@@ -24,7 +24,10 @@
#include <gtest/gtest.h>
#include <libsnapshot/cow_reader.h>
#include <libsnapshot/cow_writer.h>
+#include "cow_decompress.h"
+#include "writer_v2.h"
+using android::base::unique_fd;
using testing::AssertionFailure;
using testing::AssertionResult;
using testing::AssertionSuccess;
@@ -41,57 +44,47 @@
virtual void TearDown() override { cow_ = nullptr; }
+ unique_fd GetCowFd() { return unique_fd{dup(cow_->fd)}; }
+
std::unique_ptr<TemporaryFile> cow_;
};
-// Sink that always appends to the end of a string.
-class StringSink : public IByteSink {
- public:
- void* GetBuffer(size_t requested, size_t* actual) override {
- size_t old_size = stream_.size();
- stream_.resize(old_size + requested, '\0');
- *actual = requested;
- return stream_.data() + old_size;
- }
- bool ReturnData(void*, size_t) override { return true; }
- void Reset() { stream_.clear(); }
-
- std::string& stream() { return stream_; }
-
- private:
- std::string stream_;
-};
+// Helper to check read sizes.
+static inline bool ReadData(CowReader& reader, const CowOperation* op, void* buffer, size_t size) {
+ return reader.ReadData(op, buffer, size) == size;
+}
TEST_F(CowTest, CopyContiguous) {
CowOptions options;
options.cluster_ops = 0;
- CowWriter writer(options);
+ CowWriterV2 writer(options, GetCowFd());
- ASSERT_TRUE(writer.Initialize(cow_->fd));
+ ASSERT_TRUE(writer.Initialize());
ASSERT_TRUE(writer.AddCopy(10, 1000, 100));
ASSERT_TRUE(writer.Finalize());
ASSERT_EQ(lseek(cow_->fd, 0, SEEK_SET), 0);
CowReader reader;
- CowHeader header;
- CowFooter footer;
ASSERT_TRUE(reader.Parse(cow_->fd));
- ASSERT_TRUE(reader.GetHeader(&header));
- ASSERT_TRUE(reader.GetFooter(&footer));
- ASSERT_EQ(header.magic, kCowMagicNumber);
- ASSERT_EQ(header.major_version, kCowVersionMajor);
- ASSERT_EQ(header.minor_version, kCowVersionMinor);
+
+ const auto& header = reader.GetHeader();
+ ASSERT_EQ(header.prefix.magic, kCowMagicNumber);
+ ASSERT_EQ(header.prefix.major_version, kCowVersionMajor);
+ ASSERT_EQ(header.prefix.minor_version, kCowVersionMinor);
ASSERT_EQ(header.block_size, options.block_size);
+
+ CowFooter footer;
+ ASSERT_TRUE(reader.GetFooter(&footer));
ASSERT_EQ(footer.op.num_ops, 100);
auto iter = reader.GetOpIter();
ASSERT_NE(iter, nullptr);
- ASSERT_FALSE(iter->Done());
+ ASSERT_FALSE(iter->AtEnd());
size_t i = 0;
- while (!iter->Done()) {
- auto op = &iter->Get();
+ while (!iter->AtEnd()) {
+ auto op = iter->Get();
ASSERT_EQ(op->type, kCowCopyOp);
ASSERT_EQ(op->compression, kCowCompressNone);
ASSERT_EQ(op->data_length, 0);
@@ -107,9 +100,9 @@
TEST_F(CowTest, ReadWrite) {
CowOptions options;
options.cluster_ops = 0;
- CowWriter writer(options);
+ CowWriterV2 writer(options, GetCowFd());
- ASSERT_TRUE(writer.Initialize(cow_->fd));
+ ASSERT_TRUE(writer.Initialize());
std::string data = "This is some data, believe it";
data.resize(options.block_size, '\0');
@@ -122,21 +115,22 @@
ASSERT_EQ(lseek(cow_->fd, 0, SEEK_SET), 0);
CowReader reader;
- CowHeader header;
- CowFooter footer;
ASSERT_TRUE(reader.Parse(cow_->fd));
- ASSERT_TRUE(reader.GetHeader(&header));
- ASSERT_TRUE(reader.GetFooter(&footer));
- ASSERT_EQ(header.magic, kCowMagicNumber);
- ASSERT_EQ(header.major_version, kCowVersionMajor);
- ASSERT_EQ(header.minor_version, kCowVersionMinor);
+
+ const auto& header = reader.GetHeader();
+ ASSERT_EQ(header.prefix.magic, kCowMagicNumber);
+ ASSERT_EQ(header.prefix.major_version, kCowVersionMajor);
+ ASSERT_EQ(header.prefix.minor_version, kCowVersionMinor);
ASSERT_EQ(header.block_size, options.block_size);
+
+ CowFooter footer;
+ ASSERT_TRUE(reader.GetFooter(&footer));
ASSERT_EQ(footer.op.num_ops, 4);
auto iter = reader.GetOpIter();
ASSERT_NE(iter, nullptr);
- ASSERT_FALSE(iter->Done());
- auto op = &iter->Get();
+ ASSERT_FALSE(iter->AtEnd());
+ auto op = iter->Get();
ASSERT_EQ(op->type, kCowCopyOp);
ASSERT_EQ(op->compression, kCowCompressNone);
@@ -144,22 +138,22 @@
ASSERT_EQ(op->new_block, 10);
ASSERT_EQ(op->source, 20);
- StringSink sink;
+ std::string sink(data.size(), '\0');
iter->Next();
- ASSERT_FALSE(iter->Done());
- op = &iter->Get();
+ ASSERT_FALSE(iter->AtEnd());
+ op = iter->Get();
ASSERT_EQ(op->type, kCowReplaceOp);
- ASSERT_EQ(op->compression, kCowCompressNone);
+ ASSERT_FALSE(GetCowOpSourceInfoCompression(op));
ASSERT_EQ(op->data_length, 4096);
ASSERT_EQ(op->new_block, 50);
- ASSERT_TRUE(reader.ReadData(*op, &sink));
- ASSERT_EQ(sink.stream(), data);
+ ASSERT_TRUE(ReadData(reader, op, sink.data(), sink.size()));
+ ASSERT_EQ(sink, data);
iter->Next();
- ASSERT_FALSE(iter->Done());
- op = &iter->Get();
+ ASSERT_FALSE(iter->AtEnd());
+ op = iter->Get();
// Note: the zero operation gets split into two blocks.
ASSERT_EQ(op->type, kCowZeroOp);
@@ -169,8 +163,8 @@
ASSERT_EQ(op->source, 0);
iter->Next();
- ASSERT_FALSE(iter->Done());
- op = &iter->Get();
+ ASSERT_FALSE(iter->AtEnd());
+ op = iter->Get();
ASSERT_EQ(op->type, kCowZeroOp);
ASSERT_EQ(op->compression, kCowCompressNone);
@@ -179,15 +173,15 @@
ASSERT_EQ(op->source, 0);
iter->Next();
- ASSERT_TRUE(iter->Done());
+ ASSERT_TRUE(iter->AtEnd());
}
TEST_F(CowTest, ReadWriteXor) {
CowOptions options;
options.cluster_ops = 0;
- CowWriter writer(options);
+ CowWriterV2 writer(options, GetCowFd());
- ASSERT_TRUE(writer.Initialize(cow_->fd));
+ ASSERT_TRUE(writer.Initialize());
std::string data = "This is some data, believe it";
data.resize(options.block_size, '\0');
@@ -200,21 +194,22 @@
ASSERT_EQ(lseek(cow_->fd, 0, SEEK_SET), 0);
CowReader reader;
- CowHeader header;
- CowFooter footer;
ASSERT_TRUE(reader.Parse(cow_->fd));
- ASSERT_TRUE(reader.GetHeader(&header));
- ASSERT_TRUE(reader.GetFooter(&footer));
- ASSERT_EQ(header.magic, kCowMagicNumber);
- ASSERT_EQ(header.major_version, kCowVersionMajor);
- ASSERT_EQ(header.minor_version, kCowVersionMinor);
+
+ const auto& header = reader.GetHeader();
+ ASSERT_EQ(header.prefix.magic, kCowMagicNumber);
+ ASSERT_EQ(header.prefix.major_version, kCowVersionMajor);
+ ASSERT_EQ(header.prefix.minor_version, kCowVersionMinor);
ASSERT_EQ(header.block_size, options.block_size);
+
+ CowFooter footer;
+ ASSERT_TRUE(reader.GetFooter(&footer));
ASSERT_EQ(footer.op.num_ops, 4);
auto iter = reader.GetOpIter();
ASSERT_NE(iter, nullptr);
- ASSERT_FALSE(iter->Done());
- auto op = &iter->Get();
+ ASSERT_FALSE(iter->AtEnd());
+ auto op = iter->Get();
ASSERT_EQ(op->type, kCowCopyOp);
ASSERT_EQ(op->compression, kCowCompressNone);
@@ -222,23 +217,23 @@
ASSERT_EQ(op->new_block, 10);
ASSERT_EQ(op->source, 20);
- StringSink sink;
+ std::string sink(data.size(), '\0');
iter->Next();
- ASSERT_FALSE(iter->Done());
- op = &iter->Get();
+ ASSERT_FALSE(iter->AtEnd());
+ op = iter->Get();
ASSERT_EQ(op->type, kCowXorOp);
- ASSERT_EQ(op->compression, kCowCompressNone);
+ ASSERT_FALSE(GetCowOpSourceInfoCompression(op));
ASSERT_EQ(op->data_length, 4096);
ASSERT_EQ(op->new_block, 50);
- ASSERT_EQ(op->source, 98314); // 4096 * 24 + 10
- ASSERT_TRUE(reader.ReadData(*op, &sink));
- ASSERT_EQ(sink.stream(), data);
+ ASSERT_EQ(GetCowOpSourceInfoData(op), 98314); // 4096 * 24 + 10
+ ASSERT_TRUE(ReadData(reader, op, sink.data(), sink.size()));
+ ASSERT_EQ(sink, data);
iter->Next();
- ASSERT_FALSE(iter->Done());
- op = &iter->Get();
+ ASSERT_FALSE(iter->AtEnd());
+ op = iter->Get();
// Note: the zero operation gets split into two blocks.
ASSERT_EQ(op->type, kCowZeroOp);
@@ -248,8 +243,8 @@
ASSERT_EQ(op->source, 0);
iter->Next();
- ASSERT_FALSE(iter->Done());
- op = &iter->Get();
+ ASSERT_FALSE(iter->AtEnd());
+ op = iter->Get();
ASSERT_EQ(op->type, kCowZeroOp);
ASSERT_EQ(op->compression, kCowCompressNone);
@@ -258,16 +253,16 @@
ASSERT_EQ(op->source, 0);
iter->Next();
- ASSERT_TRUE(iter->Done());
+ ASSERT_TRUE(iter->AtEnd());
}
TEST_F(CowTest, CompressGz) {
CowOptions options;
options.cluster_ops = 0;
options.compression = "gz";
- CowWriter writer(options);
+ CowWriterV2 writer(options, GetCowFd());
- ASSERT_TRUE(writer.Initialize(cow_->fd));
+ ASSERT_TRUE(writer.Initialize());
std::string data = "This is some data, believe it";
data.resize(options.block_size, '\0');
@@ -282,32 +277,32 @@
auto iter = reader.GetOpIter();
ASSERT_NE(iter, nullptr);
- ASSERT_FALSE(iter->Done());
- auto op = &iter->Get();
+ ASSERT_FALSE(iter->AtEnd());
+ auto op = iter->Get();
- StringSink sink;
+ std::string sink(data.size(), '\0');
ASSERT_EQ(op->type, kCowReplaceOp);
- ASSERT_EQ(op->compression, kCowCompressGz);
+ ASSERT_TRUE(GetCowOpSourceInfoCompression(op));
ASSERT_EQ(op->data_length, 56); // compressed!
ASSERT_EQ(op->new_block, 50);
- ASSERT_TRUE(reader.ReadData(*op, &sink));
- ASSERT_EQ(sink.stream(), data);
+ ASSERT_TRUE(ReadData(reader, op, sink.data(), sink.size()));
+ ASSERT_EQ(sink, data);
iter->Next();
- ASSERT_TRUE(iter->Done());
+ ASSERT_TRUE(iter->AtEnd());
}
-class CompressionRWTest : public CowTest, public testing::WithParamInterface<const char*> {};
+class CompressionTest : public CowTest, public testing::WithParamInterface<const char*> {};
-TEST_P(CompressionRWTest, ThreadedBatchWrites) {
+TEST_P(CompressionTest, ThreadedBatchWrites) {
CowOptions options;
options.compression = GetParam();
options.num_compress_threads = 2;
- CowWriter writer(options);
+ CowWriterV2 writer(options, GetCowFd());
- ASSERT_TRUE(writer.Initialize(cow_->fd));
+ ASSERT_TRUE(writer.Initialize());
std::string xor_data = "This is test data-1. Testing xor";
xor_data.resize(options.block_size, '\0');
@@ -337,36 +332,37 @@
ASSERT_NE(iter, nullptr);
int total_blocks = 0;
- while (!iter->Done()) {
- auto op = &iter->Get();
+ while (!iter->AtEnd()) {
+ auto op = iter->Get();
if (op->type == kCowXorOp) {
total_blocks += 1;
- StringSink sink;
+ std::string sink(xor_data.size(), '\0');
ASSERT_EQ(op->new_block, 50);
- ASSERT_EQ(op->source, 98314); // 4096 * 24 + 10
- ASSERT_TRUE(reader.ReadData(*op, &sink));
- ASSERT_EQ(sink.stream(), xor_data);
+ ASSERT_EQ(GetCowOpSourceInfoData(op), 98314); // 4096 * 24 + 10
+ ASSERT_TRUE(ReadData(reader, op, sink.data(), sink.size()));
+ ASSERT_EQ(sink, xor_data);
}
if (op->type == kCowReplaceOp) {
total_blocks += 1;
if (op->new_block == 100) {
- StringSink sink;
- ASSERT_TRUE(reader.ReadData(*op, &sink));
data.resize(options.block_size);
- ASSERT_EQ(sink.stream(), data);
+ std::string sink(data.size(), '\0');
+ ASSERT_TRUE(ReadData(reader, op, sink.data(), sink.size()));
+ ASSERT_EQ(sink.size(), data.size());
+ ASSERT_EQ(sink, data);
}
if (op->new_block == 6000) {
- StringSink sink;
- ASSERT_TRUE(reader.ReadData(*op, &sink));
data2.resize(options.block_size);
- ASSERT_EQ(sink.stream(), data2);
+ std::string sink(data2.size(), '\0');
+ ASSERT_TRUE(ReadData(reader, op, sink.data(), sink.size()));
+ ASSERT_EQ(sink, data2);
}
if (op->new_block == 9000) {
- StringSink sink;
- ASSERT_TRUE(reader.ReadData(*op, &sink));
- ASSERT_EQ(sink.stream(), data3);
+ std::string sink(data3.size(), '\0');
+ ASSERT_TRUE(ReadData(reader, op, sink.data(), sink.size()));
+ ASSERT_EQ(sink, data3);
}
}
@@ -376,15 +372,15 @@
ASSERT_EQ(total_blocks, expected_blocks);
}
-TEST_P(CompressionRWTest, NoBatchWrites) {
+TEST_P(CompressionTest, NoBatchWrites) {
CowOptions options;
options.compression = GetParam();
options.num_compress_threads = 1;
options.cluster_ops = 0;
- CowWriter writer(options);
+ CowWriterV2 writer(options, GetCowFd());
- ASSERT_TRUE(writer.Initialize(cow_->fd));
+ ASSERT_TRUE(writer.Initialize());
std::string data = "Testing replace ops without batch writes";
data.resize(options.block_size * 1024, '\0');
@@ -410,27 +406,27 @@
ASSERT_NE(iter, nullptr);
int total_blocks = 0;
- while (!iter->Done()) {
- auto op = &iter->Get();
+ while (!iter->AtEnd()) {
+ auto op = iter->Get();
if (op->type == kCowReplaceOp) {
total_blocks += 1;
if (op->new_block == 50) {
- StringSink sink;
- ASSERT_TRUE(reader.ReadData(*op, &sink));
data.resize(options.block_size);
- ASSERT_EQ(sink.stream(), data);
+ std::string sink(data.size(), '\0');
+ ASSERT_TRUE(ReadData(reader, op, sink.data(), sink.size()));
+ ASSERT_EQ(sink, data);
}
if (op->new_block == 3000) {
- StringSink sink;
- ASSERT_TRUE(reader.ReadData(*op, &sink));
data2.resize(options.block_size);
- ASSERT_EQ(sink.stream(), data2);
+ std::string sink(data2.size(), '\0');
+ ASSERT_TRUE(ReadData(reader, op, sink.data(), sink.size()));
+ ASSERT_EQ(sink, data2);
}
if (op->new_block == 5000) {
- StringSink sink;
- ASSERT_TRUE(reader.ReadData(*op, &sink));
- ASSERT_EQ(sink.stream(), data3);
+ std::string sink(data3.size(), '\0');
+ ASSERT_TRUE(ReadData(reader, op, sink.data(), sink.size()));
+ ASSERT_EQ(sink, data3);
}
}
@@ -440,15 +436,74 @@
ASSERT_EQ(total_blocks, expected_blocks);
}
-INSTANTIATE_TEST_SUITE_P(CowApi, CompressionRWTest, testing::Values("none", "gz", "brotli", "lz4"));
+template <typename T>
+class HorribleStream : public IByteStream {
+ public:
+ HorribleStream(const std::basic_string<T>& input) : input_(input) {}
+
+ ssize_t Read(void* buffer, size_t length) override {
+ if (pos_ >= input_.size()) {
+ return 0;
+ }
+ if (length) {
+ *reinterpret_cast<char*>(buffer) = input_[pos_];
+ }
+ pos_++;
+ return 1;
+ }
+ size_t Size() const override { return input_.size(); }
+
+ private:
+ std::basic_string<T> input_;
+ size_t pos_ = 0;
+};
+
+TEST(HorribleStream, ReadFully) {
+ std::string expected = "this is some data";
+
+ HorribleStream<char> stream(expected);
+
+ std::string buffer(expected.size(), '\0');
+ ASSERT_TRUE(stream.ReadFully(buffer.data(), buffer.size()));
+ ASSERT_EQ(buffer, expected);
+}
+
+TEST_P(CompressionTest, HorribleStream) {
+ if (strcmp(GetParam(), "none") == 0) {
+ GTEST_SKIP();
+ }
+
+ auto algorithm = CompressionAlgorithmFromString(GetParam());
+ ASSERT_TRUE(algorithm.has_value());
+
+ std::string expected = "The quick brown fox jumps over the lazy dog.";
+ expected.resize(4096, '\0');
+
+ auto result = CompressWorker::Compress(*algorithm, expected.data(), expected.size());
+ ASSERT_FALSE(result.empty());
+
+ HorribleStream<uint8_t> stream(result);
+ auto decomp = IDecompressor::FromString(GetParam());
+ ASSERT_NE(decomp, nullptr);
+ decomp->set_stream(&stream);
+
+ expected = expected.substr(10, 500);
+
+ std::string buffer(expected.size(), '\0');
+ ASSERT_EQ(decomp->Decompress(buffer.data(), 500, 4096, 10), 500);
+ ASSERT_EQ(buffer, expected);
+}
+
+INSTANTIATE_TEST_SUITE_P(AllCompressors, CompressionTest,
+ testing::Values("none", "gz", "brotli", "lz4"));
TEST_F(CowTest, ClusterCompressGz) {
CowOptions options;
options.compression = "gz";
options.cluster_ops = 2;
- CowWriter writer(options);
+ CowWriterV2 writer(options, GetCowFd());
- ASSERT_TRUE(writer.Initialize(cow_->fd));
+ ASSERT_TRUE(writer.Initialize());
std::string data = "This is some data, believe it";
data.resize(options.block_size, '\0');
@@ -467,52 +522,53 @@
auto iter = reader.GetOpIter();
ASSERT_NE(iter, nullptr);
- ASSERT_FALSE(iter->Done());
- auto op = &iter->Get();
+ ASSERT_FALSE(iter->AtEnd());
+ auto op = iter->Get();
- StringSink sink;
+ std::string sink(data.size(), '\0');
ASSERT_EQ(op->type, kCowReplaceOp);
- ASSERT_EQ(op->compression, kCowCompressGz);
+ ASSERT_TRUE(GetCowOpSourceInfoCompression(op));
ASSERT_EQ(op->data_length, 56); // compressed!
ASSERT_EQ(op->new_block, 50);
- ASSERT_TRUE(reader.ReadData(*op, &sink));
- ASSERT_EQ(sink.stream(), data);
+ ASSERT_TRUE(ReadData(reader, op, sink.data(), sink.size()));
+ ASSERT_EQ(sink, data);
iter->Next();
- ASSERT_FALSE(iter->Done());
- op = &iter->Get();
+ ASSERT_FALSE(iter->AtEnd());
+ op = iter->Get();
ASSERT_EQ(op->type, kCowClusterOp);
iter->Next();
- ASSERT_FALSE(iter->Done());
- op = &iter->Get();
+ ASSERT_FALSE(iter->AtEnd());
+ op = iter->Get();
- sink.Reset();
- ASSERT_EQ(op->compression, kCowCompressGz);
+ sink = {};
+ sink.resize(data2.size(), '\0');
+ ASSERT_TRUE(GetCowOpSourceInfoCompression(op));
ASSERT_EQ(op->data_length, 41); // compressed!
ASSERT_EQ(op->new_block, 51);
- ASSERT_TRUE(reader.ReadData(*op, &sink));
- ASSERT_EQ(sink.stream(), data2);
+ ASSERT_TRUE(ReadData(reader, op, sink.data(), sink.size()));
+ ASSERT_EQ(sink, data2);
iter->Next();
- ASSERT_FALSE(iter->Done());
- op = &iter->Get();
+ ASSERT_FALSE(iter->AtEnd());
+ op = iter->Get();
ASSERT_EQ(op->type, kCowClusterOp);
iter->Next();
- ASSERT_TRUE(iter->Done());
+ ASSERT_TRUE(iter->AtEnd());
}
TEST_F(CowTest, CompressTwoBlocks) {
CowOptions options;
options.compression = "gz";
options.cluster_ops = 0;
- CowWriter writer(options);
+ CowWriterV2 writer(options, GetCowFd());
- ASSERT_TRUE(writer.Initialize(cow_->fd));
+ ASSERT_TRUE(writer.Initialize());
std::string data = "This is some data, believe it";
data.resize(options.block_size * 2, '\0');
@@ -527,68 +583,28 @@
auto iter = reader.GetOpIter();
ASSERT_NE(iter, nullptr);
- ASSERT_FALSE(iter->Done());
+ ASSERT_FALSE(iter->AtEnd());
iter->Next();
- ASSERT_FALSE(iter->Done());
+ ASSERT_FALSE(iter->AtEnd());
- StringSink sink;
+ std::string sink(options.block_size, '\0');
- auto op = &iter->Get();
+ auto op = iter->Get();
ASSERT_EQ(op->type, kCowReplaceOp);
- ASSERT_EQ(op->compression, kCowCompressGz);
+ ASSERT_TRUE(GetCowOpSourceInfoCompression(op));
ASSERT_EQ(op->new_block, 51);
- ASSERT_TRUE(reader.ReadData(*op, &sink));
+ ASSERT_TRUE(ReadData(reader, op, sink.data(), sink.size()));
}
-// Only return 1-byte buffers, to stress test the partial read logic in
-// CowReader.
-class HorribleStringSink : public StringSink {
- public:
- void* GetBuffer(size_t, size_t* actual) override { return StringSink::GetBuffer(1, actual); }
-};
-
-class CompressionTest : public CowTest, public testing::WithParamInterface<const char*> {};
-
-TEST_P(CompressionTest, HorribleSink) {
- CowOptions options;
- options.compression = GetParam();
- options.cluster_ops = 0;
- CowWriter writer(options);
-
- ASSERT_TRUE(writer.Initialize(cow_->fd));
-
- std::string data = "This is some data, believe it";
- data.resize(options.block_size, '\0');
-
- ASSERT_TRUE(writer.AddRawBlocks(50, data.data(), data.size()));
- ASSERT_TRUE(writer.Finalize());
-
- ASSERT_EQ(lseek(cow_->fd, 0, SEEK_SET), 0);
-
- CowReader reader;
- ASSERT_TRUE(reader.Parse(cow_->fd));
-
- auto iter = reader.GetOpIter();
- ASSERT_NE(iter, nullptr);
- ASSERT_FALSE(iter->Done());
-
- HorribleStringSink sink;
- auto op = &iter->Get();
- ASSERT_TRUE(reader.ReadData(*op, &sink));
- ASSERT_EQ(sink.stream(), data);
-}
-
-INSTANTIATE_TEST_SUITE_P(CowApi, CompressionTest, testing::Values("none", "gz", "brotli"));
-
TEST_F(CowTest, GetSize) {
CowOptions options;
options.cluster_ops = 0;
- CowWriter writer(options);
+ CowWriterV2 writer(options, GetCowFd());
if (ftruncate(cow_->fd, 0) < 0) {
perror("Fails to set temp file size");
FAIL();
}
- ASSERT_TRUE(writer.Initialize(cow_->fd));
+ ASSERT_TRUE(writer.Initialize());
std::string data = "This is some data, believe it";
data.resize(options.block_size, '\0');
@@ -609,8 +625,8 @@
TEST_F(CowTest, AppendLabelSmall) {
CowOptions options;
options.cluster_ops = 0;
- auto writer = std::make_unique<CowWriter>(options);
- ASSERT_TRUE(writer->Initialize(cow_->fd));
+ auto writer = std::make_unique<CowWriterV2>(options, GetCowFd());
+ ASSERT_TRUE(writer->Initialize());
std::string data = "This is some data, believe it";
data.resize(options.block_size, '\0');
@@ -620,8 +636,8 @@
ASSERT_EQ(lseek(cow_->fd, 0, SEEK_SET), 0);
- writer = std::make_unique<CowWriter>(options);
- ASSERT_TRUE(writer->InitializeAppend(cow_->fd, 3));
+ writer = std::make_unique<CowWriterV2>(options, GetCowFd());
+ ASSERT_TRUE(writer->Initialize({3}));
std::string data2 = "More data!";
data2.resize(options.block_size, '\0');
@@ -641,42 +657,43 @@
ASSERT_TRUE(reader.GetLastLabel(&label));
ASSERT_EQ(label, 3);
- StringSink sink;
+ std::string sink(data.size(), '\0');
auto iter = reader.GetOpIter();
ASSERT_NE(iter, nullptr);
- ASSERT_FALSE(iter->Done());
- auto op = &iter->Get();
+ ASSERT_FALSE(iter->AtEnd());
+ auto op = iter->Get();
ASSERT_EQ(op->type, kCowReplaceOp);
- ASSERT_TRUE(reader.ReadData(*op, &sink));
- ASSERT_EQ(sink.stream(), data);
+ ASSERT_TRUE(ReadData(reader, op, sink.data(), sink.size()));
+ ASSERT_EQ(sink, data);
iter->Next();
- sink.Reset();
+ sink = {};
+ sink.resize(data2.size(), '\0');
- ASSERT_FALSE(iter->Done());
- op = &iter->Get();
+ ASSERT_FALSE(iter->AtEnd());
+ op = iter->Get();
ASSERT_EQ(op->type, kCowLabelOp);
ASSERT_EQ(op->source, 3);
iter->Next();
- ASSERT_FALSE(iter->Done());
- op = &iter->Get();
+ ASSERT_FALSE(iter->AtEnd());
+ op = iter->Get();
ASSERT_EQ(op->type, kCowReplaceOp);
- ASSERT_TRUE(reader.ReadData(*op, &sink));
- ASSERT_EQ(sink.stream(), data2);
+ ASSERT_TRUE(ReadData(reader, op, sink.data(), sink.size()));
+ ASSERT_EQ(sink, data2);
iter->Next();
- ASSERT_TRUE(iter->Done());
+ ASSERT_TRUE(iter->AtEnd());
}
TEST_F(CowTest, AppendLabelMissing) {
CowOptions options;
options.cluster_ops = 0;
- auto writer = std::make_unique<CowWriter>(options);
- ASSERT_TRUE(writer->Initialize(cow_->fd));
+ auto writer = std::make_unique<CowWriterV2>(options, GetCowFd());
+ ASSERT_TRUE(writer->Initialize());
ASSERT_TRUE(writer->AddLabel(0));
std::string data = "This is some data, believe it";
@@ -688,9 +705,9 @@
ASSERT_EQ(lseek(cow_->fd, 0, SEEK_SET), 0);
- writer = std::make_unique<CowWriter>(options);
- ASSERT_FALSE(writer->InitializeAppend(cow_->fd, 1));
- ASSERT_TRUE(writer->InitializeAppend(cow_->fd, 0));
+ writer = std::make_unique<CowWriterV2>(options, GetCowFd());
+ ASSERT_FALSE(writer->Initialize({1}));
+ ASSERT_TRUE(writer->Initialize({0}));
ASSERT_TRUE(writer->AddZeroBlocks(51, 1));
ASSERT_TRUE(writer->Finalize());
@@ -705,32 +722,30 @@
CowReader reader;
ASSERT_TRUE(reader.Parse(cow_->fd));
- StringSink sink;
-
auto iter = reader.GetOpIter();
ASSERT_NE(iter, nullptr);
- ASSERT_FALSE(iter->Done());
- auto op = &iter->Get();
+ ASSERT_FALSE(iter->AtEnd());
+ auto op = iter->Get();
ASSERT_EQ(op->type, kCowLabelOp);
ASSERT_EQ(op->source, 0);
iter->Next();
- ASSERT_FALSE(iter->Done());
- op = &iter->Get();
+ ASSERT_FALSE(iter->AtEnd());
+ op = iter->Get();
ASSERT_EQ(op->type, kCowZeroOp);
iter->Next();
- ASSERT_TRUE(iter->Done());
+ ASSERT_TRUE(iter->AtEnd());
}
TEST_F(CowTest, AppendExtendedCorrupted) {
CowOptions options;
options.cluster_ops = 0;
- auto writer = std::make_unique<CowWriter>(options);
- ASSERT_TRUE(writer->Initialize(cow_->fd));
+ auto writer = std::make_unique<CowWriterV2>(options, GetCowFd());
+ ASSERT_TRUE(writer->Initialize());
ASSERT_TRUE(writer->AddLabel(5));
@@ -752,8 +767,8 @@
ASSERT_EQ(lseek(cow_->fd, 0, SEEK_SET), 0);
- writer = std::make_unique<CowWriter>(options);
- ASSERT_TRUE(writer->InitializeAppend(cow_->fd, 5));
+ writer = std::make_unique<CowWriterV2>(options, GetCowFd());
+ ASSERT_TRUE(writer->Initialize({5}));
ASSERT_TRUE(writer->Finalize());
@@ -765,25 +780,23 @@
CowReader reader;
ASSERT_TRUE(reader.Parse(cow_->fd));
- StringSink sink;
-
auto iter = reader.GetOpIter();
ASSERT_NE(iter, nullptr);
- ASSERT_FALSE(iter->Done());
- auto op = &iter->Get();
+ ASSERT_FALSE(iter->AtEnd());
+ auto op = iter->Get();
ASSERT_EQ(op->type, kCowLabelOp);
ASSERT_EQ(op->source, 5);
iter->Next();
- ASSERT_TRUE(iter->Done());
+ ASSERT_TRUE(iter->AtEnd());
}
TEST_F(CowTest, AppendbyLabel) {
CowOptions options;
options.cluster_ops = 0;
- auto writer = std::make_unique<CowWriter>(options);
- ASSERT_TRUE(writer->Initialize(cow_->fd));
+ auto writer = std::make_unique<CowWriterV2>(options, GetCowFd());
+ ASSERT_TRUE(writer->Initialize());
std::string data = "This is some data, believe it";
data.resize(options.block_size * 2, '\0');
@@ -801,9 +814,9 @@
ASSERT_EQ(lseek(cow_->fd, 0, SEEK_SET), 0);
- writer = std::make_unique<CowWriter>(options);
- ASSERT_FALSE(writer->InitializeAppend(cow_->fd, 12));
- ASSERT_TRUE(writer->InitializeAppend(cow_->fd, 5));
+ writer = std::make_unique<CowWriterV2>(options, GetCowFd());
+ ASSERT_FALSE(writer->Initialize({12}));
+ ASSERT_TRUE(writer->Initialize({5}));
// This should drop label 6
ASSERT_TRUE(writer->Finalize());
@@ -816,62 +829,62 @@
CowReader reader;
ASSERT_TRUE(reader.Parse(cow_->fd));
- StringSink sink;
+ std::string sink(options.block_size, '\0');
auto iter = reader.GetOpIter();
ASSERT_NE(iter, nullptr);
- ASSERT_FALSE(iter->Done());
- auto op = &iter->Get();
+ ASSERT_FALSE(iter->AtEnd());
+ auto op = iter->Get();
ASSERT_EQ(op->type, kCowReplaceOp);
- ASSERT_TRUE(reader.ReadData(*op, &sink));
- ASSERT_EQ(sink.stream(), data.substr(0, options.block_size));
+ ASSERT_TRUE(ReadData(reader, op, sink.data(), sink.size()));
+ ASSERT_EQ(sink, data.substr(0, options.block_size));
iter->Next();
- sink.Reset();
+ sink = {};
+ sink.resize(options.block_size, '\0');
- ASSERT_FALSE(iter->Done());
- op = &iter->Get();
+ ASSERT_FALSE(iter->AtEnd());
+ op = iter->Get();
ASSERT_EQ(op->type, kCowReplaceOp);
- ASSERT_TRUE(reader.ReadData(*op, &sink));
- ASSERT_EQ(sink.stream(), data.substr(options.block_size, 2 * options.block_size));
+ ASSERT_TRUE(ReadData(reader, op, sink.data(), sink.size()));
+ ASSERT_EQ(sink, data.substr(options.block_size, 2 * options.block_size));
iter->Next();
- sink.Reset();
- ASSERT_FALSE(iter->Done());
- op = &iter->Get();
+ ASSERT_FALSE(iter->AtEnd());
+ op = iter->Get();
ASSERT_EQ(op->type, kCowLabelOp);
ASSERT_EQ(op->source, 4);
iter->Next();
- ASSERT_FALSE(iter->Done());
- op = &iter->Get();
+ ASSERT_FALSE(iter->AtEnd());
+ op = iter->Get();
ASSERT_EQ(op->type, kCowZeroOp);
iter->Next();
- ASSERT_FALSE(iter->Done());
- op = &iter->Get();
+ ASSERT_FALSE(iter->AtEnd());
+ op = iter->Get();
ASSERT_EQ(op->type, kCowZeroOp);
iter->Next();
- ASSERT_FALSE(iter->Done());
- op = &iter->Get();
+ ASSERT_FALSE(iter->AtEnd());
+ op = iter->Get();
ASSERT_EQ(op->type, kCowLabelOp);
ASSERT_EQ(op->source, 5);
iter->Next();
- ASSERT_TRUE(iter->Done());
+ ASSERT_TRUE(iter->AtEnd());
}
TEST_F(CowTest, ClusterTest) {
CowOptions options;
options.cluster_ops = 4;
- auto writer = std::make_unique<CowWriter>(options);
- ASSERT_TRUE(writer->Initialize(cow_->fd));
+ auto writer = std::make_unique<CowWriterV2>(options, GetCowFd());
+ ASSERT_TRUE(writer->Initialize());
std::string data = "This is some data, believe it";
data.resize(options.block_size, '\0');
@@ -897,87 +910,86 @@
CowReader reader;
ASSERT_TRUE(reader.Parse(cow_->fd));
- StringSink sink;
+ std::string sink(data.size(), '\0');
auto iter = reader.GetOpIter();
ASSERT_NE(iter, nullptr);
- ASSERT_FALSE(iter->Done());
- auto op = &iter->Get();
+ ASSERT_FALSE(iter->AtEnd());
+ auto op = iter->Get();
ASSERT_EQ(op->type, kCowReplaceOp);
- ASSERT_TRUE(reader.ReadData(*op, &sink));
- ASSERT_EQ(sink.stream(), data.substr(0, options.block_size));
+ ASSERT_TRUE(ReadData(reader, op, sink.data(), sink.size()));
+ ASSERT_EQ(sink, data.substr(0, options.block_size));
iter->Next();
- sink.Reset();
- ASSERT_FALSE(iter->Done());
- op = &iter->Get();
+ ASSERT_FALSE(iter->AtEnd());
+ op = iter->Get();
ASSERT_EQ(op->type, kCowLabelOp);
ASSERT_EQ(op->source, 4);
iter->Next();
- ASSERT_FALSE(iter->Done());
- op = &iter->Get();
+ ASSERT_FALSE(iter->AtEnd());
+ op = iter->Get();
ASSERT_EQ(op->type, kCowZeroOp);
iter->Next();
- ASSERT_FALSE(iter->Done());
- op = &iter->Get();
+ ASSERT_FALSE(iter->AtEnd());
+ op = iter->Get();
ASSERT_EQ(op->type, kCowClusterOp);
iter->Next();
- ASSERT_FALSE(iter->Done());
- op = &iter->Get();
+ ASSERT_FALSE(iter->AtEnd());
+ op = iter->Get();
ASSERT_EQ(op->type, kCowZeroOp);
iter->Next();
- ASSERT_FALSE(iter->Done());
- op = &iter->Get();
+ ASSERT_FALSE(iter->AtEnd());
+ op = iter->Get();
ASSERT_EQ(op->type, kCowLabelOp);
ASSERT_EQ(op->source, 5);
iter->Next();
- ASSERT_FALSE(iter->Done());
- op = &iter->Get();
+ ASSERT_FALSE(iter->AtEnd());
+ op = iter->Get();
ASSERT_EQ(op->type, kCowCopyOp);
iter->Next();
- ASSERT_FALSE(iter->Done());
- op = &iter->Get();
+ ASSERT_FALSE(iter->AtEnd());
+ op = iter->Get();
ASSERT_EQ(op->type, kCowClusterOp);
iter->Next();
- ASSERT_FALSE(iter->Done());
- op = &iter->Get();
+ ASSERT_FALSE(iter->AtEnd());
+ op = iter->Get();
ASSERT_EQ(op->type, kCowLabelOp);
ASSERT_EQ(op->source, 6);
iter->Next();
- ASSERT_TRUE(iter->Done());
+ ASSERT_TRUE(iter->AtEnd());
}
TEST_F(CowTest, ClusterAppendTest) {
CowOptions options;
options.cluster_ops = 3;
- auto writer = std::make_unique<CowWriter>(options);
- ASSERT_TRUE(writer->Initialize(cow_->fd));
+ auto writer = std::make_unique<CowWriterV2>(options, GetCowFd());
+ ASSERT_TRUE(writer->Initialize());
ASSERT_TRUE(writer->AddLabel(50));
ASSERT_TRUE(writer->Finalize()); // Adds a cluster op, should be dropped on append
ASSERT_EQ(lseek(cow_->fd, 0, SEEK_SET), 0);
- writer = std::make_unique<CowWriter>(options);
- ASSERT_TRUE(writer->InitializeAppend(cow_->fd, 50));
+ writer = std::make_unique<CowWriterV2>(options, GetCowFd());
+ ASSERT_TRUE(writer->Initialize({50}));
std::string data2 = "More data!";
data2.resize(options.block_size, '\0');
@@ -997,40 +1009,40 @@
ASSERT_TRUE(reader.GetLastLabel(&label));
ASSERT_EQ(label, 50);
- StringSink sink;
+ std::string sink(data2.size(), '\0');
auto iter = reader.GetOpIter();
ASSERT_NE(iter, nullptr);
- ASSERT_FALSE(iter->Done());
- auto op = &iter->Get();
+ ASSERT_FALSE(iter->AtEnd());
+ auto op = iter->Get();
ASSERT_EQ(op->type, kCowLabelOp);
ASSERT_EQ(op->source, 50);
iter->Next();
- ASSERT_FALSE(iter->Done());
- op = &iter->Get();
+ ASSERT_FALSE(iter->AtEnd());
+ op = iter->Get();
ASSERT_EQ(op->type, kCowReplaceOp);
- ASSERT_TRUE(reader.ReadData(*op, &sink));
- ASSERT_EQ(sink.stream(), data2);
+ ASSERT_TRUE(ReadData(reader, op, sink.data(), sink.size()));
+ ASSERT_EQ(sink, data2);
iter->Next();
- ASSERT_FALSE(iter->Done());
- op = &iter->Get();
+ ASSERT_FALSE(iter->AtEnd());
+ op = iter->Get();
ASSERT_EQ(op->type, kCowClusterOp);
iter->Next();
- ASSERT_TRUE(iter->Done());
+ ASSERT_TRUE(iter->AtEnd());
}
TEST_F(CowTest, AppendAfterFinalize) {
CowOptions options;
options.cluster_ops = 0;
- auto writer = std::make_unique<CowWriter>(options);
- ASSERT_TRUE(writer->Initialize(cow_->fd));
+ auto writer = std::make_unique<CowWriterV2>(options, GetCowFd());
+ ASSERT_TRUE(writer->Initialize());
std::string data = "This is some data, believe it";
data.resize(options.block_size, '\0');
@@ -1050,29 +1062,28 @@
ASSERT_TRUE(reader.Parse(cow_->fd));
}
-AssertionResult WriteDataBlock(CowWriter* writer, uint64_t new_block, std::string data) {
- data.resize(writer->options().block_size, '\0');
+AssertionResult WriteDataBlock(ICowWriter* writer, uint64_t new_block, std::string data) {
+ data.resize(writer->GetBlockSize(), '\0');
if (!writer->AddRawBlocks(new_block, data.data(), data.size())) {
return AssertionFailure() << "Failed to add raw block";
}
return AssertionSuccess();
}
-AssertionResult CompareDataBlock(CowReader* reader, const CowOperation& op,
+AssertionResult CompareDataBlock(CowReader* reader, const CowOperation* op,
const std::string& data) {
- CowHeader header;
- reader->GetHeader(&header);
+ const auto& header = reader->GetHeader();
std::string cmp = data;
cmp.resize(header.block_size, '\0');
- StringSink sink;
- if (!reader->ReadData(op, &sink)) {
+ std::string sink(cmp.size(), '\0');
+ if (!reader->ReadData(op, sink.data(), sink.size())) {
return AssertionFailure() << "Failed to read data block";
}
- if (cmp != sink.stream()) {
+ if (cmp != sink) {
return AssertionFailure() << "Data blocks did not match, expected " << cmp << ", got "
- << sink.stream();
+ << sink;
}
return AssertionSuccess();
@@ -1081,8 +1092,8 @@
TEST_F(CowTest, ResumeMidCluster) {
CowOptions options;
options.cluster_ops = 7;
- auto writer = std::make_unique<CowWriter>(options);
- ASSERT_TRUE(writer->Initialize(cow_->fd));
+ auto writer = std::make_unique<CowWriterV2>(options, GetCowFd());
+ ASSERT_TRUE(writer->Initialize());
ASSERT_TRUE(WriteDataBlock(writer.get(), 1, "Block 1"));
ASSERT_TRUE(WriteDataBlock(writer.get(), 2, "Block 2"));
@@ -1092,8 +1103,8 @@
ASSERT_TRUE(WriteDataBlock(writer.get(), 4, "Block 4"));
ASSERT_EQ(lseek(cow_->fd, 0, SEEK_SET), 0);
- writer = std::make_unique<CowWriter>(options);
- ASSERT_TRUE(writer->InitializeAppend(cow_->fd, 1));
+ writer = std::make_unique<CowWriterV2>(options, GetCowFd());
+ ASSERT_TRUE(writer->Initialize({1}));
ASSERT_TRUE(WriteDataBlock(writer.get(), 4, "Block 4"));
ASSERT_TRUE(WriteDataBlock(writer.get(), 5, "Block 5"));
ASSERT_TRUE(WriteDataBlock(writer.get(), 6, "Block 6"));
@@ -1111,18 +1122,18 @@
size_t max_in_cluster = 0;
size_t num_in_cluster = 0;
size_t num_clusters = 0;
- while (!iter->Done()) {
+ while (!iter->AtEnd()) {
const auto& op = iter->Get();
num_in_cluster++;
max_in_cluster = std::max(max_in_cluster, num_in_cluster);
- if (op.type == kCowReplaceOp) {
+ if (op->type == kCowReplaceOp) {
num_replace++;
- ASSERT_EQ(op.new_block, num_replace);
+ ASSERT_EQ(op->new_block, num_replace);
ASSERT_TRUE(CompareDataBlock(&reader, op, "Block " + std::to_string(num_replace)));
- } else if (op.type == kCowClusterOp) {
+ } else if (op->type == kCowClusterOp) {
num_in_cluster = 0;
num_clusters++;
}
@@ -1138,8 +1149,8 @@
CowOptions options;
int cluster_ops = 5;
options.cluster_ops = cluster_ops;
- auto writer = std::make_unique<CowWriter>(options);
- ASSERT_TRUE(writer->Initialize(cow_->fd));
+ auto writer = std::make_unique<CowWriterV2>(options, GetCowFd());
+ ASSERT_TRUE(writer->Initialize());
ASSERT_TRUE(WriteDataBlock(writer.get(), 1, "Block 1"));
ASSERT_TRUE(WriteDataBlock(writer.get(), 2, "Block 2"));
@@ -1153,8 +1164,8 @@
ASSERT_TRUE(WriteDataBlock(writer.get(), 8, "Block 8"));
ASSERT_EQ(lseek(cow_->fd, 0, SEEK_SET), 0);
- writer = std::make_unique<CowWriter>(options);
- ASSERT_TRUE(writer->InitializeAppend(cow_->fd, 1));
+ writer = std::make_unique<CowWriterV2>(options, GetCowFd());
+ ASSERT_TRUE(writer->Initialize({1}));
ASSERT_TRUE(WriteDataBlock(writer.get(), 4, "Block 4"));
ASSERT_TRUE(WriteDataBlock(writer.get(), 5, "Block 5"));
ASSERT_TRUE(WriteDataBlock(writer.get(), 6, "Block 6"));
@@ -1172,18 +1183,18 @@
size_t max_in_cluster = 0;
size_t num_in_cluster = 0;
size_t num_clusters = 0;
- while (!iter->Done()) {
+ while (!iter->AtEnd()) {
const auto& op = iter->Get();
num_in_cluster++;
max_in_cluster = std::max(max_in_cluster, num_in_cluster);
- if (op.type == kCowReplaceOp) {
+ if (op->type == kCowReplaceOp) {
num_replace++;
- ASSERT_EQ(op.new_block, num_replace);
+ ASSERT_EQ(op->new_block, num_replace);
ASSERT_TRUE(CompareDataBlock(&reader, op, "Block " + std::to_string(num_replace)));
- } else if (op.type == kCowClusterOp) {
+ } else if (op->type == kCowClusterOp) {
num_in_cluster = 0;
num_clusters++;
}
@@ -1198,8 +1209,8 @@
TEST_F(CowTest, DeleteMidCluster) {
CowOptions options;
options.cluster_ops = 7;
- auto writer = std::make_unique<CowWriter>(options);
- ASSERT_TRUE(writer->Initialize(cow_->fd));
+ auto writer = std::make_unique<CowWriterV2>(options, GetCowFd());
+ ASSERT_TRUE(writer->Initialize());
ASSERT_TRUE(WriteDataBlock(writer.get(), 1, "Block 1"));
ASSERT_TRUE(WriteDataBlock(writer.get(), 2, "Block 2"));
@@ -1211,8 +1222,8 @@
ASSERT_TRUE(WriteDataBlock(writer.get(), 6, "Block 6"));
ASSERT_EQ(lseek(cow_->fd, 0, SEEK_SET), 0);
- writer = std::make_unique<CowWriter>(options);
- ASSERT_TRUE(writer->InitializeAppend(cow_->fd, 1));
+ writer = std::make_unique<CowWriterV2>(options, GetCowFd());
+ ASSERT_TRUE(writer->Initialize({1}));
ASSERT_TRUE(writer->Finalize());
ASSERT_EQ(lseek(cow_->fd, 0, SEEK_SET), 0);
@@ -1224,17 +1235,17 @@
size_t max_in_cluster = 0;
size_t num_in_cluster = 0;
size_t num_clusters = 0;
- while (!iter->Done()) {
+ while (!iter->AtEnd()) {
const auto& op = iter->Get();
num_in_cluster++;
max_in_cluster = std::max(max_in_cluster, num_in_cluster);
- if (op.type == kCowReplaceOp) {
+ if (op->type == kCowReplaceOp) {
num_replace++;
- ASSERT_EQ(op.new_block, num_replace);
+ ASSERT_EQ(op->new_block, num_replace);
ASSERT_TRUE(CompareDataBlock(&reader, op, "Block " + std::to_string(num_replace)));
- } else if (op.type == kCowClusterOp) {
+ } else if (op->type == kCowClusterOp) {
num_in_cluster = 0;
num_clusters++;
}
@@ -1248,14 +1259,14 @@
TEST_F(CowTest, BigSeqOp) {
CowOptions options;
- CowWriter writer(options);
+ CowWriterV2 writer(options, GetCowFd());
const int seq_len = std::numeric_limits<uint16_t>::max() / sizeof(uint32_t) + 1;
uint32_t sequence[seq_len];
for (int i = 0; i < seq_len; i++) {
sequence[i] = i + 1;
}
- ASSERT_TRUE(writer.Initialize(cow_->fd));
+ ASSERT_TRUE(writer.Initialize());
ASSERT_TRUE(writer.AddSequenceData(seq_len, sequence));
ASSERT_TRUE(writer.AddZeroBlocks(1, seq_len));
@@ -1268,26 +1279,26 @@
auto iter = reader.GetRevMergeOpIter();
for (int i = 0; i < seq_len; i++) {
- ASSERT_TRUE(!iter->Done());
+ ASSERT_TRUE(!iter->AtEnd());
const auto& op = iter->Get();
- ASSERT_EQ(op.new_block, seq_len - i);
+ ASSERT_EQ(op->new_block, seq_len - i);
iter->Next();
}
- ASSERT_TRUE(iter->Done());
+ ASSERT_TRUE(iter->AtEnd());
}
TEST_F(CowTest, MissingSeqOp) {
CowOptions options;
- CowWriter writer(options);
+ CowWriterV2 writer(options, GetCowFd());
const int seq_len = 10;
uint32_t sequence[seq_len];
for (int i = 0; i < seq_len; i++) {
sequence[i] = i + 1;
}
- ASSERT_TRUE(writer.Initialize(cow_->fd));
+ ASSERT_TRUE(writer.Initialize());
ASSERT_TRUE(writer.AddSequenceData(seq_len, sequence));
ASSERT_TRUE(writer.AddZeroBlocks(1, seq_len - 1));
@@ -1301,14 +1312,14 @@
TEST_F(CowTest, ResumeSeqOp) {
CowOptions options;
- auto writer = std::make_unique<CowWriter>(options);
+ auto writer = std::make_unique<CowWriterV2>(options, GetCowFd());
const int seq_len = 10;
uint32_t sequence[seq_len];
for (int i = 0; i < seq_len; i++) {
sequence[i] = i + 1;
}
- ASSERT_TRUE(writer->Initialize(cow_->fd));
+ ASSERT_TRUE(writer->Initialize());
ASSERT_TRUE(writer->AddSequenceData(seq_len, sequence));
ASSERT_TRUE(writer->AddZeroBlocks(1, seq_len / 2));
@@ -1319,10 +1330,10 @@
auto reader = std::make_unique<CowReader>();
ASSERT_TRUE(reader->Parse(cow_->fd, 1));
auto itr = reader->GetRevMergeOpIter();
- ASSERT_TRUE(itr->Done());
+ ASSERT_TRUE(itr->AtEnd());
- writer = std::make_unique<CowWriter>(options);
- ASSERT_TRUE(writer->InitializeAppend(cow_->fd, 1));
+ writer = std::make_unique<CowWriterV2>(options, GetCowFd());
+ ASSERT_TRUE(writer->Initialize({1}));
ASSERT_TRUE(writer->AddZeroBlocks(1 + seq_len / 2, seq_len / 2));
ASSERT_TRUE(writer->Finalize());
@@ -1334,27 +1345,27 @@
auto iter = reader->GetRevMergeOpIter();
uint64_t expected_block = 10;
- while (!iter->Done() && expected_block > 0) {
- ASSERT_FALSE(iter->Done());
+ while (!iter->AtEnd() && expected_block > 0) {
+ ASSERT_FALSE(iter->AtEnd());
const auto& op = iter->Get();
- ASSERT_EQ(op.new_block, expected_block);
+ ASSERT_EQ(op->new_block, expected_block);
iter->Next();
expected_block--;
}
ASSERT_EQ(expected_block, 0);
- ASSERT_TRUE(iter->Done());
+ ASSERT_TRUE(iter->AtEnd());
}
TEST_F(CowTest, RevMergeOpItrTest) {
CowOptions options;
options.cluster_ops = 5;
options.num_merge_ops = 1;
- CowWriter writer(options);
+ CowWriterV2 writer(options, GetCowFd());
uint32_t sequence[] = {2, 10, 6, 7, 3, 5};
- ASSERT_TRUE(writer.Initialize(cow_->fd));
+ ASSERT_TRUE(writer.Initialize());
ASSERT_TRUE(writer.AddSequenceData(6, sequence));
ASSERT_TRUE(writer.AddCopy(6, 13));
@@ -1385,25 +1396,25 @@
auto iter = reader.GetRevMergeOpIter();
auto expected_new_block = revMergeOpSequence.begin();
- while (!iter->Done() && expected_new_block != revMergeOpSequence.end()) {
+ while (!iter->AtEnd() && expected_new_block != revMergeOpSequence.end()) {
const auto& op = iter->Get();
- ASSERT_EQ(op.new_block, *expected_new_block);
+ ASSERT_EQ(op->new_block, *expected_new_block);
iter->Next();
expected_new_block++;
}
ASSERT_EQ(expected_new_block, revMergeOpSequence.end());
- ASSERT_TRUE(iter->Done());
+ ASSERT_TRUE(iter->AtEnd());
}
TEST_F(CowTest, LegacyRevMergeOpItrTest) {
CowOptions options;
options.cluster_ops = 5;
options.num_merge_ops = 1;
- CowWriter writer(options);
+ CowWriterV2 writer(options, GetCowFd());
- ASSERT_TRUE(writer.Initialize(cow_->fd));
+ ASSERT_TRUE(writer.Initialize());
ASSERT_TRUE(writer.AddCopy(2, 11));
ASSERT_TRUE(writer.AddCopy(10, 12));
@@ -1434,16 +1445,16 @@
auto iter = reader.GetRevMergeOpIter();
auto expected_new_block = revMergeOpSequence.begin();
- while (!iter->Done() && expected_new_block != revMergeOpSequence.end()) {
+ while (!iter->AtEnd() && expected_new_block != revMergeOpSequence.end()) {
const auto& op = iter->Get();
- ASSERT_EQ(op.new_block, *expected_new_block);
+ ASSERT_EQ(op->new_block, *expected_new_block);
iter->Next();
expected_new_block++;
}
ASSERT_EQ(expected_new_block, revMergeOpSequence.end());
- ASSERT_TRUE(iter->Done());
+ ASSERT_TRUE(iter->AtEnd());
}
TEST_F(CowTest, InvalidMergeOrderTest) {
@@ -1452,10 +1463,10 @@
options.num_merge_ops = 1;
std::string data = "This is some data, believe it";
data.resize(options.block_size, '\0');
- auto writer = std::make_unique<CowWriter>(options);
+ auto writer = std::make_unique<CowWriterV2>(options, GetCowFd());
CowReader reader;
- ASSERT_TRUE(writer->Initialize(cow_->fd));
+ ASSERT_TRUE(writer->Initialize());
ASSERT_TRUE(writer->AddCopy(3, 2));
ASSERT_TRUE(writer->AddCopy(2, 1));
@@ -1464,14 +1475,14 @@
ASSERT_TRUE(reader.Parse(cow_->fd));
ASSERT_TRUE(reader.VerifyMergeOps());
- ASSERT_TRUE(writer->InitializeAppend(cow_->fd, 1));
+ ASSERT_TRUE(writer->Initialize({1}));
ASSERT_TRUE(writer->AddCopy(4, 2));
ASSERT_TRUE(writer->Finalize());
ASSERT_TRUE(reader.Parse(cow_->fd));
ASSERT_FALSE(reader.VerifyMergeOps());
- writer = std::make_unique<CowWriter>(options);
- ASSERT_TRUE(writer->Initialize(cow_->fd));
+ writer = std::make_unique<CowWriterV2>(options, GetCowFd());
+ ASSERT_TRUE(writer->Initialize());
ASSERT_TRUE(writer->AddCopy(2, 1));
ASSERT_TRUE(writer->AddXorBlocks(3, &data, data.size(), 1, 1));
ASSERT_TRUE(writer->Finalize());
diff --git a/fs_mgr/libsnapshot/libsnapshot_cow/writer_base.cpp b/fs_mgr/libsnapshot/libsnapshot_cow/writer_base.cpp
new file mode 100644
index 0000000..ff34c59
--- /dev/null
+++ b/fs_mgr/libsnapshot/libsnapshot_cow/writer_base.cpp
@@ -0,0 +1,195 @@
+// Copyright (C) 2023 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 "writer_base.h"
+
+#include <fcntl.h>
+#include <linux/fs.h>
+#include <sys/ioctl.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+#include <android-base/logging.h>
+#include "snapshot_reader.h"
+
+// The info messages here are spammy, but as useful for update_engine. Disable
+// them when running on the host.
+#ifdef __ANDROID__
+#define LOG_INFO LOG(INFO)
+#else
+#define LOG_INFO LOG(VERBOSE)
+#endif
+
+namespace android {
+namespace snapshot {
+
+using android::base::borrowed_fd;
+using android::base::unique_fd;
+
+namespace {
+std::string GetFdPath(borrowed_fd fd) {
+ const auto fd_path = "/proc/self/fd/" + std::to_string(fd.get());
+ std::string file_path(512, '\0');
+ const auto err = readlink(fd_path.c_str(), file_path.data(), file_path.size());
+ if (err <= 0) {
+ PLOG(ERROR) << "Failed to determine path for fd " << fd.get();
+ file_path.clear();
+ } else {
+ file_path.resize(err);
+ }
+ return file_path;
+}
+} // namespace
+
+CowWriterBase::CowWriterBase(const CowOptions& options, unique_fd&& fd)
+ : options_(options), fd_(std::move(fd)) {}
+
+bool CowWriterBase::InitFd() {
+ if (fd_.get() < 0) {
+ fd_.reset(open("/dev/null", O_RDWR | O_CLOEXEC));
+ if (fd_ < 0) {
+ PLOG(ERROR) << "open /dev/null failed";
+ return false;
+ }
+ is_dev_null_ = true;
+ return true;
+ }
+
+ struct stat stat {};
+ if (fstat(fd_.get(), &stat) < 0) {
+ PLOG(ERROR) << "fstat failed";
+ return false;
+ }
+ const auto file_path = GetFdPath(fd_);
+ is_block_device_ = S_ISBLK(stat.st_mode);
+ if (is_block_device_) {
+ uint64_t size_in_bytes = 0;
+ if (ioctl(fd_.get(), BLKGETSIZE64, &size_in_bytes)) {
+ PLOG(ERROR) << "Failed to get total size for: " << fd_.get();
+ return false;
+ }
+ cow_image_size_ = size_in_bytes;
+ LOG_INFO << "COW image " << file_path << " has size " << size_in_bytes;
+ } else {
+ LOG_INFO << "COW image " << file_path
+ << " is not a block device, assuming unlimited space.";
+ }
+ return true;
+}
+
+bool CowWriterBase::AddCopy(uint64_t new_block, uint64_t old_block, uint64_t num_blocks) {
+ CHECK(num_blocks != 0);
+
+ for (size_t i = 0; i < num_blocks; i++) {
+ if (!ValidateNewBlock(new_block + i)) {
+ return false;
+ }
+ }
+
+ return EmitCopy(new_block, old_block, num_blocks);
+}
+
+bool CowWriterBase::AddRawBlocks(uint64_t new_block_start, const void* data, size_t size) {
+ if (size % options_.block_size != 0) {
+ LOG(ERROR) << "AddRawBlocks: size " << size << " is not a multiple of "
+ << options_.block_size;
+ return false;
+ }
+
+ uint64_t num_blocks = size / options_.block_size;
+ uint64_t last_block = new_block_start + num_blocks - 1;
+ if (!ValidateNewBlock(last_block)) {
+ return false;
+ }
+ return EmitRawBlocks(new_block_start, data, size);
+}
+
+bool CowWriterBase::AddXorBlocks(uint32_t new_block_start, const void* data, size_t size,
+ uint32_t old_block, uint16_t offset) {
+ if (size % options_.block_size != 0) {
+ LOG(ERROR) << "AddRawBlocks: size " << size << " is not a multiple of "
+ << options_.block_size;
+ return false;
+ }
+
+ uint64_t num_blocks = size / options_.block_size;
+ uint64_t last_block = new_block_start + num_blocks - 1;
+ if (!ValidateNewBlock(last_block)) {
+ return false;
+ }
+ if (offset >= options_.block_size) {
+ LOG(ERROR) << "AddXorBlocks: offset " << offset << " is not less than "
+ << options_.block_size;
+ }
+ return EmitXorBlocks(new_block_start, data, size, old_block, offset);
+}
+
+bool CowWriterBase::AddZeroBlocks(uint64_t new_block_start, uint64_t num_blocks) {
+ uint64_t last_block = new_block_start + num_blocks - 1;
+ if (!ValidateNewBlock(last_block)) {
+ return false;
+ }
+ return EmitZeroBlocks(new_block_start, num_blocks);
+}
+
+bool CowWriterBase::AddLabel(uint64_t label) {
+ return EmitLabel(label);
+}
+
+bool CowWriterBase::AddSequenceData(size_t num_ops, const uint32_t* data) {
+ return EmitSequenceData(num_ops, data);
+}
+
+bool CowWriterBase::ValidateNewBlock(uint64_t new_block) {
+ if (options_.max_blocks && new_block >= options_.max_blocks.value()) {
+ LOG(ERROR) << "New block " << new_block << " exceeds maximum block count "
+ << options_.max_blocks.value();
+ return false;
+ }
+ return true;
+}
+
+std::unique_ptr<ICowReader> CowWriterBase::OpenReader() {
+ unique_fd cow_fd(fcntl(fd_.get(), F_DUPFD | F_DUPFD_CLOEXEC, 0));
+ if (cow_fd < 0) {
+ PLOG(ERROR) << "CowWriterV2::OpenReander: dup COW device";
+ return nullptr;
+ }
+
+ auto cow = std::make_unique<CowReader>();
+ if (!cow->Parse(std::move(cow_fd))) {
+ LOG(ERROR) << "CowWriterV2::OpenReader: unable to read COW";
+ return nullptr;
+ }
+ return cow;
+}
+
+std::unique_ptr<chromeos_update_engine::FileDescriptor> CowWriterBase::OpenFileDescriptor(
+ const std::optional<std::string>& source_device) {
+ auto reader = OpenReader();
+ if (!reader) {
+ return nullptr;
+ }
+
+ std::optional<uint64_t> block_dev_size;
+ if (options_.max_blocks) {
+ block_dev_size = {*options_.max_blocks * options_.block_size};
+ }
+
+ return std::make_unique<CompressedSnapshotReader>(std::move(reader), source_device,
+ block_dev_size);
+}
+
+} // namespace snapshot
+} // namespace android
diff --git a/fs_mgr/libsnapshot/libsnapshot_cow/writer_base.h b/fs_mgr/libsnapshot/libsnapshot_cow/writer_base.h
new file mode 100644
index 0000000..c8b4772
--- /dev/null
+++ b/fs_mgr/libsnapshot/libsnapshot_cow/writer_base.h
@@ -0,0 +1,74 @@
+// Copyright (C) 2023 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 <libsnapshot/cow_writer.h>
+
+namespace android {
+namespace snapshot {
+
+class CowWriterBase : public ICowWriter {
+ public:
+ CowWriterBase(const CowOptions& options, android::base::unique_fd&& fd);
+ virtual ~CowWriterBase() {}
+
+ // Set up the writer.
+ // The file starts from the beginning.
+ //
+ // If fd is < 0, the CowWriter will be opened against /dev/null. This is for
+ // computing COW sizes without using storage space.
+ //
+ // If a label is given, any operations after the given label will be dropped.
+ // If the given label is not found, Initialize will fail.
+ virtual bool Initialize(std::optional<uint64_t> label = {}) = 0;
+
+ bool AddCopy(uint64_t new_block, uint64_t old_block, uint64_t num_blocks = 1) override;
+ bool AddRawBlocks(uint64_t new_block_start, const void* data, size_t size) override;
+ bool AddXorBlocks(uint32_t new_block_start, const void* data, size_t size, uint32_t old_block,
+ uint16_t offset) override;
+ bool AddZeroBlocks(uint64_t new_block_start, uint64_t num_blocks) override;
+ bool AddLabel(uint64_t label) override;
+ bool AddSequenceData(size_t num_ops, const uint32_t* data) override;
+ uint32_t GetBlockSize() const override { return options_.block_size; }
+ std::optional<uint32_t> GetMaxBlocks() const override { return options_.max_blocks; }
+ std::unique_ptr<ICowReader> OpenReader() override;
+ std::unique_ptr<FileDescriptor> OpenFileDescriptor(
+ const std::optional<std::string>& source_device) override;
+
+ const CowOptions& options() const { return options_; }
+
+ protected:
+ virtual bool EmitCopy(uint64_t new_block, uint64_t old_block, uint64_t num_blocks = 1) = 0;
+ virtual bool EmitRawBlocks(uint64_t new_block_start, const void* data, size_t size) = 0;
+ virtual bool EmitXorBlocks(uint32_t new_block_start, const void* data, size_t size,
+ uint32_t old_block, uint16_t offset) = 0;
+ virtual bool EmitZeroBlocks(uint64_t new_block_start, uint64_t num_blocks) = 0;
+ virtual bool EmitLabel(uint64_t label) = 0;
+ virtual bool EmitSequenceData(size_t num_ops, const uint32_t* data) = 0;
+
+ bool InitFd();
+ bool ValidateNewBlock(uint64_t new_block);
+
+ CowOptions options_;
+ CowHeader header_{};
+
+ android::base::unique_fd fd_;
+ bool is_dev_null_ = false;
+ bool is_block_device_ = false;
+ uint64_t cow_image_size_ = INT64_MAX;
+};
+
+} // namespace snapshot
+} // namespace android
diff --git a/fs_mgr/libsnapshot/libsnapshot_cow/cow_writer.cpp b/fs_mgr/libsnapshot/libsnapshot_cow/writer_v2.cpp
similarity index 70%
rename from fs_mgr/libsnapshot/libsnapshot_cow/cow_writer.cpp
rename to fs_mgr/libsnapshot/libsnapshot_cow/writer_v2.cpp
index 56b48f0..c549969 100644
--- a/fs_mgr/libsnapshot/libsnapshot_cow/cow_writer.cpp
+++ b/fs_mgr/libsnapshot/libsnapshot_cow/writer_v2.cpp
@@ -14,6 +14,8 @@
// limitations under the License.
//
+#include "writer_v2.h"
+
#include <sys/types.h>
#include <sys/uio.h>
#include <unistd.h>
@@ -37,107 +39,30 @@
#include <sys/ioctl.h>
#include <unistd.h>
+#include "parser_v2.h"
+
+// The info messages here are spammy, but as useful for update_engine. Disable
+// them when running on the host.
+#ifdef __ANDROID__
+#define LOG_INFO LOG(INFO)
+#else
+#define LOG_INFO LOG(VERBOSE)
+#endif
+
namespace android {
namespace snapshot {
-namespace {
-std::string GetFdPath(int fd) {
- const auto fd_path = "/proc/self/fd/" + std::to_string(fd);
- std::string file_path(512, '\0');
- const auto err = readlink(fd_path.c_str(), file_path.data(), file_path.size());
- if (err <= 0) {
- PLOG(ERROR) << "Failed to determine path for fd " << fd;
- file_path.clear();
- } else {
- file_path.resize(err);
- }
- return file_path;
-}
-} // namespace
-
static_assert(sizeof(off_t) == sizeof(uint64_t));
-using android::base::borrowed_fd;
using android::base::unique_fd;
-bool ICowWriter::AddCopy(uint64_t new_block, uint64_t old_block, uint64_t num_blocks) {
- CHECK(num_blocks != 0);
-
- for (size_t i = 0; i < num_blocks; i++) {
- if (!ValidateNewBlock(new_block + i)) {
- return false;
- }
- }
-
- return EmitCopy(new_block, old_block, num_blocks);
-}
-
-bool ICowWriter::AddRawBlocks(uint64_t new_block_start, const void* data, size_t size) {
- if (size % options_.block_size != 0) {
- LOG(ERROR) << "AddRawBlocks: size " << size << " is not a multiple of "
- << options_.block_size;
- return false;
- }
-
- uint64_t num_blocks = size / options_.block_size;
- uint64_t last_block = new_block_start + num_blocks - 1;
- if (!ValidateNewBlock(last_block)) {
- return false;
- }
- return EmitRawBlocks(new_block_start, data, size);
-}
-
-bool ICowWriter::AddXorBlocks(uint32_t new_block_start, const void* data, size_t size,
- uint32_t old_block, uint16_t offset) {
- if (size % options_.block_size != 0) {
- LOG(ERROR) << "AddRawBlocks: size " << size << " is not a multiple of "
- << options_.block_size;
- return false;
- }
-
- uint64_t num_blocks = size / options_.block_size;
- uint64_t last_block = new_block_start + num_blocks - 1;
- if (!ValidateNewBlock(last_block)) {
- return false;
- }
- if (offset >= options_.block_size) {
- LOG(ERROR) << "AddXorBlocks: offset " << offset << " is not less than "
- << options_.block_size;
- }
- return EmitXorBlocks(new_block_start, data, size, old_block, offset);
-}
-
-bool ICowWriter::AddZeroBlocks(uint64_t new_block_start, uint64_t num_blocks) {
- uint64_t last_block = new_block_start + num_blocks - 1;
- if (!ValidateNewBlock(last_block)) {
- return false;
- }
- return EmitZeroBlocks(new_block_start, num_blocks);
-}
-
-bool ICowWriter::AddLabel(uint64_t label) {
- return EmitLabel(label);
-}
-
-bool ICowWriter::AddSequenceData(size_t num_ops, const uint32_t* data) {
- return EmitSequenceData(num_ops, data);
-}
-
-bool ICowWriter::ValidateNewBlock(uint64_t new_block) {
- if (options_.max_blocks && new_block >= options_.max_blocks.value()) {
- LOG(ERROR) << "New block " << new_block << " exceeds maximum block count "
- << options_.max_blocks.value();
- return false;
- }
- return true;
-}
-
-CowWriter::CowWriter(const CowOptions& options) : ICowWriter(options), fd_(-1) {
+CowWriterV2::CowWriterV2(const CowOptions& options, unique_fd&& fd)
+ : CowWriterBase(options, std::move(fd)) {
SetupHeaders();
SetupWriteOptions();
}
-CowWriter::~CowWriter() {
+CowWriterV2::~CowWriterV2() {
for (size_t i = 0; i < compress_threads_.size(); i++) {
CompressWorker* worker = compress_threads_[i].get();
if (worker) {
@@ -156,7 +81,7 @@
compress_threads_.clear();
}
-void CowWriter::SetupWriteOptions() {
+void CowWriterV2::SetupWriteOptions() {
num_compress_threads_ = options_.num_compress_threads;
if (!num_compress_threads_) {
@@ -176,12 +101,12 @@
}
}
-void CowWriter::SetupHeaders() {
+void CowWriterV2::SetupHeaders() {
header_ = {};
- header_.magic = kCowMagicNumber;
- header_.major_version = kCowVersionMajor;
- header_.minor_version = kCowVersionMinor;
- header_.header_size = sizeof(CowHeader);
+ header_.prefix.magic = kCowMagicNumber;
+ header_.prefix.major_version = kCowVersionMajor;
+ header_.prefix.minor_version = kCowVersionMinor;
+ header_.prefix.header_size = sizeof(CowHeader);
header_.footer_size = sizeof(CowFooter);
header_.op_size = sizeof(CowOperation);
header_.block_size = options_.block_size;
@@ -193,19 +118,14 @@
footer_.op.type = kCowFooterOp;
}
-bool CowWriter::ParseOptions() {
- if (options_.compression == "gz") {
- compression_ = kCowCompressGz;
- } else if (options_.compression == "brotli") {
- compression_ = kCowCompressBrotli;
- } else if (options_.compression == "lz4") {
- compression_ = kCowCompressLz4;
- } else if (options_.compression == "none") {
- compression_ = kCowCompressNone;
- } else if (!options_.compression.empty()) {
+bool CowWriterV2::ParseOptions() {
+ auto algorithm = CompressionAlgorithmFromString(options_.compression);
+ if (!algorithm) {
LOG(ERROR) << "unrecognized compression: " << options_.compression;
return false;
}
+ compression_ = *algorithm;
+
if (options_.cluster_ops == 1) {
LOG(ERROR) << "Clusters must contain at least two operations to function.";
return false;
@@ -213,42 +133,7 @@
return true;
}
-bool CowWriter::SetFd(android::base::borrowed_fd fd) {
- if (fd.get() < 0) {
- owned_fd_.reset(open("/dev/null", O_RDWR | O_CLOEXEC));
- if (owned_fd_ < 0) {
- PLOG(ERROR) << "open /dev/null failed";
- return false;
- }
- fd_ = owned_fd_;
- is_dev_null_ = true;
- } else {
- fd_ = fd;
-
- struct stat stat {};
- if (fstat(fd.get(), &stat) < 0) {
- PLOG(ERROR) << "fstat failed";
- return false;
- }
- const auto file_path = GetFdPath(fd.get());
- is_block_device_ = S_ISBLK(stat.st_mode);
- if (is_block_device_) {
- uint64_t size_in_bytes = 0;
- if (ioctl(fd.get(), BLKGETSIZE64, &size_in_bytes)) {
- PLOG(ERROR) << "Failed to get total size for: " << fd.get();
- return false;
- }
- cow_image_size_ = size_in_bytes;
- LOG(INFO) << "COW image " << file_path << " has size " << size_in_bytes;
- } else {
- LOG(INFO) << "COW image " << file_path
- << " is not a block device, assuming unlimited space.";
- }
- }
- return true;
-}
-
-void CowWriter::InitBatchWrites() {
+void CowWriterV2::InitBatchWrites() {
if (batch_write_) {
cowop_vec_ = std::make_unique<struct iovec[]>(header_.cluster_ops);
data_vec_ = std::make_unique<struct iovec[]>(header_.cluster_ops);
@@ -271,12 +156,12 @@
}
std::string batch_write = batch_write_ ? "enabled" : "disabled";
- LOG(INFO) << "Batch writes: " << batch_write;
+ LOG_INFO << "Batch writes: " << batch_write;
}
-void CowWriter::InitWorkers() {
+void CowWriterV2::InitWorkers() {
if (num_compress_threads_ <= 1) {
- LOG(INFO) << "Not creating new threads for compression.";
+ LOG_INFO << "Not creating new threads for compression.";
return;
}
for (int i = 0; i < num_compress_threads_; i++) {
@@ -285,47 +170,30 @@
compress_threads_.push_back(std::move(wt));
}
- LOG(INFO) << num_compress_threads_ << " thread used for compression";
+ LOG_INFO << num_compress_threads_ << " thread used for compression";
}
-bool CowWriter::Initialize(unique_fd&& fd) {
- owned_fd_ = std::move(fd);
- return Initialize(borrowed_fd{owned_fd_});
-}
-
-bool CowWriter::Initialize(borrowed_fd fd) {
- if (!SetFd(fd) || !ParseOptions()) {
+bool CowWriterV2::Initialize(std::optional<uint64_t> label) {
+ if (!InitFd() || !ParseOptions()) {
return false;
}
-
- if (!OpenForWrite()) {
- return false;
+ if (!label) {
+ if (!OpenForWrite()) {
+ return false;
+ }
+ } else {
+ if (!OpenForAppend(*label)) {
+ return false;
+ }
}
- InitWorkers();
+ if (!compress_threads_.size()) {
+ InitWorkers();
+ }
return true;
}
-bool CowWriter::InitializeAppend(android::base::unique_fd&& fd, uint64_t label) {
- owned_fd_ = std::move(fd);
- return InitializeAppend(android::base::borrowed_fd{owned_fd_}, label);
-}
-
-bool CowWriter::InitializeAppend(android::base::borrowed_fd fd, uint64_t label) {
- if (!SetFd(fd) || !ParseOptions()) {
- return false;
- }
-
- bool ret = OpenForAppend(label);
-
- if (ret && !compress_threads_.size()) {
- InitWorkers();
- }
-
- return ret;
-}
-
-void CowWriter::InitPos() {
+void CowWriterV2::InitPos() {
next_op_pos_ = sizeof(header_) + header_.buffer_size;
cluster_size_ = header_.cluster_ops * sizeof(CowOperation);
if (header_.cluster_ops) {
@@ -337,7 +205,7 @@
current_data_size_ = 0;
}
-bool CowWriter::OpenForWrite() {
+bool CowWriterV2::OpenForWrite() {
// This limitation is tied to the data field size in CowOperation.
if (header_.block_size > std::numeric_limits<uint16_t>::max()) {
LOG(ERROR) << "Block size is too large";
@@ -385,11 +253,18 @@
return true;
}
-bool CowWriter::OpenForAppend(uint64_t label) {
- auto reader = std::make_unique<CowReader>();
- std::queue<CowOperation> toAdd;
+bool CowWriterV2::OpenForAppend(uint64_t label) {
+ if (!ReadCowHeader(fd_, &header_)) {
+ return false;
+ }
- if (!reader->Parse(fd_, {label}) || !reader->GetHeader(&header_)) {
+ CowParserV2 parser;
+ if (!parser.Parse(fd_, header_, {label})) {
+ return false;
+ }
+ if (header_.prefix.major_version > 2) {
+ LOG(ERROR) << "CowWriterV2 tried to open incompatible version "
+ << header_.prefix.major_version;
return false;
}
@@ -400,16 +275,10 @@
footer_.op.num_ops = 0;
InitPos();
- auto iter = reader->GetOpIter();
-
- while (!iter->Done()) {
- AddOperation(iter->Get());
- iter->Next();
+ for (const auto& op : *parser.ops()) {
+ AddOperation(op);
}
- // Free reader so we own the descriptor position again.
- reader = nullptr;
-
if (lseek(fd_.get(), next_op_pos_, SEEK_SET) < 0) {
PLOG(ERROR) << "lseek failed";
return false;
@@ -420,7 +289,7 @@
return EmitClusterIfNeeded();
}
-bool CowWriter::EmitCopy(uint64_t new_block, uint64_t old_block, uint64_t num_blocks) {
+bool CowWriterV2::EmitCopy(uint64_t new_block, uint64_t old_block, uint64_t num_blocks) {
CHECK(!merge_in_progress_);
for (size_t i = 0; i < num_blocks; i++) {
@@ -436,16 +305,16 @@
return true;
}
-bool CowWriter::EmitRawBlocks(uint64_t new_block_start, const void* data, size_t size) {
+bool CowWriterV2::EmitRawBlocks(uint64_t new_block_start, const void* data, size_t size) {
return EmitBlocks(new_block_start, data, size, 0, 0, kCowReplaceOp);
}
-bool CowWriter::EmitXorBlocks(uint32_t new_block_start, const void* data, size_t size,
- uint32_t old_block, uint16_t offset) {
+bool CowWriterV2::EmitXorBlocks(uint32_t new_block_start, const void* data, size_t size,
+ uint32_t old_block, uint16_t offset) {
return EmitBlocks(new_block_start, data, size, old_block, offset, kCowXorOp);
}
-bool CowWriter::CompressBlocks(size_t num_blocks, const void* data) {
+bool CowWriterV2::CompressBlocks(size_t num_blocks, const void* data) {
size_t num_threads = (num_blocks == 1) ? 1 : num_compress_threads_;
size_t num_blocks_per_thread = num_blocks / num_threads;
const uint8_t* iter = reinterpret_cast<const uint8_t*>(data);
@@ -479,8 +348,8 @@
return true;
}
-bool CowWriter::EmitBlocks(uint64_t new_block_start, const void* data, size_t size,
- uint64_t old_block, uint16_t offset, uint8_t type) {
+bool CowWriterV2::EmitBlocks(uint64_t new_block_start, const void* data, size_t size,
+ uint64_t old_block, uint16_t offset, uint8_t type) {
CHECK(!merge_in_progress_);
const uint8_t* iter = reinterpret_cast<const uint8_t*>(data);
@@ -554,7 +423,7 @@
return true;
}
-bool CowWriter::EmitZeroBlocks(uint64_t new_block_start, uint64_t num_blocks) {
+bool CowWriterV2::EmitZeroBlocks(uint64_t new_block_start, uint64_t num_blocks) {
CHECK(!merge_in_progress_);
for (uint64_t i = 0; i < num_blocks; i++) {
CowOperation op = {};
@@ -566,7 +435,7 @@
return true;
}
-bool CowWriter::EmitLabel(uint64_t label) {
+bool CowWriterV2::EmitLabel(uint64_t label) {
CHECK(!merge_in_progress_);
CowOperation op = {};
op.type = kCowLabelOp;
@@ -574,7 +443,7 @@
return WriteOperation(op) && Sync();
}
-bool CowWriter::EmitSequenceData(size_t num_ops, const uint32_t* data) {
+bool CowWriterV2::EmitSequenceData(size_t num_ops, const uint32_t* data) {
CHECK(!merge_in_progress_);
size_t to_add = 0;
size_t max_ops = (header_.block_size * 2) / sizeof(uint32_t);
@@ -594,7 +463,7 @@
return true;
}
-bool CowWriter::EmitCluster() {
+bool CowWriterV2::EmitCluster() {
CowOperation op = {};
op.type = kCowClusterOp;
// Next cluster starts after remainder of current cluster and the next data block.
@@ -602,7 +471,7 @@
return WriteOperation(op);
}
-bool CowWriter::EmitClusterIfNeeded() {
+bool CowWriterV2::EmitClusterIfNeeded() {
// If there isn't room for another op and the cluster end op, end the current cluster
if (cluster_size_ && cluster_size_ < current_cluster_size_ + 2 * sizeof(CowOperation)) {
if (!EmitCluster()) return false;
@@ -610,18 +479,7 @@
return true;
}
-// TODO: Fix compilation issues when linking libcrypto library
-// when snapuserd is compiled as part of ramdisk.
-static void SHA256(const void*, size_t, uint8_t[]) {
-#if 0
- SHA256_CTX c;
- SHA256_Init(&c);
- SHA256_Update(&c, data, length);
- SHA256_Final(out, &c);
-#endif
-}
-
-bool CowWriter::Finalize() {
+bool CowWriterV2::Finalize() {
if (!FlushCluster()) {
LOG(ERROR) << "Finalize: FlushCluster() failed";
return false;
@@ -661,10 +519,8 @@
PLOG(ERROR) << "Failed to seek to footer position.";
return false;
}
- memset(&footer_.data.ops_checksum, 0, sizeof(uint8_t) * 32);
- memset(&footer_.data.footer_checksum, 0, sizeof(uint8_t) * 32);
+ memset(&footer_.unused, 0, sizeof(footer_.unused));
- SHA256(&footer_.op, sizeof(footer_.op), footer_.data.footer_checksum);
// Write out footer at end of file
if (!android::base::WriteFully(fd_, reinterpret_cast<const uint8_t*>(&footer_),
sizeof(footer_))) {
@@ -697,7 +553,7 @@
return Sync();
}
-uint64_t CowWriter::GetCowSize() {
+uint64_t CowWriterV2::GetCowSize() {
if (current_data_size_ > 0) {
return next_data_pos_ + sizeof(footer_);
} else {
@@ -705,7 +561,7 @@
}
}
-bool CowWriter::GetDataPos(uint64_t* pos) {
+bool CowWriterV2::GetDataPos(uint64_t* pos) {
off_t offs = lseek(fd_.get(), 0, SEEK_CUR);
if (offs < 0) {
PLOG(ERROR) << "lseek failed";
@@ -715,7 +571,7 @@
return true;
}
-bool CowWriter::EnsureSpaceAvailable(const uint64_t bytes_needed) const {
+bool CowWriterV2::EnsureSpaceAvailable(const uint64_t bytes_needed) const {
if (bytes_needed > cow_image_size_) {
LOG(ERROR) << "No space left on COW device. Required: " << bytes_needed
<< ", available: " << cow_image_size_;
@@ -725,7 +581,7 @@
return true;
}
-bool CowWriter::FlushCluster() {
+bool CowWriterV2::FlushCluster() {
ssize_t ret;
if (op_vec_index_) {
@@ -754,7 +610,7 @@
return true;
}
-bool CowWriter::WriteOperation(const CowOperation& op, const void* data, size_t size) {
+bool CowWriterV2::WriteOperation(const CowOperation& op, const void* data, size_t size) {
if (!EnsureSpaceAvailable(next_op_pos_ + sizeof(op))) {
return false;
}
@@ -802,7 +658,7 @@
return EmitClusterIfNeeded();
}
-void CowWriter::AddOperation(const CowOperation& op) {
+void CowWriterV2::AddOperation(const CowOperation& op) {
footer_.op.num_ops++;
if (op.type == kCowClusterOp) {
@@ -817,14 +673,14 @@
next_op_pos_ += sizeof(CowOperation) + GetNextOpOffset(op, header_.cluster_ops);
}
-bool CowWriter::WriteRawData(const void* data, const size_t size) {
+bool CowWriterV2::WriteRawData(const void* data, const size_t size) {
if (!android::base::WriteFullyAtOffset(fd_, data, size, next_data_pos_)) {
return false;
}
return true;
}
-bool CowWriter::Sync() {
+bool CowWriterV2::Sync() {
if (is_dev_null_) {
return true;
}
@@ -835,7 +691,7 @@
return true;
}
-bool CowWriter::Truncate(off_t length) {
+bool CowWriterV2::Truncate(off_t length) {
if (is_dev_null_ || is_block_device_) {
return true;
}
diff --git a/fs_mgr/libsnapshot/libsnapshot_cow/writer_v2.h b/fs_mgr/libsnapshot/libsnapshot_cow/writer_v2.h
new file mode 100644
index 0000000..809ae57
--- /dev/null
+++ b/fs_mgr/libsnapshot/libsnapshot_cow/writer_v2.h
@@ -0,0 +1,94 @@
+// Copyright (C) 2023 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 "writer_base.h"
+
+namespace android {
+namespace snapshot {
+
+class CowWriterV2 : public CowWriterBase {
+ public:
+ explicit CowWriterV2(const CowOptions& options, android::base::unique_fd&& fd);
+ ~CowWriterV2() override;
+
+ bool Initialize(std::optional<uint64_t> label = {}) override;
+ bool Finalize() override;
+ uint64_t GetCowSize() override;
+
+ protected:
+ virtual bool EmitCopy(uint64_t new_block, uint64_t old_block, uint64_t num_blocks = 1) override;
+ virtual bool EmitRawBlocks(uint64_t new_block_start, const void* data, size_t size) override;
+ virtual bool EmitXorBlocks(uint32_t new_block_start, const void* data, size_t size,
+ uint32_t old_block, uint16_t offset) override;
+ virtual bool EmitZeroBlocks(uint64_t new_block_start, uint64_t num_blocks) override;
+ virtual bool EmitLabel(uint64_t label) override;
+ virtual bool EmitSequenceData(size_t num_ops, const uint32_t* data) override;
+
+ private:
+ bool EmitCluster();
+ bool EmitClusterIfNeeded();
+ bool EmitBlocks(uint64_t new_block_start, const void* data, size_t size, uint64_t old_block,
+ uint16_t offset, uint8_t type);
+ void SetupHeaders();
+ void SetupWriteOptions();
+ bool ParseOptions();
+ bool OpenForWrite();
+ bool OpenForAppend(uint64_t label);
+ bool GetDataPos(uint64_t* pos);
+ bool WriteRawData(const void* data, size_t size);
+ bool WriteOperation(const CowOperation& op, const void* data = nullptr, size_t size = 0);
+ void AddOperation(const CowOperation& op);
+ void InitPos();
+ void InitBatchWrites();
+ void InitWorkers();
+ bool FlushCluster();
+
+ bool CompressBlocks(size_t num_blocks, const void* data);
+ bool Sync();
+ bool Truncate(off_t length);
+ bool EnsureSpaceAvailable(const uint64_t bytes_needed) const;
+
+ private:
+ CowFooter footer_{};
+ CowCompressionAlgorithm compression_ = kCowCompressNone;
+ uint64_t current_op_pos_ = 0;
+ uint64_t next_op_pos_ = 0;
+ uint64_t next_data_pos_ = 0;
+ uint64_t current_data_pos_ = 0;
+ ssize_t total_data_written_ = 0;
+ uint32_t cluster_size_ = 0;
+ uint32_t current_cluster_size_ = 0;
+ uint64_t current_data_size_ = 0;
+ bool merge_in_progress_ = false;
+
+ int num_compress_threads_ = 1;
+ std::vector<std::unique_ptr<CompressWorker>> compress_threads_;
+ std::vector<std::future<bool>> threads_;
+ std::vector<std::basic_string<uint8_t>> compressed_buf_;
+ std::vector<std::basic_string<uint8_t>>::iterator buf_iter_;
+
+ std::vector<std::unique_ptr<CowOperation>> opbuffer_vec_;
+ std::vector<std::unique_ptr<uint8_t[]>> databuffer_vec_;
+ std::unique_ptr<struct iovec[]> cowop_vec_;
+ int op_vec_index_ = 0;
+
+ std::unique_ptr<struct iovec[]> data_vec_;
+ int data_vec_index_ = 0;
+ bool batch_write_ = false;
+};
+
+} // namespace snapshot
+} // namespace android
diff --git a/fs_mgr/libsnapshot/snapshot.cpp b/fs_mgr/libsnapshot/snapshot.cpp
index 64637c2..09d35cf 100644
--- a/fs_mgr/libsnapshot/snapshot.cpp
+++ b/fs_mgr/libsnapshot/snapshot.cpp
@@ -45,9 +45,9 @@
#include <android/snapshot/snapshot.pb.h>
#include <libsnapshot/snapshot_stats.h>
#include "device_info.h"
+#include "libsnapshot_cow/parser_v2.h"
#include "partition_cow_creator.h"
#include "snapshot_metadata_updater.h"
-#include "snapshot_reader.h"
#include "utility.h"
namespace android {
@@ -2490,9 +2490,6 @@
}
created_devices.EmplaceBack<AutoUnmapDevice>(&dm_, name);
- remaining_time = GetRemainingTime(params.timeout_ms, begin);
- if (remaining_time.count() < 0) return false;
-
cow_device = new_cow_device;
}
@@ -2507,6 +2504,9 @@
// the user-space will not start the merge. We have to explicitly inform the
// daemon to resume the merge. Check ProcessUpdateState() call stack.
if (!UpdateUsesUserSnapshots(lock)) {
+ remaining_time = GetRemainingTime(params.timeout_ms, begin);
+ if (remaining_time.count() < 0) return false;
+
std::string path;
if (!MapSnapshot(lock, params.GetPartitionName(), base_device, cow_device, remaining_time,
&path)) {
@@ -3133,6 +3133,7 @@
for (auto&& [name, status] : all_snapshot_status) {
sum += status.cow_file_size();
}
+ LOG(INFO) << "Calculated needed COW space: " << sum << " bytes";
return Return::NoSpace(sum);
}
@@ -3201,39 +3202,20 @@
CHECK(current_metadata->GetBlockDevicePartitionName(0) == LP_METADATA_DEFAULT_PARTITION_NAME &&
target_metadata->GetBlockDevicePartitionName(0) == LP_METADATA_DEFAULT_PARTITION_NAME);
- std::map<std::string, SnapshotStatus> all_snapshot_status;
-
- // In case of error, automatically delete devices that are created along the way.
- // Note that "lock" is destroyed after "created_devices", so it is safe to use |lock| for
- // these devices.
- AutoDeviceList created_devices;
-
const auto& dap_metadata = manifest.dynamic_partition_metadata();
- CowOptions options;
- CowWriter writer(options);
- bool cow_format_support = true;
- if (dap_metadata.cow_version() < writer.GetCowVersion()) {
- cow_format_support = false;
- }
-
- LOG(INFO) << " dap_metadata.cow_version(): " << dap_metadata.cow_version()
- << " writer.GetCowVersion(): " << writer.GetCowVersion();
-
- // Deduce supported features.
- bool userspace_snapshots = CanUseUserspaceSnapshots();
- bool legacy_compression = GetLegacyCompressionEnabledProperty();
std::string vabc_disable_reason;
if (!dap_metadata.vabc_enabled()) {
vabc_disable_reason = "not enabled metadata";
} else if (device_->IsRecovery()) {
vabc_disable_reason = "recovery";
- } else if (!cow_format_support) {
- vabc_disable_reason = "cow format not supported";
} else if (!KernelSupportsCompressedSnapshots()) {
vabc_disable_reason = "kernel missing userspace block device support";
}
+ // Deduce supported features.
+ bool userspace_snapshots = CanUseUserspaceSnapshots();
+ bool legacy_compression = GetLegacyCompressionEnabledProperty();
if (!vabc_disable_reason.empty()) {
if (userspace_snapshots) {
LOG(INFO) << "Userspace snapshots disabled: " << vabc_disable_reason;
@@ -3245,6 +3227,16 @@
legacy_compression = false;
}
+ if (legacy_compression || userspace_snapshots) {
+ if (dap_metadata.cow_version() < kMinCowVersion ||
+ dap_metadata.cow_version() > kMaxCowVersion) {
+ LOG(ERROR) << "Manifest cow version is out of bounds (got: "
+ << dap_metadata.cow_version() << ", min: " << kMinCowVersion
+ << ", max: " << kMaxCowVersion << ")";
+ return Return::Error();
+ }
+ }
+
const bool using_snapuserd = userspace_snapshots || legacy_compression;
if (!using_snapuserd) {
LOG(INFO) << "Using legacy Virtual A/B (dm-snapshot)";
@@ -3277,9 +3269,17 @@
cow_creator.batched_writes = dap_metadata.vabc_feature_set().batch_writes();
}
+ // In case of error, automatically delete devices that are created along the way.
+ // Note that "lock" is destroyed after "created_devices", so it is safe to use |lock| for
+ // these devices.
+ AutoDeviceList created_devices;
+ std::map<std::string, SnapshotStatus> all_snapshot_status;
auto ret = CreateUpdateSnapshotsInternal(lock.get(), manifest, &cow_creator, &created_devices,
&all_snapshot_status);
- if (!ret.is_ok()) return ret;
+ if (!ret.is_ok()) {
+ LOG(ERROR) << "CreateUpdateSnapshotsInternal failed: " << ret.string();
+ return ret;
+ }
auto exported_target_metadata = target_metadata->Export();
if (exported_target_metadata == nullptr) {
@@ -3287,7 +3287,7 @@
return Return::Error();
}
- ret = InitializeUpdateSnapshots(lock.get(), target_metadata.get(),
+ ret = InitializeUpdateSnapshots(lock.get(), dap_metadata.cow_version(), target_metadata.get(),
exported_target_metadata.get(), target_suffix,
all_snapshot_status);
if (!ret.is_ok()) return ret;
@@ -3496,7 +3496,10 @@
// Create the backing COW image if necessary.
if (snapshot_status.cow_file_size() > 0) {
auto ret = CreateCowImage(lock, name);
- if (!ret.is_ok()) return AddRequiredSpace(ret, *all_snapshot_status);
+ if (!ret.is_ok()) {
+ LOG(ERROR) << "CreateCowImage failed: " << ret.string();
+ return AddRequiredSpace(ret, *all_snapshot_status);
+ }
}
LOG(INFO) << "Successfully created snapshot for " << name;
@@ -3506,7 +3509,7 @@
}
Return SnapshotManager::InitializeUpdateSnapshots(
- LockedFile* lock, MetadataBuilder* target_metadata,
+ LockedFile* lock, uint32_t cow_version, MetadataBuilder* target_metadata,
const LpMetadata* exported_target_metadata, const std::string& target_suffix,
const std::map<std::string, SnapshotStatus>& all_snapshot_status) {
CHECK(lock);
@@ -3554,8 +3557,8 @@
}
options.compression = it->second.compression_algorithm();
- CowWriter writer(options);
- if (!writer.Initialize(fd) || !writer.Finalize()) {
+ auto writer = CreateCowWriter(cow_version, options, std::move(fd));
+ if (!writer->Finalize()) {
LOG(ERROR) << "Could not initialize COW device for " << target_partition->name();
return Return::Error();
}
@@ -3605,12 +3608,12 @@
return true;
}
-std::unique_ptr<ISnapshotWriter> SnapshotManager::OpenSnapshotWriter(
+std::unique_ptr<ICowWriter> SnapshotManager::OpenSnapshotWriter(
const android::fs_mgr::CreateLogicalPartitionParams& params,
- const std::optional<std::string>& source_device) {
+ std::optional<uint64_t> label) {
#if defined(LIBSNAPSHOT_NO_COW_WRITE)
(void)params;
- (void)source_device;
+ (void)label;
LOG(ERROR) << "Snapshots cannot be written in first-stage init or recovery";
return nullptr;
@@ -3644,20 +3647,19 @@
return nullptr;
}
- if (status.using_snapuserd()) {
- return OpenCompressedSnapshotWriter(lock.get(), source_device, params.GetPartitionName(),
- status, paths);
+ if (!status.using_snapuserd()) {
+ LOG(ERROR) << "Can only create snapshot writers with userspace or compressed snapshots";
+ return nullptr;
}
- return OpenKernelSnapshotWriter(lock.get(), source_device, params.GetPartitionName(), status,
- paths);
+
+ return OpenCompressedSnapshotWriter(lock.get(), status, paths, label);
#endif
}
#if !defined(LIBSNAPSHOT_NO_COW_WRITE)
-std::unique_ptr<ISnapshotWriter> SnapshotManager::OpenCompressedSnapshotWriter(
- LockedFile* lock, const std::optional<std::string>& source_device,
- [[maybe_unused]] const std::string& partition_name, const SnapshotStatus& status,
- const SnapshotPaths& paths) {
+std::unique_ptr<ICowWriter> SnapshotManager::OpenCompressedSnapshotWriter(
+ LockedFile* lock, const SnapshotStatus& status, const SnapshotPaths& paths,
+ std::optional<uint64_t> label) {
CHECK(lock);
CowOptions cow_options;
@@ -3674,11 +3676,6 @@
// never creates this scenario.
CHECK(status.snapshot_size() == status.device_size());
- auto writer = std::make_unique<CompressedSnapshotWriter>(cow_options);
- if (source_device) {
- writer->SetSourceDevice(*source_device);
- }
-
std::string cow_path;
if (!GetMappedImageDevicePath(paths.cow_device_name, &cow_path)) {
LOG(ERROR) << "Could not determine path for " << paths.cow_device_name;
@@ -3690,40 +3687,14 @@
PLOG(ERROR) << "OpenCompressedSnapshotWriter: open " << cow_path;
return nullptr;
}
- if (!writer->SetCowDevice(std::move(cow_fd))) {
- LOG(ERROR) << "Could not create COW writer from " << cow_path;
+
+ CowHeader header;
+ if (!ReadCowHeader(cow_fd, &header)) {
+ LOG(ERROR) << "OpenCompressedSnapshotWriter: read header failed";
return nullptr;
}
- return writer;
-}
-
-std::unique_ptr<ISnapshotWriter> SnapshotManager::OpenKernelSnapshotWriter(
- LockedFile* lock, const std::optional<std::string>& source_device,
- [[maybe_unused]] const std::string& partition_name, const SnapshotStatus& status,
- const SnapshotPaths& paths) {
- CHECK(lock);
-
- CowOptions cow_options;
- cow_options.max_blocks = {status.device_size() / cow_options.block_size};
-
- auto writer = std::make_unique<OnlineKernelSnapshotWriter>(cow_options);
-
- std::string path = paths.snapshot_device.empty() ? paths.target_device : paths.snapshot_device;
- unique_fd fd(open(path.c_str(), O_RDWR | O_CLOEXEC));
- if (fd < 0) {
- PLOG(ERROR) << "open failed: " << path;
- return nullptr;
- }
-
- if (source_device) {
- writer->SetSourceDevice(*source_device);
- }
-
- uint64_t cow_size = status.cow_partition_size() + status.cow_file_size();
- writer->SetSnapshotDevice(std::move(fd), cow_size);
-
- return writer;
+ return CreateCowWriter(header.prefix.major_version, cow_options, std::move(cow_fd), label);
}
#endif // !defined(LIBSNAPSHOT_NO_COW_WRITE)
diff --git a/fs_mgr/libsnapshot/snapshot_reader.cpp b/fs_mgr/libsnapshot/snapshot_reader.cpp
deleted file mode 100644
index 6546c2a..0000000
--- a/fs_mgr/libsnapshot/snapshot_reader.cpp
+++ /dev/null
@@ -1,355 +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 "snapshot_reader.h"
-
-#include <android-base/file.h>
-#include <android-base/logging.h>
-#include <ext4_utils/ext4_utils.h>
-
-namespace android {
-namespace snapshot {
-
-using android::base::borrowed_fd;
-
-// Not supported.
-bool ReadOnlyFileDescriptor::Open(const char*, int, mode_t) {
- errno = EINVAL;
- return false;
-}
-
-bool ReadOnlyFileDescriptor::Open(const char*, int) {
- errno = EINVAL;
- return false;
-}
-
-ssize_t ReadOnlyFileDescriptor::Write(const void*, size_t) {
- errno = EINVAL;
- return false;
-}
-
-bool ReadOnlyFileDescriptor::BlkIoctl(int, uint64_t, uint64_t, int*) {
- errno = EINVAL;
- return false;
-}
-
-ReadFdFileDescriptor::ReadFdFileDescriptor(android::base::unique_fd&& fd) : fd_(std::move(fd)) {}
-
-ssize_t ReadFdFileDescriptor::Read(void* buf, size_t count) {
- return read(fd_.get(), buf, count);
-}
-
-off64_t ReadFdFileDescriptor::Seek(off64_t offset, int whence) {
- return lseek(fd_.get(), offset, whence);
-}
-
-uint64_t ReadFdFileDescriptor::BlockDevSize() {
- return get_block_device_size(fd_.get());
-}
-
-bool ReadFdFileDescriptor::Close() {
- fd_ = {};
- return true;
-}
-
-bool ReadFdFileDescriptor::IsSettingErrno() {
- return true;
-}
-
-bool ReadFdFileDescriptor::IsOpen() {
- return fd_ >= 0;
-}
-
-bool ReadFdFileDescriptor::Flush() {
- return true;
-}
-
-bool CompressedSnapshotReader::SetCow(std::unique_ptr<CowReader>&& cow) {
- cow_ = std::move(cow);
-
- CowHeader header;
- if (!cow_->GetHeader(&header)) {
- return false;
- }
- block_size_ = header.block_size;
-
- // Populate the operation map.
- op_iter_ = cow_->GetOpIter();
- while (!op_iter_->Done()) {
- const CowOperation* op = &op_iter_->Get();
- if (IsMetadataOp(*op)) {
- op_iter_->Next();
- continue;
- }
- if (op->new_block >= ops_.size()) {
- ops_.resize(op->new_block + 1, nullptr);
- }
- ops_[op->new_block] = op;
- op_iter_->Next();
- }
-
- return true;
-}
-
-void CompressedSnapshotReader::SetSourceDevice(const std::string& source_device) {
- source_device_ = {source_device};
-}
-
-void CompressedSnapshotReader::SetBlockDeviceSize(uint64_t block_device_size) {
- block_device_size_ = block_device_size;
-}
-
-borrowed_fd CompressedSnapshotReader::GetSourceFd() {
- if (source_fd_ < 0) {
- if (!source_device_) {
- LOG(ERROR) << "CompressedSnapshotReader needs source device, but none was set";
- errno = EINVAL;
- return {-1};
- }
- source_fd_.reset(open(source_device_->c_str(), O_RDONLY | O_CLOEXEC));
- if (source_fd_ < 0) {
- PLOG(ERROR) << "open " << *source_device_;
- return {-1};
- }
- }
- return source_fd_;
-}
-
-class MemoryByteSink : public IByteSink {
- public:
- MemoryByteSink(void* buf, size_t count) {
- buf_ = reinterpret_cast<uint8_t*>(buf);
- pos_ = buf_;
- end_ = buf_ + count;
- }
-
- void* GetBuffer(size_t requested, size_t* actual) override {
- *actual = std::min(remaining(), requested);
- if (!*actual) {
- return nullptr;
- }
-
- uint8_t* start = pos_;
- pos_ += *actual;
- return start;
- }
-
- bool ReturnData(void*, size_t) override { return true; }
-
- uint8_t* buf() const { return buf_; }
- uint8_t* pos() const { return pos_; }
- size_t remaining() const { return end_ - pos_; }
-
- private:
- uint8_t* buf_;
- uint8_t* pos_;
- uint8_t* end_;
-};
-
-ssize_t CompressedSnapshotReader::Read(void* buf, size_t count) {
- // Find the start and end chunks, inclusive.
- uint64_t start_chunk = offset_ / block_size_;
- uint64_t end_chunk = (offset_ + count - 1) / block_size_;
-
- // Chop off the first N bytes if the position is not block-aligned.
- size_t start_offset = offset_ % block_size_;
-
- MemoryByteSink sink(buf, count);
-
- size_t initial_bytes = std::min(block_size_ - start_offset, sink.remaining());
- ssize_t rv = ReadBlock(start_chunk, &sink, start_offset, initial_bytes);
- if (rv < 0) {
- return -1;
- }
- offset_ += rv;
-
- for (uint64_t chunk = start_chunk + 1; chunk < end_chunk; chunk++) {
- ssize_t rv = ReadBlock(chunk, &sink, 0);
- if (rv < 0) {
- return -1;
- }
- offset_ += rv;
- }
-
- if (sink.remaining()) {
- ssize_t rv = ReadBlock(end_chunk, &sink, 0, {sink.remaining()});
- if (rv < 0) {
- return -1;
- }
- offset_ += rv;
- }
-
- errno = 0;
-
- DCHECK(sink.pos() - sink.buf() == count);
- return count;
-}
-
-// Discard the first N bytes of a sink request, or any excess bytes.
-class PartialSink : public MemoryByteSink {
- public:
- PartialSink(void* buffer, size_t size, size_t ignore_start)
- : MemoryByteSink(buffer, size), ignore_start_(ignore_start) {}
-
- void* GetBuffer(size_t requested, size_t* actual) override {
- // Throw away the first N bytes if needed.
- if (ignore_start_) {
- *actual = std::min({requested, ignore_start_, sizeof(discard_)});
- ignore_start_ -= *actual;
- return discard_;
- }
- // Throw away any excess bytes if needed.
- if (remaining() == 0) {
- *actual = std::min(requested, sizeof(discard_));
- return discard_;
- }
- return MemoryByteSink::GetBuffer(requested, actual);
- }
-
- private:
- size_t ignore_start_;
- char discard_[BLOCK_SZ];
-};
-
-ssize_t CompressedSnapshotReader::ReadBlock(uint64_t chunk, IByteSink* sink, size_t start_offset,
- const std::optional<uint64_t>& max_bytes) {
- size_t bytes_to_read = block_size_;
- if (max_bytes) {
- bytes_to_read = *max_bytes;
- }
-
- // The offset is relative to the chunk; we should be reading no more than
- // one chunk.
- CHECK(start_offset + bytes_to_read <= block_size_);
-
- const CowOperation* op = nullptr;
- if (chunk < ops_.size()) {
- op = ops_[chunk];
- }
-
- size_t actual;
- void* buffer = sink->GetBuffer(bytes_to_read, &actual);
- if (!buffer || actual < bytes_to_read) {
- // This should never happen unless we calculated the read size wrong
- // somewhere. MemoryByteSink always fulfills the entire requested
- // region unless there's not enough buffer remaining.
- LOG(ERROR) << "Asked for buffer of size " << bytes_to_read << ", got " << actual;
- errno = EINVAL;
- return -1;
- }
-
- if (!op || op->type == kCowCopyOp) {
- borrowed_fd fd = GetSourceFd();
- if (fd < 0) {
- // GetSourceFd sets errno.
- return -1;
- }
-
- if (op) {
- chunk = op->source;
- }
-
- off64_t offset = (chunk * block_size_) + start_offset;
- if (!android::base::ReadFullyAtOffset(fd, buffer, bytes_to_read, offset)) {
- PLOG(ERROR) << "read " << *source_device_;
- // ReadFullyAtOffset sets errno.
- return -1;
- }
- } else if (op->type == kCowZeroOp) {
- memset(buffer, 0, bytes_to_read);
- } else if (op->type == kCowReplaceOp) {
- PartialSink partial_sink(buffer, bytes_to_read, start_offset);
- if (!cow_->ReadData(*op, &partial_sink)) {
- LOG(ERROR) << "CompressedSnapshotReader failed to read replace op";
- errno = EIO;
- return -1;
- }
- } else if (op->type == kCowXorOp) {
- borrowed_fd fd = GetSourceFd();
- if (fd < 0) {
- // GetSourceFd sets errno.
- return -1;
- }
-
- off64_t offset = op->source + start_offset;
- char data[BLOCK_SZ];
- if (!android::base::ReadFullyAtOffset(fd, &data, bytes_to_read, offset)) {
- PLOG(ERROR) << "read " << *source_device_;
- // ReadFullyAtOffset sets errno.
- return -1;
- }
- PartialSink partial_sink(buffer, bytes_to_read, start_offset);
- if (!cow_->ReadData(*op, &partial_sink)) {
- LOG(ERROR) << "CompressedSnapshotReader failed to read xor op";
- errno = EIO;
- return -1;
- }
- for (size_t i = 0; i < bytes_to_read; i++) {
- ((char*)buffer)[i] ^= data[i];
- }
- } else {
- LOG(ERROR) << "CompressedSnapshotReader unknown op type: " << uint32_t(op->type);
- errno = EINVAL;
- return -1;
- }
-
- // MemoryByteSink doesn't do anything in ReturnBuffer, so don't bother calling it.
- return bytes_to_read;
-}
-
-off64_t CompressedSnapshotReader::Seek(off64_t offset, int whence) {
- switch (whence) {
- case SEEK_SET:
- offset_ = offset;
- break;
- case SEEK_END:
- offset_ = static_cast<off64_t>(block_device_size_) + offset;
- break;
- case SEEK_CUR:
- offset_ += offset;
- break;
- default:
- LOG(ERROR) << "Unrecognized seek whence: " << whence;
- errno = EINVAL;
- return -1;
- }
- return offset_;
-}
-
-uint64_t CompressedSnapshotReader::BlockDevSize() {
- return block_device_size_;
-}
-
-bool CompressedSnapshotReader::Close() {
- cow_ = nullptr;
- source_fd_ = {};
- return true;
-}
-
-bool CompressedSnapshotReader::IsSettingErrno() {
- return true;
-}
-
-bool CompressedSnapshotReader::IsOpen() {
- return cow_ != nullptr;
-}
-
-bool CompressedSnapshotReader::Flush() {
- return true;
-}
-
-} // namespace snapshot
-} // namespace android
diff --git a/fs_mgr/libsnapshot/snapshot_stub.cpp b/fs_mgr/libsnapshot/snapshot_stub.cpp
index 84e2226..9354102 100644
--- a/fs_mgr/libsnapshot/snapshot_stub.cpp
+++ b/fs_mgr/libsnapshot/snapshot_stub.cpp
@@ -154,8 +154,8 @@
return &snapshot_merge_stats;
}
-std::unique_ptr<ISnapshotWriter> SnapshotManagerStub::OpenSnapshotWriter(
- const CreateLogicalPartitionParams&, const std::optional<std::string>&) {
+std::unique_ptr<ICowWriter> SnapshotManagerStub::OpenSnapshotWriter(
+ const CreateLogicalPartitionParams&, std::optional<uint64_t>) {
LOG(ERROR) << __FUNCTION__ << " should never be called.";
return nullptr;
}
diff --git a/fs_mgr/libsnapshot/snapshot_test.cpp b/fs_mgr/libsnapshot/snapshot_test.cpp
index 460d49d..0a85489 100644
--- a/fs_mgr/libsnapshot/snapshot_test.cpp
+++ b/fs_mgr/libsnapshot/snapshot_test.cpp
@@ -19,6 +19,7 @@
#include <signal.h>
#include <sys/file.h>
#include <sys/stat.h>
+#include <sys/statvfs.h>
#include <sys/types.h>
#include <chrono>
@@ -196,6 +197,17 @@
return true;
}
+ bool ShouldSkipLegacyMerging() {
+ if (!GetLegacyCompressionEnabledProperty() || !snapuserd_required_) {
+ return false;
+ }
+ int api_level = android::base::GetIntProperty("ro.board.api_level", -1);
+ if (api_level == -1) {
+ api_level = android::base::GetIntProperty("ro.product.first_api_level", -1);
+ }
+ return api_level != __ANDROID_API_S__;
+ }
+
void InitializeState() {
ASSERT_TRUE(sm->EnsureImageManager());
image_manager_ = sm->image_manager();
@@ -333,7 +345,7 @@
}
AssertionResult MapUpdateSnapshot(const std::string& name,
- std::unique_ptr<ISnapshotWriter>* writer) {
+ std::unique_ptr<ICowWriter>* writer) {
TestPartitionOpener opener(fake_super);
CreateLogicalPartitionParams params{
.block_device = fake_super,
@@ -343,14 +355,10 @@
.partition_opener = &opener,
};
- auto old_partition = "/dev/block/mapper/" + GetOtherPartitionName(name);
- auto result = sm->OpenSnapshotWriter(params, {old_partition});
+ auto result = sm->OpenSnapshotWriter(params, {});
if (!result) {
return AssertionFailure() << "Cannot open snapshot for writing: " << name;
}
- if (!result->Initialize()) {
- return AssertionFailure() << "Cannot initialize snapshot for writing: " << name;
- }
if (writer) {
*writer = std::move(result);
@@ -428,7 +436,7 @@
// Prepare A/B slot for a partition named "test_partition".
AssertionResult PrepareOneSnapshot(uint64_t device_size,
- std::unique_ptr<ISnapshotWriter>* writer = nullptr) {
+ std::unique_ptr<ICowWriter>* writer = nullptr) {
lock_ = nullptr;
DeltaArchiveManifest manifest;
@@ -631,21 +639,38 @@
TEST_F(SnapshotTest, Merge) {
ASSERT_TRUE(AcquireLock());
- static const uint64_t kDeviceSize = 1024 * 1024;
-
- std::unique_ptr<ISnapshotWriter> writer;
- ASSERT_TRUE(PrepareOneSnapshot(kDeviceSize, &writer));
-
- bool userspace_snapshots = sm->UpdateUsesUserSnapshots(lock_.get());
-
- // Release the lock.
- lock_ = nullptr;
+ static constexpr uint64_t kDeviceSize = 1024 * 1024;
+ static constexpr uint32_t kBlockSize = 4096;
std::string test_string = "This is a test string.";
- test_string.resize(writer->options().block_size);
- ASSERT_TRUE(writer->AddRawBlocks(0, test_string.data(), test_string.size()));
- ASSERT_TRUE(writer->Finalize());
- writer = nullptr;
+ test_string.resize(kBlockSize);
+
+ bool userspace_snapshots = false;
+ if (snapuserd_required_) {
+ std::unique_ptr<ICowWriter> writer;
+ ASSERT_TRUE(PrepareOneSnapshot(kDeviceSize, &writer));
+
+ userspace_snapshots = sm->UpdateUsesUserSnapshots(lock_.get());
+
+ // Release the lock.
+ lock_ = nullptr;
+
+ ASSERT_TRUE(writer->AddRawBlocks(0, test_string.data(), test_string.size()));
+ ASSERT_TRUE(writer->Finalize());
+ writer = nullptr;
+ } else {
+ ASSERT_TRUE(PrepareOneSnapshot(kDeviceSize));
+
+ // Release the lock.
+ lock_ = nullptr;
+
+ std::string path;
+ ASSERT_TRUE(dm_.GetDmDevicePathByName("test_partition_b", &path));
+
+ unique_fd fd(open(path.c_str(), O_WRONLY));
+ ASSERT_GE(fd, 0);
+ ASSERT_TRUE(android::base::WriteFully(fd, test_string.data(), test_string.size()));
+ }
// Done updating.
ASSERT_TRUE(sm->FinishedSnapshotWrites(false));
@@ -654,6 +679,10 @@
test_device->set_slot_suffix("_b");
ASSERT_TRUE(sm->CreateLogicalAndSnapshotPartitions("super", snapshot_timeout_));
+ if (ShouldSkipLegacyMerging()) {
+ LOG(INFO) << "Skipping legacy merge in test";
+ return;
+ }
ASSERT_TRUE(sm->InitiateMerge());
// The device should have been switched to a snapshot-merge target.
@@ -761,6 +790,10 @@
ASSERT_NE(init, nullptr);
ASSERT_TRUE(init->NeedSnapshotsInFirstStageMount());
ASSERT_TRUE(init->CreateLogicalAndSnapshotPartitions("super", snapshot_timeout_));
+ if (ShouldSkipLegacyMerging()) {
+ LOG(INFO) << "Skipping legacy merge in test";
+ return;
+ }
ASSERT_TRUE(init->InitiateMerge());
// Now, reflash super. Note that we haven't called ProcessUpdateState, so the
@@ -1123,7 +1156,7 @@
AssertionResult MapOneUpdateSnapshot(const std::string& name) {
if (snapuserd_required_) {
- std::unique_ptr<ISnapshotWriter> writer;
+ std::unique_ptr<ICowWriter> writer;
return MapUpdateSnapshot(name, &writer);
} else {
std::string path;
@@ -1144,7 +1177,7 @@
AssertionResult WriteSnapshotAndHash(PartitionUpdate* partition) {
std::string name = partition->partition_name() + "_b";
if (snapuserd_required_) {
- std::unique_ptr<ISnapshotWriter> writer;
+ std::unique_ptr<ICowWriter> writer;
auto res = MapUpdateSnapshot(name, &writer);
if (!res) {
return res;
@@ -1183,13 +1216,13 @@
SHA256_CTX ctx;
SHA256_Init(&ctx);
- if (!writer->options().max_blocks) {
+ if (!writer->GetMaxBlocks()) {
LOG(ERROR) << "CowWriter must specify maximum number of blocks";
return false;
}
- const auto num_blocks = writer->options().max_blocks.value();
+ const auto num_blocks = writer->GetMaxBlocks().value();
- const auto block_size = writer->options().block_size;
+ const auto block_size = writer->GetBlockSize();
std::string block(block_size, '\0');
for (uint64_t i = 0; i < num_blocks; i++) {
if (!ReadFully(rand, block.data(), block.size())) {
@@ -1213,17 +1246,17 @@
// It doesn't really matter the order, we just want copies that reference
// blocks that won't exist if the partition shrinks.
AssertionResult ShiftAllSnapshotBlocks(const std::string& name, uint64_t old_size) {
- std::unique_ptr<ISnapshotWriter> writer;
+ std::unique_ptr<ICowWriter> writer;
if (auto res = MapUpdateSnapshot(name, &writer); !res) {
return res;
}
- if (!writer->options().max_blocks || !*writer->options().max_blocks) {
+ if (!writer->GetMaxBlocks() || !*writer->GetMaxBlocks()) {
return AssertionFailure() << "No max blocks set for " << name << " writer";
}
- uint64_t src_block = (old_size / writer->options().block_size) - 1;
+ uint64_t src_block = (old_size / writer->GetBlockSize()) - 1;
uint64_t dst_block = 0;
- uint64_t max_blocks = *writer->options().max_blocks;
+ uint64_t max_blocks = *writer->GetMaxBlocks();
while (dst_block < max_blocks && dst_block < src_block) {
if (!writer->AddCopy(dst_block, src_block)) {
return AssertionFailure() << "Unable to add copy for " << name << " for blocks "
@@ -1236,7 +1269,13 @@
return AssertionFailure() << "Unable to finalize writer for " << name;
}
- auto hash = HashSnapshot(writer.get());
+ auto old_partition = "/dev/block/mapper/" + GetOtherPartitionName(name);
+ auto reader = writer->OpenFileDescriptor(old_partition);
+ if (!reader) {
+ return AssertionFailure() << "Could not open file descriptor for " << name;
+ }
+
+ auto hash = HashSnapshot(reader.get());
if (hash.empty()) {
return AssertionFailure() << "Unable to hash snapshot writer for " << name;
}
@@ -1344,6 +1383,10 @@
}
// Initiate the merge and wait for it to be completed.
+ if (ShouldSkipLegacyMerging()) {
+ LOG(INFO) << "Skipping legacy merge in test";
+ return;
+ }
ASSERT_TRUE(init->InitiateMerge());
ASSERT_EQ(init->IsSnapuserdRequired(), snapuserd_required_);
{
@@ -1387,7 +1430,7 @@
for (auto* partition : partitions) {
AddOperation(partition);
- std::unique_ptr<ISnapshotWriter> writer;
+ std::unique_ptr<ICowWriter> writer;
auto res = MapUpdateSnapshot(partition->partition_name() + "_b", &writer);
ASSERT_TRUE(res);
ASSERT_TRUE(writer->AddZeroBlocks(0, 1));
@@ -1407,6 +1450,10 @@
ASSERT_TRUE(init->CreateLogicalAndSnapshotPartitions("super", snapshot_timeout_));
// Initiate the merge and wait for it to be completed.
+ if (ShouldSkipLegacyMerging()) {
+ LOG(INFO) << "Skipping legacy merge in test";
+ return;
+ }
ASSERT_TRUE(init->InitiateMerge());
ASSERT_EQ(UpdateState::MergeCompleted, init->ProcessUpdateState());
}
@@ -1476,6 +1523,10 @@
}
// Initiate the merge and wait for it to be completed.
+ if (ShouldSkipLegacyMerging()) {
+ LOG(INFO) << "Skipping legacy merge in test";
+ return;
+ }
ASSERT_TRUE(init->InitiateMerge());
ASSERT_EQ(init->IsSnapuserdRequired(), snapuserd_required_);
{
@@ -1584,6 +1635,10 @@
});
// Initiate the merge and wait for it to be completed.
+ if (ShouldSkipLegacyMerging()) {
+ LOG(INFO) << "Skipping legacy merge in test";
+ return;
+ }
ASSERT_TRUE(init->InitiateMerge());
ASSERT_EQ(init->IsSnapuserdRequired(), snapuserd_required_);
{
@@ -1786,6 +1841,10 @@
// Initiate the merge and wait for it to be completed.
auto new_sm = SnapshotManager::New(new TestDeviceInfo(fake_super, "_b"));
+ if (ShouldSkipLegacyMerging()) {
+ LOG(INFO) << "Skipping legacy merge in test";
+ return;
+ }
ASSERT_TRUE(new_sm->InitiateMerge());
ASSERT_EQ(UpdateState::MergeCompleted, new_sm->ProcessUpdateState());
@@ -1924,6 +1983,10 @@
ASSERT_GE(fd, 0);
// COW cannot be removed due to open fd, so expect a soft failure.
+ if (ShouldSkipLegacyMerging()) {
+ LOG(INFO) << "Skipping legacy merge in test";
+ return;
+ }
ASSERT_TRUE(init->InitiateMerge());
ASSERT_EQ(UpdateState::MergeNeedsReboot, init->ProcessUpdateState());
@@ -2027,6 +2090,10 @@
// Initiate the merge and then immediately stop it to simulate a reboot.
auto new_sm = SnapshotManager::New(new TestDeviceInfo(fake_super, "_b"));
+ if (ShouldSkipLegacyMerging()) {
+ LOG(INFO) << "Skipping legacy merge in test";
+ return;
+ }
ASSERT_TRUE(new_sm->InitiateMerge());
ASSERT_TRUE(UnmapAll());
@@ -2059,6 +2126,10 @@
// Initiate the merge and then immediately stop it to simulate a reboot.
auto new_sm = SnapshotManager::New(new TestDeviceInfo(fake_super, "_b"));
+ if (ShouldSkipLegacyMerging()) {
+ LOG(INFO) << "Skipping legacy merge in test";
+ return;
+ }
ASSERT_TRUE(new_sm->InitiateMerge());
ASSERT_TRUE(UnmapAll());
@@ -2136,6 +2207,10 @@
// Test update package that requests data wipe.
TEST_F(SnapshotUpdateTest, DataWipeRequiredInPackage) {
+ if (ShouldSkipLegacyMerging()) {
+ GTEST_SKIP() << "Skipping legacy merge in test";
+ }
+
AddOperationForPartitions();
// Execute the update.
ASSERT_TRUE(sm->BeginUpdate());
@@ -2175,6 +2250,10 @@
// Test update package that requests data wipe.
TEST_F(SnapshotUpdateTest, DataWipeWithStaleSnapshots) {
+ if (ShouldSkipLegacyMerging()) {
+ GTEST_SKIP() << "Skipping legacy merge in test";
+ }
+
AddOperationForPartitions();
// Execute the update.
@@ -2312,32 +2391,6 @@
<< "FinishedSnapshotWrites should detect overflow of CoW device.";
}
-TEST_F(SnapshotUpdateTest, LowSpace) {
- static constexpr auto kMaxFree = 10_MiB;
- auto userdata = std::make_unique<LowSpaceUserdata>();
- ASSERT_TRUE(userdata->Init(kMaxFree));
-
- // Grow all partitions to 10_MiB, total 30_MiB. This requires 30 MiB of CoW space. After
- // using the empty space in super (< 1 MiB), it uses 30 MiB of /userdata space.
- constexpr uint64_t partition_size = 10_MiB;
- SetSize(sys_, partition_size);
- SetSize(vnd_, partition_size);
- SetSize(prd_, partition_size);
- sys_->set_estimate_cow_size(partition_size);
- vnd_->set_estimate_cow_size(partition_size);
- prd_->set_estimate_cow_size(partition_size);
-
- AddOperationForPartitions();
-
- // Execute the update.
- ASSERT_TRUE(sm->BeginUpdate());
- auto res = sm->CreateUpdateSnapshots(manifest_);
- ASSERT_FALSE(res);
- ASSERT_EQ(Return::ErrorCode::NO_SPACE, res.error_code());
- ASSERT_GE(res.required_size(), 14_MiB);
- ASSERT_LT(res.required_size(), 40_MiB);
-}
-
TEST_F(SnapshotUpdateTest, AddPartition) {
group_->add_partition_names("dlkm");
@@ -2397,6 +2450,10 @@
}
// Initiate the merge and wait for it to be completed.
+ if (ShouldSkipLegacyMerging()) {
+ LOG(INFO) << "Skipping legacy merge in test";
+ return;
+ }
ASSERT_TRUE(init->InitiateMerge());
ASSERT_EQ(UpdateState::MergeCompleted, init->ProcessUpdateState());
@@ -2583,6 +2640,11 @@
ASSERT_TRUE(init->InitiateMerge());
ASSERT_EQ(UpdateState::MergeFailed, init->ProcessUpdateState());
+ if (ShouldSkipLegacyMerging()) {
+ LOG(INFO) << "Skipping legacy merge in test";
+ return;
+ }
+
// Simulate a reboot that tries the merge again, with the non-failing dm.
ASSERT_TRUE(UnmapAll());
init = NewManagerForFirstStageMount("_b");
@@ -2591,6 +2653,24 @@
ASSERT_EQ(UpdateState::MergeCompleted, init->ProcessUpdateState());
}
+TEST_F(SnapshotUpdateTest, BadCowVersion) {
+ if (!snapuserd_required_) {
+ GTEST_SKIP() << "VABC only";
+ }
+
+ ASSERT_TRUE(sm->BeginUpdate());
+
+ auto dynamic_partition_metadata = manifest_.mutable_dynamic_partition_metadata();
+ dynamic_partition_metadata->set_cow_version(kMinCowVersion - 1);
+ ASSERT_FALSE(sm->CreateUpdateSnapshots(manifest_));
+
+ dynamic_partition_metadata->set_cow_version(kMaxCowVersion + 1);
+ ASSERT_FALSE(sm->CreateUpdateSnapshots(manifest_));
+
+ dynamic_partition_metadata->set_cow_version(kMaxCowVersion);
+ ASSERT_TRUE(sm->CreateUpdateSnapshots(manifest_));
+}
+
class FlashAfterUpdateTest : public SnapshotUpdateTest,
public WithParamInterface<std::tuple<uint32_t, bool>> {
public:
@@ -2699,50 +2779,6 @@
"Merge"s;
});
-class ImageManagerTest : public SnapshotTest {
- protected:
- void SetUp() override {
- SKIP_IF_NON_VIRTUAL_AB();
- SnapshotTest::SetUp();
- }
- void TearDown() override {
- RETURN_IF_NON_VIRTUAL_AB();
- CleanUp();
- }
- void CleanUp() {
- if (!image_manager_) {
- return;
- }
- EXPECT_TRUE(!image_manager_->BackingImageExists(kImageName) ||
- image_manager_->DeleteBackingImage(kImageName));
- }
-
- static constexpr const char* kImageName = "my_image";
-};
-
-TEST_F(ImageManagerTest, CreateImageNoSpace) {
- bool at_least_one_failure = false;
- for (uint64_t size = 1_MiB; size <= 512_MiB; size *= 2) {
- auto userdata = std::make_unique<LowSpaceUserdata>();
- ASSERT_TRUE(userdata->Init(size));
-
- uint64_t to_allocate = userdata->free_space() + userdata->bsize();
-
- auto res = image_manager_->CreateBackingImage(kImageName, to_allocate,
- IImageManager::CREATE_IMAGE_DEFAULT);
- if (!res) {
- at_least_one_failure = true;
- } else {
- ASSERT_EQ(res.error_code(), FiemapStatus::ErrorCode::NO_SPACE) << res.string();
- }
-
- CleanUp();
- }
-
- ASSERT_TRUE(at_least_one_failure)
- << "We should have failed to allocate at least one over-sized image";
-}
-
bool Mkdir(const std::string& path) {
if (mkdir(path.c_str(), 0700) && errno != EEXIST) {
std::cerr << "Could not mkdir " << path << ": " << strerror(errno) << std::endl;
diff --git a/fs_mgr/libsnapshot/snapshot_writer.cpp b/fs_mgr/libsnapshot/snapshot_writer.cpp
deleted file mode 100644
index 82a7fd7..0000000
--- a/fs_mgr/libsnapshot/snapshot_writer.cpp
+++ /dev/null
@@ -1,243 +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 <libsnapshot/snapshot_writer.h>
-
-#include <android-base/file.h>
-#include <android-base/logging.h>
-#include <payload_consumer/file_descriptor.h>
-#include "snapshot_reader.h"
-
-namespace android {
-namespace snapshot {
-
-using android::base::borrowed_fd;
-using android::base::unique_fd;
-using chromeos_update_engine::FileDescriptor;
-
-ISnapshotWriter::ISnapshotWriter(const CowOptions& options) : ICowWriter(options) {}
-
-void ISnapshotWriter::SetSourceDevice(const std::string& source_device) {
- source_device_ = {source_device};
-}
-
-borrowed_fd ISnapshotWriter::GetSourceFd() {
- if (!source_device_) {
- LOG(ERROR) << "Attempted to read from source device but none was set";
- return borrowed_fd{-1};
- }
-
- if (source_fd_ < 0) {
- source_fd_.reset(open(source_device_->c_str(), O_RDONLY | O_CLOEXEC));
- if (source_fd_ < 0) {
- PLOG(ERROR) << "open " << *source_device_;
- return borrowed_fd{-1};
- }
- }
- return source_fd_;
-}
-
-CompressedSnapshotWriter::CompressedSnapshotWriter(const CowOptions& options)
- : ISnapshotWriter(options) {}
-
-bool CompressedSnapshotWriter::SetCowDevice(android::base::unique_fd&& cow_device) {
- cow_device_ = std::move(cow_device);
- cow_ = std::make_unique<CowWriter>(options_);
- return true;
-}
-
-bool CompressedSnapshotWriter::Finalize() {
- return cow_->Finalize();
-}
-
-uint64_t CompressedSnapshotWriter::GetCowSize() {
- return cow_->GetCowSize();
-}
-
-std::unique_ptr<CowReader> CompressedSnapshotWriter::OpenCowReader() const {
- unique_fd cow_fd(dup(cow_device_.get()));
- if (cow_fd < 0) {
- PLOG(ERROR) << "dup COW device";
- return nullptr;
- }
-
- auto cow = std::make_unique<CowReader>();
- if (!cow->Parse(std::move(cow_fd))) {
- LOG(ERROR) << "Unable to read COW";
- return nullptr;
- }
- return cow;
-}
-
-bool CompressedSnapshotWriter::VerifyMergeOps() const noexcept {
- auto cow_reader = OpenCowReader();
- if (cow_reader == nullptr) {
- LOG(ERROR) << "Couldn't open CowReader";
- return false;
- }
- return cow_reader->VerifyMergeOps();
-}
-
-std::unique_ptr<FileDescriptor> CompressedSnapshotWriter::OpenReader() {
- auto cow = OpenCowReader();
- if (cow == nullptr) {
- return nullptr;
- }
-
- auto reader = std::make_unique<CompressedSnapshotReader>();
- if (!reader->SetCow(std::move(cow))) {
- LOG(ERROR) << "Unable to initialize COW reader";
- return nullptr;
- }
- if (source_device_) {
- reader->SetSourceDevice(*source_device_);
- }
-
- const auto& cow_options = options();
- if (cow_options.max_blocks) {
- reader->SetBlockDeviceSize(*cow_options.max_blocks * cow_options.block_size);
- }
-
- return reader;
-}
-
-bool CompressedSnapshotWriter::EmitCopy(uint64_t new_block, uint64_t old_block,
- uint64_t num_blocks) {
- return cow_->AddCopy(new_block, old_block, num_blocks);
-}
-
-bool CompressedSnapshotWriter::EmitRawBlocks(uint64_t new_block_start, const void* data,
- size_t size) {
- return cow_->AddRawBlocks(new_block_start, data, size);
-}
-
-bool CompressedSnapshotWriter::EmitXorBlocks(uint32_t new_block_start, const void* data,
- size_t size, uint32_t old_block, uint16_t offset) {
- return cow_->AddXorBlocks(new_block_start, data, size, old_block, offset);
-}
-
-bool CompressedSnapshotWriter::EmitZeroBlocks(uint64_t new_block_start, uint64_t num_blocks) {
- return cow_->AddZeroBlocks(new_block_start, num_blocks);
-}
-
-bool CompressedSnapshotWriter::EmitLabel(uint64_t label) {
- return cow_->AddLabel(label);
-}
-
-bool CompressedSnapshotWriter::EmitSequenceData(size_t num_ops, const uint32_t* data) {
- return cow_->AddSequenceData(num_ops, data);
-}
-
-bool CompressedSnapshotWriter::Initialize() {
- return cow_->Initialize(cow_device_);
-}
-
-bool CompressedSnapshotWriter::InitializeAppend(uint64_t label) {
- return cow_->InitializeAppend(cow_device_, label);
-}
-
-OnlineKernelSnapshotWriter::OnlineKernelSnapshotWriter(const CowOptions& options)
- : ISnapshotWriter(options) {}
-
-void OnlineKernelSnapshotWriter::SetSnapshotDevice(android::base::unique_fd&& snapshot_fd,
- uint64_t cow_size) {
- snapshot_fd_ = std::move(snapshot_fd);
- cow_size_ = cow_size;
-}
-
-bool OnlineKernelSnapshotWriter::Finalize() {
- if (fsync(snapshot_fd_.get()) < 0) {
- PLOG(ERROR) << "fsync";
- return false;
- }
- return true;
-}
-
-bool OnlineKernelSnapshotWriter::EmitRawBlocks(uint64_t new_block_start, const void* data,
- size_t size) {
- uint64_t offset = new_block_start * options_.block_size;
- if (lseek(snapshot_fd_.get(), offset, SEEK_SET) < 0) {
- PLOG(ERROR) << "EmitRawBlocks lseek to offset " << offset;
- return false;
- }
- if (!android::base::WriteFully(snapshot_fd_, data, size)) {
- PLOG(ERROR) << "EmitRawBlocks write";
- return false;
- }
- return true;
-}
-
-bool OnlineKernelSnapshotWriter::EmitXorBlocks(uint32_t, const void*, size_t, uint32_t, uint16_t) {
- LOG(ERROR) << "EmitXorBlocks not implemented.";
- return false;
-}
-
-bool OnlineKernelSnapshotWriter::EmitZeroBlocks(uint64_t new_block_start, uint64_t num_blocks) {
- std::string zeroes(options_.block_size, 0);
- for (uint64_t i = 0; i < num_blocks; i++) {
- if (!EmitRawBlocks(new_block_start + i, zeroes.data(), zeroes.size())) {
- return false;
- }
- }
- return true;
-}
-
-bool OnlineKernelSnapshotWriter::EmitCopy(uint64_t new_block, uint64_t old_block,
- uint64_t num_blocks) {
- auto source_fd = GetSourceFd();
- if (source_fd < 0) {
- return false;
- }
-
- CHECK(num_blocks != 0);
-
- for (size_t i = 0; i < num_blocks; i++) {
- std::string buffer(options_.block_size, 0);
- uint64_t offset = (old_block + i) * options_.block_size;
- if (!android::base::ReadFullyAtOffset(source_fd, buffer.data(), buffer.size(), offset)) {
- PLOG(ERROR) << "EmitCopy read";
- return false;
- }
- if (!EmitRawBlocks(new_block + i, buffer.data(), buffer.size())) {
- PLOG(ERROR) << "EmitRawBlocks failed";
- return false;
- }
- }
-
- return true;
-}
-
-bool OnlineKernelSnapshotWriter::EmitLabel(uint64_t) {
- // Not Needed
- return true;
-}
-
-bool OnlineKernelSnapshotWriter::EmitSequenceData(size_t, const uint32_t*) {
- // Not Needed
- return true;
-}
-
-std::unique_ptr<FileDescriptor> OnlineKernelSnapshotWriter::OpenReader() {
- unique_fd fd(dup(snapshot_fd_.get()));
- if (fd < 0) {
- PLOG(ERROR) << "dup2 failed in OpenReader";
- return nullptr;
- }
- return std::make_unique<ReadFdFileDescriptor>(std::move(fd));
-}
-
-} // namespace snapshot
-} // namespace android
diff --git a/fs_mgr/libsnapshot/snapshot_writer_test.cpp b/fs_mgr/libsnapshot/snapshot_writer_test.cpp
deleted file mode 100644
index a03632b..0000000
--- a/fs_mgr/libsnapshot/snapshot_writer_test.cpp
+++ /dev/null
@@ -1,62 +0,0 @@
-//
-// Copyright (C) 2021 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 <libsnapshot/snapshot.h>
-
-#include <unordered_set>
-
-#include <android-base/file.h>
-#include <gtest/gtest.h>
-#include <libsnapshot/snapshot_writer.h>
-#include <payload_consumer/file_descriptor.h>
-
-namespace android::snapshot {
-class CompressedSnapshotWriterTest : public ::testing::Test {
- public:
- static constexpr size_t BLOCK_SIZE = 4096;
-};
-
-TEST_F(CompressedSnapshotWriterTest, ReadAfterWrite) {
- TemporaryFile cow_device_file{};
- android::snapshot::CowOptions options{.block_size = BLOCK_SIZE};
- android::snapshot::CompressedSnapshotWriter snapshot_writer{options};
- ASSERT_TRUE(snapshot_writer.SetCowDevice(android::base::unique_fd{cow_device_file.fd}));
- ASSERT_TRUE(snapshot_writer.Initialize());
- std::vector<unsigned char> buffer;
- buffer.resize(BLOCK_SIZE);
- std::fill(buffer.begin(), buffer.end(), 123);
-
- ASSERT_TRUE(snapshot_writer.AddRawBlocks(0, buffer.data(), buffer.size()));
- ASSERT_TRUE(snapshot_writer.Finalize());
- auto cow_reader = snapshot_writer.OpenReader();
- ASSERT_NE(cow_reader, nullptr);
- ASSERT_TRUE(snapshot_writer.AddRawBlocks(1, buffer.data(), buffer.size()));
- ASSERT_TRUE(snapshot_writer.AddRawBlocks(2, buffer.data(), buffer.size()));
- ASSERT_TRUE(snapshot_writer.Finalize());
- // After wrigin some data, if we call OpenReader() again, writes should
- // be visible to the newly opened reader. update_engine relies on this
- // behavior for verity writes.
- cow_reader = snapshot_writer.OpenReader();
- ASSERT_NE(cow_reader, nullptr);
- std::vector<unsigned char> read_back;
- read_back.resize(buffer.size());
- cow_reader->Seek(BLOCK_SIZE, SEEK_SET);
- const auto bytes_read = cow_reader->Read(read_back.data(), read_back.size());
- ASSERT_EQ((size_t)(bytes_read), BLOCK_SIZE);
- ASSERT_EQ(read_back, buffer);
-}
-
-} // namespace android::snapshot
diff --git a/fs_mgr/libsnapshot/snapshotctl.cpp b/fs_mgr/libsnapshot/snapshotctl.cpp
index ad3f83c..38eb719 100644
--- a/fs_mgr/libsnapshot/snapshotctl.cpp
+++ b/fs_mgr/libsnapshot/snapshotctl.cpp
@@ -133,7 +133,6 @@
// Write the "new" system partition.
auto system_target_name = "system" + target_slot;
- auto source_device = "/dev/block/mapper/" + system_source_name;
CreateLogicalPartitionParams clpp = {
.block_device = fs_mgr_get_super_partition_name(target_slot_number),
.metadata_slot = {target_slot_number},
@@ -141,15 +140,11 @@
.partition_opener = &opener,
.timeout_ms = 10s,
};
- auto writer = sm->OpenSnapshotWriter(clpp, {source_device});
+ auto writer = sm->OpenSnapshotWriter(clpp, std::nullopt);
if (!writer) {
std::cerr << "Could not open snapshot writer.\n";
return false;
}
- if (!writer->Initialize()) {
- std::cerr << "Could not initialize snapshot for writing.\n";
- return false;
- }
for (uint64_t block = 0; block < system_source_size / 4096; block++) {
if (!writer->AddCopy(block, block)) {
diff --git a/fs_mgr/libsnapshot/snapuserd/Android.bp b/fs_mgr/libsnapshot/snapuserd/Android.bp
index 9261482..a9b96e2 100644
--- a/fs_mgr/libsnapshot/snapuserd/Android.bp
+++ b/fs_mgr/libsnapshot/snapuserd/Android.bp
@@ -57,17 +57,20 @@
defaults: [
"fs_mgr_defaults",
],
+ local_include_dirs: ["include/"],
srcs: [
"dm-snapshot-merge/snapuserd.cpp",
"dm-snapshot-merge/snapuserd_worker.cpp",
"dm-snapshot-merge/snapuserd_readahead.cpp",
"snapuserd_buffer.cpp",
+ "user-space-merge/handler_manager.cpp",
+ "user-space-merge/read_worker.cpp",
"user-space-merge/snapuserd_core.cpp",
- "user-space-merge/snapuserd_dm_user.cpp",
"user-space-merge/snapuserd_merge.cpp",
"user-space-merge/snapuserd_readahead.cpp",
"user-space-merge/snapuserd_transitions.cpp",
"user-space-merge/snapuserd_verify.cpp",
+ "user-space-merge/worker.cpp",
],
static_libs: [
"libbase",
@@ -111,6 +114,7 @@
"liblz4",
"libext4_utils",
"liburing",
+ "libzstd",
],
header_libs: [
@@ -163,7 +167,7 @@
}
cc_test {
- name: "cow_snapuserd_test",
+ name: "snapuserd_test_legacy",
defaults: [
"fs_mgr_defaults",
"libsnapshot_cow_defaults",
@@ -215,16 +219,17 @@
],
static_libs: [
"libbrotli",
+ "libcutils_sockets",
+ "libdm",
+ "libext4_utils",
+ "libfs_mgr",
+ "libgflags",
"libgtest",
"libsnapshot_cow",
"libsnapshot_snapuserd",
- "libcutils_sockets",
- "libz",
- "libfs_mgr",
- "libdm",
- "libext4_utils",
+ "libsnapuserd",
"liburing",
- "libgflags",
+ "libz",
],
include_dirs: ["bionic/libc/kernel"],
header_libs: [
diff --git a/fs_mgr/libsnapshot/snapuserd/dm-snapshot-merge/cow_snapuserd_test.cpp b/fs_mgr/libsnapshot/snapuserd/dm-snapshot-merge/cow_snapuserd_test.cpp
index 3c4ab2e..737c480 100644
--- a/fs_mgr/libsnapshot/snapuserd/dm-snapshot-merge/cow_snapuserd_test.cpp
+++ b/fs_mgr/libsnapshot/snapuserd/dm-snapshot-merge/cow_snapuserd_test.cpp
@@ -122,6 +122,7 @@
void SimulateDaemonRestart();
void StartMerge();
+ std::unique_ptr<ICowWriter> CreateCowDeviceInternal();
void CreateCowDevice();
void CreateCowDeviceOrderedOps();
void CreateCowDeviceOrderedOpsInverted();
@@ -164,6 +165,7 @@
private:
void InitMetadata();
+ std::unique_ptr<ICowWriter> CreateCowDeviceInternal();
void CreateCowDevice();
void CreateCowPartialFilledArea();
@@ -258,6 +260,19 @@
}
}
+std::unique_ptr<ICowWriter> CowSnapuserdTest::CreateCowDeviceInternal() {
+ std::string path = android::base::GetExecutableDirectory();
+ cow_system_ = std::make_unique<TemporaryFile>(path);
+
+ CowOptions options;
+ options.compression = "gz";
+
+ unique_fd fd(cow_system_->fd);
+ cow_system_->fd = -1;
+
+ return CreateCowWriter(kDefaultCowVersion, options, std::move(fd));
+}
+
void CowSnapuserdTest::ReadLastBlock() {
unique_fd rnd_fd;
total_base_size_ = BLOCK_SZ * 2;
@@ -280,9 +295,6 @@
base_loop_ = std::make_unique<LoopDevice>(base_fd_, 10s);
ASSERT_TRUE(base_loop_->valid());
- std::string path = android::base::GetExecutableDirectory();
- cow_system_ = std::make_unique<TemporaryFile>(path);
-
std::unique_ptr<uint8_t[]> random_buffer_1_ = std::make_unique<uint8_t[]>(total_base_size_);
loff_t offset = 0;
@@ -294,16 +306,13 @@
offset += BLOCK_SZ;
}
- CowOptions options;
- options.compression = "gz";
- CowWriter writer(options);
+ auto writer = CreateCowDeviceInternal();
+ ASSERT_NE(writer, nullptr);
- ASSERT_TRUE(writer.Initialize(cow_system_->fd));
+ ASSERT_TRUE(writer->AddRawBlocks(0, random_buffer_1_.get(), BLOCK_SZ));
+ ASSERT_TRUE(writer->AddRawBlocks(1, (char*)random_buffer_1_.get() + BLOCK_SZ, BLOCK_SZ));
- ASSERT_TRUE(writer.AddRawBlocks(0, random_buffer_1_.get(), BLOCK_SZ));
- ASSERT_TRUE(writer.AddRawBlocks(1, (char*)random_buffer_1_.get() + BLOCK_SZ, BLOCK_SZ));
-
- ASSERT_TRUE(writer.Finalize());
+ ASSERT_TRUE(writer->Finalize());
SetDeviceControlName();
@@ -381,22 +390,16 @@
}
void CowSnapuserdTest::CreateCowDeviceWithCopyOverlap_2() {
- std::string path = android::base::GetExecutableDirectory();
- cow_system_ = std::make_unique<TemporaryFile>(path);
+ auto writer = CreateCowDeviceInternal();
+ ASSERT_NE(writer, nullptr);
- CowOptions options;
- options.compression = "gz";
- CowWriter writer(options);
-
- ASSERT_TRUE(writer.Initialize(cow_system_->fd));
-
- size_t num_blocks = size_ / options.block_size;
+ size_t num_blocks = size_ / writer->GetBlockSize();
size_t x = num_blocks;
size_t blk_src_copy = 0;
// Create overlapping copy operations
while (1) {
- ASSERT_TRUE(writer.AddCopy(blk_src_copy, blk_src_copy + 1));
+ ASSERT_TRUE(writer->AddCopy(blk_src_copy, blk_src_copy + 1));
x -= 1;
if (x == 1) {
break;
@@ -405,7 +408,7 @@
}
// Flush operations
- ASSERT_TRUE(writer.Finalize());
+ ASSERT_TRUE(writer->Finalize());
// Construct the buffer required for validation
orig_buffer_ = std::make_unique<uint8_t[]>(total_base_size_);
@@ -433,22 +436,16 @@
}
void CowSnapuserdTest::CreateCowDeviceWithCopyOverlap_1() {
- std::string path = android::base::GetExecutableDirectory();
- cow_system_ = std::make_unique<TemporaryFile>(path);
+ auto writer = CreateCowDeviceInternal();
+ ASSERT_NE(writer, nullptr);
- CowOptions options;
- options.compression = "gz";
- CowWriter writer(options);
-
- ASSERT_TRUE(writer.Initialize(cow_system_->fd));
-
- size_t num_blocks = size_ / options.block_size;
+ size_t num_blocks = size_ / writer->GetBlockSize();
size_t x = num_blocks;
size_t blk_src_copy = num_blocks - 1;
// Create overlapping copy operations
while (1) {
- ASSERT_TRUE(writer.AddCopy(blk_src_copy + 1, blk_src_copy));
+ ASSERT_TRUE(writer->AddCopy(blk_src_copy + 1, blk_src_copy));
x -= 1;
if (x == 0) {
ASSERT_EQ(blk_src_copy, 0);
@@ -458,7 +455,7 @@
}
// Flush operations
- ASSERT_TRUE(writer.Finalize());
+ ASSERT_TRUE(writer->Finalize());
// Construct the buffer required for validation
orig_buffer_ = std::make_unique<uint8_t[]>(total_base_size_);
@@ -468,10 +465,11 @@
true);
// Merged operations
- ASSERT_EQ(android::base::ReadFullyAtOffset(base_fd_, orig_buffer_.get(), options.block_size, 0),
+ ASSERT_EQ(android::base::ReadFullyAtOffset(base_fd_, orig_buffer_.get(), writer->GetBlockSize(),
+ 0),
true);
ASSERT_EQ(android::base::ReadFullyAtOffset(
- base_fd_, (char*)orig_buffer_.get() + options.block_size, size_, 0),
+ base_fd_, (char*)orig_buffer_.get() + writer->GetBlockSize(), size_, 0),
true);
}
@@ -479,8 +477,8 @@
unique_fd rnd_fd;
loff_t offset = 0;
- std::string path = android::base::GetExecutableDirectory();
- cow_system_ = std::make_unique<TemporaryFile>(path);
+ auto writer = CreateCowDeviceInternal();
+ ASSERT_NE(writer, nullptr);
rnd_fd.reset(open("/dev/random", O_RDONLY));
ASSERT_TRUE(rnd_fd > 0);
@@ -495,13 +493,7 @@
offset += 1_MiB;
}
- CowOptions options;
- options.compression = "gz";
- CowWriter writer(options);
-
- ASSERT_TRUE(writer.Initialize(cow_system_->fd));
-
- size_t num_blocks = size_ / options.block_size;
+ size_t num_blocks = size_ / writer->GetBlockSize();
size_t blk_end_copy = num_blocks * 3;
size_t source_blk = num_blocks - 1;
size_t blk_src_copy = blk_end_copy - 1;
@@ -509,7 +501,7 @@
size_t x = num_blocks;
while (1) {
- ASSERT_TRUE(writer.AddCopy(source_blk, blk_src_copy));
+ ASSERT_TRUE(writer->AddCopy(source_blk, blk_src_copy));
x -= 1;
if (x == 0) {
break;
@@ -519,12 +511,12 @@
}
for (size_t i = num_blocks; i > 0; i--) {
- ASSERT_TRUE(writer.AddXorBlocks(num_blocks + i - 1,
- &random_buffer_1_.get()[options.block_size * (i - 1)],
- options.block_size, 2 * num_blocks + i - 1, xor_offset));
+ ASSERT_TRUE(writer->AddXorBlocks(
+ num_blocks + i - 1, &random_buffer_1_.get()[writer->GetBlockSize() * (i - 1)],
+ writer->GetBlockSize(), 2 * num_blocks + i - 1, xor_offset));
}
// Flush operations
- ASSERT_TRUE(writer.Finalize());
+ ASSERT_TRUE(writer->Finalize());
// Construct the buffer required for validation
orig_buffer_ = std::make_unique<uint8_t[]>(total_base_size_);
// Read the entire base device
@@ -542,8 +534,8 @@
unique_fd rnd_fd;
loff_t offset = 0;
- std::string path = android::base::GetExecutableDirectory();
- cow_system_ = std::make_unique<TemporaryFile>(path);
+ auto writer = CreateCowDeviceInternal();
+ ASSERT_NE(writer, nullptr);
rnd_fd.reset(open("/dev/random", O_RDONLY));
ASSERT_TRUE(rnd_fd > 0);
@@ -559,20 +551,14 @@
}
memset(random_buffer_1_.get(), 0, size_);
- CowOptions options;
- options.compression = "gz";
- CowWriter writer(options);
-
- ASSERT_TRUE(writer.Initialize(cow_system_->fd));
-
- size_t num_blocks = size_ / options.block_size;
+ size_t num_blocks = size_ / writer->GetBlockSize();
size_t x = num_blocks;
size_t source_blk = 0;
size_t blk_src_copy = 2 * num_blocks;
uint16_t xor_offset = 5;
while (1) {
- ASSERT_TRUE(writer.AddCopy(source_blk, blk_src_copy));
+ ASSERT_TRUE(writer->AddCopy(source_blk, blk_src_copy));
x -= 1;
if (x == 0) {
@@ -582,10 +568,10 @@
blk_src_copy += 1;
}
- ASSERT_TRUE(writer.AddXorBlocks(num_blocks, random_buffer_1_.get(), size_, 2 * num_blocks,
- xor_offset));
+ ASSERT_TRUE(writer->AddXorBlocks(num_blocks, random_buffer_1_.get(), size_, 2 * num_blocks,
+ xor_offset));
// Flush operations
- ASSERT_TRUE(writer.Finalize());
+ ASSERT_TRUE(writer->Finalize());
// Construct the buffer required for validation
orig_buffer_ = std::make_unique<uint8_t[]>(total_base_size_);
// Read the entire base device
@@ -603,8 +589,8 @@
unique_fd rnd_fd;
loff_t offset = 0;
- std::string path = android::base::GetExecutableDirectory();
- cow_system_ = std::make_unique<TemporaryFile>(path);
+ auto writer = CreateCowDeviceInternal();
+ ASSERT_NE(writer, nullptr);
rnd_fd.reset(open("/dev/random", O_RDONLY));
ASSERT_TRUE(rnd_fd > 0);
@@ -619,13 +605,7 @@
offset += 1_MiB;
}
- CowOptions options;
- options.compression = "gz";
- CowWriter writer(options);
-
- ASSERT_TRUE(writer.Initialize(cow_system_->fd));
-
- size_t num_blocks = size_ / options.block_size;
+ size_t num_blocks = size_ / writer->GetBlockSize();
size_t blk_end_copy = num_blocks * 2;
size_t source_blk = num_blocks - 1;
size_t blk_src_copy = blk_end_copy - 1;
@@ -639,11 +619,11 @@
for (int i = 0; i < num_blocks; i++) {
sequence[num_blocks + i] = 5 * num_blocks - 1 - i;
}
- ASSERT_TRUE(writer.AddSequenceData(2 * num_blocks, sequence));
+ ASSERT_TRUE(writer->AddSequenceData(2 * num_blocks, sequence));
size_t x = num_blocks;
while (1) {
- ASSERT_TRUE(writer.AddCopy(source_blk, blk_src_copy));
+ ASSERT_TRUE(writer->AddCopy(source_blk, blk_src_copy));
x -= 1;
if (x == 0) {
break;
@@ -655,24 +635,24 @@
source_blk = num_blocks;
blk_src_copy = blk_end_copy;
- ASSERT_TRUE(writer.AddRawBlocks(source_blk, random_buffer_1_.get(), size_));
+ ASSERT_TRUE(writer->AddRawBlocks(source_blk, random_buffer_1_.get(), size_));
size_t blk_zero_copy_start = source_blk + num_blocks;
size_t blk_zero_copy_end = blk_zero_copy_start + num_blocks;
- ASSERT_TRUE(writer.AddZeroBlocks(blk_zero_copy_start, num_blocks));
+ ASSERT_TRUE(writer->AddZeroBlocks(blk_zero_copy_start, num_blocks));
size_t blk_random2_replace_start = blk_zero_copy_end;
- ASSERT_TRUE(writer.AddRawBlocks(blk_random2_replace_start, random_buffer_1_.get(), size_));
+ ASSERT_TRUE(writer->AddRawBlocks(blk_random2_replace_start, random_buffer_1_.get(), size_));
size_t blk_xor_start = blk_random2_replace_start + num_blocks;
size_t xor_offset = BLOCK_SZ / 2;
- ASSERT_TRUE(writer.AddXorBlocks(blk_xor_start, random_buffer_1_.get(), size_, num_blocks,
- xor_offset));
+ ASSERT_TRUE(writer->AddXorBlocks(blk_xor_start, random_buffer_1_.get(), size_, num_blocks,
+ xor_offset));
// Flush operations
- ASSERT_TRUE(writer.Finalize());
+ ASSERT_TRUE(writer->Finalize());
// Construct the buffer required for validation
orig_buffer_ = std::make_unique<uint8_t[]>(total_base_size_);
std::string zero_buffer(size_, 0);
@@ -902,29 +882,36 @@
ASSERT_TRUE(Merge());
}
-void CowSnapuserdMetadataTest::CreateCowPartialFilledArea() {
+std::unique_ptr<ICowWriter> CowSnapuserdMetadataTest::CreateCowDeviceInternal() {
std::string path = android::base::GetExecutableDirectory();
cow_system_ = std::make_unique<TemporaryFile>(path);
CowOptions options;
options.compression = "gz";
- CowWriter writer(options);
- ASSERT_TRUE(writer.Initialize(cow_system_->fd));
+ unique_fd fd(cow_system_->fd);
+ cow_system_->fd = -1;
+
+ return CreateCowWriter(kDefaultCowVersion, options, std::move(fd));
+}
+
+void CowSnapuserdMetadataTest::CreateCowPartialFilledArea() {
+ auto writer = CreateCowDeviceInternal();
+ ASSERT_NE(writer, nullptr);
// Area 0 is completely filled with 256 exceptions
for (int i = 0; i < 256; i++) {
- ASSERT_TRUE(writer.AddCopy(i, 256 + i));
+ ASSERT_TRUE(writer->AddCopy(i, 256 + i));
}
// Area 1 is partially filled with 2 copy ops and 10 zero ops
- ASSERT_TRUE(writer.AddCopy(500, 1000));
- ASSERT_TRUE(writer.AddCopy(501, 1001));
+ ASSERT_TRUE(writer->AddCopy(500, 1000));
+ ASSERT_TRUE(writer->AddCopy(501, 1001));
- ASSERT_TRUE(writer.AddZeroBlocks(300, 10));
+ ASSERT_TRUE(writer->AddZeroBlocks(300, 10));
// Flush operations
- ASSERT_TRUE(writer.Finalize());
+ ASSERT_TRUE(writer->Finalize());
}
void CowSnapuserdMetadataTest::ValidatePartialFilledArea() {
@@ -956,8 +943,8 @@
unique_fd rnd_fd;
loff_t offset = 0;
- std::string path = android::base::GetExecutableDirectory();
- cow_system_ = std::make_unique<TemporaryFile>(path);
+ auto writer = CreateCowDeviceInternal();
+ ASSERT_NE(writer, nullptr);
rnd_fd.reset(open("/dev/random", O_RDONLY));
ASSERT_TRUE(rnd_fd > 0);
@@ -972,50 +959,44 @@
offset += 1_MiB;
}
- CowOptions options;
- options.compression = "gz";
- CowWriter writer(options);
-
- ASSERT_TRUE(writer.Initialize(cow_system_->fd));
-
- size_t num_blocks = size_ / options.block_size;
+ size_t num_blocks = size_ / writer->GetBlockSize();
// Overlapping region. This has to be split
// into two batch operations
- ASSERT_TRUE(writer.AddCopy(23, 20));
- ASSERT_TRUE(writer.AddCopy(22, 19));
- ASSERT_TRUE(writer.AddCopy(21, 18));
- ASSERT_TRUE(writer.AddCopy(20, 17));
- ASSERT_TRUE(writer.AddCopy(19, 16));
- ASSERT_TRUE(writer.AddCopy(18, 15));
+ ASSERT_TRUE(writer->AddCopy(23, 20));
+ ASSERT_TRUE(writer->AddCopy(22, 19));
+ ASSERT_TRUE(writer->AddCopy(21, 18));
+ ASSERT_TRUE(writer->AddCopy(20, 17));
+ ASSERT_TRUE(writer->AddCopy(19, 16));
+ ASSERT_TRUE(writer->AddCopy(18, 15));
// Contiguous region but blocks in ascending order
// Daemon has to ensure that these blocks are merged
// in a batch
- ASSERT_TRUE(writer.AddCopy(50, 75));
- ASSERT_TRUE(writer.AddCopy(51, 76));
- ASSERT_TRUE(writer.AddCopy(52, 77));
- ASSERT_TRUE(writer.AddCopy(53, 78));
+ ASSERT_TRUE(writer->AddCopy(50, 75));
+ ASSERT_TRUE(writer->AddCopy(51, 76));
+ ASSERT_TRUE(writer->AddCopy(52, 77));
+ ASSERT_TRUE(writer->AddCopy(53, 78));
// Dis-contiguous region
- ASSERT_TRUE(writer.AddCopy(110, 130));
- ASSERT_TRUE(writer.AddCopy(105, 125));
- ASSERT_TRUE(writer.AddCopy(100, 120));
+ ASSERT_TRUE(writer->AddCopy(110, 130));
+ ASSERT_TRUE(writer->AddCopy(105, 125));
+ ASSERT_TRUE(writer->AddCopy(100, 120));
// Overlap
- ASSERT_TRUE(writer.AddCopy(25, 30));
- ASSERT_TRUE(writer.AddCopy(30, 31));
+ ASSERT_TRUE(writer->AddCopy(25, 30));
+ ASSERT_TRUE(writer->AddCopy(30, 31));
size_t source_blk = num_blocks;
- ASSERT_TRUE(writer.AddRawBlocks(source_blk, random_buffer_1_.get(), size_));
+ ASSERT_TRUE(writer->AddRawBlocks(source_blk, random_buffer_1_.get(), size_));
size_t blk_zero_copy_start = source_blk + num_blocks;
- ASSERT_TRUE(writer.AddZeroBlocks(blk_zero_copy_start, num_blocks));
+ ASSERT_TRUE(writer->AddZeroBlocks(blk_zero_copy_start, num_blocks));
// Flush operations
- ASSERT_TRUE(writer.Finalize());
+ ASSERT_TRUE(writer->Finalize());
}
void CowSnapuserdMetadataTest::InitMetadata() {
diff --git a/fs_mgr/libsnapshot/snapuserd/dm-snapshot-merge/snapuserd.cpp b/fs_mgr/libsnapshot/snapuserd/dm-snapshot-merge/snapuserd.cpp
index 8926030..71664bf 100644
--- a/fs_mgr/libsnapshot/snapuserd/dm-snapshot-merge/snapuserd.cpp
+++ b/fs_mgr/libsnapshot/snapuserd/dm-snapshot-merge/snapuserd.cpp
@@ -347,7 +347,6 @@
*/
bool Snapuserd::ReadMetadata() {
reader_ = std::make_unique<CowReader>();
- CowHeader header;
CowOptions options;
bool metadata_found = false;
int replace_ops = 0, zero_ops = 0, copy_ops = 0;
@@ -359,11 +358,7 @@
return false;
}
- if (!reader_->GetHeader(&header)) {
- SNAP_LOG(ERROR) << "Failed to get header";
- return false;
- }
-
+ const auto& header = reader_->GetHeader();
if (!(header.block_size == BLOCK_SZ)) {
SNAP_LOG(ERROR) << "Invalid header block size found: " << header.block_size;
return false;
@@ -395,8 +390,8 @@
// this memset will ensure that metadata read is completed.
memset(de_ptr.get(), 0, (exceptions_per_area_ * sizeof(struct disk_exception)));
- while (!cowop_rm_iter->Done()) {
- const CowOperation* cow_op = &cowop_rm_iter->Get();
+ while (!cowop_rm_iter->AtEnd()) {
+ const CowOperation* cow_op = cowop_rm_iter->Get();
struct disk_exception* de =
reinterpret_cast<struct disk_exception*>((char*)de_ptr.get() + offset);
@@ -442,7 +437,7 @@
sizeof(struct disk_exception));
memset(de_ptr.get(), 0, (exceptions_per_area_ * sizeof(struct disk_exception)));
- if (cowop_rm_iter->Done()) {
+ if (cowop_rm_iter->AtEnd()) {
vec_.push_back(std::move(de_ptr));
}
}
@@ -462,9 +457,9 @@
<< " Number of replace/zero ops completed in this area: " << num_ops
<< " Pending copy ops for this area: " << pending_ordered_ops;
- while (!cowop_rm_iter->Done()) {
+ while (!cowop_rm_iter->AtEnd()) {
do {
- const CowOperation* cow_op = &cowop_rm_iter->Get();
+ const CowOperation* cow_op = cowop_rm_iter->Get();
// We have two cases specific cases:
//
@@ -513,11 +508,9 @@
// the merge of operations are done based on the ops present
// in the file.
//===========================================================
- uint64_t block_source = cow_op->source;
- uint64_t block_offset = 0;
+ uint64_t block_source = GetCowOpSourceInfoData(cow_op);
if (prev_id.has_value()) {
- if (dest_blocks.count(cow_op->new_block) || source_blocks.count(block_source) ||
- (block_offset > 0 && source_blocks.count(block_source + 1))) {
+ if (dest_blocks.count(cow_op->new_block) || source_blocks.count(block_source)) {
break;
}
}
@@ -525,13 +518,10 @@
pending_ordered_ops -= 1;
vec.push_back(cow_op);
dest_blocks.insert(block_source);
- if (block_offset > 0) {
- dest_blocks.insert(block_source + 1);
- }
source_blocks.insert(cow_op->new_block);
prev_id = cow_op->new_block;
cowop_rm_iter->Next();
- } while (!cowop_rm_iter->Done() && pending_ordered_ops);
+ } while (!cowop_rm_iter->AtEnd() && pending_ordered_ops);
data_chunk_id = GetNextAllocatableChunkId(data_chunk_id);
SNAP_LOG(DEBUG) << "Batch Merge copy-ops of size: " << vec.size()
@@ -574,7 +564,7 @@
sizeof(struct disk_exception));
memset(de_ptr.get(), 0, (exceptions_per_area_ * sizeof(struct disk_exception)));
- if (cowop_rm_iter->Done()) {
+ if (cowop_rm_iter->AtEnd()) {
vec_.push_back(std::move(de_ptr));
SNAP_LOG(DEBUG) << "ReadMetadata() completed; Number of Areas: " << vec_.size();
}
@@ -636,11 +626,10 @@
}
bool Snapuserd::MmapMetadata() {
- CowHeader header;
- reader_->GetHeader(&header);
+ const auto& header = reader_->GetHeader();
- if (header.major_version >= 2 && header.buffer_size > 0) {
- total_mapped_addr_length_ = header.header_size + BUFFER_REGION_DEFAULT_SIZE;
+ if (header.prefix.major_version >= 2 && header.buffer_size > 0) {
+ total_mapped_addr_length_ = header.prefix.header_size + BUFFER_REGION_DEFAULT_SIZE;
read_ahead_feature_ = true;
} else {
// mmap the first 4k page - older COW format
@@ -745,8 +734,8 @@
off_t offset = 0;
for (int i = 0; i < num_threads; i++) {
- std::async(std::launch::async, &Snapuserd::ReadBlocksToCache, this, dm_block_device,
- partition_name, offset, read_sz_per_thread);
+ (void)std::async(std::launch::async, &Snapuserd::ReadBlocksToCache, this, dm_block_device,
+ partition_name, offset, read_sz_per_thread);
offset += read_sz_per_thread;
}
@@ -832,10 +821,9 @@
}
uint64_t Snapuserd::GetBufferMetadataOffset() {
- CowHeader header;
- reader_->GetHeader(&header);
+ const auto& header = reader_->GetHeader();
- size_t size = header.header_size + sizeof(BufferState);
+ size_t size = header.prefix.header_size + sizeof(BufferState);
return size;
}
@@ -848,37 +836,33 @@
*
*/
size_t Snapuserd::GetBufferMetadataSize() {
- CowHeader header;
- reader_->GetHeader(&header);
+ const auto& header = reader_->GetHeader();
size_t metadata_bytes = (header.buffer_size * sizeof(struct ScratchMetadata)) / BLOCK_SZ;
return metadata_bytes;
}
size_t Snapuserd::GetBufferDataOffset() {
- CowHeader header;
- reader_->GetHeader(&header);
+ const auto& header = reader_->GetHeader();
- return (header.header_size + GetBufferMetadataSize());
+ return (header.prefix.header_size + GetBufferMetadataSize());
}
/*
* (2MB - 8K = 2088960 bytes) will be the buffer region to hold the data.
*/
size_t Snapuserd::GetBufferDataSize() {
- CowHeader header;
- reader_->GetHeader(&header);
+ const auto& header = reader_->GetHeader();
size_t size = header.buffer_size - GetBufferMetadataSize();
return size;
}
struct BufferState* Snapuserd::GetBufferState() {
- CowHeader header;
- reader_->GetHeader(&header);
+ const auto& header = reader_->GetHeader();
struct BufferState* ra_state =
- reinterpret_cast<struct BufferState*>((char*)mapped_addr_ + header.header_size);
+ reinterpret_cast<struct BufferState*>((char*)mapped_addr_ + header.prefix.header_size);
return ra_state;
}
diff --git a/fs_mgr/libsnapshot/snapuserd/dm-snapshot-merge/snapuserd_readahead.cpp b/fs_mgr/libsnapshot/snapuserd/dm-snapshot-merge/snapuserd_readahead.cpp
index 01123f8..d5fbe91 100644
--- a/fs_mgr/libsnapshot/snapuserd/dm-snapshot-merge/snapuserd_readahead.cpp
+++ b/fs_mgr/libsnapshot/snapuserd/dm-snapshot-merge/snapuserd_readahead.cpp
@@ -172,17 +172,12 @@
}
void ReadAheadThread::CheckOverlap(const CowOperation* cow_op) {
- uint64_t source_block = cow_op->source;
- uint64_t source_offset = 0;
- if (dest_blocks_.count(cow_op->new_block) || source_blocks_.count(source_block) ||
- (source_offset > 0 && source_blocks_.count(source_block + 1))) {
+ uint64_t source_block = GetCowOpSourceInfoData(cow_op);
+ if (dest_blocks_.count(cow_op->new_block) || source_blocks_.count(source_block)) {
overlap_ = true;
}
dest_blocks_.insert(source_block);
- if (source_offset > 0) {
- dest_blocks_.insert(source_block + 1);
- }
source_blocks_.insert(cow_op->new_block);
}
@@ -196,7 +191,7 @@
// Get the first block with offset
const CowOperation* cow_op = GetRAOpIter();
CHECK_NE(cow_op, nullptr);
- *source_offset = cow_op->source;
+ *source_offset = GetCowOpSourceInfoData(cow_op);
if (cow_op->type == kCowCopyOp) {
*source_offset *= BLOCK_SZ;
}
@@ -215,7 +210,7 @@
while (!RAIterDone() && num_ops) {
const CowOperation* op = GetRAOpIter();
CHECK_NE(op, nullptr);
- uint64_t next_offset = op->source;
+ uint64_t next_offset = GetCowOpSourceInfoData(op);
if (op->type == kCowCopyOp) {
next_offset *= BLOCK_SZ;
}
diff --git a/fs_mgr/libsnapshot/snapuserd/dm-snapshot-merge/snapuserd_worker.cpp b/fs_mgr/libsnapshot/snapuserd/dm-snapshot-merge/snapuserd_worker.cpp
index 965af40..559dcc7 100644
--- a/fs_mgr/libsnapshot/snapuserd/dm-snapshot-merge/snapuserd_worker.cpp
+++ b/fs_mgr/libsnapshot/snapuserd/dm-snapshot-merge/snapuserd_worker.cpp
@@ -95,11 +95,17 @@
// internal COW format and if the block is compressed,
// it will be de-compressed.
bool WorkerThread::ProcessReplaceOp(const CowOperation* cow_op) {
- if (!reader_->ReadData(*cow_op, &bufsink_)) {
- SNAP_LOG(ERROR) << "ProcessReplaceOp failed for block " << cow_op->new_block;
+ void* buffer = bufsink_.GetPayloadBuffer(BLOCK_SZ);
+ if (!buffer) {
+ SNAP_LOG(ERROR) << "No space in buffer sink";
return false;
}
-
+ ssize_t rv = reader_->ReadData(cow_op, buffer, BLOCK_SZ);
+ if (rv != BLOCK_SZ) {
+ SNAP_LOG(ERROR) << "ProcessReplaceOp failed for block " << cow_op->new_block
+ << ", return = " << rv;
+ return false;
+ }
return true;
}
@@ -109,12 +115,13 @@
SNAP_LOG(ERROR) << "ReadFromBaseDevice: Failed to get payload buffer";
return false;
}
- SNAP_LOG(DEBUG) << " ReadFromBaseDevice...: new-block: " << cow_op->new_block
- << " Source: " << cow_op->source;
- uint64_t offset = cow_op->source;
- if (cow_op->type == kCowCopyOp) {
- offset *= BLOCK_SZ;
+ uint64_t offset;
+ if (!reader_->GetSourceOffset(cow_op, &offset)) {
+ SNAP_LOG(ERROR) << "ReadFromBaseDevice: Failed to get source offset";
+ return false;
}
+ SNAP_LOG(DEBUG) << " ReadFromBaseDevice...: new-block: " << cow_op->new_block
+ << " Source: " << offset;
if (!android::base::ReadFullyAtOffset(backing_store_fd_, buffer, BLOCK_SZ, offset)) {
SNAP_PLOG(ERROR) << "Copy op failed. Read from backing store: " << backing_store_device_
<< "at block :" << offset / BLOCK_SZ << " offset:" << offset % BLOCK_SZ;
@@ -502,7 +509,7 @@
if (read_ahead_buffer_map.find(cow_op->new_block) == read_ahead_buffer_map.end()) {
SNAP_LOG(ERROR)
<< " Block: " << cow_op->new_block << " not found in read-ahead cache"
- << " Source: " << cow_op->source;
+ << " Op: " << *cow_op;
return -1;
}
// If this is a final block merged in the read-ahead buffer
diff --git a/fs_mgr/libsnapshot/snapuserd/include/snapuserd/snapuserd_buffer.h b/fs_mgr/libsnapshot/snapuserd/include/snapuserd/snapuserd_buffer.h
index 2e4cac6..c5ca2b1 100644
--- a/fs_mgr/libsnapshot/snapuserd/include/snapuserd/snapuserd_buffer.h
+++ b/fs_mgr/libsnapshot/snapuserd/include/snapuserd/snapuserd_buffer.h
@@ -25,18 +25,32 @@
namespace android {
namespace snapshot {
-class BufferSink : public IByteSink {
+class BufferSink final {
public:
void Initialize(size_t size);
void* GetBufPtr() { return buffer_.get(); }
void Clear() { memset(GetBufPtr(), 0, buffer_size_); }
void* GetPayloadBuffer(size_t size);
- void* GetBuffer(size_t requested, size_t* actual) override;
+ void* GetBuffer(size_t requested, size_t* actual);
void UpdateBufferOffset(size_t size) { buffer_offset_ += size; }
struct dm_user_header* GetHeaderPtr();
- bool ReturnData(void*, size_t) override { return true; }
void ResetBufferOffset() { buffer_offset_ = 0; }
void* GetPayloadBufPtr();
+ loff_t GetPayloadBytesWritten() { return buffer_offset_; }
+
+ // Same as calling GetPayloadBuffer and then UpdateBufferOffset.
+ //
+ // This is preferred over GetPayloadBuffer as it does not require a
+ // separate call to UpdateBufferOffset.
+ void* AcquireBuffer(size_t size) { return AcquireBuffer(size, size); }
+
+ // Same as AcquireBuffer, but separates the requested size from the buffer
+ // offset. This is useful for a situation where a full run of data will be
+ // read, but only a partial amount will be returned.
+ //
+ // If size != to_write, the excess bytes may be reallocated by the next
+ // call to AcquireBuffer.
+ void* AcquireBuffer(size_t size, size_t to_write);
private:
std::unique_ptr<uint8_t[]> buffer_;
@@ -44,19 +58,5 @@
size_t buffer_size_;
};
-class XorSink : public IByteSink {
- public:
- void Initialize(BufferSink* sink, size_t size);
- void Reset();
- void* GetBuffer(size_t requested, size_t* actual) override;
- bool ReturnData(void* buffer, size_t len) override;
-
- private:
- BufferSink* bufsink_;
- std::unique_ptr<uint8_t[]> buffer_;
- size_t buffer_size_;
- size_t returned_;
-};
-
} // namespace snapshot
} // namespace android
diff --git a/fs_mgr/libsnapshot/snapuserd/include/snapuserd/snapuserd_kernel.h b/fs_mgr/libsnapshot/snapuserd/include/snapuserd/snapuserd_kernel.h
index c592257..46952c4 100644
--- a/fs_mgr/libsnapshot/snapuserd/include/snapuserd/snapuserd_kernel.h
+++ b/fs_mgr/libsnapshot/snapuserd/include/snapuserd/snapuserd_kernel.h
@@ -41,6 +41,9 @@
*/
static constexpr uint32_t SECTOR_SHIFT = 9;
+static constexpr size_t BLOCK_SZ = 4096;
+static constexpr size_t BLOCK_SHIFT = (__builtin_ffs(BLOCK_SZ) - 1);
+
typedef __u64 sector_t;
typedef sector_t chunk_t;
diff --git a/fs_mgr/libsnapshot/snapuserd/snapuserd_buffer.cpp b/fs_mgr/libsnapshot/snapuserd/snapuserd_buffer.cpp
index ab763ab..35065e6 100644
--- a/fs_mgr/libsnapshot/snapuserd/snapuserd_buffer.cpp
+++ b/fs_mgr/libsnapshot/snapuserd/snapuserd_buffer.cpp
@@ -15,6 +15,8 @@
*/
#include <snapuserd/snapuserd_buffer.h>
+
+#include <android-base/logging.h>
#include <snapuserd/snapuserd_kernel.h>
namespace android {
@@ -26,11 +28,23 @@
buffer_ = std::make_unique<uint8_t[]>(size);
}
-void* BufferSink::GetPayloadBuffer(size_t size) {
- if ((buffer_size_ - buffer_offset_) < size) return nullptr;
+void* BufferSink::AcquireBuffer(size_t size, size_t to_write) {
+ CHECK(to_write <= size);
+ void* ptr = GetPayloadBuffer(size);
+ if (!ptr) {
+ return nullptr;
+ }
+ UpdateBufferOffset(to_write);
+ return ptr;
+}
+
+void* BufferSink::GetPayloadBuffer(size_t size) {
char* buffer = reinterpret_cast<char*>(GetBufPtr());
struct dm_user_message* msg = (struct dm_user_message*)(&(buffer[0]));
+ if ((buffer_size_ - buffer_offset_ - sizeof(msg->header)) < size) {
+ return nullptr;
+ }
return (char*)msg->payload.buf + buffer_offset_;
}
@@ -59,38 +73,5 @@
return msg->payload.buf;
}
-void XorSink::Initialize(BufferSink* sink, size_t size) {
- bufsink_ = sink;
- buffer_size_ = size;
- returned_ = 0;
- buffer_ = std::make_unique<uint8_t[]>(size);
-}
-
-void XorSink::Reset() {
- returned_ = 0;
-}
-
-void* XorSink::GetBuffer(size_t requested, size_t* actual) {
- if (requested > buffer_size_) {
- *actual = buffer_size_;
- } else {
- *actual = requested;
- }
- return buffer_.get();
-}
-
-bool XorSink::ReturnData(void* buffer, size_t len) {
- uint8_t* xor_data = reinterpret_cast<uint8_t*>(buffer);
- uint8_t* buff = reinterpret_cast<uint8_t*>(bufsink_->GetPayloadBuffer(len + returned_));
- if (buff == nullptr) {
- return false;
- }
- for (size_t i = 0; i < len; i++) {
- buff[returned_ + i] ^= xor_data[i];
- }
- returned_ += len;
- return true;
-}
-
} // namespace snapshot
} // namespace android
diff --git a/fs_mgr/libsnapshot/snapuserd/snapuserd_daemon.cpp b/fs_mgr/libsnapshot/snapuserd/snapuserd_daemon.cpp
index ae20e1f..36dad33 100644
--- a/fs_mgr/libsnapshot/snapuserd/snapuserd_daemon.cpp
+++ b/fs_mgr/libsnapshot/snapuserd/snapuserd_daemon.cpp
@@ -114,7 +114,7 @@
return false;
}
auto handler = user_server_.AddHandler(parts[0], parts[1], parts[2], parts[3]);
- if (!handler || !user_server_.StartHandler(handler)) {
+ if (!handler || !user_server_.StartHandler(parts[0])) {
return false;
}
}
diff --git a/fs_mgr/libsnapshot/snapuserd/user-space-merge/handler_manager.cpp b/fs_mgr/libsnapshot/snapuserd/user-space-merge/handler_manager.cpp
new file mode 100644
index 0000000..4105b4b
--- /dev/null
+++ b/fs_mgr/libsnapshot/snapuserd/user-space-merge/handler_manager.cpp
@@ -0,0 +1,376 @@
+// Copyright (C) 2023 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 "handler_manager.h"
+
+#include <sys/eventfd.h>
+
+#include <android-base/logging.h>
+
+#include "read_worker.h"
+#include "snapuserd_core.h"
+#include "snapuserd_merge.h"
+
+namespace android {
+namespace snapshot {
+
+static constexpr uint8_t kMaxMergeThreads = 2;
+
+HandlerThread::HandlerThread(std::shared_ptr<SnapshotHandler> snapuserd)
+ : snapuserd_(snapuserd), misc_name_(snapuserd_->GetMiscName()) {}
+
+void HandlerThread::FreeResources() {
+ // Each worker thread holds a reference to snapuserd.
+ // Clear them so that all the resources
+ // held by snapuserd is released
+ if (snapuserd_) {
+ snapuserd_->FreeResources();
+ snapuserd_ = nullptr;
+ }
+}
+
+SnapshotHandlerManager::SnapshotHandlerManager() {
+ monitor_merge_event_fd_.reset(eventfd(0, EFD_CLOEXEC));
+ if (monitor_merge_event_fd_ == -1) {
+ PLOG(FATAL) << "monitor_merge_event_fd_: failed to create eventfd";
+ }
+}
+
+std::shared_ptr<HandlerThread> SnapshotHandlerManager::AddHandler(
+ const std::string& misc_name, const std::string& cow_device_path,
+ const std::string& backing_device, const std::string& base_path_merge,
+ int num_worker_threads, bool use_iouring, bool perform_verification) {
+ auto snapuserd = std::make_shared<SnapshotHandler>(misc_name, cow_device_path, backing_device,
+ base_path_merge, num_worker_threads,
+ use_iouring, perform_verification);
+ if (!snapuserd->InitCowDevice()) {
+ LOG(ERROR) << "Failed to initialize Snapuserd";
+ return nullptr;
+ }
+
+ if (!snapuserd->InitializeWorkers()) {
+ LOG(ERROR) << "Failed to initialize workers";
+ return nullptr;
+ }
+
+ auto handler = std::make_shared<HandlerThread>(snapuserd);
+ {
+ std::lock_guard<std::mutex> lock(lock_);
+ if (FindHandler(&lock, misc_name) != dm_users_.end()) {
+ LOG(ERROR) << "Handler already exists: " << misc_name;
+ return nullptr;
+ }
+ dm_users_.push_back(handler);
+ }
+ return handler;
+}
+
+bool SnapshotHandlerManager::StartHandler(const std::string& misc_name) {
+ std::lock_guard<std::mutex> lock(lock_);
+ auto iter = FindHandler(&lock, misc_name);
+ if (iter == dm_users_.end()) {
+ LOG(ERROR) << "Could not find handler: " << misc_name;
+ return false;
+ }
+ if (!(*iter)->snapuserd() || (*iter)->snapuserd()->IsAttached()) {
+ LOG(ERROR) << "Tried to re-attach control device: " << misc_name;
+ return false;
+ }
+ if (!StartHandler(*iter)) {
+ return false;
+ }
+ return true;
+}
+
+bool SnapshotHandlerManager::StartHandler(const std::shared_ptr<HandlerThread>& handler) {
+ if (handler->snapuserd()->IsAttached()) {
+ LOG(ERROR) << "Handler already attached";
+ return false;
+ }
+
+ handler->snapuserd()->AttachControlDevice();
+
+ handler->thread() = std::thread(std::bind(&SnapshotHandlerManager::RunThread, this, handler));
+ return true;
+}
+
+bool SnapshotHandlerManager::DeleteHandler(const std::string& misc_name) {
+ {
+ std::lock_guard<std::mutex> lock(lock_);
+ auto iter = FindHandler(&lock, misc_name);
+ if (iter == dm_users_.end()) {
+ // After merge is completed, we swap dm-user table with
+ // the underlying dm-linear base device. Hence, worker
+ // threads would have terminted and was removed from
+ // the list.
+ LOG(DEBUG) << "Could not find handler: " << misc_name;
+ return true;
+ }
+
+ if (!(*iter)->ThreadTerminated()) {
+ (*iter)->snapuserd()->NotifyIOTerminated();
+ }
+ }
+ if (!RemoveAndJoinHandler(misc_name)) {
+ return false;
+ }
+ return true;
+}
+
+void SnapshotHandlerManager::RunThread(std::shared_ptr<HandlerThread> handler) {
+ LOG(INFO) << "Entering thread for handler: " << handler->misc_name();
+
+ if (!handler->snapuserd()->Start()) {
+ LOG(ERROR) << " Failed to launch all worker threads";
+ }
+
+ handler->snapuserd()->CloseFds();
+ bool merge_completed = handler->snapuserd()->CheckMergeCompletionStatus();
+ handler->snapuserd()->UnmapBufferRegion();
+
+ auto misc_name = handler->misc_name();
+ LOG(INFO) << "Handler thread about to exit: " << misc_name;
+
+ {
+ std::lock_guard<std::mutex> lock(lock_);
+ if (merge_completed) {
+ num_partitions_merge_complete_ += 1;
+ active_merge_threads_ -= 1;
+ WakeupMonitorMergeThread();
+ }
+ handler->SetThreadTerminated();
+ auto iter = FindHandler(&lock, handler->misc_name());
+ if (iter == dm_users_.end()) {
+ // RemoveAndJoinHandler() already removed us from the list, and is
+ // now waiting on a join(), so just return. Additionally, release
+ // all the resources held by snapuserd object which are shared
+ // by worker threads. This should be done when the last reference
+ // of "handler" is released; but we will explicitly release here
+ // to make sure snapuserd object is freed as it is the biggest
+ // consumer of memory in the daemon.
+ handler->FreeResources();
+ LOG(INFO) << "Exiting handler thread to allow for join: " << misc_name;
+ return;
+ }
+
+ LOG(INFO) << "Exiting handler thread and freeing resources: " << misc_name;
+
+ if (handler->snapuserd()->IsAttached()) {
+ handler->thread().detach();
+ }
+
+ // Important: free resources within the lock. This ensures that if
+ // WaitForDelete() is called, the handler is either in the list, or
+ // it's not and its resources are guaranteed to be freed.
+ handler->FreeResources();
+ dm_users_.erase(iter);
+ }
+}
+
+bool SnapshotHandlerManager::InitiateMerge(const std::string& misc_name) {
+ std::lock_guard<std::mutex> lock(lock_);
+ auto iter = FindHandler(&lock, misc_name);
+ if (iter == dm_users_.end()) {
+ LOG(ERROR) << "Could not find handler: " << misc_name;
+ return false;
+ }
+
+ return StartMerge(&lock, *iter);
+}
+
+bool SnapshotHandlerManager::StartMerge(std::lock_guard<std::mutex>* proof_of_lock,
+ const std::shared_ptr<HandlerThread>& handler) {
+ CHECK(proof_of_lock);
+
+ if (!handler->snapuserd()->IsAttached()) {
+ LOG(ERROR) << "Handler not attached to dm-user - Merge thread cannot be started";
+ return false;
+ }
+
+ handler->snapuserd()->MonitorMerge();
+
+ if (!is_merge_monitor_started_) {
+ std::thread(&SnapshotHandlerManager::MonitorMerge, this).detach();
+ is_merge_monitor_started_ = true;
+ }
+
+ merge_handlers_.push(handler);
+ WakeupMonitorMergeThread();
+ return true;
+}
+
+void SnapshotHandlerManager::WakeupMonitorMergeThread() {
+ uint64_t notify = 1;
+ ssize_t rc = TEMP_FAILURE_RETRY(write(monitor_merge_event_fd_.get(), ¬ify, sizeof(notify)));
+ if (rc < 0) {
+ PLOG(FATAL) << "failed to notify monitor merge thread";
+ }
+}
+
+void SnapshotHandlerManager::MonitorMerge() {
+ while (!stop_monitor_merge_thread_) {
+ uint64_t testVal;
+ ssize_t ret =
+ TEMP_FAILURE_RETRY(read(monitor_merge_event_fd_.get(), &testVal, sizeof(testVal)));
+ if (ret == -1) {
+ PLOG(FATAL) << "Failed to read from eventfd";
+ } else if (ret == 0) {
+ LOG(FATAL) << "Hit EOF on eventfd";
+ }
+
+ LOG(INFO) << "MonitorMerge: active-merge-threads: " << active_merge_threads_;
+ {
+ std::lock_guard<std::mutex> lock(lock_);
+ while (active_merge_threads_ < kMaxMergeThreads && merge_handlers_.size() > 0) {
+ auto handler = merge_handlers_.front();
+ merge_handlers_.pop();
+
+ if (!handler->snapuserd()) {
+ LOG(INFO) << "MonitorMerge: skipping deleted handler: " << handler->misc_name();
+ continue;
+ }
+
+ LOG(INFO) << "Starting merge for partition: "
+ << handler->snapuserd()->GetMiscName();
+ handler->snapuserd()->InitiateMerge();
+ active_merge_threads_ += 1;
+ }
+ }
+ }
+
+ LOG(INFO) << "Exiting MonitorMerge: size: " << merge_handlers_.size();
+}
+
+std::string SnapshotHandlerManager::GetMergeStatus(const std::string& misc_name) {
+ std::lock_guard<std::mutex> lock(lock_);
+ auto iter = FindHandler(&lock, misc_name);
+ if (iter == dm_users_.end()) {
+ LOG(ERROR) << "Could not find handler: " << misc_name;
+ return {};
+ }
+
+ return (*iter)->snapuserd()->GetMergeStatus();
+}
+
+double SnapshotHandlerManager::GetMergePercentage() {
+ std::lock_guard<std::mutex> lock(lock_);
+
+ double percentage = 0.0;
+ int n = 0;
+
+ for (auto iter = dm_users_.begin(); iter != dm_users_.end(); iter++) {
+ auto& th = (*iter)->thread();
+ if (th.joinable()) {
+ // Merge percentage by individual partitions wherein merge is still
+ // in-progress
+ percentage += (*iter)->snapuserd()->GetMergePercentage();
+ n += 1;
+ }
+ }
+
+ // Calculate final merge including those partitions where merge was already
+ // completed - num_partitions_merge_complete_ will track them when each
+ // thread exists in RunThread.
+ int total_partitions = n + num_partitions_merge_complete_;
+
+ if (total_partitions) {
+ percentage = ((num_partitions_merge_complete_ * 100.0) + percentage) / total_partitions;
+ }
+
+ LOG(DEBUG) << "Merge %: " << percentage
+ << " num_partitions_merge_complete_: " << num_partitions_merge_complete_
+ << " total_partitions: " << total_partitions << " n: " << n;
+ return percentage;
+}
+
+bool SnapshotHandlerManager::GetVerificationStatus() {
+ std::lock_guard<std::mutex> lock(lock_);
+
+ bool status = true;
+ for (auto iter = dm_users_.begin(); iter != dm_users_.end(); iter++) {
+ auto& th = (*iter)->thread();
+ if (th.joinable() && status) {
+ status = (*iter)->snapuserd()->CheckPartitionVerification() && status;
+ } else {
+ // return immediately if there is a failure
+ return false;
+ }
+ }
+
+ return status;
+}
+
+bool SnapshotHandlerManager::RemoveAndJoinHandler(const std::string& misc_name) {
+ std::shared_ptr<HandlerThread> handler;
+ {
+ std::lock_guard<std::mutex> lock(lock_);
+
+ auto iter = FindHandler(&lock, misc_name);
+ if (iter == dm_users_.end()) {
+ // Client already deleted.
+ return true;
+ }
+ handler = std::move(*iter);
+ dm_users_.erase(iter);
+ }
+
+ auto& th = handler->thread();
+ if (th.joinable()) {
+ th.join();
+ }
+ return true;
+}
+
+void SnapshotHandlerManager::TerminateMergeThreads() {
+ std::lock_guard<std::mutex> guard(lock_);
+
+ for (auto iter = dm_users_.begin(); iter != dm_users_.end(); iter++) {
+ if (!(*iter)->ThreadTerminated()) {
+ (*iter)->snapuserd()->NotifyIOTerminated();
+ }
+ }
+}
+
+void SnapshotHandlerManager::JoinAllThreads() {
+ // Acquire the thread list within the lock.
+ std::vector<std::shared_ptr<HandlerThread>> dm_users;
+ {
+ std::lock_guard<std::mutex> guard(lock_);
+ dm_users = std::move(dm_users_);
+ }
+
+ for (auto& client : dm_users) {
+ auto& th = client->thread();
+
+ if (th.joinable()) th.join();
+ }
+
+ stop_monitor_merge_thread_ = true;
+ WakeupMonitorMergeThread();
+}
+
+auto SnapshotHandlerManager::FindHandler(std::lock_guard<std::mutex>* proof_of_lock,
+ const std::string& misc_name) -> HandlerList::iterator {
+ CHECK(proof_of_lock);
+
+ for (auto iter = dm_users_.begin(); iter != dm_users_.end(); iter++) {
+ if ((*iter)->misc_name() == misc_name) {
+ return iter;
+ }
+ }
+ return dm_users_.end();
+}
+
+} // namespace snapshot
+} // namespace android
diff --git a/fs_mgr/libsnapshot/snapuserd/user-space-merge/handler_manager.h b/fs_mgr/libsnapshot/snapuserd/user-space-merge/handler_manager.h
new file mode 100644
index 0000000..b7ddac1
--- /dev/null
+++ b/fs_mgr/libsnapshot/snapuserd/user-space-merge/handler_manager.h
@@ -0,0 +1,131 @@
+// Copyright (C) 2023 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 <memory>
+#include <queue>
+#include <string>
+#include <thread>
+#include <vector>
+
+#include <android-base/unique_fd.h>
+
+namespace android {
+namespace snapshot {
+
+class SnapshotHandler;
+
+class HandlerThread {
+ public:
+ explicit HandlerThread(std::shared_ptr<SnapshotHandler> snapuserd);
+
+ void FreeResources();
+ const std::shared_ptr<SnapshotHandler>& snapuserd() const { return snapuserd_; }
+ std::thread& thread() { return thread_; }
+
+ const std::string& misc_name() const { return misc_name_; }
+ bool ThreadTerminated() { return thread_terminated_; }
+ void SetThreadTerminated() { thread_terminated_ = true; }
+
+ private:
+ std::thread thread_;
+ std::shared_ptr<SnapshotHandler> snapuserd_;
+ std::string misc_name_;
+ bool thread_terminated_ = false;
+};
+
+class ISnapshotHandlerManager {
+ public:
+ virtual ~ISnapshotHandlerManager() {}
+
+ // Add a new snapshot handler but do not start serving requests yet.
+ virtual std::shared_ptr<HandlerThread> AddHandler(const std::string& misc_name,
+ const std::string& cow_device_path,
+ const std::string& backing_device,
+ const std::string& base_path_merge,
+ int num_worker_threads, bool use_iouring,
+ bool perform_verification) = 0;
+
+ // Start serving requests on a snapshot handler.
+ virtual bool StartHandler(const std::string& misc_name) = 0;
+
+ // Stop serving requests on a snapshot handler and remove it.
+ virtual bool DeleteHandler(const std::string& misc_name) = 0;
+
+ // Begin merging blocks on the given snapshot handler.
+ virtual bool InitiateMerge(const std::string& misc_name) = 0;
+
+ // Return a string containing a status code indicating the merge status
+ // on the handler. Returns empty on error.
+ virtual std::string GetMergeStatus(const std::string& misc_name) = 0;
+
+ // Wait until all handlers have terminated.
+ virtual void JoinAllThreads() = 0;
+
+ // Stop any in-progress merge threads.
+ virtual void TerminateMergeThreads() = 0;
+
+ // Returns the merge progress across all merging snapshot handlers.
+ virtual double GetMergePercentage() = 0;
+
+ // Returns whether all snapshots have verified.
+ virtual bool GetVerificationStatus() = 0;
+};
+
+class SnapshotHandlerManager final : public ISnapshotHandlerManager {
+ public:
+ SnapshotHandlerManager();
+ std::shared_ptr<HandlerThread> AddHandler(const std::string& misc_name,
+ const std::string& cow_device_path,
+ const std::string& backing_device,
+ const std::string& base_path_merge,
+ int num_worker_threads, bool use_iouring,
+ bool perform_verification) override;
+ bool StartHandler(const std::string& misc_name) override;
+ bool DeleteHandler(const std::string& misc_name) override;
+ bool InitiateMerge(const std::string& misc_name) override;
+ std::string GetMergeStatus(const std::string& misc_name) override;
+ void JoinAllThreads() override;
+ void TerminateMergeThreads() override;
+ double GetMergePercentage() override;
+ bool GetVerificationStatus() override;
+
+ private:
+ bool StartHandler(const std::shared_ptr<HandlerThread>& handler);
+ void RunThread(std::shared_ptr<HandlerThread> handler);
+ bool StartMerge(std::lock_guard<std::mutex>* proof_of_lock,
+ const std::shared_ptr<HandlerThread>& handler);
+ void MonitorMerge();
+ void WakeupMonitorMergeThread();
+ bool RemoveAndJoinHandler(const std::string& misc_name);
+
+ // Find a HandlerThread within a lock.
+ using HandlerList = std::vector<std::shared_ptr<HandlerThread>>;
+ HandlerList::iterator FindHandler(std::lock_guard<std::mutex>* proof_of_lock,
+ const std::string& misc_name);
+
+ std::mutex lock_;
+ HandlerList dm_users_;
+
+ bool is_merge_monitor_started_ = false;
+ bool stop_monitor_merge_thread_ = false;
+ int active_merge_threads_ = 0;
+ int num_partitions_merge_complete_ = 0;
+ std::queue<std::shared_ptr<HandlerThread>> merge_handlers_;
+ android::base::unique_fd monitor_merge_event_fd_;
+};
+
+} // namespace snapshot
+} // namespace android
diff --git a/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_dm_user.cpp b/fs_mgr/libsnapshot/snapuserd/user-space-merge/read_worker.cpp
similarity index 64%
rename from fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_dm_user.cpp
rename to fs_mgr/libsnapshot/snapuserd/user-space-merge/read_worker.cpp
index 0d0f711..7d2e3a6 100644
--- a/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_dm_user.cpp
+++ b/fs_mgr/libsnapshot/snapuserd/user-space-merge/read_worker.cpp
@@ -14,6 +14,8 @@
* limitations under the License.
*/
+#include "read_worker.h"
+
#include "snapuserd_core.h"
namespace android {
@@ -23,79 +25,39 @@
using namespace android::dm;
using android::base::unique_fd;
-Worker::Worker(const std::string& cow_device, const std::string& backing_device,
- const std::string& control_device, const std::string& misc_name,
- const std::string& base_path_merge, std::shared_ptr<SnapshotHandler> snapuserd) {
- cow_device_ = cow_device;
- backing_store_device_ = backing_device;
- control_device_ = control_device;
- misc_name_ = misc_name;
- base_path_merge_ = base_path_merge;
- snapuserd_ = snapuserd;
+void ReadWorker::CloseFds() {
+ ctrl_fd_ = {};
+ backing_store_fd_ = {};
+ Worker::CloseFds();
}
-bool Worker::InitializeFds() {
- backing_store_fd_.reset(open(backing_store_device_.c_str(), O_RDONLY));
- if (backing_store_fd_ < 0) {
- SNAP_PLOG(ERROR) << "Open Failed: " << backing_store_device_;
- return false;
- }
-
- cow_fd_.reset(open(cow_device_.c_str(), O_RDWR));
- if (cow_fd_ < 0) {
- SNAP_PLOG(ERROR) << "Open Failed: " << cow_device_;
- return false;
- }
-
- ctrl_fd_.reset(open(control_device_.c_str(), O_RDWR));
- if (ctrl_fd_ < 0) {
- SNAP_PLOG(ERROR) << "Unable to open " << control_device_;
- return false;
- }
-
- // Base device used by merge thread
- base_path_merge_fd_.reset(open(base_path_merge_.c_str(), O_RDWR));
- if (base_path_merge_fd_ < 0) {
- SNAP_PLOG(ERROR) << "Open Failed: " << base_path_merge_;
- return false;
- }
-
- return true;
-}
-
-bool Worker::InitReader() {
- reader_ = snapuserd_->CloneReaderForWorker();
-
- if (!reader_->InitForMerge(std::move(cow_fd_))) {
- return false;
- }
- return true;
-}
+ReadWorker::ReadWorker(const std::string& cow_device, const std::string& backing_device,
+ const std::string& control_device, const std::string& misc_name,
+ const std::string& base_path_merge,
+ std::shared_ptr<SnapshotHandler> snapuserd)
+ : Worker(cow_device, misc_name, base_path_merge, snapuserd),
+ backing_store_device_(backing_device),
+ control_device_(control_device) {}
// Start the replace operation. This will read the
// internal COW format and if the block is compressed,
// it will be de-compressed.
-bool Worker::ProcessReplaceOp(const CowOperation* cow_op) {
- if (!reader_->ReadData(*cow_op, &bufsink_)) {
+bool ReadWorker::ProcessReplaceOp(const CowOperation* cow_op, void* buffer) {
+ if (!reader_->ReadData(cow_op, buffer, BLOCK_SZ)) {
SNAP_LOG(ERROR) << "ProcessReplaceOp failed for block " << cow_op->new_block;
return false;
}
-
return true;
}
-bool Worker::ReadFromSourceDevice(const CowOperation* cow_op) {
- void* buffer = bufsink_.GetPayloadBuffer(BLOCK_SZ);
- if (buffer == nullptr) {
- SNAP_LOG(ERROR) << "ReadFromBaseDevice: Failed to get payload buffer";
+bool ReadWorker::ReadFromSourceDevice(const CowOperation* cow_op, void* buffer) {
+ uint64_t offset;
+ if (!reader_->GetSourceOffset(cow_op, &offset)) {
+ SNAP_LOG(ERROR) << "ReadFromSourceDevice: Failed to get source offset";
return false;
}
SNAP_LOG(DEBUG) << " ReadFromBaseDevice...: new-block: " << cow_op->new_block
- << " Source: " << cow_op->source;
- uint64_t offset = cow_op->source;
- if (cow_op->type == kCowCopyOp) {
- offset *= BLOCK_SZ;
- }
+ << " Op: " << *cow_op;
if (!android::base::ReadFullyAtOffset(backing_store_fd_, buffer, BLOCK_SZ, offset)) {
std::string op;
if (cow_op->type == kCowCopyOp)
@@ -113,46 +75,43 @@
// Start the copy operation. This will read the backing
// block device which is represented by cow_op->source.
-bool Worker::ProcessCopyOp(const CowOperation* cow_op) {
- if (!ReadFromSourceDevice(cow_op)) {
+bool ReadWorker::ProcessCopyOp(const CowOperation* cow_op, void* buffer) {
+ if (!ReadFromSourceDevice(cow_op, buffer)) {
return false;
}
-
return true;
}
-bool Worker::ProcessXorOp(const CowOperation* cow_op) {
- if (!ReadFromSourceDevice(cow_op)) {
- return false;
- }
- xorsink_.Reset();
- if (!reader_->ReadData(*cow_op, &xorsink_)) {
- SNAP_LOG(ERROR) << "ProcessXorOp failed for block " << cow_op->new_block;
+bool ReadWorker::ProcessXorOp(const CowOperation* cow_op, void* buffer) {
+ if (!ReadFromSourceDevice(cow_op, buffer)) {
return false;
}
+ if (xor_buffer_.empty()) {
+ xor_buffer_.resize(BLOCK_SZ);
+ }
+ CHECK(xor_buffer_.size() == BLOCK_SZ);
+
+ ssize_t size = reader_->ReadData(cow_op, xor_buffer_.data(), xor_buffer_.size());
+ if (size != BLOCK_SZ) {
+ SNAP_LOG(ERROR) << "ProcessXorOp failed for block " << cow_op->new_block
+ << ", return value: " << size;
+ return false;
+ }
+
+ auto xor_out = reinterpret_cast<uint8_t*>(buffer);
+ for (size_t i = 0; i < BLOCK_SZ; i++) {
+ xor_out[i] ^= xor_buffer_[i];
+ }
return true;
}
-bool Worker::ProcessZeroOp() {
- // Zero out the entire block
- void* buffer = bufsink_.GetPayloadBuffer(BLOCK_SZ);
- if (buffer == nullptr) {
- SNAP_LOG(ERROR) << "ProcessZeroOp: Failed to get payload buffer";
- return false;
- }
-
+bool ReadWorker::ProcessZeroOp(void* buffer) {
memset(buffer, 0, BLOCK_SZ);
return true;
}
-bool Worker::ProcessOrderedOp(const CowOperation* cow_op) {
- void* buffer = bufsink_.GetPayloadBuffer(BLOCK_SZ);
- if (buffer == nullptr) {
- SNAP_LOG(ERROR) << "ProcessOrderedOp: Failed to get payload buffer";
- return false;
- }
-
+bool ReadWorker::ProcessOrderedOp(const CowOperation* cow_op, void* buffer) {
MERGE_GROUP_STATE state = snapuserd_->ProcessMergingBlock(cow_op->new_block, buffer);
switch (state) {
@@ -162,7 +121,7 @@
SNAP_LOG(DEBUG) << "Merge-completed: Reading from base device sector: "
<< (cow_op->new_block >> SECTOR_SHIFT)
<< " Block-number: " << cow_op->new_block;
- if (!ReadDataFromBaseDevice(ChunkToSector(cow_op->new_block), BLOCK_SZ)) {
+ if (!ReadDataFromBaseDevice(ChunkToSector(cow_op->new_block), buffer, BLOCK_SZ)) {
SNAP_LOG(ERROR) << "ReadDataFromBaseDevice at sector: "
<< (cow_op->new_block >> SECTOR_SHIFT) << " after merge-complete.";
return false;
@@ -172,9 +131,9 @@
case MERGE_GROUP_STATE::GROUP_MERGE_PENDING: {
bool ret;
if (cow_op->type == kCowCopyOp) {
- ret = ProcessCopyOp(cow_op);
+ ret = ProcessCopyOp(cow_op, buffer);
} else {
- ret = ProcessXorOp(cow_op);
+ ret = ProcessXorOp(cow_op, buffer);
}
// I/O is complete - decrement the refcount irrespective of the return
@@ -199,7 +158,7 @@
return false;
}
-bool Worker::ProcessCowOp(const CowOperation* cow_op) {
+bool ReadWorker::ProcessCowOp(const CowOperation* cow_op, void* buffer) {
if (cow_op == nullptr) {
SNAP_LOG(ERROR) << "ProcessCowOp: Invalid cow_op";
return false;
@@ -207,17 +166,17 @@
switch (cow_op->type) {
case kCowReplaceOp: {
- return ProcessReplaceOp(cow_op);
+ return ProcessReplaceOp(cow_op, buffer);
}
case kCowZeroOp: {
- return ProcessZeroOp();
+ return ProcessZeroOp(buffer);
}
case kCowCopyOp:
[[fallthrough]];
case kCowXorOp: {
- return ProcessOrderedOp(cow_op);
+ return ProcessOrderedOp(cow_op, buffer);
}
default: {
@@ -227,31 +186,26 @@
return false;
}
-void Worker::InitializeBufsink() {
- // Allocate the buffer which is used to communicate between
- // daemon and dm-user. The buffer comprises of header and a fixed payload.
- // If the dm-user requests a big IO, the IO will be broken into chunks
- // of PAYLOAD_BUFFER_SZ.
- size_t buf_size = sizeof(struct dm_user_header) + PAYLOAD_BUFFER_SZ;
- bufsink_.Initialize(buf_size);
-}
-
-bool Worker::Init() {
- InitializeBufsink();
- xorsink_.Initialize(&bufsink_, BLOCK_SZ);
-
- if (!InitializeFds()) {
+bool ReadWorker::Init() {
+ if (!Worker::Init()) {
return false;
}
- if (!InitReader()) {
+ backing_store_fd_.reset(open(backing_store_device_.c_str(), O_RDONLY));
+ if (backing_store_fd_ < 0) {
+ SNAP_PLOG(ERROR) << "Open Failed: " << backing_store_device_;
return false;
}
+ ctrl_fd_.reset(open(control_device_.c_str(), O_RDWR));
+ if (ctrl_fd_ < 0) {
+ SNAP_PLOG(ERROR) << "Unable to open " << control_device_;
+ return false;
+ }
return true;
}
-bool Worker::RunThread() {
+bool ReadWorker::Run() {
SNAP_LOG(INFO) << "Processing snapshot I/O requests....";
if (setpriority(PRIO_PROCESS, gettid(), kNiceValueForMergeThreads)) {
@@ -271,26 +225,11 @@
return true;
}
-// Read Header from dm-user misc device. This gives
-// us the sector number for which IO is issued by dm-snapshot device
-bool Worker::ReadDmUserHeader() {
- if (!android::base::ReadFully(ctrl_fd_, bufsink_.GetBufPtr(), sizeof(struct dm_user_header))) {
- if (errno != ENOTBLK) {
- SNAP_PLOG(ERROR) << "Control-read failed";
- }
-
- SNAP_PLOG(DEBUG) << "ReadDmUserHeader failed....";
- return false;
- }
-
- return true;
-}
-
// Send the payload/data back to dm-user misc device.
-bool Worker::WriteDmUserPayload(size_t size, bool header_response) {
+bool ReadWorker::WriteDmUserPayload(size_t size) {
size_t payload_size = size;
void* buf = bufsink_.GetPayloadBufPtr();
- if (header_response) {
+ if (header_response_) {
payload_size += sizeof(struct dm_user_header);
buf = bufsink_.GetBufPtr();
}
@@ -300,18 +239,18 @@
return false;
}
+ // After the first header is sent in response to a request, we cannot
+ // send any additional headers.
+ header_response_ = false;
+
+ // Reset the buffer for use by the next request.
+ bufsink_.ResetBufferOffset();
return true;
}
-bool Worker::ReadDataFromBaseDevice(sector_t sector, size_t read_size) {
+bool ReadWorker::ReadDataFromBaseDevice(sector_t sector, void* buffer, size_t read_size) {
CHECK(read_size <= BLOCK_SZ);
- void* buffer = bufsink_.GetPayloadBuffer(BLOCK_SZ);
- if (buffer == nullptr) {
- SNAP_LOG(ERROR) << "ReadFromBaseDevice: Failed to get payload buffer";
- return false;
- }
-
loff_t offset = sector << SECTOR_SHIFT;
if (!android::base::ReadFullyAtOffset(base_path_merge_fd_, buffer, read_size, offset)) {
SNAP_PLOG(ERROR) << "ReadDataFromBaseDevice failed. fd: " << base_path_merge_fd_
@@ -322,21 +261,16 @@
return true;
}
-bool Worker::ReadAlignedSector(sector_t sector, size_t sz, bool header_response) {
- struct dm_user_header* header = bufsink_.GetHeaderPtr();
+bool ReadWorker::ReadAlignedSector(sector_t sector, size_t sz) {
size_t remaining_size = sz;
std::vector<std::pair<sector_t, const CowOperation*>>& chunk_vec = snapuserd_->GetChunkVec();
- bool io_error = false;
int ret = 0;
do {
// Process 1MB payload at a time
size_t read_size = std::min(PAYLOAD_BUFFER_SZ, remaining_size);
- header->type = DM_USER_RESP_SUCCESS;
size_t total_bytes_read = 0;
- io_error = false;
- bufsink_.ResetBufferOffset();
while (read_size) {
// We need to check every 4k block to verify if it is
@@ -347,98 +281,83 @@
std::make_pair(sector, nullptr), SnapshotHandler::compare);
bool not_found = (it == chunk_vec.end() || it->first != sector);
+ void* buffer = bufsink_.AcquireBuffer(BLOCK_SZ, size);
+ if (!buffer) {
+ SNAP_LOG(ERROR) << "AcquireBuffer failed in ReadAlignedSector";
+ return false;
+ }
+
if (not_found) {
// Block not found in map - which means this block was not
// changed as per the OTA. Just route the I/O to the base
// device.
- if (!ReadDataFromBaseDevice(sector, size)) {
+ if (!ReadDataFromBaseDevice(sector, buffer, size)) {
SNAP_LOG(ERROR) << "ReadDataFromBaseDevice failed";
- header->type = DM_USER_RESP_ERROR;
+ return false;
}
ret = size;
} else {
// We found the sector in mapping. Check the type of COW OP and
// process it.
- if (!ProcessCowOp(it->second)) {
+ if (!ProcessCowOp(it->second, buffer)) {
SNAP_LOG(ERROR) << "ProcessCowOp failed";
- header->type = DM_USER_RESP_ERROR;
- }
-
- ret = BLOCK_SZ;
- }
-
- // Just return the header if it is an error
- if (header->type == DM_USER_RESP_ERROR) {
- if (!RespondIOError(header_response)) {
return false;
}
- io_error = true;
- break;
+ ret = std::min(BLOCK_SZ, read_size);
}
read_size -= ret;
total_bytes_read += ret;
sector += (ret >> SECTOR_SHIFT);
- bufsink_.UpdateBufferOffset(ret);
}
- if (!io_error) {
- if (!WriteDmUserPayload(total_bytes_read, header_response)) {
- return false;
- }
-
- SNAP_LOG(DEBUG) << "WriteDmUserPayload success total_bytes_read: " << total_bytes_read
- << " header-response: " << header_response
- << " remaining_size: " << remaining_size;
- header_response = false;
- remaining_size -= total_bytes_read;
+ if (!SendBufferedIo()) {
+ return false;
}
- } while (remaining_size > 0 && !io_error);
+
+ SNAP_LOG(DEBUG) << "SendBufferedIo success total_bytes_read: " << total_bytes_read
+ << " remaining_size: " << remaining_size;
+ remaining_size -= total_bytes_read;
+ } while (remaining_size > 0);
return true;
}
-int Worker::ReadUnalignedSector(
+int ReadWorker::ReadUnalignedSector(
sector_t sector, size_t size,
std::vector<std::pair<sector_t, const CowOperation*>>::iterator& it) {
- size_t skip_sector_size = 0;
-
SNAP_LOG(DEBUG) << "ReadUnalignedSector: sector " << sector << " size: " << size
<< " Aligned sector: " << it->first;
- if (!ProcessCowOp(it->second)) {
+ int num_sectors_skip = sector - it->first;
+ size_t skip_size = num_sectors_skip << SECTOR_SHIFT;
+ size_t write_size = std::min(size, BLOCK_SZ - skip_size);
+ auto buffer = reinterpret_cast<uint8_t*>(bufsink_.AcquireBuffer(BLOCK_SZ, write_size));
+ if (!buffer) {
+ SNAP_LOG(ERROR) << "ProcessCowOp failed to allocate buffer";
+ return -1;
+ }
+
+ if (!ProcessCowOp(it->second, buffer)) {
SNAP_LOG(ERROR) << "ReadUnalignedSector: " << sector << " failed of size: " << size
<< " Aligned sector: " << it->first;
return -1;
}
- int num_sectors_skip = sector - it->first;
-
- if (num_sectors_skip > 0) {
- skip_sector_size = num_sectors_skip << SECTOR_SHIFT;
- char* buffer = reinterpret_cast<char*>(bufsink_.GetBufPtr());
- struct dm_user_message* msg = (struct dm_user_message*)(&(buffer[0]));
-
- if (skip_sector_size == BLOCK_SZ) {
+ if (skip_size) {
+ if (skip_size == BLOCK_SZ) {
SNAP_LOG(ERROR) << "Invalid un-aligned IO request at sector: " << sector
<< " Base-sector: " << it->first;
return -1;
}
-
- memmove(msg->payload.buf, (char*)msg->payload.buf + skip_sector_size,
- (BLOCK_SZ - skip_sector_size));
+ memmove(buffer, buffer + skip_size, write_size);
}
-
- bufsink_.ResetBufferOffset();
- return std::min(size, (BLOCK_SZ - skip_sector_size));
+ return write_size;
}
-bool Worker::ReadUnalignedSector(sector_t sector, size_t size) {
- struct dm_user_header* header = bufsink_.GetHeaderPtr();
- header->type = DM_USER_RESP_SUCCESS;
- bufsink_.ResetBufferOffset();
+bool ReadWorker::ReadUnalignedSector(sector_t sector, size_t size) {
std::vector<std::pair<sector_t, const CowOperation*>>& chunk_vec = snapuserd_->GetChunkVec();
auto it = std::lower_bound(chunk_vec.begin(), chunk_vec.end(), std::make_pair(sector, nullptr),
@@ -465,7 +384,6 @@
// to any COW ops. In that case, we just need to read from the base
// device.
bool merge_complete = false;
- bool header_response = true;
if (it == chunk_vec.end()) {
if (chunk_vec.size() > 0) {
// I/O request beyond the last mapped sector
@@ -484,7 +402,7 @@
--it;
}
} else {
- return ReadAlignedSector(sector, size, header_response);
+ return ReadAlignedSector(sector, size);
}
loff_t requested_offset = sector << SECTOR_SHIFT;
@@ -508,7 +426,6 @@
// requested offset in this case is beyond the last mapped COW op size (which
// is block 1 in this case).
- size_t total_bytes_read = 0;
size_t remaining_size = size;
int ret = 0;
if (!merge_complete && (requested_offset >= final_offset) &&
@@ -518,22 +435,20 @@
if (ret < 0) {
SNAP_LOG(ERROR) << "ReadUnalignedSector failed for sector: " << sector
<< " size: " << size << " it->sector: " << it->first;
- return RespondIOError(header_response);
- }
-
- remaining_size -= ret;
- total_bytes_read += ret;
- sector += (ret >> SECTOR_SHIFT);
-
- // Send the data back
- if (!WriteDmUserPayload(total_bytes_read, header_response)) {
return false;
}
- header_response = false;
+ remaining_size -= ret;
+ sector += (ret >> SECTOR_SHIFT);
+
+ // Send the data back
+ if (!SendBufferedIo()) {
+ return false;
+ }
+
// If we still have pending data to be processed, this will be aligned I/O
if (remaining_size) {
- return ReadAlignedSector(sector, remaining_size, header_response);
+ return ReadAlignedSector(sector, remaining_size);
}
} else {
// This is all about handling I/O request to be routed to base device
@@ -545,41 +460,35 @@
// Find the diff of the aligned offset
size_t diff_size = aligned_offset - requested_offset;
CHECK(diff_size <= BLOCK_SZ);
- if (remaining_size < diff_size) {
- if (!ReadDataFromBaseDevice(sector, remaining_size)) {
- return RespondIOError(header_response);
- }
- total_bytes_read += remaining_size;
- if (!WriteDmUserPayload(total_bytes_read, header_response)) {
- return false;
- }
- } else {
- if (!ReadDataFromBaseDevice(sector, diff_size)) {
- return RespondIOError(header_response);
- }
+ size_t read_size = std::min(remaining_size, diff_size);
+ void* buffer = bufsink_.AcquireBuffer(BLOCK_SZ, read_size);
+ if (!buffer) {
+ SNAP_LOG(ERROR) << "AcquireBuffer failed in ReadUnalignedSector";
+ return false;
+ }
+ if (!ReadDataFromBaseDevice(sector, buffer, read_size)) {
+ return false;
+ }
+ if (!SendBufferedIo()) {
+ return false;
+ }
- total_bytes_read += diff_size;
-
- if (!WriteDmUserPayload(total_bytes_read, header_response)) {
- return false;
- }
-
+ if (remaining_size >= diff_size) {
remaining_size -= diff_size;
size_t num_sectors_read = (diff_size >> SECTOR_SHIFT);
sector += num_sectors_read;
CHECK(IsBlockAligned(sector << SECTOR_SHIFT));
- header_response = false;
// If we still have pending data to be processed, this will be aligned I/O
- return ReadAlignedSector(sector, remaining_size, header_response);
+ return ReadAlignedSector(sector, remaining_size);
}
}
return true;
}
-bool Worker::RespondIOError(bool header_response) {
+void ReadWorker::RespondIOError() {
struct dm_user_header* header = bufsink_.GetHeaderPtr();
header->type = DM_USER_RESP_ERROR;
// This is an issue with the dm-user interface. There
@@ -591,18 +500,12 @@
// this back to dm-user.
//
// TODO: Fix the interface
- CHECK(header_response);
+ CHECK(header_response_);
- if (!WriteDmUserPayload(0, header_response)) {
- return false;
- }
-
- // There is no need to process further as we have already seen
- // an I/O error
- return true;
+ WriteDmUserPayload(0);
}
-bool Worker::DmuserReadRequest() {
+bool ReadWorker::DmuserReadRequest() {
struct dm_user_header* header = bufsink_.GetHeaderPtr();
// Unaligned I/O request
@@ -610,13 +513,23 @@
return ReadUnalignedSector(header->sector, header->len);
}
- return ReadAlignedSector(header->sector, header->len, true);
+ return ReadAlignedSector(header->sector, header->len);
}
-bool Worker::ProcessIORequest() {
- struct dm_user_header* header = bufsink_.GetHeaderPtr();
+bool ReadWorker::SendBufferedIo() {
+ return WriteDmUserPayload(bufsink_.GetPayloadBytesWritten());
+}
- if (!ReadDmUserHeader()) {
+bool ReadWorker::ProcessIORequest() {
+ // Read Header from dm-user misc device. This gives
+ // us the sector number for which IO is issued by dm-snapshot device
+ struct dm_user_header* header = bufsink_.GetHeaderPtr();
+ if (!android::base::ReadFully(ctrl_fd_, header, sizeof(*header))) {
+ if (errno != ENOTBLK) {
+ SNAP_PLOG(ERROR) << "Control-read failed";
+ }
+
+ SNAP_PLOG(DEBUG) << "ReadDmUserHeader failed....";
return false;
}
@@ -626,24 +539,37 @@
SNAP_LOG(DEBUG) << "Daemon: msg->type: " << std::dec << header->type;
SNAP_LOG(DEBUG) << "Daemon: msg->flags: " << std::dec << header->flags;
- switch (header->type) {
- case DM_USER_REQ_MAP_READ: {
- if (!DmuserReadRequest()) {
- return false;
- }
- break;
- }
+ // Use the same header buffer as the response header.
+ int request_type = header->type;
+ header->type = DM_USER_RESP_SUCCESS;
+ header_response_ = true;
- case DM_USER_REQ_MAP_WRITE: {
+ // Reset the output buffer.
+ bufsink_.ResetBufferOffset();
+
+ bool ok;
+ switch (request_type) {
+ case DM_USER_REQ_MAP_READ:
+ ok = DmuserReadRequest();
+ break;
+
+ case DM_USER_REQ_MAP_WRITE:
// TODO: We should not get any write request
// to dm-user as we mount all partitions
// as read-only. Need to verify how are TRIM commands
// handled during mount.
- return false;
- }
+ ok = false;
+ break;
+
+ default:
+ ok = false;
+ break;
}
- return true;
+ if (!ok && header->type != DM_USER_RESP_ERROR) {
+ RespondIOError();
+ }
+ return ok;
}
} // namespace snapshot
diff --git a/fs_mgr/libsnapshot/snapuserd/user-space-merge/read_worker.h b/fs_mgr/libsnapshot/snapuserd/user-space-merge/read_worker.h
new file mode 100644
index 0000000..8d6f663
--- /dev/null
+++ b/fs_mgr/libsnapshot/snapuserd/user-space-merge/read_worker.h
@@ -0,0 +1,71 @@
+// Copyright (C) 2023 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 <utility>
+#include <vector>
+
+#include "worker.h"
+
+namespace android {
+namespace snapshot {
+
+class ReadWorker : public Worker {
+ public:
+ ReadWorker(const std::string& cow_device, const std::string& backing_device,
+ const std::string& control_device, const std::string& misc_name,
+ const std::string& base_path_merge, std::shared_ptr<SnapshotHandler> snapuserd);
+
+ bool Run();
+ bool Init() override;
+ void CloseFds() override;
+
+ private:
+ // Functions interacting with dm-user
+ bool ProcessIORequest();
+ bool WriteDmUserPayload(size_t size);
+ bool DmuserReadRequest();
+ bool SendBufferedIo();
+ void RespondIOError();
+
+ bool ProcessCowOp(const CowOperation* cow_op, void* buffer);
+ bool ProcessXorOp(const CowOperation* cow_op, void* buffer);
+ bool ProcessOrderedOp(const CowOperation* cow_op, void* buffer);
+ bool ProcessCopyOp(const CowOperation* cow_op, void* buffer);
+ bool ProcessReplaceOp(const CowOperation* cow_op, void* buffer);
+ bool ProcessZeroOp(void* buffer);
+
+ bool ReadAlignedSector(sector_t sector, size_t sz);
+ bool ReadUnalignedSector(sector_t sector, size_t size);
+ int ReadUnalignedSector(sector_t sector, size_t size,
+ std::vector<std::pair<sector_t, const CowOperation*>>::iterator& it);
+ bool ReadFromSourceDevice(const CowOperation* cow_op, void* buffer);
+ bool ReadDataFromBaseDevice(sector_t sector, void* buffer, size_t read_size);
+
+ constexpr bool IsBlockAligned(size_t size) { return ((size & (BLOCK_SZ - 1)) == 0); }
+ constexpr sector_t ChunkToSector(chunk_t chunk) { return chunk << CHUNK_SHIFT; }
+
+ std::string backing_store_device_;
+ unique_fd backing_store_fd_;
+
+ std::string control_device_;
+ unique_fd ctrl_fd_;
+
+ bool header_response_ = false;
+ std::basic_string<uint8_t> xor_buffer_;
+};
+
+} // namespace snapshot
+} // namespace android
diff --git a/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_core.cpp b/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_core.cpp
index 2c201ff..2dd2ec0 100644
--- a/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_core.cpp
+++ b/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_core.cpp
@@ -23,6 +23,9 @@
#include <android-base/scopeguard.h>
#include <android-base/strings.h>
+#include "read_worker.h"
+#include "snapuserd_merge.h"
+
namespace android {
namespace snapshot {
@@ -46,9 +49,8 @@
bool SnapshotHandler::InitializeWorkers() {
for (int i = 0; i < num_worker_threads_; i++) {
- std::unique_ptr<Worker> wt =
- std::make_unique<Worker>(cow_device_, backing_store_device_, control_device_,
- misc_name_, base_path_merge_, GetSharedPtr());
+ auto wt = std::make_unique<ReadWorker>(cow_device_, backing_store_device_, control_device_,
+ misc_name_, base_path_merge_, GetSharedPtr());
if (!wt->Init()) {
SNAP_LOG(ERROR) << "Thread initialization failed";
return false;
@@ -57,8 +59,8 @@
worker_threads_.push_back(std::move(wt));
}
- merge_thread_ = std::make_unique<Worker>(cow_device_, backing_store_device_, control_device_,
- misc_name_, base_path_merge_, GetSharedPtr());
+ merge_thread_ = std::make_unique<MergeWorker>(cow_device_, misc_name_, base_path_merge_,
+ GetSharedPtr());
read_ahead_thread_ = std::make_unique<ReadAhead>(cow_device_, backing_store_device_, misc_name_,
GetSharedPtr());
@@ -98,8 +100,7 @@
}
} else {
reader_->UpdateMergeOpsCompleted(num_merge_ops);
- CowHeader header;
- reader_->GetHeader(&header);
+ const auto& header = reader_->GetHeader();
if (lseek(cow_fd_.get(), 0, SEEK_SET) < 0) {
SNAP_PLOG(ERROR) << "lseek failed";
@@ -154,7 +155,6 @@
bool SnapshotHandler::ReadMetadata() {
reader_ = std::make_unique<CowReader>(CowReader::ReaderFlags::USERSPACE_MERGE, true);
- CowHeader header;
CowOptions options;
SNAP_LOG(DEBUG) << "ReadMetadata: Parsing cow file";
@@ -164,11 +164,7 @@
return false;
}
- if (!reader_->GetHeader(&header)) {
- SNAP_LOG(ERROR) << "Failed to get header";
- return false;
- }
-
+ const auto& header = reader_->GetHeader();
if (!(header.block_size == BLOCK_SZ)) {
SNAP_LOG(ERROR) << "Invalid header block size found: " << header.block_size;
return false;
@@ -191,8 +187,8 @@
size_t copy_ops = 0, replace_ops = 0, zero_ops = 0, xor_ops = 0;
- while (!cowop_iter->Done()) {
- const CowOperation* cow_op = &cowop_iter->Get();
+ while (!cowop_iter->AtEnd()) {
+ const CowOperation* cow_op = cowop_iter->Get();
if (cow_op->type == kCowCopyOp) {
copy_ops += 1;
@@ -244,12 +240,11 @@
}
bool SnapshotHandler::MmapMetadata() {
- CowHeader header;
- reader_->GetHeader(&header);
+ const auto& header = reader_->GetHeader();
- total_mapped_addr_length_ = header.header_size + BUFFER_REGION_DEFAULT_SIZE;
+ total_mapped_addr_length_ = header.prefix.header_size + BUFFER_REGION_DEFAULT_SIZE;
- if (header.major_version >= 2 && header.buffer_size > 0) {
+ if (header.prefix.major_version >= 2 && header.buffer_size > 0) {
scratch_space_ = true;
}
@@ -313,17 +308,17 @@
ra_thread_status =
std::async(std::launch::async, &ReadAhead::RunThread, read_ahead_thread_.get());
- SNAP_LOG(INFO) << "Read-ahead thread started...";
+ SNAP_LOG(INFO) << "Read-ahead thread started";
}
// Launch worker threads
for (int i = 0; i < worker_threads_.size(); i++) {
threads.emplace_back(
- std::async(std::launch::async, &Worker::RunThread, worker_threads_[i].get()));
+ std::async(std::launch::async, &ReadWorker::Run, worker_threads_[i].get()));
}
std::future<bool> merge_thread =
- std::async(std::launch::async, &Worker::RunMergeThread, merge_thread_.get());
+ std::async(std::launch::async, &MergeWorker::Run, merge_thread_.get());
// Now that the worker threads are up, scan the partitions.
if (perform_verification_) {
@@ -367,10 +362,9 @@
}
uint64_t SnapshotHandler::GetBufferMetadataOffset() {
- CowHeader header;
- reader_->GetHeader(&header);
+ const auto& header = reader_->GetHeader();
- return (header.header_size + sizeof(BufferState));
+ return (header.prefix.header_size + sizeof(BufferState));
}
/*
@@ -383,8 +377,7 @@
*
*/
size_t SnapshotHandler::GetBufferMetadataSize() {
- CowHeader header;
- reader_->GetHeader(&header);
+ const auto& header = reader_->GetHeader();
size_t buffer_size = header.buffer_size;
// If there is no scratch space, then just use the
@@ -397,18 +390,16 @@
}
size_t SnapshotHandler::GetBufferDataOffset() {
- CowHeader header;
- reader_->GetHeader(&header);
+ const auto& header = reader_->GetHeader();
- return (header.header_size + GetBufferMetadataSize());
+ return (header.prefix.header_size + GetBufferMetadataSize());
}
/*
* (2MB - 8K = 2088960 bytes) will be the buffer region to hold the data.
*/
size_t SnapshotHandler::GetBufferDataSize() {
- CowHeader header;
- reader_->GetHeader(&header);
+ const auto& header = reader_->GetHeader();
size_t buffer_size = header.buffer_size;
// If there is no scratch space, then just use the
@@ -421,11 +412,10 @@
}
struct BufferState* SnapshotHandler::GetBufferState() {
- CowHeader header;
- reader_->GetHeader(&header);
+ const auto& header = reader_->GetHeader();
struct BufferState* ra_state =
- reinterpret_cast<struct BufferState*>((char*)mapped_addr_ + header.header_size);
+ reinterpret_cast<struct BufferState*>((char*)mapped_addr_ + header.prefix.header_size);
return ra_state;
}
@@ -433,11 +423,6 @@
struct utsname uts;
unsigned int major, minor;
- if (android::base::GetBoolProperty("snapuserd.test.io_uring.force_disable", false)) {
- SNAP_LOG(INFO) << "io_uring disabled for testing";
- return false;
- }
-
if ((uname(&uts) != 0) || (sscanf(uts.release, "%u.%u", &major, &minor) != 2)) {
SNAP_LOG(ERROR) << "Could not parse the kernel version from uname. "
<< " io_uring not supported";
@@ -465,5 +450,15 @@
return android::base::GetBoolProperty("ro.virtual_ab.io_uring.enabled", false);
}
+bool SnapshotHandler::CheckPartitionVerification() {
+ return update_verify_->CheckPartitionVerification();
+}
+
+void SnapshotHandler::FreeResources() {
+ worker_threads_.clear();
+ read_ahead_thread_ = nullptr;
+ merge_thread_ = nullptr;
+}
+
} // namespace snapshot
} // namespace android
diff --git a/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_core.h b/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_core.h
index 777aa07..cdc38c0 100644
--- a/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_core.h
+++ b/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_core.h
@@ -46,6 +46,8 @@
#include <snapuserd/snapuserd_buffer.h>
#include <snapuserd/snapuserd_kernel.h>
#include <storage_literals/storage_literals.h>
+#include "snapuserd_readahead.h"
+#include "snapuserd_verify.h"
namespace android {
namespace snapshot {
@@ -73,7 +75,8 @@
READ_AHEAD_FAILURE,
};
-class SnapshotHandler;
+class MergeWorker;
+class ReadWorker;
enum class MERGE_GROUP_STATE {
GROUP_MERGE_PENDING,
@@ -96,208 +99,6 @@
: merge_state_(state), num_ios_in_progress(n_ios) {}
};
-class ReadAhead {
- public:
- ReadAhead(const std::string& cow_device, const std::string& backing_device,
- const std::string& misc_name, std::shared_ptr<SnapshotHandler> snapuserd);
- bool RunThread();
-
- private:
- void InitializeRAIter();
- bool RAIterDone();
- void RAIterNext();
- void RAResetIter(uint64_t num_blocks);
- const CowOperation* GetRAOpIter();
-
- void InitializeBuffer();
- bool InitReader();
- bool InitializeFds();
-
- void CloseFds() { backing_store_fd_ = {}; }
-
- bool ReadAheadIOStart();
- int PrepareNextReadAhead(uint64_t* source_offset, int* pending_ops,
- std::vector<uint64_t>& blocks,
- std::vector<const CowOperation*>& xor_op_vec);
- bool ReconstructDataFromCow();
- void CheckOverlap(const CowOperation* cow_op);
-
- bool ReadAheadAsyncIO();
- bool ReapIoCompletions(int pending_ios_to_complete);
- bool ReadXorData(size_t block_index, size_t xor_op_index,
- std::vector<const CowOperation*>& xor_op_vec);
- void ProcessXorData(size_t& block_xor_index, size_t& xor_index,
- std::vector<const CowOperation*>& xor_op_vec, void* buffer,
- loff_t& buffer_offset);
- void UpdateScratchMetadata();
-
- bool ReadAheadSyncIO();
- bool InitializeIouring();
- void FinalizeIouring();
-
- void* read_ahead_buffer_;
- void* metadata_buffer_;
-
- std::unique_ptr<ICowOpIter> cowop_iter_;
-
- std::string cow_device_;
- std::string backing_store_device_;
- std::string misc_name_;
-
- unique_fd cow_fd_;
- unique_fd backing_store_fd_;
-
- std::shared_ptr<SnapshotHandler> snapuserd_;
- std::unique_ptr<CowReader> reader_;
-
- std::unordered_set<uint64_t> dest_blocks_;
- std::unordered_set<uint64_t> source_blocks_;
- bool overlap_;
- std::vector<uint64_t> blocks_;
- int total_blocks_merged_ = 0;
- std::unique_ptr<uint8_t[]> ra_temp_buffer_;
- std::unique_ptr<uint8_t[]> ra_temp_meta_buffer_;
- BufferSink bufsink_;
-
- uint64_t total_ra_blocks_completed_ = 0;
- bool read_ahead_async_ = false;
- // Queue depth of 8 seems optimal. We don't want
- // to have a huge depth as it may put more memory pressure
- // on the kernel worker threads given that we use
- // IOSQE_ASYNC flag - ASYNC flags can potentially
- // result in EINTR; Since we don't restart
- // syscalls and fallback to synchronous I/O, we
- // don't want huge queue depth
- int queue_depth_ = 8;
- std::unique_ptr<struct io_uring> ring_;
-};
-
-class UpdateVerify {
- public:
- UpdateVerify(const std::string& misc_name);
- void VerifyUpdatePartition();
- bool CheckPartitionVerification();
-
- private:
- enum class UpdateVerifyState {
- VERIFY_UNKNOWN,
- VERIFY_FAILED,
- VERIFY_SUCCESS,
- };
-
- std::string misc_name_;
- UpdateVerifyState state_;
- std::mutex m_lock_;
- std::condition_variable m_cv_;
-
- int kMinThreadsToVerify = 1;
- int kMaxThreadsToVerify = 4;
- uint64_t kThresholdSize = 512_MiB;
- uint64_t kBlockSizeVerify = 1_MiB;
-
- bool IsBlockAligned(uint64_t read_size) { return ((read_size & (BLOCK_SZ - 1)) == 0); }
- void UpdatePartitionVerificationState(UpdateVerifyState state);
- bool VerifyPartition(const std::string& partition_name, const std::string& dm_block_device);
- bool VerifyBlocks(const std::string& partition_name, const std::string& dm_block_device,
- off_t offset, int skip_blocks, uint64_t dev_sz);
-};
-
-class Worker {
- public:
- Worker(const std::string& cow_device, const std::string& backing_device,
- const std::string& control_device, const std::string& misc_name,
- const std::string& base_path_merge, std::shared_ptr<SnapshotHandler> snapuserd);
- bool RunThread();
- bool RunMergeThread();
- bool Init();
-
- private:
- // Initialization
- void InitializeBufsink();
- bool InitializeFds();
- bool InitReader();
- void CloseFds() {
- ctrl_fd_ = {};
- backing_store_fd_ = {};
- base_path_merge_fd_ = {};
- }
-
- // Functions interacting with dm-user
- bool ReadDmUserHeader();
- bool WriteDmUserPayload(size_t size, bool header_response);
- bool DmuserReadRequest();
-
- // IO Path
- bool ProcessIORequest();
- bool IsBlockAligned(size_t size) { return ((size & (BLOCK_SZ - 1)) == 0); }
-
- bool ReadDataFromBaseDevice(sector_t sector, size_t read_size);
- bool ReadFromSourceDevice(const CowOperation* cow_op);
-
- bool ReadAlignedSector(sector_t sector, size_t sz, bool header_response);
- bool ReadUnalignedSector(sector_t sector, size_t size);
- int ReadUnalignedSector(sector_t sector, size_t size,
- std::vector<std::pair<sector_t, const CowOperation*>>::iterator& it);
- bool RespondIOError(bool header_response);
-
- // Processing COW operations
- bool ProcessCowOp(const CowOperation* cow_op);
- bool ProcessReplaceOp(const CowOperation* cow_op);
- bool ProcessZeroOp();
-
- // Handles Copy and Xor
- bool ProcessCopyOp(const CowOperation* cow_op);
- bool ProcessXorOp(const CowOperation* cow_op);
- bool ProcessOrderedOp(const CowOperation* cow_op);
-
- // Merge related ops
- bool Merge();
- bool AsyncMerge();
- bool SyncMerge();
- bool MergeOrderedOps();
- bool MergeOrderedOpsAsync();
- bool MergeReplaceZeroOps();
- int PrepareMerge(uint64_t* source_offset, int* pending_ops,
- std::vector<const CowOperation*>* replace_zero_vec = nullptr);
-
- sector_t ChunkToSector(chunk_t chunk) { return chunk << CHUNK_SHIFT; }
- chunk_t SectorToChunk(sector_t sector) { return sector >> CHUNK_SHIFT; }
-
- bool InitializeIouring();
- void FinalizeIouring();
-
- std::unique_ptr<CowReader> reader_;
- BufferSink bufsink_;
- XorSink xorsink_;
-
- std::string cow_device_;
- std::string backing_store_device_;
- std::string control_device_;
- std::string misc_name_;
- std::string base_path_merge_;
-
- unique_fd cow_fd_;
- unique_fd backing_store_fd_;
- unique_fd base_path_merge_fd_;
- unique_fd ctrl_fd_;
-
- std::unique_ptr<ICowOpIter> cowop_iter_;
- size_t ra_block_index_ = 0;
- uint64_t blocks_merged_in_group_ = 0;
- bool merge_async_ = false;
- // Queue depth of 8 seems optimal. We don't want
- // to have a huge depth as it may put more memory pressure
- // on the kernel worker threads given that we use
- // IOSQE_ASYNC flag - ASYNC flags can potentially
- // result in EINTR; Since we don't restart
- // syscalls and fallback to synchronous I/O, we
- // don't want huge queue depth
- int queue_depth_ = 8;
- std::unique_ptr<struct io_uring> ring_;
-
- std::shared_ptr<SnapshotHandler> snapuserd_;
-};
-
class SnapshotHandler : public std::enable_shared_from_this<SnapshotHandler> {
public:
SnapshotHandler(std::string misc_name, std::string cow_device, std::string backing_device,
@@ -316,11 +117,7 @@
bool CommitMerge(int num_merge_ops);
void CloseFds() { cow_fd_ = {}; }
- void FreeResources() {
- worker_threads_.clear();
- read_ahead_thread_ = nullptr;
- merge_thread_ = nullptr;
- }
+ void FreeResources();
bool InitializeWorkers();
std::unique_ptr<CowReader> CloneReaderForWorker();
@@ -383,7 +180,7 @@
MERGE_GROUP_STATE ProcessMergingBlock(uint64_t new_block, void* buffer);
bool IsIouringSupported();
- bool CheckPartitionVerification() { return update_verify_->CheckPartitionVerification(); }
+ bool CheckPartitionVerification();
private:
bool ReadMetadata();
@@ -419,7 +216,7 @@
void* mapped_addr_;
size_t total_mapped_addr_length_;
- std::vector<std::unique_ptr<Worker>> worker_threads_;
+ std::vector<std::unique_ptr<ReadWorker>> worker_threads_;
// Read-ahead related
bool populate_data_from_cow_ = false;
bool ra_thread_ = false;
@@ -434,7 +231,7 @@
// Merge Block state
std::vector<std::unique_ptr<MergeGroupState>> merge_blk_state_;
- std::unique_ptr<Worker> merge_thread_;
+ std::unique_ptr<MergeWorker> merge_thread_;
double merge_completion_percentage_;
bool merge_initiated_ = false;
diff --git a/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_merge.cpp b/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_merge.cpp
index d57f434..517148d 100644
--- a/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_merge.cpp
+++ b/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_merge.cpp
@@ -13,6 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+#include "snapuserd_merge.h"
#include "snapuserd_core.h"
@@ -23,15 +24,20 @@
using namespace android::dm;
using android::base::unique_fd;
-int Worker::PrepareMerge(uint64_t* source_offset, int* pending_ops,
- std::vector<const CowOperation*>* replace_zero_vec) {
+MergeWorker::MergeWorker(const std::string& cow_device, const std::string& misc_name,
+ const std::string& base_path_merge,
+ std::shared_ptr<SnapshotHandler> snapuserd)
+ : Worker(cow_device, misc_name, base_path_merge, snapuserd) {}
+
+int MergeWorker::PrepareMerge(uint64_t* source_offset, int* pending_ops,
+ std::vector<const CowOperation*>* replace_zero_vec) {
int num_ops = *pending_ops;
int nr_consecutive = 0;
bool checkOrderedOp = (replace_zero_vec == nullptr);
do {
- if (!cowop_iter_->Done() && num_ops) {
- const CowOperation* cow_op = &cowop_iter_->Get();
+ if (!cowop_iter_->AtEnd() && num_ops) {
+ const CowOperation* cow_op = cowop_iter_->Get();
if (checkOrderedOp && !IsOrderedOp(*cow_op)) {
break;
}
@@ -45,8 +51,8 @@
num_ops -= 1;
nr_consecutive = 1;
- while (!cowop_iter_->Done() && num_ops) {
- const CowOperation* op = &cowop_iter_->Get();
+ while (!cowop_iter_->AtEnd() && num_ops) {
+ const CowOperation* op = cowop_iter_->Get();
if (checkOrderedOp && !IsOrderedOp(*op)) {
break;
}
@@ -70,7 +76,7 @@
return nr_consecutive;
}
-bool Worker::MergeReplaceZeroOps() {
+bool MergeWorker::MergeReplaceZeroOps() {
// Flush after merging 2MB. Since all ops are independent and there is no
// dependency between COW ops, we will flush the data and the number
// of ops merged in COW block device. If there is a crash, we will
@@ -85,7 +91,7 @@
SNAP_LOG(INFO) << "MergeReplaceZeroOps started....";
- while (!cowop_iter_->Done()) {
+ while (!cowop_iter_->AtEnd()) {
int num_ops = PAYLOAD_BUFFER_SZ / BLOCK_SZ;
std::vector<const CowOperation*> replace_zero_vec;
uint64_t source_offset;
@@ -93,26 +99,27 @@
int linear_blocks = PrepareMerge(&source_offset, &num_ops, &replace_zero_vec);
if (linear_blocks == 0) {
// Merge complete
- CHECK(cowop_iter_->Done());
+ CHECK(cowop_iter_->AtEnd());
break;
}
for (size_t i = 0; i < replace_zero_vec.size(); i++) {
const CowOperation* cow_op = replace_zero_vec[i];
+
+ void* buffer = bufsink_.AcquireBuffer(BLOCK_SZ);
+ if (!buffer) {
+ SNAP_LOG(ERROR) << "AcquireBuffer failed in MergeReplaceOps";
+ return false;
+ }
if (cow_op->type == kCowReplaceOp) {
- if (!ProcessReplaceOp(cow_op)) {
- SNAP_LOG(ERROR) << "Merge - ReplaceOp failed for block: " << cow_op->new_block;
+ if (!reader_->ReadData(cow_op, buffer, BLOCK_SZ)) {
+ SNAP_LOG(ERROR) << "Failed to read COW in merge";
return false;
}
} else {
CHECK(cow_op->type == kCowZeroOp);
- if (!ProcessZeroOp()) {
- SNAP_LOG(ERROR) << "Merge ZeroOp failed.";
- return false;
- }
+ memset(buffer, 0, BLOCK_SZ);
}
-
- bufsink_.UpdateBufferOffset(BLOCK_SZ);
}
size_t io_size = linear_blocks * BLOCK_SZ;
@@ -149,7 +156,7 @@
if (snapuserd_->IsIOTerminated()) {
SNAP_LOG(ERROR)
- << "MergeReplaceZeroOps: Worker threads terminated - shutting down merge";
+ << "MergeReplaceZeroOps: MergeWorker threads terminated - shutting down merge";
return false;
}
}
@@ -173,15 +180,15 @@
return true;
}
-bool Worker::MergeOrderedOpsAsync() {
+bool MergeWorker::MergeOrderedOpsAsync() {
void* mapped_addr = snapuserd_->GetMappedAddr();
void* read_ahead_buffer =
static_cast<void*>((char*)mapped_addr + snapuserd_->GetBufferDataOffset());
SNAP_LOG(INFO) << "MergeOrderedOpsAsync started....";
- while (!cowop_iter_->Done()) {
- const CowOperation* cow_op = &cowop_iter_->Get();
+ while (!cowop_iter_->AtEnd()) {
+ const CowOperation* cow_op = cowop_iter_->Get();
if (!IsOrderedOp(*cow_op)) {
break;
}
@@ -354,15 +361,15 @@
return true;
}
-bool Worker::MergeOrderedOps() {
+bool MergeWorker::MergeOrderedOps() {
void* mapped_addr = snapuserd_->GetMappedAddr();
void* read_ahead_buffer =
static_cast<void*>((char*)mapped_addr + snapuserd_->GetBufferDataOffset());
SNAP_LOG(INFO) << "MergeOrderedOps started....";
- while (!cowop_iter_->Done()) {
- const CowOperation* cow_op = &cowop_iter_->Get();
+ while (!cowop_iter_->AtEnd()) {
+ const CowOperation* cow_op = cowop_iter_->Get();
if (!IsOrderedOp(*cow_op)) {
break;
}
@@ -439,11 +446,11 @@
return true;
}
-bool Worker::AsyncMerge() {
+bool MergeWorker::AsyncMerge() {
if (!MergeOrderedOpsAsync()) {
SNAP_LOG(ERROR) << "MergeOrderedOpsAsync failed - Falling back to synchronous I/O";
// Reset the iter so that we retry the merge
- while (blocks_merged_in_group_ && !cowop_iter_->RDone()) {
+ while (blocks_merged_in_group_ && !cowop_iter_->AtBegin()) {
cowop_iter_->Prev();
blocks_merged_in_group_ -= 1;
}
@@ -455,7 +462,7 @@
return true;
}
-bool Worker::SyncMerge() {
+bool MergeWorker::SyncMerge() {
if (!MergeOrderedOps()) {
SNAP_LOG(ERROR) << "Merge failed for ordered ops";
return false;
@@ -465,7 +472,7 @@
return true;
}
-bool Worker::Merge() {
+bool MergeWorker::Merge() {
cowop_iter_ = reader_->GetOpIter(true);
bool retry = false;
@@ -511,7 +518,7 @@
return true;
}
-bool Worker::InitializeIouring() {
+bool MergeWorker::InitializeIouring() {
if (!snapuserd_->IsIouringSupported()) {
return false;
}
@@ -530,13 +537,13 @@
return true;
}
-void Worker::FinalizeIouring() {
+void MergeWorker::FinalizeIouring() {
if (merge_async_) {
io_uring_queue_exit(ring_.get());
}
}
-bool Worker::RunMergeThread() {
+bool MergeWorker::Run() {
SNAP_LOG(DEBUG) << "Waiting for merge begin...";
if (!snapuserd_->WaitForMergeBegin()) {
SNAP_LOG(ERROR) << "Merge terminated early...";
diff --git a/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_merge.h b/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_merge.h
new file mode 100644
index 0000000..f35147f
--- /dev/null
+++ b/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_merge.h
@@ -0,0 +1,58 @@
+// Copyright (C) 2023 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 "worker.h"
+
+#include <liburing.h>
+
+namespace android {
+namespace snapshot {
+
+class MergeWorker : public Worker {
+ public:
+ MergeWorker(const std::string& cow_device, const std::string& misc_name,
+ const std::string& base_path_merge, std::shared_ptr<SnapshotHandler> snapuserd);
+ bool Run();
+
+ private:
+ int PrepareMerge(uint64_t* source_offset, int* pending_ops,
+ std::vector<const CowOperation*>* replace_zero_vec = nullptr);
+ bool MergeReplaceZeroOps();
+ bool MergeOrderedOps();
+ bool MergeOrderedOpsAsync();
+ bool Merge();
+ bool AsyncMerge();
+ bool SyncMerge();
+ bool InitializeIouring();
+ void FinalizeIouring();
+
+ private:
+ std::unique_ptr<ICowOpIter> cowop_iter_;
+ std::unique_ptr<struct io_uring> ring_;
+ size_t ra_block_index_ = 0;
+ uint64_t blocks_merged_in_group_ = 0;
+ bool merge_async_ = false;
+ // Queue depth of 8 seems optimal. We don't want
+ // to have a huge depth as it may put more memory pressure
+ // on the kernel worker threads given that we use
+ // IOSQE_ASYNC flag - ASYNC flags can potentially
+ // result in EINTR; Since we don't restart
+ // syscalls and fallback to synchronous I/O, we
+ // don't want huge queue depth
+ int queue_depth_ = 8;
+};
+
+} // namespace snapshot
+} // namespace android
diff --git a/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_readahead.cpp b/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_readahead.cpp
index fbe57d2..8755820 100644
--- a/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_readahead.cpp
+++ b/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_readahead.cpp
@@ -14,6 +14,8 @@
* limitations under the License.
*/
+#include "snapuserd_readahead.h"
+
#include "snapuserd_core.h"
namespace android {
@@ -32,14 +34,17 @@
}
void ReadAhead::CheckOverlap(const CowOperation* cow_op) {
- uint64_t source_block = cow_op->source;
- uint64_t source_offset = 0;
- if (cow_op->type == kCowXorOp) {
- source_block /= BLOCK_SZ;
- source_offset = cow_op->source % BLOCK_SZ;
+ uint64_t source_offset;
+ if (!reader_->GetSourceOffset(cow_op, &source_offset)) {
+ SNAP_LOG(ERROR) << "ReadAhead operation has no source offset: " << *cow_op;
+ return;
}
+
+ uint64_t source_block = GetBlockFromOffset(header_, source_offset);
+ bool misaligned = (GetBlockRelativeOffset(header_, source_offset) != 0);
+
if (dest_blocks_.count(cow_op->new_block) || source_blocks_.count(source_block) ||
- (source_offset > 0 && source_blocks_.count(source_block + 1))) {
+ (misaligned && source_blocks_.count(source_block + 1))) {
overlap_ = true;
}
@@ -64,11 +69,12 @@
// Get the first block with offset
const CowOperation* cow_op = GetRAOpIter();
- *source_offset = cow_op->source;
- if (cow_op->type == kCowCopyOp) {
- *source_offset *= BLOCK_SZ;
- } else if (cow_op->type == kCowXorOp) {
+ if (!reader_->GetSourceOffset(cow_op, source_offset)) {
+ SNAP_LOG(ERROR) << "PrepareNextReadAhead operation has no source offset: " << *cow_op;
+ return nr_consecutive;
+ }
+ if (cow_op->type == kCowXorOp) {
xor_op_vec.push_back(cow_op);
}
@@ -86,10 +92,10 @@
*/
while (!RAIterDone() && num_ops) {
const CowOperation* op = GetRAOpIter();
- uint64_t next_offset = op->source;
-
- if (cow_op->type == kCowCopyOp) {
- next_offset *= BLOCK_SZ;
+ uint64_t next_offset;
+ if (!reader_->GetSourceOffset(op, &next_offset)) {
+ SNAP_LOG(ERROR) << "PrepareNextReadAhead operation has no source offset: " << *cow_op;
+ break;
}
// Check for consecutive blocks
@@ -114,6 +120,24 @@
return nr_consecutive;
}
+class [[nodiscard]] AutoNotifyReadAheadFailed {
+ public:
+ AutoNotifyReadAheadFailed(std::shared_ptr<SnapshotHandler> snapuserd) : snapuserd_(snapuserd) {}
+
+ ~AutoNotifyReadAheadFailed() {
+ if (cancelled_) {
+ return;
+ }
+ snapuserd_->ReadAheadIOFailed();
+ }
+
+ void Cancel() { cancelled_ = true; }
+
+ private:
+ std::shared_ptr<SnapshotHandler> snapuserd_;
+ bool cancelled_ = false;
+};
+
bool ReadAhead::ReconstructDataFromCow() {
std::unordered_map<uint64_t, void*>& read_ahead_buffer_map = snapuserd_->GetReadAheadMap();
loff_t metadata_offset = 0;
@@ -145,6 +169,8 @@
metadata_offset += sizeof(struct ScratchMetadata);
}
+ AutoNotifyReadAheadFailed notify_read_ahead_failed(snapuserd_);
+
// We are done re-constructing the mapping; however, we need to make sure
// all the COW operations to-be merged are present in the re-constructed
// mapping.
@@ -162,7 +188,6 @@
if (!(num_ops == 0)) {
SNAP_LOG(ERROR) << "ReconstructDataFromCow failed. Not all ops recoverd "
<< " Pending ops: " << num_ops;
- snapuserd_->ReadAheadIOFailed();
return false;
}
@@ -175,11 +200,11 @@
if (!snapuserd_->ReadAheadIOCompleted(true)) {
SNAP_LOG(ERROR) << "ReadAheadIOCompleted failed...";
- snapuserd_->ReadAheadIOFailed();
return false;
}
SNAP_LOG(INFO) << "ReconstructDataFromCow success";
+ notify_read_ahead_failed.Cancel();
return true;
}
@@ -467,14 +492,20 @@
if (xor_op_index < xor_op_vec.size()) {
const CowOperation* xor_op = xor_op_vec[xor_op_index];
if (xor_op->new_block == new_block) {
- if (!reader_->ReadData(*xor_op, &bufsink_)) {
+ void* buffer = bufsink_.AcquireBuffer(BLOCK_SZ);
+ if (!buffer) {
+ SNAP_LOG(ERROR) << "ReadAhead - failed to allocate buffer for block: "
+ << xor_op->new_block;
+ return false;
+ }
+ if (ssize_t rv = reader_->ReadData(xor_op, buffer, BLOCK_SZ); rv != BLOCK_SZ) {
SNAP_LOG(ERROR)
- << " ReadAhead - XorOp Read failed for block: " << xor_op->new_block;
+ << " ReadAhead - XorOp Read failed for block: " << xor_op->new_block
+ << ", return value: " << rv;
return false;
}
xor_op_index += 1;
- bufsink_.UpdateBufferOffset(BLOCK_SZ);
}
}
block_index += 1;
@@ -492,6 +523,8 @@
blocks_.clear();
std::vector<const CowOperation*> xor_op_vec;
+ AutoNotifyReadAheadFailed notify_read_ahead_failed(snapuserd_);
+
bufsink_.ResetBufferOffset();
// Number of ops to be merged in this window. This is a fixed size
@@ -518,8 +551,6 @@
<< " offset :" << source_offset % BLOCK_SZ
<< " buffer_offset : " << buffer_offset << " io_size : " << io_size
<< " buf-addr : " << read_ahead_buffer_;
-
- snapuserd_->ReadAheadIOFailed();
return false;
}
@@ -530,6 +561,7 @@
// Done with merging ordered ops
if (RAIterDone() && total_blocks_merged_ == 0) {
+ notify_read_ahead_failed.Cancel();
return true;
}
@@ -560,20 +592,25 @@
// Check if this block is an XOR op
if (xor_op->new_block == new_block) {
// Read the xor'ed data from COW
- if (!reader_->ReadData(*xor_op, &bufsink)) {
+ void* buffer = bufsink.GetPayloadBuffer(BLOCK_SZ);
+ if (!buffer) {
+ SNAP_LOG(ERROR) << "ReadAhead - failed to allocate buffer";
+ return false;
+ }
+ if (ssize_t rv = reader_->ReadData(xor_op, buffer, BLOCK_SZ); rv != BLOCK_SZ) {
SNAP_LOG(ERROR)
- << " ReadAhead - XorOp Read failed for block: " << xor_op->new_block;
- snapuserd_->ReadAheadIOFailed();
+ << " ReadAhead - XorOp Read failed for block: " << xor_op->new_block
+ << ", return value: " << rv;
return false;
}
// Pointer to the data read from base device
- uint8_t* buffer = reinterpret_cast<uint8_t*>(bufptr);
+ uint8_t* read_buffer = reinterpret_cast<uint8_t*>(bufptr);
// Get the xor'ed data read from COW device
uint8_t* xor_data = reinterpret_cast<uint8_t*>(bufsink.GetPayloadBufPtr());
// Retrieve the original data
for (size_t byte_offset = 0; byte_offset < BLOCK_SZ; byte_offset++) {
- buffer[byte_offset] ^= xor_data[byte_offset];
+ read_buffer[byte_offset] ^= xor_data[byte_offset];
}
// Move to next XOR op
@@ -604,6 +641,7 @@
bm->new_block = 0;
bm->file_offset = 0;
+ notify_read_ahead_failed.Cancel();
return true;
}
@@ -768,6 +806,7 @@
if (!reader_->InitForMerge(std::move(cow_fd_))) {
return false;
}
+ header_ = reader_->GetHeader();
return true;
}
@@ -776,7 +815,7 @@
}
bool ReadAhead::RAIterDone() {
- if (cowop_iter_->Done()) {
+ if (cowop_iter_->AtEnd()) {
return true;
}
@@ -794,15 +833,14 @@
}
void ReadAhead::RAResetIter(uint64_t num_blocks) {
- while (num_blocks && !cowop_iter_->RDone()) {
+ while (num_blocks && !cowop_iter_->AtBegin()) {
cowop_iter_->Prev();
num_blocks -= 1;
}
}
const CowOperation* ReadAhead::GetRAOpIter() {
- const CowOperation* cow_op = &cowop_iter_->Get();
- return cow_op;
+ return cowop_iter_->Get();
}
void ReadAhead::InitializeBuffer() {
diff --git a/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_readahead.h b/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_readahead.h
new file mode 100644
index 0000000..d3ba126
--- /dev/null
+++ b/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_readahead.h
@@ -0,0 +1,113 @@
+// Copyright (C) 2023 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 <stddef.h>
+#include <stdint.h>
+
+#include <memory>
+#include <string>
+#include <unordered_set>
+#include <vector>
+
+#include <android-base/unique_fd.h>
+#include <libsnapshot/cow_reader.h>
+#include <liburing.h>
+#include <snapuserd/snapuserd_buffer.h>
+
+namespace android {
+namespace snapshot {
+
+class SnapshotHandler;
+
+class ReadAhead {
+ public:
+ ReadAhead(const std::string& cow_device, const std::string& backing_device,
+ const std::string& misc_name, std::shared_ptr<SnapshotHandler> snapuserd);
+ bool RunThread();
+
+ private:
+ void InitializeRAIter();
+ bool RAIterDone();
+ void RAIterNext();
+ void RAResetIter(uint64_t num_blocks);
+ const CowOperation* GetRAOpIter();
+
+ void InitializeBuffer();
+ bool InitReader();
+ bool InitializeFds();
+
+ void CloseFds() { backing_store_fd_ = {}; }
+
+ bool ReadAheadIOStart();
+ int PrepareNextReadAhead(uint64_t* source_offset, int* pending_ops,
+ std::vector<uint64_t>& blocks,
+ std::vector<const CowOperation*>& xor_op_vec);
+ bool ReconstructDataFromCow();
+ void CheckOverlap(const CowOperation* cow_op);
+
+ bool ReadAheadAsyncIO();
+ bool ReapIoCompletions(int pending_ios_to_complete);
+ bool ReadXorData(size_t block_index, size_t xor_op_index,
+ std::vector<const CowOperation*>& xor_op_vec);
+ void ProcessXorData(size_t& block_xor_index, size_t& xor_index,
+ std::vector<const CowOperation*>& xor_op_vec, void* buffer,
+ loff_t& buffer_offset);
+ void UpdateScratchMetadata();
+
+ bool ReadAheadSyncIO();
+ bool InitializeIouring();
+ void FinalizeIouring();
+
+ void* read_ahead_buffer_;
+ void* metadata_buffer_;
+
+ std::unique_ptr<ICowOpIter> cowop_iter_;
+
+ std::string cow_device_;
+ std::string backing_store_device_;
+ std::string misc_name_;
+
+ android::base::unique_fd cow_fd_;
+ android::base::unique_fd backing_store_fd_;
+
+ std::shared_ptr<SnapshotHandler> snapuserd_;
+ std::unique_ptr<CowReader> reader_;
+ CowHeader header_;
+
+ std::unordered_set<uint64_t> dest_blocks_;
+ std::unordered_set<uint64_t> source_blocks_;
+ bool overlap_;
+ std::vector<uint64_t> blocks_;
+ int total_blocks_merged_ = 0;
+ std::unique_ptr<uint8_t[]> ra_temp_buffer_;
+ std::unique_ptr<uint8_t[]> ra_temp_meta_buffer_;
+ BufferSink bufsink_;
+
+ uint64_t total_ra_blocks_completed_ = 0;
+ bool read_ahead_async_ = false;
+ // Queue depth of 8 seems optimal. We don't want
+ // to have a huge depth as it may put more memory pressure
+ // on the kernel worker threads given that we use
+ // IOSQE_ASYNC flag - ASYNC flags can potentially
+ // result in EINTR; Since we don't restart
+ // syscalls and fallback to synchronous I/O, we
+ // don't want huge queue depth
+ int queue_depth_ = 8;
+ std::unique_ptr<struct io_uring> ring_;
+};
+
+} // namespace snapshot
+} // namespace android
diff --git a/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_server.cpp b/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_server.cpp
index b7ce210..c953f1a 100644
--- a/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_server.cpp
+++ b/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_server.cpp
@@ -45,28 +45,9 @@
using android::base::borrowed_fd;
using android::base::unique_fd;
-DaemonOps UserSnapshotServer::Resolveop(std::string& input) {
- if (input == "init") return DaemonOps::INIT;
- if (input == "start") return DaemonOps::START;
- if (input == "stop") return DaemonOps::STOP;
- if (input == "query") return DaemonOps::QUERY;
- if (input == "delete") return DaemonOps::DELETE;
- if (input == "detach") return DaemonOps::DETACH;
- if (input == "supports") return DaemonOps::SUPPORTS;
- if (input == "initiate_merge") return DaemonOps::INITIATE;
- if (input == "merge_percent") return DaemonOps::PERCENTAGE;
- if (input == "getstatus") return DaemonOps::GETSTATUS;
- if (input == "update-verify") return DaemonOps::UPDATE_VERIFY;
-
- return DaemonOps::INVALID;
-}
-
UserSnapshotServer::UserSnapshotServer() {
- monitor_merge_event_fd_.reset(eventfd(0, EFD_CLOEXEC));
- if (monitor_merge_event_fd_ == -1) {
- PLOG(FATAL) << "monitor_merge_event_fd_: failed to create eventfd";
- }
terminating_ = false;
+ handlers_ = std::make_unique<SnapshotHandlerManager>();
}
UserSnapshotServer::~UserSnapshotServer() {
@@ -99,12 +80,9 @@
void UserSnapshotServer::ShutdownThreads() {
terminating_ = true;
- JoinAllThreads();
+ handlers_->JoinAllThreads();
}
-HandlerThread::HandlerThread(std::shared_ptr<SnapshotHandler> snapuserd)
- : snapuserd_(snapuserd), misc_name_(snapuserd_->GetMiscName()) {}
-
bool UserSnapshotServer::Sendmsg(android::base::borrowed_fd fd, const std::string& msg) {
ssize_t ret = TEMP_FAILURE_RETRY(send(fd.get(), msg.data(), msg.size(), MSG_NOSIGNAL));
if (ret < 0) {
@@ -135,226 +113,118 @@
std::vector<std::string> out;
Parsemsg(str, delim, out);
- DaemonOps op = Resolveop(out[0]);
- switch (op) {
- case DaemonOps::INIT: {
- // Message format:
- // init,<misc_name>,<cow_device_path>,<backing_device>,<base_path_merge>
- //
- // Reads the metadata and send the number of sectors
- if (out.size() != 5) {
- LOG(ERROR) << "Malformed init message, " << out.size() << " parts";
- return Sendmsg(fd, "fail");
- }
-
- auto handler = AddHandler(out[1], out[2], out[3], out[4]);
- if (!handler) {
- return Sendmsg(fd, "fail");
- }
-
- auto retval = "success," + std::to_string(handler->snapuserd()->GetNumSectors());
- return Sendmsg(fd, retval);
- }
- case DaemonOps::START: {
- // Message format:
- // start,<misc_name>
- //
- // Start the new thread which binds to dm-user misc device
- if (out.size() != 2) {
- LOG(ERROR) << "Malformed start message, " << out.size() << " parts";
- return Sendmsg(fd, "fail");
- }
-
- std::lock_guard<std::mutex> lock(lock_);
- auto iter = FindHandler(&lock, out[1]);
- if (iter == dm_users_.end()) {
- LOG(ERROR) << "Could not find handler: " << out[1];
- return Sendmsg(fd, "fail");
- }
- if (!(*iter)->snapuserd() || (*iter)->snapuserd()->IsAttached()) {
- LOG(ERROR) << "Tried to re-attach control device: " << out[1];
- return Sendmsg(fd, "fail");
- }
- if (!StartHandler(*iter)) {
- return Sendmsg(fd, "fail");
- }
- return Sendmsg(fd, "success");
- }
- case DaemonOps::STOP: {
- // Message format: stop
- //
- // Stop all the threads gracefully and then shutdown the
- // main thread
- SetTerminating();
- ShutdownThreads();
- return true;
- }
- case DaemonOps::QUERY: {
- // Message format: query
- //
- // As part of transition, Second stage daemon will be
- // created before terminating the first stage daemon. Hence,
- // for a brief period client may have to distiguish between
- // first stage daemon and second stage daemon.
- //
- // Second stage daemon is marked as active and hence will
- // be ready to receive control message.
- return Sendmsg(fd, GetDaemonStatus());
- }
- case DaemonOps::DELETE: {
- // Message format:
- // delete,<misc_name>
- if (out.size() != 2) {
- LOG(ERROR) << "Malformed delete message, " << out.size() << " parts";
- return Sendmsg(fd, "fail");
- }
- {
- std::lock_guard<std::mutex> lock(lock_);
- auto iter = FindHandler(&lock, out[1]);
- if (iter == dm_users_.end()) {
- // After merge is completed, we swap dm-user table with
- // the underlying dm-linear base device. Hence, worker
- // threads would have terminted and was removed from
- // the list.
- LOG(DEBUG) << "Could not find handler: " << out[1];
- return Sendmsg(fd, "success");
- }
-
- if (!(*iter)->ThreadTerminated()) {
- (*iter)->snapuserd()->NotifyIOTerminated();
- }
- }
- if (!RemoveAndJoinHandler(out[1])) {
- return Sendmsg(fd, "fail");
- }
- return Sendmsg(fd, "success");
- }
- case DaemonOps::DETACH: {
- std::lock_guard<std::mutex> lock(lock_);
- TerminateMergeThreads(&lock);
- terminating_ = true;
- return true;
- }
- case DaemonOps::SUPPORTS: {
- if (out.size() != 2) {
- LOG(ERROR) << "Malformed supports message, " << out.size() << " parts";
- return Sendmsg(fd, "fail");
- }
- if (out[1] == "second_stage_socket_handoff") {
- return Sendmsg(fd, "success");
- }
+ const auto& cmd = out[0];
+ if (cmd == "init") {
+ // Message format:
+ // init,<misc_name>,<cow_device_path>,<backing_device>,<base_path_merge>
+ //
+ // Reads the metadata and send the number of sectors
+ if (out.size() != 5) {
+ LOG(ERROR) << "Malformed init message, " << out.size() << " parts";
return Sendmsg(fd, "fail");
}
- case DaemonOps::INITIATE: {
- if (out.size() != 2) {
- LOG(ERROR) << "Malformed initiate-merge message, " << out.size() << " parts";
- return Sendmsg(fd, "fail");
- }
- if (out[0] == "initiate_merge") {
- std::lock_guard<std::mutex> lock(lock_);
- auto iter = FindHandler(&lock, out[1]);
- if (iter == dm_users_.end()) {
- LOG(ERROR) << "Could not find handler: " << out[1];
- return Sendmsg(fd, "fail");
- }
- if (!StartMerge(&lock, *iter)) {
- return Sendmsg(fd, "fail");
- }
-
- return Sendmsg(fd, "success");
- }
+ auto handler = AddHandler(out[1], out[2], out[3], out[4]);
+ if (!handler) {
return Sendmsg(fd, "fail");
}
- case DaemonOps::PERCENTAGE: {
- std::lock_guard<std::mutex> lock(lock_);
- double percentage = GetMergePercentage(&lock);
- return Sendmsg(fd, std::to_string(percentage));
+ auto retval = "success," + std::to_string(handler->snapuserd()->GetNumSectors());
+ return Sendmsg(fd, retval);
+ } else if (cmd == "start") {
+ // Message format:
+ // start,<misc_name>
+ //
+ // Start the new thread which binds to dm-user misc device
+ if (out.size() != 2) {
+ LOG(ERROR) << "Malformed start message, " << out.size() << " parts";
+ return Sendmsg(fd, "fail");
}
- case DaemonOps::GETSTATUS: {
- // Message format:
- // getstatus,<misc_name>
- if (out.size() != 2) {
- LOG(ERROR) << "Malformed delete message, " << out.size() << " parts";
- return Sendmsg(fd, "snapshot-merge-failed");
- }
- {
- std::lock_guard<std::mutex> lock(lock_);
- auto iter = FindHandler(&lock, out[1]);
- if (iter == dm_users_.end()) {
- LOG(ERROR) << "Could not find handler: " << out[1];
- return Sendmsg(fd, "snapshot-merge-failed");
- }
- std::string merge_status = GetMergeStatus(*iter);
- return Sendmsg(fd, merge_status);
- }
+ if (!handlers_->StartHandler(out[1])) {
+ return Sendmsg(fd, "fail");
}
- case DaemonOps::UPDATE_VERIFY: {
- std::lock_guard<std::mutex> lock(lock_);
- if (!UpdateVerification(&lock)) {
- return Sendmsg(fd, "fail");
- }
-
+ return Sendmsg(fd, "success");
+ } else if (cmd == "stop") {
+ // Message format: stop
+ //
+ // Stop all the threads gracefully and then shutdown the
+ // main thread
+ SetTerminating();
+ ShutdownThreads();
+ return true;
+ } else if (cmd == "query") {
+ // Message format: query
+ //
+ // As part of transition, Second stage daemon will be
+ // created before terminating the first stage daemon. Hence,
+ // for a brief period client may have to distiguish between
+ // first stage daemon and second stage daemon.
+ //
+ // Second stage daemon is marked as active and hence will
+ // be ready to receive control message.
+ return Sendmsg(fd, GetDaemonStatus());
+ } else if (cmd == "delete") {
+ // Message format:
+ // delete,<misc_name>
+ if (out.size() != 2) {
+ LOG(ERROR) << "Malformed delete message, " << out.size() << " parts";
+ return Sendmsg(fd, "fail");
+ }
+ if (!handlers_->DeleteHandler(out[1])) {
+ return Sendmsg(fd, "fail");
+ }
+ return Sendmsg(fd, "success");
+ } else if (cmd == "detach") {
+ handlers_->TerminateMergeThreads();
+ terminating_ = true;
+ return true;
+ } else if (cmd == "supports") {
+ if (out.size() != 2) {
+ LOG(ERROR) << "Malformed supports message, " << out.size() << " parts";
+ return Sendmsg(fd, "fail");
+ }
+ if (out[1] == "second_stage_socket_handoff") {
return Sendmsg(fd, "success");
}
- default: {
- LOG(ERROR) << "Received unknown message type from client";
- Sendmsg(fd, "fail");
- return false;
+ return Sendmsg(fd, "fail");
+ } else if (cmd == "initiate_merge") {
+ if (out.size() != 2) {
+ LOG(ERROR) << "Malformed initiate-merge message, " << out.size() << " parts";
+ return Sendmsg(fd, "fail");
}
- }
-}
-
-void UserSnapshotServer::RunThread(std::shared_ptr<HandlerThread> handler) {
- LOG(INFO) << "Entering thread for handler: " << handler->misc_name();
-
- if (!handler->snapuserd()->Start()) {
- LOG(ERROR) << " Failed to launch all worker threads";
- }
-
- handler->snapuserd()->CloseFds();
- bool merge_completed = handler->snapuserd()->CheckMergeCompletionStatus();
- handler->snapuserd()->UnmapBufferRegion();
-
- auto misc_name = handler->misc_name();
- LOG(INFO) << "Handler thread about to exit: " << misc_name;
-
- {
- std::lock_guard<std::mutex> lock(lock_);
- if (merge_completed) {
- num_partitions_merge_complete_ += 1;
- active_merge_threads_ -= 1;
- WakeupMonitorMergeThread();
+ if (out[0] == "initiate_merge") {
+ if (!handlers_->InitiateMerge(out[1])) {
+ return Sendmsg(fd, "fail");
+ }
+ return Sendmsg(fd, "success");
}
- handler->SetThreadTerminated();
- auto iter = FindHandler(&lock, handler->misc_name());
- if (iter == dm_users_.end()) {
- // RemoveAndJoinHandler() already removed us from the list, and is
- // now waiting on a join(), so just return. Additionally, release
- // all the resources held by snapuserd object which are shared
- // by worker threads. This should be done when the last reference
- // of "handler" is released; but we will explicitly release here
- // to make sure snapuserd object is freed as it is the biggest
- // consumer of memory in the daemon.
- handler->FreeResources();
- LOG(INFO) << "Exiting handler thread to allow for join: " << misc_name;
- return;
+ return Sendmsg(fd, "fail");
+ } else if (cmd == "merge_percent") {
+ double percentage = handlers_->GetMergePercentage();
+ return Sendmsg(fd, std::to_string(percentage));
+ } else if (cmd == "getstatus") {
+ // Message format:
+ // getstatus,<misc_name>
+ if (out.size() != 2) {
+ LOG(ERROR) << "Malformed delete message, " << out.size() << " parts";
+ return Sendmsg(fd, "snapshot-merge-failed");
}
-
- LOG(INFO) << "Exiting handler thread and freeing resources: " << misc_name;
-
- if (handler->snapuserd()->IsAttached()) {
- handler->thread().detach();
+ auto status = handlers_->GetMergeStatus(out[1]);
+ if (status.empty()) {
+ return Sendmsg(fd, "snapshot-merge-failed");
}
-
- // Important: free resources within the lock. This ensures that if
- // WaitForDelete() is called, the handler is either in the list, or
- // it's not and its resources are guaranteed to be freed.
- handler->FreeResources();
- dm_users_.erase(iter);
+ return Sendmsg(fd, status);
+ } else if (cmd == "update-verify") {
+ if (!handlers_->GetVerificationStatus()) {
+ return Sendmsg(fd, "fail");
+ }
+ return Sendmsg(fd, "success");
+ } else {
+ LOG(ERROR) << "Received unknown message type from client";
+ Sendmsg(fd, "fail");
+ return false;
}
}
@@ -423,28 +293,10 @@
}
}
- JoinAllThreads();
+ handlers_->JoinAllThreads();
return true;
}
-void UserSnapshotServer::JoinAllThreads() {
- // Acquire the thread list within the lock.
- std::vector<std::shared_ptr<HandlerThread>> dm_users;
- {
- std::lock_guard<std::mutex> guard(lock_);
- dm_users = std::move(dm_users_);
- }
-
- for (auto& client : dm_users) {
- auto& th = client->thread();
-
- if (th.joinable()) th.join();
- }
-
- stop_monitor_merge_thread_ = true;
- WakeupMonitorMergeThread();
-}
-
void UserSnapshotServer::AddWatchedFd(android::base::borrowed_fd fd, int events) {
struct pollfd p = {};
p.fd = fd.get();
@@ -506,185 +358,13 @@
perform_verification = false;
}
- auto snapuserd = std::make_shared<SnapshotHandler>(misc_name, cow_device_path, backing_device,
- base_path_merge, num_worker_threads,
- io_uring_enabled_, perform_verification);
- if (!snapuserd->InitCowDevice()) {
- LOG(ERROR) << "Failed to initialize Snapuserd";
- return nullptr;
- }
-
- if (!snapuserd->InitializeWorkers()) {
- LOG(ERROR) << "Failed to initialize workers";
- return nullptr;
- }
-
- auto handler = std::make_shared<HandlerThread>(snapuserd);
- {
- std::lock_guard<std::mutex> lock(lock_);
- if (FindHandler(&lock, misc_name) != dm_users_.end()) {
- LOG(ERROR) << "Handler already exists: " << misc_name;
- return nullptr;
- }
- dm_users_.push_back(handler);
- }
- return handler;
-}
-
-bool UserSnapshotServer::StartHandler(const std::shared_ptr<HandlerThread>& handler) {
- if (handler->snapuserd()->IsAttached()) {
- LOG(ERROR) << "Handler already attached";
- return false;
- }
-
- handler->snapuserd()->AttachControlDevice();
-
- handler->thread() = std::thread(std::bind(&UserSnapshotServer::RunThread, this, handler));
- return true;
-}
-
-bool UserSnapshotServer::StartMerge(std::lock_guard<std::mutex>* proof_of_lock,
- const std::shared_ptr<HandlerThread>& handler) {
- CHECK(proof_of_lock);
-
- if (!handler->snapuserd()->IsAttached()) {
- LOG(ERROR) << "Handler not attached to dm-user - Merge thread cannot be started";
- return false;
- }
-
- handler->snapuserd()->MonitorMerge();
-
- if (!is_merge_monitor_started_.has_value()) {
- std::thread(&UserSnapshotServer::MonitorMerge, this).detach();
- is_merge_monitor_started_ = true;
- }
-
- merge_handlers_.push(handler);
- WakeupMonitorMergeThread();
- return true;
-}
-
-auto UserSnapshotServer::FindHandler(std::lock_guard<std::mutex>* proof_of_lock,
- const std::string& misc_name) -> HandlerList::iterator {
- CHECK(proof_of_lock);
-
- for (auto iter = dm_users_.begin(); iter != dm_users_.end(); iter++) {
- if ((*iter)->misc_name() == misc_name) {
- return iter;
- }
- }
- return dm_users_.end();
-}
-
-void UserSnapshotServer::TerminateMergeThreads(std::lock_guard<std::mutex>* proof_of_lock) {
- CHECK(proof_of_lock);
-
- for (auto iter = dm_users_.begin(); iter != dm_users_.end(); iter++) {
- if (!(*iter)->ThreadTerminated()) {
- (*iter)->snapuserd()->NotifyIOTerminated();
- }
- }
-}
-
-std::string UserSnapshotServer::GetMergeStatus(const std::shared_ptr<HandlerThread>& handler) {
- return handler->snapuserd()->GetMergeStatus();
-}
-
-double UserSnapshotServer::GetMergePercentage(std::lock_guard<std::mutex>* proof_of_lock) {
- CHECK(proof_of_lock);
- double percentage = 0.0;
- int n = 0;
-
- for (auto iter = dm_users_.begin(); iter != dm_users_.end(); iter++) {
- auto& th = (*iter)->thread();
- if (th.joinable()) {
- // Merge percentage by individual partitions wherein merge is still
- // in-progress
- percentage += (*iter)->snapuserd()->GetMergePercentage();
- n += 1;
- }
- }
-
- // Calculate final merge including those partitions where merge was already
- // completed - num_partitions_merge_complete_ will track them when each
- // thread exists in RunThread.
- int total_partitions = n + num_partitions_merge_complete_;
-
- if (total_partitions) {
- percentage = ((num_partitions_merge_complete_ * 100.0) + percentage) / total_partitions;
- }
-
- LOG(DEBUG) << "Merge %: " << percentage
- << " num_partitions_merge_complete_: " << num_partitions_merge_complete_
- << " total_partitions: " << total_partitions << " n: " << n;
- return percentage;
-}
-
-bool UserSnapshotServer::RemoveAndJoinHandler(const std::string& misc_name) {
- std::shared_ptr<HandlerThread> handler;
- {
- std::lock_guard<std::mutex> lock(lock_);
-
- auto iter = FindHandler(&lock, misc_name);
- if (iter == dm_users_.end()) {
- // Client already deleted.
- return true;
- }
- handler = std::move(*iter);
- dm_users_.erase(iter);
- }
-
- auto& th = handler->thread();
- if (th.joinable()) {
- th.join();
- }
- return true;
-}
-
-void UserSnapshotServer::WakeupMonitorMergeThread() {
- uint64_t notify = 1;
- ssize_t rc = TEMP_FAILURE_RETRY(write(monitor_merge_event_fd_.get(), ¬ify, sizeof(notify)));
- if (rc < 0) {
- PLOG(FATAL) << "failed to notify monitor merge thread";
- }
-}
-
-void UserSnapshotServer::MonitorMerge() {
- while (!stop_monitor_merge_thread_) {
- uint64_t testVal;
- ssize_t ret =
- TEMP_FAILURE_RETRY(read(monitor_merge_event_fd_.get(), &testVal, sizeof(testVal)));
- if (ret == -1) {
- PLOG(FATAL) << "Failed to read from eventfd";
- } else if (ret == 0) {
- LOG(FATAL) << "Hit EOF on eventfd";
- }
-
- LOG(INFO) << "MonitorMerge: active-merge-threads: " << active_merge_threads_;
- {
- std::lock_guard<std::mutex> lock(lock_);
- while (active_merge_threads_ < kMaxMergeThreads && merge_handlers_.size() > 0) {
- auto handler = merge_handlers_.front();
- merge_handlers_.pop();
-
- if (!handler->snapuserd()) {
- LOG(INFO) << "MonitorMerge: skipping deleted handler: " << handler->misc_name();
- continue;
- }
-
- LOG(INFO) << "Starting merge for partition: "
- << handler->snapuserd()->GetMiscName();
- handler->snapuserd()->InitiateMerge();
- active_merge_threads_ += 1;
- }
- }
- }
-
- LOG(INFO) << "Exiting MonitorMerge: size: " << merge_handlers_.size();
+ return handlers_->AddHandler(misc_name, cow_device_path, backing_device, base_path_merge,
+ num_worker_threads, io_uring_enabled_, perform_verification);
}
bool UserSnapshotServer::WaitForSocket() {
- auto scope_guard = android::base::make_scope_guard([this]() -> void { JoinAllThreads(); });
+ auto scope_guard =
+ android::base::make_scope_guard([this]() -> void { handlers_->JoinAllThreads(); });
auto socket_path = ANDROID_SOCKET_DIR "/"s + kSnapuserdSocketProxy;
@@ -781,21 +461,8 @@
return true;
}
-bool UserSnapshotServer::UpdateVerification(std::lock_guard<std::mutex>* proof_of_lock) {
- CHECK(proof_of_lock);
-
- bool status = true;
- for (auto iter = dm_users_.begin(); iter != dm_users_.end(); iter++) {
- auto& th = (*iter)->thread();
- if (th.joinable() && status) {
- status = (*iter)->snapuserd()->CheckPartitionVerification() && status;
- } else {
- // return immediately if there is a failure
- return false;
- }
- }
-
- return status;
+bool UserSnapshotServer::StartHandler(const std::string& misc_name) {
+ return handlers_->StartHandler(misc_name);
}
} // namespace snapshot
diff --git a/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_server.h b/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_server.h
index 12c3903..988c01a 100644
--- a/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_server.h
+++ b/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_server.h
@@ -31,6 +31,7 @@
#include <vector>
#include <android-base/unique_fd.h>
+#include "handler_manager.h"
#include "snapuserd_core.h"
namespace android {
@@ -39,48 +40,6 @@
static constexpr uint32_t kMaxPacketSize = 512;
static constexpr uint8_t kMaxMergeThreads = 2;
-enum class DaemonOps {
- INIT,
- START,
- QUERY,
- STOP,
- DELETE,
- DETACH,
- SUPPORTS,
- INITIATE,
- PERCENTAGE,
- GETSTATUS,
- UPDATE_VERIFY,
- INVALID,
-};
-
-class HandlerThread {
- public:
- explicit HandlerThread(std::shared_ptr<SnapshotHandler> snapuserd);
-
- void FreeResources() {
- // Each worker thread holds a reference to snapuserd.
- // Clear them so that all the resources
- // held by snapuserd is released
- if (snapuserd_) {
- snapuserd_->FreeResources();
- snapuserd_ = nullptr;
- }
- }
- const std::shared_ptr<SnapshotHandler>& snapuserd() const { return snapuserd_; }
- std::thread& thread() { return thread_; }
-
- const std::string& misc_name() const { return misc_name_; }
- bool ThreadTerminated() { return thread_terminated_; }
- void SetThreadTerminated() { thread_terminated_ = true; }
-
- private:
- std::thread thread_;
- std::shared_ptr<SnapshotHandler> snapuserd_;
- std::string misc_name_;
- bool thread_terminated_ = false;
-};
-
class UserSnapshotServer {
private:
android::base::unique_fd sockfd_;
@@ -88,21 +47,12 @@
volatile bool received_socket_signal_ = false;
std::vector<struct pollfd> watched_fds_;
bool is_socket_present_ = false;
- int num_partitions_merge_complete_ = 0;
- int active_merge_threads_ = 0;
- bool stop_monitor_merge_thread_ = false;
bool is_server_running_ = false;
bool io_uring_enabled_ = false;
- std::optional<bool> is_merge_monitor_started_;
-
- android::base::unique_fd monitor_merge_event_fd_;
+ std::unique_ptr<ISnapshotHandlerManager> handlers_;
std::mutex lock_;
- using HandlerList = std::vector<std::shared_ptr<HandlerThread>>;
- HandlerList dm_users_;
- std::queue<std::shared_ptr<HandlerThread>> merge_handlers_;
-
void AddWatchedFd(android::base::borrowed_fd fd, int events);
void AcceptClient();
bool HandleClient(android::base::borrowed_fd fd, int revents);
@@ -111,28 +61,14 @@
bool Receivemsg(android::base::borrowed_fd fd, const std::string& str);
void ShutdownThreads();
- bool RemoveAndJoinHandler(const std::string& control_device);
- DaemonOps Resolveop(std::string& input);
std::string GetDaemonStatus();
void Parsemsg(std::string const& msg, const char delim, std::vector<std::string>& out);
bool IsTerminating() { return terminating_; }
- void RunThread(std::shared_ptr<HandlerThread> handler);
- void MonitorMerge();
-
void JoinAllThreads();
bool StartWithSocket(bool start_listening);
- // Find a HandlerThread within a lock.
- HandlerList::iterator FindHandler(std::lock_guard<std::mutex>* proof_of_lock,
- const std::string& misc_name);
-
- double GetMergePercentage(std::lock_guard<std::mutex>* proof_of_lock);
- void TerminateMergeThreads(std::lock_guard<std::mutex>* proof_of_lock);
-
- bool UpdateVerification(std::lock_guard<std::mutex>* proof_of_lock);
-
public:
UserSnapshotServer();
~UserSnapshotServer();
@@ -147,12 +83,8 @@
const std::string& cow_device_path,
const std::string& backing_device,
const std::string& base_path_merge);
- bool StartHandler(const std::shared_ptr<HandlerThread>& handler);
- bool StartMerge(std::lock_guard<std::mutex>* proof_of_lock,
- const std::shared_ptr<HandlerThread>& handler);
- std::string GetMergeStatus(const std::shared_ptr<HandlerThread>& handler);
+ bool StartHandler(const std::string& misc_name);
- void WakeupMonitorMergeThread();
void SetTerminating() { terminating_ = true; }
void ReceivedSocketSignal() { received_socket_signal_ = true; }
void SetServerRunning() { is_server_running_ = true; }
diff --git a/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_test.cpp b/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_test.cpp
index 1421403..efe0c14 100644
--- a/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_test.cpp
+++ b/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_test.cpp
@@ -37,9 +37,9 @@
#include <libdm/dm.h>
#include <libdm/loop_control.h>
#include <libsnapshot/cow_writer.h>
-#include <snapuserd/snapuserd_client.h>
#include <storage_literals/storage_literals.h>
+#include "handler_manager.h"
#include "snapuserd_core.h"
DEFINE_string(force_config, "", "Force testing mode with iouring disabled");
@@ -54,8 +54,6 @@
using namespace android::dm;
using namespace std;
-static constexpr char kSnapuserdSocketTest[] = "snapuserdTest";
-
class Tempdevice {
public:
Tempdevice(const std::string& name, const DmTable& table)
@@ -68,15 +66,15 @@
}
~Tempdevice() {
if (valid_) {
- dm_.DeleteDevice(name_);
+ dm_.DeleteDeviceIfExists(name_);
}
}
bool Destroy() {
if (!valid_) {
- return false;
+ return true;
}
valid_ = false;
- return dm_.DeleteDevice(name_);
+ return dm_.DeleteDeviceIfExists(name_);
}
const std::string& path() const { return path_; }
const std::string& name() const { return name_; }
@@ -127,6 +125,7 @@
void SimulateDaemonRestart();
+ std::unique_ptr<ICowWriter> CreateCowDeviceInternal();
void CreateCowDevice();
void CreateCowDeviceOrderedOps();
void CreateCowDeviceOrderedOpsInverted();
@@ -138,7 +137,6 @@
void SetDeviceControlName();
void InitDaemon();
void CreateDmUserDevice();
- void StartSnapuserdDaemon();
unique_ptr<LoopDevice> base_loop_;
unique_ptr<Tempdevice> dmuser_dev_;
@@ -148,9 +146,9 @@
unique_fd base_fd_;
std::unique_ptr<TemporaryFile> cow_system_;
- std::unique_ptr<SnapuserdClient> client_;
std::unique_ptr<uint8_t[]> orig_buffer_;
std::unique_ptr<uint8_t[]> merged_buffer_;
+ SnapshotHandlerManager handlers_;
bool setup_ok_ = false;
bool merge_ok_ = false;
size_t size_ = 100_MiB;
@@ -180,9 +178,9 @@
ASSERT_TRUE(dmuser_dev_->Destroy());
auto misc_device = "/dev/dm-user/" + system_device_ctrl_name_;
- ASSERT_TRUE(client_->WaitForDeviceDelete(system_device_ctrl_name_));
+ ASSERT_TRUE(handlers_.DeleteHandler(system_device_ctrl_name_));
ASSERT_TRUE(android::fs_mgr::WaitForFileDeleted(misc_device, 10s));
- ASSERT_TRUE(client_->DetachSnapuserd());
+ handlers_.TerminateMergeThreads();
}
bool SnapuserdTest::SetupDefault() {
@@ -217,8 +215,6 @@
bool SnapuserdTest::SetupDaemon() {
SetDeviceControlName();
- StartSnapuserdDaemon();
-
CreateDmUserDevice();
InitCowDevice();
InitDaemon();
@@ -228,20 +224,6 @@
return setup_ok_;
}
-void SnapuserdTest::StartSnapuserdDaemon() {
- pid_t pid = fork();
- ASSERT_GE(pid, 0);
- if (pid == 0) {
- std::string arg0 = "/system/bin/snapuserd";
- std::string arg1 = "-socket="s + kSnapuserdSocketTest;
- char* const argv[] = {arg0.data(), arg1.data(), nullptr};
- ASSERT_GE(execv(arg0.c_str(), argv), 0);
- } else {
- client_ = SnapuserdClient::Connect(kSnapuserdSocketTest, 10s);
- ASSERT_NE(client_, nullptr);
- }
-}
-
void SnapuserdTest::CreateBaseDevice() {
unique_fd rnd_fd;
@@ -296,23 +278,30 @@
ASSERT_EQ(memcmp(snapuserd_buffer.get(), (char*)orig_buffer_.get() + (size_ * 4), size_), 0);
}
-void SnapuserdTest::CreateCowDeviceWithCopyOverlap_2() {
+std::unique_ptr<ICowWriter> SnapuserdTest::CreateCowDeviceInternal() {
std::string path = android::base::GetExecutableDirectory();
cow_system_ = std::make_unique<TemporaryFile>(path);
CowOptions options;
options.compression = "gz";
- CowWriter writer(options);
- ASSERT_TRUE(writer.Initialize(cow_system_->fd));
+ unique_fd fd(cow_system_->fd);
+ cow_system_->fd = -1;
- size_t num_blocks = size_ / options.block_size;
+ return CreateCowWriter(kDefaultCowVersion, options, std::move(fd));
+}
+
+void SnapuserdTest::CreateCowDeviceWithCopyOverlap_2() {
+ auto writer = CreateCowDeviceInternal();
+ ASSERT_NE(writer, nullptr);
+
+ size_t num_blocks = size_ / writer->GetBlockSize();
size_t x = num_blocks;
size_t blk_src_copy = 0;
// Create overlapping copy operations
while (1) {
- ASSERT_TRUE(writer.AddCopy(blk_src_copy, blk_src_copy + 1));
+ ASSERT_TRUE(writer->AddCopy(blk_src_copy, blk_src_copy + 1));
x -= 1;
if (x == 1) {
break;
@@ -321,7 +310,7 @@
}
// Flush operations
- ASSERT_TRUE(writer.Finalize());
+ ASSERT_TRUE(writer->Finalize());
// Construct the buffer required for validation
orig_buffer_ = std::make_unique<uint8_t[]>(total_base_size_);
@@ -349,22 +338,16 @@
}
void SnapuserdTest::CreateCowDeviceWithCopyOverlap_1() {
- std::string path = android::base::GetExecutableDirectory();
- cow_system_ = std::make_unique<TemporaryFile>(path);
+ auto writer = CreateCowDeviceInternal();
+ ASSERT_NE(writer, nullptr);
- CowOptions options;
- options.compression = "gz";
- CowWriter writer(options);
-
- ASSERT_TRUE(writer.Initialize(cow_system_->fd));
-
- size_t num_blocks = size_ / options.block_size;
+ size_t num_blocks = size_ / writer->GetBlockSize();
size_t x = num_blocks;
size_t blk_src_copy = num_blocks - 1;
// Create overlapping copy operations
while (1) {
- ASSERT_TRUE(writer.AddCopy(blk_src_copy + 1, blk_src_copy));
+ ASSERT_TRUE(writer->AddCopy(blk_src_copy + 1, blk_src_copy));
x -= 1;
if (x == 0) {
ASSERT_EQ(blk_src_copy, 0);
@@ -374,7 +357,7 @@
}
// Flush operations
- ASSERT_TRUE(writer.Finalize());
+ ASSERT_TRUE(writer->Finalize());
// Construct the buffer required for validation
orig_buffer_ = std::make_unique<uint8_t[]>(total_base_size_);
@@ -384,10 +367,11 @@
true);
// Merged operations
- ASSERT_EQ(android::base::ReadFullyAtOffset(base_fd_, orig_buffer_.get(), options.block_size, 0),
+ ASSERT_EQ(android::base::ReadFullyAtOffset(base_fd_, orig_buffer_.get(), writer->GetBlockSize(),
+ 0),
true);
ASSERT_EQ(android::base::ReadFullyAtOffset(
- base_fd_, (char*)orig_buffer_.get() + options.block_size, size_, 0),
+ base_fd_, (char*)orig_buffer_.get() + writer->GetBlockSize(), size_, 0),
true);
}
@@ -395,8 +379,8 @@
unique_fd rnd_fd;
loff_t offset = 0;
- std::string path = android::base::GetExecutableDirectory();
- cow_system_ = std::make_unique<TemporaryFile>(path);
+ auto writer = CreateCowDeviceInternal();
+ ASSERT_NE(writer, nullptr);
rnd_fd.reset(open("/dev/random", O_RDONLY));
ASSERT_TRUE(rnd_fd > 0);
@@ -411,13 +395,7 @@
offset += 1_MiB;
}
- CowOptions options;
- options.compression = "gz";
- CowWriter writer(options);
-
- ASSERT_TRUE(writer.Initialize(cow_system_->fd));
-
- size_t num_blocks = size_ / options.block_size;
+ size_t num_blocks = size_ / writer->GetBlockSize();
size_t blk_end_copy = num_blocks * 3;
size_t source_blk = num_blocks - 1;
size_t blk_src_copy = blk_end_copy - 1;
@@ -425,7 +403,7 @@
size_t x = num_blocks;
while (1) {
- ASSERT_TRUE(writer.AddCopy(source_blk, blk_src_copy));
+ ASSERT_TRUE(writer->AddCopy(source_blk, blk_src_copy));
x -= 1;
if (x == 0) {
break;
@@ -435,12 +413,12 @@
}
for (size_t i = num_blocks; i > 0; i--) {
- ASSERT_TRUE(writer.AddXorBlocks(num_blocks + i - 1,
- &random_buffer_1_.get()[options.block_size * (i - 1)],
- options.block_size, 2 * num_blocks + i - 1, xor_offset));
+ ASSERT_TRUE(writer->AddXorBlocks(
+ num_blocks + i - 1, &random_buffer_1_.get()[writer->GetBlockSize() * (i - 1)],
+ writer->GetBlockSize(), 2 * num_blocks + i - 1, xor_offset));
}
// Flush operations
- ASSERT_TRUE(writer.Finalize());
+ ASSERT_TRUE(writer->Finalize());
// Construct the buffer required for validation
orig_buffer_ = std::make_unique<uint8_t[]>(total_base_size_);
// Read the entire base device
@@ -458,8 +436,8 @@
unique_fd rnd_fd;
loff_t offset = 0;
- std::string path = android::base::GetExecutableDirectory();
- cow_system_ = std::make_unique<TemporaryFile>(path);
+ auto writer = CreateCowDeviceInternal();
+ ASSERT_NE(writer, nullptr);
rnd_fd.reset(open("/dev/random", O_RDONLY));
ASSERT_TRUE(rnd_fd > 0);
@@ -475,20 +453,14 @@
}
memset(random_buffer_1_.get(), 0, size_);
- CowOptions options;
- options.compression = "gz";
- CowWriter writer(options);
-
- ASSERT_TRUE(writer.Initialize(cow_system_->fd));
-
- size_t num_blocks = size_ / options.block_size;
+ size_t num_blocks = size_ / writer->GetBlockSize();
size_t x = num_blocks;
size_t source_blk = 0;
size_t blk_src_copy = 2 * num_blocks;
uint16_t xor_offset = 5;
while (1) {
- ASSERT_TRUE(writer.AddCopy(source_blk, blk_src_copy));
+ ASSERT_TRUE(writer->AddCopy(source_blk, blk_src_copy));
x -= 1;
if (x == 0) {
@@ -498,10 +470,10 @@
blk_src_copy += 1;
}
- ASSERT_TRUE(writer.AddXorBlocks(num_blocks, random_buffer_1_.get(), size_, 2 * num_blocks,
- xor_offset));
+ ASSERT_TRUE(writer->AddXorBlocks(num_blocks, random_buffer_1_.get(), size_, 2 * num_blocks,
+ xor_offset));
// Flush operations
- ASSERT_TRUE(writer.Finalize());
+ ASSERT_TRUE(writer->Finalize());
// Construct the buffer required for validation
orig_buffer_ = std::make_unique<uint8_t[]>(total_base_size_);
// Read the entire base device
@@ -519,8 +491,8 @@
unique_fd rnd_fd;
loff_t offset = 0;
- std::string path = android::base::GetExecutableDirectory();
- cow_system_ = std::make_unique<TemporaryFile>(path);
+ auto writer = CreateCowDeviceInternal();
+ ASSERT_NE(writer, nullptr);
rnd_fd.reset(open("/dev/random", O_RDONLY));
ASSERT_TRUE(rnd_fd > 0);
@@ -535,13 +507,7 @@
offset += 1_MiB;
}
- CowOptions options;
- options.compression = "gz";
- CowWriter writer(options);
-
- ASSERT_TRUE(writer.Initialize(cow_system_->fd));
-
- size_t num_blocks = size_ / options.block_size;
+ size_t num_blocks = size_ / writer->GetBlockSize();
size_t blk_end_copy = num_blocks * 2;
size_t source_blk = num_blocks - 1;
size_t blk_src_copy = blk_end_copy - 1;
@@ -555,11 +521,11 @@
for (int i = 0; i < num_blocks; i++) {
sequence[num_blocks + i] = 5 * num_blocks - 1 - i;
}
- ASSERT_TRUE(writer.AddSequenceData(2 * num_blocks, sequence));
+ ASSERT_TRUE(writer->AddSequenceData(2 * num_blocks, sequence));
size_t x = num_blocks;
while (1) {
- ASSERT_TRUE(writer.AddCopy(source_blk, blk_src_copy));
+ ASSERT_TRUE(writer->AddCopy(source_blk, blk_src_copy));
x -= 1;
if (x == 0) {
break;
@@ -571,24 +537,24 @@
source_blk = num_blocks;
blk_src_copy = blk_end_copy;
- ASSERT_TRUE(writer.AddRawBlocks(source_blk, random_buffer_1_.get(), size_));
+ ASSERT_TRUE(writer->AddRawBlocks(source_blk, random_buffer_1_.get(), size_));
size_t blk_zero_copy_start = source_blk + num_blocks;
size_t blk_zero_copy_end = blk_zero_copy_start + num_blocks;
- ASSERT_TRUE(writer.AddZeroBlocks(blk_zero_copy_start, num_blocks));
+ ASSERT_TRUE(writer->AddZeroBlocks(blk_zero_copy_start, num_blocks));
size_t blk_random2_replace_start = blk_zero_copy_end;
- ASSERT_TRUE(writer.AddRawBlocks(blk_random2_replace_start, random_buffer_1_.get(), size_));
+ ASSERT_TRUE(writer->AddRawBlocks(blk_random2_replace_start, random_buffer_1_.get(), size_));
size_t blk_xor_start = blk_random2_replace_start + num_blocks;
size_t xor_offset = BLOCK_SZ / 2;
- ASSERT_TRUE(writer.AddXorBlocks(blk_xor_start, random_buffer_1_.get(), size_, num_blocks,
- xor_offset));
+ ASSERT_TRUE(writer->AddXorBlocks(blk_xor_start, random_buffer_1_.get(), size_, num_blocks,
+ xor_offset));
// Flush operations
- ASSERT_TRUE(writer.Finalize());
+ ASSERT_TRUE(writer->Finalize());
// Construct the buffer required for validation
orig_buffer_ = std::make_unique<uint8_t[]>(total_base_size_);
std::string zero_buffer(size_, 0);
@@ -606,9 +572,17 @@
}
void SnapuserdTest::InitCowDevice() {
- uint64_t num_sectors = client_->InitDmUserCow(system_device_ctrl_name_, cow_system_->path,
- base_loop_->device(), base_loop_->device());
- ASSERT_NE(num_sectors, 0);
+ bool use_iouring = true;
+ if (FLAGS_force_config == "iouring_disabled") {
+ use_iouring = false;
+ }
+
+ auto handler =
+ handlers_.AddHandler(system_device_ctrl_name_, cow_system_->path, base_loop_->device(),
+ base_loop_->device(), 1, use_iouring, false);
+ ASSERT_NE(handler, nullptr);
+ ASSERT_NE(handler->snapuserd(), nullptr);
+ ASSERT_NE(handler->snapuserd()->GetNumSectors(), 0);
}
void SnapuserdTest::SetDeviceControlName() {
@@ -646,13 +620,12 @@
}
void SnapuserdTest::InitDaemon() {
- bool ok = client_->AttachDmUser(system_device_ctrl_name_);
- ASSERT_TRUE(ok);
+ ASSERT_TRUE(handlers_.StartHandler(system_device_ctrl_name_));
}
void SnapuserdTest::CheckMergeCompletion() {
while (true) {
- double percentage = client_->GetMergePercent();
+ double percentage = handlers_.GetMergePercentage();
if ((int)percentage == 100) {
break;
}
@@ -667,8 +640,6 @@
SetDeviceControlName();
- StartSnapuserdDaemon();
-
CreateDmUserDevice();
InitCowDevice();
InitDaemon();
@@ -684,8 +655,7 @@
}
void SnapuserdTest::StartMerge() {
- bool ok = client_->InitiateMerge(system_device_ctrl_name_);
- ASSERT_TRUE(ok);
+ ASSERT_TRUE(handlers_.InitiateMerge(system_device_ctrl_name_));
}
void SnapuserdTest::ValidateMerge() {
@@ -699,7 +669,6 @@
Shutdown();
std::this_thread::sleep_for(500ms);
SetDeviceControlName();
- StartSnapuserdDaemon();
CreateDmUserDevice();
InitCowDevice();
InitDaemon();
@@ -859,20 +828,5 @@
gflags::ParseCommandLineFlags(&argc, &argv, false);
- android::base::SetProperty("ctl.stop", "snapuserd");
-
- if (FLAGS_force_config == "iouring_disabled") {
- if (!android::base::SetProperty("snapuserd.test.io_uring.force_disable", "1")) {
- return testing::AssertionFailure()
- << "Failed to disable property: snapuserd.test.io_uring.disabled";
- }
- }
-
- int ret = RUN_ALL_TESTS();
-
- if (FLAGS_force_config == "iouring_disabled") {
- android::base::SetProperty("snapuserd.test.io_uring.force_disable", "0");
- }
-
- return ret;
+ return RUN_ALL_TESTS();
}
diff --git a/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_verify.cpp b/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_verify.cpp
index 18c1dfc..6817340 100644
--- a/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_verify.cpp
+++ b/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_verify.cpp
@@ -14,12 +14,14 @@
* limitations under the License.
*/
-#include "snapuserd_core.h"
+#include "snapuserd_verify.h"
#include <android-base/chrono_utils.h>
#include <android-base/scopeguard.h>
#include <android-base/strings.h>
+#include "snapuserd_core.h"
+
namespace android {
namespace snapshot {
diff --git a/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_verify.h b/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_verify.h
new file mode 100644
index 0000000..d07d2f8
--- /dev/null
+++ b/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_verify.h
@@ -0,0 +1,64 @@
+// Copyright (C) 2023 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 <stdint.h>
+#include <sys/types.h>
+
+#include <condition_variable>
+#include <mutex>
+#include <string>
+
+#include <snapuserd/snapuserd_kernel.h>
+#include <storage_literals/storage_literals.h>
+
+namespace android {
+namespace snapshot {
+
+using namespace android::storage_literals;
+
+class UpdateVerify {
+ public:
+ UpdateVerify(const std::string& misc_name);
+ void VerifyUpdatePartition();
+ bool CheckPartitionVerification();
+
+ private:
+ enum class UpdateVerifyState {
+ VERIFY_UNKNOWN,
+ VERIFY_FAILED,
+ VERIFY_SUCCESS,
+ };
+
+ std::string misc_name_;
+ UpdateVerifyState state_;
+ std::mutex m_lock_;
+ std::condition_variable m_cv_;
+
+ int kMinThreadsToVerify = 1;
+ int kMaxThreadsToVerify = 4;
+ uint64_t kThresholdSize = 512_MiB;
+ uint64_t kBlockSizeVerify = 1_MiB;
+
+ bool IsBlockAligned(uint64_t read_size) { return ((read_size & (BLOCK_SZ - 1)) == 0); }
+ void UpdatePartitionVerificationState(UpdateVerifyState state);
+ bool VerifyPartition(const std::string& partition_name, const std::string& dm_block_device);
+ bool VerifyBlocks(const std::string& partition_name, const std::string& dm_block_device,
+ off_t offset, int skip_blocks, uint64_t dev_sz);
+};
+
+} // namespace snapshot
+} // namespace android
diff --git a/fs_mgr/libsnapshot/snapuserd/user-space-merge/worker.cpp b/fs_mgr/libsnapshot/snapuserd/user-space-merge/worker.cpp
new file mode 100644
index 0000000..aa15630
--- /dev/null
+++ b/fs_mgr/libsnapshot/snapuserd/user-space-merge/worker.cpp
@@ -0,0 +1,80 @@
+// Copyright (C) 2023 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 "worker.h"
+
+#include "snapuserd_core.h"
+
+namespace android {
+namespace snapshot {
+
+Worker::Worker(const std::string& cow_device, const std::string& misc_name,
+ const std::string& base_path_merge, std::shared_ptr<SnapshotHandler> snapuserd) {
+ cow_device_ = cow_device;
+ misc_name_ = misc_name;
+ base_path_merge_ = base_path_merge;
+ snapuserd_ = snapuserd;
+}
+
+void Worker::InitializeBufsink() {
+ // Allocate the buffer which is used to communicate between
+ // daemon and dm-user. The buffer comprises of header and a fixed payload.
+ // If the dm-user requests a big IO, the IO will be broken into chunks
+ // of PAYLOAD_BUFFER_SZ.
+ size_t buf_size = sizeof(struct dm_user_header) + PAYLOAD_BUFFER_SZ;
+ bufsink_.Initialize(buf_size);
+}
+
+bool Worker::Init() {
+ InitializeBufsink();
+
+ if (!InitializeFds()) {
+ return false;
+ }
+
+ if (!InitReader()) {
+ return false;
+ }
+
+ return true;
+}
+
+bool Worker::InitReader() {
+ reader_ = snapuserd_->CloneReaderForWorker();
+
+ if (!reader_->InitForMerge(std::move(cow_fd_))) {
+ return false;
+ }
+ return true;
+}
+
+bool Worker::InitializeFds() {
+ cow_fd_.reset(open(cow_device_.c_str(), O_RDWR));
+ if (cow_fd_ < 0) {
+ SNAP_PLOG(ERROR) << "Open Failed: " << cow_device_;
+ return false;
+ }
+
+ // Base device used by merge thread
+ base_path_merge_fd_.reset(open(base_path_merge_.c_str(), O_RDWR));
+ if (base_path_merge_fd_ < 0) {
+ SNAP_PLOG(ERROR) << "Open Failed: " << base_path_merge_;
+ return false;
+ }
+
+ return true;
+}
+
+} // namespace snapshot
+} // namespace android
diff --git a/fs_mgr/libsnapshot/snapuserd/user-space-merge/worker.h b/fs_mgr/libsnapshot/snapuserd/user-space-merge/worker.h
new file mode 100644
index 0000000..813b159
--- /dev/null
+++ b/fs_mgr/libsnapshot/snapuserd/user-space-merge/worker.h
@@ -0,0 +1,65 @@
+// Copyright (C) 2023 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 <stddef.h>
+
+#include <memory>
+#include <string>
+
+#include <android-base/unique_fd.h>
+#include <libsnapshot/cow_reader.h>
+#include <snapuserd/snapuserd_buffer.h>
+#include <snapuserd/snapuserd_kernel.h>
+
+namespace android {
+namespace snapshot {
+
+using android::base::unique_fd;
+
+class SnapshotHandler;
+
+class Worker {
+ public:
+ Worker(const std::string& cow_device, const std::string& misc_name,
+ const std::string& base_path_merge, std::shared_ptr<SnapshotHandler> snapuserd);
+ virtual ~Worker() = default;
+
+ virtual bool Init();
+
+ protected:
+ // Initialization
+ void InitializeBufsink();
+ bool InitializeFds();
+ bool InitReader();
+ virtual void CloseFds() { base_path_merge_fd_ = {}; }
+
+ std::unique_ptr<CowReader> reader_;
+ BufferSink bufsink_;
+
+ std::string misc_name_; // Needed for SNAP_LOG.
+
+ unique_fd base_path_merge_fd_;
+
+ std::shared_ptr<SnapshotHandler> snapuserd_;
+
+ private:
+ std::string cow_device_;
+ std::string base_path_merge_;
+ unique_fd cow_fd_;
+};
+
+} // namespace snapshot
+} // namespace android
diff --git a/fs_mgr/libsnapshot/test_helpers.cpp b/fs_mgr/libsnapshot/test_helpers.cpp
index 9f1d676..2eac347 100644
--- a/fs_mgr/libsnapshot/test_helpers.cpp
+++ b/fs_mgr/libsnapshot/test_helpers.cpp
@@ -130,12 +130,7 @@
return true;
}
-std::string HashSnapshot(ISnapshotWriter* writer) {
- auto reader = writer->OpenReader();
- if (!reader) {
- return {};
- }
-
+std::string HashSnapshot(ICowWriter::FileDescriptor* reader) {
SHA256_CTX ctx;
SHA256_Init(&ctx);
@@ -214,68 +209,6 @@
return partition_update->mutable_new_partition_info()->size();
}
-AssertionResult LowSpaceUserdata::Init(uint64_t max_free_space) {
- auto res = ReadUserdataStats();
- if (!res) return res;
-
- // Try to fill up the disk as much as possible until free_space_ <= max_free_space.
- big_file_ = std::make_unique<TemporaryFile>();
- if (big_file_->fd == -1) {
- return AssertionFailure() << strerror(errno);
- }
- if (!android::base::StartsWith(big_file_->path, kUserDataDevice)) {
- return AssertionFailure() << "Temp file allocated to " << big_file_->path << ", not in "
- << kUserDataDevice;
- }
- uint64_t next_consume = std::min(std::max(available_space_, max_free_space) - max_free_space,
- (uint64_t)std::numeric_limits<off_t>::max());
- off_t allocated = 0;
- while (next_consume > 0 && free_space_ > max_free_space) {
- int status = fallocate(big_file_->fd, 0, allocated, next_consume);
- if (status == -1 && errno == ENOSPC) {
- next_consume /= 2;
- continue;
- }
- if (status == -1) {
- return AssertionFailure() << strerror(errno);
- }
- allocated += next_consume;
-
- res = ReadUserdataStats();
- if (!res) return res;
- }
-
- LOG(INFO) << allocated << " bytes allocated to " << big_file_->path;
- initialized_ = true;
- return AssertionSuccess();
-}
-
-AssertionResult LowSpaceUserdata::ReadUserdataStats() {
- struct statvfs buf;
- if (statvfs(kUserDataDevice, &buf) == -1) {
- return AssertionFailure() << strerror(errno);
- }
- bsize_ = buf.f_bsize;
- free_space_ = bsize_ * buf.f_bfree;
- available_space_ = bsize_ * buf.f_bavail;
- return AssertionSuccess();
-}
-
-uint64_t LowSpaceUserdata::free_space() const {
- CHECK(initialized_);
- return free_space_;
-}
-
-uint64_t LowSpaceUserdata::available_space() const {
- CHECK(initialized_);
- return available_space_;
-}
-
-uint64_t LowSpaceUserdata::bsize() const {
- CHECK(initialized_);
- return bsize_;
-}
-
bool IsVirtualAbEnabled() {
return android::base::GetBoolProperty("ro.virtual_ab.enabled", false);
}
diff --git a/fs_mgr/libstorage_literals/storage_literals/storage_literals.h b/fs_mgr/libstorage_literals/storage_literals/storage_literals.h
index ac0dfbd..bbeabd5 100644
--- a/fs_mgr/libstorage_literals/storage_literals/storage_literals.h
+++ b/fs_mgr/libstorage_literals/storage_literals/storage_literals.h
@@ -37,6 +37,7 @@
using KiB = Size<10>;
using MiB = Size<20>;
using GiB = Size<30>;
+using TiB = Size<40>;
constexpr B operator""_B(unsigned long long v) { // NOLINT
return B{v};
@@ -54,6 +55,10 @@
return GiB{v};
}
+constexpr TiB operator""_TiB(unsigned long long v) { // NOLINT
+ return TiB{v};
+}
+
template <typename Dest, typename Src>
constexpr Dest size_cast(Src src) {
if (Src::power < Dest::power) {
@@ -69,6 +74,7 @@
static_assert(1_KiB == 1 << 10);
static_assert(1_MiB == 1 << 20);
static_assert(1_GiB == 1 << 30);
+static_assert(1_TiB == 1ULL << 40);
static_assert(size_cast<KiB>(1_B).count() == 0);
static_assert(size_cast<KiB>(1024_B).count() == 1);
static_assert(size_cast<KiB>(1_MiB).count() == 1024);
diff --git a/fs_mgr/tests/fs_mgr_test.cpp b/fs_mgr/tests/fs_mgr_test.cpp
index e33681c..5f889ca 100644
--- a/fs_mgr/tests/fs_mgr_test.cpp
+++ b/fs_mgr/tests/fs_mgr_test.cpp
@@ -497,6 +497,7 @@
EXPECT_EQ("none0", entry->mount_point);
{
FstabEntry::FsMgrFlags flags = {};
+ flags.file_encryption = true;
EXPECT_TRUE(CompareFlags(flags, entry->fs_mgr_flags));
}
EXPECT_EQ("", entry->metadata_key_dir);
diff --git a/gatekeeperd/Android.bp b/gatekeeperd/Android.bp
index 838f734..534fc1a 100644
--- a/gatekeeperd/Android.bp
+++ b/gatekeeperd/Android.bp
@@ -18,8 +18,8 @@
default_applicable_licenses: ["Android-Apache-2.0"],
}
-cc_binary {
- name: "gatekeeperd",
+cc_defaults {
+ name: "gatekeeperd_defaults",
cflags: [
"-Wall",
"-Wextra",
@@ -52,6 +52,16 @@
static_libs: ["libscrypt_static"],
include_dirs: ["external/scrypt/lib/crypto"],
+}
+
+cc_binary {
+ name: "gatekeeperd",
+ defaults: [
+ "gatekeeperd_defaults",
+ ],
+ srcs: [
+ "main.cpp",
+ ],
init_rc: ["gatekeeperd.rc"],
}
@@ -88,3 +98,20 @@
"libbinder",
],
}
+
+cc_fuzz {
+ name: "gatekeeperd_service_fuzzer",
+ defaults: [
+ "gatekeeperd_defaults",
+ "service_fuzzer_defaults"
+ ],
+ srcs: [
+ "fuzzer/GateKeeperServiceFuzzer.cpp",
+ ],
+ fuzz_config: {
+ cc: [
+ "subrahmanyaman@google.com",
+ "swillden@google.com",
+ ],
+ },
+}
\ No newline at end of file
diff --git a/gatekeeperd/fuzzer/GateKeeperServiceFuzzer.cpp b/gatekeeperd/fuzzer/GateKeeperServiceFuzzer.cpp
new file mode 100644
index 0000000..bc0d5fe
--- /dev/null
+++ b/gatekeeperd/fuzzer/GateKeeperServiceFuzzer.cpp
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2023 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 <fuzzbinder/libbinder_driver.h>
+
+#include "gatekeeperd.h"
+
+using android::fuzzService;
+using android::GateKeeperProxy;
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+ auto gatekeeperService = new GateKeeperProxy();
+ fuzzService(gatekeeperService, FuzzedDataProvider(data, size));
+ return 0;
+}
\ No newline at end of file
diff --git a/gatekeeperd/gatekeeperd.cpp b/gatekeeperd/gatekeeperd.cpp
index d2fc651..bdfb7f6 100644
--- a/gatekeeperd/gatekeeperd.cpp
+++ b/gatekeeperd/gatekeeperd.cpp
@@ -13,11 +13,9 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-
#define LOG_TAG "gatekeeperd"
-#include <android/service/gatekeeper/BnGateKeeperService.h>
-#include <gatekeeper/GateKeeperResponse.h>
+#include "gatekeeperd.h"
#include <endian.h>
#include <errno.h>
@@ -39,25 +37,18 @@
#include <log/log.h>
#include <utils/String16.h>
-#include <aidl/android/hardware/gatekeeper/IGatekeeper.h>
#include <aidl/android/hardware/security/keymint/HardwareAuthToken.h>
#include <aidl/android/security/authorization/IKeystoreAuthorization.h>
-#include <android/hardware/gatekeeper/1.0/IGatekeeper.h>
#include <hidl/HidlSupport.h>
using android::sp;
using android::hardware::Return;
using android::hardware::gatekeeper::V1_0::GatekeeperResponse;
using android::hardware::gatekeeper::V1_0::GatekeeperStatusCode;
-using android::hardware::gatekeeper::V1_0::IGatekeeper;
using AidlGatekeeperEnrollResp = aidl::android::hardware::gatekeeper::GatekeeperEnrollResponse;
using AidlGatekeeperVerifyResp = aidl::android::hardware::gatekeeper::GatekeeperVerifyResponse;
-using AidlIGatekeeper = aidl::android::hardware::gatekeeper::IGatekeeper;
-using ::android::binder::Status;
-using ::android::service::gatekeeper::BnGateKeeperService;
-using GKResponse = ::android::service::gatekeeper::GateKeeperResponse;
using GKResponseCode = ::android::service::gatekeeper::ResponseCode;
using ::aidl::android::hardware::security::keymint::HardwareAuthenticatorType;
using ::aidl::android::hardware::security::keymint::HardwareAuthToken;
@@ -70,176 +61,184 @@
static const String16 DUMP_PERMISSION("android.permission.DUMP");
constexpr const char gatekeeperServiceName[] = "android.hardware.gatekeeper.IGatekeeper/default";
-class GateKeeperProxy : public BnGateKeeperService {
- public:
- GateKeeperProxy() {
- clear_state_if_needed_done = false;
- if (AServiceManager_isDeclared(gatekeeperServiceName)) {
- ::ndk::SpAIBinder ks2Binder(AServiceManager_waitForService(gatekeeperServiceName));
- aidl_hw_device = AidlIGatekeeper::fromBinder(ks2Binder);
- }
- if (!aidl_hw_device) {
- hw_device = IGatekeeper::getService();
- }
- is_running_gsi = android::base::GetBoolProperty(android::gsi::kGsiBootedProp, false);
+GateKeeperProxy::GateKeeperProxy() {
+ clear_state_if_needed_done = false;
+ if (AServiceManager_isDeclared(gatekeeperServiceName)) {
+ ::ndk::SpAIBinder ks2Binder(AServiceManager_waitForService(gatekeeperServiceName));
+ aidl_hw_device = AidlIGatekeeper::fromBinder(ks2Binder);
+ }
+ if (!aidl_hw_device) {
+ hw_device = IGatekeeper::getService();
+ }
+ is_running_gsi = android::base::GetBoolProperty(android::gsi::kGsiBootedProp, false);
- if (!aidl_hw_device && !hw_device) {
- LOG(ERROR) << "Could not find Gatekeeper device, which makes me very sad.";
+ if (!aidl_hw_device && !hw_device) {
+ LOG(ERROR) << "Could not find Gatekeeper device, which makes me very sad.";
+ }
+}
+
+void GateKeeperProxy::store_sid(uint32_t userId, uint64_t sid) {
+ char filename[21];
+ snprintf(filename, sizeof(filename), "%u", userId);
+ int fd = open(filename, O_WRONLY | O_TRUNC | O_CREAT, S_IRUSR | S_IWUSR);
+ if (fd < 0) {
+ ALOGE("could not open file: %s: %s", filename, strerror(errno));
+ return;
+ }
+ write(fd, &sid, sizeof(sid));
+ close(fd);
+}
+
+void GateKeeperProxy::clear_state_if_needed() {
+ if (clear_state_if_needed_done) {
+ return;
+ }
+
+ if (mark_cold_boot() && !is_running_gsi) {
+ ALOGI("cold boot: clearing state");
+ if (aidl_hw_device) {
+ aidl_hw_device->deleteAllUsers();
+ } else if (hw_device) {
+ hw_device->deleteAllUsers([](const GatekeeperResponse&) {});
}
}
- virtual ~GateKeeperProxy() {}
+ clear_state_if_needed_done = true;
+}
- void store_sid(uint32_t userId, uint64_t sid) {
- char filename[21];
- snprintf(filename, sizeof(filename), "%u", userId);
+bool GateKeeperProxy::mark_cold_boot() {
+ const char* filename = ".coldboot";
+ if (access(filename, F_OK) == -1) {
int fd = open(filename, O_WRONLY | O_TRUNC | O_CREAT, S_IRUSR | S_IWUSR);
if (fd < 0) {
- ALOGE("could not open file: %s: %s", filename, strerror(errno));
- return;
+ ALOGE("could not open file: %s : %s", filename, strerror(errno));
+ return false;
}
- write(fd, &sid, sizeof(sid));
close(fd);
+ return true;
+ }
+ return false;
+}
+
+void GateKeeperProxy::maybe_store_sid(uint32_t userId, uint64_t sid) {
+ char filename[21];
+ snprintf(filename, sizeof(filename), "%u", userId);
+ if (access(filename, F_OK) == -1) {
+ store_sid(userId, sid);
+ }
+}
+
+uint64_t GateKeeperProxy::read_sid(uint32_t userId) {
+ char filename[21];
+ uint64_t sid;
+ snprintf(filename, sizeof(filename), "%u", userId);
+ int fd = open(filename, O_RDONLY);
+ if (fd < 0) return 0;
+ read(fd, &sid, sizeof(sid));
+ close(fd);
+ return sid;
+}
+
+void GateKeeperProxy::clear_sid(uint32_t userId) {
+ char filename[21];
+ snprintf(filename, sizeof(filename), "%u", userId);
+ if (remove(filename) < 0 && errno != ENOENT) {
+ ALOGE("%s: could not remove file [%s], attempting 0 write", __func__, strerror(errno));
+ store_sid(userId, 0);
+ }
+}
+
+Status GateKeeperProxy::adjust_userId(uint32_t userId, uint32_t* hw_userId) {
+ static constexpr uint32_t kGsiOffset = 1000000;
+ if (userId >= kGsiOffset) {
+ return Status::fromExceptionCode(Status::EX_ILLEGAL_ARGUMENT);
}
- void clear_state_if_needed() {
- if (clear_state_if_needed_done) {
- return;
- }
-
- if (mark_cold_boot() && !is_running_gsi) {
- ALOGI("cold boot: clearing state");
- if (aidl_hw_device) {
- aidl_hw_device->deleteAllUsers();
- } else if (hw_device) {
- hw_device->deleteAllUsers([](const GatekeeperResponse&) {});
- }
- }
-
- clear_state_if_needed_done = true;
+ if ((aidl_hw_device == nullptr) && (hw_device == nullptr)) {
+ return Status::fromExceptionCode(Status::EX_ILLEGAL_STATE);
}
- bool mark_cold_boot() {
- const char* filename = ".coldboot";
- if (access(filename, F_OK) == -1) {
- int fd = open(filename, O_WRONLY | O_TRUNC | O_CREAT, S_IRUSR | S_IWUSR);
- if (fd < 0) {
- ALOGE("could not open file: %s : %s", filename, strerror(errno));
- return false;
- }
- close(fd);
- return true;
- }
- return false;
+ if (is_running_gsi) {
+ *hw_userId = userId + kGsiOffset;
+ return Status::ok();
}
-
- void maybe_store_sid(uint32_t userId, uint64_t sid) {
- char filename[21];
- snprintf(filename, sizeof(filename), "%u", userId);
- if (access(filename, F_OK) == -1) {
- store_sid(userId, sid);
- }
- }
-
- uint64_t read_sid(uint32_t userId) {
- char filename[21];
- uint64_t sid;
- snprintf(filename, sizeof(filename), "%u", userId);
- int fd = open(filename, O_RDONLY);
- if (fd < 0) return 0;
- read(fd, &sid, sizeof(sid));
- close(fd);
- return sid;
- }
-
- void clear_sid(uint32_t userId) {
- char filename[21];
- snprintf(filename, sizeof(filename), "%u", userId);
- if (remove(filename) < 0 && errno != ENOENT) {
- ALOGE("%s: could not remove file [%s], attempting 0 write", __func__, strerror(errno));
- store_sid(userId, 0);
- }
- }
-
- // This should only be called on userIds being passed to the GateKeeper HAL. It ensures that
- // secure storage shared across a GSI image and a host image will not overlap.
- uint32_t adjust_userId(uint32_t userId) {
- static constexpr uint32_t kGsiOffset = 1000000;
- CHECK(userId < kGsiOffset);
- CHECK((aidl_hw_device != nullptr) || (hw_device != nullptr));
- if (is_running_gsi) {
- return userId + kGsiOffset;
- }
- return userId;
- }
+ *hw_userId = userId;
+ return Status::ok();
+}
#define GK_ERROR *gkResponse = GKResponse::error(), Status::ok()
- Status enroll(int32_t userId, const std::optional<std::vector<uint8_t>>& currentPasswordHandle,
- const std::optional<std::vector<uint8_t>>& currentPassword,
- const std::vector<uint8_t>& desiredPassword, GKResponse* gkResponse) override {
- IPCThreadState* ipc = IPCThreadState::self();
- const int calling_pid = ipc->getCallingPid();
- const int calling_uid = ipc->getCallingUid();
- if (!PermissionCache::checkPermission(KEYGUARD_PERMISSION, calling_pid, calling_uid)) {
- return GK_ERROR;
- }
+Status GateKeeperProxy::enroll(int32_t userId,
+ const std::optional<std::vector<uint8_t>>& currentPasswordHandle,
+ const std::optional<std::vector<uint8_t>>& currentPassword,
+ const std::vector<uint8_t>& desiredPassword,
+ GKResponse* gkResponse) {
+ IPCThreadState* ipc = IPCThreadState::self();
+ const int calling_pid = ipc->getCallingPid();
+ const int calling_uid = ipc->getCallingUid();
+ if (!PermissionCache::checkPermission(KEYGUARD_PERMISSION, calling_pid, calling_uid)) {
+ return GK_ERROR;
+ }
- // Make sure to clear any state from before factory reset as soon as a credential is
- // enrolled (which may happen during device setup).
- clear_state_if_needed();
+ // Make sure to clear any state from before factory reset as soon as a credential is
+ // enrolled (which may happen during device setup).
+ clear_state_if_needed();
- // need a desired password to enroll
- if (desiredPassword.size() == 0) return GK_ERROR;
+ // need a desired password to enroll
+ if (desiredPassword.size() == 0) return GK_ERROR;
- if (!aidl_hw_device && !hw_device) {
- LOG(ERROR) << "has no HAL to talk to";
- return GK_ERROR;
- }
+ if (!aidl_hw_device && !hw_device) {
+ LOG(ERROR) << "has no HAL to talk to";
+ return GK_ERROR;
+ }
- android::hardware::hidl_vec<uint8_t> curPwdHandle;
- android::hardware::hidl_vec<uint8_t> curPwd;
+ android::hardware::hidl_vec<uint8_t> curPwdHandle;
+ android::hardware::hidl_vec<uint8_t> curPwd;
- if (currentPasswordHandle && currentPassword) {
- if (hw_device) {
- // Hidl Implementations expects passwordHandle to be in
- // gatekeeper::password_handle_t format.
- if (currentPasswordHandle->size() != sizeof(gatekeeper::password_handle_t)) {
- LOG(INFO) << "Password handle has wrong length";
- return GK_ERROR;
- }
- }
- curPwdHandle.setToExternal(const_cast<uint8_t*>(currentPasswordHandle->data()),
- currentPasswordHandle->size());
- curPwd.setToExternal(const_cast<uint8_t*>(currentPassword->data()),
- currentPassword->size());
- }
-
- android::hardware::hidl_vec<uint8_t> newPwd;
- newPwd.setToExternal(const_cast<uint8_t*>(desiredPassword.data()), desiredPassword.size());
-
- uint32_t hw_userId = adjust_userId(userId);
- uint64_t secureUserId = 0;
- if (aidl_hw_device) {
- // AIDL gatekeeper service
- AidlGatekeeperEnrollResp rsp;
- auto result = aidl_hw_device->enroll(hw_userId, curPwdHandle, curPwd, newPwd, &rsp);
- if (!result.isOk()) {
- LOG(ERROR) << "enroll transaction failed";
+ if (currentPasswordHandle && currentPassword) {
+ if (hw_device) {
+ // Hidl Implementations expects passwordHandle to be in
+ // gatekeeper::password_handle_t format.
+ if (currentPasswordHandle->size() != sizeof(gatekeeper::password_handle_t)) {
+ LOG(INFO) << "Password handle has wrong length";
return GK_ERROR;
}
- if (rsp.statusCode >= AidlIGatekeeper::STATUS_OK) {
- *gkResponse = GKResponse::ok({rsp.data.begin(), rsp.data.end()});
- secureUserId = static_cast<uint64_t>(rsp.secureUserId);
- } else if (rsp.statusCode == AidlIGatekeeper::ERROR_RETRY_TIMEOUT &&
- rsp.timeoutMs > 0) {
- *gkResponse = GKResponse::retry(rsp.timeoutMs);
- } else {
- *gkResponse = GKResponse::error();
- }
- } else if (hw_device) {
- // HIDL gatekeeper service
- Return<void> hwRes = hw_device->enroll(
+ }
+ curPwdHandle.setToExternal(const_cast<uint8_t*>(currentPasswordHandle->data()),
+ currentPasswordHandle->size());
+ curPwd.setToExternal(const_cast<uint8_t*>(currentPassword->data()),
+ currentPassword->size());
+ }
+
+ android::hardware::hidl_vec<uint8_t> newPwd;
+ newPwd.setToExternal(const_cast<uint8_t*>(desiredPassword.data()), desiredPassword.size());
+
+ uint32_t hw_userId = 0;
+ Status result = adjust_userId(userId, &hw_userId);
+ if (!result.isOk()) {
+ return result;
+ }
+
+ uint64_t secureUserId = 0;
+ if (aidl_hw_device) {
+ // AIDL gatekeeper service
+ AidlGatekeeperEnrollResp rsp;
+ auto result = aidl_hw_device->enroll(hw_userId, curPwdHandle, curPwd, newPwd, &rsp);
+ if (!result.isOk()) {
+ LOG(ERROR) << "enroll transaction failed";
+ return GK_ERROR;
+ }
+ if (rsp.statusCode >= AidlIGatekeeper::STATUS_OK) {
+ *gkResponse = GKResponse::ok({rsp.data.begin(), rsp.data.end()});
+ secureUserId = static_cast<uint64_t>(rsp.secureUserId);
+ } else if (rsp.statusCode == AidlIGatekeeper::ERROR_RETRY_TIMEOUT && rsp.timeoutMs > 0) {
+ *gkResponse = GKResponse::retry(rsp.timeoutMs);
+ } else {
+ *gkResponse = GKResponse::error();
+ }
+ } else if (hw_device) {
+ // HIDL gatekeeper service
+ Return<void> hwRes = hw_device->enroll(
hw_userId, curPwdHandle, curPwd, newPwd,
[&gkResponse](const GatekeeperResponse& rsp) {
if (rsp.code >= GatekeeperStatusCode::STATUS_OK) {
@@ -251,110 +250,115 @@
*gkResponse = GKResponse::error();
}
});
- if (!hwRes.isOk()) {
- LOG(ERROR) << "enroll transaction failed";
+ if (!hwRes.isOk()) {
+ LOG(ERROR) << "enroll transaction failed";
+ return GK_ERROR;
+ }
+ if (gkResponse->response_code() == GKResponseCode::OK) {
+ if (gkResponse->payload().size() != sizeof(gatekeeper::password_handle_t)) {
+ LOG(ERROR) << "HAL returned password handle of invalid length "
+ << gkResponse->payload().size();
return GK_ERROR;
}
- if (gkResponse->response_code() == GKResponseCode::OK) {
- if (gkResponse->payload().size() != sizeof(gatekeeper::password_handle_t)) {
- LOG(ERROR) << "HAL returned password handle of invalid length "
- << gkResponse->payload().size();
- return GK_ERROR;
- }
- const gatekeeper::password_handle_t* handle =
+ const gatekeeper::password_handle_t* handle =
reinterpret_cast<const gatekeeper::password_handle_t*>(
- gkResponse->payload().data());
- secureUserId = handle->user_id;
- }
+ gkResponse->payload().data());
+ secureUserId = handle->user_id;
}
-
- if (gkResponse->response_code() == GKResponseCode::OK && !gkResponse->should_reenroll()) {
- store_sid(userId, secureUserId);
-
- GKResponse verifyResponse;
- // immediately verify this password so we don't ask the user to enter it again
- // if they just created it.
- auto status = verify(userId, gkResponse->payload(), desiredPassword, &verifyResponse);
- if (!status.isOk() || verifyResponse.response_code() != GKResponseCode::OK) {
- LOG(ERROR) << "Failed to verify password after enrolling";
- }
- }
-
- return Status::ok();
}
- Status verify(int32_t userId, const ::std::vector<uint8_t>& enrolledPasswordHandle,
- const ::std::vector<uint8_t>& providedPassword, GKResponse* gkResponse) override {
- return verifyChallenge(userId, 0 /* challenge */, enrolledPasswordHandle, providedPassword,
- gkResponse);
+ if (gkResponse->response_code() == GKResponseCode::OK && !gkResponse->should_reenroll()) {
+ store_sid(userId, secureUserId);
+
+ GKResponse verifyResponse;
+ // immediately verify this password so we don't ask the user to enter it again
+ // if they just created it.
+ auto status = verify(userId, gkResponse->payload(), desiredPassword, &verifyResponse);
+ if (!status.isOk() || verifyResponse.response_code() != GKResponseCode::OK) {
+ LOG(ERROR) << "Failed to verify password after enrolling";
+ }
}
- Status verifyChallenge(int32_t userId, int64_t challenge,
- const std::vector<uint8_t>& enrolledPasswordHandle,
- const std::vector<uint8_t>& providedPassword,
- GKResponse* gkResponse) override {
- IPCThreadState* ipc = IPCThreadState::self();
- const int calling_pid = ipc->getCallingPid();
- const int calling_uid = ipc->getCallingUid();
- if (!PermissionCache::checkPermission(KEYGUARD_PERMISSION, calling_pid, calling_uid)) {
+ return Status::ok();
+}
+
+Status GateKeeperProxy::verify(int32_t userId, const ::std::vector<uint8_t>& enrolledPasswordHandle,
+ const ::std::vector<uint8_t>& providedPassword,
+ GKResponse* gkResponse) {
+ return verifyChallenge(userId, 0 /* challenge */, enrolledPasswordHandle, providedPassword,
+ gkResponse);
+}
+
+Status GateKeeperProxy::verifyChallenge(int32_t userId, int64_t challenge,
+ const std::vector<uint8_t>& enrolledPasswordHandle,
+ const std::vector<uint8_t>& providedPassword,
+ GKResponse* gkResponse) {
+ IPCThreadState* ipc = IPCThreadState::self();
+ const int calling_pid = ipc->getCallingPid();
+ const int calling_uid = ipc->getCallingUid();
+ if (!PermissionCache::checkPermission(KEYGUARD_PERMISSION, calling_pid, calling_uid)) {
+ return GK_ERROR;
+ }
+
+ // can't verify if we're missing either param
+ if (enrolledPasswordHandle.size() == 0 || providedPassword.size() == 0) return GK_ERROR;
+
+ if (!aidl_hw_device && !hw_device) {
+ LOG(ERROR) << "has no HAL to talk to";
+ return GK_ERROR;
+ }
+
+ if (hw_device) {
+ // Hidl Implementations expects passwordHandle to be in gatekeeper::password_handle_t
+ if (enrolledPasswordHandle.size() != sizeof(gatekeeper::password_handle_t)) {
+ LOG(INFO) << "Password handle has wrong length";
return GK_ERROR;
}
+ }
- // can't verify if we're missing either param
- if (enrolledPasswordHandle.size() == 0 || providedPassword.size() == 0) return GK_ERROR;
+ uint32_t hw_userId = 0;
+ Status result = adjust_userId(userId, &hw_userId);
+ if (!result.isOk()) {
+ return result;
+ }
- if (!aidl_hw_device && !hw_device) {
- LOG(ERROR) << "has no HAL to talk to";
+ android::hardware::hidl_vec<uint8_t> curPwdHandle;
+ curPwdHandle.setToExternal(const_cast<uint8_t*>(enrolledPasswordHandle.data()),
+ enrolledPasswordHandle.size());
+ android::hardware::hidl_vec<uint8_t> enteredPwd;
+ enteredPwd.setToExternal(const_cast<uint8_t*>(providedPassword.data()),
+ providedPassword.size());
+
+ uint64_t secureUserId = 0;
+ if (aidl_hw_device) {
+ // AIDL gatekeeper service
+ AidlGatekeeperVerifyResp rsp;
+ auto result = aidl_hw_device->verify(hw_userId, challenge, curPwdHandle, enteredPwd, &rsp);
+ if (!result.isOk()) {
+ LOG(ERROR) << "verify transaction failed";
return GK_ERROR;
}
-
- if (hw_device) {
- // Hidl Implementations expects passwordHandle to be in gatekeeper::password_handle_t
- if (enrolledPasswordHandle.size() != sizeof(gatekeeper::password_handle_t)) {
- LOG(INFO) << "Password handle has wrong length";
- return GK_ERROR;
- }
+ if (rsp.statusCode >= AidlIGatekeeper::STATUS_OK) {
+ secureUserId = rsp.hardwareAuthToken.userId;
+ // Serialize HardwareAuthToken to a vector as hw_auth_token_t.
+ *gkResponse = GKResponse::ok(
+ authToken2AidlVec(rsp.hardwareAuthToken),
+ rsp.statusCode == AidlIGatekeeper::STATUS_REENROLL /* reenroll */);
+ } else if (rsp.statusCode == AidlIGatekeeper::ERROR_RETRY_TIMEOUT) {
+ *gkResponse = GKResponse::retry(rsp.timeoutMs);
+ } else {
+ *gkResponse = GKResponse::error();
}
-
- uint32_t hw_userId = adjust_userId(userId);
- android::hardware::hidl_vec<uint8_t> curPwdHandle;
- curPwdHandle.setToExternal(const_cast<uint8_t*>(enrolledPasswordHandle.data()),
- enrolledPasswordHandle.size());
- android::hardware::hidl_vec<uint8_t> enteredPwd;
- enteredPwd.setToExternal(const_cast<uint8_t*>(providedPassword.data()),
- providedPassword.size());
-
- uint64_t secureUserId = 0;
- if (aidl_hw_device) {
- // AIDL gatekeeper service
- AidlGatekeeperVerifyResp rsp;
- auto result =
- aidl_hw_device->verify(hw_userId, challenge, curPwdHandle, enteredPwd, &rsp);
- if (!result.isOk()) {
- LOG(ERROR) << "verify transaction failed";
- return GK_ERROR;
- }
- if (rsp.statusCode >= AidlIGatekeeper::STATUS_OK) {
- secureUserId = rsp.hardwareAuthToken.userId;
- // Serialize HardwareAuthToken to a vector as hw_auth_token_t.
- *gkResponse = GKResponse::ok(authToken2AidlVec(rsp.hardwareAuthToken),
- rsp.statusCode ==
- AidlIGatekeeper::STATUS_REENROLL /* reenroll */);
- } else if (rsp.statusCode == AidlIGatekeeper::ERROR_RETRY_TIMEOUT) {
- *gkResponse = GKResponse::retry(rsp.timeoutMs);
- } else {
- *gkResponse = GKResponse::error();
- }
- } else if (hw_device) {
- // HIDL gatekeeper service
- Return<void> hwRes = hw_device->verify(
+ } else if (hw_device) {
+ // HIDL gatekeeper service
+ Return<void> hwRes = hw_device->verify(
hw_userId, challenge, curPwdHandle, enteredPwd,
[&gkResponse](const GatekeeperResponse& rsp) {
if (rsp.code >= GatekeeperStatusCode::STATUS_OK) {
*gkResponse = GKResponse::ok(
- {rsp.data.begin(), rsp.data.end()},
- rsp.code == GatekeeperStatusCode::STATUS_REENROLL /* reenroll */);
+ {rsp.data.begin(), rsp.data.end()},
+ rsp.code == GatekeeperStatusCode::STATUS_REENROLL /* reenroll */);
} else if (rsp.code == GatekeeperStatusCode::ERROR_RETRY_TIMEOUT) {
*gkResponse = GKResponse::retry(rsp.timeout);
} else {
@@ -362,149 +366,115 @@
}
});
- if (!hwRes.isOk()) {
- LOG(ERROR) << "verify transaction failed";
- return GK_ERROR;
- }
- const gatekeeper::password_handle_t* handle =
- reinterpret_cast<const gatekeeper::password_handle_t*>(
- enrolledPasswordHandle.data());
- secureUserId = handle->user_id;
+ if (!hwRes.isOk()) {
+ LOG(ERROR) << "verify transaction failed";
+ return GK_ERROR;
}
+ const gatekeeper::password_handle_t* handle =
+ reinterpret_cast<const gatekeeper::password_handle_t*>(
+ enrolledPasswordHandle.data());
+ secureUserId = handle->user_id;
+ }
- if (gkResponse->response_code() == GKResponseCode::OK) {
- if (gkResponse->payload().size() != 0) {
- // try to connect to IKeystoreAuthorization AIDL service first.
- AIBinder* authzAIBinder =
- AServiceManager_getService("android.security.authorization");
- ::ndk::SpAIBinder authzBinder(authzAIBinder);
- auto authzService = IKeystoreAuthorization::fromBinder(authzBinder);
- if (authzService) {
- if (gkResponse->payload().size() != sizeof(hw_auth_token_t)) {
- LOG(ERROR) << "Incorrect size of AuthToken payload.";
- return GK_ERROR;
- }
-
- const hw_auth_token_t* hwAuthToken =
- reinterpret_cast<const hw_auth_token_t*>(gkResponse->payload().data());
- HardwareAuthToken authToken;
-
- authToken.timestamp.milliSeconds = betoh64(hwAuthToken->timestamp);
- authToken.challenge = hwAuthToken->challenge;
- authToken.userId = hwAuthToken->user_id;
- authToken.authenticatorId = hwAuthToken->authenticator_id;
- authToken.authenticatorType = static_cast<HardwareAuthenticatorType>(
- betoh32(hwAuthToken->authenticator_type));
- authToken.mac.assign(&hwAuthToken->hmac[0], &hwAuthToken->hmac[32]);
- auto result = authzService->addAuthToken(authToken);
- if (!result.isOk()) {
- LOG(ERROR) << "Failure in sending AuthToken to AuthorizationService.";
- return GK_ERROR;
- }
- } else {
- LOG(ERROR) << "Cannot deliver auth token. Unable to communicate with "
- "Keystore.";
+ if (gkResponse->response_code() == GKResponseCode::OK) {
+ if (gkResponse->payload().size() != 0) {
+ // try to connect to IKeystoreAuthorization AIDL service first.
+ AIBinder* authzAIBinder = AServiceManager_getService("android.security.authorization");
+ ::ndk::SpAIBinder authzBinder(authzAIBinder);
+ auto authzService = IKeystoreAuthorization::fromBinder(authzBinder);
+ if (authzService) {
+ if (gkResponse->payload().size() != sizeof(hw_auth_token_t)) {
+ LOG(ERROR) << "Incorrect size of AuthToken payload.";
return GK_ERROR;
}
+
+ const hw_auth_token_t* hwAuthToken =
+ reinterpret_cast<const hw_auth_token_t*>(gkResponse->payload().data());
+ HardwareAuthToken authToken;
+
+ authToken.timestamp.milliSeconds = betoh64(hwAuthToken->timestamp);
+ authToken.challenge = hwAuthToken->challenge;
+ authToken.userId = hwAuthToken->user_id;
+ authToken.authenticatorId = hwAuthToken->authenticator_id;
+ authToken.authenticatorType = static_cast<HardwareAuthenticatorType>(
+ betoh32(hwAuthToken->authenticator_type));
+ authToken.mac.assign(&hwAuthToken->hmac[0], &hwAuthToken->hmac[32]);
+ auto result = authzService->addAuthToken(authToken);
+ if (!result.isOk()) {
+ LOG(ERROR) << "Failure in sending AuthToken to AuthorizationService.";
+ return GK_ERROR;
+ }
+ } else {
+ LOG(ERROR) << "Cannot deliver auth token. Unable to communicate with "
+ "Keystore.";
+ return GK_ERROR;
}
-
- maybe_store_sid(userId, secureUserId);
}
- return Status::ok();
+ maybe_store_sid(userId, secureUserId);
}
- Status getSecureUserId(int32_t userId, int64_t* sid) override {
- *sid = read_sid(userId);
- return Status::ok();
- }
-
- Status clearSecureUserId(int32_t userId) override {
- IPCThreadState* ipc = IPCThreadState::self();
- const int calling_pid = ipc->getCallingPid();
- const int calling_uid = ipc->getCallingUid();
- if (!PermissionCache::checkPermission(KEYGUARD_PERMISSION, calling_pid, calling_uid)) {
- ALOGE("%s: permission denied for [%d:%d]", __func__, calling_pid, calling_uid);
- return Status::ok();
- }
- clear_sid(userId);
-
- uint32_t hw_userId = adjust_userId(userId);
- if (aidl_hw_device) {
- aidl_hw_device->deleteUser(hw_userId);
- } else if (hw_device) {
- hw_device->deleteUser(hw_userId, [](const GatekeeperResponse&) {});
- }
- return Status::ok();
- }
-
- Status reportDeviceSetupComplete() override {
- IPCThreadState* ipc = IPCThreadState::self();
- const int calling_pid = ipc->getCallingPid();
- const int calling_uid = ipc->getCallingUid();
- if (!PermissionCache::checkPermission(KEYGUARD_PERMISSION, calling_pid, calling_uid)) {
- ALOGE("%s: permission denied for [%d:%d]", __func__, calling_pid, calling_uid);
- return Status::ok();
- }
-
- clear_state_if_needed();
- return Status::ok();
- }
-
- status_t dump(int fd, const Vector<String16>&) override {
- IPCThreadState* ipc = IPCThreadState::self();
- const int pid = ipc->getCallingPid();
- const int uid = ipc->getCallingUid();
- if (!PermissionCache::checkPermission(DUMP_PERMISSION, pid, uid)) {
- return PERMISSION_DENIED;
- }
-
- if (aidl_hw_device == nullptr && hw_device == nullptr) {
- const char* result = "Device not available";
- write(fd, result, strlen(result) + 1);
- } else {
- const char* result = "OK";
- write(fd, result, strlen(result) + 1);
- }
-
- return OK;
- }
-
- private:
- // AIDL gatekeeper service.
- std::shared_ptr<AidlIGatekeeper> aidl_hw_device;
- // HIDL gatekeeper service.
- sp<IGatekeeper> hw_device;
-
- bool clear_state_if_needed_done;
- bool is_running_gsi;
-};
-} // namespace android
-
-int main(int argc, char* argv[]) {
- ALOGI("Starting gatekeeperd...");
- if (argc < 2) {
- ALOGE("A directory must be specified!");
- return 1;
- }
- if (chdir(argv[1]) == -1) {
- ALOGE("chdir: %s: %s", argv[1], strerror(errno));
- return 1;
- }
-
- android::sp<android::IServiceManager> sm = android::defaultServiceManager();
- android::sp<android::GateKeeperProxy> proxy = new android::GateKeeperProxy();
- android::status_t ret =
- sm->addService(android::String16("android.service.gatekeeper.IGateKeeperService"), proxy);
- if (ret != android::OK) {
- ALOGE("Couldn't register binder service!");
- return -1;
- }
-
- /*
- * We're the only thread in existence, so we're just going to process
- * Binder transaction as a single-threaded program.
- */
- android::IPCThreadState::self()->joinThreadPool();
- return 0;
+ return Status::ok();
}
+
+Status GateKeeperProxy::getSecureUserId(int32_t userId, int64_t* sid) {
+ *sid = read_sid(userId);
+ return Status::ok();
+}
+
+Status GateKeeperProxy::clearSecureUserId(int32_t userId) {
+ IPCThreadState* ipc = IPCThreadState::self();
+ const int calling_pid = ipc->getCallingPid();
+ const int calling_uid = ipc->getCallingUid();
+ if (!PermissionCache::checkPermission(KEYGUARD_PERMISSION, calling_pid, calling_uid)) {
+ ALOGE("%s: permission denied for [%d:%d]", __func__, calling_pid, calling_uid);
+ return Status::ok();
+ }
+ clear_sid(userId);
+
+ uint32_t hw_userId = 0;
+ Status result = adjust_userId(userId, &hw_userId);
+ if (!result.isOk()) {
+ return result;
+ }
+
+ if (aidl_hw_device) {
+ aidl_hw_device->deleteUser(hw_userId);
+ } else if (hw_device) {
+ hw_device->deleteUser(hw_userId, [](const GatekeeperResponse&) {});
+ }
+ return Status::ok();
+}
+
+Status GateKeeperProxy::reportDeviceSetupComplete() {
+ IPCThreadState* ipc = IPCThreadState::self();
+ const int calling_pid = ipc->getCallingPid();
+ const int calling_uid = ipc->getCallingUid();
+ if (!PermissionCache::checkPermission(KEYGUARD_PERMISSION, calling_pid, calling_uid)) {
+ ALOGE("%s: permission denied for [%d:%d]", __func__, calling_pid, calling_uid);
+ return Status::ok();
+ }
+
+ clear_state_if_needed();
+ return Status::ok();
+}
+
+status_t GateKeeperProxy::dump(int fd, const Vector<String16>&) {
+ IPCThreadState* ipc = IPCThreadState::self();
+ const int pid = ipc->getCallingPid();
+ const int uid = ipc->getCallingUid();
+ if (!PermissionCache::checkPermission(DUMP_PERMISSION, pid, uid)) {
+ return PERMISSION_DENIED;
+ }
+
+ if (aidl_hw_device == nullptr && hw_device == nullptr) {
+ const char* result = "Device not available";
+ write(fd, result, strlen(result) + 1);
+ } else {
+ const char* result = "OK";
+ write(fd, result, strlen(result) + 1);
+ }
+
+ return OK;
+}
+} // namespace android
diff --git a/gatekeeperd/gatekeeperd.h b/gatekeeperd/gatekeeperd.h
new file mode 100644
index 0000000..b1f08c6
--- /dev/null
+++ b/gatekeeperd/gatekeeperd.h
@@ -0,0 +1,83 @@
+/*
+ * Copyright (C) 2023 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 <aidl/android/hardware/gatekeeper/IGatekeeper.h>
+#include <android/hardware/gatekeeper/1.0/IGatekeeper.h>
+#include <android/service/gatekeeper/BnGateKeeperService.h>
+#include <gatekeeper/GateKeeperResponse.h>
+
+using ::android::hardware::gatekeeper::V1_0::IGatekeeper;
+using AidlIGatekeeper = ::aidl::android::hardware::gatekeeper::IGatekeeper;
+using ::android::binder::Status;
+using ::android::service::gatekeeper::BnGateKeeperService;
+using GKResponse = ::android::service::gatekeeper::GateKeeperResponse;
+
+namespace android {
+
+class GateKeeperProxy : public BnGateKeeperService {
+ public:
+ GateKeeperProxy();
+
+ virtual ~GateKeeperProxy() {}
+
+ void store_sid(uint32_t userId, uint64_t sid);
+
+ void clear_state_if_needed();
+
+ bool mark_cold_boot();
+
+ void maybe_store_sid(uint32_t userId, uint64_t sid);
+
+ uint64_t read_sid(uint32_t userId);
+
+ void clear_sid(uint32_t userId);
+
+ // This should only be called on userIds being passed to the GateKeeper HAL. It ensures that
+ // secure storage shared across a GSI image and a host image will not overlap.
+ Status adjust_userId(uint32_t userId, uint32_t* hw_userId);
+
+#define GK_ERROR *gkResponse = GKResponse::error(), Status::ok()
+
+ Status enroll(int32_t userId, const std::optional<std::vector<uint8_t>>& currentPasswordHandle,
+ const std::optional<std::vector<uint8_t>>& currentPassword,
+ const std::vector<uint8_t>& desiredPassword, GKResponse* gkResponse) override;
+
+ Status verify(int32_t userId, const ::std::vector<uint8_t>& enrolledPasswordHandle,
+ const ::std::vector<uint8_t>& providedPassword, GKResponse* gkResponse) override;
+
+ Status verifyChallenge(int32_t userId, int64_t challenge,
+ const std::vector<uint8_t>& enrolledPasswordHandle,
+ const std::vector<uint8_t>& providedPassword,
+ GKResponse* gkResponse) override;
+
+ Status getSecureUserId(int32_t userId, int64_t* sid) override;
+
+ Status clearSecureUserId(int32_t userId) override;
+
+ Status reportDeviceSetupComplete() override;
+
+ status_t dump(int fd, const Vector<String16>&) override;
+
+ private:
+ // AIDL gatekeeper service.
+ std::shared_ptr<AidlIGatekeeper> aidl_hw_device;
+ // HIDL gatekeeper service.
+ sp<IGatekeeper> hw_device;
+
+ bool clear_state_if_needed_done;
+ bool is_running_gsi;
+};
+} // namespace android
diff --git a/gatekeeperd/main.cpp b/gatekeeperd/main.cpp
new file mode 100644
index 0000000..a01f9de
--- /dev/null
+++ b/gatekeeperd/main.cpp
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2023 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 <binder/IPCThreadState.h>
+#include <binder/IServiceManager.h>
+
+#include <log/log.h>
+
+#include "gatekeeperd.h"
+
+int main(int argc, char* argv[]) {
+ ALOGI("Starting gatekeeperd...");
+ if (argc < 2) {
+ ALOGE("A directory must be specified!");
+ return 1;
+ }
+ if (chdir(argv[1]) == -1) {
+ ALOGE("chdir: %s: %s", argv[1], strerror(errno));
+ return 1;
+ }
+
+ android::sp<android::IServiceManager> sm = android::defaultServiceManager();
+ android::sp<android::GateKeeperProxy> proxy = new android::GateKeeperProxy();
+ android::status_t ret = sm->addService(
+ android::String16("android.service.gatekeeper.IGateKeeperService"), proxy);
+ if (ret != android::OK) {
+ ALOGE("Couldn't register binder service!");
+ return 1;
+ }
+
+ /*
+ * We're the only thread in existence, so we're just going to process
+ * Binder transaction as a single-threaded program.
+ */
+ android::IPCThreadState::self()->joinThreadPool();
+ return 1;
+}
diff --git a/init/Android.bp b/init/Android.bp
index 7b52903..ee34215 100644
--- a/init/Android.bp
+++ b/init/Android.bp
@@ -166,10 +166,9 @@
"libbootloader_message",
"libc++fs",
"libcgrouprc_format",
- "libfsverity_init",
"liblmkd_utils",
"liblz4",
- "libmini_keyctl_static",
+ "libzstd",
"libmodprobe",
"libprocinfo",
"libprotobuf-cpp-lite",
@@ -213,8 +212,8 @@
visibility: [":__subpackages__"],
}
-cc_library_static {
- name: "libinit",
+cc_defaults {
+ name: "libinit_defaults",
recovery_available: true,
defaults: [
"init_defaults",
@@ -227,7 +226,6 @@
],
whole_static_libs: [
"libcap",
- "libcom.android.sysprop.apex",
"libcom.android.sysprop.init",
],
header_libs: ["bootimg_headers"],
@@ -251,10 +249,17 @@
],
},
},
- visibility: [
- "//system/apex/apexd",
- "//frameworks/native/cmds/installd",
- ],
+}
+
+cc_library_static {
+ name: "libinit",
+ defaults: ["libinit_defaults"],
+}
+
+cc_library_static {
+ name: "libinit.microdroid",
+ defaults: ["libinit_defaults"],
+ cflags: ["-DMICRODROID=1"],
}
phony {
@@ -264,12 +269,11 @@
],
}
-cc_binary {
- name: "init_second_stage",
+cc_defaults {
+ name: "init_second_stage_defaults",
recovery_available: true,
stem: "init",
defaults: ["init_defaults"],
- static_libs: ["libinit"],
srcs: ["main.cpp"],
symlinks: ["ueventd"],
target: {
@@ -303,9 +307,24 @@
],
},
},
+}
+
+cc_binary {
+ name: "init_second_stage",
+ defaults: ["init_second_stage_defaults"],
+ static_libs: ["libinit"],
+}
+
+cc_binary {
+ name: "init_second_stage.microdroid",
+ defaults: ["init_second_stage_defaults"],
+ static_libs: ["libinit.microdroid"],
+ cflags: ["-DMICRODROID=1"],
+ installable: false,
visibility: ["//packages/modules/Virtualization/microdroid"],
}
+
soong_config_module_type {
name: "init_first_stage_cc_defaults",
module_type: "cc_defaults",
@@ -323,12 +342,8 @@
installable: false,
},
},
-}
-cc_binary {
- name: "init_first_stage",
stem: "init",
- defaults: ["init_first_stage_defaults"],
srcs: [
"block_dev_initializer.cpp",
@@ -370,12 +385,17 @@
"libprotobuf-cpp-lite",
"libsnapshot_cow",
"liblz4",
+ "libzstd",
"libsnapshot_init",
"update_metadata-protos",
"libprocinfo",
],
static_executable: true,
+ lto: {
+ // b/169004486 ThinLTO breaks x86 static executables.
+ never: true,
+ },
system_shared_libs: [],
cflags: [
@@ -441,6 +461,18 @@
install_in_root: true,
}
+cc_binary {
+ name: "init_first_stage",
+ defaults: ["init_first_stage_defaults"],
+}
+
+cc_binary {
+ name: "init_first_stage.microdroid",
+ defaults: ["init_first_stage_defaults"],
+ cflags: ["-DMICRODROID=1"],
+ installable: false,
+}
+
phony {
name: "init_system",
required: ["init_second_stage"],
diff --git a/init/README.md b/init/README.md
index 6bdff4a..5fced19 100644
--- a/init/README.md
+++ b/init/README.md
@@ -344,11 +344,14 @@
intended to be used with the `exec_start` builtin for any must-have checks during boot.
`restart_period <seconds>`
-> If a non-oneshot service exits, it will be restarted at its start time plus
- this period. It defaults to 5s to rate limit crashing services.
- This can be increased for services that are meant to run periodically. For
- example, it may be set to 3600 to indicate that the service should run every hour
- or 86400 to indicate that the service should run every day.
+> If a non-oneshot service exits, it will be restarted at its previous start time plus this period.
+ The default value is 5s. This can be used to implement periodic services together with the
+ `timeout_period` command below. For example, it may be set to 3600 to indicate that the service
+ should run every hour or 86400 to indicate that the service should run every day. This can be set
+ to a value shorter than 5s for example 0, but the minimum 5s delay is enforced if the restart was
+ due to a crash. This is to rate limit persistentally crashing services. In other words,
+ `<seconds>` smaller than 5 is respected only when the service exits deliverately and successfully
+ (i.e. by calling exit(0)).
`rlimit <resource> <cur> <max>`
> This applies the given rlimit to the service. rlimits are inherited by child
diff --git a/init/builtins.cpp b/init/builtins.cpp
index bc23972..2176233 100644
--- a/init/builtins.cpp
+++ b/init/builtins.cpp
@@ -60,6 +60,8 @@
#include <cutils/android_reboot.h>
#include <fs_mgr.h>
#include <fscrypt/fscrypt.h>
+#include <libdm/dm.h>
+#include <libdm/loop_control.h>
#include <libgsi/libgsi.h>
#include <logwrap/logwrap.h>
#include <private/android_filesystem_config.h>
@@ -506,29 +508,29 @@
if (android::base::StartsWith(source, "loop@")) {
int mode = (flags & MS_RDONLY) ? O_RDONLY : O_RDWR;
- unique_fd fd(TEMP_FAILURE_RETRY(open(source + 5, mode | O_CLOEXEC)));
- if (fd < 0) return ErrnoError() << "open(" << source + 5 << ", " << mode << ") failed";
+ const char* file_path = source + strlen("loop@");
- for (size_t n = 0;; n++) {
- std::string tmp = android::base::StringPrintf("/dev/block/loop%zu", n);
- unique_fd loop(TEMP_FAILURE_RETRY(open(tmp.c_str(), mode | O_CLOEXEC)));
- if (loop < 0) return ErrnoError() << "open(" << tmp << ", " << mode << ") failed";
-
- loop_info info;
- /* if it is a blank loop device */
- if (ioctl(loop.get(), LOOP_GET_STATUS, &info) < 0 && errno == ENXIO) {
- /* if it becomes our loop device */
- if (ioctl(loop.get(), LOOP_SET_FD, fd.get()) >= 0) {
- if (mount(tmp.c_str(), target, system, flags, options) < 0) {
- ioctl(loop.get(), LOOP_CLR_FD, 0);
- return ErrnoError() << "mount() failed";
- }
- return {};
- }
- }
+ // Open source file
+ if (wait) {
+ wait_for_file(file_path, kCommandRetryTimeout);
}
- return Error() << "out of loopback devices";
+ unique_fd fd(TEMP_FAILURE_RETRY(open(file_path, mode | O_CLOEXEC)));
+ if (fd < 0) {
+ return ErrnoError() << "open(" << file_path << ", " << mode << ") failed";
+ }
+
+ // Allocate loop device and attach it to file_path.
+ android::dm::LoopControl loop_control;
+ std::string loop_device;
+ if (!loop_control.Attach(fd.get(), 5s, &loop_device)) {
+ return ErrnoError() << "loop_control.Attach " << file_path << " failed";
+ }
+
+ if (mount(loop_device.c_str(), target, system, flags, options) < 0) {
+ loop_control.Detach(loop_device);
+ return ErrnoError() << "mount() failed";
+ }
} else {
if (wait)
wait_for_file(source, kCommandRetryTimeout);
@@ -575,7 +577,7 @@
*
* return code is processed based on input code
*/
-static Result<void> queue_fs_event(int code, bool userdata_remount) {
+static Result<void> queue_fs_event(int code) {
if (code == FS_MGR_MNTALL_DEV_NOT_ENCRYPTABLE) {
SetProperty("ro.crypto.state", "unsupported");
ActionManager::GetInstance().QueueEventTrigger("nonencrypted");
@@ -589,27 +591,9 @@
const std::vector<std::string> options = {"--wipe_data", "--reason=fs_mgr_mount_all" };
return reboot_into_recovery(options);
/* If reboot worked, there is no return. */
- } else if (code == FS_MGR_MNTALL_DEV_FILE_ENCRYPTED) {
- if (!FscryptInstallKeyring()) {
- return Error() << "FscryptInstallKeyring() failed";
- }
- SetProperty("ro.crypto.state", "encrypted");
-
- // Although encrypted, we have device key, so we do not need to
- // do anything different from the nonencrypted case.
- ActionManager::GetInstance().QueueEventTrigger("nonencrypted");
- return {};
- } else if (code == FS_MGR_MNTALL_DEV_IS_METADATA_ENCRYPTED) {
- if (!FscryptInstallKeyring()) {
- return Error() << "FscryptInstallKeyring() failed";
- }
- SetProperty("ro.crypto.state", "encrypted");
-
- // Although encrypted, vold has already set the device up, so we do not need to
- // do anything different from the nonencrypted case.
- ActionManager::GetInstance().QueueEventTrigger("nonencrypted");
- return {};
- } else if (code == FS_MGR_MNTALL_DEV_NEEDS_METADATA_ENCRYPTION) {
+ } else if (code == FS_MGR_MNTALL_DEV_FILE_ENCRYPTED ||
+ code == FS_MGR_MNTALL_DEV_IS_METADATA_ENCRYPTED ||
+ code == FS_MGR_MNTALL_DEV_NEEDS_METADATA_ENCRYPTION) {
if (!FscryptInstallKeyring()) {
return Error() << "FscryptInstallKeyring() failed";
}
@@ -681,7 +665,7 @@
if (queue_event) {
/* queue_fs_event will queue event based on mount_fstab return code
* and return processed return code*/
- auto queue_fs_result = queue_fs_event(mount_fstab_result.code, false);
+ auto queue_fs_result = queue_fs_event(mount_fstab_result.code);
if (!queue_fs_result.ok()) {
return Error() << "queue_fs_event() failed: " << queue_fs_result.error();
}
@@ -1215,7 +1199,7 @@
"/metadata/userspacereboot/mount_info.txt");
trigger_shutdown("reboot,mount_userdata_failed");
}
- if (auto result = queue_fs_event(initial_mount_fstab_return_code, true); !result.ok()) {
+ if (auto result = queue_fs_event(initial_mount_fstab_return_code); !result.ok()) {
return Error() << "queue_fs_event() failed: " << result.error();
}
return {};
diff --git a/init/devices.cpp b/init/devices.cpp
index d29ffd6..7c23492 100644
--- a/init/devices.cpp
+++ b/init/devices.cpp
@@ -568,6 +568,8 @@
return;
} else if (uevent.subsystem == "misc" && StartsWith(uevent.device_name, "dm-user/")) {
devpath = "/dev/dm-user/" + uevent.device_name.substr(8);
+ } else if (uevent.subsystem == "misc" && uevent.device_name == "vfio/vfio") {
+ devpath = "/dev/" + uevent.device_name;
} else {
devpath = "/dev/" + Basename(uevent.path);
}
diff --git a/init/firmware_handler.cpp b/init/firmware_handler.cpp
index b9fa58c..3c012fe 100644
--- a/init/firmware_handler.cpp
+++ b/init/firmware_handler.cpp
@@ -33,6 +33,7 @@
#include <android-base/chrono_utils.h>
#include <android-base/file.h>
#include <android-base/logging.h>
+#include <android-base/properties.h>
#include <android-base/scopeguard.h>
#include <android-base/strings.h>
#include <android-base/unique_fd.h>
@@ -43,6 +44,7 @@
using android::base::Timer;
using android::base::Trim;
using android::base::unique_fd;
+using android::base::WaitForProperty;
using android::base::WriteFully;
namespace android {
@@ -82,6 +84,33 @@
return access("/dev/.booting", F_OK) == 0;
}
+static bool IsApexActivated() {
+ static bool apex_activated = []() {
+ // Wait for com.android.runtime.apex activation
+ // Property name and value must be kept in sync with system/apexd/apex/apex_constants.h
+ // 60s is the default firmware sysfs fallback timeout. (/sys/class/firmware/timeout)
+ if (!WaitForProperty("apexd.status", "activated", 60s)) {
+ LOG(ERROR) << "Apexd activation wait timeout";
+ return false;
+ }
+ return true;
+ }();
+
+ return apex_activated;
+}
+
+static bool NeedsRerunExternalHandler() {
+ static bool first = true;
+
+ // Rerun external handler only on the first try and when apex is activated
+ if (first) {
+ first = false;
+ return IsApexActivated();
+ }
+
+ return first;
+}
+
ExternalFirmwareHandler::ExternalFirmwareHandler(std::string devpath, uid_t uid, gid_t gid,
std::string handler_path)
: devpath(std::move(devpath)), uid(uid), gid(gid), handler_path(std::move(handler_path)) {
@@ -210,6 +239,11 @@
auto result = RunExternalHandler(external_handler.handler_path, external_handler.uid,
external_handler.gid, uevent);
+ if (!result.ok() && NeedsRerunExternalHandler()) {
+ auto res = RunExternalHandler(external_handler.handler_path, external_handler.uid,
+ external_handler.gid, uevent);
+ result = std::move(res);
+ }
if (!result.ok()) {
LOG(ERROR) << "Using default firmware; External firmware handler failed: "
<< result.error();
diff --git a/init/first_stage_init.cpp b/init/first_stage_init.cpp
index 107e99a..7fabbac 100644
--- a/init/first_stage_init.cpp
+++ b/init/first_stage_init.cpp
@@ -58,6 +58,12 @@
namespace {
+enum class BootMode {
+ NORMAL_MODE,
+ RECOVERY_MODE,
+ CHARGER_MODE,
+};
+
void FreeRamdisk(DIR* dir, dev_t dev) {
int dfd = dirfd(dir);
@@ -149,13 +155,27 @@
}
} // namespace
-std::string GetModuleLoadList(bool recovery, const std::string& dir_path) {
- auto module_load_file = "modules.load";
- if (recovery) {
- struct stat fileStat;
- std::string recovery_load_path = dir_path + "/modules.load.recovery";
- if (!stat(recovery_load_path.c_str(), &fileStat)) {
+std::string GetModuleLoadList(BootMode boot_mode, const std::string& dir_path) {
+ std::string module_load_file;
+
+ switch (boot_mode) {
+ case BootMode::NORMAL_MODE:
+ module_load_file = "modules.load";
+ break;
+ case BootMode::RECOVERY_MODE:
module_load_file = "modules.load.recovery";
+ break;
+ case BootMode::CHARGER_MODE:
+ module_load_file = "modules.load.charger";
+ break;
+ }
+
+ if (module_load_file != "modules.load") {
+ struct stat fileStat;
+ std::string load_path = dir_path + "/" + module_load_file;
+ // Fall back to modules.load if the other files aren't accessible
+ if (stat(load_path.c_str(), &fileStat)) {
+ module_load_file = "modules.load";
}
}
@@ -163,7 +183,8 @@
}
#define MODULE_BASE_DIR "/lib/modules"
-bool LoadKernelModules(bool recovery, bool want_console, bool want_parallel, int& modules_loaded) {
+bool LoadKernelModules(BootMode boot_mode, bool want_console, bool want_parallel,
+ int& modules_loaded) {
struct utsname uts;
if (uname(&uts)) {
LOG(FATAL) << "Failed to get kernel version.";
@@ -203,7 +224,7 @@
for (const auto& module_dir : module_dirs) {
std::string dir_path = MODULE_BASE_DIR "/";
dir_path.append(module_dir);
- Modprobe m({dir_path}, GetModuleLoadList(recovery, dir_path));
+ Modprobe m({dir_path}, GetModuleLoadList(boot_mode, dir_path));
bool retval = m.LoadListedModules(!want_console);
modules_loaded = m.GetModuleCount();
if (modules_loaded > 0) {
@@ -211,7 +232,7 @@
}
}
- Modprobe m({MODULE_BASE_DIR}, GetModuleLoadList(recovery, MODULE_BASE_DIR));
+ Modprobe m({MODULE_BASE_DIR}, GetModuleLoadList(boot_mode, MODULE_BASE_DIR));
bool retval = (want_parallel) ? m.LoadModulesParallel(std::thread::hardware_concurrency())
: m.LoadListedModules(!want_console);
modules_loaded = m.GetModuleCount();
@@ -221,6 +242,31 @@
return true;
}
+static bool IsChargerMode(const std::string& cmdline, const std::string& bootconfig) {
+ return bootconfig.find("androidboot.mode = \"charger\"") != std::string::npos ||
+ cmdline.find("androidboot.mode=charger") != std::string::npos;
+}
+
+static BootMode GetBootMode(const std::string& cmdline, const std::string& bootconfig)
+{
+ if (IsChargerMode(cmdline, bootconfig))
+ return BootMode::CHARGER_MODE;
+ else if (IsRecoveryMode() && !ForceNormalBoot(cmdline, bootconfig))
+ return BootMode::RECOVERY_MODE;
+
+ return BootMode::NORMAL_MODE;
+}
+
+static std::unique_ptr<FirstStageMount> CreateFirstStageMount(const std::string& cmdline) {
+ auto ret = FirstStageMount::Create(cmdline);
+ if (ret.ok()) {
+ return std::move(*ret);
+ } else {
+ LOG(ERROR) << "Failed to create FirstStageMount : " << ret.error();
+ return nullptr;
+ }
+}
+
int FirstStageMain(int argc, char** argv) {
if (REBOOT_BOOTLOADER_ON_PANIC) {
InstallRebootSignalHandlers();
@@ -311,6 +357,18 @@
LOG(INFO) << "init first stage started!";
+ // We only allow /vendor partition in debuggable Microdrod until it is verified during boot.
+ // TODO(b/285855436): remove this check.
+ if (IsMicrodroid()) {
+ bool mount_vendor =
+ cmdline.find("androidboot.microdroid.mount_vendor=1") != std::string::npos;
+ bool debuggable =
+ bootconfig.find("androidboot.microdroid.debuggable = \"1\"") != std::string::npos;
+ if (mount_vendor && !debuggable) {
+ LOG(FATAL) << "Attempted to mount /vendor partition for non-debuggable Microdroid VM";
+ }
+ }
+
auto old_root_dir = std::unique_ptr<DIR, decltype(&closedir)>{opendir("/"), closedir};
if (!old_root_dir) {
PLOG(ERROR) << "Could not opendir(\"/\"), not freeing ramdisk";
@@ -328,7 +386,8 @@
boot_clock::time_point module_start_time = boot_clock::now();
int module_count = 0;
- if (!LoadKernelModules(IsRecoveryMode() && !ForceNormalBoot(cmdline, bootconfig), want_console,
+ BootMode boot_mode = GetBootMode(cmdline, bootconfig);
+ if (!LoadKernelModules(boot_mode, want_console,
want_parallel, module_count)) {
if (want_console != FirstStageConsoleParam::DISABLED) {
LOG(ERROR) << "Failed to load kernel modules, starting console";
@@ -344,12 +403,17 @@
<< module_elapse_time.count() << " ms";
}
+ std::unique_ptr<FirstStageMount> fsm;
+
bool created_devices = false;
if (want_console == FirstStageConsoleParam::CONSOLE_ON_FAILURE) {
if (!IsRecoveryMode()) {
- created_devices = DoCreateDevices();
- if (!created_devices) {
- LOG(ERROR) << "Failed to create device nodes early";
+ fsm = CreateFirstStageMount(cmdline);
+ if (fsm) {
+ created_devices = fsm->DoCreateDevices();
+ if (!created_devices) {
+ LOG(ERROR) << "Failed to create device nodes early";
+ }
}
}
StartConsole(cmdline);
@@ -400,8 +464,23 @@
SwitchRoot("/first_stage_ramdisk");
}
- if (!DoFirstStageMount(!created_devices)) {
- LOG(FATAL) << "Failed to mount required partitions early ...";
+ if (IsRecoveryMode()) {
+ LOG(INFO) << "First stage mount skipped (recovery mode)";
+ } else {
+ if (!fsm) {
+ fsm = CreateFirstStageMount(cmdline);
+ }
+ if (!fsm) {
+ LOG(FATAL) << "FirstStageMount not available";
+ }
+
+ if (!created_devices && !fsm->DoCreateDevices()) {
+ LOG(FATAL) << "Failed to create devices required for first stage mount";
+ }
+
+ if (!fsm->DoFirstStageMount()) {
+ LOG(FATAL) << "Failed to mount required partitions early ...";
+ }
}
struct stat new_root_info;
diff --git a/init/first_stage_mount.cpp b/init/first_stage_mount.cpp
index 07ce458..d0f68a8 100644
--- a/init/first_stage_mount.cpp
+++ b/init/first_stage_mount.cpp
@@ -76,21 +76,21 @@
// Class Declarations
// ------------------
-class FirstStageMount {
+class FirstStageMountVBootV2 : public FirstStageMount {
public:
- FirstStageMount(Fstab fstab);
- virtual ~FirstStageMount() = default;
+ friend void SetInitAvbVersionInRecovery();
- // The factory method to create a FirstStageMountVBootV2 instance.
- static Result<std::unique_ptr<FirstStageMount>> Create();
- bool DoCreateDevices(); // Creates devices and logical partitions from storage devices
- bool DoFirstStageMount(); // Mounts fstab entries read from device tree.
+ FirstStageMountVBootV2(Fstab fstab);
+ virtual ~FirstStageMountVBootV2() = default;
+
+ bool DoCreateDevices() override;
+ bool DoFirstStageMount() override;
+
+ private:
bool InitDevices();
-
- protected:
bool InitRequiredDevices(std::set<std::string> devices);
bool CreateLogicalPartitions();
- bool CreateSnapshotPartitions(android::snapshot::SnapshotManager* sm);
+ bool CreateSnapshotPartitions(SnapshotManager* sm);
bool MountPartition(const Fstab::iterator& begin, bool erase_same_mounts,
Fstab::iterator* end = nullptr);
@@ -106,9 +106,10 @@
// revocation check by DSU installation service.
void CopyDsuAvbKeys();
- // Pure virtual functions.
- virtual bool GetDmVerityDevices(std::set<std::string>* devices) = 0;
- virtual bool SetUpDmVerity(FstabEntry* fstab_entry) = 0;
+ bool GetDmVerityDevices(std::set<std::string>* devices);
+ bool SetUpDmVerity(FstabEntry* fstab_entry);
+
+ bool InitAvbHandle();
bool need_dm_verity_;
bool dsu_not_on_userdata_ = false;
@@ -122,19 +123,6 @@
// Reads all AVB keys before chroot into /system, as they might be used
// later when mounting other partitions, e.g., /vendor and /product.
std::map<std::string, std::vector<std::string>> preload_avb_key_blobs_;
-};
-
-class FirstStageMountVBootV2 : public FirstStageMount {
- public:
- friend void SetInitAvbVersionInRecovery();
-
- FirstStageMountVBootV2(Fstab fstab);
- ~FirstStageMountVBootV2() override = default;
-
- protected:
- bool GetDmVerityDevices(std::set<std::string>* devices) override;
- bool SetUpDmVerity(FstabEntry* fstab_entry) override;
- bool InitAvbHandle();
std::vector<std::string> vbmeta_partitions_;
AvbUniquePtr avb_handle_;
@@ -150,7 +138,7 @@
return is_android_dt_value_expected("vbmeta/compatible", "android,vbmeta");
}
-static Result<Fstab> ReadFirstStageFstab() {
+static Result<Fstab> ReadFirstStageFstabAndroid() {
Fstab fstab;
if (!ReadFstabFromDt(&fstab)) {
if (ReadDefaultFstab(&fstab)) {
@@ -166,6 +154,24 @@
return fstab;
}
+// Note: this is a temporary solution to avoid blocking devs that depend on /vendor partition in
+// Microdroid. For the proper solution the /vendor fstab should probably be defined in the DT.
+// TODO(b/285855430): refactor this
+// TODO(b/285855436): verify key microdroid-vendor was signed with.
+// TODO(b/285855436): should be mounted on top of dm-verity device.
+static Result<Fstab> ReadFirstStageFstabMicrodroid(const std::string& cmdline) {
+ Fstab fstab;
+ if (!ReadDefaultFstab(&fstab)) {
+ return Error() << "failed to read fstab";
+ }
+ if (cmdline.find("androidboot.microdroid.mount_vendor=1") == std::string::npos) {
+ // We weren't asked to mount /vendor partition, filter it out from the fstab.
+ auto predicate = [](const auto& entry) { return entry.mount_point == "/vendor"; };
+ fstab.erase(std::remove_if(fstab.begin(), fstab.end(), predicate), fstab.end());
+ }
+ return fstab;
+}
+
static bool GetRootEntry(FstabEntry* root_entry) {
Fstab proc_mounts;
if (!ReadFstabFromFile("/proc/mounts", &proc_mounts)) {
@@ -218,14 +224,13 @@
return rollbacked;
}
-// Class Definitions
-// -----------------
-FirstStageMount::FirstStageMount(Fstab fstab) : need_dm_verity_(false), fstab_(std::move(fstab)) {
- super_partition_name_ = fs_mgr_get_super_partition_name();
-}
-
-Result<std::unique_ptr<FirstStageMount>> FirstStageMount::Create() {
- auto fstab = ReadFirstStageFstab();
+Result<std::unique_ptr<FirstStageMount>> FirstStageMount::Create(const std::string& cmdline) {
+ Result<Fstab> fstab;
+ if (IsMicrodroid()) {
+ fstab = ReadFirstStageFstabMicrodroid(cmdline);
+ } else {
+ fstab = ReadFirstStageFstabAndroid();
+ }
if (!fstab.ok()) {
return fstab.error();
}
@@ -233,7 +238,7 @@
return std::make_unique<FirstStageMountVBootV2>(std::move(*fstab));
}
-bool FirstStageMount::DoCreateDevices() {
+bool FirstStageMountVBootV2::DoCreateDevices() {
if (!InitDevices()) return false;
// Mount /metadata before creating logical partitions, since we need to
@@ -255,7 +260,7 @@
return true;
}
-bool FirstStageMount::DoFirstStageMount() {
+bool FirstStageMountVBootV2::DoFirstStageMount() {
if (!IsDmLinearEnabled() && fstab_.empty()) {
// Nothing to mount.
LOG(INFO) << "First stage mount skipped (missing/incompatible/empty fstab in device tree)";
@@ -267,7 +272,7 @@
return true;
}
-bool FirstStageMount::InitDevices() {
+bool FirstStageMountVBootV2::InitDevices() {
std::set<std::string> devices;
GetSuperDeviceName(&devices);
@@ -288,14 +293,14 @@
return true;
}
-bool FirstStageMount::IsDmLinearEnabled() {
+bool FirstStageMountVBootV2::IsDmLinearEnabled() {
for (const auto& entry : fstab_) {
if (entry.fs_mgr_flags.logical) return true;
}
return false;
}
-void FirstStageMount::GetSuperDeviceName(std::set<std::string>* devices) {
+void FirstStageMountVBootV2::GetSuperDeviceName(std::set<std::string>* devices) {
// Add any additional devices required for dm-linear mappings.
if (!IsDmLinearEnabled()) {
return;
@@ -307,7 +312,7 @@
// Creates devices with uevent->partition_name matching ones in the given set.
// Found partitions will then be removed from it for the subsequent member
// function to check which devices are NOT created.
-bool FirstStageMount::InitRequiredDevices(std::set<std::string> devices) {
+bool FirstStageMountVBootV2::InitRequiredDevices(std::set<std::string> devices) {
if (!block_dev_init_.InitDeviceMapper()) {
return false;
}
@@ -317,7 +322,8 @@
return block_dev_init_.InitDevices(std::move(devices));
}
-bool FirstStageMount::InitDmLinearBackingDevices(const android::fs_mgr::LpMetadata& metadata) {
+bool FirstStageMountVBootV2::InitDmLinearBackingDevices(
+ const android::fs_mgr::LpMetadata& metadata) {
std::set<std::string> devices;
auto partition_names = android::fs_mgr::GetBlockDevicePartitionNames(metadata);
@@ -334,7 +340,7 @@
return InitRequiredDevices(std::move(devices));
}
-bool FirstStageMount::CreateLogicalPartitions() {
+bool FirstStageMountVBootV2::CreateLogicalPartitions() {
if (!IsDmLinearEnabled()) {
return true;
}
@@ -365,7 +371,7 @@
return android::fs_mgr::CreateLogicalPartitions(*metadata.get(), super_path_);
}
-bool FirstStageMount::CreateSnapshotPartitions(SnapshotManager* sm) {
+bool FirstStageMountVBootV2::CreateSnapshotPartitions(SnapshotManager* sm) {
// When COW images are present for snapshots, they are stored on
// the data partition.
if (!InitRequiredDevices({"userdata"})) {
@@ -400,8 +406,8 @@
return true;
}
-bool FirstStageMount::MountPartition(const Fstab::iterator& begin, bool erase_same_mounts,
- Fstab::iterator* end) {
+bool FirstStageMountVBootV2::MountPartition(const Fstab::iterator& begin, bool erase_same_mounts,
+ Fstab::iterator* end) {
// Sets end to begin + 1, so we can just return on failure below.
if (end) {
*end = begin + 1;
@@ -445,7 +451,7 @@
return mounted;
}
-void FirstStageMount::PreloadAvbKeys() {
+void FirstStageMountVBootV2::PreloadAvbKeys() {
for (const auto& entry : fstab_) {
// No need to cache the key content if it's empty, or is already cached.
if (entry.avb_keys.empty() || preload_avb_key_blobs_.count(entry.avb_keys)) {
@@ -492,7 +498,7 @@
// If system is in the fstab then we're not a system-as-root device, and in
// this case, we mount system first then pivot to it. From that point on,
// we are effectively identical to a system-as-root device.
-bool FirstStageMount::TrySwitchSystemAsRoot() {
+bool FirstStageMountVBootV2::TrySwitchSystemAsRoot() {
UseDsuIfPresent();
// Preloading all AVB keys from the ramdisk before switching root to /system.
PreloadAvbKeys();
@@ -521,7 +527,7 @@
return true;
}
-bool FirstStageMount::MountPartitions() {
+bool FirstStageMountVBootV2::MountPartitions() {
if (!TrySwitchSystemAsRoot()) return false;
if (!SkipMountingPartitions(&fstab_, true /* verbose */)) return false;
@@ -604,7 +610,7 @@
// copy files to /metadata is NOT fatal, because it is auxiliary to perform
// public key matching before booting into DSU images on next boot. The actual
// public key matching will still be done on next boot to DSU.
-void FirstStageMount::CopyDsuAvbKeys() {
+void FirstStageMountVBootV2::CopyDsuAvbKeys() {
std::error_code ec;
// Removing existing keys in gsi::kDsuAvbKeyDir as they might be stale.
std::filesystem::remove_all(gsi::kDsuAvbKeyDir, ec);
@@ -620,7 +626,7 @@
}
}
-void FirstStageMount::UseDsuIfPresent() {
+void FirstStageMountVBootV2::UseDsuIfPresent() {
std::string error;
if (!android::gsi::CanBootIntoGsi(&error)) {
@@ -657,10 +663,10 @@
TransformFstabForDsu(&fstab_, active_dsu, dsu_partitions);
}
-// First retrieve any vbmeta partitions from device tree (legacy) then read through the fstab
-// for any further vbmeta partitions.
FirstStageMountVBootV2::FirstStageMountVBootV2(Fstab fstab)
- : FirstStageMount(std::move(fstab)), avb_handle_(nullptr) {
+ : need_dm_verity_(false), fstab_(std::move(fstab)), avb_handle_(nullptr) {
+ super_partition_name_ = fs_mgr_get_super_partition_name();
+
std::string device_tree_vbmeta_parts;
read_android_dt_file("vbmeta/parts", &device_tree_vbmeta_parts);
@@ -793,46 +799,13 @@
return true;
}
-// Public functions
-// ----------------
-// Creates devices and logical partitions from storage devices
-bool DoCreateDevices() {
- auto fsm = FirstStageMount::Create();
- if (!fsm.ok()) {
- LOG(ERROR) << "Failed to create FirstStageMount: " << fsm.error();
- return false;
- }
- return (*fsm)->DoCreateDevices();
-}
-
-// Mounts partitions specified by fstab in device tree.
-bool DoFirstStageMount(bool create_devices) {
- // Skips first stage mount if we're in recovery mode.
- if (IsRecoveryMode()) {
- LOG(INFO) << "First stage mount skipped (recovery mode)";
- return true;
- }
-
- auto fsm = FirstStageMount::Create();
- if (!fsm.ok()) {
- LOG(ERROR) << "Failed to create FirstStageMount " << fsm.error();
- return false;
- }
-
- if (create_devices) {
- if (!(*fsm)->DoCreateDevices()) return false;
- }
-
- return (*fsm)->DoFirstStageMount();
-}
-
void SetInitAvbVersionInRecovery() {
if (!IsRecoveryMode()) {
LOG(INFO) << "Skipped setting INIT_AVB_VERSION (not in recovery mode)";
return;
}
- auto fstab = ReadFirstStageFstab();
+ auto fstab = ReadFirstStageFstabAndroid();
if (!fstab.ok()) {
LOG(ERROR) << fstab.error();
return;
diff --git a/init/first_stage_mount.h b/init/first_stage_mount.h
index 2f4e663..54501d8 100644
--- a/init/first_stage_mount.h
+++ b/init/first_stage_mount.h
@@ -16,11 +16,28 @@
#pragma once
+#include <memory>
+
+#include "result.h"
+
namespace android {
namespace init {
-bool DoCreateDevices();
-bool DoFirstStageMount(bool create_devices);
+class FirstStageMount {
+ public:
+ virtual ~FirstStageMount() = default;
+
+ // The factory method to create a FirstStageMount instance.
+ static Result<std::unique_ptr<FirstStageMount>> Create(const std::string& cmdline);
+ // Creates devices and logical partitions from storage devices
+ virtual bool DoCreateDevices() = 0;
+ // Mounts fstab entries read from device tree.
+ virtual bool DoFirstStageMount() = 0;
+
+ protected:
+ FirstStageMount() = default;
+};
+
void SetInitAvbVersionInRecovery();
} // namespace init
diff --git a/init/fuzzer/Android.bp b/init/fuzzer/Android.bp
index c21a196..856ca8c 100644
--- a/init/fuzzer/Android.bp
+++ b/init/fuzzer/Android.bp
@@ -18,7 +18,7 @@
}
cc_defaults {
- name: "libinit_defaults",
+ name: "libinit_fuzzer_defaults",
static_libs: [
"libc++fs",
"liblmkd_utils",
@@ -53,7 +53,7 @@
],
shared_libs: ["libhidlmetadata",],
defaults: [
- "libinit_defaults",
+ "libinit_fuzzer_defaults",
],
}
@@ -62,7 +62,7 @@
srcs: [
"init_property_fuzzer.cpp",
],
- defaults: ["libinit_defaults"],
+ defaults: ["libinit_fuzzer_defaults"],
}
cc_fuzz {
@@ -71,6 +71,6 @@
"init_ueventHandler_fuzzer.cpp",
],
defaults: [
- "libinit_defaults",
+ "libinit_fuzzer_defaults",
],
}
diff --git a/init/init_test.cpp b/init/init_test.cpp
index 0fc3ffc..7e8513b 100644
--- a/init/init_test.cpp
+++ b/init/init_test.cpp
@@ -180,9 +180,11 @@
std::string init_script = R"init(
service A something
class first
+ user nobody
service A something
class second
+ user nobody
override
)init";
@@ -610,6 +612,31 @@
EXPECT_EQ(2, num_executed);
}
+TEST(init, RejectsNoUserStartingInV) {
+ std::string init_script =
+ R"init(
+service A something
+ class first
+)init";
+
+ TemporaryFile tf;
+ ASSERT_TRUE(tf.fd != -1);
+ ASSERT_TRUE(android::base::WriteStringToFd(init_script, tf.fd));
+
+ ServiceList service_list;
+ Parser parser;
+ parser.AddSectionParser("service",
+ std::make_unique<ServiceParser>(&service_list, nullptr, std::nullopt));
+
+ ASSERT_TRUE(parser.ParseConfig(tf.path));
+
+ if (GetIntProperty("ro.vendor.api_level", 0) > __ANDROID_API_U__) {
+ ASSERT_EQ(1u, parser.parse_error_count());
+ } else {
+ ASSERT_EQ(0u, parser.parse_error_count());
+ }
+}
+
TEST(init, RejectsCriticalAndOneshotService) {
if (GetIntProperty("ro.product.first_api_level", 10000) < 30) {
GTEST_SKIP() << "Test only valid for devices launching with R or later";
@@ -619,6 +646,7 @@
R"init(
service A something
class first
+ user root
critical
oneshot
)init";
diff --git a/init/mount_namespace.cpp b/init/mount_namespace.cpp
index fead371..5b53d50 100644
--- a/init/mount_namespace.cpp
+++ b/init/mount_namespace.cpp
@@ -21,7 +21,6 @@
#include <string>
#include <vector>
-#include <ApexProperties.sysprop.h>
#include <android-base/file.h>
#include <android-base/logging.h>
#include <android-base/properties.h>
@@ -30,16 +29,6 @@
#include "util.h"
-#ifndef RECOVERY
-#define ACTIVATE_FLATTENED_APEX 1
-#endif
-
-#ifdef ACTIVATE_FLATTENED_APEX
-#include <apex_manifest.pb.h>
-#include <com_android_apex.h>
-#include <selinux/android.h>
-#endif // ACTIVATE_FLATTENED_APEX
-
namespace android {
namespace init {
namespace {
@@ -77,15 +66,9 @@
return ret;
}
-static bool IsApexUpdatable() {
- static bool updatable = android::sysprop::ApexProperties::updatable().value_or(false);
- return updatable;
-}
-
// In case we have two sets of APEXes (non-updatable, updatable), we need two separate mount
// namespaces.
static bool NeedsTwoMountNamespaces() {
- if (!IsApexUpdatable()) return false;
if (IsRecoveryMode()) return false;
// In microdroid, there's only one set of APEXes in built-in directories include block devices.
if (IsMicrodroid()) return false;
@@ -193,7 +176,7 @@
// Switch the mount namespace of the current process from bootstrap to default OR from default to
// bootstrap. If the current mount namespace is neither bootstrap nor default, keep it that way.
Result<void> SwitchToMountNamespaceIfNeeded(MountNamespace target_mount_namespace) {
- if (IsRecoveryMode() || !IsApexUpdatable()) {
+ if (IsRecoveryMode()) {
// we don't have multiple namespaces in recovery mode or if apex is not updatable
return {};
}
diff --git a/init/reboot.cpp b/init/reboot.cpp
index 27a7876..3351c4c 100644
--- a/init/reboot.cpp
+++ b/init/reboot.cpp
@@ -680,8 +680,8 @@
<< "': " << result.error();
}
s->SetShutdownCritical();
- } else if (do_shutdown_animation) {
- continue;
+ } else if (do_shutdown_animation && s->classnames().count("animation") > 0) {
+ // Need these for shutdown animations.
} else if (s->IsShutdownCritical()) {
// Start shutdown critical service if not started.
if (auto result = s->Start(); !result.ok()) {
diff --git a/init/selinux.cpp b/init/selinux.cpp
index 907eb80..51093d8 100644
--- a/init/selinux.cpp
+++ b/init/selinux.cpp
@@ -74,10 +74,8 @@
#include <android-base/unique_fd.h>
#include <fs_avb/fs_avb.h>
#include <fs_mgr.h>
-#include <fsverity_init.h>
#include <libgsi/libgsi.h>
#include <libsnapshot/snapshot.h>
-#include <mini_keyctl_utils.h>
#include <selinux/android.h>
#include <ziparchive/zip_archive.h>
@@ -498,7 +496,7 @@
bool OpenMonolithicPolicy(PolicyFile* policy_file) {
static constexpr char kSepolicyFile[] = "/sepolicy";
- LOG(VERBOSE) << "Opening SELinux policy from monolithic file";
+ LOG(INFO) << "Opening SELinux policy from monolithic file " << kSepolicyFile;
policy_file->fd.reset(open(kSepolicyFile, O_RDONLY | O_CLOEXEC | O_NOFOLLOW));
if (policy_file->fd < 0) {
PLOG(ERROR) << "Failed to open monolithic SELinux policy";
@@ -510,7 +508,6 @@
constexpr const char* kSigningCertRelease =
"/system/etc/selinux/com.android.sepolicy.cert-release.der";
-constexpr const char* kFsVerityProcPath = "/proc/sys/fs/verity";
const std::string kSepolicyApexMetadataDir = "/metadata/sepolicy/";
const std::string kSepolicyApexSystemDir = "/system/etc/selinux/apex/";
const std::string kSepolicyZip = "SEPolicy.zip";
@@ -614,24 +611,6 @@
return {};
}
-Result<void> LoadSepolicyApexCerts() {
- key_serial_t keyring_id = android::GetKeyringId(".fs-verity");
- if (keyring_id < 0) {
- return Error() << "Failed to find .fs-verity keyring id";
- }
-
- // TODO(b/199914227) the release key should always exist. Once it's checked in, start
- // throwing an error here if it doesn't exist.
- if (access(kSigningCertRelease, F_OK) == 0) {
- LoadKeyFromFile(keyring_id, "fsv_sepolicy_apex_release", kSigningCertRelease);
- }
- return {};
-}
-
-Result<void> SepolicyFsVerityCheck() {
- return Error() << "TODO implement support for fsverity SEPolicy.";
-}
-
Result<void> SepolicyCheckSignature(const std::string& dir) {
std::string signature;
if (!android::base::ReadFileToString(dir + kSepolicySignature, &signature)) {
@@ -654,18 +633,7 @@
return verifySignature(sepolicyStr, signature, *releaseKey);
}
-Result<void> SepolicyVerify(const std::string& dir, bool supportsFsVerity) {
- if (supportsFsVerity) {
- auto fsVerityCheck = SepolicyFsVerityCheck();
- if (fsVerityCheck.ok()) {
- return fsVerityCheck;
- }
- // TODO(b/199914227) If the device supports fsverity, but we fail here, we should fail to
- // boot and not carry on. For now, fallback to a signature checkuntil the fsverity
- // logic is implemented.
- LOG(INFO) << "Falling back to standard signature check. " << fsVerityCheck.error();
- }
-
+Result<void> SepolicyVerify(const std::string& dir) {
auto sepolicySignature = SepolicyCheckSignature(dir);
if (!sepolicySignature.ok()) {
return Error() << "Apex SEPolicy failed signature check";
@@ -698,23 +666,19 @@
// 6. Sets selinux into enforcing mode and continues normal booting.
//
void PrepareApexSepolicy() {
- bool supportsFsVerity = access(kFsVerityProcPath, F_OK) == 0;
- if (supportsFsVerity) {
- auto loadSepolicyApexCerts = LoadSepolicyApexCerts();
- if (!loadSepolicyApexCerts.ok()) {
- // TODO(b/199914227) If the device supports fsverity, but we fail here, we should fail
- // to boot and not carry on. For now, fallback to a signature checkuntil the fsverity
- // logic is implemented.
- LOG(INFO) << loadSepolicyApexCerts.error();
- }
- }
// If apex sepolicy zip exists in /metadata/sepolicy, use that, otherwise use version on
- // /system.
- auto dir = (access((kSepolicyApexMetadataDir + kSepolicyZip).c_str(), F_OK) == 0)
- ? kSepolicyApexMetadataDir
- : kSepolicyApexSystemDir;
+ // /system. If neither exists, do nothing.
+ std::string dir;
+ if (access((kSepolicyApexMetadataDir + kSepolicyZip).c_str(), F_OK) == 0) {
+ dir = kSepolicyApexMetadataDir;
+ } else if (access((kSepolicyApexSystemDir + kSepolicyZip).c_str(), F_OK) == 0) {
+ dir = kSepolicyApexSystemDir;
+ } else {
+ LOG(INFO) << "APEX Sepolicy not found";
+ return;
+ }
- auto sepolicyVerify = SepolicyVerify(dir, supportsFsVerity);
+ auto sepolicyVerify = SepolicyVerify(dir);
if (!sepolicyVerify.ok()) {
LOG(INFO) << "Error: " << sepolicyVerify.error();
// If signature verification fails, fall back to version on /system.
@@ -851,6 +815,10 @@
}
int SelinuxGetVendorAndroidVersion() {
+ if (IsMicrodroid()) {
+ // As of now Microdroid doesn't have any vendor code.
+ return __ANDROID_API_FUTURE__;
+ }
static int vendor_android_version = [] {
if (!IsSplitPolicyDevice()) {
// If this device does not split sepolicy files, it's not a Treble device and therefore,
@@ -954,6 +922,26 @@
}
}
+// Encapsulates steps to load SELinux policy in Microdroid.
+// So far the process is very straightforward - just load the precompiled policy from /system.
+void LoadSelinuxPolicyMicrodroid() {
+ constexpr const char kMicrodroidPrecompiledSepolicy[] =
+ "/system/etc/selinux/microdroid_precompiled_sepolicy";
+
+ LOG(INFO) << "Opening SELinux policy from " << kMicrodroidPrecompiledSepolicy;
+ unique_fd policy_fd(open(kMicrodroidPrecompiledSepolicy, O_RDONLY | O_CLOEXEC | O_NOFOLLOW));
+ if (policy_fd < 0) {
+ PLOG(FATAL) << "Failed to open " << kMicrodroidPrecompiledSepolicy;
+ }
+
+ std::string policy;
+ if (!android::base::ReadFdToString(policy_fd, &policy)) {
+ PLOG(FATAL) << "Failed to read policy file: " << kMicrodroidPrecompiledSepolicy;
+ }
+
+ LoadSelinuxPolicy(policy);
+}
+
// The SELinux setup process is carefully orchestrated around snapuserd. Policy
// must be loaded off dynamic partitions, and during an OTA, those partitions
// cannot be read without snapuserd. But, with kernel-privileged snapuserd
@@ -969,20 +957,9 @@
// (5) Re-launch snapuserd and attach it to the dm-user devices from step (2).
//
// After this sequence, it is safe to enable enforcing mode and continue booting.
-int SetupSelinux(char** argv) {
- SetStdioToDevNull(argv);
- InitKernelLogging(argv);
-
- if (REBOOT_BOOTLOADER_ON_PANIC) {
- InstallRebootSignalHandlers();
- }
-
- boot_clock::time_point start_time = boot_clock::now();
-
+void LoadSelinuxPolicyAndroid() {
MountMissingSystemPartitions();
- SelinuxSetupKernelLogging();
-
LOG(INFO) << "Opening SELinux policy";
PrepareApexSepolicy();
@@ -994,9 +971,8 @@
auto snapuserd_helper = SnapuserdSelinuxHelper::CreateIfNeeded();
if (snapuserd_helper) {
- // Kill the old snapused to avoid audit messages. After this we cannot
- // read from /system (or other dynamic partitions) until we call
- // FinishTransition().
+ // Kill the old snapused to avoid audit messages. After this we cannot read from /system
+ // (or other dynamic partitions) until we call FinishTransition().
snapuserd_helper->StartTransition();
}
@@ -1014,6 +990,26 @@
if (selinux_android_restorecon("/dev/selinux/", SELINUX_ANDROID_RESTORECON_RECURSE) == -1) {
PLOG(FATAL) << "restorecon failed of /dev/selinux failed";
}
+}
+
+int SetupSelinux(char** argv) {
+ SetStdioToDevNull(argv);
+ InitKernelLogging(argv);
+
+ if (REBOOT_BOOTLOADER_ON_PANIC) {
+ InstallRebootSignalHandlers();
+ }
+
+ boot_clock::time_point start_time = boot_clock::now();
+
+ SelinuxSetupKernelLogging();
+
+ // TODO(b/287206497): refactor into different headers to only include what we need.
+ if (IsMicrodroid()) {
+ LoadSelinuxPolicyMicrodroid();
+ } else {
+ LoadSelinuxPolicyAndroid();
+ }
SelinuxSetEnforcement();
diff --git a/init/service.cpp b/init/service.cpp
index 35beaad..a0b3478 100644
--- a/init/service.cpp
+++ b/init/service.cpp
@@ -50,7 +50,6 @@
#endif
#ifdef INIT_FULL_SOURCES
-#include <ApexProperties.sysprop.h>
#include <android/api-level.h>
#include "mount_namespace.h"
@@ -140,9 +139,10 @@
Service::Service(const std::string& name, Subcontext* subcontext_for_restart_commands,
const std::string& filename, const std::vector<std::string>& args)
- : Service(name, 0, 0, 0, {}, 0, "", subcontext_for_restart_commands, filename, args) {}
+ : Service(name, 0, std::nullopt, 0, {}, 0, "", subcontext_for_restart_commands, filename,
+ args) {}
-Service::Service(const std::string& name, unsigned flags, uid_t uid, gid_t gid,
+Service::Service(const std::string& name, unsigned flags, std::optional<uid_t> uid, gid_t gid,
const std::vector<gid_t>& supp_gids, int namespace_flags,
const std::string& seclabel, Subcontext* subcontext_for_restart_commands,
const std::string& filename, const std::vector<std::string>& args)
@@ -153,7 +153,7 @@
crash_count_(0),
proc_attr_{.ioprio_class = IoSchedClass_NONE,
.ioprio_pri = 0,
- .uid = uid,
+ .parsed_uid = uid,
.gid = gid,
.supp_gids = supp_gids,
.priority = 0},
@@ -205,9 +205,9 @@
int max_processes = 0;
int r;
if (signal == SIGTERM) {
- r = killProcessGroupOnce(proc_attr_.uid, pid_, signal, &max_processes);
+ r = killProcessGroupOnce(uid(), pid_, signal, &max_processes);
} else {
- r = killProcessGroup(proc_attr_.uid, pid_, signal, &max_processes);
+ r = killProcessGroup(uid(), pid_, signal, &max_processes);
}
if (report_oneshot && max_processes > 0) {
@@ -228,7 +228,7 @@
void Service::SetProcessAttributesAndCaps(InterprocessFifo setsid_finished) {
// Keep capabilites on uid change.
- if (capabilities_ && proc_attr_.uid) {
+ if (capabilities_ && uid()) {
// If Android is running in a container, some securebits might already
// be locked, so don't change those.
unsigned long securebits = prctl(PR_GET_SECUREBITS);
@@ -255,7 +255,7 @@
if (!SetCapsForExec(*capabilities_)) {
LOG(FATAL) << "cannot set capabilities for " << name_;
}
- } else if (proc_attr_.uid) {
+ } else if (uid()) {
// Inheritable caps can be non-zero when running in a container.
if (!DropInheritableCaps()) {
LOG(FATAL) << "cannot drop inheritable caps for " << name_;
@@ -307,6 +307,7 @@
pid_ = 0;
flags_ &= (~SVC_RUNNING);
start_order_ = 0;
+ was_last_exit_ok_ = siginfo.si_code == CLD_EXITED && siginfo.si_status == 0;
// Oneshot processes go into the disabled state on exit,
// except when manually restarted.
@@ -321,7 +322,7 @@
}
#if INIT_FULL_SOURCES
- static bool is_apex_updatable = android::sysprop::ApexProperties::updatable().value_or(false);
+ static bool is_apex_updatable = true;
#else
static bool is_apex_updatable = false;
#endif
@@ -360,7 +361,8 @@
// If we crash > 4 times in 'fatal_crash_window_' minutes or before boot_completed,
// reboot into bootloader or set crashing property
boot_clock::time_point now = boot_clock::now();
- if (((flags_ & SVC_CRITICAL) || is_process_updatable) && !(flags_ & SVC_RESTART)) {
+ if (((flags_ & SVC_CRITICAL) || is_process_updatable) && !(flags_ & SVC_RESTART) &&
+ !was_last_exit_ok_) {
bool boot_completed = GetBoolProperty("sys.boot_completed", false);
if (now < time_crashed_ + fatal_crash_window_ || !boot_completed) {
if (++crash_count_ > 4) {
@@ -434,8 +436,8 @@
flags_ |= SVC_EXEC;
is_exec_service_running_ = true;
- LOG(INFO) << "SVC_EXEC service '" << name_ << "' pid " << pid_ << " (uid " << proc_attr_.uid
- << " gid " << proc_attr_.gid << "+" << proc_attr_.supp_gids.size() << " context "
+ LOG(INFO) << "SVC_EXEC service '" << name_ << "' pid " << pid_ << " (uid " << uid() << " gid "
+ << proc_attr_.gid << "+" << proc_attr_.supp_gids.size() << " context "
<< (!seclabel_.empty() ? seclabel_ : "default") << ") started; waiting...";
reboot_on_failure.Disable();
@@ -475,13 +477,13 @@
// Configures the memory cgroup properties for the service.
void Service::ConfigureMemcg() {
if (swappiness_ != -1) {
- if (!setProcessGroupSwappiness(proc_attr_.uid, pid_, swappiness_)) {
+ if (!setProcessGroupSwappiness(uid(), pid_, swappiness_)) {
PLOG(ERROR) << "setProcessGroupSwappiness failed";
}
}
if (soft_limit_in_bytes_ != -1) {
- if (!setProcessGroupSoftLimit(proc_attr_.uid, pid_, soft_limit_in_bytes_)) {
+ if (!setProcessGroupSoftLimit(uid(), pid_, soft_limit_in_bytes_)) {
PLOG(ERROR) << "setProcessGroupSoftLimit failed";
}
}
@@ -508,7 +510,7 @@
}
if (computed_limit_in_bytes != size_t(-1)) {
- if (!setProcessGroupLimit(proc_attr_.uid, pid_, computed_limit_in_bytes)) {
+ if (!setProcessGroupLimit(uid(), pid_, computed_limit_in_bytes)) {
PLOG(ERROR) << "setProcessGroupLimit failed";
}
}
@@ -705,21 +707,20 @@
if (CgroupsAvailable()) {
bool use_memcg = swappiness_ != -1 || soft_limit_in_bytes_ != -1 || limit_in_bytes_ != -1 ||
limit_percent_ != -1 || !limit_property_.empty();
- errno = -createProcessGroup(proc_attr_.uid, pid_, use_memcg);
+ errno = -createProcessGroup(uid(), pid_, use_memcg);
if (errno != 0) {
Result<void> result = cgroups_activated.Write(kActivatingCgroupsFailed);
if (!result.ok()) {
return Error() << "Sending notification failed: " << result.error();
}
- return Error() << "createProcessGroup(" << proc_attr_.uid << ", " << pid_ << ", "
- << use_memcg << ") failed for service '" << name_
- << "': " << strerror(errno);
+ return Error() << "createProcessGroup(" << uid() << ", " << pid_ << ", " << use_memcg
+ << ") failed for service '" << name_ << "': " << strerror(errno);
}
// When the blkio controller is mounted in the v1 hierarchy, NormalIoPriority is
// the default (/dev/blkio). When the blkio controller is mounted in the v2 hierarchy, the
// NormalIoPriority profile has to be applied explicitly.
- SetProcessProfiles(proc_attr_.uid, pid_, {"NormalIoPriority"});
+ SetProcessProfiles(uid(), pid_, {"NormalIoPriority"});
if (use_memcg) {
ConfigureMemcg();
@@ -727,7 +728,7 @@
}
if (oom_score_adjust_ != DEFAULT_OOM_SCORE_ADJUST) {
- LmkdRegister(name_, proc_attr_.uid, pid_, oom_score_adjust_);
+ LmkdRegister(name_, uid(), pid_, oom_score_adjust_);
}
if (Result<void> result = cgroups_activated.Write(kCgroupsActivated); !result.ok()) {
diff --git a/init/service.h b/init/service.h
index 3ef8902..b858eef 100644
--- a/init/service.h
+++ b/init/service.h
@@ -19,6 +19,7 @@
#include <signal.h>
#include <sys/types.h>
+#include <algorithm>
#include <chrono>
#include <memory>
#include <optional>
@@ -71,7 +72,7 @@
Service(const std::string& name, Subcontext* subcontext_for_restart_commands,
const std::string& filename, const std::vector<std::string>& args);
- Service(const std::string& name, unsigned flags, uid_t uid, gid_t gid,
+ Service(const std::string& name, unsigned flags, std::optional<uid_t> uid, gid_t gid,
const std::vector<gid_t>& supp_gids, int namespace_flags, const std::string& seclabel,
Subcontext* subcontext_for_restart_commands, const std::string& filename,
const std::vector<std::string>& args);
@@ -115,7 +116,8 @@
pid_t pid() const { return pid_; }
android::base::boot_clock::time_point time_started() const { return time_started_; }
int crash_count() const { return crash_count_; }
- uid_t uid() const { return proc_attr_.uid; }
+ int was_last_exit_ok() const { return was_last_exit_ok_; }
+ uid_t uid() const { return proc_attr_.uid(); }
gid_t gid() const { return proc_attr_.gid; }
int namespace_flags() const { return namespaces_.flags; }
const std::vector<gid_t>& supp_gids() const { return proc_attr_.supp_gids; }
@@ -130,7 +132,15 @@
bool process_cgroup_empty() const { return process_cgroup_empty_; }
unsigned long start_order() const { return start_order_; }
void set_sigstop(bool value) { sigstop_ = value; }
- std::chrono::seconds restart_period() const { return restart_period_; }
+ std::chrono::seconds restart_period() const {
+ // If the service exited abnormally or due to timeout, late limit the restart even if
+ // restart_period is set to a very short value.
+ // If not, i.e. restart after a deliberate and successful exit, respect the period.
+ if (!was_last_exit_ok_) {
+ return std::max(restart_period_, default_restart_period_);
+ }
+ return restart_period_;
+ }
std::optional<std::chrono::seconds> timeout_period() const { return timeout_period_; }
const std::vector<std::string>& args() const { return args_; }
bool is_updatable() const { return updatable_; }
@@ -172,6 +182,8 @@
bool upgraded_mte_ = false; // whether we upgraded async MTE -> sync MTE before
std::chrono::minutes fatal_crash_window_ = 4min; // fatal() when more than 4 crashes in it
std::optional<std::string> fatal_reboot_target_; // reboot target of fatal handler
+ bool was_last_exit_ok_ =
+ true; // true if the service never exited, or exited with status code 0
std::optional<CapSet> capabilities_;
ProcessAttributes proc_attr_;
@@ -214,7 +226,8 @@
bool sigstop_ = false;
- std::chrono::seconds restart_period_ = 5s;
+ const std::chrono::seconds default_restart_period_ = 5s;
+ std::chrono::seconds restart_period_ = default_restart_period_;
std::optional<std::chrono::seconds> timeout_period_;
bool updatable_ = false;
diff --git a/init/service_parser.cpp b/init/service_parser.cpp
index 3563084..a1b2cc5 100644
--- a/init/service_parser.cpp
+++ b/init/service_parser.cpp
@@ -25,6 +25,7 @@
#include <android-base/logging.h>
#include <android-base/parseint.h>
+#include <android-base/properties.h>
#include <android-base/strings.h>
#include <hidl-util/FQName.h>
#include <processgroup/processgroup.h>
@@ -369,8 +370,8 @@
Result<void> ServiceParser::ParseRestartPeriod(std::vector<std::string>&& args) {
int period;
- if (!ParseInt(args[1], &period, 5)) {
- return Error() << "restart_period value must be an integer >= 5";
+ if (!ParseInt(args[1], &period, 0)) {
+ return Error() << "restart_period value must be an integer >= 0";
}
service_->restart_period_ = std::chrono::seconds(period);
return {};
@@ -534,7 +535,7 @@
if (!uid.ok()) {
return Error() << "Unable to find UID for '" << args[1] << "': " << uid.error();
}
- service_->proc_attr_.uid = *uid;
+ service_->proc_attr_.parsed_uid = *uid;
return {};
}
@@ -677,6 +678,16 @@
return {};
}
+ if (service_->proc_attr_.parsed_uid == std::nullopt) {
+ if (android::base::GetIntProperty("ro.vendor.api_level", 0) > __ANDROID_API_U__) {
+ return Error() << "No user specified for service '" << service_->name()
+ << "'. Defaults to root.";
+ } else {
+ LOG(WARNING) << "No user specified for service '" << service_->name()
+ << "'. Defaults to root.";
+ }
+ }
+
if (interface_inheritance_hierarchy_) {
if (const auto& check_hierarchy_result = CheckInterfaceInheritanceHierarchy(
service_->interfaces(), *interface_inheritance_hierarchy_);
diff --git a/init/service_utils.cpp b/init/service_utils.cpp
index 7004d8d..0e19bcc 100644
--- a/init/service_utils.cpp
+++ b/init/service_utils.cpp
@@ -271,8 +271,8 @@
if (setgroups(attr.supp_gids.size(), const_cast<gid_t*>(&attr.supp_gids[0])) != 0) {
return ErrnoError() << "setgroups failed";
}
- if (attr.uid) {
- if (setuid(attr.uid) != 0) {
+ if (attr.uid()) {
+ if (setuid(attr.uid()) != 0) {
return ErrnoError() << "setuid failed";
}
}
diff --git a/init/service_utils.h b/init/service_utils.h
index d4143aa..27ec450 100644
--- a/init/service_utils.h
+++ b/init/service_utils.h
@@ -91,11 +91,13 @@
IoSchedClass ioprio_class;
int ioprio_pri;
std::vector<std::pair<int, rlimit>> rlimits;
- uid_t uid;
+ std::optional<uid_t> parsed_uid;
gid_t gid;
std::vector<gid_t> supp_gids;
int priority;
bool stdio_to_kmsg;
+
+ uid_t uid() const { return parsed_uid.value_or(0); }
};
inline bool RequiresConsole(const ProcessAttributes& attr) {
diff --git a/init/test_kill_services/init_kill_services_test.cpp b/init/test_kill_services/init_kill_services_test.cpp
index 5355703..510ad8a 100644
--- a/init/test_kill_services/init_kill_services_test.cpp
+++ b/init/test_kill_services/init_kill_services_test.cpp
@@ -16,14 +16,26 @@
#include <gtest/gtest.h>
+#include <android-base/logging.h>
#include <android-base/properties.h>
#include <iostream>
using ::android::base::GetProperty;
using ::android::base::SetProperty;
+using ::android::base::WaitForProperty;
+using std::literals::chrono_literals::operator""s;
void ExpectKillingServiceRecovers(const std::string& service_name) {
+ LOG(INFO) << "before we say hi to " << service_name << ", I can't have apexd around!";
+
+ // b/280514080 - servicemanager will restart apexd, and apexd will restart the
+ // system when crashed. This is fine as the device recovers, but it causes
+ // flakes in this test.
+ ASSERT_TRUE(WaitForProperty("init.svc.apexd", "stopped", 120s))
+ << (system("cat /dev/binderfs/binder_logs/state"), "apexd won't stop");
+
+ LOG(INFO) << "hello " << service_name << "!";
const std::string status_prop = "init.svc." + service_name;
const std::string pid_prop = "init.svc_debug_pid." + service_name;
@@ -32,6 +44,7 @@
ASSERT_EQ("running", GetProperty(status_prop, "")) << status_prop;
ASSERT_NE("", initial_pid) << pid_prop;
+ LOG(INFO) << "okay, now goodbye " << service_name;
EXPECT_EQ(0, system(("kill -9 " + initial_pid).c_str()));
constexpr size_t kMaxWaitMilliseconds = 10000;
@@ -42,11 +55,16 @@
for (size_t retry = 0; retry < kRetryTimes; retry++) {
const std::string& pid = GetProperty(pid_prop, "");
if (pid != initial_pid && pid != "") break;
+ LOG(INFO) << "I said goodbye " << service_name << "!";
usleep(kRetryWaitMilliseconds * 1000);
}
+ LOG(INFO) << "are you still there " << service_name << "?";
+
// svc_debug_pid is set after svc property
EXPECT_EQ("running", GetProperty(status_prop, ""));
+
+ LOG(INFO) << "I'm done with " << service_name;
}
class InitKillServicesTest : public ::testing::TestWithParam<std::string> {};
diff --git a/init/util.cpp b/init/util.cpp
index bc8ea6e..d0478e8 100644
--- a/init/util.cpp
+++ b/init/util.cpp
@@ -732,11 +732,6 @@
is_default_mount_namespace_ready = true;
}
-bool IsMicrodroid() {
- static bool is_microdroid = android::base::GetProperty("ro.hardware", "") == "microdroid";
- return is_microdroid;
-}
-
bool Has32BitAbi() {
static bool has = !android::base::GetProperty("ro.product.cpu.abilist32", "").empty();
return has;
diff --git a/init/util.h b/init/util.h
index e58e70e..3f0a4e0 100644
--- a/init/util.h
+++ b/init/util.h
@@ -105,7 +105,14 @@
bool IsDefaultMountNamespaceReady();
void SetDefaultMountNamespaceReady();
-bool IsMicrodroid();
+inline constexpr bool IsMicrodroid() {
+#ifdef MICRODROID
+ return MICRODROID;
+#else
+ return false;
+#endif
+}
+
bool Has32BitAbi();
std::string GetApexNameFromFileName(const std::string& path);
diff --git a/libbinderwrapper/Android.bp b/libbinderwrapper/Android.bp
deleted file mode 100644
index 75f43ee..0000000
--- a/libbinderwrapper/Android.bp
+++ /dev/null
@@ -1,66 +0,0 @@
-//
-// Copyright (C) 2015 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: ["Android-Apache-2.0"],
-}
-
-cc_defaults {
- name: "libbinderwrapper_defaults",
-
- cflags: [
- "-Wall",
- "-Werror",
- "-Wno-unused-parameter",
-
- // for libchrome
- "-Wno-sign-promo",
- ],
- export_include_dirs: ["include"],
- shared_libs: [
- "libbinder",
- "libchrome",
- "libutils",
- ],
-}
-
-// libbinderwrapper shared library
-// ========================================================
-cc_library_shared {
- name: "libbinderwrapper",
- defaults: ["libbinderwrapper_defaults"],
- vendor_available: true,
-
- srcs: [
- "binder_wrapper.cc",
- "real_binder_wrapper.cc",
- ],
-}
-
-// libbinderwrapper_test_support static library
-// ========================================================
-cc_library_static {
- name: "libbinderwrapper_test_support",
- defaults: ["libbinderwrapper_defaults"],
-
- static_libs: ["libgtest"],
- shared_libs: ["libbinderwrapper"],
-
- srcs: [
- "binder_test_base.cc",
- "stub_binder_wrapper.cc",
- ],
-}
diff --git a/libbinderwrapper/binder_test_base.cc b/libbinderwrapper/binder_test_base.cc
deleted file mode 100644
index af93a04..0000000
--- a/libbinderwrapper/binder_test_base.cc
+++ /dev/null
@@ -1,33 +0,0 @@
-/*
- * Copyright (C) 2015 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 <binderwrapper/binder_test_base.h>
-
-#include <binderwrapper/binder_wrapper.h>
-#include <binderwrapper/stub_binder_wrapper.h>
-
-namespace android {
-
-BinderTestBase::BinderTestBase() : binder_wrapper_(new StubBinderWrapper()) {
- // Pass ownership.
- BinderWrapper::InitForTesting(binder_wrapper_);
-}
-
-BinderTestBase::~BinderTestBase() {
- BinderWrapper::Destroy();
-}
-
-} // namespace android
diff --git a/libbinderwrapper/binder_wrapper.cc b/libbinderwrapper/binder_wrapper.cc
deleted file mode 100644
index ca9c1df..0000000
--- a/libbinderwrapper/binder_wrapper.cc
+++ /dev/null
@@ -1,60 +0,0 @@
-/*
- * Copyright (C) 2015 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 <binderwrapper/binder_wrapper.h>
-
-#include <base/logging.h>
-
-#include "real_binder_wrapper.h"
-
-namespace android {
-
-// Singleton instance.
-BinderWrapper* BinderWrapper::instance_ = nullptr;
-
-// static
-void BinderWrapper::Create() {
- CHECK(!instance_) << "Already initialized; missing call to Destroy()?";
- instance_ = new RealBinderWrapper();
-}
-
-// static
-void BinderWrapper::InitForTesting(BinderWrapper* wrapper) {
- CHECK(!instance_) << "Already initialized; missing call to Destroy()?";
- instance_ = wrapper;
-}
-
-// static
-void BinderWrapper::Destroy() {
- CHECK(instance_) << "Not initialized; missing call to Create()?";
- delete instance_;
- instance_ = nullptr;
-}
-
-// static
-BinderWrapper* BinderWrapper::Get() {
- CHECK(instance_) << "Not initialized; missing call to Create()?";
- return instance_;
-}
-
-// static
-BinderWrapper* BinderWrapper::GetOrCreateInstance() {
- if (!instance_)
- instance_ = new RealBinderWrapper();
- return instance_;
-}
-
-} // namespace android
diff --git a/libbinderwrapper/include/binderwrapper/binder_test_base.h b/libbinderwrapper/include/binderwrapper/binder_test_base.h
deleted file mode 100644
index 06543de..0000000
--- a/libbinderwrapper/include/binderwrapper/binder_test_base.h
+++ /dev/null
@@ -1,45 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef SYSTEM_CORE_INCLUDE_BINDERWRAPPER_BINDER_TEST_BASE_H_
-#define SYSTEM_CORE_INCLUDE_BINDERWRAPPER_BINDER_TEST_BASE_H_
-
-#include <base/macros.h>
-#include <gtest/gtest.h>
-
-namespace android {
-
-class StubBinderWrapper;
-
-// Class that can be inherited from (or aliased via typedef/using) when writing
-// tests that use StubBinderManager.
-class BinderTestBase : public ::testing::Test {
- public:
- BinderTestBase();
- ~BinderTestBase() override;
-
- StubBinderWrapper* binder_wrapper() { return binder_wrapper_; }
-
- protected:
- StubBinderWrapper* binder_wrapper_; // Not owned.
-
- private:
- DISALLOW_COPY_AND_ASSIGN(BinderTestBase);
-};
-
-} // namespace android
-
-#endif // SYSTEM_CORE_INCLUDE_BINDERWRAPPER_BINDER_TEST_BASE_H_
diff --git a/libbinderwrapper/include/binderwrapper/binder_wrapper.h b/libbinderwrapper/include/binderwrapper/binder_wrapper.h
deleted file mode 100644
index a104bff..0000000
--- a/libbinderwrapper/include/binderwrapper/binder_wrapper.h
+++ /dev/null
@@ -1,88 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef SYSTEM_CORE_INCLUDE_BINDERWRAPPER_BINDER_WRAPPER_H_
-#define SYSTEM_CORE_INCLUDE_BINDERWRAPPER_BINDER_WRAPPER_H_
-
-#include <sys/types.h>
-
-#include <string>
-
-#include <base/callback.h>
-#include <utils/StrongPointer.h>
-
-namespace android {
-
-class BBinder;
-class IBinder;
-
-// Wraps libbinder to make it testable.
-// NOTE: Static methods of this class are not thread-safe.
-class BinderWrapper {
- public:
- virtual ~BinderWrapper() {}
-
- // Creates and initializes the singleton (using a wrapper that communicates
- // with the real binder system).
- static void Create();
-
- // Initializes |wrapper| as the singleton, taking ownership of it. Tests that
- // want to inject their own wrappers should call this instead of Create().
- static void InitForTesting(BinderWrapper* wrapper);
-
- // Destroys the singleton. Must be called before calling Create() or
- // InitForTesting() a second time.
- static void Destroy();
-
- // Returns the singleton instance previously created by Create() or set by
- // InitForTesting().
- static BinderWrapper* Get();
-
- // Returns the singleton instance if it was previously created by Create() or
- // set by InitForTesting(), or creates a new one by calling Create().
- static BinderWrapper* GetOrCreateInstance();
-
- // Gets the binder for communicating with the service identified by
- // |service_name|, returning null immediately if it doesn't exist.
- virtual sp<IBinder> GetService(const std::string& service_name) = 0;
-
- // Registers |binder| as |service_name| with the service manager.
- virtual bool RegisterService(const std::string& service_name,
- const sp<IBinder>& binder) = 0;
-
- // Creates a local binder object.
- virtual sp<BBinder> CreateLocalBinder() = 0;
-
- // Registers |callback| to be invoked when |binder| dies. If another callback
- // is currently registered for |binder|, it will be replaced.
- virtual bool RegisterForDeathNotifications(
- const sp<IBinder>& binder,
- const ::base::Closure& callback) = 0;
-
- // Unregisters the callback, if any, for |binder|.
- virtual bool UnregisterForDeathNotifications(const sp<IBinder>& binder) = 0;
-
- // When called while in a transaction, returns the caller's UID or PID.
- virtual uid_t GetCallingUid() = 0;
- virtual pid_t GetCallingPid() = 0;
-
- private:
- static BinderWrapper* instance_;
-};
-
-} // namespace android
-
-#endif // SYSTEM_CORE_INCLUDE_BINDERWRAPPER_BINDER_WRAPPER_H_
diff --git a/libbinderwrapper/include/binderwrapper/stub_binder_wrapper.h b/libbinderwrapper/include/binderwrapper/stub_binder_wrapper.h
deleted file mode 100644
index 9d4578e..0000000
--- a/libbinderwrapper/include/binderwrapper/stub_binder_wrapper.h
+++ /dev/null
@@ -1,133 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef SYSTEM_CORE_INCLUDE_BINDERWRAPPER_STUB_BINDER_WRAPPER_H_
-#define SYSTEM_CORE_INCLUDE_BINDERWRAPPER_STUB_BINDER_WRAPPER_H_
-
-#include <map>
-#include <string>
-#include <vector>
-
-#include <base/macros.h>
-#include <binder/Binder.h>
-#include <binder/IBinder.h>
-#include <binderwrapper/binder_wrapper.h>
-
-namespace android {
-
-// Stub implementation of BinderWrapper for testing.
-//
-// Example usage:
-//
-// First, assuming a base IFoo binder interface, create a stub class that
-// derives from BnFoo to implement the receiver side of the communication:
-//
-// class StubFoo : public BnFoo {
-// public:
-// ...
-// status_t doSomething(int arg) override {
-// // e.g. save passed-in value for later inspection by tests.
-// return OK;
-// }
-// };
-//
-// Next, from your test code, inject a StubBinderManager either directly or by
-// inheriting from the BinderTestBase class:
-//
-// StubBinderWrapper* wrapper = new StubBinderWrapper();
-// BinderWrapper::InitForTesting(wrapper); // Takes ownership.
-//
-// Also from your test, create a StubFoo and register it with the wrapper:
-//
-// StubFoo* foo = new StubFoo();
-// sp<IBinder> binder(foo);
-// wrapper->SetBinderForService("foo", binder);
-//
-// The code being tested can now use the wrapper to get the stub and call it:
-//
-// sp<IBinder> binder = BinderWrapper::Get()->GetService("foo");
-// CHECK(binder.get());
-// sp<IFoo> foo = interface_cast<IFoo>(binder);
-// CHECK_EQ(foo->doSomething(3), OK);
-//
-// To create a local BBinder object, production code can call
-// CreateLocalBinder(). Then, a test can get the BBinder's address via
-// local_binders() to check that they're passed as expected in binder calls.
-//
-class StubBinderWrapper : public BinderWrapper {
- public:
- StubBinderWrapper();
- ~StubBinderWrapper() override;
-
- const std::vector<sp<BBinder>>& local_binders() const {
- return local_binders_;
- }
- void clear_local_binders() { local_binders_.clear(); }
-
- void set_calling_uid(uid_t uid) { calling_uid_ = uid; }
- void set_calling_pid(pid_t pid) { calling_pid_ = pid; }
-
- // Sets the binder to return when |service_name| is passed to GetService() or
- // WaitForService().
- void SetBinderForService(const std::string& service_name,
- const sp<IBinder>& binder);
-
- // Returns the binder previously registered for |service_name| via
- // RegisterService(), or null if the service hasn't been registered.
- sp<IBinder> GetRegisteredService(const std::string& service_name) const;
-
- // Run the calback in |death_callbacks_| corresponding to |binder|.
- void NotifyAboutBinderDeath(const sp<IBinder>& binder);
-
- // BinderWrapper:
- sp<IBinder> GetService(const std::string& service_name) override;
- bool RegisterService(const std::string& service_name,
- const sp<IBinder>& binder) override;
- sp<BBinder> CreateLocalBinder() override;
- bool RegisterForDeathNotifications(const sp<IBinder>& binder,
- const ::base::Closure& callback) override;
- bool UnregisterForDeathNotifications(const sp<IBinder>& binder) override;
- uid_t GetCallingUid() override;
- pid_t GetCallingPid() override;
-
- private:
- using ServiceMap = std::map<std::string, sp<IBinder>>;
-
- // Map from service name to associated binder handle. Used by GetService() and
- // WaitForService().
- ServiceMap services_to_return_;
-
- // Map from service name to associated binder handle. Updated by
- // RegisterService().
- ServiceMap registered_services_;
-
- // Local binders returned by CreateLocalBinder().
- std::vector<sp<BBinder>> local_binders_;
-
- // Map from binder handle to the callback that should be invoked on binder
- // death.
- std::map<sp<IBinder>, ::base::Closure> death_callbacks_;
-
- // Values to return from GetCallingUid() and GetCallingPid();
- uid_t calling_uid_;
- pid_t calling_pid_;
-
- DISALLOW_COPY_AND_ASSIGN(StubBinderWrapper);
-};
-
-} // namespace android
-
-#endif // SYSTEM_CORE_INCLUDE_BINDERWRAPPER_STUB_BINDER_WRAPPER_H_
diff --git a/libbinderwrapper/real_binder_wrapper.cc b/libbinderwrapper/real_binder_wrapper.cc
deleted file mode 100644
index f93f183..0000000
--- a/libbinderwrapper/real_binder_wrapper.cc
+++ /dev/null
@@ -1,123 +0,0 @@
-/*
- * Copyright (C) 2015 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 "real_binder_wrapper.h"
-
-#include <base/logging.h>
-#include <binder/Binder.h>
-#include <binder/IBinder.h>
-#include <binder/IPCThreadState.h>
-#include <binder/IServiceManager.h>
-
-namespace android {
-
-// Class that handles binder death notifications. libbinder wants the recipient
-// to be wrapped in sp<>, so registering RealBinderWrapper as a recipient would
-// be awkward.
-class RealBinderWrapper::DeathRecipient : public IBinder::DeathRecipient {
- public:
- explicit DeathRecipient(const ::base::Closure& callback)
- : callback_(callback) {}
- ~DeathRecipient() = default;
-
- // IBinder::DeathRecipient:
- void binderDied(const wp<IBinder>& who) override {
- callback_.Run();
- }
-
- private:
- // Callback to run in response to binder death.
- ::base::Closure callback_;
-
- DISALLOW_COPY_AND_ASSIGN(DeathRecipient);
-};
-
-RealBinderWrapper::RealBinderWrapper() = default;
-
-RealBinderWrapper::~RealBinderWrapper() = default;
-
-sp<IBinder> RealBinderWrapper::GetService(const std::string& service_name) {
- sp<IServiceManager> service_manager = defaultServiceManager();
- if (!service_manager.get()) {
- LOG(ERROR) << "Unable to get service manager";
- return sp<IBinder>();
- }
- sp<IBinder> binder =
- service_manager->checkService(String16(service_name.c_str()));
- if (!binder.get())
- LOG(ERROR) << "Unable to get \"" << service_name << "\" service";
- return binder;
-}
-
-bool RealBinderWrapper::RegisterService(const std::string& service_name,
- const sp<IBinder>& binder) {
- sp<IServiceManager> service_manager = defaultServiceManager();
- if (!service_manager.get()) {
- LOG(ERROR) << "Unable to get service manager";
- return false;
- }
- status_t status = defaultServiceManager()->addService(
- String16(service_name.c_str()), binder);
- if (status != OK) {
- LOG(ERROR) << "Failed to register \"" << service_name << "\" with service "
- << "manager";
- return false;
- }
- return true;
-}
-
-sp<BBinder> RealBinderWrapper::CreateLocalBinder() {
- return sp<BBinder>(new BBinder());
-}
-
-bool RealBinderWrapper::RegisterForDeathNotifications(
- const sp<IBinder>& binder,
- const ::base::Closure& callback) {
- sp<DeathRecipient> recipient(new DeathRecipient(callback));
- if (binder->linkToDeath(recipient) != OK) {
- LOG(ERROR) << "Failed to register for death notifications on "
- << binder.get();
- return false;
- }
- death_recipients_[binder] = recipient;
- return true;
-}
-
-bool RealBinderWrapper::UnregisterForDeathNotifications(
- const sp<IBinder>& binder) {
- auto it = death_recipients_.find(binder);
- if (it == death_recipients_.end()) {
- LOG(ERROR) << "Not registered for death notifications on " << binder.get();
- return false;
- }
- if (binder->unlinkToDeath(it->second) != OK) {
- LOG(ERROR) << "Failed to unregister for death notifications on "
- << binder.get();
- return false;
- }
- death_recipients_.erase(it);
- return true;
-}
-
-uid_t RealBinderWrapper::GetCallingUid() {
- return IPCThreadState::self()->getCallingUid();
-}
-
-pid_t RealBinderWrapper::GetCallingPid() {
- return IPCThreadState::self()->getCallingPid();
-}
-
-} // namespace android
diff --git a/libbinderwrapper/real_binder_wrapper.h b/libbinderwrapper/real_binder_wrapper.h
deleted file mode 100644
index fa05383..0000000
--- a/libbinderwrapper/real_binder_wrapper.h
+++ /dev/null
@@ -1,58 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef SYSTEM_CORE_LIBBINDERWRAPPER_REAL_BINDER_WRAPPER_H_
-#define SYSTEM_CORE_LIBBINDERWRAPPER_REAL_BINDER_WRAPPER_H_
-
-#include <map>
-
-#include <base/macros.h>
-#include <binderwrapper/binder_wrapper.h>
-
-namespace android {
-
-class IBinder;
-
-// Real implementation of BinderWrapper.
-class RealBinderWrapper : public BinderWrapper {
- public:
- RealBinderWrapper();
- ~RealBinderWrapper() override;
-
- // BinderWrapper:
- sp<IBinder> GetService(const std::string& service_name) override;
- bool RegisterService(const std::string& service_name,
- const sp<IBinder>& binder) override;
- sp<BBinder> CreateLocalBinder() override;
- bool RegisterForDeathNotifications(const sp<IBinder>& binder,
- const ::base::Closure& callback) override;
- bool UnregisterForDeathNotifications(const sp<IBinder>& binder) override;
- uid_t GetCallingUid() override;
- pid_t GetCallingPid() override;
-
- private:
- class DeathRecipient;
-
- // Map from binder handle to object that should be notified of the binder's
- // death.
- std::map<sp<IBinder>, sp<DeathRecipient>> death_recipients_;
-
- DISALLOW_COPY_AND_ASSIGN(RealBinderWrapper);
-};
-
-} // namespace android
-
-#endif // SYSTEM_CORE_LIBBINDER_WRAPPER_REAL_BINDER_WRAPPER_H_
diff --git a/libbinderwrapper/stub_binder_wrapper.cc b/libbinderwrapper/stub_binder_wrapper.cc
deleted file mode 100644
index 8e75f62..0000000
--- a/libbinderwrapper/stub_binder_wrapper.cc
+++ /dev/null
@@ -1,86 +0,0 @@
-/*
- * Copyright (C) 2015 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 <binderwrapper/stub_binder_wrapper.h>
-
-#include <base/logging.h>
-#include <binder/Binder.h>
-#include <binder/IBinder.h>
-
-namespace android {
-
-StubBinderWrapper::StubBinderWrapper()
- : calling_uid_(-1),
- calling_pid_(-1) {}
-
-StubBinderWrapper::~StubBinderWrapper() = default;
-
-void StubBinderWrapper::SetBinderForService(const std::string& service_name,
- const sp<IBinder>& binder) {
- services_to_return_[service_name] = binder;
-}
-
-sp<IBinder> StubBinderWrapper::GetRegisteredService(
- const std::string& service_name) const {
- const auto it = registered_services_.find(service_name);
- return it != registered_services_.end() ? it->second : sp<IBinder>();
-}
-
-void StubBinderWrapper::NotifyAboutBinderDeath(const sp<IBinder>& binder) {
- const auto it = death_callbacks_.find(binder);
- if (it != death_callbacks_.end())
- it->second.Run();
-}
-
-sp<IBinder> StubBinderWrapper::GetService(const std::string& service_name) {
- const auto it = services_to_return_.find(service_name);
- return it != services_to_return_.end() ? it->second : sp<IBinder>();
-}
-
-bool StubBinderWrapper::RegisterService(const std::string& service_name,
- const sp<IBinder>& binder) {
- registered_services_[service_name] = binder;
- return true;
-}
-
-sp<BBinder> StubBinderWrapper::CreateLocalBinder() {
- sp<BBinder> binder(new BBinder());
- local_binders_.push_back(binder);
- return binder;
-}
-
-bool StubBinderWrapper::RegisterForDeathNotifications(
- const sp<IBinder>& binder,
- const ::base::Closure& callback) {
- death_callbacks_[binder] = callback;
- return true;
-}
-
-bool StubBinderWrapper::UnregisterForDeathNotifications(
- const sp<IBinder>& binder) {
- death_callbacks_.erase(binder);
- return true;
-}
-
-uid_t StubBinderWrapper::GetCallingUid() {
- return calling_uid_;
-}
-
-pid_t StubBinderWrapper::GetCallingPid() {
- return calling_pid_;
-}
-
-} // namespace android
diff --git a/libcutils/Android.bp b/libcutils/Android.bp
index 0b5c125..92486e3 100644
--- a/libcutils/Android.bp
+++ b/libcutils/Android.bp
@@ -219,11 +219,19 @@
exclude_srcs: [
"qtaguid.cpp",
],
+ header_abi_checker: {
+ enabled: true,
+ ref_dump_dirs: ["abi-dumps"],
+ },
},
product: {
exclude_srcs: [
"qtaguid.cpp",
],
+ header_abi_checker: {
+ enabled: true,
+ ref_dump_dirs: ["abi-dumps"],
+ },
},
},
diff --git a/libcutils/KernelLibcutilsTest.xml b/libcutils/KernelLibcutilsTest.xml
index 40e4ef4..9750cbf 100644
--- a/libcutils/KernelLibcutilsTest.xml
+++ b/libcutils/KernelLibcutilsTest.xml
@@ -22,11 +22,11 @@
<target_preparer class="com.android.tradefed.targetprep.PushFilePreparer">
<option name="cleanup" value="true" />
- <option name="push" value="KernelLibcutilsTest->/data/local/tmp/KernelLibcutilsTest" />
+ <option name="push" value="KernelLibcutilsTest->/data/local/tests/unrestricted/KernelLibcutilsTest" />
</target_preparer>
<test class="com.android.tradefed.testtype.GTest" >
- <option name="native-test-device-path" value="/data/local/tmp" />
+ <option name="native-test-device-path" value="/data/local/tests/unrestricted" />
<option name="module-name" value="KernelLibcutilsTest" />
<option name="include-filter" value="*AshmemTest*" />
</test>
diff --git a/libcutils/OWNERS b/libcutils/OWNERS
index 7529cb9..e1cbe4a 100644
--- a/libcutils/OWNERS
+++ b/libcutils/OWNERS
@@ -1 +1,2 @@
+# Bug component: 128577
include platform/system/core:/janitors/OWNERS
diff --git a/libcutils/TEST_MAPPING b/libcutils/TEST_MAPPING
index eb63aa5..78b3e44 100644
--- a/libcutils/TEST_MAPPING
+++ b/libcutils/TEST_MAPPING
@@ -12,6 +12,9 @@
"kernel-presubmit": [
{
"name": "libcutils_test"
+ },
+ {
+ "name": "KernelLibcutilsTest"
}
]
}
diff --git a/libcutils/abi-dumps/arm64/source-based/libcutils.so.lsdump b/libcutils/abi-dumps/arm64/source-based/libcutils.so.lsdump
new file mode 100644
index 0000000..333e61c
--- /dev/null
+++ b/libcutils/abi-dumps/arm64/source-based/libcutils.so.lsdump
@@ -0,0 +1,2690 @@
+{
+ "array_types" :
+ [
+ {
+ "alignment" : 4,
+ "linker_set_key" : "_ZTIA0_i",
+ "name" : "int[0]",
+ "referenced_type" : "_ZTIi",
+ "self_type" : "_ZTIA0_i",
+ "source_file" : "system/core/libcutils/include/cutils/native_handle.h"
+ }
+ ],
+ "builtin_types" :
+ [
+ {
+ "alignment" : 1,
+ "is_integral" : true,
+ "linker_set_key" : "_ZTIa",
+ "name" : "signed char",
+ "referenced_type" : "_ZTIa",
+ "self_type" : "_ZTIa",
+ "size" : 1
+ },
+ {
+ "alignment" : 1,
+ "is_integral" : true,
+ "is_unsigned" : true,
+ "linker_set_key" : "_ZTIb",
+ "name" : "bool",
+ "referenced_type" : "_ZTIb",
+ "self_type" : "_ZTIb",
+ "size" : 1
+ },
+ {
+ "alignment" : 1,
+ "is_integral" : true,
+ "is_unsigned" : true,
+ "linker_set_key" : "_ZTIc",
+ "name" : "char",
+ "referenced_type" : "_ZTIc",
+ "self_type" : "_ZTIc",
+ "size" : 1
+ },
+ {
+ "alignment" : 4,
+ "linker_set_key" : "_ZTIf",
+ "name" : "float",
+ "referenced_type" : "_ZTIf",
+ "self_type" : "_ZTIf",
+ "size" : 4
+ },
+ {
+ "alignment" : 4,
+ "is_integral" : true,
+ "linker_set_key" : "_ZTIi",
+ "name" : "int",
+ "referenced_type" : "_ZTIi",
+ "self_type" : "_ZTIi",
+ "size" : 4
+ },
+ {
+ "alignment" : 4,
+ "is_integral" : true,
+ "is_unsigned" : true,
+ "linker_set_key" : "_ZTIj",
+ "name" : "unsigned int",
+ "referenced_type" : "_ZTIj",
+ "self_type" : "_ZTIj",
+ "size" : 4
+ },
+ {
+ "alignment" : 8,
+ "is_integral" : true,
+ "linker_set_key" : "_ZTIl",
+ "name" : "long",
+ "referenced_type" : "_ZTIl",
+ "self_type" : "_ZTIl",
+ "size" : 8
+ },
+ {
+ "alignment" : 8,
+ "is_integral" : true,
+ "is_unsigned" : true,
+ "linker_set_key" : "_ZTIm",
+ "name" : "unsigned long",
+ "referenced_type" : "_ZTIm",
+ "self_type" : "_ZTIm",
+ "size" : 8
+ },
+ {
+ "linker_set_key" : "_ZTIv",
+ "name" : "void",
+ "referenced_type" : "_ZTIv",
+ "self_type" : "_ZTIv"
+ }
+ ],
+ "elf_functions" :
+ [
+ {
+ "name" : "_Z23socket_make_sockaddr_unPKciP11sockaddr_unPj"
+ },
+ {
+ "binding" : "weak",
+ "name" : "_ZN7android4base4TrimIRNSt3__112basic_stringIcNS2_11char_traitsIcEENS2_9allocatorIcEEEEEES8_OT_"
+ },
+ {
+ "binding" : "weak",
+ "name" : "_ZNSt3__113basic_filebufIcNS_11char_traitsIcEEE4syncEv"
+ },
+ {
+ "binding" : "weak",
+ "name" : "_ZNSt3__113basic_filebufIcNS_11char_traitsIcEEE5imbueERKNS_6localeE"
+ },
+ {
+ "binding" : "weak",
+ "name" : "_ZNSt3__113basic_filebufIcNS_11char_traitsIcEEE6setbufEPcl"
+ },
+ {
+ "binding" : "weak",
+ "name" : "_ZNSt3__113basic_filebufIcNS_11char_traitsIcEEE7seekoffExNS_8ios_base7seekdirEj"
+ },
+ {
+ "binding" : "weak",
+ "name" : "_ZNSt3__113basic_filebufIcNS_11char_traitsIcEEE7seekposENS_4fposI9mbstate_tEEj"
+ },
+ {
+ "binding" : "weak",
+ "name" : "_ZNSt3__113basic_filebufIcNS_11char_traitsIcEEE8overflowEi"
+ },
+ {
+ "binding" : "weak",
+ "name" : "_ZNSt3__113basic_filebufIcNS_11char_traitsIcEEE9pbackfailEi"
+ },
+ {
+ "binding" : "weak",
+ "name" : "_ZNSt3__113basic_filebufIcNS_11char_traitsIcEEE9underflowEv"
+ },
+ {
+ "binding" : "weak",
+ "name" : "_ZNSt3__113basic_filebufIcNS_11char_traitsIcEEEC2Ev"
+ },
+ {
+ "binding" : "weak",
+ "name" : "_ZNSt3__113basic_filebufIcNS_11char_traitsIcEEED0Ev"
+ },
+ {
+ "binding" : "weak",
+ "name" : "_ZNSt3__113basic_filebufIcNS_11char_traitsIcEEED2Ev"
+ },
+ {
+ "binding" : "weak",
+ "name" : "_ZNSt3__124__put_character_sequenceIcNS_11char_traitsIcEEEERNS_13basic_ostreamIT_T0_EES7_PKS4_m"
+ },
+ {
+ "binding" : "weak",
+ "name" : "_ZNSt3__16vectorI5EntryNS_9allocatorIS1_EEE24__emplace_back_slow_pathIJS1_EEEvDpOT_"
+ },
+ {
+ "binding" : "weak",
+ "name" : "_ZNSt3__17getlineIcNS_11char_traitsIcEENS_9allocatorIcEEEERNS_13basic_istreamIT_T0_EES9_RNS_12basic_stringIS6_S7_T1_EES6_"
+ },
+ {
+ "binding" : "weak",
+ "name" : "_ZNSt3__1lsINS_11char_traitsIcEEEERNS_13basic_ostreamIcT_EES6_PKc"
+ },
+ {
+ "name" : "android_get_control_file"
+ },
+ {
+ "name" : "android_get_control_socket"
+ },
+ {
+ "name" : "android_get_ioprio"
+ },
+ {
+ "name" : "android_reboot"
+ },
+ {
+ "name" : "android_set_ioprio"
+ },
+ {
+ "name" : "ashmem_create_region"
+ },
+ {
+ "name" : "ashmem_get_size_region"
+ },
+ {
+ "name" : "ashmem_pin_region"
+ },
+ {
+ "name" : "ashmem_set_prot_region"
+ },
+ {
+ "name" : "ashmem_unpin_region"
+ },
+ {
+ "name" : "ashmem_valid"
+ },
+ {
+ "name" : "atrace_async_begin_body"
+ },
+ {
+ "name" : "atrace_async_end_body"
+ },
+ {
+ "name" : "atrace_async_for_track_begin_body"
+ },
+ {
+ "name" : "atrace_async_for_track_end_body"
+ },
+ {
+ "name" : "atrace_begin_body"
+ },
+ {
+ "name" : "atrace_end_body"
+ },
+ {
+ "name" : "atrace_get_enabled_tags"
+ },
+ {
+ "name" : "atrace_init"
+ },
+ {
+ "name" : "atrace_instant_body"
+ },
+ {
+ "name" : "atrace_instant_for_track_body"
+ },
+ {
+ "name" : "atrace_int64_body"
+ },
+ {
+ "name" : "atrace_int_body"
+ },
+ {
+ "name" : "atrace_set_tracing_enabled"
+ },
+ {
+ "name" : "atrace_setup"
+ },
+ {
+ "name" : "atrace_update_tags"
+ },
+ {
+ "name" : "canned_fs_config"
+ },
+ {
+ "name" : "config_bool"
+ },
+ {
+ "name" : "config_find"
+ },
+ {
+ "name" : "config_free"
+ },
+ {
+ "name" : "config_load"
+ },
+ {
+ "name" : "config_load_file"
+ },
+ {
+ "name" : "config_node"
+ },
+ {
+ "name" : "config_set"
+ },
+ {
+ "name" : "config_str"
+ },
+ {
+ "name" : "fs_config"
+ },
+ {
+ "name" : "fs_mkdirs"
+ },
+ {
+ "name" : "fs_prepare_dir"
+ },
+ {
+ "name" : "fs_prepare_dir_strict"
+ },
+ {
+ "name" : "fs_prepare_file_strict"
+ },
+ {
+ "name" : "fs_read_atomic_int"
+ },
+ {
+ "name" : "fs_write_atomic_int"
+ },
+ {
+ "name" : "hashmapCreate"
+ },
+ {
+ "name" : "hashmapForEach"
+ },
+ {
+ "name" : "hashmapFree"
+ },
+ {
+ "name" : "hashmapGet"
+ },
+ {
+ "name" : "hashmapHash"
+ },
+ {
+ "name" : "hashmapLock"
+ },
+ {
+ "name" : "hashmapPut"
+ },
+ {
+ "name" : "hashmapRemove"
+ },
+ {
+ "name" : "hashmapUnlock"
+ },
+ {
+ "name" : "klog_set_level"
+ },
+ {
+ "name" : "klog_write"
+ },
+ {
+ "name" : "klog_writev"
+ },
+ {
+ "name" : "load_canned_fs_config"
+ },
+ {
+ "name" : "load_file"
+ },
+ {
+ "name" : "multiuser_convert_sdk_sandbox_to_app_uid"
+ },
+ {
+ "name" : "multiuser_get_app_id"
+ },
+ {
+ "name" : "multiuser_get_cache_gid"
+ },
+ {
+ "name" : "multiuser_get_ext_cache_gid"
+ },
+ {
+ "name" : "multiuser_get_ext_gid"
+ },
+ {
+ "name" : "multiuser_get_sdk_sandbox_uid"
+ },
+ {
+ "name" : "multiuser_get_shared_app_gid"
+ },
+ {
+ "name" : "multiuser_get_shared_gid"
+ },
+ {
+ "name" : "multiuser_get_uid"
+ },
+ {
+ "name" : "multiuser_get_user_id"
+ },
+ {
+ "name" : "native_handle_clone"
+ },
+ {
+ "name" : "native_handle_close"
+ },
+ {
+ "name" : "native_handle_close_with_tag"
+ },
+ {
+ "name" : "native_handle_create"
+ },
+ {
+ "name" : "native_handle_delete"
+ },
+ {
+ "name" : "native_handle_init"
+ },
+ {
+ "name" : "native_handle_set_fdsan_tag"
+ },
+ {
+ "name" : "native_handle_unset_fdsan_tag"
+ },
+ {
+ "name" : "partition_wiped"
+ },
+ {
+ "name" : "property_get"
+ },
+ {
+ "name" : "property_get_bool"
+ },
+ {
+ "name" : "property_get_int32"
+ },
+ {
+ "name" : "property_get_int64"
+ },
+ {
+ "name" : "property_list"
+ },
+ {
+ "name" : "property_set"
+ },
+ {
+ "name" : "record_stream_free"
+ },
+ {
+ "name" : "record_stream_get_next"
+ },
+ {
+ "name" : "record_stream_new"
+ },
+ {
+ "name" : "socket_close"
+ },
+ {
+ "name" : "socket_get_local_port"
+ },
+ {
+ "name" : "socket_inaddr_any_server"
+ },
+ {
+ "name" : "socket_local_client"
+ },
+ {
+ "name" : "socket_local_client_connect"
+ },
+ {
+ "name" : "socket_local_server"
+ },
+ {
+ "name" : "socket_local_server_bind"
+ },
+ {
+ "name" : "socket_network_client"
+ },
+ {
+ "name" : "socket_network_client_timeout"
+ },
+ {
+ "name" : "socket_send_buffers"
+ },
+ {
+ "name" : "str_parms_add_float"
+ },
+ {
+ "name" : "str_parms_add_int"
+ },
+ {
+ "name" : "str_parms_add_str"
+ },
+ {
+ "name" : "str_parms_create"
+ },
+ {
+ "name" : "str_parms_create_str"
+ },
+ {
+ "name" : "str_parms_del"
+ },
+ {
+ "name" : "str_parms_destroy"
+ },
+ {
+ "name" : "str_parms_dump"
+ },
+ {
+ "name" : "str_parms_get_float"
+ },
+ {
+ "name" : "str_parms_get_int"
+ },
+ {
+ "name" : "str_parms_get_str"
+ },
+ {
+ "name" : "str_parms_has_key"
+ },
+ {
+ "name" : "str_parms_to_str"
+ },
+ {
+ "name" : "uevent_kernel_multicast_recv"
+ },
+ {
+ "name" : "uevent_kernel_multicast_uid_recv"
+ },
+ {
+ "name" : "uevent_kernel_recv"
+ },
+ {
+ "name" : "uevent_open_socket"
+ }
+ ],
+ "elf_objects" :
+ [
+ {
+ "binding" : "weak",
+ "name" : "_ZTCNSt3__114basic_ifstreamIcNS_11char_traitsIcEEEE0_NS_13basic_istreamIcS2_EE"
+ },
+ {
+ "binding" : "weak",
+ "name" : "_ZTTNSt3__114basic_ifstreamIcNS_11char_traitsIcEEEE"
+ },
+ {
+ "binding" : "weak",
+ "name" : "_ZTVNSt3__113basic_filebufIcNS_11char_traitsIcEEEE"
+ },
+ {
+ "binding" : "weak",
+ "name" : "_ZTVNSt3__114basic_ifstreamIcNS_11char_traitsIcEEEE"
+ },
+ {
+ "name" : "atrace_enabled_tags"
+ },
+ {
+ "name" : "atrace_is_ready"
+ },
+ {
+ "name" : "atrace_marker_fd"
+ }
+ ],
+ "enum_types" :
+ [
+ {
+ "alignment" : 4,
+ "enum_fields" :
+ [
+ {
+ "enum_field_value" : 0,
+ "name" : "IoSchedClass_NONE"
+ },
+ {
+ "enum_field_value" : 1,
+ "name" : "IoSchedClass_RT"
+ },
+ {
+ "enum_field_value" : 2,
+ "name" : "IoSchedClass_BE"
+ },
+ {
+ "enum_field_value" : 3,
+ "name" : "IoSchedClass_IDLE"
+ }
+ ],
+ "linker_set_key" : "_ZTI12IoSchedClass",
+ "name" : "IoSchedClass",
+ "referenced_type" : "_ZTI12IoSchedClass",
+ "self_type" : "_ZTI12IoSchedClass",
+ "size" : 4,
+ "source_file" : "system/core/libcutils/include/cutils/iosched_policy.h",
+ "underlying_type" : "_ZTIj"
+ }
+ ],
+ "function_types" :
+ [
+ {
+ "alignment" : 4,
+ "linker_set_key" : "_ZTIFbPvS_E",
+ "name" : "bool (void *, void *)",
+ "parameters" :
+ [
+ {
+ "referenced_type" : "_ZTIPv"
+ },
+ {
+ "referenced_type" : "_ZTIPv"
+ }
+ ],
+ "referenced_type" : "_ZTIFbPvS_E",
+ "return_type" : "_ZTIb",
+ "self_type" : "_ZTIFbPvS_E",
+ "source_file" : "system/core/libcutils/include/cutils/hashmap.h"
+ },
+ {
+ "alignment" : 4,
+ "linker_set_key" : "_ZTIFbPvS_S_E",
+ "name" : "bool (void *, void *, void *)",
+ "parameters" :
+ [
+ {
+ "referenced_type" : "_ZTIPv"
+ },
+ {
+ "referenced_type" : "_ZTIPv"
+ },
+ {
+ "referenced_type" : "_ZTIPv"
+ }
+ ],
+ "referenced_type" : "_ZTIFbPvS_S_E",
+ "return_type" : "_ZTIb",
+ "self_type" : "_ZTIFbPvS_S_E",
+ "source_file" : "system/core/libcutils/include/cutils/hashmap.h"
+ },
+ {
+ "alignment" : 4,
+ "linker_set_key" : "_ZTIFiPvE",
+ "name" : "int (void *)",
+ "parameters" :
+ [
+ {
+ "referenced_type" : "_ZTIPv"
+ }
+ ],
+ "referenced_type" : "_ZTIFiPvE",
+ "return_type" : "_ZTIi",
+ "self_type" : "_ZTIFiPvE",
+ "source_file" : "system/core/libcutils/include/cutils/hashmap.h"
+ },
+ {
+ "alignment" : 4,
+ "linker_set_key" : "_ZTIFvPKcS0_PvE",
+ "name" : "void (const char *, const char *, void *)",
+ "parameters" :
+ [
+ {
+ "referenced_type" : "_ZTIPKc"
+ },
+ {
+ "referenced_type" : "_ZTIPKc"
+ },
+ {
+ "referenced_type" : "_ZTIPv"
+ }
+ ],
+ "referenced_type" : "_ZTIFvPKcS0_PvE",
+ "return_type" : "_ZTIv",
+ "self_type" : "_ZTIFvPKcS0_PvE",
+ "source_file" : "system/core/libcutils/include/cutils/properties.h"
+ }
+ ],
+ "functions" :
+ [
+ {
+ "function_name" : "android_get_control_file",
+ "linker_set_key" : "android_get_control_file",
+ "parameters" :
+ [
+ {
+ "referenced_type" : "_ZTIPKc"
+ }
+ ],
+ "return_type" : "_ZTIi",
+ "source_file" : "system/core/libcutils/include/cutils/android_get_control_file.h"
+ },
+ {
+ "function_name" : "android_get_control_socket",
+ "linker_set_key" : "android_get_control_socket",
+ "parameters" :
+ [
+ {
+ "referenced_type" : "_ZTIPKc"
+ }
+ ],
+ "return_type" : "_ZTIi",
+ "source_file" : "system/core/libcutils/include/cutils/sockets.h"
+ },
+ {
+ "function_name" : "android_get_ioprio",
+ "linker_set_key" : "android_get_ioprio",
+ "parameters" :
+ [
+ {
+ "referenced_type" : "_ZTIi"
+ },
+ {
+ "referenced_type" : "_ZTIP12IoSchedClass"
+ },
+ {
+ "referenced_type" : "_ZTIPi"
+ }
+ ],
+ "return_type" : "_ZTIi",
+ "source_file" : "system/core/libcutils/include/cutils/iosched_policy.h"
+ },
+ {
+ "function_name" : "android_reboot",
+ "linker_set_key" : "android_reboot",
+ "parameters" :
+ [
+ {
+ "referenced_type" : "_ZTIj"
+ },
+ {
+ "referenced_type" : "_ZTIi"
+ },
+ {
+ "referenced_type" : "_ZTIPKc"
+ }
+ ],
+ "return_type" : "_ZTIi",
+ "source_file" : "system/core/libcutils/include/cutils/android_reboot.h"
+ },
+ {
+ "function_name" : "android_set_ioprio",
+ "linker_set_key" : "android_set_ioprio",
+ "parameters" :
+ [
+ {
+ "referenced_type" : "_ZTIi"
+ },
+ {
+ "referenced_type" : "_ZTI12IoSchedClass"
+ },
+ {
+ "referenced_type" : "_ZTIi"
+ }
+ ],
+ "return_type" : "_ZTIi",
+ "source_file" : "system/core/libcutils/include/cutils/iosched_policy.h"
+ },
+ {
+ "function_name" : "ashmem_create_region",
+ "linker_set_key" : "ashmem_create_region",
+ "parameters" :
+ [
+ {
+ "referenced_type" : "_ZTIPKc"
+ },
+ {
+ "referenced_type" : "_ZTIm"
+ }
+ ],
+ "return_type" : "_ZTIi",
+ "source_file" : "system/core/libcutils/include/cutils/ashmem.h"
+ },
+ {
+ "function_name" : "ashmem_get_size_region",
+ "linker_set_key" : "ashmem_get_size_region",
+ "parameters" :
+ [
+ {
+ "referenced_type" : "_ZTIi"
+ }
+ ],
+ "return_type" : "_ZTIi",
+ "source_file" : "system/core/libcutils/include/cutils/ashmem.h"
+ },
+ {
+ "function_name" : "ashmem_pin_region",
+ "linker_set_key" : "ashmem_pin_region",
+ "parameters" :
+ [
+ {
+ "referenced_type" : "_ZTIi"
+ },
+ {
+ "referenced_type" : "_ZTIm"
+ },
+ {
+ "referenced_type" : "_ZTIm"
+ }
+ ],
+ "return_type" : "_ZTIi",
+ "source_file" : "system/core/libcutils/include/cutils/ashmem.h"
+ },
+ {
+ "function_name" : "ashmem_set_prot_region",
+ "linker_set_key" : "ashmem_set_prot_region",
+ "parameters" :
+ [
+ {
+ "referenced_type" : "_ZTIi"
+ },
+ {
+ "referenced_type" : "_ZTIi"
+ }
+ ],
+ "return_type" : "_ZTIi",
+ "source_file" : "system/core/libcutils/include/cutils/ashmem.h"
+ },
+ {
+ "function_name" : "ashmem_unpin_region",
+ "linker_set_key" : "ashmem_unpin_region",
+ "parameters" :
+ [
+ {
+ "referenced_type" : "_ZTIi"
+ },
+ {
+ "referenced_type" : "_ZTIm"
+ },
+ {
+ "referenced_type" : "_ZTIm"
+ }
+ ],
+ "return_type" : "_ZTIi",
+ "source_file" : "system/core/libcutils/include/cutils/ashmem.h"
+ },
+ {
+ "function_name" : "ashmem_valid",
+ "linker_set_key" : "ashmem_valid",
+ "parameters" :
+ [
+ {
+ "referenced_type" : "_ZTIi"
+ }
+ ],
+ "return_type" : "_ZTIi",
+ "source_file" : "system/core/libcutils/include/cutils/ashmem.h"
+ },
+ {
+ "function_name" : "atrace_async_begin_body",
+ "linker_set_key" : "atrace_async_begin_body",
+ "parameters" :
+ [
+ {
+ "referenced_type" : "_ZTIPKc"
+ },
+ {
+ "referenced_type" : "_ZTIi"
+ }
+ ],
+ "return_type" : "_ZTIv",
+ "source_file" : "system/core/libcutils/include/cutils/trace.h"
+ },
+ {
+ "function_name" : "atrace_async_end_body",
+ "linker_set_key" : "atrace_async_end_body",
+ "parameters" :
+ [
+ {
+ "referenced_type" : "_ZTIPKc"
+ },
+ {
+ "referenced_type" : "_ZTIi"
+ }
+ ],
+ "return_type" : "_ZTIv",
+ "source_file" : "system/core/libcutils/include/cutils/trace.h"
+ },
+ {
+ "function_name" : "atrace_async_for_track_begin_body",
+ "linker_set_key" : "atrace_async_for_track_begin_body",
+ "parameters" :
+ [
+ {
+ "referenced_type" : "_ZTIPKc"
+ },
+ {
+ "referenced_type" : "_ZTIPKc"
+ },
+ {
+ "referenced_type" : "_ZTIi"
+ }
+ ],
+ "return_type" : "_ZTIv",
+ "source_file" : "system/core/libcutils/include/cutils/trace.h"
+ },
+ {
+ "function_name" : "atrace_async_for_track_end_body",
+ "linker_set_key" : "atrace_async_for_track_end_body",
+ "parameters" :
+ [
+ {
+ "referenced_type" : "_ZTIPKc"
+ },
+ {
+ "referenced_type" : "_ZTIi"
+ }
+ ],
+ "return_type" : "_ZTIv",
+ "source_file" : "system/core/libcutils/include/cutils/trace.h"
+ },
+ {
+ "function_name" : "atrace_begin_body",
+ "linker_set_key" : "atrace_begin_body",
+ "parameters" :
+ [
+ {
+ "referenced_type" : "_ZTIPKc"
+ }
+ ],
+ "return_type" : "_ZTIv",
+ "source_file" : "system/core/libcutils/include/cutils/trace.h"
+ },
+ {
+ "function_name" : "atrace_end_body",
+ "linker_set_key" : "atrace_end_body",
+ "return_type" : "_ZTIv",
+ "source_file" : "system/core/libcutils/include/cutils/trace.h"
+ },
+ {
+ "function_name" : "atrace_get_enabled_tags",
+ "linker_set_key" : "atrace_get_enabled_tags",
+ "return_type" : "_ZTIm",
+ "source_file" : "system/core/libcutils/include/cutils/trace.h"
+ },
+ {
+ "function_name" : "atrace_init",
+ "linker_set_key" : "atrace_init",
+ "return_type" : "_ZTIv",
+ "source_file" : "system/core/libcutils/include/cutils/trace.h"
+ },
+ {
+ "function_name" : "atrace_instant_body",
+ "linker_set_key" : "atrace_instant_body",
+ "parameters" :
+ [
+ {
+ "referenced_type" : "_ZTIPKc"
+ }
+ ],
+ "return_type" : "_ZTIv",
+ "source_file" : "system/core/libcutils/include/cutils/trace.h"
+ },
+ {
+ "function_name" : "atrace_instant_for_track_body",
+ "linker_set_key" : "atrace_instant_for_track_body",
+ "parameters" :
+ [
+ {
+ "referenced_type" : "_ZTIPKc"
+ },
+ {
+ "referenced_type" : "_ZTIPKc"
+ }
+ ],
+ "return_type" : "_ZTIv",
+ "source_file" : "system/core/libcutils/include/cutils/trace.h"
+ },
+ {
+ "function_name" : "atrace_int64_body",
+ "linker_set_key" : "atrace_int64_body",
+ "parameters" :
+ [
+ {
+ "referenced_type" : "_ZTIPKc"
+ },
+ {
+ "referenced_type" : "_ZTIl"
+ }
+ ],
+ "return_type" : "_ZTIv",
+ "source_file" : "system/core/libcutils/include/cutils/trace.h"
+ },
+ {
+ "function_name" : "atrace_int_body",
+ "linker_set_key" : "atrace_int_body",
+ "parameters" :
+ [
+ {
+ "referenced_type" : "_ZTIPKc"
+ },
+ {
+ "referenced_type" : "_ZTIi"
+ }
+ ],
+ "return_type" : "_ZTIv",
+ "source_file" : "system/core/libcutils/include/cutils/trace.h"
+ },
+ {
+ "function_name" : "atrace_set_tracing_enabled",
+ "linker_set_key" : "atrace_set_tracing_enabled",
+ "parameters" :
+ [
+ {
+ "referenced_type" : "_ZTIb"
+ }
+ ],
+ "return_type" : "_ZTIv",
+ "source_file" : "system/core/libcutils/include/cutils/trace.h"
+ },
+ {
+ "function_name" : "atrace_setup",
+ "linker_set_key" : "atrace_setup",
+ "return_type" : "_ZTIv",
+ "source_file" : "system/core/libcutils/include/cutils/trace.h"
+ },
+ {
+ "function_name" : "atrace_update_tags",
+ "linker_set_key" : "atrace_update_tags",
+ "return_type" : "_ZTIv",
+ "source_file" : "system/core/libcutils/include/cutils/trace.h"
+ },
+ {
+ "function_name" : "canned_fs_config",
+ "linker_set_key" : "canned_fs_config",
+ "parameters" :
+ [
+ {
+ "referenced_type" : "_ZTIPKc"
+ },
+ {
+ "referenced_type" : "_ZTIi"
+ },
+ {
+ "referenced_type" : "_ZTIPKc"
+ },
+ {
+ "referenced_type" : "_ZTIPj"
+ },
+ {
+ "referenced_type" : "_ZTIPj"
+ },
+ {
+ "referenced_type" : "_ZTIPj"
+ },
+ {
+ "referenced_type" : "_ZTIPm"
+ }
+ ],
+ "return_type" : "_ZTIv",
+ "source_file" : "system/core/libcutils/include/private/canned_fs_config.h"
+ },
+ {
+ "function_name" : "config_bool",
+ "linker_set_key" : "config_bool",
+ "parameters" :
+ [
+ {
+ "referenced_type" : "_ZTIP5cnode"
+ },
+ {
+ "referenced_type" : "_ZTIPKc"
+ },
+ {
+ "referenced_type" : "_ZTIi"
+ }
+ ],
+ "return_type" : "_ZTIi",
+ "source_file" : "system/core/libcutils/include/cutils/config_utils.h"
+ },
+ {
+ "function_name" : "config_find",
+ "linker_set_key" : "config_find",
+ "parameters" :
+ [
+ {
+ "referenced_type" : "_ZTIP5cnode"
+ },
+ {
+ "referenced_type" : "_ZTIPKc"
+ }
+ ],
+ "return_type" : "_ZTIP5cnode",
+ "source_file" : "system/core/libcutils/include/cutils/config_utils.h"
+ },
+ {
+ "function_name" : "config_free",
+ "linker_set_key" : "config_free",
+ "parameters" :
+ [
+ {
+ "referenced_type" : "_ZTIP5cnode"
+ }
+ ],
+ "return_type" : "_ZTIv",
+ "source_file" : "system/core/libcutils/include/cutils/config_utils.h"
+ },
+ {
+ "function_name" : "config_load",
+ "linker_set_key" : "config_load",
+ "parameters" :
+ [
+ {
+ "referenced_type" : "_ZTIP5cnode"
+ },
+ {
+ "referenced_type" : "_ZTIPc"
+ }
+ ],
+ "return_type" : "_ZTIv",
+ "source_file" : "system/core/libcutils/include/cutils/config_utils.h"
+ },
+ {
+ "function_name" : "config_load_file",
+ "linker_set_key" : "config_load_file",
+ "parameters" :
+ [
+ {
+ "referenced_type" : "_ZTIP5cnode"
+ },
+ {
+ "referenced_type" : "_ZTIPKc"
+ }
+ ],
+ "return_type" : "_ZTIv",
+ "source_file" : "system/core/libcutils/include/cutils/config_utils.h"
+ },
+ {
+ "function_name" : "config_node",
+ "linker_set_key" : "config_node",
+ "parameters" :
+ [
+ {
+ "referenced_type" : "_ZTIPKc"
+ },
+ {
+ "referenced_type" : "_ZTIPKc"
+ }
+ ],
+ "return_type" : "_ZTIP5cnode",
+ "source_file" : "system/core/libcutils/include/cutils/config_utils.h"
+ },
+ {
+ "function_name" : "config_set",
+ "linker_set_key" : "config_set",
+ "parameters" :
+ [
+ {
+ "referenced_type" : "_ZTIP5cnode"
+ },
+ {
+ "referenced_type" : "_ZTIPKc"
+ },
+ {
+ "referenced_type" : "_ZTIPKc"
+ }
+ ],
+ "return_type" : "_ZTIv",
+ "source_file" : "system/core/libcutils/include/cutils/config_utils.h"
+ },
+ {
+ "function_name" : "config_str",
+ "linker_set_key" : "config_str",
+ "parameters" :
+ [
+ {
+ "referenced_type" : "_ZTIP5cnode"
+ },
+ {
+ "referenced_type" : "_ZTIPKc"
+ },
+ {
+ "referenced_type" : "_ZTIPKc"
+ }
+ ],
+ "return_type" : "_ZTIPKc",
+ "source_file" : "system/core/libcutils/include/cutils/config_utils.h"
+ },
+ {
+ "function_name" : "fs_config",
+ "linker_set_key" : "fs_config",
+ "parameters" :
+ [
+ {
+ "referenced_type" : "_ZTIPKc"
+ },
+ {
+ "referenced_type" : "_ZTIi"
+ },
+ {
+ "referenced_type" : "_ZTIPKc"
+ },
+ {
+ "referenced_type" : "_ZTIPj"
+ },
+ {
+ "referenced_type" : "_ZTIPj"
+ },
+ {
+ "referenced_type" : "_ZTIPj"
+ },
+ {
+ "referenced_type" : "_ZTIPm"
+ }
+ ],
+ "return_type" : "_ZTIv",
+ "source_file" : "system/core/libcutils/include/private/fs_config.h"
+ },
+ {
+ "function_name" : "fs_mkdirs",
+ "linker_set_key" : "fs_mkdirs",
+ "parameters" :
+ [
+ {
+ "referenced_type" : "_ZTIPKc"
+ },
+ {
+ "referenced_type" : "_ZTIj"
+ }
+ ],
+ "return_type" : "_ZTIi",
+ "source_file" : "system/core/libcutils/include/cutils/fs.h"
+ },
+ {
+ "function_name" : "fs_prepare_dir",
+ "linker_set_key" : "fs_prepare_dir",
+ "parameters" :
+ [
+ {
+ "referenced_type" : "_ZTIPKc"
+ },
+ {
+ "referenced_type" : "_ZTIj"
+ },
+ {
+ "referenced_type" : "_ZTIj"
+ },
+ {
+ "referenced_type" : "_ZTIj"
+ }
+ ],
+ "return_type" : "_ZTIi",
+ "source_file" : "system/core/libcutils/include/cutils/fs.h"
+ },
+ {
+ "function_name" : "fs_prepare_dir_strict",
+ "linker_set_key" : "fs_prepare_dir_strict",
+ "parameters" :
+ [
+ {
+ "referenced_type" : "_ZTIPKc"
+ },
+ {
+ "referenced_type" : "_ZTIj"
+ },
+ {
+ "referenced_type" : "_ZTIj"
+ },
+ {
+ "referenced_type" : "_ZTIj"
+ }
+ ],
+ "return_type" : "_ZTIi",
+ "source_file" : "system/core/libcutils/include/cutils/fs.h"
+ },
+ {
+ "function_name" : "fs_prepare_file_strict",
+ "linker_set_key" : "fs_prepare_file_strict",
+ "parameters" :
+ [
+ {
+ "referenced_type" : "_ZTIPKc"
+ },
+ {
+ "referenced_type" : "_ZTIj"
+ },
+ {
+ "referenced_type" : "_ZTIj"
+ },
+ {
+ "referenced_type" : "_ZTIj"
+ }
+ ],
+ "return_type" : "_ZTIi",
+ "source_file" : "system/core/libcutils/include/cutils/fs.h"
+ },
+ {
+ "function_name" : "fs_read_atomic_int",
+ "linker_set_key" : "fs_read_atomic_int",
+ "parameters" :
+ [
+ {
+ "referenced_type" : "_ZTIPKc"
+ },
+ {
+ "referenced_type" : "_ZTIPi"
+ }
+ ],
+ "return_type" : "_ZTIi",
+ "source_file" : "system/core/libcutils/include/cutils/fs.h"
+ },
+ {
+ "function_name" : "fs_write_atomic_int",
+ "linker_set_key" : "fs_write_atomic_int",
+ "parameters" :
+ [
+ {
+ "referenced_type" : "_ZTIPKc"
+ },
+ {
+ "referenced_type" : "_ZTIi"
+ }
+ ],
+ "return_type" : "_ZTIi",
+ "source_file" : "system/core/libcutils/include/cutils/fs.h"
+ },
+ {
+ "function_name" : "hashmapCreate",
+ "linker_set_key" : "hashmapCreate",
+ "parameters" :
+ [
+ {
+ "referenced_type" : "_ZTIm"
+ },
+ {
+ "referenced_type" : "_ZTIPFiPvE"
+ },
+ {
+ "referenced_type" : "_ZTIPFbPvS_E"
+ }
+ ],
+ "return_type" : "_ZTIP7Hashmap",
+ "source_file" : "system/core/libcutils/include/cutils/hashmap.h"
+ },
+ {
+ "function_name" : "hashmapForEach",
+ "linker_set_key" : "hashmapForEach",
+ "parameters" :
+ [
+ {
+ "referenced_type" : "_ZTIP7Hashmap"
+ },
+ {
+ "referenced_type" : "_ZTIPFbPvS_S_E"
+ },
+ {
+ "referenced_type" : "_ZTIPv"
+ }
+ ],
+ "return_type" : "_ZTIv",
+ "source_file" : "system/core/libcutils/include/cutils/hashmap.h"
+ },
+ {
+ "function_name" : "hashmapFree",
+ "linker_set_key" : "hashmapFree",
+ "parameters" :
+ [
+ {
+ "referenced_type" : "_ZTIP7Hashmap"
+ }
+ ],
+ "return_type" : "_ZTIv",
+ "source_file" : "system/core/libcutils/include/cutils/hashmap.h"
+ },
+ {
+ "function_name" : "hashmapGet",
+ "linker_set_key" : "hashmapGet",
+ "parameters" :
+ [
+ {
+ "referenced_type" : "_ZTIP7Hashmap"
+ },
+ {
+ "referenced_type" : "_ZTIPv"
+ }
+ ],
+ "return_type" : "_ZTIPv",
+ "source_file" : "system/core/libcutils/include/cutils/hashmap.h"
+ },
+ {
+ "function_name" : "hashmapHash",
+ "linker_set_key" : "hashmapHash",
+ "parameters" :
+ [
+ {
+ "referenced_type" : "_ZTIPv"
+ },
+ {
+ "referenced_type" : "_ZTIm"
+ }
+ ],
+ "return_type" : "_ZTIi",
+ "source_file" : "system/core/libcutils/include/cutils/hashmap.h"
+ },
+ {
+ "function_name" : "hashmapLock",
+ "linker_set_key" : "hashmapLock",
+ "parameters" :
+ [
+ {
+ "referenced_type" : "_ZTIP7Hashmap"
+ }
+ ],
+ "return_type" : "_ZTIv",
+ "source_file" : "system/core/libcutils/include/cutils/hashmap.h"
+ },
+ {
+ "function_name" : "hashmapPut",
+ "linker_set_key" : "hashmapPut",
+ "parameters" :
+ [
+ {
+ "referenced_type" : "_ZTIP7Hashmap"
+ },
+ {
+ "referenced_type" : "_ZTIPv"
+ },
+ {
+ "referenced_type" : "_ZTIPv"
+ }
+ ],
+ "return_type" : "_ZTIPv",
+ "source_file" : "system/core/libcutils/include/cutils/hashmap.h"
+ },
+ {
+ "function_name" : "hashmapRemove",
+ "linker_set_key" : "hashmapRemove",
+ "parameters" :
+ [
+ {
+ "referenced_type" : "_ZTIP7Hashmap"
+ },
+ {
+ "referenced_type" : "_ZTIPv"
+ }
+ ],
+ "return_type" : "_ZTIPv",
+ "source_file" : "system/core/libcutils/include/cutils/hashmap.h"
+ },
+ {
+ "function_name" : "hashmapUnlock",
+ "linker_set_key" : "hashmapUnlock",
+ "parameters" :
+ [
+ {
+ "referenced_type" : "_ZTIP7Hashmap"
+ }
+ ],
+ "return_type" : "_ZTIv",
+ "source_file" : "system/core/libcutils/include/cutils/hashmap.h"
+ },
+ {
+ "function_name" : "klog_set_level",
+ "linker_set_key" : "klog_set_level",
+ "parameters" :
+ [
+ {
+ "referenced_type" : "_ZTIi"
+ }
+ ],
+ "return_type" : "_ZTIv",
+ "source_file" : "system/core/libcutils/include/cutils/klog.h"
+ },
+ {
+ "function_name" : "klog_write",
+ "linker_set_key" : "klog_write",
+ "parameters" :
+ [
+ {
+ "referenced_type" : "_ZTIi"
+ },
+ {
+ "referenced_type" : "_ZTIPKc"
+ }
+ ],
+ "return_type" : "_ZTIv",
+ "source_file" : "system/core/libcutils/include/cutils/klog.h"
+ },
+ {
+ "function_name" : "klog_writev",
+ "linker_set_key" : "klog_writev",
+ "parameters" :
+ [
+ {
+ "referenced_type" : "_ZTIi"
+ },
+ {
+ "referenced_type" : "_ZTIPK5iovec"
+ },
+ {
+ "referenced_type" : "_ZTIi"
+ }
+ ],
+ "return_type" : "_ZTIv",
+ "source_file" : "system/core/libcutils/include/cutils/klog.h"
+ },
+ {
+ "function_name" : "load_canned_fs_config",
+ "linker_set_key" : "load_canned_fs_config",
+ "parameters" :
+ [
+ {
+ "referenced_type" : "_ZTIPKc"
+ }
+ ],
+ "return_type" : "_ZTIi",
+ "source_file" : "system/core/libcutils/include/private/canned_fs_config.h"
+ },
+ {
+ "function_name" : "load_file",
+ "linker_set_key" : "load_file",
+ "parameters" :
+ [
+ {
+ "referenced_type" : "_ZTIPKc"
+ },
+ {
+ "referenced_type" : "_ZTIPj"
+ }
+ ],
+ "return_type" : "_ZTIPv",
+ "source_file" : "system/core/libcutils/include/cutils/misc.h"
+ },
+ {
+ "function_name" : "multiuser_convert_sdk_sandbox_to_app_uid",
+ "linker_set_key" : "multiuser_convert_sdk_sandbox_to_app_uid",
+ "parameters" :
+ [
+ {
+ "referenced_type" : "_ZTIj"
+ }
+ ],
+ "return_type" : "_ZTIj",
+ "source_file" : "system/core/libcutils/include/cutils/multiuser.h"
+ },
+ {
+ "function_name" : "multiuser_get_app_id",
+ "linker_set_key" : "multiuser_get_app_id",
+ "parameters" :
+ [
+ {
+ "referenced_type" : "_ZTIj"
+ }
+ ],
+ "return_type" : "_ZTIj",
+ "source_file" : "system/core/libcutils/include/cutils/multiuser.h"
+ },
+ {
+ "function_name" : "multiuser_get_cache_gid",
+ "linker_set_key" : "multiuser_get_cache_gid",
+ "parameters" :
+ [
+ {
+ "referenced_type" : "_ZTIj"
+ },
+ {
+ "referenced_type" : "_ZTIj"
+ }
+ ],
+ "return_type" : "_ZTIj",
+ "source_file" : "system/core/libcutils/include/cutils/multiuser.h"
+ },
+ {
+ "function_name" : "multiuser_get_ext_cache_gid",
+ "linker_set_key" : "multiuser_get_ext_cache_gid",
+ "parameters" :
+ [
+ {
+ "referenced_type" : "_ZTIj"
+ },
+ {
+ "referenced_type" : "_ZTIj"
+ }
+ ],
+ "return_type" : "_ZTIj",
+ "source_file" : "system/core/libcutils/include/cutils/multiuser.h"
+ },
+ {
+ "function_name" : "multiuser_get_ext_gid",
+ "linker_set_key" : "multiuser_get_ext_gid",
+ "parameters" :
+ [
+ {
+ "referenced_type" : "_ZTIj"
+ },
+ {
+ "referenced_type" : "_ZTIj"
+ }
+ ],
+ "return_type" : "_ZTIj",
+ "source_file" : "system/core/libcutils/include/cutils/multiuser.h"
+ },
+ {
+ "function_name" : "multiuser_get_sdk_sandbox_uid",
+ "linker_set_key" : "multiuser_get_sdk_sandbox_uid",
+ "parameters" :
+ [
+ {
+ "referenced_type" : "_ZTIj"
+ },
+ {
+ "referenced_type" : "_ZTIj"
+ }
+ ],
+ "return_type" : "_ZTIj",
+ "source_file" : "system/core/libcutils/include/cutils/multiuser.h"
+ },
+ {
+ "function_name" : "multiuser_get_shared_app_gid",
+ "linker_set_key" : "multiuser_get_shared_app_gid",
+ "parameters" :
+ [
+ {
+ "referenced_type" : "_ZTIj"
+ }
+ ],
+ "return_type" : "_ZTIj",
+ "source_file" : "system/core/libcutils/include/cutils/multiuser.h"
+ },
+ {
+ "function_name" : "multiuser_get_shared_gid",
+ "linker_set_key" : "multiuser_get_shared_gid",
+ "parameters" :
+ [
+ {
+ "referenced_type" : "_ZTIj"
+ },
+ {
+ "referenced_type" : "_ZTIj"
+ }
+ ],
+ "return_type" : "_ZTIj",
+ "source_file" : "system/core/libcutils/include/cutils/multiuser.h"
+ },
+ {
+ "function_name" : "multiuser_get_uid",
+ "linker_set_key" : "multiuser_get_uid",
+ "parameters" :
+ [
+ {
+ "referenced_type" : "_ZTIj"
+ },
+ {
+ "referenced_type" : "_ZTIj"
+ }
+ ],
+ "return_type" : "_ZTIj",
+ "source_file" : "system/core/libcutils/include/cutils/multiuser.h"
+ },
+ {
+ "function_name" : "multiuser_get_user_id",
+ "linker_set_key" : "multiuser_get_user_id",
+ "parameters" :
+ [
+ {
+ "referenced_type" : "_ZTIj"
+ }
+ ],
+ "return_type" : "_ZTIj",
+ "source_file" : "system/core/libcutils/include/cutils/multiuser.h"
+ },
+ {
+ "function_name" : "native_handle_clone",
+ "linker_set_key" : "native_handle_clone",
+ "parameters" :
+ [
+ {
+ "referenced_type" : "_ZTIPK13native_handle"
+ }
+ ],
+ "return_type" : "_ZTIP13native_handle",
+ "source_file" : "system/core/libcutils/include/cutils/native_handle.h"
+ },
+ {
+ "function_name" : "native_handle_close",
+ "linker_set_key" : "native_handle_close",
+ "parameters" :
+ [
+ {
+ "referenced_type" : "_ZTIPK13native_handle"
+ }
+ ],
+ "return_type" : "_ZTIi",
+ "source_file" : "system/core/libcutils/include/cutils/native_handle.h"
+ },
+ {
+ "function_name" : "native_handle_close_with_tag",
+ "linker_set_key" : "native_handle_close_with_tag",
+ "parameters" :
+ [
+ {
+ "referenced_type" : "_ZTIPK13native_handle"
+ }
+ ],
+ "return_type" : "_ZTIi",
+ "source_file" : "system/core/libcutils/include/cutils/native_handle.h"
+ },
+ {
+ "function_name" : "native_handle_create",
+ "linker_set_key" : "native_handle_create",
+ "parameters" :
+ [
+ {
+ "referenced_type" : "_ZTIi"
+ },
+ {
+ "referenced_type" : "_ZTIi"
+ }
+ ],
+ "return_type" : "_ZTIP13native_handle",
+ "source_file" : "system/core/libcutils/include/cutils/native_handle.h"
+ },
+ {
+ "function_name" : "native_handle_delete",
+ "linker_set_key" : "native_handle_delete",
+ "parameters" :
+ [
+ {
+ "referenced_type" : "_ZTIP13native_handle"
+ }
+ ],
+ "return_type" : "_ZTIi",
+ "source_file" : "system/core/libcutils/include/cutils/native_handle.h"
+ },
+ {
+ "function_name" : "native_handle_init",
+ "linker_set_key" : "native_handle_init",
+ "parameters" :
+ [
+ {
+ "referenced_type" : "_ZTIPc"
+ },
+ {
+ "referenced_type" : "_ZTIi"
+ },
+ {
+ "referenced_type" : "_ZTIi"
+ }
+ ],
+ "return_type" : "_ZTIP13native_handle",
+ "source_file" : "system/core/libcutils/include/cutils/native_handle.h"
+ },
+ {
+ "function_name" : "native_handle_set_fdsan_tag",
+ "linker_set_key" : "native_handle_set_fdsan_tag",
+ "parameters" :
+ [
+ {
+ "referenced_type" : "_ZTIPK13native_handle"
+ }
+ ],
+ "return_type" : "_ZTIv",
+ "source_file" : "system/core/libcutils/include/cutils/native_handle.h"
+ },
+ {
+ "function_name" : "native_handle_unset_fdsan_tag",
+ "linker_set_key" : "native_handle_unset_fdsan_tag",
+ "parameters" :
+ [
+ {
+ "referenced_type" : "_ZTIPK13native_handle"
+ }
+ ],
+ "return_type" : "_ZTIv",
+ "source_file" : "system/core/libcutils/include/cutils/native_handle.h"
+ },
+ {
+ "function_name" : "partition_wiped",
+ "linker_set_key" : "partition_wiped",
+ "parameters" :
+ [
+ {
+ "referenced_type" : "_ZTIPKc"
+ }
+ ],
+ "return_type" : "_ZTIi",
+ "source_file" : "system/core/libcutils/include/cutils/partition_utils.h"
+ },
+ {
+ "function_name" : "property_get",
+ "linker_set_key" : "property_get",
+ "parameters" :
+ [
+ {
+ "referenced_type" : "_ZTIPKc"
+ },
+ {
+ "referenced_type" : "_ZTIPc"
+ },
+ {
+ "referenced_type" : "_ZTIPKc"
+ }
+ ],
+ "return_type" : "_ZTIi",
+ "source_file" : "system/core/libcutils/include/cutils/properties.h"
+ },
+ {
+ "function_name" : "property_get_bool",
+ "linker_set_key" : "property_get_bool",
+ "parameters" :
+ [
+ {
+ "referenced_type" : "_ZTIPKc"
+ },
+ {
+ "referenced_type" : "_ZTIa"
+ }
+ ],
+ "return_type" : "_ZTIa",
+ "source_file" : "system/core/libcutils/include/cutils/properties.h"
+ },
+ {
+ "function_name" : "property_get_int32",
+ "linker_set_key" : "property_get_int32",
+ "parameters" :
+ [
+ {
+ "referenced_type" : "_ZTIPKc"
+ },
+ {
+ "referenced_type" : "_ZTIi"
+ }
+ ],
+ "return_type" : "_ZTIi",
+ "source_file" : "system/core/libcutils/include/cutils/properties.h"
+ },
+ {
+ "function_name" : "property_get_int64",
+ "linker_set_key" : "property_get_int64",
+ "parameters" :
+ [
+ {
+ "referenced_type" : "_ZTIPKc"
+ },
+ {
+ "referenced_type" : "_ZTIl"
+ }
+ ],
+ "return_type" : "_ZTIl",
+ "source_file" : "system/core/libcutils/include/cutils/properties.h"
+ },
+ {
+ "function_name" : "property_list",
+ "linker_set_key" : "property_list",
+ "parameters" :
+ [
+ {
+ "referenced_type" : "_ZTIPFvPKcS0_PvE"
+ },
+ {
+ "referenced_type" : "_ZTIPv"
+ }
+ ],
+ "return_type" : "_ZTIi",
+ "source_file" : "system/core/libcutils/include/cutils/properties.h"
+ },
+ {
+ "function_name" : "property_set",
+ "linker_set_key" : "property_set",
+ "parameters" :
+ [
+ {
+ "referenced_type" : "_ZTIPKc"
+ },
+ {
+ "referenced_type" : "_ZTIPKc"
+ }
+ ],
+ "return_type" : "_ZTIi",
+ "source_file" : "system/core/libcutils/include/cutils/properties.h"
+ },
+ {
+ "function_name" : "record_stream_free",
+ "linker_set_key" : "record_stream_free",
+ "parameters" :
+ [
+ {
+ "referenced_type" : "_ZTIP12RecordStream"
+ }
+ ],
+ "return_type" : "_ZTIv",
+ "source_file" : "system/core/libcutils/include/cutils/record_stream.h"
+ },
+ {
+ "function_name" : "record_stream_get_next",
+ "linker_set_key" : "record_stream_get_next",
+ "parameters" :
+ [
+ {
+ "referenced_type" : "_ZTIP12RecordStream"
+ },
+ {
+ "referenced_type" : "_ZTIPPv"
+ },
+ {
+ "referenced_type" : "_ZTIPm"
+ }
+ ],
+ "return_type" : "_ZTIi",
+ "source_file" : "system/core/libcutils/include/cutils/record_stream.h"
+ },
+ {
+ "function_name" : "record_stream_new",
+ "linker_set_key" : "record_stream_new",
+ "parameters" :
+ [
+ {
+ "referenced_type" : "_ZTIi"
+ },
+ {
+ "referenced_type" : "_ZTIm"
+ }
+ ],
+ "return_type" : "_ZTIP12RecordStream",
+ "source_file" : "system/core/libcutils/include/cutils/record_stream.h"
+ },
+ {
+ "function_name" : "socket_close",
+ "linker_set_key" : "socket_close",
+ "parameters" :
+ [
+ {
+ "referenced_type" : "_ZTIi"
+ }
+ ],
+ "return_type" : "_ZTIi",
+ "source_file" : "system/core/libcutils/include/cutils/sockets.h"
+ },
+ {
+ "function_name" : "socket_get_local_port",
+ "linker_set_key" : "socket_get_local_port",
+ "parameters" :
+ [
+ {
+ "referenced_type" : "_ZTIi"
+ }
+ ],
+ "return_type" : "_ZTIi",
+ "source_file" : "system/core/libcutils/include/cutils/sockets.h"
+ },
+ {
+ "function_name" : "socket_inaddr_any_server",
+ "linker_set_key" : "socket_inaddr_any_server",
+ "parameters" :
+ [
+ {
+ "referenced_type" : "_ZTIi"
+ },
+ {
+ "referenced_type" : "_ZTIi"
+ }
+ ],
+ "return_type" : "_ZTIi",
+ "source_file" : "system/core/libcutils/include/cutils/sockets.h"
+ },
+ {
+ "function_name" : "socket_local_client",
+ "linker_set_key" : "socket_local_client",
+ "parameters" :
+ [
+ {
+ "referenced_type" : "_ZTIPKc"
+ },
+ {
+ "referenced_type" : "_ZTIi"
+ },
+ {
+ "referenced_type" : "_ZTIi"
+ }
+ ],
+ "return_type" : "_ZTIi",
+ "source_file" : "system/core/libcutils/include/cutils/sockets.h"
+ },
+ {
+ "function_name" : "socket_local_client_connect",
+ "linker_set_key" : "socket_local_client_connect",
+ "parameters" :
+ [
+ {
+ "referenced_type" : "_ZTIi"
+ },
+ {
+ "referenced_type" : "_ZTIPKc"
+ },
+ {
+ "referenced_type" : "_ZTIi"
+ },
+ {
+ "referenced_type" : "_ZTIi"
+ }
+ ],
+ "return_type" : "_ZTIi",
+ "source_file" : "system/core/libcutils/include/cutils/sockets.h"
+ },
+ {
+ "function_name" : "socket_local_server",
+ "linker_set_key" : "socket_local_server",
+ "parameters" :
+ [
+ {
+ "referenced_type" : "_ZTIPKc"
+ },
+ {
+ "referenced_type" : "_ZTIi"
+ },
+ {
+ "referenced_type" : "_ZTIi"
+ }
+ ],
+ "return_type" : "_ZTIi",
+ "source_file" : "system/core/libcutils/include/cutils/sockets.h"
+ },
+ {
+ "function_name" : "socket_local_server_bind",
+ "linker_set_key" : "socket_local_server_bind",
+ "parameters" :
+ [
+ {
+ "referenced_type" : "_ZTIi"
+ },
+ {
+ "referenced_type" : "_ZTIPKc"
+ },
+ {
+ "referenced_type" : "_ZTIi"
+ }
+ ],
+ "return_type" : "_ZTIi",
+ "source_file" : "system/core/libcutils/include/cutils/sockets.h"
+ },
+ {
+ "function_name" : "socket_network_client",
+ "linker_set_key" : "socket_network_client",
+ "parameters" :
+ [
+ {
+ "referenced_type" : "_ZTIPKc"
+ },
+ {
+ "referenced_type" : "_ZTIi"
+ },
+ {
+ "referenced_type" : "_ZTIi"
+ }
+ ],
+ "return_type" : "_ZTIi",
+ "source_file" : "system/core/libcutils/include/cutils/sockets.h"
+ },
+ {
+ "function_name" : "socket_network_client_timeout",
+ "linker_set_key" : "socket_network_client_timeout",
+ "parameters" :
+ [
+ {
+ "referenced_type" : "_ZTIPKc"
+ },
+ {
+ "referenced_type" : "_ZTIi"
+ },
+ {
+ "referenced_type" : "_ZTIi"
+ },
+ {
+ "referenced_type" : "_ZTIi"
+ },
+ {
+ "referenced_type" : "_ZTIPi"
+ }
+ ],
+ "return_type" : "_ZTIi",
+ "source_file" : "system/core/libcutils/include/cutils/sockets.h"
+ },
+ {
+ "function_name" : "socket_send_buffers",
+ "linker_set_key" : "socket_send_buffers",
+ "parameters" :
+ [
+ {
+ "referenced_type" : "_ZTIi"
+ },
+ {
+ "referenced_type" : "_ZTIPK22cutils_socket_buffer_t"
+ },
+ {
+ "referenced_type" : "_ZTIm"
+ }
+ ],
+ "return_type" : "_ZTIl",
+ "source_file" : "system/core/libcutils/include/cutils/sockets.h"
+ },
+ {
+ "function_name" : "str_parms_add_float",
+ "linker_set_key" : "str_parms_add_float",
+ "parameters" :
+ [
+ {
+ "referenced_type" : "_ZTIP9str_parms"
+ },
+ {
+ "referenced_type" : "_ZTIPKc"
+ },
+ {
+ "referenced_type" : "_ZTIf"
+ }
+ ],
+ "return_type" : "_ZTIi",
+ "source_file" : "system/core/libcutils/include/cutils/str_parms.h"
+ },
+ {
+ "function_name" : "str_parms_add_int",
+ "linker_set_key" : "str_parms_add_int",
+ "parameters" :
+ [
+ {
+ "referenced_type" : "_ZTIP9str_parms"
+ },
+ {
+ "referenced_type" : "_ZTIPKc"
+ },
+ {
+ "referenced_type" : "_ZTIi"
+ }
+ ],
+ "return_type" : "_ZTIi",
+ "source_file" : "system/core/libcutils/include/cutils/str_parms.h"
+ },
+ {
+ "function_name" : "str_parms_add_str",
+ "linker_set_key" : "str_parms_add_str",
+ "parameters" :
+ [
+ {
+ "referenced_type" : "_ZTIP9str_parms"
+ },
+ {
+ "referenced_type" : "_ZTIPKc"
+ },
+ {
+ "referenced_type" : "_ZTIPKc"
+ }
+ ],
+ "return_type" : "_ZTIi",
+ "source_file" : "system/core/libcutils/include/cutils/str_parms.h"
+ },
+ {
+ "function_name" : "str_parms_create",
+ "linker_set_key" : "str_parms_create",
+ "return_type" : "_ZTIP9str_parms",
+ "source_file" : "system/core/libcutils/include/cutils/str_parms.h"
+ },
+ {
+ "function_name" : "str_parms_create_str",
+ "linker_set_key" : "str_parms_create_str",
+ "parameters" :
+ [
+ {
+ "referenced_type" : "_ZTIPKc"
+ }
+ ],
+ "return_type" : "_ZTIP9str_parms",
+ "source_file" : "system/core/libcutils/include/cutils/str_parms.h"
+ },
+ {
+ "function_name" : "str_parms_del",
+ "linker_set_key" : "str_parms_del",
+ "parameters" :
+ [
+ {
+ "referenced_type" : "_ZTIP9str_parms"
+ },
+ {
+ "referenced_type" : "_ZTIPKc"
+ }
+ ],
+ "return_type" : "_ZTIv",
+ "source_file" : "system/core/libcutils/include/cutils/str_parms.h"
+ },
+ {
+ "function_name" : "str_parms_destroy",
+ "linker_set_key" : "str_parms_destroy",
+ "parameters" :
+ [
+ {
+ "referenced_type" : "_ZTIP9str_parms"
+ }
+ ],
+ "return_type" : "_ZTIv",
+ "source_file" : "system/core/libcutils/include/cutils/str_parms.h"
+ },
+ {
+ "function_name" : "str_parms_dump",
+ "linker_set_key" : "str_parms_dump",
+ "parameters" :
+ [
+ {
+ "referenced_type" : "_ZTIP9str_parms"
+ }
+ ],
+ "return_type" : "_ZTIv",
+ "source_file" : "system/core/libcutils/include/cutils/str_parms.h"
+ },
+ {
+ "function_name" : "str_parms_get_float",
+ "linker_set_key" : "str_parms_get_float",
+ "parameters" :
+ [
+ {
+ "referenced_type" : "_ZTIP9str_parms"
+ },
+ {
+ "referenced_type" : "_ZTIPKc"
+ },
+ {
+ "referenced_type" : "_ZTIPf"
+ }
+ ],
+ "return_type" : "_ZTIi",
+ "source_file" : "system/core/libcutils/include/cutils/str_parms.h"
+ },
+ {
+ "function_name" : "str_parms_get_int",
+ "linker_set_key" : "str_parms_get_int",
+ "parameters" :
+ [
+ {
+ "referenced_type" : "_ZTIP9str_parms"
+ },
+ {
+ "referenced_type" : "_ZTIPKc"
+ },
+ {
+ "referenced_type" : "_ZTIPi"
+ }
+ ],
+ "return_type" : "_ZTIi",
+ "source_file" : "system/core/libcutils/include/cutils/str_parms.h"
+ },
+ {
+ "function_name" : "str_parms_get_str",
+ "linker_set_key" : "str_parms_get_str",
+ "parameters" :
+ [
+ {
+ "referenced_type" : "_ZTIP9str_parms"
+ },
+ {
+ "referenced_type" : "_ZTIPKc"
+ },
+ {
+ "referenced_type" : "_ZTIPc"
+ },
+ {
+ "referenced_type" : "_ZTIi"
+ }
+ ],
+ "return_type" : "_ZTIi",
+ "source_file" : "system/core/libcutils/include/cutils/str_parms.h"
+ },
+ {
+ "function_name" : "str_parms_has_key",
+ "linker_set_key" : "str_parms_has_key",
+ "parameters" :
+ [
+ {
+ "referenced_type" : "_ZTIP9str_parms"
+ },
+ {
+ "referenced_type" : "_ZTIPKc"
+ }
+ ],
+ "return_type" : "_ZTIi",
+ "source_file" : "system/core/libcutils/include/cutils/str_parms.h"
+ },
+ {
+ "function_name" : "str_parms_to_str",
+ "linker_set_key" : "str_parms_to_str",
+ "parameters" :
+ [
+ {
+ "referenced_type" : "_ZTIP9str_parms"
+ }
+ ],
+ "return_type" : "_ZTIPc",
+ "source_file" : "system/core/libcutils/include/cutils/str_parms.h"
+ },
+ {
+ "function_name" : "uevent_kernel_multicast_recv",
+ "linker_set_key" : "uevent_kernel_multicast_recv",
+ "parameters" :
+ [
+ {
+ "referenced_type" : "_ZTIi"
+ },
+ {
+ "referenced_type" : "_ZTIPv"
+ },
+ {
+ "referenced_type" : "_ZTIm"
+ }
+ ],
+ "return_type" : "_ZTIl",
+ "source_file" : "system/core/libcutils/include/cutils/uevent.h"
+ },
+ {
+ "function_name" : "uevent_kernel_multicast_uid_recv",
+ "linker_set_key" : "uevent_kernel_multicast_uid_recv",
+ "parameters" :
+ [
+ {
+ "referenced_type" : "_ZTIi"
+ },
+ {
+ "referenced_type" : "_ZTIPv"
+ },
+ {
+ "referenced_type" : "_ZTIm"
+ },
+ {
+ "referenced_type" : "_ZTIPj"
+ }
+ ],
+ "return_type" : "_ZTIl",
+ "source_file" : "system/core/libcutils/include/cutils/uevent.h"
+ },
+ {
+ "function_name" : "uevent_kernel_recv",
+ "linker_set_key" : "uevent_kernel_recv",
+ "parameters" :
+ [
+ {
+ "referenced_type" : "_ZTIi"
+ },
+ {
+ "referenced_type" : "_ZTIPv"
+ },
+ {
+ "referenced_type" : "_ZTIm"
+ },
+ {
+ "referenced_type" : "_ZTIb"
+ },
+ {
+ "referenced_type" : "_ZTIPj"
+ }
+ ],
+ "return_type" : "_ZTIl",
+ "source_file" : "system/core/libcutils/include/cutils/uevent.h"
+ },
+ {
+ "function_name" : "uevent_open_socket",
+ "linker_set_key" : "uevent_open_socket",
+ "parameters" :
+ [
+ {
+ "referenced_type" : "_ZTIi"
+ },
+ {
+ "referenced_type" : "_ZTIb"
+ }
+ ],
+ "return_type" : "_ZTIi",
+ "source_file" : "system/core/libcutils/include/cutils/uevent.h"
+ }
+ ],
+ "global_vars" :
+ [
+ {
+ "linker_set_key" : "atrace_enabled_tags",
+ "name" : "atrace_enabled_tags",
+ "referenced_type" : "_ZTIm",
+ "source_file" : "system/core/libcutils/include/cutils/trace.h"
+ },
+ {
+ "linker_set_key" : "atrace_is_ready",
+ "name" : "atrace_is_ready",
+ "referenced_type" : "_ZTINSt3__16atomicIbEE",
+ "source_file" : "system/core/libcutils/include/cutils/trace.h"
+ },
+ {
+ "linker_set_key" : "atrace_marker_fd",
+ "name" : "atrace_marker_fd",
+ "referenced_type" : "_ZTIi",
+ "source_file" : "system/core/libcutils/include/cutils/trace.h"
+ }
+ ],
+ "lvalue_reference_types" : [],
+ "pointer_types" :
+ [
+ {
+ "alignment" : 8,
+ "linker_set_key" : "_ZTIP12IoSchedClass",
+ "name" : "IoSchedClass *",
+ "referenced_type" : "_ZTI12IoSchedClass",
+ "self_type" : "_ZTIP12IoSchedClass",
+ "size" : 8,
+ "source_file" : "system/core/libcutils/include/cutils/iosched_policy.h"
+ },
+ {
+ "alignment" : 8,
+ "linker_set_key" : "_ZTIP12RecordStream",
+ "name" : "RecordStream *",
+ "referenced_type" : "_ZTI12RecordStream",
+ "self_type" : "_ZTIP12RecordStream",
+ "size" : 8,
+ "source_file" : "system/core/libcutils/include/cutils/record_stream.h"
+ },
+ {
+ "alignment" : 8,
+ "linker_set_key" : "_ZTIP13native_handle",
+ "name" : "native_handle *",
+ "referenced_type" : "_ZTI13native_handle",
+ "self_type" : "_ZTIP13native_handle",
+ "size" : 8,
+ "source_file" : "system/core/libcutils/include/cutils/native_handle.h"
+ },
+ {
+ "alignment" : 8,
+ "linker_set_key" : "_ZTIP5cnode",
+ "name" : "cnode *",
+ "referenced_type" : "_ZTI5cnode",
+ "self_type" : "_ZTIP5cnode",
+ "size" : 8,
+ "source_file" : "system/core/libcutils/include/cutils/config_utils.h"
+ },
+ {
+ "alignment" : 8,
+ "linker_set_key" : "_ZTIP7Hashmap",
+ "name" : "Hashmap *",
+ "referenced_type" : "_ZTI7Hashmap",
+ "self_type" : "_ZTIP7Hashmap",
+ "size" : 8,
+ "source_file" : "system/core/libcutils/include/cutils/hashmap.h"
+ },
+ {
+ "alignment" : 8,
+ "linker_set_key" : "_ZTIP9str_parms",
+ "name" : "str_parms *",
+ "referenced_type" : "_ZTI9str_parms",
+ "self_type" : "_ZTIP9str_parms",
+ "size" : 8,
+ "source_file" : "system/core/libcutils/include/cutils/str_parms.h"
+ },
+ {
+ "alignment" : 8,
+ "linker_set_key" : "_ZTIPFbPvS_E",
+ "name" : "bool (*)(void *, void *)",
+ "referenced_type" : "_ZTIFbPvS_E",
+ "self_type" : "_ZTIPFbPvS_E",
+ "size" : 8,
+ "source_file" : "system/core/libcutils/include/cutils/hashmap.h"
+ },
+ {
+ "alignment" : 8,
+ "linker_set_key" : "_ZTIPFbPvS_S_E",
+ "name" : "bool (*)(void *, void *, void *)",
+ "referenced_type" : "_ZTIFbPvS_S_E",
+ "self_type" : "_ZTIPFbPvS_S_E",
+ "size" : 8,
+ "source_file" : "system/core/libcutils/include/cutils/hashmap.h"
+ },
+ {
+ "alignment" : 8,
+ "linker_set_key" : "_ZTIPFiPvE",
+ "name" : "int (*)(void *)",
+ "referenced_type" : "_ZTIFiPvE",
+ "self_type" : "_ZTIPFiPvE",
+ "size" : 8,
+ "source_file" : "system/core/libcutils/include/cutils/hashmap.h"
+ },
+ {
+ "alignment" : 8,
+ "linker_set_key" : "_ZTIPFvPKcS0_PvE",
+ "name" : "void (*)(const char *, const char *, void *)",
+ "referenced_type" : "_ZTIFvPKcS0_PvE",
+ "self_type" : "_ZTIPFvPKcS0_PvE",
+ "size" : 8,
+ "source_file" : "system/core/libcutils/include/cutils/properties.h"
+ },
+ {
+ "alignment" : 8,
+ "linker_set_key" : "_ZTIPK13native_handle",
+ "name" : "const native_handle *",
+ "referenced_type" : "_ZTIK13native_handle",
+ "self_type" : "_ZTIPK13native_handle",
+ "size" : 8,
+ "source_file" : "system/core/libcutils/include/cutils/native_handle.h"
+ },
+ {
+ "alignment" : 8,
+ "linker_set_key" : "_ZTIPK22cutils_socket_buffer_t",
+ "name" : "const cutils_socket_buffer_t *",
+ "referenced_type" : "_ZTIK22cutils_socket_buffer_t",
+ "self_type" : "_ZTIPK22cutils_socket_buffer_t",
+ "size" : 8,
+ "source_file" : "system/core/libcutils/include/cutils/sockets.h"
+ },
+ {
+ "alignment" : 8,
+ "linker_set_key" : "_ZTIPK5iovec",
+ "name" : "const iovec *",
+ "referenced_type" : "_ZTIK5iovec",
+ "self_type" : "_ZTIPK5iovec",
+ "size" : 8,
+ "source_file" : "system/core/libcutils/include/cutils/klog.h"
+ },
+ {
+ "alignment" : 8,
+ "linker_set_key" : "_ZTIPKc",
+ "name" : "const char *",
+ "referenced_type" : "_ZTIKc",
+ "self_type" : "_ZTIPKc",
+ "size" : 8,
+ "source_file" : "system/core/libcutils/include/cutils/config_utils.h"
+ },
+ {
+ "alignment" : 8,
+ "linker_set_key" : "_ZTIPKv",
+ "name" : "const void *",
+ "referenced_type" : "_ZTIKv",
+ "self_type" : "_ZTIPKv",
+ "size" : 8,
+ "source_file" : "system/core/libcutils/include/cutils/sockets.h"
+ },
+ {
+ "alignment" : 8,
+ "linker_set_key" : "_ZTIPPv",
+ "name" : "void **",
+ "referenced_type" : "_ZTIPv",
+ "self_type" : "_ZTIPPv",
+ "size" : 8,
+ "source_file" : "system/core/libcutils/include/cutils/record_stream.h"
+ },
+ {
+ "alignment" : 8,
+ "linker_set_key" : "_ZTIPc",
+ "name" : "char *",
+ "referenced_type" : "_ZTIc",
+ "self_type" : "_ZTIPc",
+ "size" : 8,
+ "source_file" : "system/core/libcutils/include/cutils/config_utils.h"
+ },
+ {
+ "alignment" : 8,
+ "linker_set_key" : "_ZTIPf",
+ "name" : "float *",
+ "referenced_type" : "_ZTIf",
+ "self_type" : "_ZTIPf",
+ "size" : 8,
+ "source_file" : "system/core/libcutils/include/cutils/str_parms.h"
+ },
+ {
+ "alignment" : 8,
+ "linker_set_key" : "_ZTIPi",
+ "name" : "int *",
+ "referenced_type" : "_ZTIi",
+ "self_type" : "_ZTIPi",
+ "size" : 8,
+ "source_file" : "system/core/libcutils/include/cutils/iosched_policy.h"
+ },
+ {
+ "alignment" : 8,
+ "linker_set_key" : "_ZTIPj",
+ "name" : "unsigned int *",
+ "referenced_type" : "_ZTIj",
+ "self_type" : "_ZTIPj",
+ "size" : 8,
+ "source_file" : "system/core/libcutils/include/cutils/misc.h"
+ },
+ {
+ "alignment" : 8,
+ "linker_set_key" : "_ZTIPm",
+ "name" : "unsigned long *",
+ "referenced_type" : "_ZTIm",
+ "self_type" : "_ZTIPm",
+ "size" : 8,
+ "source_file" : "system/core/libcutils/include/cutils/record_stream.h"
+ },
+ {
+ "alignment" : 8,
+ "linker_set_key" : "_ZTIPv",
+ "name" : "void *",
+ "referenced_type" : "_ZTIv",
+ "self_type" : "_ZTIPv",
+ "size" : 8,
+ "source_file" : "system/core/libcutils/include/cutils/misc.h"
+ }
+ ],
+ "qualified_types" :
+ [
+ {
+ "alignment" : 4,
+ "is_const" : true,
+ "linker_set_key" : "_ZTIK13native_handle",
+ "name" : "const native_handle",
+ "referenced_type" : "_ZTI13native_handle",
+ "self_type" : "_ZTIK13native_handle",
+ "size" : 12,
+ "source_file" : "system/core/libcutils/include/cutils/native_handle.h"
+ },
+ {
+ "alignment" : 8,
+ "is_const" : true,
+ "linker_set_key" : "_ZTIK22cutils_socket_buffer_t",
+ "name" : "const cutils_socket_buffer_t",
+ "referenced_type" : "_ZTI22cutils_socket_buffer_t",
+ "self_type" : "_ZTIK22cutils_socket_buffer_t",
+ "size" : 16,
+ "source_file" : "system/core/libcutils/include/cutils/sockets.h"
+ },
+ {
+ "alignment" : 8,
+ "is_const" : true,
+ "linker_set_key" : "_ZTIK5iovec",
+ "name" : "const iovec",
+ "referenced_type" : "_ZTI5iovec",
+ "self_type" : "_ZTIK5iovec",
+ "size" : 16,
+ "source_file" : "system/core/libcutils/include/cutils/klog.h"
+ },
+ {
+ "alignment" : 1,
+ "is_const" : true,
+ "linker_set_key" : "_ZTIKc",
+ "name" : "const char",
+ "referenced_type" : "_ZTIc",
+ "self_type" : "_ZTIKc",
+ "size" : 1,
+ "source_file" : "system/core/libcutils/include/cutils/config_utils.h"
+ },
+ {
+ "is_const" : true,
+ "linker_set_key" : "_ZTIKv",
+ "name" : "const void",
+ "referenced_type" : "_ZTIv",
+ "self_type" : "_ZTIKv",
+ "source_file" : "system/core/libcutils/include/cutils/sockets.h"
+ }
+ ],
+ "record_types" :
+ [
+ {
+ "alignment" : 4,
+ "fields" :
+ [
+ {
+ "field_name" : "version",
+ "referenced_type" : "_ZTIi"
+ },
+ {
+ "field_name" : "numFds",
+ "field_offset" : 32,
+ "referenced_type" : "_ZTIi"
+ },
+ {
+ "field_name" : "numInts",
+ "field_offset" : 64,
+ "referenced_type" : "_ZTIi"
+ },
+ {
+ "field_name" : "data",
+ "field_offset" : 96,
+ "referenced_type" : "_ZTIA0_i"
+ }
+ ],
+ "linker_set_key" : "_ZTI13native_handle",
+ "name" : "native_handle",
+ "referenced_type" : "_ZTI13native_handle",
+ "self_type" : "_ZTI13native_handle",
+ "size" : 12,
+ "source_file" : "system/core/libcutils/include/cutils/native_handle.h"
+ },
+ {
+ "alignment" : 8,
+ "fields" :
+ [
+ {
+ "field_name" : "data",
+ "referenced_type" : "_ZTIPKv"
+ },
+ {
+ "field_name" : "length",
+ "field_offset" : 64,
+ "referenced_type" : "_ZTIm"
+ }
+ ],
+ "linker_set_key" : "_ZTI22cutils_socket_buffer_t",
+ "name" : "cutils_socket_buffer_t",
+ "referenced_type" : "_ZTI22cutils_socket_buffer_t",
+ "self_type" : "_ZTI22cutils_socket_buffer_t",
+ "size" : 16,
+ "source_file" : "system/core/libcutils/include/cutils/sockets.h"
+ },
+ {
+ "alignment" : 8,
+ "fields" :
+ [
+ {
+ "field_name" : "next",
+ "referenced_type" : "_ZTIP5cnode"
+ },
+ {
+ "field_name" : "first_child",
+ "field_offset" : 64,
+ "referenced_type" : "_ZTIP5cnode"
+ },
+ {
+ "field_name" : "last_child",
+ "field_offset" : 128,
+ "referenced_type" : "_ZTIP5cnode"
+ },
+ {
+ "field_name" : "name",
+ "field_offset" : 192,
+ "referenced_type" : "_ZTIPKc"
+ },
+ {
+ "field_name" : "value",
+ "field_offset" : 256,
+ "referenced_type" : "_ZTIPKc"
+ }
+ ],
+ "linker_set_key" : "_ZTI5cnode",
+ "name" : "cnode",
+ "referenced_type" : "_ZTI5cnode",
+ "self_type" : "_ZTI5cnode",
+ "size" : 40,
+ "source_file" : "system/core/libcutils/include/cutils/config_utils.h"
+ }
+ ],
+ "rvalue_reference_types" : []
+}
diff --git a/libcutils/abi-dumps/arm_arm64/source-based/libcutils.so.lsdump b/libcutils/abi-dumps/arm_arm64/source-based/libcutils.so.lsdump
new file mode 100644
index 0000000..f612fb9
--- /dev/null
+++ b/libcutils/abi-dumps/arm_arm64/source-based/libcutils.so.lsdump
@@ -0,0 +1,2700 @@
+{
+ "array_types" :
+ [
+ {
+ "alignment" : 4,
+ "linker_set_key" : "_ZTIA0_i",
+ "name" : "int[0]",
+ "referenced_type" : "_ZTIi",
+ "self_type" : "_ZTIA0_i",
+ "source_file" : "system/core/libcutils/include/cutils/native_handle.h"
+ }
+ ],
+ "builtin_types" :
+ [
+ {
+ "alignment" : 1,
+ "is_integral" : true,
+ "linker_set_key" : "_ZTIa",
+ "name" : "signed char",
+ "referenced_type" : "_ZTIa",
+ "self_type" : "_ZTIa",
+ "size" : 1
+ },
+ {
+ "alignment" : 1,
+ "is_integral" : true,
+ "is_unsigned" : true,
+ "linker_set_key" : "_ZTIb",
+ "name" : "bool",
+ "referenced_type" : "_ZTIb",
+ "self_type" : "_ZTIb",
+ "size" : 1
+ },
+ {
+ "alignment" : 1,
+ "is_integral" : true,
+ "is_unsigned" : true,
+ "linker_set_key" : "_ZTIc",
+ "name" : "char",
+ "referenced_type" : "_ZTIc",
+ "self_type" : "_ZTIc",
+ "size" : 1
+ },
+ {
+ "alignment" : 4,
+ "linker_set_key" : "_ZTIf",
+ "name" : "float",
+ "referenced_type" : "_ZTIf",
+ "self_type" : "_ZTIf",
+ "size" : 4
+ },
+ {
+ "alignment" : 4,
+ "is_integral" : true,
+ "linker_set_key" : "_ZTIi",
+ "name" : "int",
+ "referenced_type" : "_ZTIi",
+ "self_type" : "_ZTIi",
+ "size" : 4
+ },
+ {
+ "alignment" : 4,
+ "is_integral" : true,
+ "is_unsigned" : true,
+ "linker_set_key" : "_ZTIj",
+ "name" : "unsigned int",
+ "referenced_type" : "_ZTIj",
+ "self_type" : "_ZTIj",
+ "size" : 4
+ },
+ {
+ "alignment" : 2,
+ "is_integral" : true,
+ "is_unsigned" : true,
+ "linker_set_key" : "_ZTIt",
+ "name" : "unsigned short",
+ "referenced_type" : "_ZTIt",
+ "self_type" : "_ZTIt",
+ "size" : 2
+ },
+ {
+ "linker_set_key" : "_ZTIv",
+ "name" : "void",
+ "referenced_type" : "_ZTIv",
+ "self_type" : "_ZTIv"
+ },
+ {
+ "alignment" : 8,
+ "is_integral" : true,
+ "linker_set_key" : "_ZTIx",
+ "name" : "long long",
+ "referenced_type" : "_ZTIx",
+ "self_type" : "_ZTIx",
+ "size" : 8
+ },
+ {
+ "alignment" : 8,
+ "is_integral" : true,
+ "is_unsigned" : true,
+ "linker_set_key" : "_ZTIy",
+ "name" : "unsigned long long",
+ "referenced_type" : "_ZTIy",
+ "self_type" : "_ZTIy",
+ "size" : 8
+ }
+ ],
+ "elf_functions" :
+ [
+ {
+ "name" : "_Z23socket_make_sockaddr_unPKciP11sockaddr_unPi"
+ },
+ {
+ "binding" : "weak",
+ "name" : "_ZN7android4base4TrimIRNSt3__112basic_stringIcNS2_11char_traitsIcEENS2_9allocatorIcEEEEEES8_OT_"
+ },
+ {
+ "binding" : "weak",
+ "name" : "_ZNSt3__113basic_filebufIcNS_11char_traitsIcEEE4syncEv"
+ },
+ {
+ "binding" : "weak",
+ "name" : "_ZNSt3__113basic_filebufIcNS_11char_traitsIcEEE5imbueERKNS_6localeE"
+ },
+ {
+ "binding" : "weak",
+ "name" : "_ZNSt3__113basic_filebufIcNS_11char_traitsIcEEE6setbufEPci"
+ },
+ {
+ "binding" : "weak",
+ "name" : "_ZNSt3__113basic_filebufIcNS_11char_traitsIcEEE7seekoffExNS_8ios_base7seekdirEj"
+ },
+ {
+ "binding" : "weak",
+ "name" : "_ZNSt3__113basic_filebufIcNS_11char_traitsIcEEE7seekposENS_4fposI9mbstate_tEEj"
+ },
+ {
+ "binding" : "weak",
+ "name" : "_ZNSt3__113basic_filebufIcNS_11char_traitsIcEEE8overflowEi"
+ },
+ {
+ "binding" : "weak",
+ "name" : "_ZNSt3__113basic_filebufIcNS_11char_traitsIcEEE9pbackfailEi"
+ },
+ {
+ "binding" : "weak",
+ "name" : "_ZNSt3__113basic_filebufIcNS_11char_traitsIcEEE9underflowEv"
+ },
+ {
+ "binding" : "weak",
+ "name" : "_ZNSt3__113basic_filebufIcNS_11char_traitsIcEEEC2Ev"
+ },
+ {
+ "binding" : "weak",
+ "name" : "_ZNSt3__113basic_filebufIcNS_11char_traitsIcEEED0Ev"
+ },
+ {
+ "binding" : "weak",
+ "name" : "_ZNSt3__113basic_filebufIcNS_11char_traitsIcEEED2Ev"
+ },
+ {
+ "binding" : "weak",
+ "name" : "_ZNSt3__124__put_character_sequenceIcNS_11char_traitsIcEEEERNS_13basic_ostreamIT_T0_EES7_PKS4_j"
+ },
+ {
+ "binding" : "weak",
+ "name" : "_ZNSt3__16vectorI5EntryNS_9allocatorIS1_EEE24__emplace_back_slow_pathIJS1_EEEvDpOT_"
+ },
+ {
+ "binding" : "weak",
+ "name" : "_ZNSt3__17getlineIcNS_11char_traitsIcEENS_9allocatorIcEEEERNS_13basic_istreamIT_T0_EES9_RNS_12basic_stringIS6_S7_T1_EES6_"
+ },
+ {
+ "binding" : "weak",
+ "name" : "_ZNSt3__1lsINS_11char_traitsIcEEEERNS_13basic_ostreamIcT_EES6_PKc"
+ },
+ {
+ "name" : "android_get_control_file"
+ },
+ {
+ "name" : "android_get_control_socket"
+ },
+ {
+ "name" : "android_get_ioprio"
+ },
+ {
+ "name" : "android_reboot"
+ },
+ {
+ "name" : "android_set_ioprio"
+ },
+ {
+ "name" : "ashmem_create_region"
+ },
+ {
+ "name" : "ashmem_get_size_region"
+ },
+ {
+ "name" : "ashmem_pin_region"
+ },
+ {
+ "name" : "ashmem_set_prot_region"
+ },
+ {
+ "name" : "ashmem_unpin_region"
+ },
+ {
+ "name" : "ashmem_valid"
+ },
+ {
+ "name" : "atrace_async_begin_body"
+ },
+ {
+ "name" : "atrace_async_end_body"
+ },
+ {
+ "name" : "atrace_async_for_track_begin_body"
+ },
+ {
+ "name" : "atrace_async_for_track_end_body"
+ },
+ {
+ "name" : "atrace_begin_body"
+ },
+ {
+ "name" : "atrace_end_body"
+ },
+ {
+ "name" : "atrace_get_enabled_tags"
+ },
+ {
+ "name" : "atrace_init"
+ },
+ {
+ "name" : "atrace_instant_body"
+ },
+ {
+ "name" : "atrace_instant_for_track_body"
+ },
+ {
+ "name" : "atrace_int64_body"
+ },
+ {
+ "name" : "atrace_int_body"
+ },
+ {
+ "name" : "atrace_set_tracing_enabled"
+ },
+ {
+ "name" : "atrace_setup"
+ },
+ {
+ "name" : "atrace_update_tags"
+ },
+ {
+ "name" : "canned_fs_config"
+ },
+ {
+ "name" : "config_bool"
+ },
+ {
+ "name" : "config_find"
+ },
+ {
+ "name" : "config_free"
+ },
+ {
+ "name" : "config_load"
+ },
+ {
+ "name" : "config_load_file"
+ },
+ {
+ "name" : "config_node"
+ },
+ {
+ "name" : "config_set"
+ },
+ {
+ "name" : "config_str"
+ },
+ {
+ "name" : "fs_config"
+ },
+ {
+ "name" : "fs_mkdirs"
+ },
+ {
+ "name" : "fs_prepare_dir"
+ },
+ {
+ "name" : "fs_prepare_dir_strict"
+ },
+ {
+ "name" : "fs_prepare_file_strict"
+ },
+ {
+ "name" : "fs_read_atomic_int"
+ },
+ {
+ "name" : "fs_write_atomic_int"
+ },
+ {
+ "name" : "hashmapCreate"
+ },
+ {
+ "name" : "hashmapForEach"
+ },
+ {
+ "name" : "hashmapFree"
+ },
+ {
+ "name" : "hashmapGet"
+ },
+ {
+ "name" : "hashmapHash"
+ },
+ {
+ "name" : "hashmapLock"
+ },
+ {
+ "name" : "hashmapPut"
+ },
+ {
+ "name" : "hashmapRemove"
+ },
+ {
+ "name" : "hashmapUnlock"
+ },
+ {
+ "name" : "klog_set_level"
+ },
+ {
+ "name" : "klog_write"
+ },
+ {
+ "name" : "klog_writev"
+ },
+ {
+ "name" : "load_canned_fs_config"
+ },
+ {
+ "name" : "load_file"
+ },
+ {
+ "name" : "multiuser_convert_sdk_sandbox_to_app_uid"
+ },
+ {
+ "name" : "multiuser_get_app_id"
+ },
+ {
+ "name" : "multiuser_get_cache_gid"
+ },
+ {
+ "name" : "multiuser_get_ext_cache_gid"
+ },
+ {
+ "name" : "multiuser_get_ext_gid"
+ },
+ {
+ "name" : "multiuser_get_sdk_sandbox_uid"
+ },
+ {
+ "name" : "multiuser_get_shared_app_gid"
+ },
+ {
+ "name" : "multiuser_get_shared_gid"
+ },
+ {
+ "name" : "multiuser_get_uid"
+ },
+ {
+ "name" : "multiuser_get_user_id"
+ },
+ {
+ "name" : "native_handle_clone"
+ },
+ {
+ "name" : "native_handle_close"
+ },
+ {
+ "name" : "native_handle_close_with_tag"
+ },
+ {
+ "name" : "native_handle_create"
+ },
+ {
+ "name" : "native_handle_delete"
+ },
+ {
+ "name" : "native_handle_init"
+ },
+ {
+ "name" : "native_handle_set_fdsan_tag"
+ },
+ {
+ "name" : "native_handle_unset_fdsan_tag"
+ },
+ {
+ "name" : "partition_wiped"
+ },
+ {
+ "name" : "property_get"
+ },
+ {
+ "name" : "property_get_bool"
+ },
+ {
+ "name" : "property_get_int32"
+ },
+ {
+ "name" : "property_get_int64"
+ },
+ {
+ "name" : "property_list"
+ },
+ {
+ "name" : "property_set"
+ },
+ {
+ "name" : "record_stream_free"
+ },
+ {
+ "name" : "record_stream_get_next"
+ },
+ {
+ "name" : "record_stream_new"
+ },
+ {
+ "name" : "socket_close"
+ },
+ {
+ "name" : "socket_get_local_port"
+ },
+ {
+ "name" : "socket_inaddr_any_server"
+ },
+ {
+ "name" : "socket_local_client"
+ },
+ {
+ "name" : "socket_local_client_connect"
+ },
+ {
+ "name" : "socket_local_server"
+ },
+ {
+ "name" : "socket_local_server_bind"
+ },
+ {
+ "name" : "socket_network_client"
+ },
+ {
+ "name" : "socket_network_client_timeout"
+ },
+ {
+ "name" : "socket_send_buffers"
+ },
+ {
+ "name" : "str_parms_add_float"
+ },
+ {
+ "name" : "str_parms_add_int"
+ },
+ {
+ "name" : "str_parms_add_str"
+ },
+ {
+ "name" : "str_parms_create"
+ },
+ {
+ "name" : "str_parms_create_str"
+ },
+ {
+ "name" : "str_parms_del"
+ },
+ {
+ "name" : "str_parms_destroy"
+ },
+ {
+ "name" : "str_parms_dump"
+ },
+ {
+ "name" : "str_parms_get_float"
+ },
+ {
+ "name" : "str_parms_get_int"
+ },
+ {
+ "name" : "str_parms_get_str"
+ },
+ {
+ "name" : "str_parms_has_key"
+ },
+ {
+ "name" : "str_parms_to_str"
+ },
+ {
+ "name" : "uevent_kernel_multicast_recv"
+ },
+ {
+ "name" : "uevent_kernel_multicast_uid_recv"
+ },
+ {
+ "name" : "uevent_kernel_recv"
+ },
+ {
+ "name" : "uevent_open_socket"
+ }
+ ],
+ "elf_objects" :
+ [
+ {
+ "binding" : "weak",
+ "name" : "_ZTCNSt3__114basic_ifstreamIcNS_11char_traitsIcEEEE0_NS_13basic_istreamIcS2_EE"
+ },
+ {
+ "binding" : "weak",
+ "name" : "_ZTTNSt3__114basic_ifstreamIcNS_11char_traitsIcEEEE"
+ },
+ {
+ "binding" : "weak",
+ "name" : "_ZTVNSt3__113basic_filebufIcNS_11char_traitsIcEEEE"
+ },
+ {
+ "binding" : "weak",
+ "name" : "_ZTVNSt3__114basic_ifstreamIcNS_11char_traitsIcEEEE"
+ },
+ {
+ "name" : "atrace_enabled_tags"
+ },
+ {
+ "name" : "atrace_is_ready"
+ },
+ {
+ "name" : "atrace_marker_fd"
+ }
+ ],
+ "enum_types" :
+ [
+ {
+ "alignment" : 4,
+ "enum_fields" :
+ [
+ {
+ "enum_field_value" : 0,
+ "name" : "IoSchedClass_NONE"
+ },
+ {
+ "enum_field_value" : 1,
+ "name" : "IoSchedClass_RT"
+ },
+ {
+ "enum_field_value" : 2,
+ "name" : "IoSchedClass_BE"
+ },
+ {
+ "enum_field_value" : 3,
+ "name" : "IoSchedClass_IDLE"
+ }
+ ],
+ "linker_set_key" : "_ZTI12IoSchedClass",
+ "name" : "IoSchedClass",
+ "referenced_type" : "_ZTI12IoSchedClass",
+ "self_type" : "_ZTI12IoSchedClass",
+ "size" : 4,
+ "source_file" : "system/core/libcutils/include/cutils/iosched_policy.h",
+ "underlying_type" : "_ZTIj"
+ }
+ ],
+ "function_types" :
+ [
+ {
+ "alignment" : 4,
+ "linker_set_key" : "_ZTIFbPvS_E",
+ "name" : "bool (void *, void *)",
+ "parameters" :
+ [
+ {
+ "referenced_type" : "_ZTIPv"
+ },
+ {
+ "referenced_type" : "_ZTIPv"
+ }
+ ],
+ "referenced_type" : "_ZTIFbPvS_E",
+ "return_type" : "_ZTIb",
+ "self_type" : "_ZTIFbPvS_E",
+ "source_file" : "system/core/libcutils/include/cutils/hashmap.h"
+ },
+ {
+ "alignment" : 4,
+ "linker_set_key" : "_ZTIFbPvS_S_E",
+ "name" : "bool (void *, void *, void *)",
+ "parameters" :
+ [
+ {
+ "referenced_type" : "_ZTIPv"
+ },
+ {
+ "referenced_type" : "_ZTIPv"
+ },
+ {
+ "referenced_type" : "_ZTIPv"
+ }
+ ],
+ "referenced_type" : "_ZTIFbPvS_S_E",
+ "return_type" : "_ZTIb",
+ "self_type" : "_ZTIFbPvS_S_E",
+ "source_file" : "system/core/libcutils/include/cutils/hashmap.h"
+ },
+ {
+ "alignment" : 4,
+ "linker_set_key" : "_ZTIFiPvE",
+ "name" : "int (void *)",
+ "parameters" :
+ [
+ {
+ "referenced_type" : "_ZTIPv"
+ }
+ ],
+ "referenced_type" : "_ZTIFiPvE",
+ "return_type" : "_ZTIi",
+ "self_type" : "_ZTIFiPvE",
+ "source_file" : "system/core/libcutils/include/cutils/hashmap.h"
+ },
+ {
+ "alignment" : 4,
+ "linker_set_key" : "_ZTIFvPKcS0_PvE",
+ "name" : "void (const char *, const char *, void *)",
+ "parameters" :
+ [
+ {
+ "referenced_type" : "_ZTIPKc"
+ },
+ {
+ "referenced_type" : "_ZTIPKc"
+ },
+ {
+ "referenced_type" : "_ZTIPv"
+ }
+ ],
+ "referenced_type" : "_ZTIFvPKcS0_PvE",
+ "return_type" : "_ZTIv",
+ "self_type" : "_ZTIFvPKcS0_PvE",
+ "source_file" : "system/core/libcutils/include/cutils/properties.h"
+ }
+ ],
+ "functions" :
+ [
+ {
+ "function_name" : "android_get_control_file",
+ "linker_set_key" : "android_get_control_file",
+ "parameters" :
+ [
+ {
+ "referenced_type" : "_ZTIPKc"
+ }
+ ],
+ "return_type" : "_ZTIi",
+ "source_file" : "system/core/libcutils/include/cutils/android_get_control_file.h"
+ },
+ {
+ "function_name" : "android_get_control_socket",
+ "linker_set_key" : "android_get_control_socket",
+ "parameters" :
+ [
+ {
+ "referenced_type" : "_ZTIPKc"
+ }
+ ],
+ "return_type" : "_ZTIi",
+ "source_file" : "system/core/libcutils/include/cutils/sockets.h"
+ },
+ {
+ "function_name" : "android_get_ioprio",
+ "linker_set_key" : "android_get_ioprio",
+ "parameters" :
+ [
+ {
+ "referenced_type" : "_ZTIi"
+ },
+ {
+ "referenced_type" : "_ZTIP12IoSchedClass"
+ },
+ {
+ "referenced_type" : "_ZTIPi"
+ }
+ ],
+ "return_type" : "_ZTIi",
+ "source_file" : "system/core/libcutils/include/cutils/iosched_policy.h"
+ },
+ {
+ "function_name" : "android_reboot",
+ "linker_set_key" : "android_reboot",
+ "parameters" :
+ [
+ {
+ "referenced_type" : "_ZTIj"
+ },
+ {
+ "referenced_type" : "_ZTIi"
+ },
+ {
+ "referenced_type" : "_ZTIPKc"
+ }
+ ],
+ "return_type" : "_ZTIi",
+ "source_file" : "system/core/libcutils/include/cutils/android_reboot.h"
+ },
+ {
+ "function_name" : "android_set_ioprio",
+ "linker_set_key" : "android_set_ioprio",
+ "parameters" :
+ [
+ {
+ "referenced_type" : "_ZTIi"
+ },
+ {
+ "referenced_type" : "_ZTI12IoSchedClass"
+ },
+ {
+ "referenced_type" : "_ZTIi"
+ }
+ ],
+ "return_type" : "_ZTIi",
+ "source_file" : "system/core/libcutils/include/cutils/iosched_policy.h"
+ },
+ {
+ "function_name" : "ashmem_create_region",
+ "linker_set_key" : "ashmem_create_region",
+ "parameters" :
+ [
+ {
+ "referenced_type" : "_ZTIPKc"
+ },
+ {
+ "referenced_type" : "_ZTIj"
+ }
+ ],
+ "return_type" : "_ZTIi",
+ "source_file" : "system/core/libcutils/include/cutils/ashmem.h"
+ },
+ {
+ "function_name" : "ashmem_get_size_region",
+ "linker_set_key" : "ashmem_get_size_region",
+ "parameters" :
+ [
+ {
+ "referenced_type" : "_ZTIi"
+ }
+ ],
+ "return_type" : "_ZTIi",
+ "source_file" : "system/core/libcutils/include/cutils/ashmem.h"
+ },
+ {
+ "function_name" : "ashmem_pin_region",
+ "linker_set_key" : "ashmem_pin_region",
+ "parameters" :
+ [
+ {
+ "referenced_type" : "_ZTIi"
+ },
+ {
+ "referenced_type" : "_ZTIj"
+ },
+ {
+ "referenced_type" : "_ZTIj"
+ }
+ ],
+ "return_type" : "_ZTIi",
+ "source_file" : "system/core/libcutils/include/cutils/ashmem.h"
+ },
+ {
+ "function_name" : "ashmem_set_prot_region",
+ "linker_set_key" : "ashmem_set_prot_region",
+ "parameters" :
+ [
+ {
+ "referenced_type" : "_ZTIi"
+ },
+ {
+ "referenced_type" : "_ZTIi"
+ }
+ ],
+ "return_type" : "_ZTIi",
+ "source_file" : "system/core/libcutils/include/cutils/ashmem.h"
+ },
+ {
+ "function_name" : "ashmem_unpin_region",
+ "linker_set_key" : "ashmem_unpin_region",
+ "parameters" :
+ [
+ {
+ "referenced_type" : "_ZTIi"
+ },
+ {
+ "referenced_type" : "_ZTIj"
+ },
+ {
+ "referenced_type" : "_ZTIj"
+ }
+ ],
+ "return_type" : "_ZTIi",
+ "source_file" : "system/core/libcutils/include/cutils/ashmem.h"
+ },
+ {
+ "function_name" : "ashmem_valid",
+ "linker_set_key" : "ashmem_valid",
+ "parameters" :
+ [
+ {
+ "referenced_type" : "_ZTIi"
+ }
+ ],
+ "return_type" : "_ZTIi",
+ "source_file" : "system/core/libcutils/include/cutils/ashmem.h"
+ },
+ {
+ "function_name" : "atrace_async_begin_body",
+ "linker_set_key" : "atrace_async_begin_body",
+ "parameters" :
+ [
+ {
+ "referenced_type" : "_ZTIPKc"
+ },
+ {
+ "referenced_type" : "_ZTIi"
+ }
+ ],
+ "return_type" : "_ZTIv",
+ "source_file" : "system/core/libcutils/include/cutils/trace.h"
+ },
+ {
+ "function_name" : "atrace_async_end_body",
+ "linker_set_key" : "atrace_async_end_body",
+ "parameters" :
+ [
+ {
+ "referenced_type" : "_ZTIPKc"
+ },
+ {
+ "referenced_type" : "_ZTIi"
+ }
+ ],
+ "return_type" : "_ZTIv",
+ "source_file" : "system/core/libcutils/include/cutils/trace.h"
+ },
+ {
+ "function_name" : "atrace_async_for_track_begin_body",
+ "linker_set_key" : "atrace_async_for_track_begin_body",
+ "parameters" :
+ [
+ {
+ "referenced_type" : "_ZTIPKc"
+ },
+ {
+ "referenced_type" : "_ZTIPKc"
+ },
+ {
+ "referenced_type" : "_ZTIi"
+ }
+ ],
+ "return_type" : "_ZTIv",
+ "source_file" : "system/core/libcutils/include/cutils/trace.h"
+ },
+ {
+ "function_name" : "atrace_async_for_track_end_body",
+ "linker_set_key" : "atrace_async_for_track_end_body",
+ "parameters" :
+ [
+ {
+ "referenced_type" : "_ZTIPKc"
+ },
+ {
+ "referenced_type" : "_ZTIi"
+ }
+ ],
+ "return_type" : "_ZTIv",
+ "source_file" : "system/core/libcutils/include/cutils/trace.h"
+ },
+ {
+ "function_name" : "atrace_begin_body",
+ "linker_set_key" : "atrace_begin_body",
+ "parameters" :
+ [
+ {
+ "referenced_type" : "_ZTIPKc"
+ }
+ ],
+ "return_type" : "_ZTIv",
+ "source_file" : "system/core/libcutils/include/cutils/trace.h"
+ },
+ {
+ "function_name" : "atrace_end_body",
+ "linker_set_key" : "atrace_end_body",
+ "return_type" : "_ZTIv",
+ "source_file" : "system/core/libcutils/include/cutils/trace.h"
+ },
+ {
+ "function_name" : "atrace_get_enabled_tags",
+ "linker_set_key" : "atrace_get_enabled_tags",
+ "return_type" : "_ZTIy",
+ "source_file" : "system/core/libcutils/include/cutils/trace.h"
+ },
+ {
+ "function_name" : "atrace_init",
+ "linker_set_key" : "atrace_init",
+ "return_type" : "_ZTIv",
+ "source_file" : "system/core/libcutils/include/cutils/trace.h"
+ },
+ {
+ "function_name" : "atrace_instant_body",
+ "linker_set_key" : "atrace_instant_body",
+ "parameters" :
+ [
+ {
+ "referenced_type" : "_ZTIPKc"
+ }
+ ],
+ "return_type" : "_ZTIv",
+ "source_file" : "system/core/libcutils/include/cutils/trace.h"
+ },
+ {
+ "function_name" : "atrace_instant_for_track_body",
+ "linker_set_key" : "atrace_instant_for_track_body",
+ "parameters" :
+ [
+ {
+ "referenced_type" : "_ZTIPKc"
+ },
+ {
+ "referenced_type" : "_ZTIPKc"
+ }
+ ],
+ "return_type" : "_ZTIv",
+ "source_file" : "system/core/libcutils/include/cutils/trace.h"
+ },
+ {
+ "function_name" : "atrace_int64_body",
+ "linker_set_key" : "atrace_int64_body",
+ "parameters" :
+ [
+ {
+ "referenced_type" : "_ZTIPKc"
+ },
+ {
+ "referenced_type" : "_ZTIx"
+ }
+ ],
+ "return_type" : "_ZTIv",
+ "source_file" : "system/core/libcutils/include/cutils/trace.h"
+ },
+ {
+ "function_name" : "atrace_int_body",
+ "linker_set_key" : "atrace_int_body",
+ "parameters" :
+ [
+ {
+ "referenced_type" : "_ZTIPKc"
+ },
+ {
+ "referenced_type" : "_ZTIi"
+ }
+ ],
+ "return_type" : "_ZTIv",
+ "source_file" : "system/core/libcutils/include/cutils/trace.h"
+ },
+ {
+ "function_name" : "atrace_set_tracing_enabled",
+ "linker_set_key" : "atrace_set_tracing_enabled",
+ "parameters" :
+ [
+ {
+ "referenced_type" : "_ZTIb"
+ }
+ ],
+ "return_type" : "_ZTIv",
+ "source_file" : "system/core/libcutils/include/cutils/trace.h"
+ },
+ {
+ "function_name" : "atrace_setup",
+ "linker_set_key" : "atrace_setup",
+ "return_type" : "_ZTIv",
+ "source_file" : "system/core/libcutils/include/cutils/trace.h"
+ },
+ {
+ "function_name" : "atrace_update_tags",
+ "linker_set_key" : "atrace_update_tags",
+ "return_type" : "_ZTIv",
+ "source_file" : "system/core/libcutils/include/cutils/trace.h"
+ },
+ {
+ "function_name" : "canned_fs_config",
+ "linker_set_key" : "canned_fs_config",
+ "parameters" :
+ [
+ {
+ "referenced_type" : "_ZTIPKc"
+ },
+ {
+ "referenced_type" : "_ZTIi"
+ },
+ {
+ "referenced_type" : "_ZTIPKc"
+ },
+ {
+ "referenced_type" : "_ZTIPj"
+ },
+ {
+ "referenced_type" : "_ZTIPj"
+ },
+ {
+ "referenced_type" : "_ZTIPj"
+ },
+ {
+ "referenced_type" : "_ZTIPy"
+ }
+ ],
+ "return_type" : "_ZTIv",
+ "source_file" : "system/core/libcutils/include/private/canned_fs_config.h"
+ },
+ {
+ "function_name" : "config_bool",
+ "linker_set_key" : "config_bool",
+ "parameters" :
+ [
+ {
+ "referenced_type" : "_ZTIP5cnode"
+ },
+ {
+ "referenced_type" : "_ZTIPKc"
+ },
+ {
+ "referenced_type" : "_ZTIi"
+ }
+ ],
+ "return_type" : "_ZTIi",
+ "source_file" : "system/core/libcutils/include/cutils/config_utils.h"
+ },
+ {
+ "function_name" : "config_find",
+ "linker_set_key" : "config_find",
+ "parameters" :
+ [
+ {
+ "referenced_type" : "_ZTIP5cnode"
+ },
+ {
+ "referenced_type" : "_ZTIPKc"
+ }
+ ],
+ "return_type" : "_ZTIP5cnode",
+ "source_file" : "system/core/libcutils/include/cutils/config_utils.h"
+ },
+ {
+ "function_name" : "config_free",
+ "linker_set_key" : "config_free",
+ "parameters" :
+ [
+ {
+ "referenced_type" : "_ZTIP5cnode"
+ }
+ ],
+ "return_type" : "_ZTIv",
+ "source_file" : "system/core/libcutils/include/cutils/config_utils.h"
+ },
+ {
+ "function_name" : "config_load",
+ "linker_set_key" : "config_load",
+ "parameters" :
+ [
+ {
+ "referenced_type" : "_ZTIP5cnode"
+ },
+ {
+ "referenced_type" : "_ZTIPc"
+ }
+ ],
+ "return_type" : "_ZTIv",
+ "source_file" : "system/core/libcutils/include/cutils/config_utils.h"
+ },
+ {
+ "function_name" : "config_load_file",
+ "linker_set_key" : "config_load_file",
+ "parameters" :
+ [
+ {
+ "referenced_type" : "_ZTIP5cnode"
+ },
+ {
+ "referenced_type" : "_ZTIPKc"
+ }
+ ],
+ "return_type" : "_ZTIv",
+ "source_file" : "system/core/libcutils/include/cutils/config_utils.h"
+ },
+ {
+ "function_name" : "config_node",
+ "linker_set_key" : "config_node",
+ "parameters" :
+ [
+ {
+ "referenced_type" : "_ZTIPKc"
+ },
+ {
+ "referenced_type" : "_ZTIPKc"
+ }
+ ],
+ "return_type" : "_ZTIP5cnode",
+ "source_file" : "system/core/libcutils/include/cutils/config_utils.h"
+ },
+ {
+ "function_name" : "config_set",
+ "linker_set_key" : "config_set",
+ "parameters" :
+ [
+ {
+ "referenced_type" : "_ZTIP5cnode"
+ },
+ {
+ "referenced_type" : "_ZTIPKc"
+ },
+ {
+ "referenced_type" : "_ZTIPKc"
+ }
+ ],
+ "return_type" : "_ZTIv",
+ "source_file" : "system/core/libcutils/include/cutils/config_utils.h"
+ },
+ {
+ "function_name" : "config_str",
+ "linker_set_key" : "config_str",
+ "parameters" :
+ [
+ {
+ "referenced_type" : "_ZTIP5cnode"
+ },
+ {
+ "referenced_type" : "_ZTIPKc"
+ },
+ {
+ "referenced_type" : "_ZTIPKc"
+ }
+ ],
+ "return_type" : "_ZTIPKc",
+ "source_file" : "system/core/libcutils/include/cutils/config_utils.h"
+ },
+ {
+ "function_name" : "fs_config",
+ "linker_set_key" : "fs_config",
+ "parameters" :
+ [
+ {
+ "referenced_type" : "_ZTIPKc"
+ },
+ {
+ "referenced_type" : "_ZTIi"
+ },
+ {
+ "referenced_type" : "_ZTIPKc"
+ },
+ {
+ "referenced_type" : "_ZTIPj"
+ },
+ {
+ "referenced_type" : "_ZTIPj"
+ },
+ {
+ "referenced_type" : "_ZTIPj"
+ },
+ {
+ "referenced_type" : "_ZTIPy"
+ }
+ ],
+ "return_type" : "_ZTIv",
+ "source_file" : "system/core/libcutils/include/private/fs_config.h"
+ },
+ {
+ "function_name" : "fs_mkdirs",
+ "linker_set_key" : "fs_mkdirs",
+ "parameters" :
+ [
+ {
+ "referenced_type" : "_ZTIPKc"
+ },
+ {
+ "referenced_type" : "_ZTIt"
+ }
+ ],
+ "return_type" : "_ZTIi",
+ "source_file" : "system/core/libcutils/include/cutils/fs.h"
+ },
+ {
+ "function_name" : "fs_prepare_dir",
+ "linker_set_key" : "fs_prepare_dir",
+ "parameters" :
+ [
+ {
+ "referenced_type" : "_ZTIPKc"
+ },
+ {
+ "referenced_type" : "_ZTIt"
+ },
+ {
+ "referenced_type" : "_ZTIj"
+ },
+ {
+ "referenced_type" : "_ZTIj"
+ }
+ ],
+ "return_type" : "_ZTIi",
+ "source_file" : "system/core/libcutils/include/cutils/fs.h"
+ },
+ {
+ "function_name" : "fs_prepare_dir_strict",
+ "linker_set_key" : "fs_prepare_dir_strict",
+ "parameters" :
+ [
+ {
+ "referenced_type" : "_ZTIPKc"
+ },
+ {
+ "referenced_type" : "_ZTIt"
+ },
+ {
+ "referenced_type" : "_ZTIj"
+ },
+ {
+ "referenced_type" : "_ZTIj"
+ }
+ ],
+ "return_type" : "_ZTIi",
+ "source_file" : "system/core/libcutils/include/cutils/fs.h"
+ },
+ {
+ "function_name" : "fs_prepare_file_strict",
+ "linker_set_key" : "fs_prepare_file_strict",
+ "parameters" :
+ [
+ {
+ "referenced_type" : "_ZTIPKc"
+ },
+ {
+ "referenced_type" : "_ZTIt"
+ },
+ {
+ "referenced_type" : "_ZTIj"
+ },
+ {
+ "referenced_type" : "_ZTIj"
+ }
+ ],
+ "return_type" : "_ZTIi",
+ "source_file" : "system/core/libcutils/include/cutils/fs.h"
+ },
+ {
+ "function_name" : "fs_read_atomic_int",
+ "linker_set_key" : "fs_read_atomic_int",
+ "parameters" :
+ [
+ {
+ "referenced_type" : "_ZTIPKc"
+ },
+ {
+ "referenced_type" : "_ZTIPi"
+ }
+ ],
+ "return_type" : "_ZTIi",
+ "source_file" : "system/core/libcutils/include/cutils/fs.h"
+ },
+ {
+ "function_name" : "fs_write_atomic_int",
+ "linker_set_key" : "fs_write_atomic_int",
+ "parameters" :
+ [
+ {
+ "referenced_type" : "_ZTIPKc"
+ },
+ {
+ "referenced_type" : "_ZTIi"
+ }
+ ],
+ "return_type" : "_ZTIi",
+ "source_file" : "system/core/libcutils/include/cutils/fs.h"
+ },
+ {
+ "function_name" : "hashmapCreate",
+ "linker_set_key" : "hashmapCreate",
+ "parameters" :
+ [
+ {
+ "referenced_type" : "_ZTIj"
+ },
+ {
+ "referenced_type" : "_ZTIPFiPvE"
+ },
+ {
+ "referenced_type" : "_ZTIPFbPvS_E"
+ }
+ ],
+ "return_type" : "_ZTIP7Hashmap",
+ "source_file" : "system/core/libcutils/include/cutils/hashmap.h"
+ },
+ {
+ "function_name" : "hashmapForEach",
+ "linker_set_key" : "hashmapForEach",
+ "parameters" :
+ [
+ {
+ "referenced_type" : "_ZTIP7Hashmap"
+ },
+ {
+ "referenced_type" : "_ZTIPFbPvS_S_E"
+ },
+ {
+ "referenced_type" : "_ZTIPv"
+ }
+ ],
+ "return_type" : "_ZTIv",
+ "source_file" : "system/core/libcutils/include/cutils/hashmap.h"
+ },
+ {
+ "function_name" : "hashmapFree",
+ "linker_set_key" : "hashmapFree",
+ "parameters" :
+ [
+ {
+ "referenced_type" : "_ZTIP7Hashmap"
+ }
+ ],
+ "return_type" : "_ZTIv",
+ "source_file" : "system/core/libcutils/include/cutils/hashmap.h"
+ },
+ {
+ "function_name" : "hashmapGet",
+ "linker_set_key" : "hashmapGet",
+ "parameters" :
+ [
+ {
+ "referenced_type" : "_ZTIP7Hashmap"
+ },
+ {
+ "referenced_type" : "_ZTIPv"
+ }
+ ],
+ "return_type" : "_ZTIPv",
+ "source_file" : "system/core/libcutils/include/cutils/hashmap.h"
+ },
+ {
+ "function_name" : "hashmapHash",
+ "linker_set_key" : "hashmapHash",
+ "parameters" :
+ [
+ {
+ "referenced_type" : "_ZTIPv"
+ },
+ {
+ "referenced_type" : "_ZTIj"
+ }
+ ],
+ "return_type" : "_ZTIi",
+ "source_file" : "system/core/libcutils/include/cutils/hashmap.h"
+ },
+ {
+ "function_name" : "hashmapLock",
+ "linker_set_key" : "hashmapLock",
+ "parameters" :
+ [
+ {
+ "referenced_type" : "_ZTIP7Hashmap"
+ }
+ ],
+ "return_type" : "_ZTIv",
+ "source_file" : "system/core/libcutils/include/cutils/hashmap.h"
+ },
+ {
+ "function_name" : "hashmapPut",
+ "linker_set_key" : "hashmapPut",
+ "parameters" :
+ [
+ {
+ "referenced_type" : "_ZTIP7Hashmap"
+ },
+ {
+ "referenced_type" : "_ZTIPv"
+ },
+ {
+ "referenced_type" : "_ZTIPv"
+ }
+ ],
+ "return_type" : "_ZTIPv",
+ "source_file" : "system/core/libcutils/include/cutils/hashmap.h"
+ },
+ {
+ "function_name" : "hashmapRemove",
+ "linker_set_key" : "hashmapRemove",
+ "parameters" :
+ [
+ {
+ "referenced_type" : "_ZTIP7Hashmap"
+ },
+ {
+ "referenced_type" : "_ZTIPv"
+ }
+ ],
+ "return_type" : "_ZTIPv",
+ "source_file" : "system/core/libcutils/include/cutils/hashmap.h"
+ },
+ {
+ "function_name" : "hashmapUnlock",
+ "linker_set_key" : "hashmapUnlock",
+ "parameters" :
+ [
+ {
+ "referenced_type" : "_ZTIP7Hashmap"
+ }
+ ],
+ "return_type" : "_ZTIv",
+ "source_file" : "system/core/libcutils/include/cutils/hashmap.h"
+ },
+ {
+ "function_name" : "klog_set_level",
+ "linker_set_key" : "klog_set_level",
+ "parameters" :
+ [
+ {
+ "referenced_type" : "_ZTIi"
+ }
+ ],
+ "return_type" : "_ZTIv",
+ "source_file" : "system/core/libcutils/include/cutils/klog.h"
+ },
+ {
+ "function_name" : "klog_write",
+ "linker_set_key" : "klog_write",
+ "parameters" :
+ [
+ {
+ "referenced_type" : "_ZTIi"
+ },
+ {
+ "referenced_type" : "_ZTIPKc"
+ }
+ ],
+ "return_type" : "_ZTIv",
+ "source_file" : "system/core/libcutils/include/cutils/klog.h"
+ },
+ {
+ "function_name" : "klog_writev",
+ "linker_set_key" : "klog_writev",
+ "parameters" :
+ [
+ {
+ "referenced_type" : "_ZTIi"
+ },
+ {
+ "referenced_type" : "_ZTIPK5iovec"
+ },
+ {
+ "referenced_type" : "_ZTIi"
+ }
+ ],
+ "return_type" : "_ZTIv",
+ "source_file" : "system/core/libcutils/include/cutils/klog.h"
+ },
+ {
+ "function_name" : "load_canned_fs_config",
+ "linker_set_key" : "load_canned_fs_config",
+ "parameters" :
+ [
+ {
+ "referenced_type" : "_ZTIPKc"
+ }
+ ],
+ "return_type" : "_ZTIi",
+ "source_file" : "system/core/libcutils/include/private/canned_fs_config.h"
+ },
+ {
+ "function_name" : "load_file",
+ "linker_set_key" : "load_file",
+ "parameters" :
+ [
+ {
+ "referenced_type" : "_ZTIPKc"
+ },
+ {
+ "referenced_type" : "_ZTIPj"
+ }
+ ],
+ "return_type" : "_ZTIPv",
+ "source_file" : "system/core/libcutils/include/cutils/misc.h"
+ },
+ {
+ "function_name" : "multiuser_convert_sdk_sandbox_to_app_uid",
+ "linker_set_key" : "multiuser_convert_sdk_sandbox_to_app_uid",
+ "parameters" :
+ [
+ {
+ "referenced_type" : "_ZTIj"
+ }
+ ],
+ "return_type" : "_ZTIj",
+ "source_file" : "system/core/libcutils/include/cutils/multiuser.h"
+ },
+ {
+ "function_name" : "multiuser_get_app_id",
+ "linker_set_key" : "multiuser_get_app_id",
+ "parameters" :
+ [
+ {
+ "referenced_type" : "_ZTIj"
+ }
+ ],
+ "return_type" : "_ZTIj",
+ "source_file" : "system/core/libcutils/include/cutils/multiuser.h"
+ },
+ {
+ "function_name" : "multiuser_get_cache_gid",
+ "linker_set_key" : "multiuser_get_cache_gid",
+ "parameters" :
+ [
+ {
+ "referenced_type" : "_ZTIj"
+ },
+ {
+ "referenced_type" : "_ZTIj"
+ }
+ ],
+ "return_type" : "_ZTIj",
+ "source_file" : "system/core/libcutils/include/cutils/multiuser.h"
+ },
+ {
+ "function_name" : "multiuser_get_ext_cache_gid",
+ "linker_set_key" : "multiuser_get_ext_cache_gid",
+ "parameters" :
+ [
+ {
+ "referenced_type" : "_ZTIj"
+ },
+ {
+ "referenced_type" : "_ZTIj"
+ }
+ ],
+ "return_type" : "_ZTIj",
+ "source_file" : "system/core/libcutils/include/cutils/multiuser.h"
+ },
+ {
+ "function_name" : "multiuser_get_ext_gid",
+ "linker_set_key" : "multiuser_get_ext_gid",
+ "parameters" :
+ [
+ {
+ "referenced_type" : "_ZTIj"
+ },
+ {
+ "referenced_type" : "_ZTIj"
+ }
+ ],
+ "return_type" : "_ZTIj",
+ "source_file" : "system/core/libcutils/include/cutils/multiuser.h"
+ },
+ {
+ "function_name" : "multiuser_get_sdk_sandbox_uid",
+ "linker_set_key" : "multiuser_get_sdk_sandbox_uid",
+ "parameters" :
+ [
+ {
+ "referenced_type" : "_ZTIj"
+ },
+ {
+ "referenced_type" : "_ZTIj"
+ }
+ ],
+ "return_type" : "_ZTIj",
+ "source_file" : "system/core/libcutils/include/cutils/multiuser.h"
+ },
+ {
+ "function_name" : "multiuser_get_shared_app_gid",
+ "linker_set_key" : "multiuser_get_shared_app_gid",
+ "parameters" :
+ [
+ {
+ "referenced_type" : "_ZTIj"
+ }
+ ],
+ "return_type" : "_ZTIj",
+ "source_file" : "system/core/libcutils/include/cutils/multiuser.h"
+ },
+ {
+ "function_name" : "multiuser_get_shared_gid",
+ "linker_set_key" : "multiuser_get_shared_gid",
+ "parameters" :
+ [
+ {
+ "referenced_type" : "_ZTIj"
+ },
+ {
+ "referenced_type" : "_ZTIj"
+ }
+ ],
+ "return_type" : "_ZTIj",
+ "source_file" : "system/core/libcutils/include/cutils/multiuser.h"
+ },
+ {
+ "function_name" : "multiuser_get_uid",
+ "linker_set_key" : "multiuser_get_uid",
+ "parameters" :
+ [
+ {
+ "referenced_type" : "_ZTIj"
+ },
+ {
+ "referenced_type" : "_ZTIj"
+ }
+ ],
+ "return_type" : "_ZTIj",
+ "source_file" : "system/core/libcutils/include/cutils/multiuser.h"
+ },
+ {
+ "function_name" : "multiuser_get_user_id",
+ "linker_set_key" : "multiuser_get_user_id",
+ "parameters" :
+ [
+ {
+ "referenced_type" : "_ZTIj"
+ }
+ ],
+ "return_type" : "_ZTIj",
+ "source_file" : "system/core/libcutils/include/cutils/multiuser.h"
+ },
+ {
+ "function_name" : "native_handle_clone",
+ "linker_set_key" : "native_handle_clone",
+ "parameters" :
+ [
+ {
+ "referenced_type" : "_ZTIPK13native_handle"
+ }
+ ],
+ "return_type" : "_ZTIP13native_handle",
+ "source_file" : "system/core/libcutils/include/cutils/native_handle.h"
+ },
+ {
+ "function_name" : "native_handle_close",
+ "linker_set_key" : "native_handle_close",
+ "parameters" :
+ [
+ {
+ "referenced_type" : "_ZTIPK13native_handle"
+ }
+ ],
+ "return_type" : "_ZTIi",
+ "source_file" : "system/core/libcutils/include/cutils/native_handle.h"
+ },
+ {
+ "function_name" : "native_handle_close_with_tag",
+ "linker_set_key" : "native_handle_close_with_tag",
+ "parameters" :
+ [
+ {
+ "referenced_type" : "_ZTIPK13native_handle"
+ }
+ ],
+ "return_type" : "_ZTIi",
+ "source_file" : "system/core/libcutils/include/cutils/native_handle.h"
+ },
+ {
+ "function_name" : "native_handle_create",
+ "linker_set_key" : "native_handle_create",
+ "parameters" :
+ [
+ {
+ "referenced_type" : "_ZTIi"
+ },
+ {
+ "referenced_type" : "_ZTIi"
+ }
+ ],
+ "return_type" : "_ZTIP13native_handle",
+ "source_file" : "system/core/libcutils/include/cutils/native_handle.h"
+ },
+ {
+ "function_name" : "native_handle_delete",
+ "linker_set_key" : "native_handle_delete",
+ "parameters" :
+ [
+ {
+ "referenced_type" : "_ZTIP13native_handle"
+ }
+ ],
+ "return_type" : "_ZTIi",
+ "source_file" : "system/core/libcutils/include/cutils/native_handle.h"
+ },
+ {
+ "function_name" : "native_handle_init",
+ "linker_set_key" : "native_handle_init",
+ "parameters" :
+ [
+ {
+ "referenced_type" : "_ZTIPc"
+ },
+ {
+ "referenced_type" : "_ZTIi"
+ },
+ {
+ "referenced_type" : "_ZTIi"
+ }
+ ],
+ "return_type" : "_ZTIP13native_handle",
+ "source_file" : "system/core/libcutils/include/cutils/native_handle.h"
+ },
+ {
+ "function_name" : "native_handle_set_fdsan_tag",
+ "linker_set_key" : "native_handle_set_fdsan_tag",
+ "parameters" :
+ [
+ {
+ "referenced_type" : "_ZTIPK13native_handle"
+ }
+ ],
+ "return_type" : "_ZTIv",
+ "source_file" : "system/core/libcutils/include/cutils/native_handle.h"
+ },
+ {
+ "function_name" : "native_handle_unset_fdsan_tag",
+ "linker_set_key" : "native_handle_unset_fdsan_tag",
+ "parameters" :
+ [
+ {
+ "referenced_type" : "_ZTIPK13native_handle"
+ }
+ ],
+ "return_type" : "_ZTIv",
+ "source_file" : "system/core/libcutils/include/cutils/native_handle.h"
+ },
+ {
+ "function_name" : "partition_wiped",
+ "linker_set_key" : "partition_wiped",
+ "parameters" :
+ [
+ {
+ "referenced_type" : "_ZTIPKc"
+ }
+ ],
+ "return_type" : "_ZTIi",
+ "source_file" : "system/core/libcutils/include/cutils/partition_utils.h"
+ },
+ {
+ "function_name" : "property_get",
+ "linker_set_key" : "property_get",
+ "parameters" :
+ [
+ {
+ "referenced_type" : "_ZTIPKc"
+ },
+ {
+ "referenced_type" : "_ZTIPc"
+ },
+ {
+ "referenced_type" : "_ZTIPKc"
+ }
+ ],
+ "return_type" : "_ZTIi",
+ "source_file" : "system/core/libcutils/include/cutils/properties.h"
+ },
+ {
+ "function_name" : "property_get_bool",
+ "linker_set_key" : "property_get_bool",
+ "parameters" :
+ [
+ {
+ "referenced_type" : "_ZTIPKc"
+ },
+ {
+ "referenced_type" : "_ZTIa"
+ }
+ ],
+ "return_type" : "_ZTIa",
+ "source_file" : "system/core/libcutils/include/cutils/properties.h"
+ },
+ {
+ "function_name" : "property_get_int32",
+ "linker_set_key" : "property_get_int32",
+ "parameters" :
+ [
+ {
+ "referenced_type" : "_ZTIPKc"
+ },
+ {
+ "referenced_type" : "_ZTIi"
+ }
+ ],
+ "return_type" : "_ZTIi",
+ "source_file" : "system/core/libcutils/include/cutils/properties.h"
+ },
+ {
+ "function_name" : "property_get_int64",
+ "linker_set_key" : "property_get_int64",
+ "parameters" :
+ [
+ {
+ "referenced_type" : "_ZTIPKc"
+ },
+ {
+ "referenced_type" : "_ZTIx"
+ }
+ ],
+ "return_type" : "_ZTIx",
+ "source_file" : "system/core/libcutils/include/cutils/properties.h"
+ },
+ {
+ "function_name" : "property_list",
+ "linker_set_key" : "property_list",
+ "parameters" :
+ [
+ {
+ "referenced_type" : "_ZTIPFvPKcS0_PvE"
+ },
+ {
+ "referenced_type" : "_ZTIPv"
+ }
+ ],
+ "return_type" : "_ZTIi",
+ "source_file" : "system/core/libcutils/include/cutils/properties.h"
+ },
+ {
+ "function_name" : "property_set",
+ "linker_set_key" : "property_set",
+ "parameters" :
+ [
+ {
+ "referenced_type" : "_ZTIPKc"
+ },
+ {
+ "referenced_type" : "_ZTIPKc"
+ }
+ ],
+ "return_type" : "_ZTIi",
+ "source_file" : "system/core/libcutils/include/cutils/properties.h"
+ },
+ {
+ "function_name" : "record_stream_free",
+ "linker_set_key" : "record_stream_free",
+ "parameters" :
+ [
+ {
+ "referenced_type" : "_ZTIP12RecordStream"
+ }
+ ],
+ "return_type" : "_ZTIv",
+ "source_file" : "system/core/libcutils/include/cutils/record_stream.h"
+ },
+ {
+ "function_name" : "record_stream_get_next",
+ "linker_set_key" : "record_stream_get_next",
+ "parameters" :
+ [
+ {
+ "referenced_type" : "_ZTIP12RecordStream"
+ },
+ {
+ "referenced_type" : "_ZTIPPv"
+ },
+ {
+ "referenced_type" : "_ZTIPj"
+ }
+ ],
+ "return_type" : "_ZTIi",
+ "source_file" : "system/core/libcutils/include/cutils/record_stream.h"
+ },
+ {
+ "function_name" : "record_stream_new",
+ "linker_set_key" : "record_stream_new",
+ "parameters" :
+ [
+ {
+ "referenced_type" : "_ZTIi"
+ },
+ {
+ "referenced_type" : "_ZTIj"
+ }
+ ],
+ "return_type" : "_ZTIP12RecordStream",
+ "source_file" : "system/core/libcutils/include/cutils/record_stream.h"
+ },
+ {
+ "function_name" : "socket_close",
+ "linker_set_key" : "socket_close",
+ "parameters" :
+ [
+ {
+ "referenced_type" : "_ZTIi"
+ }
+ ],
+ "return_type" : "_ZTIi",
+ "source_file" : "system/core/libcutils/include/cutils/sockets.h"
+ },
+ {
+ "function_name" : "socket_get_local_port",
+ "linker_set_key" : "socket_get_local_port",
+ "parameters" :
+ [
+ {
+ "referenced_type" : "_ZTIi"
+ }
+ ],
+ "return_type" : "_ZTIi",
+ "source_file" : "system/core/libcutils/include/cutils/sockets.h"
+ },
+ {
+ "function_name" : "socket_inaddr_any_server",
+ "linker_set_key" : "socket_inaddr_any_server",
+ "parameters" :
+ [
+ {
+ "referenced_type" : "_ZTIi"
+ },
+ {
+ "referenced_type" : "_ZTIi"
+ }
+ ],
+ "return_type" : "_ZTIi",
+ "source_file" : "system/core/libcutils/include/cutils/sockets.h"
+ },
+ {
+ "function_name" : "socket_local_client",
+ "linker_set_key" : "socket_local_client",
+ "parameters" :
+ [
+ {
+ "referenced_type" : "_ZTIPKc"
+ },
+ {
+ "referenced_type" : "_ZTIi"
+ },
+ {
+ "referenced_type" : "_ZTIi"
+ }
+ ],
+ "return_type" : "_ZTIi",
+ "source_file" : "system/core/libcutils/include/cutils/sockets.h"
+ },
+ {
+ "function_name" : "socket_local_client_connect",
+ "linker_set_key" : "socket_local_client_connect",
+ "parameters" :
+ [
+ {
+ "referenced_type" : "_ZTIi"
+ },
+ {
+ "referenced_type" : "_ZTIPKc"
+ },
+ {
+ "referenced_type" : "_ZTIi"
+ },
+ {
+ "referenced_type" : "_ZTIi"
+ }
+ ],
+ "return_type" : "_ZTIi",
+ "source_file" : "system/core/libcutils/include/cutils/sockets.h"
+ },
+ {
+ "function_name" : "socket_local_server",
+ "linker_set_key" : "socket_local_server",
+ "parameters" :
+ [
+ {
+ "referenced_type" : "_ZTIPKc"
+ },
+ {
+ "referenced_type" : "_ZTIi"
+ },
+ {
+ "referenced_type" : "_ZTIi"
+ }
+ ],
+ "return_type" : "_ZTIi",
+ "source_file" : "system/core/libcutils/include/cutils/sockets.h"
+ },
+ {
+ "function_name" : "socket_local_server_bind",
+ "linker_set_key" : "socket_local_server_bind",
+ "parameters" :
+ [
+ {
+ "referenced_type" : "_ZTIi"
+ },
+ {
+ "referenced_type" : "_ZTIPKc"
+ },
+ {
+ "referenced_type" : "_ZTIi"
+ }
+ ],
+ "return_type" : "_ZTIi",
+ "source_file" : "system/core/libcutils/include/cutils/sockets.h"
+ },
+ {
+ "function_name" : "socket_network_client",
+ "linker_set_key" : "socket_network_client",
+ "parameters" :
+ [
+ {
+ "referenced_type" : "_ZTIPKc"
+ },
+ {
+ "referenced_type" : "_ZTIi"
+ },
+ {
+ "referenced_type" : "_ZTIi"
+ }
+ ],
+ "return_type" : "_ZTIi",
+ "source_file" : "system/core/libcutils/include/cutils/sockets.h"
+ },
+ {
+ "function_name" : "socket_network_client_timeout",
+ "linker_set_key" : "socket_network_client_timeout",
+ "parameters" :
+ [
+ {
+ "referenced_type" : "_ZTIPKc"
+ },
+ {
+ "referenced_type" : "_ZTIi"
+ },
+ {
+ "referenced_type" : "_ZTIi"
+ },
+ {
+ "referenced_type" : "_ZTIi"
+ },
+ {
+ "referenced_type" : "_ZTIPi"
+ }
+ ],
+ "return_type" : "_ZTIi",
+ "source_file" : "system/core/libcutils/include/cutils/sockets.h"
+ },
+ {
+ "function_name" : "socket_send_buffers",
+ "linker_set_key" : "socket_send_buffers",
+ "parameters" :
+ [
+ {
+ "referenced_type" : "_ZTIi"
+ },
+ {
+ "referenced_type" : "_ZTIPK22cutils_socket_buffer_t"
+ },
+ {
+ "referenced_type" : "_ZTIj"
+ }
+ ],
+ "return_type" : "_ZTIi",
+ "source_file" : "system/core/libcutils/include/cutils/sockets.h"
+ },
+ {
+ "function_name" : "str_parms_add_float",
+ "linker_set_key" : "str_parms_add_float",
+ "parameters" :
+ [
+ {
+ "referenced_type" : "_ZTIP9str_parms"
+ },
+ {
+ "referenced_type" : "_ZTIPKc"
+ },
+ {
+ "referenced_type" : "_ZTIf"
+ }
+ ],
+ "return_type" : "_ZTIi",
+ "source_file" : "system/core/libcutils/include/cutils/str_parms.h"
+ },
+ {
+ "function_name" : "str_parms_add_int",
+ "linker_set_key" : "str_parms_add_int",
+ "parameters" :
+ [
+ {
+ "referenced_type" : "_ZTIP9str_parms"
+ },
+ {
+ "referenced_type" : "_ZTIPKc"
+ },
+ {
+ "referenced_type" : "_ZTIi"
+ }
+ ],
+ "return_type" : "_ZTIi",
+ "source_file" : "system/core/libcutils/include/cutils/str_parms.h"
+ },
+ {
+ "function_name" : "str_parms_add_str",
+ "linker_set_key" : "str_parms_add_str",
+ "parameters" :
+ [
+ {
+ "referenced_type" : "_ZTIP9str_parms"
+ },
+ {
+ "referenced_type" : "_ZTIPKc"
+ },
+ {
+ "referenced_type" : "_ZTIPKc"
+ }
+ ],
+ "return_type" : "_ZTIi",
+ "source_file" : "system/core/libcutils/include/cutils/str_parms.h"
+ },
+ {
+ "function_name" : "str_parms_create",
+ "linker_set_key" : "str_parms_create",
+ "return_type" : "_ZTIP9str_parms",
+ "source_file" : "system/core/libcutils/include/cutils/str_parms.h"
+ },
+ {
+ "function_name" : "str_parms_create_str",
+ "linker_set_key" : "str_parms_create_str",
+ "parameters" :
+ [
+ {
+ "referenced_type" : "_ZTIPKc"
+ }
+ ],
+ "return_type" : "_ZTIP9str_parms",
+ "source_file" : "system/core/libcutils/include/cutils/str_parms.h"
+ },
+ {
+ "function_name" : "str_parms_del",
+ "linker_set_key" : "str_parms_del",
+ "parameters" :
+ [
+ {
+ "referenced_type" : "_ZTIP9str_parms"
+ },
+ {
+ "referenced_type" : "_ZTIPKc"
+ }
+ ],
+ "return_type" : "_ZTIv",
+ "source_file" : "system/core/libcutils/include/cutils/str_parms.h"
+ },
+ {
+ "function_name" : "str_parms_destroy",
+ "linker_set_key" : "str_parms_destroy",
+ "parameters" :
+ [
+ {
+ "referenced_type" : "_ZTIP9str_parms"
+ }
+ ],
+ "return_type" : "_ZTIv",
+ "source_file" : "system/core/libcutils/include/cutils/str_parms.h"
+ },
+ {
+ "function_name" : "str_parms_dump",
+ "linker_set_key" : "str_parms_dump",
+ "parameters" :
+ [
+ {
+ "referenced_type" : "_ZTIP9str_parms"
+ }
+ ],
+ "return_type" : "_ZTIv",
+ "source_file" : "system/core/libcutils/include/cutils/str_parms.h"
+ },
+ {
+ "function_name" : "str_parms_get_float",
+ "linker_set_key" : "str_parms_get_float",
+ "parameters" :
+ [
+ {
+ "referenced_type" : "_ZTIP9str_parms"
+ },
+ {
+ "referenced_type" : "_ZTIPKc"
+ },
+ {
+ "referenced_type" : "_ZTIPf"
+ }
+ ],
+ "return_type" : "_ZTIi",
+ "source_file" : "system/core/libcutils/include/cutils/str_parms.h"
+ },
+ {
+ "function_name" : "str_parms_get_int",
+ "linker_set_key" : "str_parms_get_int",
+ "parameters" :
+ [
+ {
+ "referenced_type" : "_ZTIP9str_parms"
+ },
+ {
+ "referenced_type" : "_ZTIPKc"
+ },
+ {
+ "referenced_type" : "_ZTIPi"
+ }
+ ],
+ "return_type" : "_ZTIi",
+ "source_file" : "system/core/libcutils/include/cutils/str_parms.h"
+ },
+ {
+ "function_name" : "str_parms_get_str",
+ "linker_set_key" : "str_parms_get_str",
+ "parameters" :
+ [
+ {
+ "referenced_type" : "_ZTIP9str_parms"
+ },
+ {
+ "referenced_type" : "_ZTIPKc"
+ },
+ {
+ "referenced_type" : "_ZTIPc"
+ },
+ {
+ "referenced_type" : "_ZTIi"
+ }
+ ],
+ "return_type" : "_ZTIi",
+ "source_file" : "system/core/libcutils/include/cutils/str_parms.h"
+ },
+ {
+ "function_name" : "str_parms_has_key",
+ "linker_set_key" : "str_parms_has_key",
+ "parameters" :
+ [
+ {
+ "referenced_type" : "_ZTIP9str_parms"
+ },
+ {
+ "referenced_type" : "_ZTIPKc"
+ }
+ ],
+ "return_type" : "_ZTIi",
+ "source_file" : "system/core/libcutils/include/cutils/str_parms.h"
+ },
+ {
+ "function_name" : "str_parms_to_str",
+ "linker_set_key" : "str_parms_to_str",
+ "parameters" :
+ [
+ {
+ "referenced_type" : "_ZTIP9str_parms"
+ }
+ ],
+ "return_type" : "_ZTIPc",
+ "source_file" : "system/core/libcutils/include/cutils/str_parms.h"
+ },
+ {
+ "function_name" : "uevent_kernel_multicast_recv",
+ "linker_set_key" : "uevent_kernel_multicast_recv",
+ "parameters" :
+ [
+ {
+ "referenced_type" : "_ZTIi"
+ },
+ {
+ "referenced_type" : "_ZTIPv"
+ },
+ {
+ "referenced_type" : "_ZTIj"
+ }
+ ],
+ "return_type" : "_ZTIi",
+ "source_file" : "system/core/libcutils/include/cutils/uevent.h"
+ },
+ {
+ "function_name" : "uevent_kernel_multicast_uid_recv",
+ "linker_set_key" : "uevent_kernel_multicast_uid_recv",
+ "parameters" :
+ [
+ {
+ "referenced_type" : "_ZTIi"
+ },
+ {
+ "referenced_type" : "_ZTIPv"
+ },
+ {
+ "referenced_type" : "_ZTIj"
+ },
+ {
+ "referenced_type" : "_ZTIPj"
+ }
+ ],
+ "return_type" : "_ZTIi",
+ "source_file" : "system/core/libcutils/include/cutils/uevent.h"
+ },
+ {
+ "function_name" : "uevent_kernel_recv",
+ "linker_set_key" : "uevent_kernel_recv",
+ "parameters" :
+ [
+ {
+ "referenced_type" : "_ZTIi"
+ },
+ {
+ "referenced_type" : "_ZTIPv"
+ },
+ {
+ "referenced_type" : "_ZTIj"
+ },
+ {
+ "referenced_type" : "_ZTIb"
+ },
+ {
+ "referenced_type" : "_ZTIPj"
+ }
+ ],
+ "return_type" : "_ZTIi",
+ "source_file" : "system/core/libcutils/include/cutils/uevent.h"
+ },
+ {
+ "function_name" : "uevent_open_socket",
+ "linker_set_key" : "uevent_open_socket",
+ "parameters" :
+ [
+ {
+ "referenced_type" : "_ZTIi"
+ },
+ {
+ "referenced_type" : "_ZTIb"
+ }
+ ],
+ "return_type" : "_ZTIi",
+ "source_file" : "system/core/libcutils/include/cutils/uevent.h"
+ }
+ ],
+ "global_vars" :
+ [
+ {
+ "linker_set_key" : "atrace_enabled_tags",
+ "name" : "atrace_enabled_tags",
+ "referenced_type" : "_ZTIy",
+ "source_file" : "system/core/libcutils/include/cutils/trace.h"
+ },
+ {
+ "linker_set_key" : "atrace_is_ready",
+ "name" : "atrace_is_ready",
+ "referenced_type" : "_ZTINSt3__16atomicIbEE",
+ "source_file" : "system/core/libcutils/include/cutils/trace.h"
+ },
+ {
+ "linker_set_key" : "atrace_marker_fd",
+ "name" : "atrace_marker_fd",
+ "referenced_type" : "_ZTIi",
+ "source_file" : "system/core/libcutils/include/cutils/trace.h"
+ }
+ ],
+ "lvalue_reference_types" : [],
+ "pointer_types" :
+ [
+ {
+ "alignment" : 4,
+ "linker_set_key" : "_ZTIP12IoSchedClass",
+ "name" : "IoSchedClass *",
+ "referenced_type" : "_ZTI12IoSchedClass",
+ "self_type" : "_ZTIP12IoSchedClass",
+ "size" : 4,
+ "source_file" : "system/core/libcutils/include/cutils/iosched_policy.h"
+ },
+ {
+ "alignment" : 4,
+ "linker_set_key" : "_ZTIP12RecordStream",
+ "name" : "RecordStream *",
+ "referenced_type" : "_ZTI12RecordStream",
+ "self_type" : "_ZTIP12RecordStream",
+ "size" : 4,
+ "source_file" : "system/core/libcutils/include/cutils/record_stream.h"
+ },
+ {
+ "alignment" : 4,
+ "linker_set_key" : "_ZTIP13native_handle",
+ "name" : "native_handle *",
+ "referenced_type" : "_ZTI13native_handle",
+ "self_type" : "_ZTIP13native_handle",
+ "size" : 4,
+ "source_file" : "system/core/libcutils/include/cutils/native_handle.h"
+ },
+ {
+ "alignment" : 4,
+ "linker_set_key" : "_ZTIP5cnode",
+ "name" : "cnode *",
+ "referenced_type" : "_ZTI5cnode",
+ "self_type" : "_ZTIP5cnode",
+ "size" : 4,
+ "source_file" : "system/core/libcutils/include/cutils/config_utils.h"
+ },
+ {
+ "alignment" : 4,
+ "linker_set_key" : "_ZTIP7Hashmap",
+ "name" : "Hashmap *",
+ "referenced_type" : "_ZTI7Hashmap",
+ "self_type" : "_ZTIP7Hashmap",
+ "size" : 4,
+ "source_file" : "system/core/libcutils/include/cutils/hashmap.h"
+ },
+ {
+ "alignment" : 4,
+ "linker_set_key" : "_ZTIP9str_parms",
+ "name" : "str_parms *",
+ "referenced_type" : "_ZTI9str_parms",
+ "self_type" : "_ZTIP9str_parms",
+ "size" : 4,
+ "source_file" : "system/core/libcutils/include/cutils/str_parms.h"
+ },
+ {
+ "alignment" : 4,
+ "linker_set_key" : "_ZTIPFbPvS_E",
+ "name" : "bool (*)(void *, void *)",
+ "referenced_type" : "_ZTIFbPvS_E",
+ "self_type" : "_ZTIPFbPvS_E",
+ "size" : 4,
+ "source_file" : "system/core/libcutils/include/cutils/hashmap.h"
+ },
+ {
+ "alignment" : 4,
+ "linker_set_key" : "_ZTIPFbPvS_S_E",
+ "name" : "bool (*)(void *, void *, void *)",
+ "referenced_type" : "_ZTIFbPvS_S_E",
+ "self_type" : "_ZTIPFbPvS_S_E",
+ "size" : 4,
+ "source_file" : "system/core/libcutils/include/cutils/hashmap.h"
+ },
+ {
+ "alignment" : 4,
+ "linker_set_key" : "_ZTIPFiPvE",
+ "name" : "int (*)(void *)",
+ "referenced_type" : "_ZTIFiPvE",
+ "self_type" : "_ZTIPFiPvE",
+ "size" : 4,
+ "source_file" : "system/core/libcutils/include/cutils/hashmap.h"
+ },
+ {
+ "alignment" : 4,
+ "linker_set_key" : "_ZTIPFvPKcS0_PvE",
+ "name" : "void (*)(const char *, const char *, void *)",
+ "referenced_type" : "_ZTIFvPKcS0_PvE",
+ "self_type" : "_ZTIPFvPKcS0_PvE",
+ "size" : 4,
+ "source_file" : "system/core/libcutils/include/cutils/properties.h"
+ },
+ {
+ "alignment" : 4,
+ "linker_set_key" : "_ZTIPK13native_handle",
+ "name" : "const native_handle *",
+ "referenced_type" : "_ZTIK13native_handle",
+ "self_type" : "_ZTIPK13native_handle",
+ "size" : 4,
+ "source_file" : "system/core/libcutils/include/cutils/native_handle.h"
+ },
+ {
+ "alignment" : 4,
+ "linker_set_key" : "_ZTIPK22cutils_socket_buffer_t",
+ "name" : "const cutils_socket_buffer_t *",
+ "referenced_type" : "_ZTIK22cutils_socket_buffer_t",
+ "self_type" : "_ZTIPK22cutils_socket_buffer_t",
+ "size" : 4,
+ "source_file" : "system/core/libcutils/include/cutils/sockets.h"
+ },
+ {
+ "alignment" : 4,
+ "linker_set_key" : "_ZTIPK5iovec",
+ "name" : "const iovec *",
+ "referenced_type" : "_ZTIK5iovec",
+ "self_type" : "_ZTIPK5iovec",
+ "size" : 4,
+ "source_file" : "system/core/libcutils/include/cutils/klog.h"
+ },
+ {
+ "alignment" : 4,
+ "linker_set_key" : "_ZTIPKc",
+ "name" : "const char *",
+ "referenced_type" : "_ZTIKc",
+ "self_type" : "_ZTIPKc",
+ "size" : 4,
+ "source_file" : "system/core/libcutils/include/cutils/config_utils.h"
+ },
+ {
+ "alignment" : 4,
+ "linker_set_key" : "_ZTIPKv",
+ "name" : "const void *",
+ "referenced_type" : "_ZTIKv",
+ "self_type" : "_ZTIPKv",
+ "size" : 4,
+ "source_file" : "system/core/libcutils/include/cutils/sockets.h"
+ },
+ {
+ "alignment" : 4,
+ "linker_set_key" : "_ZTIPPv",
+ "name" : "void **",
+ "referenced_type" : "_ZTIPv",
+ "self_type" : "_ZTIPPv",
+ "size" : 4,
+ "source_file" : "system/core/libcutils/include/cutils/record_stream.h"
+ },
+ {
+ "alignment" : 4,
+ "linker_set_key" : "_ZTIPc",
+ "name" : "char *",
+ "referenced_type" : "_ZTIc",
+ "self_type" : "_ZTIPc",
+ "size" : 4,
+ "source_file" : "system/core/libcutils/include/cutils/config_utils.h"
+ },
+ {
+ "alignment" : 4,
+ "linker_set_key" : "_ZTIPf",
+ "name" : "float *",
+ "referenced_type" : "_ZTIf",
+ "self_type" : "_ZTIPf",
+ "size" : 4,
+ "source_file" : "system/core/libcutils/include/cutils/str_parms.h"
+ },
+ {
+ "alignment" : 4,
+ "linker_set_key" : "_ZTIPi",
+ "name" : "int *",
+ "referenced_type" : "_ZTIi",
+ "self_type" : "_ZTIPi",
+ "size" : 4,
+ "source_file" : "system/core/libcutils/include/cutils/iosched_policy.h"
+ },
+ {
+ "alignment" : 4,
+ "linker_set_key" : "_ZTIPj",
+ "name" : "unsigned int *",
+ "referenced_type" : "_ZTIj",
+ "self_type" : "_ZTIPj",
+ "size" : 4,
+ "source_file" : "system/core/libcutils/include/cutils/misc.h"
+ },
+ {
+ "alignment" : 4,
+ "linker_set_key" : "_ZTIPv",
+ "name" : "void *",
+ "referenced_type" : "_ZTIv",
+ "self_type" : "_ZTIPv",
+ "size" : 4,
+ "source_file" : "system/core/libcutils/include/cutils/misc.h"
+ },
+ {
+ "alignment" : 4,
+ "linker_set_key" : "_ZTIPy",
+ "name" : "unsigned long long *",
+ "referenced_type" : "_ZTIy",
+ "self_type" : "_ZTIPy",
+ "size" : 4,
+ "source_file" : "system/core/libcutils/include/private/canned_fs_config.h"
+ }
+ ],
+ "qualified_types" :
+ [
+ {
+ "alignment" : 4,
+ "is_const" : true,
+ "linker_set_key" : "_ZTIK13native_handle",
+ "name" : "const native_handle",
+ "referenced_type" : "_ZTI13native_handle",
+ "self_type" : "_ZTIK13native_handle",
+ "size" : 12,
+ "source_file" : "system/core/libcutils/include/cutils/native_handle.h"
+ },
+ {
+ "alignment" : 4,
+ "is_const" : true,
+ "linker_set_key" : "_ZTIK22cutils_socket_buffer_t",
+ "name" : "const cutils_socket_buffer_t",
+ "referenced_type" : "_ZTI22cutils_socket_buffer_t",
+ "self_type" : "_ZTIK22cutils_socket_buffer_t",
+ "size" : 8,
+ "source_file" : "system/core/libcutils/include/cutils/sockets.h"
+ },
+ {
+ "alignment" : 4,
+ "is_const" : true,
+ "linker_set_key" : "_ZTIK5iovec",
+ "name" : "const iovec",
+ "referenced_type" : "_ZTI5iovec",
+ "self_type" : "_ZTIK5iovec",
+ "size" : 8,
+ "source_file" : "system/core/libcutils/include/cutils/klog.h"
+ },
+ {
+ "alignment" : 1,
+ "is_const" : true,
+ "linker_set_key" : "_ZTIKc",
+ "name" : "const char",
+ "referenced_type" : "_ZTIc",
+ "self_type" : "_ZTIKc",
+ "size" : 1,
+ "source_file" : "system/core/libcutils/include/cutils/config_utils.h"
+ },
+ {
+ "is_const" : true,
+ "linker_set_key" : "_ZTIKv",
+ "name" : "const void",
+ "referenced_type" : "_ZTIv",
+ "self_type" : "_ZTIKv",
+ "source_file" : "system/core/libcutils/include/cutils/sockets.h"
+ }
+ ],
+ "record_types" :
+ [
+ {
+ "alignment" : 4,
+ "fields" :
+ [
+ {
+ "field_name" : "version",
+ "referenced_type" : "_ZTIi"
+ },
+ {
+ "field_name" : "numFds",
+ "field_offset" : 32,
+ "referenced_type" : "_ZTIi"
+ },
+ {
+ "field_name" : "numInts",
+ "field_offset" : 64,
+ "referenced_type" : "_ZTIi"
+ },
+ {
+ "field_name" : "data",
+ "field_offset" : 96,
+ "referenced_type" : "_ZTIA0_i"
+ }
+ ],
+ "linker_set_key" : "_ZTI13native_handle",
+ "name" : "native_handle",
+ "referenced_type" : "_ZTI13native_handle",
+ "self_type" : "_ZTI13native_handle",
+ "size" : 12,
+ "source_file" : "system/core/libcutils/include/cutils/native_handle.h"
+ },
+ {
+ "alignment" : 4,
+ "fields" :
+ [
+ {
+ "field_name" : "data",
+ "referenced_type" : "_ZTIPKv"
+ },
+ {
+ "field_name" : "length",
+ "field_offset" : 32,
+ "referenced_type" : "_ZTIj"
+ }
+ ],
+ "linker_set_key" : "_ZTI22cutils_socket_buffer_t",
+ "name" : "cutils_socket_buffer_t",
+ "referenced_type" : "_ZTI22cutils_socket_buffer_t",
+ "self_type" : "_ZTI22cutils_socket_buffer_t",
+ "size" : 8,
+ "source_file" : "system/core/libcutils/include/cutils/sockets.h"
+ },
+ {
+ "alignment" : 4,
+ "fields" :
+ [
+ {
+ "field_name" : "next",
+ "referenced_type" : "_ZTIP5cnode"
+ },
+ {
+ "field_name" : "first_child",
+ "field_offset" : 32,
+ "referenced_type" : "_ZTIP5cnode"
+ },
+ {
+ "field_name" : "last_child",
+ "field_offset" : 64,
+ "referenced_type" : "_ZTIP5cnode"
+ },
+ {
+ "field_name" : "name",
+ "field_offset" : 96,
+ "referenced_type" : "_ZTIPKc"
+ },
+ {
+ "field_name" : "value",
+ "field_offset" : 128,
+ "referenced_type" : "_ZTIPKc"
+ }
+ ],
+ "linker_set_key" : "_ZTI5cnode",
+ "name" : "cnode",
+ "referenced_type" : "_ZTI5cnode",
+ "self_type" : "_ZTI5cnode",
+ "size" : 20,
+ "source_file" : "system/core/libcutils/include/cutils/config_utils.h"
+ }
+ ],
+ "rvalue_reference_types" : []
+}
diff --git a/libcutils/include/cutils/threads.h b/libcutils/include/cutils/threads.h
index 0082c6c..1886184 100644
--- a/libcutils/include/cutils/threads.h
+++ b/libcutils/include/cutils/threads.h
@@ -31,7 +31,7 @@
//
// Deprecated: use android::base::GetThreadId instead, which doesn't truncate on Mac/Windows.
//
-#if !defined(__GLIBC__) || __GLIBC__ >= 2 && __GLIBC_MINOR__ < 32
+#if !defined(__GLIBC__) || __GLIBC__ >= 2 && __GLIBC_MINOR__ < 30
extern pid_t gettid();
#endif
diff --git a/libcutils/include/cutils/trace.h b/libcutils/include/cutils/trace.h
index 3867f34..7f57637 100644
--- a/libcutils/include/cutils/trace.h
+++ b/libcutils/include/cutils/trace.h
@@ -89,6 +89,36 @@
#error ATRACE_TAG must be defined to be one of the tags defined in cutils/trace.h
#endif
+/** Internal implementation detail. Do not use. */
+void atrace_begin_body(const char*);
+
+/** Internal implementation detail. Do not use. */
+void atrace_end_body();
+
+/** Internal implementation detail. Do not use. */
+void atrace_async_begin_body(const char*, int32_t);
+
+/** Internal implementation detail. Do not use. */
+void atrace_async_end_body(const char*, int32_t);
+
+/** Internal implementation detail. Do not use. */
+void atrace_async_for_track_begin_body(const char*, const char*, int32_t);
+
+/** Internal implementation detail. Do not use. */
+void atrace_async_for_track_end_body(const char*, int32_t);
+
+/** Internal implementation detail. Do not use. */
+void atrace_instant_body(const char*);
+
+/** Internal implementation detail. Do not use. */
+void atrace_instant_for_track_body(const char*, const char*);
+
+/** Internal implementation detail. Do not use. */
+void atrace_int_body(const char*, int32_t);
+
+/** Internal implementation detail. Do not use. */
+void atrace_int64_body(const char*, int64_t);
+
/**
* Opens the trace file for writing and reads the property for initial tags.
* The atrace.tags.enableflags property sets the tags to trace.
@@ -159,7 +189,6 @@
static inline void atrace_begin(uint64_t tag, const char* name)
{
if (CC_UNLIKELY(atrace_is_tag_enabled(tag))) {
- void atrace_begin_body(const char*);
atrace_begin_body(name);
}
}
@@ -172,7 +201,6 @@
static inline void atrace_end(uint64_t tag)
{
if (CC_UNLIKELY(atrace_is_tag_enabled(tag))) {
- void atrace_end_body();
atrace_end_body();
}
}
@@ -190,7 +218,6 @@
int32_t cookie)
{
if (CC_UNLIKELY(atrace_is_tag_enabled(tag))) {
- void atrace_async_begin_body(const char*, int32_t);
atrace_async_begin_body(name, cookie);
}
}
@@ -203,7 +230,6 @@
static inline void atrace_async_end(uint64_t tag, const char* name, int32_t cookie)
{
if (CC_UNLIKELY(atrace_is_tag_enabled(tag))) {
- void atrace_async_end_body(const char*, int32_t);
atrace_async_end_body(name, cookie);
}
}
@@ -221,7 +247,6 @@
static inline void atrace_async_for_track_begin(uint64_t tag, const char* track_name,
const char* name, int32_t cookie) {
if (CC_UNLIKELY(atrace_is_tag_enabled(tag))) {
- void atrace_async_for_track_begin_body(const char*, const char*, int32_t);
atrace_async_for_track_begin_body(track_name, name, cookie);
}
}
@@ -235,7 +260,6 @@
static inline void atrace_async_for_track_end(uint64_t tag, const char* track_name,
int32_t cookie) {
if (CC_UNLIKELY(atrace_is_tag_enabled(tag))) {
- void atrace_async_for_track_end_body(const char*, int32_t);
atrace_async_for_track_end_body(track_name, cookie);
}
}
@@ -252,7 +276,6 @@
#define ATRACE_INSTANT(name) atrace_instant(ATRACE_TAG, name)
static inline void atrace_instant(uint64_t tag, const char* name) {
if (CC_UNLIKELY(atrace_is_tag_enabled(tag))) {
- void atrace_instant_body(const char*);
atrace_instant_body(name);
}
}
@@ -269,7 +292,6 @@
static inline void atrace_instant_for_track(uint64_t tag, const char* track_name,
const char* name) {
if (CC_UNLIKELY(atrace_is_tag_enabled(tag))) {
- void atrace_instant_for_track_body(const char*, const char*);
atrace_instant_for_track_body(track_name, name);
}
}
@@ -282,7 +304,6 @@
static inline void atrace_int(uint64_t tag, const char* name, int32_t value)
{
if (CC_UNLIKELY(atrace_is_tag_enabled(tag))) {
- void atrace_int_body(const char*, int32_t);
atrace_int_body(name, value);
}
}
@@ -295,7 +316,6 @@
static inline void atrace_int64(uint64_t tag, const char* name, int64_t value)
{
if (CC_UNLIKELY(atrace_is_tag_enabled(tag))) {
- void atrace_int64_body(const char*, int64_t);
atrace_int64_body(name, value);
}
}
diff --git a/libcutils/threads.cpp b/libcutils/threads.cpp
index 6ece7a3..2638720 100644
--- a/libcutils/threads.cpp
+++ b/libcutils/threads.cpp
@@ -25,9 +25,9 @@
#include <windows.h>
#endif
-#if defined(__BIONIC__) || defined(__GLIBC__) && __GLIBC_MINOR__ >= 32
+#if defined(__BIONIC__) || defined(__GLIBC__) && __GLIBC_MINOR__ >= 30
// No definition needed for Android because we'll just pick up bionic's copy.
-// No definition needed for Glibc >= 2.32 because it exposes its own copy.
+// No definition needed for Glibc >= 2.30 because it exposes its own copy.
#else
pid_t gettid() {
#if defined(__APPLE__)
diff --git a/libcutils/trace-host.cpp b/libcutils/trace-host.cpp
index e9f58c3..2bf57eb 100644
--- a/libcutils/trace-host.cpp
+++ b/libcutils/trace-host.cpp
@@ -20,7 +20,6 @@
int atrace_marker_fd = -1;
uint64_t atrace_enabled_tags = 0;
-void atrace_set_debuggable(bool /*debuggable*/) {}
void atrace_set_tracing_enabled(bool /*enabled*/) {}
void atrace_update_tags() { }
void atrace_setup() { }
diff --git a/libdiskconfig/Android.bp b/libdiskconfig/Android.bp
deleted file mode 100644
index f523d4e..0000000
--- a/libdiskconfig/Android.bp
+++ /dev/null
@@ -1,36 +0,0 @@
-package {
- default_applicable_licenses: ["Android-Apache-2.0"],
-}
-
-cc_library {
- name: "libdiskconfig",
- vendor_available: true,
- vndk: {
- enabled: true,
- },
- srcs: [
- "diskconfig.c",
- "diskutils.c",
- "write_lst.c",
- "config_mbr.c",
- ],
-
- shared_libs: [
- "libcutils",
- "liblog",
- ],
- cflags: ["-Werror"],
- export_include_dirs: ["include"],
- local_include_dirs: ["include"],
-
- target: {
- darwin: {
- enabled: false,
- },
- host_linux: {
- cflags: [
- "-D_LARGEFILE64_SOURCE",
- ],
- },
- },
-}
diff --git a/libdiskconfig/config_mbr.c b/libdiskconfig/config_mbr.c
deleted file mode 100644
index ace9bbf..0000000
--- a/libdiskconfig/config_mbr.c
+++ /dev/null
@@ -1,351 +0,0 @@
-/* libs/diskconfig/diskconfig.c
- *
- * Copyright 2008, 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.
- */
-
-#define LOG_TAG "config_mbr"
-
-#include <stdint.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-
-#include <diskconfig/diskconfig.h>
-#include <log/log.h>
-
-/* start and len are in LBA units */
-static void
-cfg_pentry(struct pc_partition *pentry, uint8_t status, uint8_t type,
- uint32_t start, uint32_t len)
-{
- if (len > 0) {
- /* seems that somes BIOSens can get wedged on boot while verifying
- * the mbr if these are 0 */
- memset(&pentry->start, 0xff, sizeof(struct chs));
- memset(&pentry->end, 0xff, sizeof(struct chs));
- } else {
- /* zero out the c/h/s entries.. they are not used */
- memset(&pentry->start, 0, sizeof(struct chs));
- memset(&pentry->end, 0, sizeof(struct chs));
- }
-
- pentry->status = status;
- pentry->type = type;
- pentry->start_lba = start;
- pentry->len_lba = len;
-
- ALOGI("Configuring pentry. status=0x%x type=0x%x start_lba=%u len_lba=%u",
- pentry->status, pentry->type, pentry->start_lba, pentry->len_lba);
-}
-
-
-static inline uint32_t
-kb_to_lba(uint32_t len_kb, uint32_t sect_size)
-{
- uint64_t lba;
-
- lba = (uint64_t)len_kb * 1024;
- /* bump it up to the next LBA boundary just in case */
- lba = (lba + (uint64_t)sect_size - 1) & ~((uint64_t)sect_size - 1);
- lba /= (uint64_t)sect_size;
- if (lba >= 0xffffffffULL)
- ALOGE("Error converting kb -> lba. 32bit overflow, expect weirdness");
- return (uint32_t)(lba & 0xffffffffULL);
-}
-
-
-static struct write_list *
-mk_pri_pentry(struct disk_info *dinfo, struct part_info *pinfo, int pnum,
- uint32_t *lba)
-{
- struct write_list *item;
- struct pc_partition *pentry;
-
- if (pnum >= PC_NUM_BOOT_RECORD_PARTS) {
- ALOGE("Maximum number of primary partition exceeded.");
- return NULL;
- }
-
- if (!(item = alloc_wl(sizeof(struct pc_partition)))) {
- ALOGE("Unable to allocate memory for partition entry.");
- return NULL;
- }
-
- {
- /* DO NOT DEREFERENCE */
- struct pc_boot_record *mbr = (void *)PC_MBR_DISK_OFFSET;
- /* grab the offset in mbr where to write this partition entry. */
- item->offset = (loff_t)((uintptr_t)((uint8_t *)(&mbr->ptable[pnum])));
- }
-
- pentry = (struct pc_partition *) &item->data;
-
- /* need a standard primary partition entry */
- if (pinfo) {
- /* need this to be 64 bit in case len_kb is large */
- uint64_t len_lba;
-
- if (pinfo->len_kb != (uint32_t)-1) {
- /* bump it up to the next LBA boundary just in case */
- len_lba = ((uint64_t)pinfo->len_kb * 1024);
- len_lba += ((uint64_t)dinfo->sect_size - 1);
- len_lba &= ~((uint64_t)dinfo->sect_size - 1);
- len_lba /= (uint64_t)dinfo->sect_size;
- } else {
- /* make it fill the rest of disk */
- len_lba = dinfo->num_lba - *lba;
- }
-
- cfg_pentry(pentry, ((pinfo->flags & PART_ACTIVE_FLAG) ?
- PC_PART_ACTIVE : PC_PART_NORMAL),
- pinfo->type, *lba, (uint32_t)len_lba);
-
- pinfo->start_lba = *lba;
- *lba += (uint32_t)len_lba;
- } else {
- /* this should be made an extended partition, and should take
- * up the rest of the disk as a primary partition */
- cfg_pentry(pentry, PC_PART_NORMAL, PC_PART_TYPE_EXTENDED,
- *lba, dinfo->num_lba - *lba);
-
- /* note that we do not update the *lba because we now have to
- * create a chain of extended partition tables, and first one is at
- * *lba */
- }
-
- return item;
-}
-
-
-/* This function configures an extended boot record at the beginning of an
- * extended partition. This creates a logical partition and a pointer to
- * the next EBR.
- *
- * ext_lba == The start of the toplevel extended partition (pointed to by the
- * entry in the MBR).
- */
-static struct write_list *
-mk_ext_pentry(struct disk_info *dinfo, struct part_info *pinfo, uint32_t *lba,
- uint32_t ext_lba, struct part_info *pnext)
-{
- struct write_list *item;
- struct pc_boot_record *ebr;
- uint32_t len; /* in lba units */
-
- if (!(item = alloc_wl(sizeof(struct pc_boot_record)))) {
- ALOGE("Unable to allocate memory for EBR.");
- return NULL;
- }
-
- /* we are going to write the ebr at the current LBA, and then bump the
- * lba counter since that is where the logical data partition will start */
- item->offset = ((loff_t)(*lba)) * dinfo->sect_size;
- (*lba)++;
-
- ebr = (struct pc_boot_record *) &item->data;
- memset(ebr, 0, sizeof(struct pc_boot_record));
- ebr->mbr_sig = PC_BIOS_BOOT_SIG;
-
- if (pinfo->len_kb != (uint32_t)-1)
- len = kb_to_lba(pinfo->len_kb, dinfo->sect_size);
- else {
- if (pnext) {
- ALOGE("Only the last partition can be specified to fill the disk "
- "(name = '%s')", pinfo->name);
- goto fail;
- }
- len = dinfo->num_lba - *lba;
- /* update the pinfo structure to reflect the new size, for
- * bookkeeping */
- pinfo->len_kb =
- (uint32_t)(((uint64_t)len * (uint64_t)dinfo->sect_size) /
- ((uint64_t)1024));
- }
-
- cfg_pentry(&ebr->ptable[PC_EBR_LOGICAL_PART], PC_PART_NORMAL,
- pinfo->type, 1, len);
-
- pinfo->start_lba = *lba;
- *lba += len;
-
- /* If this is not the last partition, we have to create a link to the
- * next extended partition.
- *
- * Otherwise, there's nothing to do since the "pointer entry" is
- * already zero-filled.
- */
- if (pnext) {
- /* The start lba for next partition is an offset from the beginning
- * of the top-level extended partition */
- uint32_t next_start_lba = *lba - ext_lba;
- uint32_t next_len_lba;
- if (pnext->len_kb != (uint32_t)-1)
- next_len_lba = 1 + kb_to_lba(pnext->len_kb, dinfo->sect_size);
- else
- next_len_lba = dinfo->num_lba - *lba;
- cfg_pentry(&ebr->ptable[PC_EBR_NEXT_PTR_PART], PC_PART_NORMAL,
- PC_PART_TYPE_EXTENDED, next_start_lba, next_len_lba);
- }
-
- return item;
-
-fail:
- free_wl(item);
- return NULL;
-}
-
-
-static struct write_list *
-mk_mbr_sig()
-{
- struct write_list *item;
- if (!(item = alloc_wl(sizeof(uint16_t)))) {
- ALOGE("Unable to allocate memory for MBR signature.");
- return NULL;
- }
-
- {
- /* DO NOT DEREFERENCE */
- struct pc_boot_record *mbr = (void *)PC_MBR_DISK_OFFSET;
- /* grab the offset in mbr where to write mbr signature. */
- item->offset = (loff_t)((uintptr_t)((uint8_t *)(&mbr->mbr_sig)));
- }
-
- *((uint16_t*)item->data) = PC_BIOS_BOOT_SIG;
- return item;
-}
-
-struct write_list *
-config_mbr(struct disk_info *dinfo)
-{
- struct part_info *pinfo;
- uint32_t cur_lba = dinfo->skip_lba;
- uint32_t ext_lba = 0;
- struct write_list *wr_list = NULL;
- struct write_list *temp_wr = NULL;
- int cnt = 0;
- int extended = 0;
-
- if (!dinfo->part_lst)
- return NULL;
-
- for (cnt = 0; cnt < dinfo->num_parts; ++cnt) {
- pinfo = &dinfo->part_lst[cnt];
-
- /* Should we create an extedned partition? */
- if (cnt == (PC_NUM_BOOT_RECORD_PARTS - 1)) {
- if (cnt + 1 < dinfo->num_parts) {
- extended = 1;
- ext_lba = cur_lba;
- if ((temp_wr = mk_pri_pentry(dinfo, NULL, cnt, &cur_lba)))
- wlist_add(&wr_list, temp_wr);
- else {
- ALOGE("Cannot create primary extended partition.");
- goto fail;
- }
- }
- }
-
- /* if extended, need 1 lba for ebr */
- if ((cur_lba + extended) >= dinfo->num_lba)
- goto nospace;
- else if (pinfo->len_kb != (uint32_t)-1) {
- uint32_t sz_lba = (pinfo->len_kb / dinfo->sect_size) * 1024;
- if ((cur_lba + sz_lba + extended) > dinfo->num_lba)
- goto nospace;
- }
-
- if (!extended)
- temp_wr = mk_pri_pentry(dinfo, pinfo, cnt, &cur_lba);
- else {
- struct part_info *pnext;
- pnext = cnt + 1 < dinfo->num_parts ? &dinfo->part_lst[cnt+1] : NULL;
- temp_wr = mk_ext_pentry(dinfo, pinfo, &cur_lba, ext_lba, pnext);
- }
-
- if (temp_wr)
- wlist_add(&wr_list, temp_wr);
- else {
- ALOGE("Cannot create partition %d (%s).", cnt, pinfo->name);
- goto fail;
- }
- }
-
- /* fill in the rest of the MBR with empty parts (if needed). */
- for (; cnt < PC_NUM_BOOT_RECORD_PARTS; ++cnt) {
- struct part_info blank;
- cur_lba = 0;
- memset(&blank, 0, sizeof(struct part_info));
- if (!(temp_wr = mk_pri_pentry(dinfo, &blank, cnt, &cur_lba))) {
- ALOGE("Cannot create blank partition %d.", cnt);
- goto fail;
- }
- wlist_add(&wr_list, temp_wr);
- }
-
- if ((temp_wr = mk_mbr_sig()))
- wlist_add(&wr_list, temp_wr);
- else {
- ALOGE("Cannot set MBR signature");
- goto fail;
- }
-
- return wr_list;
-
-nospace:
- ALOGE("Not enough space to add parttion '%s'.", pinfo->name);
-
-fail:
- wlist_free(wr_list);
- return NULL;
-}
-
-
-/* Returns the device path of the partition referred to by 'name'
- * Must be freed by the caller.
- */
-char *
-find_mbr_part(struct disk_info *dinfo, const char *name)
-{
- struct part_info *plist = dinfo->part_lst;
- int num = 0;
- char *dev_name = NULL;
- int has_extended = (dinfo->num_parts > PC_NUM_BOOT_RECORD_PARTS);
-
- for(num = 1; num <= dinfo->num_parts; ++num) {
- if (!strcmp(plist[num-1].name, name))
- break;
- }
-
- if (num > dinfo->num_parts)
- return NULL;
-
- if (has_extended && (num >= PC_NUM_BOOT_RECORD_PARTS))
- num++;
-
- if (!(dev_name = malloc(MAX_NAME_LEN))) {
- ALOGE("Cannot allocate memory.");
- return NULL;
- }
-
- num = snprintf(dev_name, MAX_NAME_LEN, "%s%d", dinfo->device, num);
- if (num >= MAX_NAME_LEN) {
- ALOGE("Device name is too long?!");
- free(dev_name);
- return NULL;
- }
-
- return dev_name;
-}
diff --git a/libdiskconfig/diskconfig.c b/libdiskconfig/diskconfig.c
deleted file mode 100644
index 5f34748..0000000
--- a/libdiskconfig/diskconfig.c
+++ /dev/null
@@ -1,535 +0,0 @@
-/* libs/diskconfig/diskconfig.c
- *
- * Copyright 2008, 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.
- */
-
-#define LOG_TAG "diskconfig"
-
-#include <errno.h>
-#include <fcntl.h>
-#include <inttypes.h>
-#include <linux/fs.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/ioctl.h>
-#include <sys/stat.h>
-#include <unistd.h>
-
-#include <cutils/config_utils.h>
-#include <log/log.h>
-
-#include <diskconfig/diskconfig.h>
-
-static int
-parse_len(const char *str, uint64_t *plen)
-{
- char tmp[64];
- int len_str;
- uint32_t multiple = 1;
-
- strncpy(tmp, str, sizeof(tmp));
- tmp[sizeof(tmp)-1] = '\0';
- len_str = strlen(tmp);
- if (!len_str) {
- ALOGE("Invalid disk length specified.");
- return 1;
- }
-
- switch(tmp[len_str - 1]) {
- case 'M': case 'm':
- /* megabyte */
- multiple <<= 10;
- case 'K': case 'k':
- /* kilobytes */
- multiple <<= 10;
- tmp[len_str - 1] = '\0';
- break;
- default:
- break;
- }
-
- *plen = strtoull(tmp, NULL, 0);
- if (!*plen) {
- ALOGE("Invalid length specified: %s", str);
- return 1;
- }
-
- if (*plen == (uint64_t)-1) {
- if (multiple > 1) {
- ALOGE("Size modifier illegal when len is -1");
- return 1;
- }
- } else {
- /* convert len to kilobytes */
- if (multiple > 1024)
- multiple >>= 10;
- *plen *= multiple;
-
- if (*plen > 0xffffffffULL) {
- ALOGE("Length specified is too large!: %"PRIu64" KB", *plen);
- return 1;
- }
- }
-
- return 0;
-}
-
-
-static int
-load_partitions(cnode *root, struct disk_info *dinfo)
-{
- cnode *partnode;
-
- dinfo->num_parts = 0;
- for (partnode = root->first_child; partnode; partnode = partnode->next) {
- struct part_info *pinfo = &dinfo->part_lst[dinfo->num_parts];
- const char *tmp;
-
- /* bleh, i will leak memory here, but i DONT CARE since
- * the only right thing to do when this function fails
- * is to quit */
- pinfo->name = strdup(partnode->name);
-
- if(config_bool(partnode, "active", 0))
- pinfo->flags |= PART_ACTIVE_FLAG;
-
- if (!(tmp = config_str(partnode, "type", NULL))) {
- ALOGE("Partition type required: %s", pinfo->name);
- return 1;
- }
-
- /* possible values are: linux, fat32 */
- if (!strcmp(tmp, "linux")) {
- pinfo->type = PC_PART_TYPE_LINUX;
- } else if (!strcmp(tmp, "fat32")) {
- pinfo->type = PC_PART_TYPE_FAT32;
- } else {
- ALOGE("Unsupported partition type found: %s", tmp);
- return 1;
- }
-
- if ((tmp = config_str(partnode, "len", NULL)) != NULL) {
- uint64_t len;
- if (parse_len(tmp, &len))
- return 1;
- pinfo->len_kb = (uint32_t) len;
- } else
- pinfo->len_kb = 0;
-
- ++dinfo->num_parts;
- }
-
- return 0;
-}
-
-struct disk_info *
-load_diskconfig(const char *fn, char *path_override)
-{
- struct disk_info *dinfo;
- cnode *devroot;
- cnode *partnode;
- cnode *root = config_node("", "");
- const char *tmp;
-
- if (!(dinfo = malloc(sizeof(struct disk_info)))) {
- ALOGE("Could not malloc disk_info");
- return NULL;
- }
- memset(dinfo, 0, sizeof(struct disk_info));
-
- if (!(dinfo->part_lst = malloc(MAX_NUM_PARTS * sizeof(struct part_info)))) {
- ALOGE("Could not malloc part_lst");
- goto fail;
- }
- memset(dinfo->part_lst, 0,
- (MAX_NUM_PARTS * sizeof(struct part_info)));
-
- config_load_file(root, fn);
- if (root->first_child == NULL) {
- ALOGE("Could not read config file %s", fn);
- goto fail;
- }
-
- if (!(devroot = config_find(root, "device"))) {
- ALOGE("Could not find device section in config file '%s'", fn);
- goto fail;
- }
-
-
- if (!(tmp = config_str(devroot, "path", path_override))) {
- ALOGE("device path is requried");
- goto fail;
- }
- dinfo->device = strdup(tmp);
-
- /* find the partition scheme */
- if (!(tmp = config_str(devroot, "scheme", NULL))) {
- ALOGE("partition scheme is required");
- goto fail;
- } else if (!strcmp(tmp, "mbr")) {
- dinfo->scheme = PART_SCHEME_MBR;
- } else if (!strcmp(tmp, "gpt")) {
- ALOGE("'gpt' partition scheme not supported yet.");
- goto fail;
- } else {
- ALOGE("Unknown partition scheme specified: %s", tmp);
- goto fail;
- }
-
- /* grab the sector size (in bytes) */
- tmp = config_str(devroot, "sector_size", "512");
- dinfo->sect_size = strtol(tmp, NULL, 0);
- if (!dinfo->sect_size) {
- ALOGE("Invalid sector size: %s", tmp);
- goto fail;
- }
-
- /* first lba where the partitions will start on disk */
- if (!(tmp = config_str(devroot, "start_lba", NULL))) {
- ALOGE("start_lba must be provided");
- goto fail;
- }
-
- if (!(dinfo->skip_lba = strtol(tmp, NULL, 0))) {
- ALOGE("Invalid starting LBA (or zero): %s", tmp);
- goto fail;
- }
-
- /* Number of LBAs on disk */
- if (!(tmp = config_str(devroot, "num_lba", NULL))) {
- ALOGE("num_lba is required");
- goto fail;
- }
- dinfo->num_lba = strtoul(tmp, NULL, 0);
-
- if (!(partnode = config_find(devroot, "partitions"))) {
- ALOGE("Device must specify partition list");
- goto fail;
- }
-
- if (load_partitions(partnode, dinfo))
- goto fail;
-
- return dinfo;
-
-fail:
- if (dinfo->part_lst)
- free(dinfo->part_lst);
- if (dinfo->device)
- free(dinfo->device);
- free(dinfo);
- return NULL;
-}
-
-static int
-sync_ptable(int fd)
-{
- struct stat stat;
- int rv;
-
- sync();
-
- if (fstat(fd, &stat)) {
- ALOGE("Cannot stat, errno=%d.", errno);
- return -1;
- }
-
- if (S_ISBLK(stat.st_mode) && ((rv = ioctl(fd, BLKRRPART, NULL)) < 0)) {
- ALOGE("Could not re-read partition table. REBOOT!. (errno=%d)", errno);
- return -1;
- }
-
- return 0;
-}
-
-/* This function verifies that the disk info provided is valid, and if so,
- * returns an open file descriptor.
- *
- * This does not necessarily mean that it will later be successfully written
- * though. If we use the pc-bios partitioning scheme, we must use extended
- * partitions, which eat up some hd space. If the user manually provisioned
- * every single partition, but did not account for the extra needed space,
- * then we will later fail.
- *
- * TODO: Make validation more complete.
- */
-static int
-validate(struct disk_info *dinfo)
-{
- int fd;
- int sect_sz;
- uint64_t disk_size;
- uint64_t total_size;
- int cnt;
- struct stat stat;
-
- if (!dinfo)
- return -1;
-
- if ((fd = open(dinfo->device, O_RDWR)) < 0) {
- ALOGE("Cannot open device '%s' (errno=%d)", dinfo->device, errno);
- return -1;
- }
-
- if (fstat(fd, &stat)) {
- ALOGE("Cannot stat file '%s', errno=%d.", dinfo->device, errno);
- goto fail;
- }
-
-
- /* XXX: Some of the code below is kind of redundant and should probably
- * be refactored a little, but it will do for now. */
-
- /* Verify that we can operate on the device that was requested.
- * We presently only support block devices and regular file images. */
- if (S_ISBLK(stat.st_mode)) {
- /* get the sector size and make sure we agree */
- if (ioctl(fd, BLKSSZGET, §_sz) < 0) {
- ALOGE("Cannot get sector size (errno=%d)", errno);
- goto fail;
- }
-
- if (!sect_sz || sect_sz != dinfo->sect_size) {
- ALOGE("Device sector size is zero or sector sizes do not match!");
- goto fail;
- }
-
- /* allow the user override the "disk size" if they provided num_lba */
- if (!dinfo->num_lba) {
- if (ioctl(fd, BLKGETSIZE64, &disk_size) < 0) {
- ALOGE("Could not get block device size (errno=%d)", errno);
- goto fail;
- }
- /* XXX: we assume that the disk has < 2^32 sectors :-) */
- dinfo->num_lba = (uint32_t)(disk_size / (uint64_t)dinfo->sect_size);
- } else
- disk_size = (uint64_t)dinfo->num_lba * (uint64_t)dinfo->sect_size;
- } else if (S_ISREG(stat.st_mode)) {
- ALOGI("Requesting operation on a regular file, not block device.");
- if (!dinfo->sect_size) {
- ALOGE("Sector size for regular file images cannot be zero");
- goto fail;
- }
- if (dinfo->num_lba)
- disk_size = (uint64_t)dinfo->num_lba * (uint64_t)dinfo->sect_size;
- else {
- dinfo->num_lba = (uint32_t)(stat.st_size / dinfo->sect_size);
- disk_size = (uint64_t)stat.st_size;
- }
- } else {
- ALOGE("Device does not refer to a regular file or a block device!");
- goto fail;
- }
-
-#if 1
- ALOGV("Device/file %s: size=%" PRIu64 " bytes, num_lba=%u, sect_size=%d",
- dinfo->device, disk_size, dinfo->num_lba, dinfo->sect_size);
-#endif
-
- /* since this is our offset into the disk, we start off with that as
- * our size of needed partitions */
- total_size = dinfo->skip_lba * dinfo->sect_size;
-
- /* add up all the partition sizes and make sure it fits */
- for (cnt = 0; cnt < dinfo->num_parts; ++cnt) {
- struct part_info *part = &dinfo->part_lst[cnt];
- if (part->len_kb != (uint32_t)-1) {
- total_size += part->len_kb * 1024;
- } else if (part->len_kb == 0) {
- ALOGE("Zero-size partition '%s' is invalid.", part->name);
- goto fail;
- } else {
- /* the partition requests the rest of the disk. */
- if (cnt + 1 != dinfo->num_parts) {
- ALOGE("Only the last partition in the list can request to fill "
- "the rest of disk.");
- goto fail;
- }
- }
-
- if ((part->type != PC_PART_TYPE_LINUX) &&
- (part->type != PC_PART_TYPE_FAT32)) {
- ALOGE("Unknown partition type (0x%x) encountered for partition "
- "'%s'\n", part->type, part->name);
- goto fail;
- }
- }
-
- /* only matters for disks, not files */
- if (S_ISBLK(stat.st_mode) && total_size > disk_size) {
- ALOGE("Total requested size of partitions (%"PRIu64") is greater than disk "
- "size (%"PRIu64").", total_size, disk_size);
- goto fail;
- }
-
- return fd;
-
-fail:
- close(fd);
- return -1;
-}
-
-static int
-validate_and_config(struct disk_info *dinfo, int *fd, struct write_list **lst)
-{
- *lst = NULL;
- *fd = -1;
-
- if ((*fd = validate(dinfo)) < 0)
- return 1;
-
- switch (dinfo->scheme) {
- case PART_SCHEME_MBR:
- *lst = config_mbr(dinfo);
- return *lst == NULL;
- case PART_SCHEME_GPT:
- /* not supported yet */
- default:
- ALOGE("Unknown partition scheme.");
- break;
- }
-
- close(*fd);
- *lst = NULL;
- return 1;
-}
-
-/* validate and process the disk layout configuration.
- * This will cause an update to the partitions' start lba.
- *
- * Basically, this does the same thing as apply_disk_config in test mode,
- * except that wlist_commit is not called to print out the data to be
- * written.
- */
-int
-process_disk_config(struct disk_info *dinfo)
-{
- struct write_list *lst;
- int fd;
-
- if (validate_and_config(dinfo, &fd, &lst) != 0)
- return 1;
-
- close(fd);
- wlist_free(lst);
- return 0;
-}
-
-
-int
-apply_disk_config(struct disk_info *dinfo, int test)
-{
- int fd;
- struct write_list *wr_lst = NULL;
- int rv;
-
- if (validate_and_config(dinfo, &fd, &wr_lst) != 0) {
- ALOGE("Configuration is invalid.");
- goto fail;
- }
-
- if ((rv = wlist_commit(fd, wr_lst, test)) >= 0)
- rv = test ? 0 : sync_ptable(fd);
-
- close(fd);
- wlist_free(wr_lst);
- return rv;
-
-fail:
- close(fd);
- if (wr_lst)
- wlist_free(wr_lst);
- return 1;
-}
-
-int
-dump_disk_config(struct disk_info *dinfo)
-{
- int cnt;
- struct part_info *part;
-
- printf("Device: %s\n", dinfo->device);
- printf("Scheme: ");
- switch (dinfo->scheme) {
- case PART_SCHEME_MBR:
- printf("MBR");
- break;
- case PART_SCHEME_GPT:
- printf("GPT (unsupported)");
- break;
- default:
- printf("Unknown");
- break;
- }
- printf ("\n");
-
- printf("Sector size: %d\n", dinfo->sect_size);
- printf("Skip leading LBAs: %u\n", dinfo->skip_lba);
- printf("Number of LBAs: %u\n", dinfo->num_lba);
- printf("Partitions:\n");
-
- for (cnt = 0; cnt < dinfo->num_parts; ++cnt) {
- part = &dinfo->part_lst[cnt];
- printf("\tname = %s\n", part->name);
- printf("\t\tflags = %s\n",
- part->flags & PART_ACTIVE_FLAG ? "Active" : "None");
- printf("\t\ttype = %s\n",
- part->type == PC_PART_TYPE_LINUX ? "Linux" : "Unknown");
- if (part->len_kb == (uint32_t)-1)
- printf("\t\tlen = rest of disk\n");
- else
- printf("\t\tlen = %uKB\n", part->len_kb);
- }
- printf("Total number of partitions: %d\n", cnt);
- printf("\n");
-
- return 0;
-}
-
-struct part_info *
-find_part(struct disk_info *dinfo, const char *name)
-{
- struct part_info *pinfo;
- int cnt;
-
- for (cnt = 0; cnt < dinfo->num_parts; ++cnt) {
- pinfo = &dinfo->part_lst[cnt];
- if (!strcmp(pinfo->name, name))
- return pinfo;
- }
-
- return NULL;
-}
-
-/* NOTE: If the returned ptr is non-NULL, it must be freed by the caller. */
-char *
-find_part_device(struct disk_info *dinfo, const char *name)
-{
- switch (dinfo->scheme) {
- case PART_SCHEME_MBR:
- return find_mbr_part(dinfo, name);
- case PART_SCHEME_GPT:
- ALOGE("GPT is presently not supported");
- break;
- default:
- ALOGE("Unknown partition table scheme");
- break;
- }
-
- return NULL;
-}
-
-
diff --git a/libdiskconfig/diskutils.c b/libdiskconfig/diskutils.c
deleted file mode 100644
index fe1b4c1..0000000
--- a/libdiskconfig/diskutils.c
+++ /dev/null
@@ -1,118 +0,0 @@
-/* libs/diskconfig/diskutils.c
- *
- * Copyright 2008, 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.
- */
-
-#define LOG_TAG "diskutils"
-
-#include <errno.h>
-#include <fcntl.h>
-#include <inttypes.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/stat.h>
-#include <unistd.h>
-
-#include <log/log.h>
-
-#include <diskconfig/diskconfig.h>
-
-int
-write_raw_image(const char *dst, const char *src, loff_t offset, int test)
-{
- int dst_fd = -1;
- int src_fd = -1;
- uint8_t buffer[2048];
- ssize_t nr_bytes;
- ssize_t tmp;
- int done = 0;
- uint64_t total = 0;
-
- ALOGI("Writing RAW image '%s' to '%s' (offset=%llu)", src, dst, (unsigned long long)offset);
- if ((src_fd = open(src, O_RDONLY)) < 0) {
- ALOGE("Could not open %s for reading (errno=%d).", src, errno);
- goto fail;
- }
-
- if (!test) {
- if ((dst_fd = open(dst, O_RDWR)) < 0) {
- ALOGE("Could not open '%s' for read/write (errno=%d).", dst, errno);
- goto fail;
- }
-
- if (lseek64(dst_fd, offset, SEEK_SET) != offset) {
- ALOGE("Could not seek to offset %lld in %s.", (long long)offset, dst);
- goto fail;
- }
- }
-
- while (!done) {
- if ((nr_bytes = read(src_fd, buffer, sizeof(buffer))) < 0) {
- /* XXX: Should we not even bother with EINTR? */
- if (errno == EINTR)
- continue;
- ALOGE("Error (%d) while reading from '%s'", errno, src);
- goto fail;
- }
-
- if (!nr_bytes) {
- /* we're done. */
- done = 1;
- break;
- }
-
- total += nr_bytes;
-
- /* skip the write loop if we're testing */
- if (test)
- nr_bytes = 0;
-
- while (nr_bytes > 0) {
- if ((tmp = write(dst_fd, buffer, nr_bytes)) < 0) {
- /* XXX: Should we not even bother with EINTR? */
- if (errno == EINTR)
- continue;
- ALOGE("Error (%d) while writing to '%s'", errno, dst);
- goto fail;
- }
- if (!tmp)
- continue;
- nr_bytes -= tmp;
- }
- }
-
- if (!done) {
- ALOGE("Exited read/write loop without setting flag! WTF?!");
- goto fail;
- }
-
- if (dst_fd >= 0)
- fsync(dst_fd);
-
- ALOGI("Wrote %" PRIu64 " bytes to %s @ %lld", total, dst, (long long)offset);
-
- close(src_fd);
- if (dst_fd >= 0)
- close(dst_fd);
- return 0;
-
-fail:
- if (dst_fd >= 0)
- close(dst_fd);
- if (src_fd >= 0)
- close(src_fd);
- return 1;
-}
diff --git a/libdiskconfig/dump_diskconfig.c b/libdiskconfig/dump_diskconfig.c
deleted file mode 100644
index 3c4f620..0000000
--- a/libdiskconfig/dump_diskconfig.c
+++ /dev/null
@@ -1,43 +0,0 @@
-/* libs/diskconfig/dump_diskconfig.c
- *
- * Copyright 2008, 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.
- */
-
-#define LOG_TAG "dump_diskconfig"
-
-#include <stdio.h>
-
-#include <log/log.h>
-
-#include "diskconfig.h"
-
-int
-main(int argc, char *argv[])
-{
- struct disk_info *dinfo;
-
- if (argc < 2) {
- ALOGE("usage: %s <conf file>", argv[0]);
- return 1;
- }
-
- if (!(dinfo = load_diskconfig(argv[1], NULL)))
- return 1;
-
- dump_disk_config(dinfo);
-
- return 0;
-}
-
diff --git a/libdiskconfig/include/diskconfig/diskconfig.h b/libdiskconfig/include/diskconfig/diskconfig.h
deleted file mode 100644
index d45b99e..0000000
--- a/libdiskconfig/include/diskconfig/diskconfig.h
+++ /dev/null
@@ -1,130 +0,0 @@
-/* system/core/include/diskconfig/diskconfig.h
- *
- * Copyright 2008, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef __LIBS_DISKCONFIG_H
-#define __LIBS_DISKCONFIG_H
-
-#include <stdint.h>
-#include <sys/types.h>
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-#define MAX_NAME_LEN 512
-#define MAX_NUM_PARTS 16
-
-/* known partition schemes */
-#define PART_SCHEME_MBR 0x1
-#define PART_SCHEME_GPT 0x2
-
-/* PC Bios partition status */
-#define PC_PART_ACTIVE 0x80
-#define PC_PART_NORMAL 0x0
-
-/* Known (rather, used by us) partition types */
-#define PC_PART_TYPE_LINUX 0x83
-#define PC_PART_TYPE_EXTENDED 0x05
-#define PC_PART_TYPE_FAT32 0x0c
-
-#define PC_NUM_BOOT_RECORD_PARTS 4
-
-#define PC_EBR_LOGICAL_PART 0
-#define PC_EBR_NEXT_PTR_PART 1
-
-#define PC_BIOS_BOOT_SIG 0xAA55
-
-#define PC_MBR_DISK_OFFSET 0
-#define PC_MBR_SIZE 512
-
-#define PART_ACTIVE_FLAG 0x1
-
-struct chs {
- uint8_t head;
- uint8_t sector;
- uint8_t cylinder;
-} __attribute__((__packed__));
-
-/* 16 byte pc partition descriptor that sits in MBR and EPBR.
- * Note: multi-byte entities have little-endian layout on disk */
-struct pc_partition {
- uint8_t status; /* byte 0 */
- struct chs start; /* bytes 1-3 */
- uint8_t type; /* byte 4 */
- struct chs end; /* bytes 5-7 */
- uint32_t start_lba; /* bytes 8-11 */
- uint32_t len_lba; /* bytes 12-15 */
-} __attribute__((__packed__));
-
-struct pc_boot_record {
- uint8_t code[440]; /* bytes 0-439 */
- uint32_t disk_sig; /* bytes 440-443 */
- uint16_t pad; /* bytes 444-445 */
- struct pc_partition ptable[PC_NUM_BOOT_RECORD_PARTS]; /* bytes 446-509 */
- uint16_t mbr_sig; /* bytes 510-511 */
-} __attribute__((__packed__));
-
-struct part_info {
- char *name;
- uint8_t flags;
- uint8_t type;
- uint32_t len_kb; /* in 1K-bytes */
- uint32_t start_lba; /* the LBA where this partition begins */
-};
-
-struct disk_info {
- char *device;
- uint8_t scheme;
- int sect_size; /* expected sector size in bytes. MUST BE POWER OF 2 */
- uint32_t skip_lba; /* in sectors (1 unit of LBA) */
- uint32_t num_lba; /* the size of the disk in LBA units */
- struct part_info *part_lst;
- int num_parts;
-};
-
-struct write_list {
- struct write_list *next;
- loff_t offset;
- uint32_t len;
- uint8_t data[0];
-};
-
-
-struct write_list *alloc_wl(uint32_t data_len);
-void free_wl(struct write_list *item);
-struct write_list *wlist_add(struct write_list **lst, struct write_list *item);
-void wlist_free(struct write_list *lst);
-int wlist_commit(int fd, struct write_list *lst, int test);
-
-struct disk_info *load_diskconfig(const char *fn, char *path_override);
-int dump_disk_config(struct disk_info *dinfo);
-int apply_disk_config(struct disk_info *dinfo, int test);
-char *find_part_device(struct disk_info *dinfo, const char *name);
-int process_disk_config(struct disk_info *dinfo);
-struct part_info *find_part(struct disk_info *dinfo, const char *name);
-
-int write_raw_image(const char *dst, const char *src, loff_t offset, int test);
-
-/* For MBR partition schemes */
-struct write_list *config_mbr(struct disk_info *dinfo);
-char *find_mbr_part(struct disk_info *dinfo, const char *name);
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif /* __LIBS_DISKCONFIG_H */
diff --git a/libdiskconfig/write_lst.c b/libdiskconfig/write_lst.c
deleted file mode 100644
index c3d5c0a..0000000
--- a/libdiskconfig/write_lst.c
+++ /dev/null
@@ -1,92 +0,0 @@
-/* libs/diskconfig/write_lst.c
- *
- * Copyright 2008, 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.
- */
-
-#define LOG_TAG "write_lst"
-
-#include <stdint.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <sys/types.h>
-#include <unistd.h>
-
-#include <diskconfig/diskconfig.h>
-#include <log/log.h>
-
-struct write_list *
-alloc_wl(uint32_t data_len)
-{
- struct write_list *item;
-
- if (!(item = malloc(sizeof(struct write_list) + data_len))) {
- ALOGE("Unable to allocate memory.");
- return NULL;
- }
-
- item->len = data_len;
- return item;
-}
-
-void
-free_wl(struct write_list *item)
-{
- if (item)
- free(item);
-}
-
-struct write_list *
-wlist_add(struct write_list **lst, struct write_list *item)
-{
- item->next = (*lst);
- *lst = item;
- return item;
-}
-
-void
-wlist_free(struct write_list *lst)
-{
- struct write_list *temp_wr;
- while (lst) {
- temp_wr = lst->next;
- free_wl(lst);
- lst = temp_wr;
- }
-}
-
-int
-wlist_commit(int fd, struct write_list *lst, int test)
-{
- for(; lst; lst = lst->next) {
- if (lseek64(fd, lst->offset, SEEK_SET) != (loff_t)lst->offset) {
- ALOGE("Cannot seek to the specified position (%lld).", (long long)lst->offset);
- goto fail;
- }
-
- if (!test) {
- if (write(fd, lst->data, lst->len) != (int)lst->len) {
- ALOGE("Failed writing %u bytes at position %lld.", lst->len,
- (long long)lst->offset);
- goto fail;
- }
- } else
- ALOGI("Would write %d bytes @ offset %lld.", lst->len, (long long)lst->offset);
- }
-
- return 0;
-
-fail:
- return -1;
-}
diff --git a/libmodprobe/libmodprobe.cpp b/libmodprobe/libmodprobe.cpp
index 1971f01..5023c79 100644
--- a/libmodprobe/libmodprobe.cpp
+++ b/libmodprobe/libmodprobe.cpp
@@ -230,7 +230,7 @@
}
std::vector<std::string> lines = android::base::Split(cfg_contents, "\n");
- for (const std::string line : lines) {
+ for (const auto& line : lines) {
if (line.empty() || line[0] == '#') {
continue;
}
@@ -421,7 +421,8 @@
}
if (strict && !module_loaded) {
- LOG(ERROR) << "LoadWithAliases was unable to load " << module_name;
+ LOG(ERROR) << "LoadWithAliases was unable to load " << module_name
+ << ", tried: " << android::base::Join(modules_to_load, ", ");
return false;
}
return true;
@@ -439,54 +440,58 @@
return module_blocklist_.count(canonical_name) > 0;
}
-// Another option to load kernel modules. load in independent modules in parallel
-// and then update dependency list of other remaining modules, repeat these steps
-// until all modules are loaded.
+// Another option to load kernel modules. load independent modules dependencies
+// in parallel and then update dependency list of other remaining modules,
+// repeat these steps until all modules are loaded.
+// Discard all blocklist.
+// Softdeps are taken care in InsmodWithDeps().
bool Modprobe::LoadModulesParallel(int num_threads) {
bool ret = true;
- int count = -1;
- std::map<std::string, std::set<std::string>> mod_with_deps;
+ std::unordered_map<std::string, std::vector<std::string>> mod_with_deps;
// Get dependencies
for (const auto& module : module_load_) {
+ // Skip blocklist modules
+ if (IsBlocklisted(module)) {
+ LOG(VERBOSE) << "LMP: Blocklist: Module " << module << " skipping...";
+ continue;
+ }
auto dependencies = GetDependencies(MakeCanonical(module));
-
- for (auto dep = dependencies.rbegin(); dep != dependencies.rend(); dep++) {
- mod_with_deps[module].emplace(*dep);
+ if (dependencies.empty()) {
+ LOG(ERROR) << "LMP: Hard-dep: Module " << module
+ << " not in .dep file";
+ return false;
}
+ mod_with_deps[MakeCanonical(module)] = dependencies;
}
- // Get soft dependencies
- for (const auto& [it_mod, it_softdep] : module_pre_softdep_) {
- if (mod_with_deps.find(MakeCanonical(it_softdep)) != mod_with_deps.end()) {
- mod_with_deps[MakeCanonical(it_mod)].emplace(
- GetDependencies(MakeCanonical(it_softdep))[0]);
- }
- }
-
- // Get soft post dependencies
- for (const auto& [it_mod, it_softdep] : module_post_softdep_) {
- if (mod_with_deps.find(MakeCanonical(it_softdep)) != mod_with_deps.end()) {
- mod_with_deps[MakeCanonical(it_softdep)].emplace(
- GetDependencies(MakeCanonical(it_mod))[0]);
- }
- }
-
- while (!mod_with_deps.empty() && count != module_loaded_.size()) {
+ while (!mod_with_deps.empty()) {
std::vector<std::thread> threads;
std::vector<std::string> mods_path_to_load;
std::mutex vector_lock;
- count = module_loaded_.size();
// Find independent modules
for (const auto& [it_mod, it_dep] : mod_with_deps) {
- if (it_dep.size() == 1) {
- if (module_options_[it_mod].find("load_sequential=1") != std::string::npos) {
- if (!LoadWithAliases(it_mod, true) && !IsBlocklisted(it_mod)) {
- return false;
- }
- } else {
- mods_path_to_load.emplace_back(it_mod);
+ auto itd_last = it_dep.rbegin();
+ if (itd_last == it_dep.rend())
+ continue;
+
+ auto cnd_last = MakeCanonical(*itd_last);
+ // Hard-dependencies cannot be blocklisted
+ if (IsBlocklisted(cnd_last)) {
+ LOG(ERROR) << "LMP: Blocklist: Module-dep " << cnd_last
+ << " : failed to load module " << it_mod;
+ return false;
+ }
+
+ if (module_options_[cnd_last].find("load_sequential=1") != std::string::npos) {
+ if (!LoadWithAliases(cnd_last, true)) {
+ return false;
+ }
+ } else {
+ if (std::find(mods_path_to_load.begin(), mods_path_to_load.end(),
+ cnd_last) == mods_path_to_load.end()) {
+ mods_path_to_load.emplace_back(cnd_last);
}
}
}
@@ -502,7 +507,7 @@
lk.unlock();
ret_load &= LoadWithAliases(mod_to_load, true);
lk.lock();
- if (!ret_load && !IsBlocklisted(mod_to_load)) {
+ if (!ret_load) {
ret &= ret_load;
}
}
@@ -520,14 +525,16 @@
std::lock_guard guard(module_loaded_lock_);
// Remove loaded module form mod_with_deps and soft dependencies of other modules
- for (const auto& module_loaded : module_loaded_) {
+ for (const auto& module_loaded : module_loaded_)
mod_with_deps.erase(module_loaded);
- }
// Remove loaded module form dependencies of other modules which are not loaded yet
for (const auto& module_loaded_path : module_loaded_paths_) {
for (auto& [mod, deps] : mod_with_deps) {
- deps.erase(module_loaded_path);
+ auto it = std::find(deps.begin(), deps.end(), module_loaded_path);
+ if (it != deps.end()) {
+ deps.erase(it);
+ }
}
}
}
diff --git a/libmodprobe/libmodprobe_ext.cpp b/libmodprobe/libmodprobe_ext.cpp
index 94a1dc4..c4519e3 100644
--- a/libmodprobe/libmodprobe_ext.cpp
+++ b/libmodprobe/libmodprobe_ext.cpp
@@ -84,7 +84,7 @@
}
bool Modprobe::ModuleExists(const std::string& module_name) {
- struct stat fileStat;
+ struct stat fileStat {};
if (blocklist_enabled && module_blocklist_.count(module_name)) {
LOG(INFO) << "module " << module_name << " is blocklisted";
return false;
@@ -95,7 +95,7 @@
return false;
}
if (stat(deps.front().c_str(), &fileStat)) {
- LOG(INFO) << "module " << module_name << " does not exist";
+ PLOG(INFO) << "module " << module_name << " can't be loaded; can't access " << deps.front();
return false;
}
if (!S_ISREG(fileStat.st_mode)) {
diff --git a/libnetutils/ifc_utils.c b/libnetutils/ifc_utils.c
index 5999e39..7cca105 100644
--- a/libnetutils/ifc_utils.c
+++ b/libnetutils/ifc_utils.c
@@ -362,14 +362,19 @@
return err->error;
}
+// Pass bitwise complement of prefix length to disable DAD, ie. use ~64 instead of 64.
// Returns zero on success and negative errno on failure.
int ifc_add_address(const char *name, const char *address, int prefixlen) {
- return ifc_act_on_address(RTM_NEWADDR, name, address, prefixlen, /*nodad*/ false);
+ bool nodad = (prefixlen < 0);
+ if (nodad) prefixlen = ~prefixlen;
+ return ifc_act_on_address(RTM_NEWADDR, name, address, prefixlen, nodad);
}
// Returns zero on success and negative errno on failure.
int ifc_del_address(const char *name, const char * address, int prefixlen) {
- return ifc_act_on_address(RTM_DELADDR, name, address, prefixlen, /*nodad*/ false);
+ bool nodad = (prefixlen < 0);
+ if (nodad) prefixlen = ~prefixlen;
+ return ifc_act_on_address(RTM_DELADDR, name, address, prefixlen, nodad);
}
/*
diff --git a/libprocessgroup/processgroup.cpp b/libprocessgroup/processgroup.cpp
index 234b793..4506439 100644
--- a/libprocessgroup/processgroup.cpp
+++ b/libprocessgroup/processgroup.cpp
@@ -206,17 +206,16 @@
}
static std::string ConvertUidToPath(const char* cgroup, uid_t uid) {
- return StringPrintf("%s/uid_%d", cgroup, uid);
+ return StringPrintf("%s/uid_%u", cgroup, uid);
}
static std::string ConvertUidPidToPath(const char* cgroup, uid_t uid, int pid) {
- return StringPrintf("%s/uid_%d/pid_%d", cgroup, uid, pid);
+ return StringPrintf("%s/uid_%u/pid_%d", cgroup, uid, pid);
}
static int RemoveProcessGroup(const char* cgroup, uid_t uid, int pid, unsigned int retries) {
int ret = 0;
auto uid_pid_path = ConvertUidPidToPath(cgroup, uid, pid);
- auto uid_path = ConvertUidToPath(cgroup, uid);
while (retries--) {
ret = rmdir(uid_pid_path.c_str());
@@ -224,6 +223,13 @@
std::this_thread::sleep_for(5ms);
}
+ if (!ret && uid >= AID_ISOLATED_START && uid <= AID_ISOLATED_END) {
+ // Isolated UIDs are unlikely to be reused soon after removal,
+ // so free up the kernel resources for the UID level cgroup.
+ const auto uid_path = ConvertUidToPath(cgroup, uid);
+ ret = rmdir(uid_path.c_str());
+ }
+
return ret;
}
diff --git a/libsparse/Android.bp b/libsparse/Android.bp
index 8e83e16..5a7d0fc 100644
--- a/libsparse/Android.bp
+++ b/libsparse/Android.bp
@@ -28,6 +28,9 @@
"libbase",
],
target: {
+ darwin: {
+ enabled: true,
+ },
windows: {
enabled: true,
},
@@ -52,6 +55,11 @@
],
cflags: ["-Werror"],
+ target: {
+ darwin: {
+ enabled: true,
+ },
+ },
}
cc_binary {
diff --git a/libstats/pull_rust/Android.bp b/libstats/pull_rust/Android.bp
index 85a38f8..4609e6b 100644
--- a/libstats/pull_rust/Android.bp
+++ b/libstats/pull_rust/Android.bp
@@ -28,7 +28,6 @@
],
source_stem: "bindings",
bindgen_flags: [
- "--size_t-is-usize",
"--allowlist-function=AStatsEventList_addStatsEvent",
"--allowlist-function=AStatsEvent_.*",
"--allowlist-function=AStatsManager_.*",
diff --git a/libstats/pull_rust/stats_pull.rs b/libstats/pull_rust/stats_pull.rs
index 09b2623..d188b5f 100644
--- a/libstats/pull_rust/stats_pull.rs
+++ b/libstats/pull_rust/stats_pull.rs
@@ -111,7 +111,9 @@
static ref COOKIES: Mutex<HashMap<i32, fn() -> StatsPullResult>> = Mutex::new(HashMap::new());
}
-// Safety: We store our callbacks in the global so they are valid.
+/// # Safety
+///
+/// `data` must be a valid pointer with no aliases.
unsafe extern "C" fn callback_wrapper(
atom_tag: i32,
data: *mut AStatsEventList,
@@ -126,7 +128,8 @@
let stats = cb();
let result = stats
.iter()
- .map(|stat| stat.add_astats_event(&mut *data))
+ // Safety: The caller promises that `data` is valid and unaliased.
+ .map(|stat| stat.add_astats_event(unsafe { &mut *data }))
.collect::<Result<Vec<()>, StatsError>>();
match result {
Ok(_) => {
diff --git a/libstats/push_compat/Android.bp b/libstats/push_compat/Android.bp
index 819066e..c5c1934 100644
--- a/libstats/push_compat/Android.bp
+++ b/libstats/push_compat/Android.bp
@@ -26,7 +26,7 @@
cc_defaults {
name: "libstatspush_compat_defaults",
srcs: [
- "statsd_writer.c",
+ "statsd_writer.cpp",
"stats_event_list.c",
"StatsEventCompat.cpp"
],
diff --git a/libstats/push_compat/statsd_writer.c b/libstats/push_compat/statsd_writer.cpp
similarity index 97%
rename from libstats/push_compat/statsd_writer.c
rename to libstats/push_compat/statsd_writer.cpp
index 4818d11..a3600f3 100644
--- a/libstats/push_compat/statsd_writer.c
+++ b/libstats/push_compat/statsd_writer.cpp
@@ -15,9 +15,9 @@
*/
#include "statsd_writer.h"
+#include <android-base/threads.h>
#include <cutils/fs.h>
#include <cutils/sockets.h>
-#include <cutils/threads.h>
#include <errno.h>
#include <fcntl.h>
#include <inttypes.h>
@@ -108,7 +108,7 @@
case -ECONNREFUSED:
case -ENOENT:
i = atomic_exchange(&statsdLoggerWrite.sock, ret);
- /* FALLTHRU */
+ break;
default:
break;
}
@@ -188,7 +188,7 @@
* };
*/
- header.tid = gettid();
+ header.tid = android::base::GetThreadId();
header.realtime.tv_sec = ts->tv_sec;
header.realtime.tv_nsec = ts->tv_nsec;
@@ -272,7 +272,7 @@
if (ret < 0) {
ret = -errno;
}
- /* FALLTHRU */
+ break;
default:
break;
}
diff --git a/libstats/push_compat/statsd_writer.h b/libstats/push_compat/statsd_writer.h
index fe2d37c..f030b96 100644
--- a/libstats/push_compat/statsd_writer.h
+++ b/libstats/push_compat/statsd_writer.h
@@ -21,6 +21,8 @@
#include <stdatomic.h>
#include <sys/socket.h>
+__BEGIN_DECLS
+
/**
* Internal lock should not be exposed. This is bad design.
* TODO: rewrite it in c++ code and encapsulate the functionality in a
@@ -42,4 +44,6 @@
void (*noteDrop)(int error, int tag);
};
+__END_DECLS
+
#endif // ANDROID_STATS_LOG_STATS_WRITER_H
diff --git a/libsysutils/Android.bp b/libsysutils/Android.bp
index 5f472b2..1b41a6b 100644
--- a/libsysutils/Android.bp
+++ b/libsysutils/Android.bp
@@ -29,6 +29,10 @@
"liblog",
],
+ header_libs: [
+ "bpf_headers",
+ ],
+
export_include_dirs: ["include"],
tidy: true,
diff --git a/libsysutils/src/NetlinkEvent.cpp b/libsysutils/src/NetlinkEvent.cpp
index 515cc10..55bbe46 100644
--- a/libsysutils/src/NetlinkEvent.cpp
+++ b/libsysutils/src/NetlinkEvent.cpp
@@ -37,10 +37,12 @@
#include <sys/utsname.h>
#include <android-base/parseint.h>
+#include <bpf/KernelUtils.h>
#include <log/log.h>
#include <sysutils/NetlinkEvent.h>
using android::base::ParseInt;
+using android::bpf::isKernel64Bit;
/* From kernel's net/netfilter/xt_quota2.c */
const int LOCAL_QLOG_NL_EVENT = 112;
@@ -138,60 +140,6 @@
static_assert(sizeof(ulog_packet_msg32_t) == 168);
static_assert(sizeof(ulog_packet_msg64_t) == 192);
-// Figure out the bitness of userspace.
-// Trivial and known at compile time.
-static bool isUserspace64bit(void) {
- return sizeof(long) == 8;
-}
-
-// Figure out the bitness of the kernel.
-static bool isKernel64Bit(void) {
- // a 64-bit userspace requires a 64-bit kernel
- if (isUserspace64bit()) return true;
-
- static bool init = false;
- static bool cache = false;
- if (init) return cache;
-
- // Retrieve current personality - on Linux this system call *cannot* fail.
- int p = personality(0xffffffff);
- // But if it does just assume kernel and userspace (which is 32-bit) match...
- if (p == -1) return false;
-
- // This will effectively mask out the bottom 8 bits, and switch to 'native'
- // personality, and then return the previous personality of this thread
- // (likely PER_LINUX or PER_LINUX32) with any extra options unmodified.
- int q = personality((p & ~PER_MASK) | PER_LINUX);
- // Per man page this theoretically could error out with EINVAL,
- // but kernel code analysis suggests setting PER_LINUX cannot fail.
- // Either way, assume kernel and userspace (which is 32-bit) match...
- if (q != p) return false;
-
- struct utsname u;
- (void)uname(&u); // only possible failure is EFAULT, but u is on stack.
-
- // Switch back to previous personality.
- // Theoretically could fail with EINVAL on arm64 with no 32-bit support,
- // but then we wouldn't have fetched 'p' from the kernel in the first place.
- // Either way there's nothing meaningul we can do in case of error.
- // Since PER_LINUX32 vs PER_LINUX only affects uname.machine it doesn't
- // really hurt us either. We're really just switching back to be 'clean'.
- (void)personality(p);
-
- // Possible values of utsname.machine observed on x86_64 desktop (arm via qemu):
- // x86_64 i686 aarch64 armv7l
- // additionally observed on arm device:
- // armv8l
- // presumably also might just be possible:
- // i386 i486 i586
- // and there might be other weird arm32 cases.
- // We note that the 64 is present in both 64-bit archs,
- // and in general is likely to be present in only 64-bit archs.
- cache = !!strstr(u.machine, "64");
- init = true;
- return cache;
-}
-
/******************************************************************************/
NetlinkEvent::NetlinkEvent() {
@@ -202,15 +150,10 @@
}
NetlinkEvent::~NetlinkEvent() {
- int i;
- if (mPath)
- free(mPath);
- if (mSubsystem)
- free(mSubsystem);
- for (i = 0; i < NL_PARAMS_MAX; i++) {
- if (!mParams[i])
- break;
- free(mParams[i]);
+ free(mPath);
+ free(mSubsystem);
+ for (auto param : mParams) {
+ free(param);
}
}
diff --git a/libutils/Errors.cpp b/libutils/Errors.cpp
index 74f3bef..dfb4d9b 100644
--- a/libutils/Errors.cpp
+++ b/libutils/Errors.cpp
@@ -15,6 +15,8 @@
*/
#include <utils/Errors.h>
+#include <string.h>
+
namespace android {
std::string statusToString(status_t s) {
diff --git a/libutils/LruCache_test.cpp b/libutils/LruCache_test.cpp
index 8b16947..5cd3cbb 100644
--- a/libutils/LruCache_test.cpp
+++ b/libutils/LruCache_test.cpp
@@ -29,6 +29,8 @@
struct ComplexKey {
int k;
+ explicit ComplexKey() : k(0) { instanceCount += 1; }
+
explicit ComplexKey(int k) : k(k) {
instanceCount += 1;
}
@@ -57,6 +59,8 @@
struct ComplexValue {
int v;
+ explicit ComplexValue() : v(0) { instanceCount += 1; }
+
explicit ComplexValue(int v) : v(v) {
instanceCount += 1;
}
@@ -83,10 +87,9 @@
struct KeyFailsOnCopy : public ComplexKey {
public:
- KeyFailsOnCopy(const KeyFailsOnCopy& key) : ComplexKey(key) {
- ADD_FAILURE();
- }
- KeyFailsOnCopy(int key) : ComplexKey(key) { }
+ KeyFailsOnCopy() : ComplexKey() {}
+ KeyFailsOnCopy(const KeyFailsOnCopy& key) : ComplexKey(key) { ADD_FAILURE(); }
+ KeyFailsOnCopy(int key) : ComplexKey(key) {}
};
} // namespace
diff --git a/libutils/RefBase_test.cpp b/libutils/RefBase_test.cpp
index aed3b9b..d675598 100644
--- a/libutils/RefBase_test.cpp
+++ b/libutils/RefBase_test.cpp
@@ -28,7 +28,7 @@
using namespace android;
-static constexpr int NITERS = 1000000;
+static constexpr int NITERS = 500000;
static constexpr int INITIAL_STRONG_VALUE = 1 << 28; // Mirroring RefBase definition.
diff --git a/libutils/String8.cpp b/libutils/String8.cpp
index 82f5cb6..8d312b5 100644
--- a/libutils/String8.cpp
+++ b/libutils/String8.cpp
@@ -393,6 +393,11 @@
}
bool String8::removeAll(const char* other) {
+ ALOG_ASSERT(other, "String8::removeAll() requires a non-NULL string");
+
+ if (*other == '\0')
+ return true;
+
ssize_t index = find(other);
if (index < 0) return false;
diff --git a/libutils/String8_fuzz.cpp b/libutils/String8_fuzz.cpp
index faf49b6..e5dcd31 100644
--- a/libutils/String8_fuzz.cpp
+++ b/libutils/String8_fuzz.cpp
@@ -45,6 +45,8 @@
str1->toLower();
},
[](FuzzedDataProvider*, android::String8* str1, android::String8* str2) -> void {
+ if (str2->size() == 0) return;
+
str1->removeAll(str2->c_str());
},
[](FuzzedDataProvider*, android::String8* str1, android::String8* str2) -> void {
diff --git a/libutils/String8_test.cpp b/libutils/String8_test.cpp
index 1356cd0..35fd512 100644
--- a/libutils/String8_test.cpp
+++ b/libutils/String8_test.cpp
@@ -114,3 +114,21 @@
EXPECT_EQ(NO_MEMORY, s.append("baz", SIZE_MAX));
EXPECT_STREQ("foobar", s);
}
+
+TEST_F(String8Test, removeAll) {
+ String8 s("Hello, world!");
+
+ // NULL input should cause an assertion failure and error message in logcat
+ EXPECT_DEATH(s.removeAll(NULL), "");
+
+ // expect to return true and string content should remain unchanged
+ EXPECT_TRUE(s.removeAll(""));
+ EXPECT_STREQ("Hello, world!", s);
+
+ // expect to return false
+ EXPECT_FALSE(s.removeAll("x"));
+ EXPECT_STREQ("Hello, world!", s);
+
+ EXPECT_TRUE(s.removeAll("o"));
+ EXPECT_STREQ("Hell, wrld!", s);
+}
diff --git a/libutils/Unicode.cpp b/libutils/Unicode.cpp
index 3ffcf7e..364a177 100644
--- a/libutils/Unicode.cpp
+++ b/libutils/Unicode.cpp
@@ -280,158 +280,181 @@
: 0);
}
+// is_any_surrogate() returns true if w is either a high or low surrogate
+static constexpr bool is_any_surrogate(char16_t w) {
+ return (w & 0xf800) == 0xd800;
+}
+
+// is_surrogate_pair() returns true if w1 and w2 form a valid surrogate pair
+static constexpr bool is_surrogate_pair(char16_t w1, char16_t w2) {
+ return ((w1 & 0xfc00) == 0xd800) && ((w2 & 0xfc00) == 0xdc00);
+}
+
+// TODO: currently utf16_to_utf8_length() returns -1 if src_len == 0,
+// which is inconsistent with utf8_to_utf16_length(), here we keep the
+// current behavior as intended not to break compatibility
+ssize_t utf16_to_utf8_length(const char16_t *src, size_t src_len)
+{
+ if (src == nullptr || src_len == 0)
+ return -1;
+
+ const char16_t* const end = src + src_len;
+ const char16_t* in = src;
+ size_t utf8_len = 0;
+
+ while (in < end) {
+ char16_t w = *in++;
+ if (LIKELY(w < 0x0080)) {
+ utf8_len += 1;
+ continue;
+ }
+ if (LIKELY(w < 0x0800)) {
+ utf8_len += 2;
+ continue;
+ }
+ if (LIKELY(!is_any_surrogate(w))) {
+ utf8_len += 3;
+ continue;
+ }
+ if (in < end && is_surrogate_pair(w, *in)) {
+ utf8_len += 4;
+ in++;
+ continue;
+ }
+ /* skip if at the end of the string or invalid surrogate pair */
+ }
+ return (in == end && utf8_len < SSIZE_MAX) ? utf8_len : -1;
+}
+
void utf16_to_utf8(const char16_t* src, size_t src_len, char* dst, size_t dst_len)
{
if (src == nullptr || src_len == 0 || dst == nullptr) {
return;
}
- const char16_t* cur_utf16 = src;
- const char16_t* const end_utf16 = src + src_len;
- char *cur = dst;
- while (cur_utf16 < end_utf16) {
- char32_t utf32;
- // surrogate pairs
- if((*cur_utf16 & 0xFC00) == 0xD800 && (cur_utf16 + 1) < end_utf16
- && (*(cur_utf16 + 1) & 0xFC00) == 0xDC00) {
- utf32 = (*cur_utf16++ - 0xD800) << 10;
- utf32 |= *cur_utf16++ - 0xDC00;
- utf32 += 0x10000;
- } else {
- utf32 = (char32_t) *cur_utf16++;
+ const char16_t* in = src;
+ const char16_t* const in_end = src + src_len;
+ char* out = dst;
+ const char* const out_end = dst + dst_len;
+ char16_t w2;
+
+ auto err_out = [&out, &out_end, &dst_len]() {
+ LOG_ALWAYS_FATAL_IF(out >= out_end,
+ "target utf8 string size %zu too short", dst_len);
+ };
+
+ while (in < in_end) {
+ char16_t w = *in++;
+ if (LIKELY(w < 0x0080)) {
+ if (out + 1 > out_end)
+ return err_out();
+ *out++ = (char)(w & 0xff);
+ continue;
}
- const size_t len = utf32_codepoint_utf8_length(utf32);
- LOG_ALWAYS_FATAL_IF(dst_len < len, "%zu < %zu", dst_len, len);
- utf32_codepoint_to_utf8((uint8_t*)cur, utf32, len);
- cur += len;
- dst_len -= len;
+ if (LIKELY(w < 0x0800)) {
+ if (out + 2 > out_end)
+ return err_out();
+ *out++ = (char)(0xc0 | ((w >> 6) & 0x1f));
+ *out++ = (char)(0x80 | ((w >> 0) & 0x3f));
+ continue;
+ }
+ if (LIKELY(!is_any_surrogate(w))) {
+ if (out + 3 > out_end)
+ return err_out();
+ *out++ = (char)(0xe0 | ((w >> 12) & 0xf));
+ *out++ = (char)(0x80 | ((w >> 6) & 0x3f));
+ *out++ = (char)(0x80 | ((w >> 0) & 0x3f));
+ continue;
+ }
+ /* surrogate pair */
+ if (in < in_end && (w2 = *in, is_surrogate_pair(w, w2))) {
+ if (out + 4 > out_end)
+ return err_out();
+ char32_t dw = (char32_t)(0x10000 + ((w - 0xd800) << 10) + (w2 - 0xdc00));
+ *out++ = (char)(0xf0 | ((dw >> 18) & 0x07));
+ *out++ = (char)(0x80 | ((dw >> 12) & 0x3f));
+ *out++ = (char)(0x80 | ((dw >> 6) & 0x3f));
+ *out++ = (char)(0x80 | ((dw >> 0) & 0x3f));
+ in++;
+ }
+ /* We reach here in two cases:
+ * 1) (in == in_end), which means end of the input string
+ * 2) (w2 & 0xfc00) != 0xdc00, which means invalid surrogate pair
+ * In either case, we intentionally do nothing and skip
+ */
}
- LOG_ALWAYS_FATAL_IF(dst_len < 1, "%zu < 1", dst_len);
- *cur = '\0';
+ *out = '\0';
+ return;
}
// --------------------------------------------------------------------------
// UTF-8
// --------------------------------------------------------------------------
-ssize_t utf16_to_utf8_length(const char16_t *src, size_t src_len)
-{
- if (src == nullptr || src_len == 0) {
- return -1;
- }
-
- size_t ret = 0;
- const char16_t* const end = src + src_len;
- while (src < end) {
- size_t char_len;
- if ((*src & 0xFC00) == 0xD800 && (src + 1) < end
- && (*(src + 1) & 0xFC00) == 0xDC00) {
- // surrogate pairs are always 4 bytes.
- char_len = 4;
- src += 2;
- } else {
- char_len = utf32_codepoint_utf8_length((char32_t)*src++);
- }
- if (SSIZE_MAX - char_len < ret) {
- // If this happens, we would overflow the ssize_t type when
- // returning from this function, so we cannot express how
- // long this string is in an ssize_t.
- android_errorWriteLog(0x534e4554, "37723026");
- return -1;
- }
- ret += char_len;
- }
- return ret;
+static char32_t utf8_4b_to_utf32(uint8_t c1, uint8_t c2, uint8_t c3, uint8_t c4) {
+ return ((c1 & 0x07) << 18) | ((c2 & 0x3f) << 12) | ((c3 & 0x3f) << 6) | (c4 & 0x3f);
}
-/**
- * Returns 1-4 based on the number of leading bits.
- *
- * 1111 -> 4
- * 1110 -> 3
- * 110x -> 2
- * 10xx -> 1
- * 0xxx -> 1
- */
-static inline size_t utf8_codepoint_len(uint8_t ch)
-{
- return ((0xe5000000 >> ((ch >> 3) & 0x1e)) & 3) + 1;
-}
-
-static inline void utf8_shift_and_mask(uint32_t* codePoint, const uint8_t byte)
-{
- *codePoint <<= 6;
- *codePoint |= 0x3F & byte;
-}
-
-static inline uint32_t utf8_to_utf32_codepoint(const uint8_t *src, size_t length)
-{
- uint32_t unicode;
-
- switch (length)
- {
- case 1:
- return src[0];
- case 2:
- unicode = src[0] & 0x1f;
- utf8_shift_and_mask(&unicode, src[1]);
- return unicode;
- case 3:
- unicode = src[0] & 0x0f;
- utf8_shift_and_mask(&unicode, src[1]);
- utf8_shift_and_mask(&unicode, src[2]);
- return unicode;
- case 4:
- unicode = src[0] & 0x07;
- utf8_shift_and_mask(&unicode, src[1]);
- utf8_shift_and_mask(&unicode, src[2]);
- utf8_shift_and_mask(&unicode, src[3]);
- return unicode;
- default:
- return 0xffff;
- }
-
- //printf("Char at %p: len=%d, utf-16=%p\n", src, length, (void*)result);
-}
+// TODO: current behavior of converting UTF8 to UTF-16 has a few issues below
+//
+// 1. invalid trailing bytes (i.e. not b'10xxxxxx) are treated as valid trailing
+// bytes and follows normal conversion rules
+// 2. invalid leading byte (b'10xxxxxx) is treated as a valid single UTF-8 byte
+// 3. invalid leading byte (b'11111xxx) is treated as a valid leading byte
+// (same as b'11110xxx) for a 4-byte UTF-8 sequence
+// 4. an invalid 4-byte UTF-8 sequence that translates to a codepoint < U+10000
+// will be converted as a valid UTF-16 character
+//
+// We keep the current behavior as is but with warnings logged, so as not to
+// break compatibility. However, this needs to be addressed later.
ssize_t utf8_to_utf16_length(const uint8_t* u8str, size_t u8len, bool overreadIsFatal)
{
- const uint8_t* const u8end = u8str + u8len;
- const uint8_t* u8cur = u8str;
-
- /* Validate that the UTF-8 is the correct len */
- size_t u16measuredLen = 0;
- while (u8cur < u8end) {
- u16measuredLen++;
- int u8charLen = utf8_codepoint_len(*u8cur);
- // Malformed utf8, some characters are beyond the end.
- // Cases:
- // If u8charLen == 1, this becomes u8cur >= u8end, which cannot happen as u8cur < u8end,
- // then this condition fail and we continue, as expected.
- // If u8charLen == 2, this becomes u8cur + 1 >= u8end, which fails only if
- // u8cur == u8end - 1, that is, there was only one remaining character to read but we need
- // 2 of them. This condition holds and we return -1, as expected.
- if (u8cur + u8charLen - 1 >= u8end) {
- if (overreadIsFatal) {
- LOG_ALWAYS_FATAL("Attempt to overread computing length of utf8 string");
- } else {
- return -1;
- }
- }
- uint32_t codepoint = utf8_to_utf32_codepoint(u8cur, u8charLen);
- if (codepoint > 0xFFFF) u16measuredLen++; // this will be a surrogate pair in utf16
- u8cur += u8charLen;
- }
-
- /**
- * Make sure that we ended where we thought we would and the output UTF-16
- * will be exactly how long we were told it would be.
- */
- if (u8cur != u8end) {
+ if (u8str == nullptr)
return -1;
- }
- return u16measuredLen;
+ const uint8_t* const in_end = u8str + u8len;
+ const uint8_t* in = u8str;
+ size_t utf16_len = 0;
+
+ while (in < in_end) {
+ uint8_t c = *in;
+ utf16_len++;
+ if (LIKELY((c & 0x80) == 0)) {
+ in++;
+ continue;
+ }
+ if (UNLIKELY(c < 0xc0)) {
+ ALOGW("Invalid UTF-8 leading byte: 0x%02x", c);
+ in++;
+ continue;
+ }
+ if (LIKELY(c < 0xe0)) {
+ in += 2;
+ continue;
+ }
+ if (LIKELY(c < 0xf0)) {
+ in += 3;
+ continue;
+ } else {
+ uint8_t c2, c3, c4;
+ if (UNLIKELY(c >= 0xf8)) {
+ ALOGW("Invalid UTF-8 leading byte: 0x%02x", c);
+ }
+ c2 = in[1]; c3 = in[2]; c4 = in[3];
+ if (utf8_4b_to_utf32(c, c2, c3, c4) >= 0x10000) {
+ utf16_len++;
+ }
+ in += 4;
+ continue;
+ }
+ }
+ if (in == in_end) {
+ return utf16_len < SSIZE_MAX ? utf16_len : -1;
+ }
+ if (overreadIsFatal)
+ LOG_ALWAYS_FATAL("Attempt to overread computing length of utf8 string");
+ return -1;
}
char16_t* utf8_to_utf16(const uint8_t* u8str, size_t u8len, char16_t* u16str, size_t u16len) {
@@ -444,38 +467,75 @@
char16_t* utf8_to_utf16_no_null_terminator(
const uint8_t* src, size_t srcLen, char16_t* dst, size_t dstLen) {
- if (dstLen == 0) {
+ if (src == nullptr || srcLen == 0 || dstLen == 0) {
return dst;
}
// A value > SSIZE_MAX is probably a negative value returned as an error and casted.
LOG_ALWAYS_FATAL_IF(dstLen > SSIZE_MAX, "dstLen is %zu", dstLen);
- const uint8_t* const u8end = src + srcLen;
- const uint8_t* u8cur = src;
- const char16_t* const u16end = dst + dstLen;
- char16_t* u16cur = dst;
- while (u8cur < u8end && u16cur < u16end) {
- size_t u8len = utf8_codepoint_len(*u8cur);
- uint32_t codepoint = utf8_to_utf32_codepoint(u8cur, u8len);
+ const uint8_t* const in_end = src + srcLen;
+ const uint8_t* in = src;
+ const char16_t* const out_end = dst + dstLen;
+ char16_t* out = dst;
+ uint8_t c, c2, c3, c4;
+ char32_t w;
- // Convert the UTF32 codepoint to one or more UTF16 codepoints
- if (codepoint <= 0xFFFF) {
- // Single UTF16 character
- *u16cur++ = (char16_t) codepoint;
- } else {
- // Multiple UTF16 characters with surrogates
- codepoint = codepoint - 0x10000;
- *u16cur++ = (char16_t) ((codepoint >> 10) + 0xD800);
- if (u16cur >= u16end) {
- // Ooops... not enough room for this surrogate pair.
- return u16cur-1;
- }
- *u16cur++ = (char16_t) ((codepoint & 0x3FF) + 0xDC00);
+ auto err_in = [&c, &out]() {
+ ALOGW("Unended UTF-8 byte: 0x%02x", c);
+ return out;
+ };
+
+ while (in < in_end && out < out_end) {
+ c = *in++;
+ if (LIKELY((c & 0x80) == 0)) {
+ *out++ = (char16_t)(c);
+ continue;
}
-
- u8cur += u8len;
+ if (UNLIKELY(c < 0xc0)) {
+ ALOGW("Invalid UTF-8 leading byte: 0x%02x", c);
+ *out++ = (char16_t)(c);
+ continue;
+ }
+ if (LIKELY(c < 0xe0)) {
+ if (UNLIKELY(in + 1 > in_end)) {
+ return err_in();
+ }
+ c2 = *in++;
+ *out++ = (char16_t)(((c & 0x1f) << 6) | (c2 & 0x3f));
+ continue;
+ }
+ if (LIKELY(c < 0xf0)) {
+ if (UNLIKELY(in + 2 > in_end)) {
+ return err_in();
+ }
+ c2 = *in++; c3 = *in++;
+ *out++ = (char16_t)(((c & 0x0f) << 12) |
+ ((c2 & 0x3f) << 6) | (c3 & 0x3f));
+ continue;
+ } else {
+ if (UNLIKELY(in + 3 > in_end)) {
+ return err_in();
+ }
+ if (UNLIKELY(c >= 0xf8)) {
+ ALOGW("Invalid UTF-8 leading byte: 0x%02x", c);
+ }
+ // Multiple UTF16 characters with surrogates
+ c2 = *in++; c3 = *in++; c4 = *in++;
+ w = utf8_4b_to_utf32(c, c2, c3, c4);
+ if (UNLIKELY(w < 0x10000)) {
+ *out++ = (char16_t)(w);
+ } else {
+ if (UNLIKELY(out + 2 > out_end)) {
+ // Ooops.... not enough room for this surrogate pair.
+ return out;
+ }
+ *out++ = (char16_t)(((w - 0x10000) >> 10) + 0xd800);
+ *out++ = (char16_t)(((w - 0x10000) & 0x3ff) + 0xdc00);
+ }
+ continue;
+ }
}
- return u16cur;
+ return out;
}
}
diff --git a/libutils/Vector_fuzz.cpp b/libutils/Vector_fuzz.cpp
index f6df051..6fd2baf 100644
--- a/libutils/Vector_fuzz.cpp
+++ b/libutils/Vector_fuzz.cpp
@@ -13,71 +13,203 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-#include "fuzzer/FuzzedDataProvider.h"
-#include "utils/Vector.h"
-static constexpr uint16_t MAX_VEC_SIZE = 5000;
+#include <fuzzer/FuzzedDataProvider.h>
+#include <utils/Log.h>
+#include <utils/Vector.h>
-void runVectorFuzz(const uint8_t* data, size_t size) {
- FuzzedDataProvider dataProvider(data, size);
- android::Vector<uint8_t> vec = android::Vector<uint8_t>();
- // We want to test handling of sizeof as well.
- android::Vector<uint32_t> vec32 = android::Vector<uint32_t>();
+#include <functional>
- // We're going to generate two vectors of this size
- size_t vectorSize = dataProvider.ConsumeIntegralInRange<size_t>(0, MAX_VEC_SIZE);
- vec.setCapacity(vectorSize);
- vec32.setCapacity(vectorSize);
- for (size_t i = 0; i < vectorSize; i++) {
- uint8_t count = dataProvider.ConsumeIntegralInRange<uint8_t>(1, 5);
- vec.insertAt((uint8_t)i, i, count);
- vec32.insertAt((uint32_t)i, i, count);
- vec.push_front(i);
- vec32.push(i);
+using android::Vector;
+
+static constexpr uint16_t MAX_VEC_SIZE = 100;
+static constexpr bool kLog = false;
+
+struct NonTrivialDestructor {
+ NonTrivialDestructor() : mInit(1) {}
+ ~NonTrivialDestructor() {
+ LOG_ALWAYS_FATAL_IF(mInit != 1, "mInit should be 1, but it's: %d", mInit);
+ mInit--;
+ LOG_ALWAYS_FATAL_IF(mInit != 0, "mInit should be 0, but it's: %d", mInit);
}
- // Now we'll perform some test operations with any remaining data
- // Index to perform operations at
- size_t index = dataProvider.ConsumeIntegralInRange<size_t>(0, vec.size());
- std::vector<uint8_t> remainingVec = dataProvider.ConsumeRemainingBytes<uint8_t>();
- // Insert an array and vector
- vec.insertArrayAt(remainingVec.data(), index, remainingVec.size());
- android::Vector<uint8_t> vecCopy = android::Vector<uint8_t>(vec);
- vec.insertVectorAt(vecCopy, index);
- // Same thing for 32 bit vector
- android::Vector<uint32_t> vec32Copy = android::Vector<uint32_t>(vec32);
- vec32.insertArrayAt(vec32Copy.array(), index, vec32.size());
- vec32.insertVectorAt(vec32Copy, index);
- // Replace single character
- if (remainingVec.size() > 0) {
- vec.replaceAt(remainingVec[0], index);
- vec32.replaceAt(static_cast<uint32_t>(remainingVec[0]), index);
- } else {
- vec.replaceAt(0, index);
- vec32.replaceAt(0, index);
+ private:
+ uint8_t mInit;
+};
+
+template <typename T>
+struct VectorFuzzerData {
+ Vector<T> vector;
+ const std::vector<std::function<void(FuzzedDataProvider&, Vector<T>&)>> funcs = {
+ [&](FuzzedDataProvider& provider, Vector<T>& vector) {
+ (void)provider;
+ // operator= Vector<TYPE>, still needs for SortedVector
+ if (kLog) ALOGI("operator=");
+ vector = testVector(provider);
+ },
+ [&](FuzzedDataProvider& provider, Vector<T>& vector) {
+ (void)provider;
+ if (kLog) ALOGI("clear");
+ vector.clear();
+ },
+ [&](FuzzedDataProvider& provider, Vector<T>& vector) {
+ (void)provider;
+ if (kLog) ALOGI("size");
+ vector.size();
+ },
+ [&](FuzzedDataProvider& provider, Vector<T>& vector) {
+ (void)provider;
+ if (kLog) ALOGI("isEmpty");
+ vector.isEmpty();
+ },
+ [&](FuzzedDataProvider& provider, Vector<T>& vector) {
+ (void)provider;
+ if (kLog) ALOGI("capacity");
+ vector.capacity();
+ },
+ [&](FuzzedDataProvider& provider, Vector<T>& vector) {
+ size_t vectorSize = provider.ConsumeIntegralInRange<size_t>(0, MAX_VEC_SIZE);
+ if (kLog) ALOGI("setCapacity");
+ vector.setCapacity(vectorSize);
+ },
+ [&](FuzzedDataProvider& provider, Vector<T>& vector) {
+ size_t vectorSize = provider.ConsumeIntegralInRange<size_t>(0, MAX_VEC_SIZE);
+ if (kLog) ALOGI("resize");
+ vector.resize(vectorSize);
+ },
+ [&](FuzzedDataProvider& provider, Vector<T>& vector) {
+ (void)provider;
+ if (kLog) ALOGI("array");
+ vector.array();
+ },
+ [&](FuzzedDataProvider& provider, Vector<T>& vector) {
+ (void)provider;
+ if (kLog) ALOGI("editArray");
+ vector.editArray();
+ },
+ [&](FuzzedDataProvider& provider, Vector<T>& vector) {
+ if (vector.size() == 0) return;
+ size_t idx = provider.ConsumeIntegralInRange<size_t>(0, vector.size() - 1);
+ if (kLog) ALOGI("operator[]");
+ vector[idx]; // returns a const value for Vector
+ },
+ [&](FuzzedDataProvider& provider, Vector<T>& vector) {
+ if (vector.size() == 0) return;
+ size_t idx = provider.ConsumeIntegralInRange<size_t>(0, vector.size() - 1);
+ if (kLog) ALOGI("itemAt");
+ vector.itemAt(idx);
+ },
+ [&](FuzzedDataProvider& provider, Vector<T>& vector) {
+ (void)provider;
+ if (vector.size() == 0) return;
+ if (kLog) ALOGI("top");
+ vector.top();
+ },
+ [&](FuzzedDataProvider& provider, Vector<T>& vector) {
+ if (vector.size() == 0) return;
+ size_t idx = provider.ConsumeIntegralInRange<size_t>(0, vector.size() - 1);
+ if (kLog) ALOGI("editItemAt");
+ vector.editItemAt(idx);
+ },
+ [&](FuzzedDataProvider& provider, Vector<T>& vector) {
+ (void)provider;
+ if (vector.size() == 0) return;
+ if (kLog) ALOGI("editTop");
+ vector.editTop() = T{};
+ },
+ [&](FuzzedDataProvider& provider, Vector<T>& vector) {
+ uint8_t idx = provider.ConsumeIntegralInRange<uint8_t>(0, vector.size());
+ Vector vec2 = testVector(provider);
+ if (vec2.size() == 0) return; // TODO: maybe we should support this?
+ if (kLog) ALOGI("insertVectorAt %d of size %zu", idx, vec2.size());
+ vector.insertVectorAt(vec2, idx);
+ },
+ [&](FuzzedDataProvider& provider, Vector<T>& vector) {
+ if (kLog) ALOGI("appendVector");
+ vector.appendVector(testVector(provider));
+ },
+ // TODO: insertArrayAt
+ // TODO: appendArray
+ [&](FuzzedDataProvider& provider, Vector<T>& vector) {
+ uint8_t idx = provider.ConsumeIntegralInRange<uint8_t>(0, vector.size());
+ uint8_t numItems = provider.ConsumeIntegralInRange<uint8_t>(1, 100);
+ if (kLog) ALOGI("insertAt");
+ vector.insertAt(idx, numItems);
+ },
+ [&](FuzzedDataProvider& provider, Vector<T>& vector) {
+ uint8_t idx = provider.ConsumeIntegralInRange<uint8_t>(0, vector.size());
+ uint8_t numItems = provider.ConsumeIntegralInRange<uint8_t>(1, 100);
+ if (kLog) ALOGI("insertAt");
+ vector.insertAt(T{}, idx, numItems);
+ },
+ [&](FuzzedDataProvider& provider, Vector<T>& vector) {
+ (void)provider;
+ if (vector.size() == 0) return;
+ if (kLog) ALOGI("pop");
+ vector.pop();
+ },
+ [&](FuzzedDataProvider& provider, Vector<T>& vector) {
+ (void)provider;
+ if (kLog) ALOGI("push");
+ vector.push();
+ },
+ [&](FuzzedDataProvider& provider, Vector<T>& vector) {
+ (void)provider;
+ if (kLog) ALOGI("add");
+ vector.add();
+ },
+ [&](FuzzedDataProvider& provider, Vector<T>& vector) {
+ (void)provider;
+ if (kLog) ALOGI("add");
+ vector.add(T{});
+ },
+ [&](FuzzedDataProvider& provider, Vector<T>& vector) {
+ uint8_t idx = provider.ConsumeIntegralInRange<uint8_t>(0, vector.size() - 1);
+ if (kLog) ALOGI("replaceAt");
+ vector.replaceAt(idx);
+ },
+ [&](FuzzedDataProvider& provider, Vector<T>& vector) {
+ uint8_t idx = provider.ConsumeIntegralInRange<uint8_t>(0, vector.size() - 1);
+ if (kLog) ALOGI("replaceAt");
+ vector.replaceAt(T{}, idx);
+ },
+ [&](FuzzedDataProvider& provider, Vector<T>& vector) {
+ if (vector.size() == 0) return;
+ uint8_t idx = provider.ConsumeIntegralInRange<uint8_t>(0, vector.size() - 1);
+ if (kLog) ALOGI("remoteItemsAt");
+ vector.removeItemsAt(idx); // TODO: different count
+ },
+ // removeAt is alias for removeItemsAt
+ // TODO: sort
+ [&](FuzzedDataProvider& provider, Vector<T>& vector) {
+ (void)provider;
+ if (kLog) ALOGI("getItemSize");
+ vector.getItemSize();
+ },
+ // TODO: iterators
+ };
+
+ Vector<T> testVector(FuzzedDataProvider& provider) {
+ Vector<T> vec;
+ size_t vectorSize = provider.ConsumeIntegralInRange<size_t>(0, MAX_VEC_SIZE);
+ return vec;
}
- // Add any remaining bytes
- for (uint8_t i : remainingVec) {
- vec.add(i);
- vec32.add(static_cast<uint32_t>(i));
+
+ void fuzz(FuzzedDataProvider&& provider) {
+ while (provider.remaining_bytes()) {
+ size_t funcIdx = provider.ConsumeIntegralInRange<size_t>(0, funcs.size() - 1);
+ funcs[funcIdx](provider, vector);
+ }
}
- // Shrink capactiy
- vec.setCapacity(remainingVec.size());
- vec32.setCapacity(remainingVec.size());
- // Iterate through each pointer
- size_t sum = 0;
- for (auto& it : vec) {
- sum += it;
- }
- for (auto& it : vec32) {
- sum += it;
- }
- // Cleanup
- vec.clear();
- vecCopy.clear();
- vec32.clear();
- vec32Copy.clear();
-}
+};
+
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
- runVectorFuzz(data, size);
+ FuzzedDataProvider provider(data, size);
+
+ provider.PickValueInArray<std::function<void()>>({
+ [&]() { VectorFuzzerData<uint8_t>().fuzz(std::move(provider)); },
+ [&]() { VectorFuzzerData<int32_t>().fuzz(std::move(provider)); },
+ [&]() { VectorFuzzerData<NonTrivialDestructor>().fuzz(std::move(provider)); },
+ })();
+
return 0;
}
diff --git a/libutils/include/utils/LruCache.h b/libutils/include/utils/LruCache.h
index b4243a3..70901b6 100644
--- a/libutils/include/utils/LruCache.h
+++ b/libutils/include/utils/LruCache.h
@@ -161,12 +161,12 @@
// Implementation is here, because it's fully templated
template <typename TKey, typename TValue>
LruCache<TKey, TValue>::LruCache(uint32_t maxCapacity)
- : mSet(new LruCacheSet())
- , mListener(nullptr)
- , mOldest(nullptr)
- , mYoungest(nullptr)
- , mMaxCapacity(maxCapacity)
- , mNullValue(0) {
+ : mSet(new LruCacheSet()),
+ mListener(nullptr),
+ mOldest(nullptr),
+ mYoungest(nullptr),
+ mMaxCapacity(maxCapacity),
+ mNullValue{} {
mSet->max_load_factor(1.0);
};
diff --git a/libutils/include/utils/Vector.h b/libutils/include/utils/Vector.h
index be35ea2..d5db3cb 100644
--- a/libutils/include/utils/Vector.h
+++ b/libutils/include/utils/Vector.h
@@ -67,13 +67,10 @@
virtual ~Vector();
/*! copy operator */
- const Vector<TYPE>& operator = (const Vector<TYPE>& rhs) const;
- Vector<TYPE>& operator = (const Vector<TYPE>& rhs);
+ Vector<TYPE>& operator=(const Vector<TYPE>& rhs); // NOLINT(cert-oop54-cpp)
+ Vector<TYPE>& operator=(const SortedVector<TYPE>& rhs); // NOLINT(cert-oop54-cpp)
- const Vector<TYPE>& operator = (const SortedVector<TYPE>& rhs) const;
- Vector<TYPE>& operator = (const SortedVector<TYPE>& rhs);
-
- /*
+ /*
* empty the vector
*/
@@ -248,27 +245,18 @@
finish_vector();
}
-template<class TYPE> inline
-Vector<TYPE>& Vector<TYPE>::operator = (const Vector<TYPE>& rhs) {
- VectorImpl::operator = (rhs);
+template <class TYPE>
+inline Vector<TYPE>& Vector<TYPE>::operator=(const Vector<TYPE>& rhs) // NOLINT(cert-oop54-cpp)
+{
+ VectorImpl::operator=(rhs);
return *this;
}
-template<class TYPE> inline
-const Vector<TYPE>& Vector<TYPE>::operator = (const Vector<TYPE>& rhs) const {
- VectorImpl::operator = (static_cast<const VectorImpl&>(rhs));
- return *this;
-}
-
-template<class TYPE> inline
-Vector<TYPE>& Vector<TYPE>::operator = (const SortedVector<TYPE>& rhs) {
- VectorImpl::operator = (static_cast<const VectorImpl&>(rhs));
- return *this;
-}
-
-template<class TYPE> inline
-const Vector<TYPE>& Vector<TYPE>::operator = (const SortedVector<TYPE>& rhs) const {
- VectorImpl::operator = (rhs);
+template <class TYPE>
+inline Vector<TYPE>& Vector<TYPE>::operator=(
+ const SortedVector<TYPE>& rhs) // NOLINT(cert-oop54-cpp)
+{
+ VectorImpl::operator=(static_cast<const VectorImpl&>(rhs));
return *this;
}
diff --git a/rootdir/init.rc b/rootdir/init.rc
index 7da2646..951934f 100644
--- a/rootdir/init.rc
+++ b/rootdir/init.rc
@@ -553,7 +553,7 @@
# Should be before netd, but after apex, properties and logging is available.
trigger load_bpf_programs
- # Now we can start zygote for devices with file based encryption
+ # Now we can start zygote.
trigger zygote-start
# Remove a file to wake up anything waiting for firmware.
@@ -624,8 +624,8 @@
chmod 0700 /metadata/vold
mkdir /metadata/password_slots 0771 root system
mkdir /metadata/bootstat 0750 system log
- mkdir /metadata/ota 0700 root system
- mkdir /metadata/ota/snapshots 0700 root system
+ mkdir /metadata/ota 0750 root system
+ mkdir /metadata/ota/snapshots 0750 root system
mkdir /metadata/userspacereboot 0770 root system
mkdir /metadata/watchdog 0770 root system
@@ -939,15 +939,22 @@
# encryption policies apply recursively. These directories should never
# contain any subdirectories other than the per-user ones. /data/media/obb
# is an exception that exists for legacy reasons.
- mkdir /data/media 0770 media_rw media_rw encryption=None
- mkdir /data/misc_ce 01771 system misc encryption=None
- mkdir /data/misc_de 01771 system misc encryption=None
- mkdir /data/system_ce 0770 system system encryption=None
- mkdir /data/system_de 0770 system system encryption=None
- mkdir /data/user 0711 system system encryption=None
- mkdir /data/user_de 0711 system system encryption=None
- mkdir /data/vendor_ce 0771 root root encryption=None
- mkdir /data/vendor_de 0771 root root encryption=None
+ #
+ # Don't use any write mode bits (0222) for any of these directories, since
+ # the only process that should write to them directly is vold (since it
+ # needs to set up file-based encryption on the subdirectories), which runs
+ # as root with CAP_DAC_OVERRIDE. This is also fully enforced via the
+ # SELinux policy. But we also set the DAC file modes accordingly, to try to
+ # minimize differences in behavior if SELinux is set to permissive mode.
+ mkdir /data/media 0550 media_rw media_rw encryption=None
+ mkdir /data/misc_ce 0551 system misc encryption=None
+ mkdir /data/misc_de 0551 system misc encryption=None
+ mkdir /data/system_ce 0550 system system encryption=None
+ mkdir /data/system_de 0550 system system encryption=None
+ mkdir /data/user 0511 system system encryption=None
+ mkdir /data/user_de 0511 system system encryption=None
+ mkdir /data/vendor_ce 0551 root root encryption=None
+ mkdir /data/vendor_de 0551 root root encryption=None
# Set the casefold flag on /data/media. For upgrades, a restorecon can be
# needed first to relabel the directory from media_rw_data_file.
@@ -1010,7 +1017,7 @@
perform_apex_config
# Create directories for boot animation.
- mkdir /data/bootanim 0755 system system encryption=DeleteIfNecessary
+ mkdir /data/misc/bootanim 0755 system system encryption=DeleteIfNecessary
exec_start derive_sdk
@@ -1034,13 +1041,9 @@
# Must start after 'derive_classpath' to have *CLASSPATH variables set.
start odsign
- # Before we can lock keys and proceed to the next boot stage, wait for
- # odsign to be done with the key
+ # Wait for odsign to be done with the key.
wait_for_prop odsign.key.done 1
- # Lock the fs-verity keyring, so no more keys can be added
- exec -- /system/bin/fsverity_init --lock
-
# Bump the boot level to 1000000000; this prevents further on-device signing.
# This is a special value that shuts down the thread which listens for
# further updates.
@@ -1069,28 +1072,10 @@
# It is recommended to put unnecessary data/ initialization from post-fs-data
# to start-zygote in device's init.rc to unblock zygote start.
-on zygote-start && property:ro.crypto.state=unencrypted
+on zygote-start
wait_for_prop odsign.verification.done 1
# A/B update verifier that marks a successful boot.
- exec_start update_verifier_nonencrypted
- start statsd
- start netd
- start zygote
- start zygote_secondary
-
-on zygote-start && property:ro.crypto.state=unsupported
- wait_for_prop odsign.verification.done 1
- # A/B update verifier that marks a successful boot.
- exec_start update_verifier_nonencrypted
- start statsd
- start netd
- start zygote
- start zygote_secondary
-
-on zygote-start && property:ro.crypto.state=encrypted && property:ro.crypto.type=file
- wait_for_prop odsign.verification.done 1
- # A/B update verifier that marks a successful boot.
- exec_start update_verifier_nonencrypted
+ exec_start update_verifier
start statsd
start netd
start zygote
diff --git a/rootdir/init.usb.rc b/rootdir/init.usb.rc
index 0730cce..dde784e 100644
--- a/rootdir/init.usb.rc
+++ b/rootdir/init.usb.rc
@@ -18,6 +18,7 @@
disabled
updatable
seclabel u:r:adbd:s0
+ user root
on property:vendor.sys.usb.adb.disabled=*
setprop sys.usb.adb.disabled ${vendor.sys.usb.adb.disabled}
diff --git a/rootdir/ramdisk_node_list b/rootdir/ramdisk_node_list
index d3ab8a6..4f45faa 100644
--- a/rootdir/ramdisk_node_list
+++ b/rootdir/ramdisk_node_list
@@ -1,3 +1,4 @@
dir dev 0755 0 0
nod dev/null 0600 0 0 c 1 3
nod dev/console 0600 0 0 c 5 1
+nod dev/urandom 0600 0 0 c 1 9
diff --git a/rootdir/ueventd.rc b/rootdir/ueventd.rc
index 0b7ffb8..60dcc2a 100644
--- a/rootdir/ueventd.rc
+++ b/rootdir/ueventd.rc
@@ -23,6 +23,11 @@
subsystem dma_heap
devname uevent_devpath
dirname /dev/dma_heap
+
+subsystem vfio
+ devname uevent_devpath
+ dirname /dev/vfio
+
# ueventd can only set permissions on device nodes and their associated
# sysfs attributes, not on arbitrary paths.
#
@@ -43,6 +48,7 @@
/dev/binder 0666 root root
/dev/hwbinder 0666 root root
/dev/vndbinder 0666 root root
+/dev/vfio/* 0666 root root
/dev/pmsg0 0222 root log
/dev/dma_heap/system 0444 system system
diff --git a/storaged/Android.bp b/storaged/Android.bp
index c3447d2..fe8c1f3 100644
--- a/storaged/Android.bp
+++ b/storaged/Android.bp
@@ -136,3 +136,37 @@
],
path: "binder",
}
+
+cc_defaults {
+ name: "storaged_service_fuzzer_defaults",
+ defaults: [
+ "storaged_defaults",
+ "service_fuzzer_defaults",
+ "fuzzer_disable_leaks",
+ ],
+ static_libs: [
+ "libstoraged",
+ ],
+ fuzz_config: {
+ cc: [
+ "dvander@google.com",
+ ],
+ triage_assignee: "waghpawan@google.com",
+ },
+}
+
+cc_fuzz {
+ name: "storaged_service_fuzzer",
+ defaults: [
+ "storaged_service_fuzzer_defaults",
+ ],
+ srcs: ["tests/fuzzers/storaged_service_fuzzer.cpp"],
+}
+
+cc_fuzz {
+ name: "storaged_private_service_fuzzer",
+ defaults: [
+ "storaged_service_fuzzer_defaults",
+ ],
+ srcs: ["tests/fuzzers/storaged_private_service_fuzzer.cpp"],
+}
diff --git a/storaged/include/storaged_service.h b/storaged/include/storaged_service.h
index 7ec6864..bf7af80 100644
--- a/storaged/include/storaged_service.h
+++ b/storaged/include/storaged_service.h
@@ -28,6 +28,7 @@
using namespace android::os;
using namespace android::os::storaged;
+namespace android {
class StoragedService : public BinderService<StoragedService>, public BnStoraged {
private:
void dumpUidRecordsDebug(int fd, const vector<struct uid_record>& entries);
@@ -53,4 +54,5 @@
sp<IStoragedPrivate> get_storaged_pri_service();
+} // namespace android
#endif /* _STORAGED_SERVICE_H_ */
\ No newline at end of file
diff --git a/storaged/storaged_service.cpp b/storaged/storaged_service.cpp
index 45f1d4d..00d36d7 100644
--- a/storaged/storaged_service.cpp
+++ b/storaged/storaged_service.cpp
@@ -38,6 +38,7 @@
extern sp<storaged_t> storaged_sp;
+namespace android {
status_t StoragedService::start() {
return BinderService<StoragedService>::publish();
}
@@ -218,3 +219,4 @@
return interface_cast<IStoragedPrivate>(binder);
}
+} // namespace android
\ No newline at end of file
diff --git a/storaged/tests/fuzzers/storaged_private_service_fuzzer.cpp b/storaged/tests/fuzzers/storaged_private_service_fuzzer.cpp
new file mode 100644
index 0000000..82eb796
--- /dev/null
+++ b/storaged/tests/fuzzers/storaged_private_service_fuzzer.cpp
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2023 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 <fuzzbinder/libbinder_driver.h>
+
+#include <storaged.h>
+#include <storaged_service.h>
+
+sp<storaged_t> storaged_sp;
+
+extern "C" int LLVMFuzzerInitialize(int /**argc*/, char /****argv*/) {
+ storaged_sp = new storaged_t();
+ storaged_sp->init();
+ return 0;
+}
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+ auto storagedPrivateService = new StoragedPrivateService();
+ fuzzService(storagedPrivateService, FuzzedDataProvider(data, size));
+ return 0;
+}
\ No newline at end of file
diff --git a/storaged/tests/fuzzers/storaged_service_fuzzer.cpp b/storaged/tests/fuzzers/storaged_service_fuzzer.cpp
new file mode 100644
index 0000000..d11ecc3
--- /dev/null
+++ b/storaged/tests/fuzzers/storaged_service_fuzzer.cpp
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2023 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 <fuzzbinder/libbinder_driver.h>
+
+#include <storaged.h>
+#include <storaged_service.h>
+
+sp<storaged_t> storaged_sp;
+
+extern "C" int LLVMFuzzerInitialize(int /**argc*/, char /****argv*/) {
+ storaged_sp = new storaged_t();
+ storaged_sp->init();
+ return 0;
+}
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+ auto storagedService = new StoragedService();
+ fuzzService(storagedService, FuzzedDataProvider(data, size));
+ return 0;
+}
\ No newline at end of file
diff --git a/trusty/confirmationui/fuzz/Android.bp b/trusty/confirmationui/fuzz/Android.bp
index 4780943..96804bd 100644
--- a/trusty/confirmationui/fuzz/Android.bp
+++ b/trusty/confirmationui/fuzz/Android.bp
@@ -26,7 +26,8 @@
"-DTRUSTY_APP_FILENAME=\"confirmationui.syms.elf\"",
],
fuzz_config: {
- cc: ["trong@google.com"],
+ cc: ["mikemcternan@google.com"],
+ componentid: 1290237,
},
}
@@ -40,7 +41,8 @@
"libdmabufheap",
],
fuzz_config: {
- cc: ["trong@google.com"],
+ cc: ["mikemcternan@google.com"],
+ componentid: 1290237,
},
// The initial corpus for this fuzzer was derived by dumping messages from/to
diff --git a/trusty/keymaster/set_attestation_ids/set_attestation_ids.cpp b/trusty/keymaster/set_attestation_ids/set_attestation_ids.cpp
index e944167..6b8f90f 100644
--- a/trusty/keymaster/set_attestation_ids/set_attestation_ids.cpp
+++ b/trusty/keymaster/set_attestation_ids/set_attestation_ids.cpp
@@ -17,8 +17,10 @@
#include <getopt.h>
#include <string>
+#include <vector>
#include <android-base/properties.h>
+#include <android-base/strings.h>
#include <trusty_keymaster/ipc/trusty_keymaster_ipc.h>
namespace {
@@ -34,14 +36,66 @@
{"model", required_argument, nullptr, 'm'},
{"imei", required_argument, nullptr, 'i'},
{"meid", required_argument, nullptr, 'c'},
+ {"imei2", required_argument, nullptr, '2'},
{0, 0, 0, 0},
};
+std::string TELEPHONY_CMD_GET_IMEI = "cmd phone get-imei ";
+
+// Run a shell command and collect the output of it. If any error, set an empty string as the
+// output.
+std::string exec_command(const std::string& command) {
+ char buffer[128];
+ std::string result = "";
+
+ FILE* pipe = popen(command.c_str(), "r");
+ if (!pipe) {
+ fprintf(stderr, "popen('%s') failed\n", command.c_str());
+ return result;
+ }
+
+ while (!feof(pipe)) {
+ if (fgets(buffer, 128, pipe) != NULL) {
+ result += buffer;
+ }
+ }
+
+ pclose(pipe);
+ return result;
+}
+
+// Get IMEI using Telephony service shell command. If any error while executing the command
+// then empty string will be returned as output.
+std::string get_imei(int slot) {
+ std::string cmd = TELEPHONY_CMD_GET_IMEI + std::to_string(slot);
+ std::string output = exec_command(cmd);
+
+ if (output.empty()) {
+ fprintf(stderr, "Retrieve IMEI command ('%s') failed\n", cmd.c_str());
+ return "";
+ }
+
+ std::vector<std::string> out =
+ ::android::base::Tokenize(::android::base::Trim(output), "Device IMEI:");
+
+ if (out.size() != 1) {
+ fprintf(stderr, "Error parsing command ('%s') output '%s'\n", cmd.c_str(), output.c_str());
+ return "";
+ }
+
+ std::string imei = ::android::base::Trim(out[0]);
+ if (imei.compare("null") == 0) {
+ fprintf(stderr, "IMEI value from command ('%s') is null, skipping", cmd.c_str());
+ return "";
+ }
+ return imei;
+}
+
std::string buf2string(const keymaster::Buffer& buf) {
return std::string(reinterpret_cast<const char*>(buf.peek_read()), buf.available_read());
}
-void print_usage(const char* prog, const keymaster::SetAttestationIdsRequest& req) {
+void print_usage(const char* prog, const keymaster::SetAttestationIdsKM3Request& req) {
fprintf(stderr,
"Usage: %s [options]\n"
"\n"
@@ -55,34 +109,53 @@
" -m, --model <val> set model (default '%s')\n"
" -i, --imei <val> set IMEI (default '%s')\n"
" -c, --meid <val> set MEID (default '%s')\n"
+ " -2, --imei2 <val> set second IMEI (default '%s')\n"
"\n",
- prog, buf2string(req.brand).c_str(), buf2string(req.device).c_str(),
- buf2string(req.product).c_str(), buf2string(req.serial).c_str(),
- buf2string(req.manufacturer).c_str(), buf2string(req.model).c_str(),
- buf2string(req.imei).c_str(), buf2string(req.meid).c_str());
+ prog, buf2string(req.base.brand).c_str(), buf2string(req.base.device).c_str(),
+ buf2string(req.base.product).c_str(), buf2string(req.base.serial).c_str(),
+ buf2string(req.base.manufacturer).c_str(), buf2string(req.base.model).c_str(),
+ buf2string(req.base.imei).c_str(), buf2string(req.base.meid).c_str(),
+ buf2string(req.second_imei).c_str());
+}
+
+void set_to(keymaster::Buffer* buf, const std::string& value) {
+ if (!value.empty()) {
+ buf->Reinitialize(value.data(), value.size());
+ }
}
void set_from_prop(keymaster::Buffer* buf, const std::string& prop) {
std::string prop_value = ::android::base::GetProperty(prop, /* default_value = */ "");
- if (!prop_value.empty()) {
- buf->Reinitialize(prop_value.data(), prop_value.size());
- }
+ set_to(buf, prop_value);
}
-void populate_ids(keymaster::SetAttestationIdsRequest* req) {
+void populate_base_ids(keymaster::SetAttestationIdsRequest* req) {
set_from_prop(&req->brand, "ro.product.brand");
set_from_prop(&req->device, "ro.product.device");
set_from_prop(&req->product, "ro.product.name");
set_from_prop(&req->serial, "ro.serialno");
set_from_prop(&req->manufacturer, "ro.product.manufacturer");
set_from_prop(&req->model, "ro.product.model");
+ std::string imei = get_imei(0);
+ set_to(&req->imei, imei);
+}
+
+void populate_ids(keymaster::SetAttestationIdsKM3Request* req) {
+ populate_base_ids(&req->base);
+
+ // - "What about IMEI?"
+ // - "You've already had it."
+ // - "We've had one, yes. What about second IMEI?"
+ // - "I don't think he knows about second IMEI, Pip."
+ std::string imei2 = get_imei(1);
+ set_to(&req->second_imei, imei2);
}
} // namespace
int main(int argc, char** argv) {
// By default, set attestation IDs to the values in userspace properties.
- keymaster::SetAttestationIdsRequest req(/* ver = */ 4);
+ keymaster::SetAttestationIdsKM3Request req(/* ver = */ 4);
populate_ids(&req);
while (true) {
@@ -94,28 +167,31 @@
switch (c) {
case 'b':
- req.brand.Reinitialize(optarg, strlen(optarg));
+ req.base.brand.Reinitialize(optarg, strlen(optarg));
break;
case 'd':
- req.device.Reinitialize(optarg, strlen(optarg));
+ req.base.device.Reinitialize(optarg, strlen(optarg));
break;
case 'p':
- req.product.Reinitialize(optarg, strlen(optarg));
+ req.base.product.Reinitialize(optarg, strlen(optarg));
break;
case 's':
- req.serial.Reinitialize(optarg, strlen(optarg));
+ req.base.serial.Reinitialize(optarg, strlen(optarg));
break;
case 'M':
- req.manufacturer.Reinitialize(optarg, strlen(optarg));
+ req.base.manufacturer.Reinitialize(optarg, strlen(optarg));
break;
case 'm':
- req.model.Reinitialize(optarg, strlen(optarg));
+ req.base.model.Reinitialize(optarg, strlen(optarg));
break;
case 'i':
- req.imei.Reinitialize(optarg, strlen(optarg));
+ req.base.imei.Reinitialize(optarg, strlen(optarg));
break;
case 'c':
- req.meid.Reinitialize(optarg, strlen(optarg));
+ req.base.meid.Reinitialize(optarg, strlen(optarg));
+ break;
+ case '2':
+ req.second_imei.Reinitialize(optarg, strlen(optarg));
break;
case 'h':
print_usage(argv[0], req);
@@ -144,19 +220,33 @@
" manufacturer: %s\n"
" model: %s\n"
" IMEI: %s\n"
- " MEID: %s\n",
- buf2string(req.brand).c_str(), buf2string(req.device).c_str(),
- buf2string(req.product).c_str(), buf2string(req.serial).c_str(),
- buf2string(req.manufacturer).c_str(), buf2string(req.model).c_str(),
- buf2string(req.imei).c_str(), buf2string(req.meid).c_str());
+ " MEID: %s\n"
+ " SECOND_IMEI: %s\n\n",
+ buf2string(req.base.brand).c_str(), buf2string(req.base.device).c_str(),
+ buf2string(req.base.product).c_str(), buf2string(req.base.serial).c_str(),
+ buf2string(req.base.manufacturer).c_str(), buf2string(req.base.model).c_str(),
+ buf2string(req.base.imei).c_str(), buf2string(req.base.meid).c_str(),
+ buf2string(req.second_imei).c_str());
+ fflush(stdout);
keymaster::EmptyKeymasterResponse rsp(/* ver = */ 4);
- ret = trusty_keymaster_send(KM_SET_ATTESTATION_IDS, req, &rsp);
- if (ret) {
- fprintf(stderr, "SET_ATTESTATION_IDS failed: %d\n", ret);
- trusty_keymaster_disconnect();
- return EXIT_FAILURE;
+ const char* msg;
+ if (req.second_imei.available_read() == 0) {
+ // No SECOND_IMEI set, use base command.
+ ret = trusty_keymaster_send(KM_SET_ATTESTATION_IDS, req.base, &rsp);
+ msg = "SET_ATTESTATION_IDS";
+ } else {
+ // SECOND_IMEI is set, use updated command.
+ ret = trusty_keymaster_send(KM_SET_ATTESTATION_IDS_KM3, req, &rsp);
+ msg = "SET_ATTESTATION_IDS_KM3";
}
+ trusty_keymaster_disconnect();
- return EXIT_SUCCESS;
+ if (ret) {
+ fprintf(stderr, "%s failed: %d\n", msg, ret);
+ return EXIT_FAILURE;
+ } else {
+ printf("done\n");
+ return EXIT_SUCCESS;
+ }
}
diff --git a/trusty/keymint/Android.bp b/trusty/keymint/Android.bp
index c19ebbd..19dcc98 100644
--- a/trusty/keymint/Android.bp
+++ b/trusty/keymint/Android.bp
@@ -35,6 +35,7 @@
"liblibc",
"liblog_rust",
],
+ prefer_rlib: true,
required: [
"android.hardware.hardware_keystore.xml",
],
diff --git a/trusty/libtrusty-rs/src/lib.rs b/trusty/libtrusty-rs/src/lib.rs
index 28ea075..22b894a 100644
--- a/trusty/libtrusty-rs/src/lib.rs
+++ b/trusty/libtrusty-rs/src/lib.rs
@@ -102,6 +102,8 @@
let file = File::options().read(true).write(true).open(device)?;
let srv_name = CString::new(service).expect("Service name contained null bytes");
+ // SAFETY: The file descriptor is valid because it came from a `File`, and the name is a
+ // valid C string because it came from a `CString`.
unsafe {
tipc_connect(file.as_raw_fd(), srv_name.as_ptr())?;
}