Merge Android U (ab/10368041)
Bug: 291102124
Merged-In: I849958d9e9de3be48e32f57acc7b3526e184482c
Change-Id: Ic765b54aa981b6370b5dff6b4cf046bbe9fbdbaf
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/Android.bp b/bootstat/Android.bp
index ca59ef3..0c8760c 100644
--- a/bootstat/Android.bp
+++ b/bootstat/Android.bp
@@ -72,9 +72,6 @@
],
init_rc: ["bootstat.rc"],
product_variables: {
- pdk: {
- enabled: false,
- },
debuggable: {
init_rc: ["bootstat-debug.rc"],
},
diff --git a/debuggerd/Android.bp b/debuggerd/Android.bp
index d20de6b..5393e25 100644
--- a/debuggerd/Android.bp
+++ b/debuggerd/Android.bp
@@ -188,6 +188,7 @@
cc_library_static {
name: "libdebuggerd",
defaults: ["debuggerd_defaults"],
+ ramdisk_available: true,
recovery_available: true,
vendor_ramdisk_available: true,
@@ -221,9 +222,6 @@
"libbase",
"libcutils",
],
- runtime_libs: [
- "libdexfile", // libdexfile_support dependency
- ],
whole_static_libs: [
"libasync_safe",
@@ -250,6 +248,19 @@
"libdexfile",
],
},
+ ramdisk: {
+ exclude_static_libs: [
+ "libdexfile_support",
+ ],
+ exclude_runtime_libs: [
+ "libdexfile",
+ ],
+ },
+ android: {
+ runtime_libs: [
+ "libdexfile", // libdexfile_support dependency
+ ],
+ },
},
product_variables: {
diff --git a/debuggerd/crasher/arm/crashglue.S b/debuggerd/crasher/arm/crashglue.S
index e4adf40..0def8ae 100644
--- a/debuggerd/crasher/arm/crashglue.S
+++ b/debuggerd/crasher/arm/crashglue.S
@@ -23,10 +23,11 @@
ldr lr, [lr]
b .
.cfi_endproc
+ .size crash1, .-crash1
-.globl crashnostack
-.type crashnostack, %function
-crashnostack:
+.globl crash_no_stack
+.type crash_no_stack, %function
+crash_no_stack:
.cfi_startproc
mov r1, sp
.cfi_def_cfa_register r1
@@ -35,3 +36,4 @@
ldr r0, [r0]
b .
.cfi_endproc
+ .size crash_no_stack, .-crash_no_stack
diff --git a/debuggerd/crasher/arm64/crashglue.S b/debuggerd/crasher/arm64/crashglue.S
index 97c824e..c56e19a 100644
--- a/debuggerd/crasher/arm64/crashglue.S
+++ b/debuggerd/crasher/arm64/crashglue.S
@@ -41,11 +41,12 @@
ldr x30, [x30]
b .
.cfi_endproc
+ .size crash1, .-crash1
-.globl crashnostack
-.type crashnostack, %function
-crashnostack:
+.globl crash_no_stack
+.type crash_no_stack, %function
+crash_no_stack:
.cfi_startproc
mov x1, sp
.cfi_def_cfa_register x1
@@ -54,3 +55,41 @@
ldr x0, [x0]
b .
.cfi_endproc
+ .size crash_no_stack, .-crash_no_stack
+
+
+.globl crash_bti
+.type crash_bti, %function
+crash_bti:
+ .cfi_startproc
+ adr x16, 1f
+ br x16
+1: // Deliberatly not a bti instruction so we crash here.
+ b .
+ .cfi_endproc
+ .size crash_bti, .-crash_bti
+
+
+.globl crash_pac
+.type crash_pac, %function
+crash_pac:
+ .cfi_startproc
+ paciasp
+ // Since sp is a pac input, this ensures a mismatch.
+ sub sp, sp, #16
+ autiasp
+ b .
+ .cfi_endproc
+ .size crash_pac, .-crash_pac
+
+// Set the PAC and BTI bits for this object file.
+.section .note.gnu.property, "a"
+.balign 8
+.long 4
+.long 0x10
+.long 0x5
+.asciz "GNU"
+.long 0xc0000000
+.long 4
+.long 0x3
+.long 0
diff --git a/debuggerd/crasher/crasher.cpp b/debuggerd/crasher/crasher.cpp
index 6a19878..3b52776 100644
--- a/debuggerd/crasher/crasher.cpp
+++ b/debuggerd/crasher/crasher.cpp
@@ -19,6 +19,7 @@
#include <assert.h>
#include <dirent.h>
#include <errno.h>
+#include <error.h>
#include <fcntl.h>
#include <pthread.h>
#include <signal.h>
@@ -29,6 +30,9 @@
#include <sys/prctl.h>
#include <unistd.h>
+#include <android-base/file.h>
+#include <android-base/strings.h>
+
// We test both kinds of logging.
#include <android-base/logging.h>
#include <log/log.h>
@@ -59,8 +63,10 @@
// Avoid name mangling so that stacks are more readable.
extern "C" {
-void crash1(void);
-void crashnostack(void);
+void crash1();
+void crash_no_stack();
+void crash_bti();
+void crash_pac();
int do_action(const char* arg);
@@ -148,7 +154,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';
}
}
@@ -196,13 +202,6 @@
fprintf(stderr, " fdsan_file close a file descriptor that's owned by a FILE*\n");
fprintf(stderr, " fdsan_dir close a file descriptor that's owned by a DIR*\n");
fprintf(stderr, " seccomp fail a seccomp check\n");
-#if defined(__arm__)
- fprintf(stderr, " kuser_helper_version call kuser_helper_version\n");
- fprintf(stderr, " kuser_get_tls call kuser_get_tls\n");
- fprintf(stderr, " kuser_cmpxchg call kuser_cmpxchg\n");
- fprintf(stderr, " kuser_memory_barrier call kuser_memory_barrier\n");
- fprintf(stderr, " kuser_cmpxchg64 call kuser_cmpxchg64\n");
-#endif
fprintf(stderr, " xom read execute-only memory\n");
fprintf(stderr, "\n");
fprintf(stderr, " LOG_ALWAYS_FATAL call liblog LOG_ALWAYS_FATAL\n");
@@ -223,6 +222,20 @@
fprintf(stderr, "\n");
fprintf(stderr, " no_new_privs set PR_SET_NO_NEW_PRIVS and then abort\n");
fprintf(stderr, "\n");
+#if defined(__arm__)
+ fprintf(stderr, "Also, since this is an arm32 binary:\n");
+ fprintf(stderr, " kuser_helper_version call kuser_helper_version\n");
+ fprintf(stderr, " kuser_get_tls call kuser_get_tls\n");
+ fprintf(stderr, " kuser_cmpxchg call kuser_cmpxchg\n");
+ fprintf(stderr, " kuser_memory_barrier call kuser_memory_barrier\n");
+ fprintf(stderr, " kuser_cmpxchg64 call kuser_cmpxchg64\n");
+#endif
+#if defined(__aarch64__)
+ fprintf(stderr, "Also, since this is an arm64 binary:\n");
+ fprintf(stderr, " bti fail a branch target identification (BTI) check\n");
+ fprintf(stderr, " pac fail a pointer authentication (PAC) check\n");
+#endif
+ fprintf(stderr, "\n");
fprintf(stderr, "prefix any of the above with 'thread-' to run on a new thread\n");
fprintf(stderr, "prefix any of the above with 'exhaustfd-' to exhaust\n");
fprintf(stderr, "all available file descriptors before crashing.\n");
@@ -231,6 +244,21 @@
return EXIT_FAILURE;
}
+[[maybe_unused]] static void CheckCpuFeature(const std::string& name) {
+ std::string cpuinfo;
+ if (!android::base::ReadFileToString("/proc/cpuinfo", &cpuinfo)) {
+ error(1, errno, "couldn't read /proc/cpuinfo");
+ }
+ std::vector<std::string> lines = android::base::Split(cpuinfo, "\n");
+ for (std::string_view line : lines) {
+ if (!android::base::ConsumePrefix(&line, "Features\t:")) continue;
+ std::vector<std::string> features = android::base::Split(std::string(line), " ");
+ if (std::find(features.begin(), features.end(), name) == features.end()) {
+ error(1, 0, "/proc/cpuinfo does not report feature '%s'", name.c_str());
+ }
+ }
+}
+
noinline int do_action(const char* arg) {
// Prefixes.
if (!strncmp(arg, "wait-", strlen("wait-"))) {
@@ -256,7 +284,7 @@
} else if (!strcasecmp(arg, "stack-overflow")) {
overflow_stack(nullptr);
} else if (!strcasecmp(arg, "nostack")) {
- crashnostack();
+ crash_no_stack();
} else if (!strcasecmp(arg, "exit")) {
exit(1);
} else if (!strcasecmp(arg, "call-null")) {
@@ -350,6 +378,14 @@
} else if (!strcasecmp(arg, "kuser_cmpxchg64")) {
return __kuser_cmpxchg64(0, 0, 0);
#endif
+#if defined(__aarch64__)
+ } else if (!strcasecmp(arg, "bti")) {
+ CheckCpuFeature("bti");
+ crash_bti();
+ } else if (!strcasecmp(arg, "pac")) {
+ CheckCpuFeature("paca");
+ crash_pac();
+#endif
} else if (!strcasecmp(arg, "no_new_privs")) {
if (prctl(PR_SET_NO_NEW_PRIVS, 1) != 0) {
fprintf(stderr, "prctl(PR_SET_NO_NEW_PRIVS, 1) failed: %s\n", strerror(errno));
diff --git a/debuggerd/crasher/riscv64/crashglue.S b/debuggerd/crasher/riscv64/crashglue.S
index 42f59b3..f179e33 100644
--- a/debuggerd/crasher/riscv64/crashglue.S
+++ b/debuggerd/crasher/riscv64/crashglue.S
@@ -43,10 +43,11 @@
ld t2, 0(zero)
j .
.cfi_endproc
+ .size crash1, .-crash1
-.globl crashnostack
-crashnostack:
+.globl crash_no_stack
+crash_no_stack:
.cfi_startproc
mv t1, sp
.cfi_def_cfa_register t1
@@ -54,3 +55,4 @@
ld t2, 0(zero)
j .
.cfi_endproc
+ .size crash_no_stack, .-crash_no_stack
diff --git a/debuggerd/crasher/x86/crashglue.S b/debuggerd/crasher/x86/crashglue.S
index e8eb3a7..453035b 100644
--- a/debuggerd/crasher/x86/crashglue.S
+++ b/debuggerd/crasher/x86/crashglue.S
@@ -6,13 +6,15 @@
movl $0, %edx
jmp *%edx
+ .size crash1, .-crash1
-.globl crashnostack
-crashnostack:
+.globl crash_no_stack
+crash_no_stack:
.cfi_startproc
movl %esp, %eax
.cfi_def_cfa_register %eax
movl $0, %esp
movl (%esp), %ebx
.cfi_endproc
+ .size crash_no_stack, .-crash_no_stack
diff --git a/debuggerd/crasher/x86_64/crashglue.S b/debuggerd/crasher/x86_64/crashglue.S
index 8f67214..c3d39c4 100644
--- a/debuggerd/crasher/x86_64/crashglue.S
+++ b/debuggerd/crasher/x86_64/crashglue.S
@@ -6,13 +6,15 @@
movl $0, %edx
jmp *%rdx
+ .size crash1, .-crash1
-.globl crashnostack
-crashnostack:
+.globl crash_no_stack
+crash_no_stack:
.cfi_startproc
movq %rsp, %rax
.cfi_def_cfa_register %rax
movq $0, %rsp
movq (%rsp), %rbx
.cfi_endproc
+ .size crash_no_stack, .-crash_no_stack
diff --git a/debuggerd/debuggerd_test.cpp b/debuggerd/debuggerd_test.cpp
index 4cd6193..19ff7eb 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 {
@@ -2281,10 +2264,14 @@
ASSERT_MATCH(result, R"(\nmemory map \(.*\): \(fault address prefixed with --->)\n)");
- // Assumes that the open files section comes after the map section.
- // If that assumption changes, the regex below needs to change.
+ // Verifies that the fault address error message is at the end of the
+ // maps section. To do this, the check below looks for the start of the
+ // open files section or the start of the log file section. It's possible
+ // for either of these sections to be present after the maps section right
+ // now.
+ // If the sections move around, this check might need to be modified.
match_str = android::base::StringPrintf(
- R"(\n--->Fault address falls at %s after any mapped regions\n\nopen files:)",
+ R"(\n--->Fault address falls at %s after any mapped regions\n(---------|\nopen files:))",
format_pointer(crash_uptr).c_str());
ASSERT_MATCH(result, match_str);
}
diff --git a/debuggerd/handler/debuggerd_handler.cpp b/debuggerd/handler/debuggerd_handler.cpp
index c6a535a..01365f2 100644
--- a/debuggerd/handler/debuggerd_handler.cpp
+++ b/debuggerd/handler/debuggerd_handler.cpp
@@ -552,8 +552,14 @@
}
debugger_process_info process_info = {};
+ if (g_callbacks.get_process_info) {
+ process_info = g_callbacks.get_process_info();
+ }
uintptr_t si_val = reinterpret_cast<uintptr_t>(info->si_ptr);
if (signal_number == BIONIC_SIGNAL_DEBUGGER) {
+ // Applications can set abort messages via android_set_abort_message without
+ // actually aborting; ignore those messages in non-fatal dumps.
+ process_info.abort_msg = nullptr;
if (info->si_code == SI_QUEUE && info->si_pid == __getpid()) {
// Allow for the abort message to be explicitly specified via the sigqueue value.
// Keep the bottom bit intact for representing whether we want a backtrace or a tombstone.
@@ -562,8 +568,6 @@
info->si_ptr = reinterpret_cast<void*>(si_val & 1);
}
}
- } else if (g_callbacks.get_process_info) {
- process_info = g_callbacks.get_process_info();
}
gwp_asan_callbacks_t gwp_asan_callbacks = {};
@@ -721,19 +725,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/libdebuggerd/tombstone_proto.cpp b/debuggerd/libdebuggerd/tombstone_proto.cpp
index 7b2e068..744bfab 100644
--- a/debuggerd/libdebuggerd/tombstone_proto.cpp
+++ b/debuggerd/libdebuggerd/tombstone_proto.cpp
@@ -493,27 +493,48 @@
}
}
+// This creates a fake log message that indicates an error occurred when
+// reading the log.
+static void add_error_log_msg(Tombstone* tombstone, const std::string&& error_msg) {
+ LogBuffer buffer;
+ buffer.set_name("ERROR");
+
+ LogMessage* log_msg = buffer.add_logs();
+ log_msg->set_timestamp("00-00 00:00:00.000");
+ log_msg->set_pid(0);
+ log_msg->set_tid(0);
+ log_msg->set_priority(ANDROID_LOG_ERROR);
+ log_msg->set_tag("");
+ log_msg->set_message(error_msg);
+
+ *tombstone->add_log_buffers() = std::move(buffer);
+
+ async_safe_format_log(ANDROID_LOG_ERROR, LOG_TAG, "%s", error_msg.c_str());
+}
+
static void dump_log_file(Tombstone* tombstone, const char* logger, pid_t pid) {
logger_list* logger_list = android_logger_list_open(android_name_to_log_id(logger),
ANDROID_LOG_NONBLOCK, kMaxLogMessages, pid);
+ if (logger_list == nullptr) {
+ add_error_log_msg(tombstone, android::base::StringPrintf("Cannot open log file %s", logger));
+ return;
+ }
LogBuffer buffer;
-
while (true) {
log_msg log_entry;
ssize_t actual = android_logger_list_read(logger_list, &log_entry);
-
if (actual < 0) {
if (actual == -EINTR) {
// interrupted by signal, retry
continue;
}
- if (actual == -EAGAIN) {
- // non-blocking EOF; we're done
- break;
- } else {
- break;
+ // Don't consider EAGAIN an error since this is a non-blocking call.
+ if (actual != -EAGAIN) {
+ add_error_log_msg(tombstone, android::base::StringPrintf("reading log %s failed (%s)",
+ logger, strerror(-actual)));
}
+ break;
} else if (actual == 0) {
break;
}
diff --git a/debuggerd/libdebuggerd/tombstone_proto_to_text.cpp b/debuggerd/libdebuggerd/tombstone_proto_to_text.cpp
index 8e6abdf..eed81fc 100644
--- a/debuggerd/libdebuggerd/tombstone_proto_to_text.cpp
+++ b/debuggerd/libdebuggerd/tombstone_proto_to_text.cpp
@@ -81,6 +81,8 @@
if (!tombstone.command_line().empty()) {
process_name = tombstone.command_line()[0].c_str();
CB(should_log, "Cmdline: %s", android::base::Join(tombstone.command_line(), " ").c_str());
+ } else {
+ CB(should_log, "Cmdline: <unknown>");
}
CB(should_log, "pid: %d, tid: %d, name: %s >>> %s <<<", tombstone.pid(), thread.id(),
thread.name().c_str(), process_name);
diff --git a/debuggerd/proto/Android.bp b/debuggerd/proto/Android.bp
index 73cf573..804f805 100644
--- a/debuggerd/proto/Android.bp
+++ b/debuggerd/proto/Android.bp
@@ -35,6 +35,7 @@
"com.android.runtime",
],
+ ramdisk_available: true,
recovery_available: true,
vendor_ramdisk_available: true,
}
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/fastboot/README.md b/fastboot/README.md
index 63db5c3..55583eb 100644
--- a/fastboot/README.md
+++ b/fastboot/README.md
@@ -165,6 +165,28 @@
using the new bootloader.
+## Flashing Logic
+
+Fastboot binary will follow directions listed out fastboot-info.txt
+build artifact for fastboot flashall && fastboot update comamnds.
+This build artifact will live inside of ANDROID_PRODUCT_OUT &&
+target_files_package && updatepackage.
+
+
+The currently defined commands are:
+
+ flash %s Flash a given partition. Optional arguments include
+ --slot-other, {filename_path}, --apply-vbmeta
+
+ reboot %s Reboot to either bootloader or fastbootd
+
+ update-super Updates the super partition
+
+ if-wipe Conditionally run some other functionality if
+ wipe is specified
+
+ erase %s Erase a given partition (can only be used in conjunction)
+ with if-wipe -> eg. if-wipe erase cache
## Client Variables
diff --git a/fastboot/fastboot.cpp b/fastboot/fastboot.cpp
index 42d4f60..71a228e 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"
@@ -104,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;
@@ -120,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",
@@ -132,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 },
@@ -174,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;
@@ -630,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?
@@ -997,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);
@@ -1016,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;
@@ -1038,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;
@@ -1056,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);
@@ -1072,7 +1079,7 @@
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) {
@@ -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()) {
@@ -1487,22 +1496,32 @@
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 (fp->source) {
unique_fd fd = fp->source->OpenFile(fname);
- if (fd < 0 || !load_buf_fd(std::move(fd), &buf)) {
+ if (fd < 0 || !load_buf_fd(std::move(fd), &buf, fp)) {
die("could not load '%s': %s", fname, strerror(errno));
}
- } else if (!load_buf(fname, &buf)) {
+ 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);
}
@@ -1741,7 +1760,7 @@
}
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()) {
@@ -1781,10 +1800,37 @@
CancelSnapshotIfNeeded();
- HardcodedFlash();
+ tasks_ = CollectTasks();
+
+ for (auto& task : tasks_) {
+ task->Run();
+ }
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();
+ }
+ if (fp_->exclude_dynamic_partitions) {
+ auto is_non_static_flash_task = [](const auto& task) -> bool {
+ if (auto flash_task = task->AsFlashTask()) {
+ if (!should_flash_in_userspace(flash_task->GetPartitionAndSlot())) {
+ return false;
+ }
+ }
+ return true;
+ };
+ tasks.erase(std::remove_if(tasks.begin(), tasks.end(), is_non_static_flash_task),
+ tasks.end());
+ }
+ return tasks;
+}
+
void FlashAllTool::CheckRequirements() {
std::vector<char> contents;
if (!fp_->source->ReadFile("android-info.txt", &contents)) {
@@ -1833,15 +1879,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.
@@ -1853,7 +1897,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);
@@ -1863,43 +1907,39 @@
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);
-}
-
bool ZipImageSource::ReadFile(const std::string& name, std::vector<char>* out) const {
return UnzipToMemory(zip_, name, out);
}
@@ -1916,7 +1956,6 @@
}
ZipImageSource zp = ZipImageSource(zip);
fp->source = &zp;
- fp->wants_wipe = false;
FlashAllTool tool(fp);
tool.Flash();
@@ -1982,7 +2021,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;
@@ -1995,8 +2034,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) {
@@ -2051,7 +2090,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));
@@ -2192,6 +2231,8 @@
{"disable-verification", no_argument, 0, 0},
{"disable-verity", no_argument, 0, 0},
{"disable-super-optimization", no_argument, 0, 0},
+ {"exclude-dynamic-partitions", 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},
@@ -2214,8 +2255,9 @@
{0, 0, 0, 0}};
serial = getenv("FASTBOOT_DEVICE");
- if (!serial)
+ if (!serial) {
serial = getenv("ANDROID_SERIAL");
+ }
int c;
while ((c = getopt_long(argc, argv, "a::hls:S:vw", longopts, &longindex)) != -1) {
@@ -2231,6 +2273,11 @@
g_disable_verity = true;
} else if (name == "disable-super-optimization") {
fp->should_optimize_flash_super = false;
+ } else if (name == "exclude-dynamic-partitions") {
+ fp->exclude_dynamic_partitions = true;
+ 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") {
@@ -2286,7 +2333,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;
@@ -2404,7 +2451,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") {
@@ -2498,7 +2546,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);
@@ -2559,7 +2607,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());
}
diff --git a/fastboot/fastboot.h b/fastboot/fastboot.h
index ae488d8..75b8d29 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"
@@ -97,6 +98,9 @@
bool skip_secondary = false;
bool force_flash = false;
bool should_optimize_flash_super = true;
+ bool should_use_fastboot_info = true;
+ bool exclude_dynamic_partitions = false;
+ uint64_t sparse_limit = 0;
std::string slot_override;
std::string current_slot;
@@ -110,17 +114,22 @@
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_;
};
@@ -140,6 +149,7 @@
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,
@@ -176,11 +186,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 6ac26ce..8774ead 100644
--- a/fastboot/fastboot_driver.h
+++ b/fastboot/fastboot_driver.h
@@ -105,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 d2a123b..7c41d78 100644
--- a/fastboot/fastboot_driver_mock.h
+++ b/fastboot/fastboot_driver_mock.h
@@ -28,15 +28,16 @@
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/task.cpp b/fastboot/task.cpp
index 96b952c..146064c 100644
--- a/fastboot/task.cpp
+++ b/fastboot/task.cpp
@@ -32,7 +32,7 @@
void FlashTask::Run() {
auto flash = [&](const std::string& partition) {
- if (should_flash_in_userspace(partition) && !is_userspace_fastboot()) {
+ if (should_flash_in_userspace(partition) && !is_userspace_fastboot() && !fp_->force_flash) {
die("The partition you are trying to flash is dynamic, and "
"should be flashed via fastbootd. Please run:\n"
"\n"
@@ -96,20 +96,22 @@
return "reboot " + reboot_target_;
}
-FlashSuperLayoutTask::FlashSuperLayoutTask(const std::string& super_name,
- std::unique_ptr<SuperFlashHelper> helper,
- SparsePtr sparse_layout, uint64_t super_size)
+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_));
@@ -118,11 +120,11 @@
// Send the data to the device.
flash_partition_files(super_name_, files);
}
-std::string FlashSuperLayoutTask::ToString() {
+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";
@@ -186,11 +188,11 @@
};
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";
@@ -259,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) {}
@@ -330,7 +332,7 @@
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() {
diff --git a/fastboot/task.h b/fastboot/task.h
index 500655d..f7c8801 100644
--- a/fastboot/task.h
+++ b/fastboot/task.h
@@ -79,13 +79,13 @@
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;
@@ -96,6 +96,7 @@
std::unique_ptr<SuperFlashHelper> helper_;
SparsePtr sparse_layout_;
uint64_t super_size_;
+ const FlashingPlan* fp_;
};
class UpdateSuperTask : public Task {
diff --git a/fastboot/task_test.cpp b/fastboot/task_test.cpp
index b4e139b..1ba3f4a 100644
--- a/fastboot/task_test.cpp
+++ b/fastboot/task_test.cpp
@@ -24,6 +24,7 @@
#include <memory>
#include <unordered_map>
#include "android-base/strings.h"
+
using android::base::Split;
using testing::_;
@@ -60,6 +61,33 @@
return ParseFastbootInfoLine(fp, vec_command);
}
+// 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"};
@@ -159,3 +187,51 @@
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/fs_mgr/Android.bp b/fs_mgr/Android.bp
index dd61272..87db98b 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,7 +59,7 @@
name: "libfs_mgr_defaults",
defaults: ["fs_mgr_defaults"],
export_include_dirs: ["include"],
- include_dirs: ["system/vold"],
+ local_include_dirs: ["include/"],
cflags: [
"-D_FILE_OFFSET_BITS=64",
],
@@ -70,8 +69,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",
],
@@ -89,8 +89,6 @@
static_libs: [
"libavb",
"libfs_avb",
- "libfstab",
- "libdm",
"libgsi",
],
export_static_lib_headers: [
@@ -174,38 +172,22 @@
}
cc_library_static {
- // Do not ever make this a shared library as long as it is vendor_available.
- // It does not have a stable interface.
- name: "libfstab",
- vendor_available: true,
+ name: "libfs_mgr_file_wait",
+ defaults: ["fs_mgr_defaults"],
+ export_include_dirs: ["include"],
+ cflags: [
+ "-D_FILE_OFFSET_BITS=64",
+ ],
+ srcs: [
+ "file_wait.cpp",
+ ],
+ shared_libs: [
+ "libbase",
+ ],
+ host_supported: true,
ramdisk_available: true,
vendor_ramdisk_available: true,
recovery_available: true,
- host_supported: true,
- defaults: ["fs_mgr_defaults"],
- srcs: [
- "fs_mgr_fstab.cpp",
- "fs_mgr_boot_config.cpp",
- "fs_mgr_slotselect.cpp",
- ],
- target: {
- darwin: {
- enabled: false,
- },
- vendor: {
- cflags: [
- // Skipping entries in fstab should only be done in a system
- // process as the config file is in /system_ext.
- // Remove the op from the vendor variant.
- "-DNO_SKIP_MOUNT",
- ],
- },
- },
- export_include_dirs: ["include_fstab"],
- header_libs: [
- "libbase_headers",
- "libgsi_headers",
- ],
}
cc_binary {
diff --git a/fs_mgr/fs_mgr.cpp b/fs_mgr/fs_mgr.cpp
index 742cdfa..d55f8d3 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;
@@ -2258,8 +2227,8 @@
}
bool fs_mgr_mount_overlayfs_fstab_entry(const FstabEntry& entry) {
- auto overlayfs_valid_result = fs_mgr_overlayfs_valid();
- if (overlayfs_valid_result == OverlayfsValidResult::kNotSupported) {
+ const auto overlayfs_check_result = android::fs_mgr::CheckOverlayfs();
+ if (!overlayfs_check_result.supported) {
LERROR << __FUNCTION__ << "(): kernel does not support overlayfs";
return false;
}
@@ -2311,10 +2280,7 @@
}
}
- auto options = "lowerdir=" + lowerdir;
- if (overlayfs_valid_result == OverlayfsValidResult::kOverrideCredsRequired) {
- options += ",override_creds=off";
- }
+ const auto options = "lowerdir=" + lowerdir + overlayfs_check_result.mount_flags;
// Use "overlay-" + entry.blk_device as the mount() source, so that adb-remout-test don't
// confuse this with adb remount overlay, whose device name is "overlay".
@@ -2370,30 +2336,34 @@
return context;
}
-OverlayfsValidResult fs_mgr_overlayfs_valid() {
- // Overlayfs available in the kernel, and patched for override_creds?
- if (access("/sys/module/overlay/parameters/override_creds", F_OK) == 0) {
- return OverlayfsValidResult::kOverrideCredsRequired;
- }
+namespace android {
+namespace fs_mgr {
+
+OverlayfsCheckResult CheckOverlayfs() {
if (!fs_mgr_filesystem_available("overlay")) {
- return OverlayfsValidResult::kNotSupported;
+ return {.supported = false};
}
struct utsname uts;
if (uname(&uts) == -1) {
- return OverlayfsValidResult::kNotSupported;
+ return {.supported = false};
}
int major, minor;
if (sscanf(uts.release, "%d.%d", &major, &minor) != 2) {
- return OverlayfsValidResult::kNotSupported;
+ return {.supported = false};
}
- if (major < 4) {
- return OverlayfsValidResult::kOk;
+ // Overlayfs available in the kernel, and patched for override_creds?
+ if (access("/sys/module/overlay/parameters/override_creds", F_OK) == 0) {
+ auto mount_flags = ",override_creds=off"s;
+ if (major > 5 || (major == 5 && minor >= 15)) {
+ mount_flags += ",userxattr"s;
+ }
+ return {.supported = true, .mount_flags = mount_flags};
}
- if (major > 4) {
- return OverlayfsValidResult::kNotSupported;
+ if (major < 4 || (major == 4 && minor <= 3)) {
+ return {.supported = true};
}
- if (minor > 3) {
- return OverlayfsValidResult::kNotSupported;
- }
- return OverlayfsValidResult::kOk;
+ return {.supported = false};
}
+
+} // namespace fs_mgr
+} // namespace android
diff --git a/fs_mgr/fs_mgr_boot_config.cpp b/fs_mgr/fs_mgr_boot_config.cpp
deleted file mode 100644
index 75d1e0d..0000000
--- a/fs_mgr/fs_mgr_boot_config.cpp
+++ /dev/null
@@ -1,173 +0,0 @@
-/*
- * Copyright (C) 2017 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 <algorithm>
-#include <iterator>
-#include <string>
-#include <vector>
-
-#include <android-base/file.h>
-#include <android-base/stringprintf.h>
-#include <android-base/strings.h>
-#include <android-base/properties.h>
-
-#include "fs_mgr_priv.h"
-
-std::vector<std::pair<std::string, std::string>> fs_mgr_parse_cmdline(const std::string& cmdline) {
- static constexpr char quote = '"';
-
- std::vector<std::pair<std::string, std::string>> result;
- size_t base = 0;
- while (true) {
- // skip quoted spans
- auto found = base;
- while (((found = cmdline.find_first_of(" \"", found)) != cmdline.npos) &&
- (cmdline[found] == quote)) {
- // unbalanced quote is ok
- if ((found = cmdline.find(quote, found + 1)) == cmdline.npos) break;
- ++found;
- }
- std::string piece;
- auto source = cmdline.substr(base, found - base);
- std::remove_copy(source.begin(), source.end(),
- std::back_insert_iterator<std::string>(piece), quote);
- auto equal_sign = piece.find('=');
- if (equal_sign == piece.npos) {
- if (!piece.empty()) {
- // no difference between <key> and <key>=
- result.emplace_back(std::move(piece), "");
- }
- } else {
- result.emplace_back(piece.substr(0, equal_sign), piece.substr(equal_sign + 1));
- }
- if (found == cmdline.npos) break;
- base = found + 1;
- }
-
- return result;
-}
-
-std::vector<std::pair<std::string, std::string>> fs_mgr_parse_proc_bootconfig(
- const std::string& cmdline) {
- static constexpr char quote = '"';
-
- std::vector<std::pair<std::string, std::string>> result;
- for (auto& line : android::base::Split(cmdline, "\n")) {
- line.erase(std::remove(line.begin(), line.end(), quote), line.end());
- auto equal_sign = line.find('=');
- if (equal_sign == line.npos) {
- if (!line.empty()) {
- // no difference between <key> and <key>=
- result.emplace_back(std::move(line), "");
- }
- } else {
- result.emplace_back(android::base::Trim(line.substr(0, equal_sign)),
- android::base::Trim(line.substr(equal_sign + 1)));
- }
- }
-
- return result;
-}
-
-bool fs_mgr_get_boot_config_from_bootconfig(const std::string& bootconfig,
- const std::string& android_key, std::string* out_val) {
- FS_MGR_CHECK(out_val != nullptr);
-
- const std::string bootconfig_key("androidboot." + android_key);
- for (const auto& [key, value] : fs_mgr_parse_proc_bootconfig(bootconfig)) {
- if (key == bootconfig_key) {
- *out_val = value;
- return true;
- }
- }
-
- *out_val = "";
- return false;
-}
-
-bool fs_mgr_get_boot_config_from_kernel(const std::string& cmdline, const std::string& android_key,
- std::string* out_val) {
- FS_MGR_CHECK(out_val != nullptr);
-
- const std::string cmdline_key("androidboot." + android_key);
- for (const auto& [key, value] : fs_mgr_parse_cmdline(cmdline)) {
- if (key == cmdline_key) {
- *out_val = value;
- return true;
- }
- }
-
- *out_val = "";
- return false;
-}
-
-// Tries to get the given boot config value from bootconfig.
-// Returns true if successfully found, false otherwise.
-bool fs_mgr_get_boot_config_from_bootconfig_source(const std::string& key, std::string* out_val) {
- std::string bootconfig;
- if (!android::base::ReadFileToString("/proc/bootconfig", &bootconfig)) return false;
- if (!bootconfig.empty() && bootconfig.back() == '\n') {
- bootconfig.pop_back();
- }
- return fs_mgr_get_boot_config_from_bootconfig(bootconfig, key, out_val);
-}
-
-// Tries to get the given boot config value from kernel cmdline.
-// Returns true if successfully found, false otherwise.
-bool fs_mgr_get_boot_config_from_kernel_cmdline(const std::string& key, std::string* out_val) {
- std::string cmdline;
- if (!android::base::ReadFileToString("/proc/cmdline", &cmdline)) return false;
- if (!cmdline.empty() && cmdline.back() == '\n') {
- cmdline.pop_back();
- }
- return fs_mgr_get_boot_config_from_kernel(cmdline, key, out_val);
-}
-
-// Tries to get the boot config value in device tree, properties and
-// kernel cmdline (in that order). Returns 'true' if successfully
-// found, 'false' otherwise.
-bool fs_mgr_get_boot_config(const std::string& key, std::string* out_val) {
- FS_MGR_CHECK(out_val != nullptr);
-
- // firstly, check the device tree
- if (is_dt_compatible()) {
- std::string file_name = get_android_dt_dir() + "/" + key;
- if (android::base::ReadFileToString(file_name, out_val)) {
- if (!out_val->empty()) {
- out_val->pop_back(); // Trims the trailing '\0' out.
- return true;
- }
- }
- }
-
- // next, check if we have "ro.boot" property already
- *out_val = android::base::GetProperty("ro.boot." + key, "");
- if (!out_val->empty()) {
- return true;
- }
-
- // next, check if we have the property in bootconfig
- if (fs_mgr_get_boot_config_from_bootconfig_source(key, out_val)) {
- return true;
- }
-
- // finally, fallback to kernel cmdline, properties may not be ready yet
- if (fs_mgr_get_boot_config_from_kernel_cmdline(key, out_val)) {
- return true;
- }
-
- return false;
-}
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_overlayfs.cpp b/fs_mgr/fs_mgr_overlayfs.cpp
deleted file mode 100644
index f04fc8d..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(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();
- 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..8fb63b1
--- /dev/null
+++ b/fs_mgr/fs_mgr_overlayfs_mount.cpp
@@ -0,0 +1,752 @@
+/*
+ * 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/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 "";
+}
+
+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 + android::fs_mgr::CheckOverlayfs().mount_flags;
+ 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 (!android::fs_mgr::CheckOverlayfs().supported) {
+ 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..7e4d5e5 100644
--- a/fs_mgr/fs_mgr_priv.h
+++ b/fs_mgr/fs_mgr_priv.h
@@ -23,15 +23,7 @@
#include <fs_mgr.h>
#include <fstab/fstab.h>
-#include "fs_mgr_priv_boot_config.h"
-
-/* The CHECK() in logging.h will use program invocation name as the tag.
- * Thus, the log will have prefix "init: " when libfs_mgr is statically
- * linked in the init process. This might be opaque when debugging.
- * Appends "in libfs_mgr" at the end of the abort message to explicitly
- * indicate the check happens in fs_mgr.
- */
-#define FS_MGR_CHECK(x) CHECK(x) << "in libfs_mgr "
+#include "libfstab/fstab_priv.h"
#define FS_MGR_TAG "[libfs_mgr] "
@@ -89,28 +81,25 @@
using namespace std::chrono_literals;
bool fs_mgr_set_blk_ro(const std::string& blockdev, bool readonly = true);
-bool fs_mgr_update_for_slotselect(android::fs_mgr::Fstab* fstab);
bool fs_mgr_is_device_unlocked();
-const std::string& get_android_dt_dir();
-bool is_dt_compatible();
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);
-enum class OverlayfsValidResult {
- kNotSupported = 0,
- kOk,
- kOverrideCredsRequired,
-};
-OverlayfsValidResult fs_mgr_overlayfs_valid();
-
namespace android {
namespace fs_mgr {
+
bool UnmapDevice(const std::string& name);
+
+struct OverlayfsCheckResult {
+ bool supported;
+ std::string mount_flags;
+};
+
+OverlayfsCheckResult CheckOverlayfs();
+
} // namespace fs_mgr
} // namespace android
diff --git a/fs_mgr/fs_mgr_priv_boot_config.h b/fs_mgr/fs_mgr_priv_boot_config.h
deleted file mode 100644
index 6a38401..0000000
--- a/fs_mgr/fs_mgr_priv_boot_config.h
+++ /dev/null
@@ -1,37 +0,0 @@
-/*
- * Copyright (C) 2017 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 __CORE_FS_MGR_PRIV_BOOTCONFIG_H
-#define __CORE_FS_MGR_PRIV_BOOTCONFIG_H
-
-#include <sys/cdefs.h>
-#include <string>
-#include <utility>
-#include <vector>
-
-std::vector<std::pair<std::string, std::string>> fs_mgr_parse_cmdline(const std::string& cmdline);
-
-bool fs_mgr_get_boot_config_from_kernel(const std::string& cmdline, const std::string& key,
- std::string* out_val);
-bool fs_mgr_get_boot_config_from_kernel_cmdline(const std::string& key, std::string* out_val);
-bool fs_mgr_get_boot_config(const std::string& key, std::string* out_val);
-std::vector<std::pair<std::string, std::string>> fs_mgr_parse_proc_bootconfig(
- const std::string& bootconfig);
-bool fs_mgr_get_boot_config_from_bootconfig(const std::string& bootconfig, const std::string& key,
- std::string* out_val);
-bool fs_mgr_get_boot_config_from_bootconfig_source(const std::string& key, std::string* out_val);
-
-#endif /* __CORE_FS_MGR_PRIV_BOOTCONFIG_H */
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..4b3a5d3 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;
@@ -127,12 +128,11 @@
}
static android::sp<android::os::IVold> GetVold() {
+ auto sm = android::defaultServiceManager();
while (true) {
- if (auto sm = android::defaultServiceManager()) {
- if (auto binder = sm->getService(android::String16("vold"))) {
- if (auto vold = android::interface_cast<android::os::IVold>(binder)) {
- return vold;
- }
+ if (auto binder = sm->checkService(android::String16("vold"))) {
+ if (auto vold = android::interface_cast<android::os::IVold>(binder)) {
+ return vold;
}
}
std::this_thread::sleep_for(2s);
diff --git a/fs_mgr/fs_mgr_vendor_overlay.cpp b/fs_mgr/fs_mgr_vendor_overlay.cpp
index 6b32b4d..bacfa4b 100644
--- a/fs_mgr/fs_mgr_vendor_overlay.cpp
+++ b/fs_mgr/fs_mgr_vendor_overlay.cpp
@@ -85,10 +85,8 @@
return false;
}
- auto options = kLowerdirOption + source_directory + ":" + vendor_mount_point;
- if (fs_mgr_overlayfs_valid() == OverlayfsValidResult::kOverrideCredsRequired) {
- options += ",override_creds=off";
- }
+ const auto options = kLowerdirOption + source_directory + ":" + vendor_mount_point +
+ android::fs_mgr::CheckOverlayfs().mount_flags;
auto report = "__mount(source=overlay,target="s + vendor_mount_point + ",type=overlay," +
options + ")=";
auto ret = mount("overlay", vendor_mount_point.c_str(), "overlay", MS_RDONLY | MS_NOATIME,
@@ -120,7 +118,7 @@
const auto vendor_overlay_dirs = fs_mgr_get_vendor_overlay_dirs(vndk_version);
if (vendor_overlay_dirs.empty()) return true;
- if (fs_mgr_overlayfs_valid() == OverlayfsValidResult::kNotSupported) {
+ if (!android::fs_mgr::CheckOverlayfs().supported) {
LINFO << "vendor overlay: kernel does not support overlayfs";
return false;
}
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 b/fs_mgr/include_fstab
new file mode 120000
index 0000000..728737f
--- /dev/null
+++ b/fs_mgr/include_fstab
@@ -0,0 +1 @@
+libfstab/include
\ No newline at end of file
diff --git a/fs_mgr/libfiemap/Android.bp b/fs_mgr/libfiemap/Android.bp
index ddda648..c8d5756 100644
--- a/fs_mgr/libfiemap/Android.bp
+++ b/fs_mgr/libfiemap/Android.bp
@@ -24,6 +24,7 @@
vendor_ramdisk_available: true,
recovery_available: true,
export_include_dirs: ["include"],
+ host_supported: true,
}
filegroup {
diff --git a/fs_mgr/libfiemap/binder.cpp b/fs_mgr/libfiemap/binder.cpp
index 41e534a..439aac9 100644
--- a/fs_mgr/libfiemap/binder.cpp
+++ b/fs_mgr/libfiemap/binder.cpp
@@ -77,7 +77,7 @@
static FiemapStatus ToFiemapStatus(const char* func, const binder::Status& status) {
if (!status.isOk()) {
- LOG(ERROR) << func << " binder returned: " << status.toString8().string();
+ LOG(ERROR) << func << " binder returned: " << status.toString8().c_str();
if (status.serviceSpecificErrorCode() != 0) {
return FiemapStatus::FromErrorCode(status.serviceSpecificErrorCode());
} else {
@@ -106,7 +106,7 @@
auto status = manager_->deleteBackingImage(name);
if (!status.isOk()) {
LOG(ERROR) << __PRETTY_FUNCTION__
- << " binder returned: " << status.exceptionMessage().string();
+ << " binder returned: " << status.exceptionMessage().c_str();
return false;
}
return true;
@@ -122,7 +122,7 @@
auto status = manager_->mapImageDevice(name, timeout_ms_count, &map);
if (!status.isOk()) {
LOG(ERROR) << __PRETTY_FUNCTION__
- << " binder returned: " << status.exceptionMessage().string();
+ << " binder returned: " << status.exceptionMessage().c_str();
return false;
}
*path = map.path;
@@ -133,7 +133,7 @@
auto status = manager_->unmapImageDevice(name);
if (!status.isOk()) {
LOG(ERROR) << __PRETTY_FUNCTION__
- << " binder returned: " << status.exceptionMessage().string();
+ << " binder returned: " << status.exceptionMessage().c_str();
return false;
}
return true;
@@ -144,7 +144,7 @@
auto status = manager_->backingImageExists(name, &retval);
if (!status.isOk()) {
LOG(ERROR) << __PRETTY_FUNCTION__
- << " binder returned: " << status.exceptionMessage().string();
+ << " binder returned: " << status.exceptionMessage().c_str();
return false;
}
return retval;
@@ -155,7 +155,7 @@
auto status = manager_->isImageMapped(name, &retval);
if (!status.isOk()) {
LOG(ERROR) << __PRETTY_FUNCTION__
- << " binder returned: " << status.exceptionMessage().string();
+ << " binder returned: " << status.exceptionMessage().c_str();
return false;
}
return retval;
@@ -175,7 +175,7 @@
auto status = manager_->getAllBackingImages(&retval);
if (!status.isOk()) {
LOG(ERROR) << __PRETTY_FUNCTION__
- << " binder returned: " << status.exceptionMessage().string();
+ << " binder returned: " << status.exceptionMessage().c_str();
}
return retval;
}
@@ -189,7 +189,7 @@
auto status = manager_->removeAllImages();
if (!status.isOk()) {
LOG(ERROR) << __PRETTY_FUNCTION__
- << " binder returned: " << status.exceptionMessage().string();
+ << " binder returned: " << status.exceptionMessage().c_str();
return false;
}
return true;
@@ -199,7 +199,7 @@
auto status = manager_->disableImage(name);
if (!status.isOk()) {
LOG(ERROR) << __PRETTY_FUNCTION__
- << " binder returned: " << status.exceptionMessage().string();
+ << " binder returned: " << status.exceptionMessage().c_str();
return false;
}
return true;
@@ -209,7 +209,7 @@
auto status = manager_->removeDisabledImages();
if (!status.isOk()) {
LOG(ERROR) << __PRETTY_FUNCTION__
- << " binder returned: " << status.exceptionMessage().string();
+ << " binder returned: " << status.exceptionMessage().c_str();
return false;
}
return true;
@@ -219,7 +219,7 @@
auto status = manager_->getMappedImageDevice(name, device);
if (!status.isOk()) {
LOG(ERROR) << __PRETTY_FUNCTION__
- << " binder returned: " << status.exceptionMessage().string();
+ << " binder returned: " << status.exceptionMessage().c_str();
return false;
}
return !device->empty();
@@ -230,7 +230,7 @@
auto status = manager_->isImageDisabled(name, &retval);
if (!status.isOk()) {
LOG(ERROR) << __PRETTY_FUNCTION__
- << " binder returned: " << status.exceptionMessage().string();
+ << " binder returned: " << status.exceptionMessage().c_str();
return false;
}
return retval;
@@ -249,7 +249,7 @@
auto status = service->openImageService(dir, &manager);
if (!status.isOk() || !manager) {
- LOG(ERROR) << "Could not acquire IImageManager: " << status.exceptionMessage().string();
+ LOG(ERROR) << "Could not acquire IImageManager: " << status.exceptionMessage().c_str();
return nullptr;
}
return std::make_unique<ImageManagerBinder>(std::move(service), std::move(manager));
diff --git a/fs_mgr/libfiemap/fiemap_writer_test.cpp b/fs_mgr/libfiemap/fiemap_writer_test.cpp
index bd97a78..c37329c 100644
--- a/fs_mgr/libfiemap/fiemap_writer_test.cpp
+++ b/fs_mgr/libfiemap/fiemap_writer_test.cpp
@@ -27,6 +27,7 @@
#include <sys/vfs.h>
#include <unistd.h>
+#include <cstring>
#include <string>
#include <utility>
@@ -518,7 +519,8 @@
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)
+ << strerror(errno);
}
void TearDown() override {
diff --git a/fs_mgr/libfs_avb/avb_ops.cpp b/fs_mgr/libfs_avb/avb_ops.cpp
index a119bfc..cc19776 100644
--- a/fs_mgr/libfs_avb/avb_ops.cpp
+++ b/fs_mgr/libfs_avb/avb_ops.cpp
@@ -108,8 +108,8 @@
// Converts a partition name (with ab_suffix) to the corresponding mount point.
// e.g., "system_a" => "/system",
// e.g., "vendor_a" => "/vendor",
-static std::string DeriveMountPoint(const std::string& partition_name) {
- const std::string ab_suffix = fs_mgr_get_slot_suffix();
+static std::string DeriveMountPoint(const std::string& partition_name,
+ const std::string& ab_suffix) {
std::string mount_point(partition_name);
auto found = partition_name.rfind(ab_suffix);
if (found != std::string::npos) {
@@ -119,7 +119,7 @@
return "/" + mount_point;
}
-FsManagerAvbOps::FsManagerAvbOps() {
+FsManagerAvbOps::FsManagerAvbOps(const std::string& slot_suffix) {
// We only need to provide the implementation of read_from_partition()
// operation since that's all what is being used by the avb_slot_verify().
// Other I/O operations are only required in bootloader but not in
@@ -135,6 +135,11 @@
// Sets user_data for GetInstanceFromAvbOps() to convert it back to FsManagerAvbOps.
avb_ops_.user_data = this;
+
+ slot_suffix_ = slot_suffix;
+ if (slot_suffix_.empty()) {
+ slot_suffix_ = fs_mgr_get_slot_suffix();
+ }
}
// Given a partition name (with ab_suffix), e.g., system_a, returns the corresponding
@@ -149,7 +154,7 @@
return "";
}
- const auto mount_point = DeriveMountPoint(partition_name);
+ const auto mount_point = DeriveMountPoint(partition_name, slot_suffix_);
if (mount_point.empty()) return "";
auto fstab_entry = GetEntryForMountPoint(&fstab_, mount_point);
diff --git a/fs_mgr/libfs_avb/avb_ops.h b/fs_mgr/libfs_avb/avb_ops.h
index 12686a6..709091e 100644
--- a/fs_mgr/libfs_avb/avb_ops.h
+++ b/fs_mgr/libfs_avb/avb_ops.h
@@ -48,7 +48,7 @@
//
class FsManagerAvbOps {
public:
- FsManagerAvbOps();
+ explicit FsManagerAvbOps(const std::string& slot_suffix = {});
static FsManagerAvbOps* GetInstanceFromAvbOps(AvbOps* ops) {
return reinterpret_cast<FsManagerAvbOps*>(ops->user_data);
@@ -66,6 +66,7 @@
std::string GetPartitionPath(const char* partition_name);
AvbOps avb_ops_;
Fstab fstab_;
+ std::string slot_suffix_;
};
} // namespace fs_mgr
diff --git a/fs_mgr/libfs_avb/fs_avb.cpp b/fs_mgr/libfs_avb/fs_avb.cpp
index a288876..fb22423 100644
--- a/fs_mgr/libfs_avb/fs_avb.cpp
+++ b/fs_mgr/libfs_avb/fs_avb.cpp
@@ -182,6 +182,11 @@
// class AvbHandle
// ---------------
+AvbHandle::AvbHandle() : status_(AvbHandleStatus::kUninitialized) {
+ slot_suffix_ = fs_mgr_get_slot_suffix();
+ other_slot_suffix_ = fs_mgr_get_other_slot_suffix();
+}
+
AvbUniquePtr AvbHandle::LoadAndVerifyVbmeta(
const std::string& partition_name, const std::string& ab_suffix,
const std::string& ab_other_suffix, const std::string& expected_public_key_path,
@@ -194,6 +199,9 @@
return nullptr;
}
+ avb_handle->slot_suffix_ = ab_suffix;
+ avb_handle->other_slot_suffix_ = ab_other_suffix;
+
std::string expected_key_blob;
if (!expected_public_key_path.empty()) {
if (access(expected_public_key_path.c_str(), F_OK) != 0) {
@@ -373,9 +381,14 @@
return avb_handle;
}
-AvbUniquePtr AvbHandle::LoadAndVerifyVbmeta() {
+AvbUniquePtr AvbHandle::LoadAndVerifyVbmeta(const std::string& slot_suffix) {
// Loads inline vbmeta images, starting from /vbmeta.
- return LoadAndVerifyVbmeta("vbmeta", fs_mgr_get_slot_suffix(), fs_mgr_get_other_slot_suffix(),
+ auto suffix = slot_suffix;
+ if (suffix.empty()) {
+ suffix = fs_mgr_get_slot_suffix();
+ }
+ auto other_suffix = android::fs_mgr::OtherSlotSuffix(suffix);
+ return LoadAndVerifyVbmeta("vbmeta", suffix, other_suffix,
{} /* expected_public_key, already checked by bootloader */,
HashAlgorithm::kSHA256,
IsAvbPermissive(), /* allow_verification_error */
@@ -399,7 +412,7 @@
? AVB_SLOT_VERIFY_FLAGS_ALLOW_VERIFICATION_ERROR
: AVB_SLOT_VERIFY_FLAGS_NONE;
AvbSlotVerifyResult verify_result =
- avb_ops.AvbSlotVerify(fs_mgr_get_slot_suffix(), flags, &avb_handle->vbmeta_images_);
+ avb_ops.AvbSlotVerify(avb_handle->slot_suffix_, flags, &avb_handle->vbmeta_images_);
// Only allow the following verify results:
// - AVB_SLOT_VERIFY_RESULT_OK.
@@ -492,7 +505,7 @@
}
if (!LoadAvbHashtreeToEnableVerity(fstab_entry, wait_for_verity_dev, vbmeta_images_,
- fs_mgr_get_slot_suffix(), fs_mgr_get_other_slot_suffix())) {
+ slot_suffix_, other_slot_suffix_)) {
return AvbHashtreeResult::kFail;
}
@@ -526,8 +539,8 @@
if (vbmeta_images_.size() < 1) {
return "";
}
- std::string avb_partition_name = DeriveAvbPartitionName(fstab_entry, fs_mgr_get_slot_suffix(),
- fs_mgr_get_other_slot_suffix());
+ std::string avb_partition_name =
+ DeriveAvbPartitionName(fstab_entry, slot_suffix_, other_slot_suffix_);
auto avb_prop_name = "com.android.build." + avb_partition_name + ".security_patch";
return GetAvbPropertyDescriptor(avb_prop_name, vbmeta_images_);
}
diff --git a/fs_mgr/libfs_avb/include/fs_avb/fs_avb.h b/fs_mgr/libfs_avb/include/fs_avb/fs_avb.h
index 4702e68..924ab24 100644
--- a/fs_mgr/libfs_avb/include/fs_avb/fs_avb.h
+++ b/fs_mgr/libfs_avb/include/fs_avb/fs_avb.h
@@ -83,8 +83,8 @@
// is verified and can be trusted.
//
// TODO(bowgotsai): remove Open() and switch to LoadAndVerifyVbmeta().
- static AvbUniquePtr Open(); // loads inline vbmeta, via libavb.
- static AvbUniquePtr LoadAndVerifyVbmeta(); // loads inline vbmeta.
+ static AvbUniquePtr Open(); // loads inline vbmeta, via libavb.
+ static AvbUniquePtr LoadAndVerifyVbmeta(const std::string& slot_suffix = {});
// The caller can specify optional preload_avb_key_blobs for public key matching.
// This is mostly for init to preload AVB keys before chroot into /system.
@@ -137,12 +137,14 @@
AvbHandle& operator=(AvbHandle&&) noexcept = delete; // no move assignment
private:
- AvbHandle() : status_(AvbHandleStatus::kUninitialized) {}
+ AvbHandle();
std::vector<VBMetaData> vbmeta_images_;
VBMetaInfo vbmeta_info_; // A summary info for vbmeta_images_.
AvbHandleStatus status_;
std::string avb_version_;
+ std::string slot_suffix_;
+ std::string other_slot_suffix_;
};
} // namespace fs_mgr
diff --git a/fs_mgr/libfstab/Android.bp b/fs_mgr/libfstab/Android.bp
new file mode 100644
index 0000000..df0269c
--- /dev/null
+++ b/fs_mgr/libfstab/Android.bp
@@ -0,0 +1,62 @@
+//
+// 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.
+//
+
+package {
+ default_applicable_licenses: [
+ "Android-Apache-2.0",
+ "system_core_fs_mgr_license",
+ ],
+}
+
+cc_library_static {
+ // Do not ever make this a shared library as long as it is vendor_available.
+ // It does not have a stable interface.
+ name: "libfstab",
+ vendor_available: true,
+ ramdisk_available: true,
+ vendor_ramdisk_available: true,
+ recovery_available: true,
+ host_supported: true,
+ defaults: ["fs_mgr_defaults"],
+ export_include_dirs: ["include"],
+ header_libs: [
+ "libbase_headers",
+ "libgsi_headers",
+ ],
+ srcs: [
+ "fstab.cpp",
+ "boot_config.cpp",
+ "slotselect.cpp",
+ ],
+ target: {
+ darwin: {
+ enabled: false,
+ },
+ vendor: {
+ cflags: [
+ // Skipping entries in fstab should only be done in a system
+ // process as the config file is in /system_ext.
+ // Remove the op from the vendor variant.
+ "-DNO_SKIP_MOUNT",
+ ],
+ },
+ },
+ apex_available: [
+ "//apex_available:anyapex",
+ "//apex_available:platform",
+ ],
+ min_sdk_version: "31",
+}
diff --git a/fs_mgr/libfstab/boot_config.cpp b/fs_mgr/libfstab/boot_config.cpp
new file mode 100644
index 0000000..b21495e
--- /dev/null
+++ b/fs_mgr/libfstab/boot_config.cpp
@@ -0,0 +1,205 @@
+/*
+ * Copyright (C) 2017 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 <algorithm>
+#include <iterator>
+#include <string>
+
+#include <android-base/file.h>
+#include <android-base/properties.h>
+#include <android-base/strings.h>
+
+#include "fstab_priv.h"
+#include "logging_macros.h"
+
+namespace android {
+namespace fs_mgr {
+
+const std::string& GetAndroidDtDir() {
+ // Set once and saves time for subsequent calls to this function
+ static const std::string kAndroidDtDir = [] {
+ std::string android_dt_dir;
+ if ((GetBootconfig("androidboot.android_dt_dir", &android_dt_dir) ||
+ GetKernelCmdline("androidboot.android_dt_dir", &android_dt_dir)) &&
+ !android_dt_dir.empty()) {
+ // Ensure the returned path ends with a /
+ if (android_dt_dir.back() != '/') {
+ android_dt_dir.push_back('/');
+ }
+ } else {
+ // Fall back to the standard procfs-based path
+ android_dt_dir = "/proc/device-tree/firmware/android/";
+ }
+ LINFO << "Using Android DT directory " << android_dt_dir;
+ return android_dt_dir;
+ }();
+ return kAndroidDtDir;
+}
+
+void ImportBootconfigFromString(const std::string& bootconfig,
+ const std::function<void(std::string, std::string)>& fn) {
+ for (std::string_view line : android::base::Split(bootconfig, "\n")) {
+ const auto equal_pos = line.find('=');
+ std::string key = android::base::Trim(line.substr(0, equal_pos));
+ if (key.empty()) {
+ continue;
+ }
+ std::string value;
+ if (equal_pos != line.npos) {
+ value = android::base::Trim(line.substr(equal_pos + 1));
+ // If the value is a comma-delimited list, the kernel would insert a space between the
+ // list elements when read from /proc/bootconfig.
+ // BoardConfig.mk:
+ // BOARD_BOOTCONFIG := key=value1,value2,value3
+ // /proc/bootconfig:
+ // key = "value1", "value2", "value3"
+ if (key == "androidboot.boot_device" || key == "androidboot.boot_devices") {
+ // boot_device[s] is a special case where a list element can contain comma and the
+ // caller expects a space-delimited list, so don't remove space here.
+ value.erase(std::remove(value.begin(), value.end(), '"'), value.end());
+ } else {
+ // In order to not break the expectations of existing code, we modify the value to
+ // keep the format consistent with the kernel cmdline by removing quote and space.
+ std::string_view sv(value);
+ android::base::ConsumePrefix(&sv, "\"");
+ android::base::ConsumeSuffix(&sv, "\"");
+ value = android::base::StringReplace(sv, R"(", ")", ",", true);
+ }
+ }
+ // "key" and "key =" means empty value.
+ fn(std::move(key), std::move(value));
+ }
+}
+
+bool GetBootconfigFromString(const std::string& bootconfig, const std::string& key,
+ std::string* out) {
+ bool found = false;
+ ImportBootconfigFromString(bootconfig, [&](std::string config_key, std::string value) {
+ if (!found && config_key == key) {
+ *out = std::move(value);
+ found = true;
+ }
+ });
+ return found;
+}
+
+void ImportBootconfig(const std::function<void(std::string, std::string)>& fn) {
+ std::string bootconfig;
+ android::base::ReadFileToString("/proc/bootconfig", &bootconfig);
+ ImportBootconfigFromString(bootconfig, fn);
+}
+
+bool GetBootconfig(const std::string& key, std::string* out) {
+ std::string bootconfig;
+ android::base::ReadFileToString("/proc/bootconfig", &bootconfig);
+ return GetBootconfigFromString(bootconfig, key, out);
+}
+
+void ImportKernelCmdlineFromString(const std::string& cmdline,
+ const std::function<void(std::string, std::string)>& fn) {
+ static constexpr char quote = '"';
+
+ size_t base = 0;
+ while (true) {
+ // skip quoted spans
+ auto found = base;
+ while (((found = cmdline.find_first_of(" \"", found)) != cmdline.npos) &&
+ (cmdline[found] == quote)) {
+ // unbalanced quote is ok
+ if ((found = cmdline.find(quote, found + 1)) == cmdline.npos) break;
+ ++found;
+ }
+ std::string piece = cmdline.substr(base, found - base);
+ piece.erase(std::remove(piece.begin(), piece.end(), quote), piece.end());
+ auto equal_sign = piece.find('=');
+ if (equal_sign == piece.npos) {
+ if (!piece.empty()) {
+ // no difference between <key> and <key>=
+ fn(std::move(piece), "");
+ }
+ } else {
+ std::string value = piece.substr(equal_sign + 1);
+ piece.resize(equal_sign);
+ fn(std::move(piece), std::move(value));
+ }
+ if (found == cmdline.npos) break;
+ base = found + 1;
+ }
+}
+
+bool GetKernelCmdlineFromString(const std::string& cmdline, const std::string& key,
+ std::string* out) {
+ bool found = false;
+ ImportKernelCmdlineFromString(cmdline, [&](std::string config_key, std::string value) {
+ if (!found && config_key == key) {
+ *out = std::move(value);
+ found = true;
+ }
+ });
+ return found;
+}
+
+void ImportKernelCmdline(const std::function<void(std::string, std::string)>& fn) {
+ std::string cmdline;
+ android::base::ReadFileToString("/proc/cmdline", &cmdline);
+ ImportKernelCmdlineFromString(android::base::Trim(cmdline), fn);
+}
+
+bool GetKernelCmdline(const std::string& key, std::string* out) {
+ std::string cmdline;
+ android::base::ReadFileToString("/proc/cmdline", &cmdline);
+ return GetKernelCmdlineFromString(android::base::Trim(cmdline), key, out);
+}
+
+} // namespace fs_mgr
+} // namespace android
+
+// Tries to get the boot config value in device tree, properties, kernel bootconfig and kernel
+// cmdline (in that order).
+// Returns 'true' if successfully found, 'false' otherwise.
+bool fs_mgr_get_boot_config(const std::string& key, std::string* out_val) {
+ FSTAB_CHECK(out_val != nullptr);
+
+ // firstly, check the device tree
+ if (is_dt_compatible()) {
+ std::string file_name = android::fs_mgr::GetAndroidDtDir() + key;
+ if (android::base::ReadFileToString(file_name, out_val)) {
+ if (!out_val->empty()) {
+ out_val->pop_back(); // Trims the trailing '\0' out.
+ return true;
+ }
+ }
+ }
+
+ // next, check if we have "ro.boot" property already
+ *out_val = android::base::GetProperty("ro.boot." + key, "");
+ if (!out_val->empty()) {
+ return true;
+ }
+
+ // next, check if we have the property in bootconfig
+ const std::string config_key = "androidboot." + key;
+ if (android::fs_mgr::GetBootconfig(config_key, out_val)) {
+ return true;
+ }
+
+ // finally, fallback to kernel cmdline, properties may not be ready yet
+ if (android::fs_mgr::GetKernelCmdline(config_key, out_val)) {
+ return true;
+ }
+
+ return false;
+}
diff --git a/fs_mgr/fs_mgr_fstab.cpp b/fs_mgr/libfstab/fstab.cpp
similarity index 90%
rename from fs_mgr/fs_mgr_fstab.cpp
rename to fs_mgr/libfstab/fstab.cpp
index c3c10ba..32460b1 100644
--- a/fs_mgr/fs_mgr_fstab.cpp
+++ b/fs_mgr/libfstab/fstab.cpp
@@ -36,7 +36,8 @@
#include <android-base/strings.h>
#include <libgsi/libgsi.h>
-#include "fs_mgr_priv.h"
+#include "fstab_priv.h"
+#include "logging_macros.h"
using android::base::EndsWith;
using android::base::ParseByteCount;
@@ -50,10 +51,10 @@
namespace fs_mgr {
namespace {
-constexpr char kDefaultAndroidDtDir[] = "/proc/device-tree/firmware/android";
+constexpr char kProcMountsPath[] = "/proc/mounts";
struct FlagList {
- const char *name;
+ const char* name;
uint64_t flag;
};
@@ -79,7 +80,7 @@
off64_t CalculateZramSize(int percentage) {
off64_t total;
- total = sysconf(_SC_PHYS_PAGES);
+ total = sysconf(_SC_PHYS_PAGES);
total *= percentage;
total /= 100;
@@ -327,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;
@@ -336,25 +336,14 @@
return true;
}
-std::string InitAndroidDtDir() {
- std::string android_dt_dir;
- // The platform may specify a custom Android DT path in kernel cmdline
- if (!fs_mgr_get_boot_config_from_bootconfig_source("android_dt_dir", &android_dt_dir) &&
- !fs_mgr_get_boot_config_from_kernel_cmdline("android_dt_dir", &android_dt_dir)) {
- // Fall back to the standard procfs-based path
- android_dt_dir = kDefaultAndroidDtDir;
- }
- return android_dt_dir;
-}
-
bool IsDtFstabCompatible() {
std::string dt_value;
- std::string file_name = get_android_dt_dir() + "/fstab/compatible";
+ std::string file_name = GetAndroidDtDir() + "fstab/compatible";
if (ReadDtFile(file_name, &dt_value) && dt_value == "android,fstab") {
// If there's no status property or its set to "ok" or "okay", then we use the DT fstab.
std::string status_value;
- std::string status_file_name = get_android_dt_dir() + "/fstab/status";
+ std::string status_file_name = GetAndroidDtDir() + "fstab/status";
return !ReadDtFile(status_file_name, &status_value) || status_value == "ok" ||
status_value == "okay";
}
@@ -367,7 +356,7 @@
return {};
}
- std::string fstabdir_name = get_android_dt_dir() + "/fstab";
+ std::string fstabdir_name = GetAndroidDtDir() + "fstab";
std::unique_ptr<DIR, int (*)(DIR*)> fstabdir(opendir(fstabdir_name.c_str()), closedir);
if (!fstabdir) return {};
@@ -400,7 +389,7 @@
std::string mount_point;
file_name =
- android::base::StringPrintf("%s/%s/mnt_point", fstabdir_name.c_str(), dp->d_name);
+ android::base::StringPrintf("%s/%s/mnt_point", fstabdir_name.c_str(), dp->d_name);
if (ReadDtFile(file_name, &value)) {
LINFO << "dt_fstab: Using a specified mount point " << value << " for " << dp->d_name;
mount_point = value;
@@ -416,14 +405,16 @@
}
fstab_entry.push_back(value);
- file_name = android::base::StringPrintf("%s/%s/mnt_flags", fstabdir_name.c_str(), dp->d_name);
+ file_name =
+ android::base::StringPrintf("%s/%s/mnt_flags", fstabdir_name.c_str(), dp->d_name);
if (!ReadDtFile(file_name, &value)) {
LERROR << "dt_fstab: Failed to find type for partition " << dp->d_name;
return {};
}
fstab_entry.push_back(value);
- file_name = android::base::StringPrintf("%s/%s/fsmgr_flags", fstabdir_name.c_str(), dp->d_name);
+ file_name =
+ android::base::StringPrintf("%s/%s/fsmgr_flags", fstabdir_name.c_str(), dp->d_name);
if (!ReadDtFile(file_name, &value)) {
LERROR << "dt_fstab: Failed to find type for partition " << dp->d_name;
return {};
@@ -519,6 +510,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;
@@ -699,9 +693,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 << "'";
@@ -709,11 +701,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)) {
@@ -725,20 +728,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();
@@ -822,19 +828,11 @@
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));
- }
+ fstab->insert(fstab->end(), std::make_move_iterator(default_fstab.begin()),
+ std::make_move_iterator(default_fstab.end()));
} else {
LINFO << __FUNCTION__ << "(): failed to find device default fstab";
}
@@ -864,40 +862,33 @@
}
std::set<std::string> GetBootDevices() {
+ std::set<std::string> boot_devices;
// First check bootconfig, then kernel commandline, then the device tree
- std::string dt_file_name = get_android_dt_dir() + "/boot_devices";
std::string value;
- if (fs_mgr_get_boot_config_from_bootconfig_source("boot_devices", &value) ||
- fs_mgr_get_boot_config_from_bootconfig_source("boot_device", &value)) {
- std::set<std::string> boot_devices;
- // remove quotes and split by spaces
- auto boot_device_strings = base::Split(base::StringReplace(value, "\"", "", true), " ");
- for (std::string_view device : boot_device_strings) {
- // trim the trailing comma, keep the rest.
+ if (GetBootconfig("androidboot.boot_devices", &value) ||
+ GetBootconfig("androidboot.boot_device", &value)) {
+ // split by spaces and trim the trailing comma.
+ for (std::string_view device : android::base::Split(value, " ")) {
base::ConsumeSuffix(&device, ",");
boot_devices.emplace(device);
}
return boot_devices;
}
- if (fs_mgr_get_boot_config_from_kernel_cmdline("boot_devices", &value) ||
- ReadDtFile(dt_file_name, &value)) {
- auto boot_devices = Split(value, ",");
- return std::set<std::string>(boot_devices.begin(), boot_devices.end());
+ const std::string dt_file_name = GetAndroidDtDir() + "boot_devices";
+ if (GetKernelCmdline("androidboot.boot_devices", &value) || ReadDtFile(dt_file_name, &value)) {
+ auto boot_devices_list = Split(value, ",");
+ return {std::make_move_iterator(boot_devices_list.begin()),
+ std::make_move_iterator(boot_devices_list.end())};
}
- std::string cmdline;
- if (android::base::ReadFileToString("/proc/cmdline", &cmdline)) {
- std::set<std::string> boot_devices;
- const std::string cmdline_key = "androidboot.boot_device";
- for (const auto& [key, value] : fs_mgr_parse_cmdline(cmdline)) {
- if (key == cmdline_key) {
- boot_devices.emplace(value);
- }
+ ImportKernelCmdline([&](std::string key, std::string value) {
+ if (key == "androidboot.boot_device") {
+ boot_devices.emplace(std::move(value));
}
- if (!boot_devices.empty()) {
- return boot_devices;
- }
+ });
+ if (!boot_devices.empty()) {
+ return boot_devices;
}
// Fallback to extract boot devices from fstab.
@@ -923,18 +914,22 @@
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
-// FIXME: The same logic is duplicated in system/core/init/
-const std::string& get_android_dt_dir() {
- // Set once and saves time for subsequent calls to this function
- static const std::string kAndroidDtDir = android::fs_mgr::InitAndroidDtDir();
- return kAndroidDtDir;
-}
-
bool is_dt_compatible() {
- std::string file_name = get_android_dt_dir() + "/compatible";
+ std::string file_name = android::fs_mgr::GetAndroidDtDir() + "compatible";
std::string dt_value;
if (android::fs_mgr::ReadDtFile(file_name, &dt_value)) {
if (dt_value == "android,firmware") {
diff --git a/fs_mgr/libfstab/fstab_priv.h b/fs_mgr/libfstab/fstab_priv.h
new file mode 100644
index 0000000..5105da0
--- /dev/null
+++ b/fs_mgr/libfstab/fstab_priv.h
@@ -0,0 +1,52 @@
+/*
+ * 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 <functional>
+#include <string>
+
+#include <fstab/fstab.h>
+
+// Do not include logging_macros.h here as this header is used by fs_mgr, too.
+
+bool fs_mgr_get_boot_config(const std::string& key, std::string* out_val);
+
+bool fs_mgr_update_for_slotselect(android::fs_mgr::Fstab* fstab);
+bool is_dt_compatible();
+
+namespace android {
+namespace fs_mgr {
+
+bool InRecovery();
+bool ParseFstabFromString(const std::string& fstab_str, bool proc_mounts, Fstab* fstab_out);
+bool SkipMountWithConfig(const std::string& skip_config, Fstab* fstab, bool verbose);
+std::string GetFstabPath();
+
+void ImportBootconfigFromString(const std::string& bootconfig,
+ const std::function<void(std::string, std::string)>& fn);
+
+bool GetBootconfigFromString(const std::string& bootconfig, const std::string& key,
+ std::string* out);
+
+void ImportKernelCmdlineFromString(const std::string& cmdline,
+ const std::function<void(std::string, std::string)>& fn);
+
+bool GetKernelCmdlineFromString(const std::string& cmdline, const std::string& key,
+ std::string* out);
+
+} // namespace fs_mgr
+} // namespace android
diff --git a/fs_mgr/fuzz/Android.bp b/fs_mgr/libfstab/fuzz/Android.bp
similarity index 100%
rename from fs_mgr/fuzz/Android.bp
rename to fs_mgr/libfstab/fuzz/Android.bp
diff --git a/fs_mgr/fuzz/fs_mgr_fstab_fuzzer.cpp b/fs_mgr/libfstab/fuzz/fs_mgr_fstab_fuzzer.cpp
similarity index 97%
rename from fs_mgr/fuzz/fs_mgr_fstab_fuzzer.cpp
rename to fs_mgr/libfstab/fuzz/fs_mgr_fstab_fuzzer.cpp
index b5fdad4..b09b273 100644
--- a/fs_mgr/fuzz/fs_mgr_fstab_fuzzer.cpp
+++ b/fs_mgr/libfstab/fuzz/fs_mgr_fstab_fuzzer.cpp
@@ -20,6 +20,8 @@
#include <fstab/fstab.h>
#include <fuzzer/FuzzedDataProvider.h>
+#include "../fstab_priv.h"
+
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
FuzzedDataProvider fdp(data, size);
diff --git a/fs_mgr/fuzz/fstab.dict b/fs_mgr/libfstab/fuzz/fstab.dict
similarity index 100%
rename from fs_mgr/fuzz/fstab.dict
rename to fs_mgr/libfstab/fuzz/fstab.dict
diff --git a/fs_mgr/include_fstab/fstab/fstab.h b/fs_mgr/libfstab/include/fstab/fstab.h
similarity index 78%
rename from fs_mgr/include_fstab/fstab/fstab.h
rename to fs_mgr/libfstab/include/fstab/fstab.h
index a914b53..09471f0 100644
--- a/fs_mgr/include_fstab/fstab/fstab.h
+++ b/fs_mgr/libfstab/include/fstab/fstab.h
@@ -19,6 +19,7 @@
#include <stdint.h>
#include <sys/types.h>
+#include <functional>
#include <set>
#include <string>
#include <vector>
@@ -93,14 +94,8 @@
// Unless explicitly requested, a lookup on mount point should always return the 1st one.
using Fstab = std::vector<FstabEntry>;
-// Exported for testability. Regular users should use ReadFstabFromFile().
-bool ParseFstabFromString(const std::string& fstab_str, bool proc_mounts, Fstab* fstab_out);
-// Exported for testability. Regular users should use ReadDefaultFstab().
-std::string GetFstabPath();
-// Exported for testability.
-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 +125,28 @@
// expected name.
std::string GetVerityDeviceName(const FstabEntry& entry);
+// Returns the Android Device Tree directory as specified in the kernel bootconfig or cmdline.
+// If the platform does not configure a custom DT path, returns the standard one (based in procfs).
+const std::string& GetAndroidDtDir();
+
+// Import the kernel bootconfig by calling the callback |fn| with each key-value pair.
+void ImportBootconfig(const std::function<void(std::string, std::string)>& fn);
+
+// Get the kernel bootconfig value for |key|.
+// Returns true if |key| is found in bootconfig.
+// Otherwise returns false and |*out| is not modified.
+bool GetBootconfig(const std::string& key, std::string* out);
+
+// Import the kernel cmdline by calling the callback |fn| with each key-value pair.
+void ImportKernelCmdline(const std::function<void(std::string, std::string)>& fn);
+
+// Get the kernel cmdline value for |key|.
+// Returns true if |key| is found in the kernel cmdline.
+// Otherwise returns false and |*out| is not modified.
+bool GetKernelCmdline(const std::string& key, std::string* out);
+
+// Return the "other" slot for the given slot suffix.
+std::string OtherSlotSuffix(const std::string& suffix);
+
} // namespace fs_mgr
} // namespace android
diff --git a/fs_mgr/libfstab/logging_macros.h b/fs_mgr/libfstab/logging_macros.h
new file mode 100644
index 0000000..7ea1b77
--- /dev/null
+++ b/fs_mgr/libfstab/logging_macros.h
@@ -0,0 +1,40 @@
+/*
+ * 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 <android-base/logging.h>
+
+#define FSTAB_TAG "[libfstab] "
+
+/* The CHECK() in logging.h will use program invocation name as the tag.
+ * Thus, the log will have prefix "init: " when libfs_mgr is statically
+ * linked in the init process. This might be opaque when debugging.
+ * Append a library name tag at the end of the abort message to aid debugging.
+ */
+#define FSTAB_CHECK(x) CHECK(x) << "in " << FSTAB_TAG
+
+// Logs a message to kernel
+#define LINFO LOG(INFO) << FSTAB_TAG
+#define LWARNING LOG(WARNING) << FSTAB_TAG
+#define LERROR LOG(ERROR) << FSTAB_TAG
+#define LFATAL LOG(FATAL) << FSTAB_TAG
+
+// Logs a message with strerror(errno) at the end
+#define PINFO PLOG(INFO) << FSTAB_TAG
+#define PWARNING PLOG(WARNING) << FSTAB_TAG
+#define PERROR PLOG(ERROR) << FSTAB_TAG
+#define PFATAL PLOG(FATAL) << FSTAB_TAG
diff --git a/fs_mgr/fs_mgr_slotselect.cpp b/fs_mgr/libfstab/slotselect.cpp
similarity index 91%
rename from fs_mgr/fs_mgr_slotselect.cpp
rename to fs_mgr/libfstab/slotselect.cpp
index 09c1b7e..db3f8da 100644
--- a/fs_mgr/fs_mgr_slotselect.cpp
+++ b/fs_mgr/libfstab/slotselect.cpp
@@ -18,8 +18,8 @@
#include <string>
-#include "fs_mgr.h"
-#include "fs_mgr_priv.h"
+#include "fstab_priv.h"
+#include "logging_macros.h"
// Realistically, this file should be part of the android::fs_mgr namespace;
using namespace android::fs_mgr;
@@ -74,3 +74,13 @@
}
return true;
}
+
+namespace android {
+namespace fs_mgr {
+
+std::string OtherSlotSuffix(const std::string& suffix) {
+ return other_suffix(suffix);
+}
+
+} // namespace fs_mgr
+} // namespace android
diff --git a/fs_mgr/liblp/super_layout_builder.cpp b/fs_mgr/liblp/super_layout_builder.cpp
index 37f28e1..5349e41 100644
--- a/fs_mgr/liblp/super_layout_builder.cpp
+++ b/fs_mgr/liblp/super_layout_builder.cpp
@@ -46,21 +46,21 @@
bool SuperLayoutBuilder::Open(const LpMetadata& metadata) {
for (const auto& partition : metadata.partitions) {
if (partition.attributes & LP_PARTITION_ATTR_SLOT_SUFFIXED) {
- // Retrofit devices are not supported.
+ LOG(ERROR) << "Retrofit devices are not supported";
return false;
}
if (!(partition.attributes & LP_PARTITION_ATTR_READONLY)) {
- // Writable partitions are not supported.
+ LOG(ERROR) << "Writable partitions are not supported";
return false;
}
}
if (!metadata.extents.empty()) {
- // Partitions that already have extents are not supported (should
- // never be true of super_empty.img).
+ LOG(ERROR) << "Partitions that already have extents are not supported";
+ // should never be true of super_empty.img.
return false;
}
if (metadata.block_devices.size() != 1) {
- // Only one "super" is supported.
+ LOG(ERROR) << "Only one 'super' is supported";
return false;
}
diff --git a/fs_mgr/libsnapshot/Android.bp b/fs_mgr/libsnapshot/Android.bp
index 046d30c..04b5a02 100644
--- a/fs_mgr/libsnapshot/Android.bp
+++ b/fs_mgr/libsnapshot/Android.bp
@@ -44,7 +44,7 @@
"libext2_uuid",
"libext4_utils",
"libfstab",
- "libsnapshot_snapuserd",
+ "libsnapuserd_client",
"libz",
],
header_libs: [
@@ -101,7 +101,7 @@
}
cc_library_static {
- name: "libsnapshot",
+ name: "libsnapshot_static",
defaults: [
"libsnapshot_defaults",
"libsnapshot_hal_deps",
@@ -112,6 +112,25 @@
],
}
+cc_library {
+ name: "libsnapshot",
+ defaults: [
+ "libsnapshot_defaults",
+ "libsnapshot_cow_defaults",
+ "libsnapshot_hal_deps",
+ ],
+ srcs: [":libsnapshot_sources"],
+ shared_libs: [
+ "libfs_mgr_binder",
+ "liblp",
+ "libprotobuf-cpp-lite",
+ ],
+ static_libs: [
+ "libc++fs",
+ "libsnapshot_cow",
+ ]
+}
+
cc_library_static {
name: "libsnapshot_init",
native_coverage : true,
@@ -166,7 +185,6 @@
header_libs: [
"libupdate_engine_headers",
],
- export_include_dirs: ["include"],
}
cc_library_static {
@@ -184,6 +202,7 @@
"libsnapshot_cow/writer_base.cpp",
"libsnapshot_cow/writer_v2.cpp",
],
+ export_include_dirs: ["include"],
host_supported: true,
recovery_available: true,
ramdisk_available: true,
@@ -247,7 +266,7 @@
"libgsi",
"libgmock",
"liblp",
- "libsnapshot",
+ "libsnapshot_static",
"libsnapshot_cow",
"libsnapshot_test_helpers",
"libsparse",
@@ -330,8 +349,6 @@
"libbrotli",
"libc++fs",
"libfstab",
- "libsnapshot",
- "libsnapshot_cow",
"libz",
"update_metadata-protos",
],
@@ -344,6 +361,7 @@
"liblog",
"liblp",
"libprotobuf-cpp-lite",
+ "libsnapshot",
"libstatslog",
"libutils",
],
@@ -417,6 +435,7 @@
"libbrotli",
"libcrypto_static",
"liblog",
+ "libgflags",
"libsnapshot_cow",
"libz",
],
diff --git a/fs_mgr/libsnapshot/OWNERS b/fs_mgr/libsnapshot/OWNERS
index 9d2b877..c8b1003 100644
--- a/fs_mgr/libsnapshot/OWNERS
+++ b/fs_mgr/libsnapshot/OWNERS
@@ -1,5 +1,6 @@
-# Bug component: 30545
+# Bug component: 1014951
balsini@google.com
dvander@google.com
elsk@google.com
akailash@google.com
+zhangkelvin@google.com
diff --git a/fs_mgr/libsnapshot/include/libsnapshot/cow_compress.h b/fs_mgr/libsnapshot/include/libsnapshot/cow_compress.h
new file mode 100644
index 0000000..cf65615
--- /dev/null
+++ b/fs_mgr/libsnapshot/include/libsnapshot/cow_compress.h
@@ -0,0 +1,47 @@
+//
+// 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 "libsnapshot/cow_format.h"
+
+namespace android {
+namespace snapshot {
+
+class ICompressor {
+ public:
+ explicit ICompressor(uint32_t compression_level) : compression_level_(compression_level) {}
+
+ virtual ~ICompressor() {}
+ // Factory methods for compression methods.
+ static std::unique_ptr<ICompressor> Gz(uint32_t compression_level);
+ static std::unique_ptr<ICompressor> Brotli(uint32_t compression_level);
+ static std::unique_ptr<ICompressor> Lz4(uint32_t compression_level);
+ static std::unique_ptr<ICompressor> Zstd(uint32_t compression_level, const int32_t BLOCK_SZ);
+
+ static std::unique_ptr<ICompressor> Create(CowCompression compression, const int32_t BLOCK_SZ);
+
+ uint32_t GetCompressionLevel() const { return compression_level_; }
+
+ [[nodiscard]] virtual std::basic_string<uint8_t> Compress(const void* data,
+ size_t length) const = 0;
+
+ private:
+ uint32_t compression_level_;
+};
+} // namespace snapshot
+} // namespace android
\ No newline at end of file
diff --git a/fs_mgr/libsnapshot/include/libsnapshot/cow_format.h b/fs_mgr/libsnapshot/include/libsnapshot/cow_format.h
index dd626bc..c9a4dee 100644
--- a/fs_mgr/libsnapshot/include/libsnapshot/cow_format.h
+++ b/fs_mgr/libsnapshot/include/libsnapshot/cow_format.h
@@ -161,11 +161,22 @@
kCowCompressLz4 = 3,
kCowCompressZstd = 4,
};
+struct CowCompression {
+ CowCompressionAlgorithm algorithm = kCowCompressNone;
+ uint32_t compression_level = 0;
+};
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;
uint8_t unused[64];
diff --git a/fs_mgr/libsnapshot/include/libsnapshot/cow_reader.h b/fs_mgr/libsnapshot/include/libsnapshot/cow_reader.h
index 3890b17..f4ce52f 100644
--- a/fs_mgr/libsnapshot/include/libsnapshot/cow_reader.h
+++ b/fs_mgr/libsnapshot/include/libsnapshot/cow_reader.h
@@ -73,8 +73,20 @@
// 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;
};
+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:
@@ -119,6 +131,7 @@
bool VerifyMergeOps() 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
@@ -133,6 +146,7 @@
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
@@ -154,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 d6194eb..3016e93 100644
--- a/fs_mgr/libsnapshot/include/libsnapshot/cow_writer.h
+++ b/fs_mgr/libsnapshot/include/libsnapshot/cow_writer.h
@@ -14,18 +14,17 @@
#pragma once
+#include <libsnapshot/cow_compress.h>
+
#include <stdint.h>
#include <condition_variable>
#include <cstdint>
-#include <future>
#include <memory>
#include <mutex>
#include <optional>
#include <queue>
#include <string>
-#include <thread>
-#include <utility>
#include <vector>
#include <android-base/unique_fd.h>
@@ -110,16 +109,15 @@
class CompressWorker {
public:
- CompressWorker(CowCompressionAlgorithm compression, uint32_t block_size);
+ CompressWorker(std::unique_ptr<ICompressor>&& compressor, uint32_t block_size);
bool RunThread();
void EnqueueCompressBlocks(const void* buffer, size_t num_blocks);
bool GetCompressedBuffers(std::vector<std::basic_string<uint8_t>>* compressed_buf);
void Finalize();
- static std::basic_string<uint8_t> Compress(CowCompressionAlgorithm compression,
- const void* data, size_t length);
+ static uint32_t GetDefaultCompressionLevel(CowCompressionAlgorithm compression);
- static bool CompressBlocks(CowCompressionAlgorithm compression, size_t block_size,
- const void* buffer, size_t num_blocks,
+ static bool CompressBlocks(ICompressor* compressor, size_t block_size, const void* buffer,
+ size_t num_blocks,
std::vector<std::basic_string<uint8_t>>* compressed_data);
private:
@@ -130,7 +128,7 @@
std::vector<std::basic_string<uint8_t>> compressed_data;
};
- CowCompressionAlgorithm compression_;
+ std::unique_ptr<ICompressor> compressor_;
uint32_t block_size_;
std::queue<CompressWork> work_queue_;
@@ -139,7 +137,6 @@
std::condition_variable cv_;
bool stopped_ = false;
- std::basic_string<uint8_t> Compress(const void* data, size_t length);
bool CompressBlocks(const void* buffer, size_t num_blocks,
std::vector<std::basic_string<uint8_t>>* compressed_data);
};
diff --git a/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h b/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h
index df532ee..c056a19 100644
--- a/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h
+++ b/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h
@@ -624,7 +624,7 @@
bool CollapseSnapshotDevice(LockedFile* lock, const std::string& name,
const SnapshotStatus& status);
- struct MergeResult {
+ struct [[nodiscard]] MergeResult {
explicit MergeResult(UpdateState state,
MergeFailureCode failure_code = MergeFailureCode::Ok)
: state(state), failure_code(failure_code) {}
diff --git a/fs_mgr/libsnapshot/libsnapshot_cow/cow_compress.cpp b/fs_mgr/libsnapshot/libsnapshot_cow/cow_compress.cpp
index a4a0ad6..71ac59f 100644
--- a/fs_mgr/libsnapshot/libsnapshot_cow/cow_compress.cpp
+++ b/fs_mgr/libsnapshot/libsnapshot_cow/cow_compress.cpp
@@ -18,12 +18,16 @@
#include <unistd.h>
#include <limits>
+#include <memory>
#include <queue>
#include <android-base/file.h>
#include <android-base/logging.h>
+#include <android-base/parseint.h>
+#include <android-base/strings.h>
#include <android-base/unique_fd.h>
#include <brotli/encode.h>
+#include <libsnapshot/cow_compress.h>
#include <libsnapshot/cow_format.h>
#include <libsnapshot/cow_reader.h>
#include <libsnapshot/cow_writer.h>
@@ -46,109 +50,171 @@
} else if (name == "none" || name.empty()) {
return {kCowCompressNone};
} else {
+ LOG(ERROR) << "unable to determine default compression algorithm for: " << name;
return {};
}
}
-std::basic_string<uint8_t> CompressWorker::Compress(const void* data, size_t length) {
- return Compress(compression_, data, length);
+std::unique_ptr<ICompressor> ICompressor::Create(CowCompression compression,
+ const int32_t BLOCK_SZ) {
+ switch (compression.algorithm) {
+ case kCowCompressLz4:
+ return ICompressor::Lz4(compression.compression_level);
+ case kCowCompressBrotli:
+ return ICompressor::Brotli(compression.compression_level);
+ case kCowCompressGz:
+ return ICompressor::Gz(compression.compression_level);
+ case kCowCompressZstd:
+ return ICompressor::Zstd(compression.compression_level, BLOCK_SZ);
+ case kCowCompressNone:
+ return nullptr;
+ }
+ return nullptr;
}
-std::basic_string<uint8_t> CompressWorker::Compress(CowCompressionAlgorithm compression,
- const void* data, size_t length) {
+// 1. Default compression level is determined by compression algorithm
+// 2. There might be compatibility issues if a value is changed here, as some older versions of
+// Android will assume a different compression level, causing cow_size estimation differences that
+// will lead to OTA failure. Ensure that the device and OTA package use the same compression level
+// for OTA to succeed.
+uint32_t CompressWorker::GetDefaultCompressionLevel(CowCompressionAlgorithm compression) {
switch (compression) {
case kCowCompressGz: {
- const auto bound = compressBound(length);
- std::basic_string<uint8_t> buffer(bound, '\0');
-
- uLongf dest_len = bound;
- auto rv = compress2(buffer.data(), &dest_len, reinterpret_cast<const Bytef*>(data),
- length, Z_BEST_COMPRESSION);
- if (rv != Z_OK) {
- LOG(ERROR) << "compress2 returned: " << rv;
- return {};
- }
- buffer.resize(dest_len);
- return buffer;
+ return Z_BEST_COMPRESSION;
}
case kCowCompressBrotli: {
- const auto bound = BrotliEncoderMaxCompressedSize(length);
- if (!bound) {
- LOG(ERROR) << "BrotliEncoderMaxCompressedSize returned 0";
- return {};
- }
- std::basic_string<uint8_t> buffer(bound, '\0');
-
- size_t encoded_size = bound;
- auto rv = BrotliEncoderCompress(
- BROTLI_DEFAULT_QUALITY, BROTLI_DEFAULT_WINDOW, BROTLI_DEFAULT_MODE, length,
- reinterpret_cast<const uint8_t*>(data), &encoded_size, buffer.data());
- if (!rv) {
- LOG(ERROR) << "BrotliEncoderCompress failed";
- return {};
- }
- buffer.resize(encoded_size);
- return buffer;
+ return BROTLI_DEFAULT_QUALITY;
}
case kCowCompressLz4: {
- const auto bound = LZ4_compressBound(length);
- if (!bound) {
- LOG(ERROR) << "LZ4_compressBound returned 0";
- return {};
- }
- std::basic_string<uint8_t> buffer(bound, '\0');
-
- const auto compressed_size = LZ4_compress_default(
- static_cast<const char*>(data), reinterpret_cast<char*>(buffer.data()), length,
- buffer.size());
- if (compressed_size <= 0) {
- LOG(ERROR) << "LZ4_compress_default failed, input size: " << length
- << ", compression bound: " << bound << ", ret: " << 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;
+ break;
}
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;
+ return ZSTD_defaultCLevel();
}
- default:
- LOG(ERROR) << "unhandled compression type: " << compression;
+ case kCowCompressNone: {
break;
+ }
}
- return {};
-}
-bool CompressWorker::CompressBlocks(const void* buffer, size_t num_blocks,
- std::vector<std::basic_string<uint8_t>>* compressed_data) {
- return CompressBlocks(compression_, block_size_, buffer, num_blocks, compressed_data);
+ return 0;
}
-bool CompressWorker::CompressBlocks(CowCompressionAlgorithm compression, size_t block_size,
- const void* buffer, size_t num_blocks,
+class GzCompressor final : public ICompressor {
+ public:
+ GzCompressor(uint32_t compression_level) : ICompressor(compression_level){};
+
+ std::basic_string<uint8_t> Compress(const void* data, size_t length) const override {
+ const auto bound = compressBound(length);
+ std::basic_string<uint8_t> buffer(bound, '\0');
+
+ uLongf dest_len = bound;
+ auto rv = compress2(buffer.data(), &dest_len, reinterpret_cast<const Bytef*>(data), length,
+ GetCompressionLevel());
+ if (rv != Z_OK) {
+ LOG(ERROR) << "compress2 returned: " << rv;
+ return {};
+ }
+ buffer.resize(dest_len);
+ return buffer;
+ };
+};
+
+class Lz4Compressor final : public ICompressor {
+ public:
+ Lz4Compressor(uint32_t compression_level) : ICompressor(compression_level){};
+
+ std::basic_string<uint8_t> Compress(const void* data, size_t length) const override {
+ const auto bound = LZ4_compressBound(length);
+ if (!bound) {
+ LOG(ERROR) << "LZ4_compressBound returned 0";
+ return {};
+ }
+ std::basic_string<uint8_t> buffer(bound, '\0');
+
+ const auto compressed_size =
+ LZ4_compress_default(static_cast<const char*>(data),
+ reinterpret_cast<char*>(buffer.data()), length, buffer.size());
+ if (compressed_size <= 0) {
+ LOG(ERROR) << "LZ4_compress_default failed, input size: " << length
+ << ", compression bound: " << bound << ", ret: " << 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;
+ };
+};
+
+class BrotliCompressor final : public ICompressor {
+ public:
+ BrotliCompressor(uint32_t compression_level) : ICompressor(compression_level){};
+
+ std::basic_string<uint8_t> Compress(const void* data, size_t length) const override {
+ const auto bound = BrotliEncoderMaxCompressedSize(length);
+ if (!bound) {
+ LOG(ERROR) << "BrotliEncoderMaxCompressedSize returned 0";
+ return {};
+ }
+ std::basic_string<uint8_t> buffer(bound, '\0');
+
+ size_t encoded_size = bound;
+ auto rv = BrotliEncoderCompress(
+ GetCompressionLevel(), BROTLI_DEFAULT_WINDOW, BROTLI_DEFAULT_MODE, length,
+ reinterpret_cast<const uint8_t*>(data), &encoded_size, buffer.data());
+ if (!rv) {
+ LOG(ERROR) << "BrotliEncoderCompress failed";
+ return {};
+ }
+ buffer.resize(encoded_size);
+ return buffer;
+ };
+};
+
+class ZstdCompressor final : public ICompressor {
+ public:
+ ZstdCompressor(uint32_t compression_level, const uint32_t MAX_BLOCK_SIZE)
+ : ICompressor(compression_level), zstd_context_(ZSTD_createCCtx(), ZSTD_freeCCtx) {
+ ZSTD_CCtx_setParameter(zstd_context_.get(), ZSTD_c_compressionLevel, compression_level);
+ ZSTD_CCtx_setParameter(zstd_context_.get(), ZSTD_c_windowLog, log2(MAX_BLOCK_SIZE));
+ };
+
+ std::basic_string<uint8_t> Compress(const void* data, size_t length) const override {
+ std::basic_string<uint8_t> buffer(ZSTD_compressBound(length), '\0');
+ const auto compressed_size =
+ ZSTD_compress2(zstd_context_.get(), buffer.data(), buffer.size(), data, length);
+ 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;
+ };
+
+ private:
+ std::unique_ptr<ZSTD_CCtx, decltype(&ZSTD_freeCCtx)> zstd_context_;
+};
+
+bool CompressWorker::CompressBlocks(const void* buffer, size_t num_blocks,
+ std::vector<std::basic_string<uint8_t>>* compressed_data) {
+ return CompressBlocks(compressor_.get(), block_size_, buffer, num_blocks, compressed_data);
+}
+
+bool CompressWorker::CompressBlocks(ICompressor* compressor, size_t block_size, const void* buffer,
+ size_t num_blocks,
std::vector<std::basic_string<uint8_t>>* compressed_data) {
const uint8_t* iter = reinterpret_cast<const uint8_t*>(buffer);
while (num_blocks) {
- auto data = Compress(compression, iter, block_size);
+ auto data = compressor->Compress(iter, block_size);
if (data.empty()) {
PLOG(ERROR) << "CompressBlocks: Compression failed";
return false;
@@ -247,6 +313,22 @@
return true;
}
+std::unique_ptr<ICompressor> ICompressor::Brotli(uint32_t compression_level) {
+ return std::make_unique<BrotliCompressor>(compression_level);
+}
+
+std::unique_ptr<ICompressor> ICompressor::Gz(uint32_t compression_level) {
+ return std::make_unique<GzCompressor>(compression_level);
+}
+
+std::unique_ptr<ICompressor> ICompressor::Lz4(uint32_t compression_level) {
+ return std::make_unique<Lz4Compressor>(compression_level);
+}
+
+std::unique_ptr<ICompressor> ICompressor::Zstd(uint32_t compression_level, const int32_t BLOCK_SZ) {
+ return std::make_unique<ZstdCompressor>(compression_level, BLOCK_SZ);
+}
+
void CompressWorker::Finalize() {
{
std::unique_lock<std::mutex> lock(lock_);
@@ -255,8 +337,8 @@
cv_.notify_all();
}
-CompressWorker::CompressWorker(CowCompressionAlgorithm compression, uint32_t block_size)
- : compression_(compression), block_size_(block_size) {}
+CompressWorker::CompressWorker(std::unique_ptr<ICompressor>&& compressor, uint32_t block_size)
+ : compressor_(std::move(compressor)), block_size_(block_size) {}
} // namespace snapshot
} // namespace android
diff --git a/fs_mgr/libsnapshot/libsnapshot_cow/cow_decompress.cpp b/fs_mgr/libsnapshot/libsnapshot_cow/cow_decompress.cpp
index da90cc0..2aaf388 100644
--- a/fs_mgr/libsnapshot/libsnapshot_cow/cow_decompress.cpp
+++ b/fs_mgr/libsnapshot/libsnapshot_cow/cow_decompress.cpp
@@ -18,6 +18,7 @@
#include <array>
#include <cstring>
+#include <memory>
#include <utility>
#include <vector>
@@ -62,6 +63,8 @@
return IDecompressor::Brotli();
} else if (compressor == "gz") {
return IDecompressor::Gz();
+ } else if (compressor == "zstd") {
+ return IDecompressor::Zstd();
} else {
return nullptr;
}
@@ -211,10 +214,6 @@
return true;
}
-std::unique_ptr<IDecompressor> IDecompressor::Gz() {
- return std::unique_ptr<IDecompressor>(new GzDecompressor());
-}
-
class BrotliDecompressor final : public StreamDecompressor {
public:
~BrotliDecompressor();
@@ -275,10 +274,6 @@
return true;
}
-std::unique_ptr<IDecompressor> IDecompressor::Brotli() {
- return std::unique_ptr<IDecompressor>(new BrotliDecompressor());
-}
-
class Lz4Decompressor final : public IDecompressor {
public:
~Lz4Decompressor() override = default;
@@ -356,7 +351,7 @@
return decompressed_size;
}
std::vector<unsigned char> ignore_buf(decompressed_size);
- if (!Decompress(buffer, decompressed_size)) {
+ if (!Decompress(ignore_buf.data(), decompressed_size)) {
return -1;
}
memcpy(buffer, ignore_buf.data() + ignore_bytes, buffer_size);
@@ -382,6 +377,14 @@
}
};
+std::unique_ptr<IDecompressor> IDecompressor::Brotli() {
+ return std::make_unique<BrotliDecompressor>();
+}
+
+std::unique_ptr<IDecompressor> IDecompressor::Gz() {
+ return std::make_unique<GzDecompressor>();
+}
+
std::unique_ptr<IDecompressor> IDecompressor::Lz4() {
return std::make_unique<Lz4Decompressor>();
}
diff --git a/fs_mgr/libsnapshot/libsnapshot_cow/cow_reader.cpp b/fs_mgr/libsnapshot/libsnapshot_cow/cow_reader.cpp
index c2a7fdb..1b5d724 100644
--- a/fs_mgr/libsnapshot/libsnapshot_cow/cow_reader.cpp
+++ b/fs_mgr/libsnapshot/libsnapshot_cow/cow_reader.cpp
@@ -17,9 +17,7 @@
#include <sys/types.h>
#include <unistd.h>
-#include <limits>
#include <optional>
-#include <set>
#include <unordered_map>
#include <unordered_set>
#include <vector>
@@ -103,7 +101,7 @@
footer_ = parser.footer();
fd_size_ = parser.fd_size();
last_label_ = parser.last_label();
- ops_ = std::move(parser.ops());
+ ops_ = parser.ops();
data_loc_ = parser.data_loc();
// If we're resuming a write, we're not ready to merge
@@ -230,7 +228,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;
@@ -310,21 +308,42 @@
bool CowReader::VerifyMergeOps() {
auto itr = GetMergeOpIter(true);
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 block;
- bool offset;
- if (op->type == kCowCopyOp) {
- block = op->source;
- offset = false;
- } else if (op->type == kCowXorOp) {
- block = op->source / header_.block_size;
- offset = (op->source % header_.block_size) != 0;
- } else {
+ 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;
}
+ 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];
@@ -332,7 +351,7 @@
<< op << "\noverwritten by previously merged op:\n"
<< *overwrite;
}
- if (offset && overwritten_blocks.count(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"
@@ -516,6 +535,18 @@
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_.prefix.header_size || offset >= fd_size_ - sizeof(CowFooter) ||
@@ -566,10 +597,14 @@
size_t remaining_;
};
+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:
break;
case kCowCompressGz:
@@ -589,7 +624,7 @@
}
break;
default:
- LOG(ERROR) << "Unknown compression type: " << op->compression;
+ LOG(ERROR) << "Unknown compression type: " << GetCompressionType(op);
return -1;
}
@@ -597,7 +632,7 @@
if (op->type == kCowXorOp) {
offset = data_loc_->at(op->new_block);
} else {
- offset = op->source;
+ offset = GetCowOpSourceInfoData(op);
}
if (!decompressor) {
@@ -610,5 +645,18 @@
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
} // namespace android
diff --git a/fs_mgr/libsnapshot/libsnapshot_cow/inspect_cow.cpp b/fs_mgr/libsnapshot/libsnapshot_cow/inspect_cow.cpp
index c2c86ee..a6dee4f 100644
--- a/fs_mgr/libsnapshot/libsnapshot_cow/inspect_cow.cpp
+++ b/fs_mgr/libsnapshot/libsnapshot_cow/inspect_cow.cpp
@@ -24,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) {
@@ -38,37 +53,11 @@
}
}
-static void usage(void) {
- std::cerr << "Usage: inspect_cow [-sd] <COW_FILE>\n";
- std::cerr << "\t -s Run Silent\n";
- std::cerr << "\t -d Attempt to decompress\n";
- std::cerr << "\t -b Show data for failed decompress\n";
- std::cerr << "\t -l Show ops\n";
- std::cerr << "\t -m Show ops in reverse merge order\n";
- std::cerr << "\t -n Show ops in merge order\n";
- std::cerr << "\t -a Include merged ops in any merge order listing\n";
- std::cerr << "\t -o Shows sequence op block order\n";
- std::cerr << "\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;
-};
-
static void ShowBad(CowReader& reader, const struct CowOperation* op) {
size_t count;
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";
@@ -82,7 +71,39 @@
}
}
-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;
@@ -103,7 +124,7 @@
bool has_footer = false;
if (reader.GetFooter(&footer)) has_footer = true;
- if (!opt.silent) {
+ if (!FLAGS_silent) {
std::cout << "Version: " << header.prefix.major_version << "."
<< header.prefix.minor_version << "\n";
std::cout << "Header size: " << header.prefix.header_size << "\n";
@@ -119,11 +140,11 @@
}
}
- if (!opt.silent) {
+ if (!FLAGS_silent) {
std::cout << "Parse time: " << (parse_time.count() * 1000) << "ms\n";
}
- if (opt.verify_sequence) {
+ if (FLAGS_verify_merge_sequence) {
std::cout << "\n";
if (reader.VerifyMergeOps()) {
std::cout << "\nMerge sequence is consistent.\n";
@@ -133,41 +154,56 @@
}
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);
}
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->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 (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);
}
}
- 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);
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) {
+ 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] << ", ";
@@ -189,7 +225,7 @@
iter->Next();
}
- if (!opt.silent) {
+ if (!FLAGS_silent) {
auto total_ops = replace_ops + zero_ops + copy_ops + xor_ops;
std::cout << "Data ops: " << total_ops << "\n";
std::cout << "Replace ops: " << replace_ops << "\n";
@@ -205,57 +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();
- return 1;
- }
- }
+ 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;
}
android::base::InitLogging(argv, android::snapshot::MyLogger);
- if (!android::snapshot::Inspect(argv[optind], opt)) {
+ if (!android::snapshot::Inspect(argv[1])) {
return 1;
}
return 0;
diff --git a/fs_mgr/libsnapshot/libsnapshot_cow/snapshot_reader.cpp b/fs_mgr/libsnapshot/libsnapshot_cow/snapshot_reader.cpp
index f9cdbc0..a3e40d9 100644
--- a/fs_mgr/libsnapshot/libsnapshot_cow/snapshot_reader.cpp
+++ b/fs_mgr/libsnapshot/libsnapshot_cow/snapshot_reader.cpp
@@ -155,7 +155,12 @@
}
if (op) {
- chunk = op->source;
+ 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;
@@ -179,7 +184,12 @@
return -1;
}
- off64_t offset = op->source + start_offset;
+ 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)) {
diff --git a/fs_mgr/libsnapshot/libsnapshot_cow/test_v2.cpp b/fs_mgr/libsnapshot/libsnapshot_cow/test_v2.cpp
index 120b2c0..e59bd92 100644
--- a/fs_mgr/libsnapshot/libsnapshot_cow/test_v2.cpp
+++ b/fs_mgr/libsnapshot/libsnapshot_cow/test_v2.cpp
@@ -145,7 +145,7 @@
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(ReadData(reader, op, sink.data(), sink.size()));
@@ -224,10 +224,10 @@
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_EQ(GetCowOpSourceInfoData(op), 98314); // 4096 * 24 + 10
ASSERT_TRUE(ReadData(reader, op, sink.data(), sink.size()));
ASSERT_EQ(sink, data);
@@ -283,7 +283,7 @@
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(ReadData(reader, op, sink.data(), sink.size()));
@@ -339,7 +339,7 @@
total_blocks += 1;
std::string sink(xor_data.size(), '\0');
ASSERT_EQ(op->new_block, 50);
- ASSERT_EQ(op->source, 98314); // 4096 * 24 + 10
+ ASSERT_EQ(GetCowOpSourceInfoData(op), 98314); // 4096 * 24 + 10
ASSERT_TRUE(ReadData(reader, op, sink.data(), sink.size()));
ASSERT_EQ(sink, xor_data);
}
@@ -472,14 +472,16 @@
if (strcmp(GetParam(), "none") == 0) {
GTEST_SKIP();
}
-
+ CowCompression compression;
auto algorithm = CompressionAlgorithmFromString(GetParam());
ASSERT_TRUE(algorithm.has_value());
+ compression.algorithm = algorithm.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());
+ std::unique_ptr<ICompressor> compressor = ICompressor::Create(compression, 4096);
+ auto result = compressor->Compress(expected.data(), expected.size());
ASSERT_FALSE(result.empty());
HorribleStream<uint8_t> stream(result);
@@ -528,7 +530,7 @@
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(ReadData(reader, op, sink.data(), sink.size()));
@@ -546,7 +548,7 @@
sink = {};
sink.resize(data2.size(), '\0');
- ASSERT_EQ(op->compression, kCowCompressGz);
+ ASSERT_TRUE(GetCowOpSourceInfoCompression(op));
ASSERT_EQ(op->data_length, 41); // compressed!
ASSERT_EQ(op->new_block, 51);
ASSERT_TRUE(ReadData(reader, op, sink.data(), sink.size()));
@@ -591,7 +593,7 @@
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(ReadData(reader, op, sink.data(), sink.size()));
}
@@ -1408,6 +1410,18 @@
ASSERT_TRUE(iter->AtEnd());
}
+TEST_F(CowTest, ParseOptionsTest) {
+ CowOptions options;
+ std::vector<std::pair<std::string, bool>> testcases = {
+ {"gz,4", true}, {"gz,4,4", false}, {"lz4,4", true}, {"brotli,4", true},
+ {"zstd,4", true}, {"zstd,x", false}, {"zs,4", false}, {"zstd.4", false}};
+ for (size_t i = 0; i < testcases.size(); i++) {
+ options.compression = testcases[i].first;
+ CowWriterV2 writer(options, GetCowFd());
+ ASSERT_EQ(writer.Initialize(), testcases[i].second);
+ }
+}
+
TEST_F(CowTest, LegacyRevMergeOpItrTest) {
CowOptions options;
options.cluster_ops = 5;
diff --git a/fs_mgr/libsnapshot/libsnapshot_cow/writer_v2.cpp b/fs_mgr/libsnapshot/libsnapshot_cow/writer_v2.cpp
index b6603da..ee445a2 100644
--- a/fs_mgr/libsnapshot/libsnapshot_cow/writer_v2.cpp
+++ b/fs_mgr/libsnapshot/libsnapshot_cow/writer_v2.cpp
@@ -14,12 +14,14 @@
// limitations under the License.
//
+#include "writer_v2.h"
+
#include <sys/types.h>
#include <sys/uio.h>
#include <unistd.h>
+#include <future>
#include <limits>
-#include <queue>
#include <android-base/file.h>
#include <android-base/logging.h>
@@ -37,7 +39,9 @@
#include <sys/ioctl.h>
#include <unistd.h>
-#include "writer_v2.h"
+#include "android-base/parseint.h"
+#include "android-base/strings.h"
+#include "parser_v2.h"
// The info messages here are spammy, but as useful for update_engine. Disable
// them when running on the host.
@@ -117,12 +121,29 @@
}
bool CowWriterV2::ParseOptions() {
- auto algorithm = CompressionAlgorithmFromString(options_.compression);
+ auto parts = android::base::Split(options_.compression, ",");
+
+ if (parts.size() > 2) {
+ LOG(ERROR) << "failed to parse compression parameters: invalid argument count: "
+ << parts.size() << " " << options_.compression;
+ return false;
+ }
+ auto algorithm = CompressionAlgorithmFromString(parts[0]);
if (!algorithm) {
LOG(ERROR) << "unrecognized compression: " << options_.compression;
return false;
}
- compression_ = *algorithm;
+ if (parts.size() > 1) {
+ if (!android::base::ParseUint(parts[1], &compression_.compression_level)) {
+ LOG(ERROR) << "failed to parse compression level invalid type: " << parts[1];
+ return false;
+ }
+ } else {
+ compression_.compression_level =
+ CompressWorker::GetDefaultCompressionLevel(algorithm.value());
+ }
+
+ compression_.algorithm = *algorithm;
if (options_.cluster_ops == 1) {
LOG(ERROR) << "Clusters must contain at least two operations to function.";
@@ -153,8 +174,7 @@
current_data_pos_ = next_data_pos_;
}
- std::string batch_write = batch_write_ ? "enabled" : "disabled";
- LOG_INFO << "Batch writes: " << batch_write;
+ LOG_INFO << "Batch writes: " << (batch_write_ ? "enabled" : "disabled");
}
void CowWriterV2::InitWorkers() {
@@ -163,7 +183,9 @@
return;
}
for (int i = 0; i < num_compress_threads_; i++) {
- auto wt = std::make_unique<CompressWorker>(compression_, header_.block_size);
+ std::unique_ptr<ICompressor> compressor =
+ ICompressor::Create(compression_, header_.block_size);
+ auto wt = std::make_unique<CompressWorker>(std::move(compressor), header_.block_size);
threads_.emplace_back(std::async(std::launch::async, &CompressWorker::RunThread, wt.get()));
compress_threads_.push_back(std::move(wt));
}
@@ -252,14 +274,20 @@
}
bool CowWriterV2::OpenForAppend(uint64_t label) {
- auto reader = std::make_unique<CowReader>();
- std::queue<CowOperation> toAdd;
-
- if (!reader->Parse(fd_, {label})) {
+ if (!ReadCowHeader(fd_, &header_)) {
return false;
}
- header_ = reader->GetHeader();
+ 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;
+ }
+
options_.block_size = header_.block_size;
options_.cluster_ops = header_.cluster_ops;
@@ -267,16 +295,10 @@
footer_.op.num_ops = 0;
InitPos();
- auto iter = reader->GetOpIter();
-
- while (!iter->AtEnd()) {
- 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;
@@ -318,10 +340,12 @@
const uint8_t* iter = reinterpret_cast<const uint8_t*>(data);
compressed_buf_.clear();
if (num_threads <= 1) {
- return CompressWorker::CompressBlocks(compression_, options_.block_size, data, num_blocks,
- &compressed_buf_);
+ if (!compressor_) {
+ compressor_ = ICompressor::Create(compression_, header_.block_size);
+ }
+ return CompressWorker::CompressBlocks(compressor_.get(), options_.block_size, data,
+ num_blocks, &compressed_buf_);
}
-
// Submit the blocks per thread. The retrieval of
// compressed buffers has to be done in the same order.
// We should not poll for completed buffers in a different order as the
@@ -364,7 +388,7 @@
while (num_blocks) {
size_t pending_blocks = (std::min(kProcessingBlocks, num_blocks));
- if (compression_ && num_compress_threads_ > 1) {
+ if (compression_.algorithm && num_compress_threads_ > 1) {
if (!CompressBlocks(pending_blocks, iter)) {
return false;
}
@@ -384,19 +408,22 @@
op.source = next_data_pos_;
}
- if (compression_) {
+ if (compression_.algorithm) {
auto data = [&, this]() {
if (num_compress_threads_ > 1) {
auto data = std::move(*buf_iter_);
buf_iter_++;
return data;
} else {
- auto data =
- CompressWorker::Compress(compression_, iter, header_.block_size);
+ if (!compressor_) {
+ compressor_ = ICompressor::Create(compression_, header_.block_size);
+ }
+
+ auto data = compressor_->Compress(iter, header_.block_size);
return data;
}
}();
- op.compression = compression_;
+ op.compression = compression_.algorithm;
op.data_length = static_cast<uint16_t>(data.size());
if (!WriteOperation(op, data.data(), data.size())) {
@@ -505,8 +532,8 @@
}
}
- // Footer should be at the end of a file, so if there is data after the current block, end it
- // and start a new cluster.
+ // Footer should be at the end of a file, so if there is data after the current block, end
+ // it and start a new cluster.
if (cluster_size_ && current_data_size_ > 0) {
EmitCluster();
extra_cluster = true;
diff --git a/fs_mgr/libsnapshot/libsnapshot_cow/writer_v2.h b/fs_mgr/libsnapshot/libsnapshot_cow/writer_v2.h
index 809ae57..131a068 100644
--- a/fs_mgr/libsnapshot/libsnapshot_cow/writer_v2.h
+++ b/fs_mgr/libsnapshot/libsnapshot_cow/writer_v2.h
@@ -14,6 +14,7 @@
#pragma once
+#include <future>
#include "writer_base.h"
namespace android {
@@ -63,7 +64,10 @@
private:
CowFooter footer_{};
- CowCompressionAlgorithm compression_ = kCowCompressNone;
+ CowCompression compression_;
+ // in the case that we are using one thread for compression, we can store and re-use the same
+ // compressor
+ std::unique_ptr<ICompressor> compressor_;
uint64_t current_op_pos_ = 0;
uint64_t next_op_pos_ = 0;
uint64_t next_data_pos_ = 0;
diff --git a/fs_mgr/libsnapshot/snapshot.cpp b/fs_mgr/libsnapshot/snapshot.cpp
index fbea79b..51389a0 100644
--- a/fs_mgr/libsnapshot/snapshot.cpp
+++ b/fs_mgr/libsnapshot/snapshot.cpp
@@ -729,6 +729,14 @@
LOG(ERROR) << "Failed to remove status file " << file_path << ": " << error;
return false;
}
+
+ // This path may never exist. If it is present, then it's a stale
+ // snapshot status file. Just remove the file and log the message.
+ const std::string tmp_path = file_path + ".tmp";
+ if (!android::base::RemoveFileIfExists(tmp_path, &error)) {
+ LOG(ERROR) << "Failed to remove stale snapshot file " << tmp_path;
+ }
+
return true;
}
@@ -754,10 +762,10 @@
return false;
}
- auto other_suffix = device_->GetOtherSlotSuffix();
+ auto current_slot_suffix = device_->GetSlotSuffix();
for (const auto& snapshot : snapshots) {
- if (android::base::EndsWith(snapshot, other_suffix)) {
+ if (!android::base::EndsWith(snapshot, current_slot_suffix)) {
// Allow the merge to continue, but log this unexpected case.
LOG(ERROR) << "Unexpected snapshot found during merge: " << snapshot;
continue;
@@ -1123,7 +1131,7 @@
return MergeResult(UpdateState::MergeFailed, MergeFailureCode::ListSnapshots);
}
- auto other_suffix = device_->GetOtherSlotSuffix();
+ auto current_slot_suffix = device_->GetSlotSuffix();
bool cancelled = false;
bool merging = false;
@@ -1131,9 +1139,9 @@
bool wrong_phase = false;
MergeFailureCode failure_code = MergeFailureCode::Ok;
for (const auto& snapshot : snapshots) {
- if (android::base::EndsWith(snapshot, other_suffix)) {
+ if (!android::base::EndsWith(snapshot, current_slot_suffix)) {
// This will have triggered an error message in InitiateMerge already.
- LOG(INFO) << "Skipping merge validation of unexpected snapshot: " << snapshot;
+ LOG(ERROR) << "Skipping merge validation of unexpected snapshot: " << snapshot;
continue;
}
@@ -1248,14 +1256,12 @@
GetMetadataPartitionState(*current_metadata, name) == MetadataPartitionState::Updated);
if (UpdateUsesUserSnapshots(lock)) {
- std::string merge_status;
- if (EnsureSnapuserdConnected()) {
- // Query the snapshot status from the daemon
- merge_status = snapuserd_client_->QuerySnapshotStatus(name);
- } else {
- MergeResult(UpdateState::MergeFailed, MergeFailureCode::QuerySnapshotStatus);
+ if (!EnsureSnapuserdConnected()) {
+ return MergeResult(UpdateState::MergeFailed, MergeFailureCode::QuerySnapshotStatus);
}
+ // Query the snapshot status from the daemon
+ const auto merge_status = snapuserd_client_->QuerySnapshotStatus(name);
if (merge_status == "snapshot-merge-failed") {
return MergeResult(UpdateState::MergeFailed, MergeFailureCode::UnknownTargetType);
}
@@ -2490,9 +2496,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 +2510,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)) {
diff --git a/fs_mgr/libsnapshot/snapshot_test.cpp b/fs_mgr/libsnapshot/snapshot_test.cpp
index 0a85489..3b6d26a 100644
--- a/fs_mgr/libsnapshot/snapshot_test.cpp
+++ b/fs_mgr/libsnapshot/snapshot_test.cpp
@@ -685,6 +685,17 @@
}
ASSERT_TRUE(sm->InitiateMerge());
+ // Create stale files in snapshot directory. Merge should skip these files
+ // as the suffix doesn't match the current slot.
+ auto tmp_path = test_device->GetMetadataDir() + "/snapshots/test_partition_b.tmp";
+ auto other_slot = test_device->GetMetadataDir() + "/snapshots/test_partition_a";
+
+ unique_fd fd(open(tmp_path.c_str(), O_RDWR | O_CLOEXEC | O_CREAT, 0644));
+ ASSERT_GE(fd, 0);
+
+ fd.reset(open(other_slot.c_str(), O_RDWR | O_CLOEXEC | O_CREAT, 0644));
+ ASSERT_GE(fd, 0);
+
// The device should have been switched to a snapshot-merge target.
DeviceMapper::TargetInfo target;
ASSERT_TRUE(sm->IsSnapshotDevice("test_partition_b", &target));
@@ -700,13 +711,23 @@
ASSERT_EQ(sm->ProcessUpdateState(), UpdateState::MergeCompleted);
ASSERT_EQ(sm->GetUpdateState(), UpdateState::None);
+ // Make sure that snapshot states are cleared and all stale files
+ // are deleted
+ {
+ ASSERT_TRUE(AcquireLock());
+ auto local_lock = std::move(lock_);
+ std::vector<std::string> snapshots;
+ ASSERT_TRUE(sm->ListSnapshots(local_lock.get(), &snapshots));
+ ASSERT_TRUE(snapshots.empty());
+ }
+
// The device should no longer be a snapshot or snapshot-merge.
ASSERT_FALSE(sm->IsSnapshotDevice("test_partition_b"));
// Test that we can read back the string we wrote to the snapshot. Note
// that the base device is gone now. |snap_device| contains the correct
// partition.
- unique_fd fd(open("/dev/block/mapper/test_partition_b", O_RDONLY | O_CLOEXEC));
+ fd.reset(open("/dev/block/mapper/test_partition_b", O_RDONLY | O_CLOEXEC));
ASSERT_GE(fd, 0);
std::string buffer(test_string.size(), '\0');
diff --git a/fs_mgr/libsnapshot/snapuserd/Android.bp b/fs_mgr/libsnapshot/snapuserd/Android.bp
index dd11a33..6548cc8 100644
--- a/fs_mgr/libsnapshot/snapuserd/Android.bp
+++ b/fs_mgr/libsnapshot/snapuserd/Android.bp
@@ -19,7 +19,7 @@
}
cc_defaults {
- name: "libsnapshot_snapuserd_defaults",
+ name: "libsnapuserd_client_defaults",
defaults: [
"fs_mgr_defaults",
],
@@ -33,15 +33,15 @@
}
cc_library_static {
- name: "libsnapshot_snapuserd",
+ name: "libsnapuserd_client",
defaults: [
"fs_mgr_defaults",
- "libsnapshot_snapuserd_defaults",
+ "libsnapuserd_client_defaults",
],
recovery_available: true,
static_libs: [
"libcutils_sockets",
- "libfs_mgr",
+ "libfs_mgr_file_wait",
],
shared_libs: [
"libbase",
@@ -57,32 +57,40 @@
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",
+ "dm-snapshot-merge/snapuserd_worker.cpp",
+ "dm_user_block_server.cpp",
"snapuserd_buffer.cpp",
+ "user-space-merge/handler_manager.cpp",
+ "user-space-merge/merge_worker.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",
+ "utility.cpp",
],
static_libs: [
"libbase",
"libdm",
+ "libext2_uuid",
"libext4_utils",
"libsnapshot_cow",
"liburing",
],
include_dirs: ["bionic/libc/kernel"],
+ export_include_dirs: ["include"],
header_libs: [
"libstorage_literals_headers",
],
ramdisk_available: true,
vendor_ramdisk_available: true,
recovery_available: true,
+ host_supported: true,
}
cc_defaults {
@@ -101,12 +109,13 @@
"libbrotli",
"libcutils_sockets",
"libdm",
- "libfs_mgr",
+ "libext2_uuid",
+ "libfs_mgr_file_wait",
"libgflags",
"liblog",
"libsnapshot_cow",
- "libsnapshot_snapuserd",
"libsnapuserd",
+ "libsnapuserd_client",
"libz",
"liblz4",
"libext4_utils",
@@ -141,6 +150,9 @@
init_rc: [
"snapuserd.rc",
],
+ static_libs: [
+ "libsnapuserd_client",
+ ],
ramdisk_available: false,
vendor_ramdisk_available: true,
}
@@ -164,7 +176,7 @@
}
cc_test {
- name: "cow_snapuserd_test",
+ name: "snapuserd_test_legacy",
defaults: [
"fs_mgr_defaults",
"libsnapshot_cow_defaults",
@@ -183,12 +195,13 @@
"libbrotli",
"libgtest",
"libsnapshot_cow",
- "libsnapshot_snapuserd",
+ "libsnapuserd_client",
"libcutils_sockets",
"libz",
- "libfs_mgr",
"libdm",
+ "libext2_uuid",
"libext4_utils",
+ "libfs_mgr_file_wait",
],
header_libs: [
"libstorage_literals_headers",
@@ -208,6 +221,9 @@
"libsnapshot_cow_defaults",
],
srcs: [
+ "testing/dm_user_harness.cpp",
+ "testing/harness.cpp",
+ "testing/host_harness.cpp",
"user-space-merge/snapuserd_test.cpp",
],
shared_libs: [
@@ -216,18 +232,22 @@
],
static_libs: [
"libbrotli",
+ "libcutils_sockets",
+ "libdm",
+ "libext2_uuid",
+ "libext4_utils",
+ "libfs_mgr_file_wait",
+ "libgflags",
"libgtest",
"libsnapshot_cow",
- "libsnapshot_snapuserd",
- "libcutils_sockets",
- "libz",
- "libfs_mgr",
- "libdm",
- "libext4_utils",
+ "libsnapuserd",
"liburing",
- "libgflags",
+ "libz",
],
- include_dirs: ["bionic/libc/kernel"],
+ include_dirs: [
+ "bionic/libc/kernel",
+ ".",
+ ],
header_libs: [
"libstorage_literals_headers",
"libfiemap_headers",
diff --git a/fs_mgr/libsnapshot/snapuserd/dm-snapshot-merge/snapuserd.cpp b/fs_mgr/libsnapshot/snapuserd/dm-snapshot-merge/snapuserd.cpp
index da9bd11..71664bf 100644
--- a/fs_mgr/libsnapshot/snapuserd/dm-snapshot-merge/snapuserd.cpp
+++ b/fs_mgr/libsnapshot/snapuserd/dm-snapshot-merge/snapuserd.cpp
@@ -508,7 +508,7 @@
// the merge of operations are done based on the ops present
// in the file.
//===========================================================
- uint64_t block_source = cow_op->source;
+ 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)) {
break;
@@ -734,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;
}
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 a32c2bf..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,7 +172,7 @@
}
void ReadAheadThread::CheckOverlap(const CowOperation* cow_op) {
- uint64_t source_block = cow_op->source;
+ uint64_t source_block = GetCowOpSourceInfoData(cow_op);
if (dest_blocks_.count(cow_op->new_block) || source_blocks_.count(source_block)) {
overlap_ = true;
}
@@ -191,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;
}
@@ -210,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 922df34..559dcc7 100644
--- a/fs_mgr/libsnapshot/snapuserd/dm-snapshot-merge/snapuserd_worker.cpp
+++ b/fs_mgr/libsnapshot/snapuserd/dm-snapshot-merge/snapuserd_worker.cpp
@@ -115,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;
@@ -508,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/dm_user_block_server.cpp b/fs_mgr/libsnapshot/snapuserd/dm_user_block_server.cpp
new file mode 100644
index 0000000..e988335
--- /dev/null
+++ b/fs_mgr/libsnapshot/snapuserd/dm_user_block_server.cpp
@@ -0,0 +1,152 @@
+// 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 <snapuserd/dm_user_block_server.h>
+
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <snapuserd/snapuserd_kernel.h>
+#include "snapuserd_logging.h"
+
+namespace android {
+namespace snapshot {
+
+using android::base::unique_fd;
+
+DmUserBlockServer::DmUserBlockServer(const std::string& misc_name, unique_fd&& ctrl_fd,
+ Delegate* delegate, size_t buffer_size)
+ : misc_name_(misc_name), ctrl_fd_(std::move(ctrl_fd)), delegate_(delegate) {
+ buffer_.Initialize(buffer_size);
+}
+
+bool DmUserBlockServer::ProcessRequests() {
+ struct dm_user_header* header = buffer_.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;
+ }
+
+ SNAP_LOG(DEBUG) << "Daemon: msg->seq: " << std::dec << header->seq;
+ SNAP_LOG(DEBUG) << "Daemon: msg->len: " << std::dec << header->len;
+ SNAP_LOG(DEBUG) << "Daemon: msg->sector: " << std::dec << header->sector;
+ SNAP_LOG(DEBUG) << "Daemon: msg->type: " << std::dec << header->type;
+ SNAP_LOG(DEBUG) << "Daemon: msg->flags: " << std::dec << header->flags;
+
+ if (!ProcessRequest(header)) {
+ if (header->type != DM_USER_RESP_ERROR) {
+ SendError();
+ }
+ return false;
+ }
+ return true;
+}
+
+bool DmUserBlockServer::ProcessRequest(dm_user_header* header) {
+ // Use the same header buffer as the response header.
+ int request_type = header->type;
+ header->type = DM_USER_RESP_SUCCESS;
+ header_response_ = true;
+
+ // Reset the output buffer.
+ buffer_.ResetBufferOffset();
+
+ switch (request_type) {
+ case DM_USER_REQ_MAP_READ:
+ return delegate_->RequestSectors(header->sector, header->len);
+
+ case DM_USER_REQ_MAP_WRITE:
+ // We should not get any write request to dm-user as we mount all
+ // partitions as read-only.
+ SNAP_LOG(ERROR) << "Unexpected write request from dm-user";
+ return false;
+
+ default:
+ SNAP_LOG(ERROR) << "Unexpected request from dm-user: " << request_type;
+ return false;
+ }
+}
+
+void* DmUserBlockServer::GetResponseBuffer(size_t size, size_t to_write) {
+ return buffer_.AcquireBuffer(size, to_write);
+}
+
+bool DmUserBlockServer::SendBufferedIo() {
+ return WriteDmUserPayload(buffer_.GetPayloadBytesWritten());
+}
+
+void DmUserBlockServer::SendError() {
+ struct dm_user_header* header = buffer_.GetHeaderPtr();
+ header->type = DM_USER_RESP_ERROR;
+ // This is an issue with the dm-user interface. There
+ // is no way to propagate the I/O error back to dm-user
+ // if we have already communicated the header back. Header
+ // is responded once at the beginning; however I/O can
+ // be processed in chunks. If we encounter an I/O error
+ // somewhere in the middle of the processing, we can't communicate
+ // this back to dm-user.
+ //
+ // TODO: Fix the interface
+ CHECK(header_response_);
+
+ WriteDmUserPayload(0);
+}
+
+bool DmUserBlockServer::WriteDmUserPayload(size_t size) {
+ size_t payload_size = size;
+ void* buf = buffer_.GetPayloadBufPtr();
+ if (header_response_) {
+ payload_size += sizeof(struct dm_user_header);
+ buf = buffer_.GetBufPtr();
+ }
+
+ if (!android::base::WriteFully(ctrl_fd_, buf, payload_size)) {
+ SNAP_PLOG(ERROR) << "Write to dm-user failed size: " << payload_size;
+ 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.
+ buffer_.ResetBufferOffset();
+ return true;
+}
+
+DmUserBlockServerOpener::DmUserBlockServerOpener(const std::string& misc_name,
+ const std::string& dm_user_path)
+ : misc_name_(misc_name), dm_user_path_(dm_user_path) {}
+
+std::unique_ptr<IBlockServer> DmUserBlockServerOpener::Open(IBlockServer::Delegate* delegate,
+ size_t buffer_size) {
+ unique_fd fd(open(dm_user_path_.c_str(), O_RDWR | O_CLOEXEC));
+ if (fd < 0) {
+ SNAP_PLOG(ERROR) << "Could not open dm-user path: " << dm_user_path_;
+ return nullptr;
+ }
+ return std::make_unique<DmUserBlockServer>(misc_name_, std::move(fd), delegate, buffer_size);
+}
+
+std::shared_ptr<IBlockServerOpener> DmUserBlockServerFactory::CreateOpener(
+ const std::string& misc_name) {
+ auto dm_path = "/dev/dm-user/" + misc_name;
+ return std::make_shared<DmUserBlockServerOpener>(misc_name, dm_path);
+}
+
+} // namespace snapshot
+} // namespace android
diff --git a/fs_mgr/libsnapshot/snapuserd/include/snapuserd/block_server.h b/fs_mgr/libsnapshot/snapuserd/include/snapuserd/block_server.h
new file mode 100644
index 0000000..406bf11
--- /dev/null
+++ b/fs_mgr/libsnapshot/snapuserd/include/snapuserd/block_server.h
@@ -0,0 +1,95 @@
+// 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>
+
+namespace android {
+namespace snapshot {
+
+// These interfaces model the block device driver component of snapuserd (eg,
+// dm-user).
+
+// An open connection to a userspace block device control
+class IBlockServer {
+ public:
+ class Delegate {
+ public:
+ virtual ~Delegate() {}
+
+ // Respond to a request for reading a contiguous run of sectors. This
+ // call should be followed by calls to GetResponseBuffer/CommitBuffer
+ // until the |size| is fulfilled.
+ //
+ // If false is returned, an error will be automatically reported unless
+ // SendError was called.
+ virtual bool RequestSectors(uint64_t sector, uint64_t size) = 0;
+ };
+
+ virtual ~IBlockServer() {}
+
+ // Process I/O requests. This can block the worker thread until either a
+ // request is available or the underlying connection has been destroyed.
+ //
+ // True indicates that one or more requests was processed. False indicates
+ // an unrecoverable condition and processing should stop.
+ virtual bool ProcessRequests() = 0;
+
+ // Return a buffer for fulfilling a RequestSectors request. This buffer
+ // is valid until calling SendBufferedIo. This cannot be called outside
+ // of RequestSectors().
+ //
+ // "to_write" must be <= "size". If it is < size, the excess bytes are
+ // available for writing, but will not be send via SendBufferedIo, and
+ // may be reallocated in the next call to GetResponseBuffer.
+ //
+ // All buffers returned are invalidated after SendBufferedIo or returning
+ // control from RequestSectors.
+ virtual void* GetResponseBuffer(size_t size, size_t to_write) = 0;
+
+ // Send all outstanding buffers to the driver, in order. This should
+ // be called at least once in response to RequestSectors. This returns
+ // ownership of any buffers returned by GetResponseBuffer.
+ //
+ // If false is returned, an error is automatically reported to the driver.
+ virtual bool SendBufferedIo() = 0;
+
+ void* GetResponseBuffer(size_t size) { return GetResponseBuffer(size, size); }
+};
+
+class IBlockServerOpener {
+ public:
+ virtual ~IBlockServerOpener() = default;
+
+ // Open a connection to the service. This is called on the daemon thread.
+ //
+ // buffer_size is the maximum amount of buffered I/O to use.
+ virtual std::unique_ptr<IBlockServer> Open(IBlockServer::Delegate* delegate,
+ size_t buffer_size) = 0;
+};
+
+class IBlockServerFactory {
+ public:
+ virtual ~IBlockServerFactory() {}
+
+ // Return a new IBlockServerOpener given a unique device name.
+ virtual std::shared_ptr<IBlockServerOpener> CreateOpener(const std::string& misc_name) = 0;
+};
+
+} // namespace snapshot
+} // namespace android
diff --git a/fs_mgr/libsnapshot/snapuserd/include/snapuserd/dm_user_block_server.h b/fs_mgr/libsnapshot/snapuserd/include/snapuserd/dm_user_block_server.h
new file mode 100644
index 0000000..f1f8da1
--- /dev/null
+++ b/fs_mgr/libsnapshot/snapuserd/include/snapuserd/dm_user_block_server.h
@@ -0,0 +1,68 @@
+// 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 <android-base/unique_fd.h>
+
+#include <string>
+
+#include <snapuserd/block_server.h>
+#include <snapuserd/snapuserd_buffer.h>
+
+namespace android {
+namespace snapshot {
+
+class DmUserBlockServer : public IBlockServer {
+ public:
+ DmUserBlockServer(const std::string& misc_name, android::base::unique_fd&& ctrl_fd,
+ Delegate* delegate, size_t buffer_size);
+
+ bool ProcessRequests() override;
+ void* GetResponseBuffer(size_t size, size_t to_write) override;
+ bool SendBufferedIo() override;
+ void SendError();
+
+ private:
+ bool ProcessRequest(dm_user_header* header);
+ bool WriteDmUserPayload(size_t size);
+
+ std::string misc_name_;
+ android::base::unique_fd ctrl_fd_;
+ Delegate* delegate_;
+
+ // Per-request state.
+ BufferSink buffer_;
+ bool header_response_ = false;
+};
+
+class DmUserBlockServerOpener : public IBlockServerOpener {
+ public:
+ DmUserBlockServerOpener(const std::string& misc_name, const std::string& dm_user_path);
+
+ std::unique_ptr<IBlockServer> Open(IBlockServer::Delegate* delegate,
+ size_t buffer_size) override;
+
+ private:
+ std::string misc_name_;
+ std::string dm_user_path_;
+};
+
+class DmUserBlockServerFactory : public IBlockServerFactory {
+ public:
+ std::shared_ptr<IBlockServerOpener> CreateOpener(const std::string& misc_name) override;
+};
+
+} // namespace snapshot
+} // namespace android
diff --git a/fs_mgr/libsnapshot/snapuserd/include/snapuserd/snapuserd_buffer.h b/fs_mgr/libsnapshot/snapuserd/include/snapuserd/snapuserd_buffer.h
index a6b6a7f..c5ca2b1 100644
--- a/fs_mgr/libsnapshot/snapuserd/include/snapuserd/snapuserd_buffer.h
+++ b/fs_mgr/libsnapshot/snapuserd/include/snapuserd/snapuserd_buffer.h
@@ -36,6 +36,21 @@
struct dm_user_header* GetHeaderPtr();
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_;
@@ -43,19 +58,5 @@
size_t buffer_size_;
};
-class XorSink final {
- public:
- void Initialize(BufferSink* sink, size_t size);
- void Reset();
- void* GetBuffer(size_t requested, size_t* actual);
- bool ReturnData(void* buffer, size_t len);
-
- 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 46952c4..7ab75dc 100644
--- a/fs_mgr/libsnapshot/snapuserd/include/snapuserd/snapuserd_kernel.h
+++ b/fs_mgr/libsnapshot/snapuserd/include/snapuserd/snapuserd_kernel.h
@@ -14,6 +14,8 @@
#pragma once
+#include <linux/types.h>
+
namespace android {
namespace snapshot {
@@ -40,6 +42,7 @@
* multiple of 512 bytes. Hence these two constants.
*/
static constexpr uint32_t SECTOR_SHIFT = 9;
+static constexpr uint64_t SECTOR_SIZE = (1ULL << SECTOR_SHIFT);
static constexpr size_t BLOCK_SZ = 4096;
static constexpr size_t BLOCK_SHIFT = (__builtin_ffs(BLOCK_SZ) - 1);
@@ -69,7 +72,7 @@
/* In sectors */
uint32_t chunk_size;
-} __packed;
+} __attribute__((packed));
// A disk exception is a mapping of old_chunk to new_chunk
// old_chunk is the chunk ID of a dm-snapshot device.
@@ -77,7 +80,7 @@
struct disk_exception {
uint64_t old_chunk;
uint64_t new_chunk;
-} __packed;
+} __attribute__((packed));
// Control structures to communicate with dm-user
// It comprises of header and a payload
diff --git a/fs_mgr/libsnapshot/snapuserd/snapuserd_buffer.cpp b/fs_mgr/libsnapshot/snapuserd/snapuserd_buffer.cpp
index ab763ab..490c0e6 100644
--- a/fs_mgr/libsnapshot/snapuserd/snapuserd_buffer.cpp
+++ b/fs_mgr/libsnapshot/snapuserd/snapuserd_buffer.cpp
@@ -15,22 +15,36 @@
*/
#include <snapuserd/snapuserd_buffer.h>
+
+#include <android-base/logging.h>
#include <snapuserd/snapuserd_kernel.h>
namespace android {
namespace snapshot {
void BufferSink::Initialize(size_t size) {
- buffer_size_ = size;
+ buffer_size_ = size + sizeof(struct dm_user_header);
buffer_offset_ = 0;
- buffer_ = std::make_unique<uint8_t[]>(size);
+ buffer_ = std::make_unique<uint8_t[]>(buffer_size_);
+}
+
+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) {
- if ((buffer_size_ - buffer_offset_) < size) return nullptr;
-
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/snapuserd_logging.h b/fs_mgr/libsnapshot/snapuserd/snapuserd_logging.h
new file mode 100644
index 0000000..bc470ce
--- /dev/null
+++ b/fs_mgr/libsnapshot/snapuserd/snapuserd_logging.h
@@ -0,0 +1,20 @@
+// 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 <android-base/logging.h>
+
+#define SNAP_LOG(level) LOG(level) << misc_name_ << ": "
+#define SNAP_PLOG(level) PLOG(level) << misc_name_ << ": "
diff --git a/fs_mgr/libsnapshot/snapuserd/testing/dm_user_harness.cpp b/fs_mgr/libsnapshot/snapuserd/testing/dm_user_harness.cpp
new file mode 100644
index 0000000..7cadf25
--- /dev/null
+++ b/fs_mgr/libsnapshot/snapuserd/testing/dm_user_harness.cpp
@@ -0,0 +1,67 @@
+// 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 "dm_user_harness.h"
+
+#include <fcntl.h>
+
+#include <android-base/file.h>
+#include <fs_mgr/file_wait.h>
+#include <libdm/dm.h>
+#include <snapuserd/dm_user_block_server.h>
+
+namespace android {
+namespace snapshot {
+
+using namespace std::chrono_literals;
+using android::base::unique_fd;
+
+DmUserDevice::DmUserDevice(std::unique_ptr<Tempdevice>&& dev) : dev_(std::move(dev)) {}
+
+const std::string& DmUserDevice::GetPath() {
+ return dev_->path();
+}
+
+bool DmUserDevice::Destroy() {
+ return dev_->Destroy();
+}
+
+DmUserTestHarness::DmUserTestHarness() {
+ block_server_factory_ = std::make_unique<DmUserBlockServerFactory>();
+}
+
+std::unique_ptr<IUserDevice> DmUserTestHarness::CreateUserDevice(const std::string& dev_name,
+ const std::string& misc_name,
+ uint64_t num_sectors) {
+ android::dm::DmTable dmuser_table;
+ dmuser_table.Emplace<android::dm::DmTargetUser>(0, num_sectors, misc_name);
+ auto dev = std::make_unique<Tempdevice>(dev_name, dmuser_table);
+ if (!dev->valid()) {
+ return nullptr;
+ }
+
+ auto misc_device = "/dev/dm-user/" + misc_name;
+ if (!android::fs_mgr::WaitForFile(misc_device, 10s)) {
+ return nullptr;
+ }
+
+ return std::make_unique<DmUserDevice>(std::move(dev));
+}
+
+IBlockServerFactory* DmUserTestHarness::GetBlockServerFactory() {
+ return block_server_factory_.get();
+}
+
+} // namespace snapshot
+} // namespace android
diff --git a/fs_mgr/libsnapshot/snapuserd/testing/dm_user_harness.h b/fs_mgr/libsnapshot/snapuserd/testing/dm_user_harness.h
new file mode 100644
index 0000000..cf26bed
--- /dev/null
+++ b/fs_mgr/libsnapshot/snapuserd/testing/dm_user_harness.h
@@ -0,0 +1,54 @@
+// 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 <android-base/unique_fd.h>
+
+#include "harness.h"
+#include "temp_device.h"
+
+namespace android {
+namespace snapshot {
+
+using android::base::unique_fd;
+
+class DmUserBlockServerFactory;
+
+class DmUserDevice final : public IUserDevice {
+ public:
+ explicit DmUserDevice(std::unique_ptr<Tempdevice>&& dev);
+ const std::string& GetPath() override;
+ bool Destroy() override;
+
+ private:
+ std::unique_ptr<Tempdevice> dev_;
+};
+
+class DmUserTestHarness final : public ITestHarness {
+ public:
+ DmUserTestHarness();
+
+ std::unique_ptr<IUserDevice> CreateUserDevice(const std::string& dev_name,
+ const std::string& misc_name,
+ uint64_t num_sectors) override;
+ IBlockServerFactory* GetBlockServerFactory() override;
+ bool HasUserDevice() override { return true; }
+
+ private:
+ std::unique_ptr<DmUserBlockServerFactory> block_server_factory_;
+};
+
+} // namespace snapshot
+} // namespace android
diff --git a/fs_mgr/libsnapshot/snapuserd/testing/harness.cpp b/fs_mgr/libsnapshot/snapuserd/testing/harness.cpp
new file mode 100644
index 0000000..02ae549
--- /dev/null
+++ b/fs_mgr/libsnapshot/snapuserd/testing/harness.cpp
@@ -0,0 +1,116 @@
+// 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 "harness.h"
+
+#ifdef __ANDROID__
+#include <linux/memfd.h>
+#endif
+#include <sys/mman.h>
+#include <sys/resource.h>
+#include <unistd.h>
+
+#include <android-base/file.h>
+#include <ext4_utils/ext4_utils.h>
+#include <libdm/loop_control.h>
+#include "snapuserd_logging.h"
+
+namespace android {
+namespace snapshot {
+
+using namespace std::chrono_literals;
+using android::base::unique_fd;
+using android::dm::LoopDevice;
+
+#ifdef __ANDROID__
+// Prefer this on device since it is a real block device, which is more similar
+// to how we use snapuserd.
+class MemoryBackedDevice final : public IBackingDevice {
+ public:
+ bool Init(uint64_t size) {
+ memfd_.reset(memfd_create("snapuserd_test", MFD_ALLOW_SEALING));
+ if (memfd_ < 0) {
+ PLOG(ERROR) << "memfd_create failed";
+ return false;
+ }
+ if (ftruncate(memfd_.get(), size) < 0) {
+ PLOG(ERROR) << "ftruncate failed";
+ return false;
+ }
+ if (fcntl(memfd_.get(), F_ADD_SEALS, F_SEAL_GROW | F_SEAL_SHRINK) < 0) {
+ PLOG(ERROR) << "fcntl seal failed";
+ return false;
+ }
+ dev_ = std::make_unique<LoopDevice>(memfd_, 10s);
+ return dev_->valid();
+ }
+ const std::string& GetPath() override { return dev_->device(); }
+ uint64_t GetSize() override {
+ unique_fd fd(open(GetPath().c_str(), O_RDONLY | O_CLOEXEC));
+ if (fd < 0) {
+ PLOG(ERROR) << "open failed: " << GetPath();
+ return 0;
+ }
+ return get_block_device_size(fd.get());
+ }
+
+ private:
+ unique_fd memfd_;
+ std::unique_ptr<LoopDevice> dev_;
+};
+#endif
+
+class FileBackedDevice final : public IBackingDevice {
+ public:
+ bool Init(uint64_t size) {
+ if (temp_.fd < 0) {
+ return false;
+ }
+ if (ftruncate(temp_.fd, size) < 0) {
+ PLOG(ERROR) << "ftruncate failed: " << temp_.path;
+ return false;
+ }
+ path_ = temp_.path;
+ return true;
+ }
+
+ const std::string& GetPath() override { return path_; }
+ uint64_t GetSize() override {
+ off_t off = lseek(temp_.fd, 0, SEEK_END);
+ if (off < 0) {
+ PLOG(ERROR) << "lseek failed: " << temp_.path;
+ return 0;
+ }
+ return off;
+ }
+
+ private:
+ TemporaryFile temp_;
+ std::string path_;
+};
+
+std::unique_ptr<IBackingDevice> ITestHarness::CreateBackingDevice(uint64_t size) {
+#ifdef __ANDROID__
+ auto dev = std::make_unique<MemoryBackedDevice>();
+#else
+ auto dev = std::make_unique<FileBackedDevice>();
+#endif
+ if (!dev->Init(size)) {
+ return nullptr;
+ }
+ return dev;
+}
+
+} // namespace snapshot
+} // namespace android
diff --git a/fs_mgr/libsnapshot/snapuserd/testing/harness.h b/fs_mgr/libsnapshot/snapuserd/testing/harness.h
new file mode 100644
index 0000000..ffe9f7b
--- /dev/null
+++ b/fs_mgr/libsnapshot/snapuserd/testing/harness.h
@@ -0,0 +1,56 @@
+// 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 <sys/types.h>
+
+#include <memory>
+
+#include <android-base/unique_fd.h>
+#include <snapuserd/block_server.h>
+
+namespace android {
+namespace snapshot {
+
+// Interface for a "block driver in userspace" device.
+class IUserDevice {
+ public:
+ virtual ~IUserDevice() {}
+ virtual const std::string& GetPath() = 0;
+ virtual bool Destroy() = 0;
+};
+
+// Interface for an fd/temp file that is a block device when possible.
+class IBackingDevice {
+ public:
+ virtual ~IBackingDevice() {}
+ virtual const std::string& GetPath() = 0;
+ virtual uint64_t GetSize() = 0;
+};
+
+class ITestHarness {
+ public:
+ virtual ~ITestHarness() {}
+ virtual std::unique_ptr<IUserDevice> CreateUserDevice(const std::string& dev_name,
+ const std::string& misc_name,
+ uint64_t num_sectors) = 0;
+ virtual IBlockServerFactory* GetBlockServerFactory() = 0;
+ virtual bool HasUserDevice() = 0;
+ virtual std::unique_ptr<IBackingDevice> CreateBackingDevice(uint64_t size);
+};
+
+} // namespace snapshot
+} // namespace android
diff --git a/fs_mgr/libsnapshot/snapuserd/testing/host_harness.cpp b/fs_mgr/libsnapshot/snapuserd/testing/host_harness.cpp
new file mode 100644
index 0000000..0d230ad
--- /dev/null
+++ b/fs_mgr/libsnapshot/snapuserd/testing/host_harness.cpp
@@ -0,0 +1,112 @@
+// 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 "host_harness.h"
+
+#include "snapuserd_logging.h"
+
+namespace android {
+namespace snapshot {
+
+void TestBlockServerQueue::WaitForShutdown() {
+ std::unique_lock lock(m_);
+ if (shutdown_) {
+ return;
+ }
+ cv_.wait(lock, [this]() -> bool { return shutdown_; });
+}
+
+void TestBlockServerQueue::Shutdown() {
+ std::unique_lock lock(m_);
+ shutdown_ = true;
+ cv_.notify_all();
+}
+
+TestBlockServer::TestBlockServer(std::shared_ptr<TestBlockServerQueue> queue,
+ const std::string& misc_name)
+ : queue_(queue), misc_name_(misc_name) {}
+
+bool TestBlockServer::ProcessRequests() {
+ queue_->WaitForShutdown();
+ return false;
+}
+
+void* TestBlockServer::GetResponseBuffer(size_t size, size_t to_write) {
+ std::string buffer(size, '\0');
+ buffered_.emplace_back(std::move(buffer), to_write);
+ return buffered_.back().first.data();
+}
+
+bool TestBlockServer::SendBufferedIo() {
+ for (const auto& [data, to_write] : buffered_) {
+ sent_io_ += data.substr(0, to_write);
+ }
+ buffered_.clear();
+ return true;
+}
+
+TestBlockServerOpener::TestBlockServerOpener(std::shared_ptr<TestBlockServerQueue> queue,
+ const std::string& misc_name)
+ : queue_(queue), misc_name_(misc_name) {}
+
+std::unique_ptr<IBlockServer> TestBlockServerOpener::Open(IBlockServer::Delegate*, size_t) {
+ return std::make_unique<TestBlockServer>(queue_, misc_name_);
+}
+
+std::shared_ptr<TestBlockServerOpener> TestBlockServerFactory::CreateTestOpener(
+ const std::string& misc_name) {
+ if (queues_.count(misc_name)) {
+ LOG(ERROR) << "Cannot create opener for " << misc_name << ", already exists";
+ return nullptr;
+ }
+ auto queue = std::make_shared<TestBlockServerQueue>();
+ queues_.emplace(misc_name, queue);
+ return std::make_shared<TestBlockServerOpener>(queue, misc_name);
+}
+
+std::shared_ptr<IBlockServerOpener> TestBlockServerFactory::CreateOpener(
+ const std::string& misc_name) {
+ return CreateTestOpener(misc_name);
+}
+
+bool TestBlockServerFactory::DeleteQueue(const std::string& misc_name) {
+ auto iter = queues_.find(misc_name);
+ if (iter == queues_.end()) {
+ LOG(ERROR) << "Cannot delete queue " << misc_name << ", not found";
+ return false;
+ }
+ iter->second->Shutdown();
+ queues_.erase(iter);
+ return true;
+}
+
+HostUserDevice::HostUserDevice(TestBlockServerFactory* factory, const std::string& misc_name)
+ : factory_(factory), misc_name_(misc_name) {}
+
+bool HostUserDevice::Destroy() {
+ return factory_->DeleteQueue(misc_name_);
+}
+
+std::unique_ptr<IUserDevice> HostTestHarness::CreateUserDevice(const std::string&,
+ const std::string& misc_name,
+ uint64_t) {
+ return std::make_unique<HostUserDevice>(&factory_, misc_name);
+}
+
+IBlockServerFactory* HostTestHarness::GetBlockServerFactory() {
+ return &factory_;
+}
+
+} // namespace snapshot
+} // namespace android
diff --git a/fs_mgr/libsnapshot/snapuserd/testing/host_harness.h b/fs_mgr/libsnapshot/snapuserd/testing/host_harness.h
new file mode 100644
index 0000000..ec0bd29
--- /dev/null
+++ b/fs_mgr/libsnapshot/snapuserd/testing/host_harness.h
@@ -0,0 +1,105 @@
+// 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 <condition_variable>
+#include <mutex>
+#include <string>
+#include <unordered_map>
+#include <utility>
+#include <vector>
+
+#include "harness.h"
+
+namespace android {
+namespace snapshot {
+
+class TestBlockServerQueue final {
+ public:
+ void WaitForShutdown();
+ void Shutdown();
+
+ private:
+ std::mutex m_;
+ std::condition_variable cv_;
+ bool shutdown_ = false;
+};
+
+class TestBlockServer final : public IBlockServer {
+ public:
+ TestBlockServer(std::shared_ptr<TestBlockServerQueue> queue, const std::string& misc_name);
+ bool ProcessRequests() override;
+ void* GetResponseBuffer(size_t size, size_t to_write) override;
+ bool SendBufferedIo() override;
+
+ std::string&& sent_io() { return std::move(sent_io_); }
+
+ private:
+ std::shared_ptr<TestBlockServerQueue> queue_;
+ std::string misc_name_;
+ std::string sent_io_;
+ std::vector<std::pair<std::string, size_t>> buffered_;
+};
+
+class TestBlockServerOpener final : public IBlockServerOpener {
+ public:
+ TestBlockServerOpener(std::shared_ptr<TestBlockServerQueue> queue,
+ const std::string& misc_name);
+ std::unique_ptr<IBlockServer> Open(IBlockServer::Delegate* delegate,
+ size_t buffer_size) override;
+
+ private:
+ std::shared_ptr<TestBlockServerQueue> queue_;
+ std::string misc_name_;
+};
+
+class TestBlockServerFactory final : public IBlockServerFactory {
+ public:
+ std::shared_ptr<IBlockServerOpener> CreateOpener(const std::string& misc_name) override;
+ std::shared_ptr<TestBlockServerOpener> CreateTestOpener(const std::string& misc_name);
+ bool DeleteQueue(const std::string& misc_name);
+
+ private:
+ std::unordered_map<std::string, std::shared_ptr<TestBlockServerQueue>> queues_;
+};
+
+class TestBlockServerFactory;
+
+class HostUserDevice final : public IUserDevice {
+ public:
+ HostUserDevice(TestBlockServerFactory* factory, const std::string& misc_name);
+ const std::string& GetPath() override { return empty_path_; }
+ bool Destroy();
+
+ private:
+ TestBlockServerFactory* factory_;
+ std::string misc_name_;
+ std::string empty_path_;
+};
+
+class HostTestHarness final : public ITestHarness {
+ public:
+ std::unique_ptr<IUserDevice> CreateUserDevice(const std::string& dev_name,
+ const std::string& misc_name,
+ uint64_t num_sectors) override;
+ IBlockServerFactory* GetBlockServerFactory() override;
+ bool HasUserDevice() override { return false; }
+
+ private:
+ TestBlockServerFactory factory_;
+};
+
+} // namespace snapshot
+} // namespace android
diff --git a/fs_mgr/libsnapshot/snapuserd/testing/temp_device.h b/fs_mgr/libsnapshot/snapuserd/testing/temp_device.h
new file mode 100644
index 0000000..2a6d3ea
--- /dev/null
+++ b/fs_mgr/libsnapshot/snapuserd/testing/temp_device.h
@@ -0,0 +1,72 @@
+// 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 <chrono>
+#include <string>
+
+#include <libdm/dm.h>
+
+namespace android {
+namespace snapshot {
+
+using android::dm::DeviceMapper;
+using android::dm::DmTable;
+
+class Tempdevice {
+ public:
+ Tempdevice(const std::string& name, const DmTable& table)
+ : dm_(DeviceMapper::Instance()), name_(name), valid_(false) {
+ valid_ = dm_.CreateDevice(name, table, &path_, std::chrono::seconds(5));
+ }
+ Tempdevice(Tempdevice&& other) noexcept
+ : dm_(other.dm_), name_(other.name_), path_(other.path_), valid_(other.valid_) {
+ other.valid_ = false;
+ }
+ ~Tempdevice() {
+ if (valid_) {
+ dm_.DeleteDeviceIfExists(name_);
+ }
+ }
+ bool Destroy() {
+ if (!valid_) {
+ return true;
+ }
+ valid_ = false;
+ return dm_.DeleteDeviceIfExists(name_);
+ }
+ const std::string& path() const { return path_; }
+ const std::string& name() const { return name_; }
+ bool valid() const { return valid_; }
+
+ Tempdevice(const Tempdevice&) = delete;
+ Tempdevice& operator=(const Tempdevice&) = delete;
+
+ Tempdevice& operator=(Tempdevice&& other) noexcept {
+ name_ = other.name_;
+ valid_ = other.valid_;
+ other.valid_ = false;
+ return *this;
+ }
+
+ private:
+ DeviceMapper& dm_;
+ std::string name_;
+ std::string path_;
+ bool valid_;
+};
+
+} // namespace snapshot
+} // namespace android
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..d979e20
--- /dev/null
+++ b/fs_mgr/libsnapshot/snapuserd/user-space-merge/handler_manager.cpp
@@ -0,0 +1,384 @@
+// 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 <pthread.h>
+#include <sys/eventfd.h>
+
+#include <android-base/logging.h>
+
+#include "merge_worker.h"
+#include "read_worker.h"
+#include "snapuserd_core.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,
+ std::shared_ptr<IBlockServerOpener> opener, 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, opener, 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();
+
+ pthread_setname_np(pthread_self(), "Handler");
+
+ 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 (!merge_monitor_.joinable()) {
+ merge_monitor_ = std::thread(&SnapshotHandlerManager::MonitorMerge, this);
+ }
+
+ 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() {
+ pthread_setname_np(pthread_self(), "Merge Monitor");
+ 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();
+ }
+
+ if (merge_monitor_.joinable()) {
+ stop_monitor_merge_thread_ = true;
+ WakeupMonitorMergeThread();
+
+ merge_monitor_.join();
+ }
+}
+
+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..b1605f0
--- /dev/null
+++ b/fs_mgr/libsnapshot/snapuserd/user-space-merge/handler_manager.h
@@ -0,0 +1,134 @@
+// 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>
+#include <snapuserd/block_server.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,
+ std::shared_ptr<IBlockServerOpener> opener,
+ 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,
+ std::shared_ptr<IBlockServerOpener> opener,
+ 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 stop_monitor_merge_thread_ = false;
+ int active_merge_threads_ = 0;
+ std::thread merge_monitor_;
+ 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_merge.cpp b/fs_mgr/libsnapshot/snapuserd/user-space-merge/merge_worker.cpp
similarity index 91%
rename from fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_merge.cpp
rename to fs_mgr/libsnapshot/snapuserd/user-space-merge/merge_worker.cpp
index ce95b76..11b8d7c 100644
--- a/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_merge.cpp
+++ b/fs_mgr/libsnapshot/snapuserd/user-space-merge/merge_worker.cpp
@@ -13,8 +13,12 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+#include "merge_worker.h"
+
+#include <pthread.h>
#include "snapuserd_core.h"
+#include "utility.h"
namespace android {
namespace snapshot {
@@ -23,8 +27,13 @@
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);
@@ -70,7 +79,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
@@ -99,20 +108,21 @@
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 +159,7 @@
if (snapuserd_->IsIOTerminated()) {
SNAP_LOG(ERROR)
- << "MergeReplaceZeroOps: Worker threads terminated - shutting down merge";
+ << "MergeReplaceZeroOps: MergeWorker threads terminated - shutting down merge";
return false;
}
}
@@ -173,7 +183,7 @@
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());
@@ -190,6 +200,7 @@
// Wait for RA thread to notify that the merge window
// is ready for merging.
if (!snapuserd_->WaitForMergeBegin()) {
+ SNAP_LOG(ERROR) << "Failed waiting for merge to begin";
return false;
}
@@ -295,7 +306,7 @@
// will fallback to synchronous I/O.
ret = io_uring_wait_cqe(ring_.get(), &cqe);
if (ret) {
- SNAP_LOG(ERROR) << "Merge: io_uring_wait_cqe failed: " << ret;
+ SNAP_LOG(ERROR) << "Merge: io_uring_wait_cqe failed: " << strerror(-ret);
status = false;
break;
}
@@ -354,7 +365,7 @@
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());
@@ -439,7 +450,7 @@
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
@@ -455,7 +466,7 @@
return true;
}
-bool Worker::SyncMerge() {
+bool MergeWorker::SyncMerge() {
if (!MergeOrderedOps()) {
SNAP_LOG(ERROR) << "Merge failed for ordered ops";
return false;
@@ -465,7 +476,7 @@
return true;
}
-bool Worker::Merge() {
+bool MergeWorker::Merge() {
cowop_iter_ = reader_->GetOpIter(true);
bool retry = false;
@@ -511,7 +522,7 @@
return true;
}
-bool Worker::InitializeIouring() {
+bool MergeWorker::InitializeIouring() {
if (!snapuserd_->IsIouringSupported()) {
return false;
}
@@ -530,25 +541,30 @@
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...";
+
+ pthread_setname_np(pthread_self(), "MergeWorker");
+
if (!snapuserd_->WaitForMergeBegin()) {
SNAP_LOG(ERROR) << "Merge terminated early...";
return true;
}
- if (setpriority(PRIO_PROCESS, gettid(), kNiceValueForMergeThreads)) {
- SNAP_PLOG(ERROR) << "Failed to set priority for TID: " << gettid();
+ if (!SetThreadPriority(kNiceValueForMergeThreads)) {
+ SNAP_PLOG(ERROR) << "Failed to set thread priority";
}
SNAP_LOG(INFO) << "Merge starting..";
+ bufsink_.Initialize(PAYLOAD_BUFFER_SZ);
+
if (!Init()) {
SNAP_LOG(ERROR) << "Merge thread initialization failed...";
snapuserd_->MergeFailed();
diff --git a/fs_mgr/libsnapshot/snapuserd/user-space-merge/merge_worker.h b/fs_mgr/libsnapshot/snapuserd/user-space-merge/merge_worker.h
new file mode 100644
index 0000000..478d4c8
--- /dev/null
+++ b/fs_mgr/libsnapshot/snapuserd/user-space-merge/merge_worker.h
@@ -0,0 +1,59 @@
+// 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:
+ BufferSink bufsink_;
+ 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/read_worker.cpp b/fs_mgr/libsnapshot/snapuserd/user-space-merge/read_worker.cpp
new file mode 100644
index 0000000..b9ecfa5
--- /dev/null
+++ b/fs_mgr/libsnapshot/snapuserd/user-space-merge/read_worker.cpp
@@ -0,0 +1,488 @@
+/*
+ * 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 "read_worker.h"
+
+#include <pthread.h>
+
+#include "snapuserd_core.h"
+#include "utility.h"
+
+namespace android {
+namespace snapshot {
+
+using namespace android;
+using namespace android::dm;
+using android::base::unique_fd;
+
+void ReadWorker::CloseFds() {
+ block_server_ = {};
+ backing_store_fd_ = {};
+ Worker::CloseFds();
+}
+
+ReadWorker::ReadWorker(const std::string& cow_device, const std::string& backing_device,
+ const std::string& misc_name, const std::string& base_path_merge,
+ std::shared_ptr<SnapshotHandler> snapuserd,
+ std::shared_ptr<IBlockServerOpener> opener)
+ : Worker(cow_device, misc_name, base_path_merge, snapuserd),
+ backing_store_device_(backing_device),
+ block_server_opener_(opener) {}
+
+// Start the replace operation. This will read the
+// internal COW format and if the block is compressed,
+// it will be de-compressed.
+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 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
+ << " Op: " << *cow_op;
+ if (!android::base::ReadFullyAtOffset(backing_store_fd_, buffer, BLOCK_SZ, offset)) {
+ std::string op;
+ if (cow_op->type == kCowCopyOp)
+ op = "Copy-op";
+ else {
+ op = "Xor-op";
+ }
+ SNAP_PLOG(ERROR) << op << " failed. Read from backing store: " << backing_store_device_
+ << "at block :" << offset / BLOCK_SZ << " offset:" << offset % BLOCK_SZ;
+ return false;
+ }
+
+ return true;
+}
+
+// Start the copy operation. This will read the backing
+// block device which is represented by cow_op->source.
+bool ReadWorker::ProcessCopyOp(const CowOperation* cow_op, void* buffer) {
+ if (!ReadFromSourceDevice(cow_op, buffer)) {
+ return false;
+ }
+ return true;
+}
+
+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 ReadWorker::ProcessZeroOp(void* buffer) {
+ memset(buffer, 0, BLOCK_SZ);
+ return true;
+}
+
+bool ReadWorker::ProcessOrderedOp(const CowOperation* cow_op, void* buffer) {
+ MERGE_GROUP_STATE state = snapuserd_->ProcessMergingBlock(cow_op->new_block, buffer);
+
+ switch (state) {
+ case MERGE_GROUP_STATE::GROUP_MERGE_COMPLETED: {
+ // Merge is completed for this COW op; just read directly from
+ // the base device
+ 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), buffer, BLOCK_SZ)) {
+ SNAP_LOG(ERROR) << "ReadDataFromBaseDevice at sector: "
+ << (cow_op->new_block >> SECTOR_SHIFT) << " after merge-complete.";
+ return false;
+ }
+ return true;
+ }
+ case MERGE_GROUP_STATE::GROUP_MERGE_PENDING: {
+ bool ret;
+ if (cow_op->type == kCowCopyOp) {
+ ret = ProcessCopyOp(cow_op, buffer);
+ } else {
+ ret = ProcessXorOp(cow_op, buffer);
+ }
+
+ // I/O is complete - decrement the refcount irrespective of the return
+ // status
+ snapuserd_->NotifyIOCompletion(cow_op->new_block);
+ return ret;
+ }
+ // We already have the data in the buffer retrieved from RA thread.
+ // Nothing to process further.
+ case MERGE_GROUP_STATE::GROUP_MERGE_RA_READY: {
+ [[fallthrough]];
+ }
+ case MERGE_GROUP_STATE::GROUP_MERGE_IN_PROGRESS: {
+ return true;
+ }
+ default: {
+ // All other states, fail the I/O viz (GROUP_MERGE_FAILED and GROUP_INVALID)
+ return false;
+ }
+ }
+
+ return false;
+}
+
+bool ReadWorker::ProcessCowOp(const CowOperation* cow_op, void* buffer) {
+ if (cow_op == nullptr) {
+ SNAP_LOG(ERROR) << "ProcessCowOp: Invalid cow_op";
+ return false;
+ }
+
+ switch (cow_op->type) {
+ case kCowReplaceOp: {
+ return ProcessReplaceOp(cow_op, buffer);
+ }
+
+ case kCowZeroOp: {
+ return ProcessZeroOp(buffer);
+ }
+
+ case kCowCopyOp:
+ [[fallthrough]];
+ case kCowXorOp: {
+ return ProcessOrderedOp(cow_op, buffer);
+ }
+
+ default: {
+ SNAP_LOG(ERROR) << "Unknown operation-type found: " << cow_op->type;
+ }
+ }
+ return false;
+}
+
+bool ReadWorker::Init() {
+ if (!Worker::Init()) {
+ return false;
+ }
+
+ 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;
+ }
+
+ block_server_ = block_server_opener_->Open(this, PAYLOAD_BUFFER_SZ);
+ if (!block_server_) {
+ SNAP_PLOG(ERROR) << "Unable to open block server";
+ return false;
+ }
+ return true;
+}
+
+bool ReadWorker::Run() {
+ SNAP_LOG(INFO) << "Processing snapshot I/O requests....";
+
+ pthread_setname_np(pthread_self(), "ReadWorker");
+
+ if (!SetThreadPriority(kNiceValueForMergeThreads)) {
+ SNAP_PLOG(ERROR) << "Failed to set thread priority";
+ }
+
+ // Start serving IO
+ while (true) {
+ if (!block_server_->ProcessRequests()) {
+ break;
+ }
+ }
+
+ CloseFds();
+ reader_->CloseCowFd();
+
+ return true;
+}
+
+bool ReadWorker::ReadDataFromBaseDevice(sector_t sector, void* buffer, size_t read_size) {
+ CHECK(read_size <= BLOCK_SZ);
+
+ 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_
+ << "at sector :" << sector << " size: " << read_size;
+ return false;
+ }
+
+ return true;
+}
+
+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();
+ int ret = 0;
+
+ do {
+ // Process 1MB payload at a time
+ size_t read_size = std::min(PAYLOAD_BUFFER_SZ, remaining_size);
+
+ size_t total_bytes_read = 0;
+
+ while (read_size) {
+ // We need to check every 4k block to verify if it is
+ // present in the mapping.
+ size_t size = std::min(BLOCK_SZ, read_size);
+
+ auto it = std::lower_bound(chunk_vec.begin(), chunk_vec.end(),
+ std::make_pair(sector, nullptr), SnapshotHandler::compare);
+ bool not_found = (it == chunk_vec.end() || it->first != sector);
+
+ void* buffer = block_server_->GetResponseBuffer(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, buffer, size)) {
+ SNAP_LOG(ERROR) << "ReadDataFromBaseDevice failed";
+ return false;
+ }
+
+ ret = size;
+ } else {
+ // We found the sector in mapping. Check the type of COW OP and
+ // process it.
+ if (!ProcessCowOp(it->second, buffer)) {
+ SNAP_LOG(ERROR) << "ProcessCowOp failed";
+ return false;
+ }
+
+ ret = std::min(BLOCK_SZ, read_size);
+ }
+
+ read_size -= ret;
+ total_bytes_read += ret;
+ sector += (ret >> SECTOR_SHIFT);
+ }
+
+ if (!SendBufferedIo()) {
+ return false;
+ }
+
+ 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 ReadWorker::ReadUnalignedSector(
+ sector_t sector, size_t size,
+ std::vector<std::pair<sector_t, const CowOperation*>>::iterator& it) {
+ SNAP_LOG(DEBUG) << "ReadUnalignedSector: sector " << sector << " size: " << size
+ << " Aligned sector: " << it->first;
+
+ 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*>(block_server_->GetResponseBuffer(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;
+ }
+
+ 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(buffer, buffer + skip_size, write_size);
+ }
+ return write_size;
+}
+
+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),
+ SnapshotHandler::compare);
+
+ // |-------|-------|-------|
+ // 0 1 2 3
+ //
+ // Block 0 - op 1
+ // Block 1 - op 2
+ // Block 2 - op 3
+ //
+ // chunk_vec will have block 0, 1, 2 which maps to relavant COW ops.
+ //
+ // Each block is 4k bytes. Thus, the last block will span 8 sectors
+ // ranging till block 3 (However, block 3 won't be in chunk_vec as
+ // it doesn't have any mapping to COW ops. Now, if we get an I/O request for a sector
+ // spanning between block 2 and block 3, we need to step back
+ // and get hold of the last element.
+ //
+ // Additionally, we need to make sure that the requested sector is
+ // indeed within the range of the final sector. It is perfectly valid
+ // to get an I/O request for block 3 and beyond which are not mapped
+ // to any COW ops. In that case, we just need to read from the base
+ // device.
+ bool merge_complete = false;
+ if (it == chunk_vec.end()) {
+ if (chunk_vec.size() > 0) {
+ // I/O request beyond the last mapped sector
+ it = std::prev(chunk_vec.end());
+ } else {
+ // This can happen when a partition merge is complete but snapshot
+ // state in /metadata is not yet deleted; during this window if the
+ // device is rebooted, subsequent attempt will mount the snapshot.
+ // However, since the merge was completed we wouldn't have any
+ // mapping to COW ops thus chunk_vec will be empty. In that case,
+ // mark this as merge_complete and route the I/O to the base device.
+ merge_complete = true;
+ }
+ } else if (it->first != sector) {
+ if (it != chunk_vec.begin()) {
+ --it;
+ }
+ } else {
+ return ReadAlignedSector(sector, size);
+ }
+
+ loff_t requested_offset = sector << SECTOR_SHIFT;
+
+ loff_t final_offset = 0;
+ if (!merge_complete) {
+ final_offset = it->first << SECTOR_SHIFT;
+ }
+
+ // Since a COW op span 4k block size, we need to make sure that the requested
+ // offset is within the 4k region. Consider the following case:
+ //
+ // |-------|-------|-------|
+ // 0 1 2 3
+ //
+ // Block 0 - op 1
+ // Block 1 - op 2
+ //
+ // We have an I/O request for a sector between block 2 and block 3. However,
+ // we have mapping to COW ops only for block 0 and block 1. Thus, the
+ // requested offset in this case is beyond the last mapped COW op size (which
+ // is block 1 in this case).
+
+ size_t remaining_size = size;
+ int ret = 0;
+ if (!merge_complete && (requested_offset >= final_offset) &&
+ (requested_offset - final_offset) < BLOCK_SZ) {
+ // Read the partial un-aligned data
+ ret = ReadUnalignedSector(sector, remaining_size, it);
+ if (ret < 0) {
+ SNAP_LOG(ERROR) << "ReadUnalignedSector failed for sector: " << sector
+ << " size: " << size << " it->sector: " << it->first;
+ return 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);
+ }
+ } else {
+ // This is all about handling I/O request to be routed to base device
+ // as the I/O is not mapped to any of the COW ops.
+ loff_t aligned_offset = requested_offset;
+ // Align to nearest 4k
+ aligned_offset += BLOCK_SZ - 1;
+ aligned_offset &= ~(BLOCK_SZ - 1);
+ // Find the diff of the aligned offset
+ size_t diff_size = aligned_offset - requested_offset;
+ CHECK(diff_size <= BLOCK_SZ);
+
+ size_t read_size = std::min(remaining_size, diff_size);
+ void* buffer = block_server_->GetResponseBuffer(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;
+ }
+
+ 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));
+
+ // If we still have pending data to be processed, this will be aligned I/O
+ return ReadAlignedSector(sector, remaining_size);
+ }
+ }
+
+ return true;
+}
+
+bool ReadWorker::RequestSectors(uint64_t sector, uint64_t len) {
+ // Unaligned I/O request
+ if (!IsBlockAligned(sector << SECTOR_SHIFT)) {
+ return ReadUnalignedSector(sector, len);
+ }
+
+ return ReadAlignedSector(sector, len);
+}
+
+bool ReadWorker::SendBufferedIo() {
+ return block_server_->SendBufferedIo();
+}
+
+} // namespace snapshot
+} // namespace android
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..6dbae81
--- /dev/null
+++ b/fs_mgr/libsnapshot/snapuserd/user-space-merge/read_worker.h
@@ -0,0 +1,70 @@
+// 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 <snapuserd/block_server.h>
+#include "worker.h"
+
+namespace android {
+namespace snapshot {
+
+class ReadWorker : public Worker, public IBlockServer::Delegate {
+ public:
+ ReadWorker(const std::string& cow_device, const std::string& backing_device,
+ const std::string& misc_name, const std::string& base_path_merge,
+ std::shared_ptr<SnapshotHandler> snapuserd,
+ std::shared_ptr<IBlockServerOpener> opener);
+
+ bool Run();
+ bool Init() override;
+ void CloseFds() override;
+ bool RequestSectors(uint64_t sector, uint64_t size) override;
+
+ IBlockServer* block_server() const { return block_server_.get(); }
+
+ private:
+ bool SendBufferedIo();
+
+ 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::shared_ptr<IBlockServerOpener> block_server_opener_;
+ std::unique_ptr<IBlockServer> block_server_;
+
+ 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 c8a5cc5..c295851 100644
--- a/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_core.cpp
+++ b/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_core.cpp
@@ -22,6 +22,10 @@
#include <android-base/properties.h>
#include <android-base/scopeguard.h>
#include <android-base/strings.h>
+#include <snapuserd/dm_user_block_server.h>
+
+#include "merge_worker.h"
+#include "read_worker.h"
namespace android {
namespace snapshot {
@@ -32,12 +36,12 @@
SnapshotHandler::SnapshotHandler(std::string misc_name, std::string cow_device,
std::string backing_device, std::string base_path_merge,
- int num_worker_threads, bool use_iouring,
- bool perform_verification) {
+ std::shared_ptr<IBlockServerOpener> opener, int num_worker_threads,
+ bool use_iouring, bool perform_verification) {
misc_name_ = std::move(misc_name);
cow_device_ = std::move(cow_device);
backing_store_device_ = std::move(backing_device);
- control_device_ = "/dev/dm-user/" + misc_name_;
+ block_server_opener_ = std::move(opener);
base_path_merge_ = std::move(base_path_merge);
num_worker_threads_ = num_worker_threads;
is_io_uring_enabled_ = use_iouring;
@@ -46,9 +50,9 @@
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_, misc_name_,
+ base_path_merge_, GetSharedPtr(),
+ block_server_opener_);
if (!wt->Init()) {
SNAP_LOG(ERROR) << "Thread initialization failed";
return false;
@@ -57,8 +61,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());
@@ -278,20 +282,6 @@
return false;
}
- unique_fd fd(TEMP_FAILURE_RETRY(open(base_path_merge_.c_str(), O_RDONLY | O_CLOEXEC)));
- if (fd < 0) {
- SNAP_LOG(ERROR) << "Cannot open block device";
- return false;
- }
-
- uint64_t dev_sz = get_block_device_size(fd.get());
- if (!dev_sz) {
- SNAP_LOG(ERROR) << "Failed to find block device size: " << base_path_merge_;
- return false;
- }
-
- num_sectors_ = dev_sz >> SECTOR_SHIFT;
-
return ReadMetadata();
}
@@ -305,18 +295,16 @@
if (ra_thread_) {
ra_thread_status =
std::async(std::launch::async, &ReadAhead::RunThread, read_ahead_thread_.get());
-
- 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_) {
@@ -421,11 +409,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";
@@ -457,5 +440,27 @@
return update_verify_->CheckPartitionVerification();
}
+void SnapshotHandler::FreeResources() {
+ worker_threads_.clear();
+ read_ahead_thread_ = nullptr;
+ merge_thread_ = nullptr;
+}
+
+uint64_t SnapshotHandler::GetNumSectors() const {
+ unique_fd fd(TEMP_FAILURE_RETRY(open(base_path_merge_.c_str(), O_RDONLY | O_CLOEXEC)));
+ if (fd < 0) {
+ SNAP_LOG(ERROR) << "Cannot open base path: " << base_path_merge_;
+ return false;
+ }
+
+ uint64_t dev_sz = get_block_device_size(fd.get());
+ if (!dev_sz) {
+ SNAP_LOG(ERROR) << "Failed to find block device size: " << base_path_merge_;
+ return false;
+ }
+
+ return dev_sz / SECTOR_SIZE;
+}
+
} // 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 c16ad24..622fc50 100644
--- a/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_core.h
+++ b/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_core.h
@@ -28,6 +28,7 @@
#include <iostream>
#include <limits>
#include <mutex>
+#include <ostream>
#include <string>
#include <thread>
#include <unordered_map>
@@ -43,6 +44,7 @@
#include <libsnapshot/cow_reader.h>
#include <libsnapshot/cow_writer.h>
#include <liburing.h>
+#include <snapuserd/block_server.h>
#include <snapuserd/snapuserd_buffer.h>
#include <snapuserd/snapuserd_kernel.h>
#include <storage_literals/storage_literals.h>
@@ -67,15 +69,17 @@
#define SNAP_PLOG(level) PLOG(level) << misc_name_ << ": "
enum class MERGE_IO_TRANSITION {
+ INVALID,
MERGE_READY,
MERGE_BEGIN,
MERGE_FAILED,
MERGE_COMPLETE,
IO_TERMINATED,
- READ_AHEAD_FAILURE,
+ READ_AHEAD_FAILURE
};
-class SnapshotHandler;
+class MergeWorker;
+class ReadWorker;
enum class MERGE_GROUP_STATE {
GROUP_MERGE_PENDING,
@@ -98,113 +102,17 @@
: merge_state_(state), num_ios_in_progress(n_ios) {}
};
-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,
- std::string base_path_merge, int num_workers, bool use_iouring,
- bool perform_verification);
+ std::string base_path_merge, std::shared_ptr<IBlockServerOpener> opener,
+ int num_workers, bool use_iouring, bool perform_verification);
bool InitCowDevice();
bool Start();
const std::string& GetControlDevicePath() { return control_device_; }
const std::string& GetMiscName() { return misc_name_; }
- const uint64_t& GetNumSectors() { return num_sectors_; }
+ uint64_t GetNumSectors() const;
const bool& IsAttached() const { return attached_; }
void AttachControlDevice() { attached_ = true; }
@@ -212,11 +120,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();
@@ -301,8 +205,6 @@
unique_fd cow_fd_;
- uint64_t num_sectors_;
-
std::unique_ptr<CowReader> reader_;
// chunk_vec stores the pseudo mapping of sector
@@ -315,12 +217,12 @@
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;
int total_ra_blocks_merged_ = 0;
- MERGE_IO_TRANSITION io_state_;
+ MERGE_IO_TRANSITION io_state_ = MERGE_IO_TRANSITION::INVALID;
std::unique_ptr<ReadAhead> read_ahead_thread_;
std::unordered_map<uint64_t, void*> read_ahead_buffer_map_;
@@ -330,7 +232,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;
@@ -343,7 +245,10 @@
std::unique_ptr<struct io_uring> ring_;
std::unique_ptr<UpdateVerify> update_verify_;
+ std::shared_ptr<IBlockServerOpener> block_server_opener_;
};
+std::ostream& operator<<(std::ostream& os, MERGE_IO_TRANSITION value);
+
} // 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/snapuserd_dm_user.cpp
deleted file mode 100644
index 7858216..0000000
--- a/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_dm_user.cpp
+++ /dev/null
@@ -1,668 +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 "snapuserd_core.h"
-
-namespace android {
-namespace snapshot {
-
-using namespace android;
-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;
-}
-
-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;
-}
-
-// 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) {
- void* buffer = bufsink_.GetPayloadBuffer(BLOCK_SZ);
- if (!buffer) {
- SNAP_LOG(ERROR) << "ProcessReplaceOp failed to allocate buffer";
- return false;
- }
- 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";
- 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;
- }
- if (!android::base::ReadFullyAtOffset(backing_store_fd_, buffer, BLOCK_SZ, offset)) {
- std::string op;
- if (cow_op->type == kCowCopyOp)
- op = "Copy-op";
- else {
- op = "Xor-op";
- }
- SNAP_PLOG(ERROR) << op << " failed. Read from backing store: " << backing_store_device_
- << "at block :" << offset / BLOCK_SZ << " offset:" << offset % BLOCK_SZ;
- return false;
- }
-
- return true;
-}
-
-// 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)) {
- return false;
- }
-
- return true;
-}
-
-bool Worker::ProcessXorOp(const CowOperation* cow_op) {
- if (!ReadFromSourceDevice(cow_op)) {
- return false;
- }
-
- xorsink_.Reset();
-
- size_t actual = 0;
- void* buffer = xorsink_.GetBuffer(BLOCK_SZ, &actual);
- if (!buffer || actual < BLOCK_SZ) {
- SNAP_LOG(ERROR) << "ProcessXorOp failed to get buffer of " << BLOCK_SZ << " size, got "
- << actual;
- return false;
- }
- ssize_t size = reader_->ReadData(cow_op, buffer, BLOCK_SZ);
- if (size != BLOCK_SZ) {
- SNAP_LOG(ERROR) << "ProcessXorOp failed for block " << cow_op->new_block
- << ", return value: " << size;
- return false;
- }
- if (!xorsink_.ReturnData(buffer, size)) {
- SNAP_LOG(ERROR) << "ProcessXorOp failed to return data";
- return false;
- }
- 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;
- }
-
- 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;
- }
-
- MERGE_GROUP_STATE state = snapuserd_->ProcessMergingBlock(cow_op->new_block, buffer);
-
- switch (state) {
- case MERGE_GROUP_STATE::GROUP_MERGE_COMPLETED: {
- // Merge is completed for this COW op; just read directly from
- // the base device
- 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)) {
- SNAP_LOG(ERROR) << "ReadDataFromBaseDevice at sector: "
- << (cow_op->new_block >> SECTOR_SHIFT) << " after merge-complete.";
- return false;
- }
- return true;
- }
- case MERGE_GROUP_STATE::GROUP_MERGE_PENDING: {
- bool ret;
- if (cow_op->type == kCowCopyOp) {
- ret = ProcessCopyOp(cow_op);
- } else {
- ret = ProcessXorOp(cow_op);
- }
-
- // I/O is complete - decrement the refcount irrespective of the return
- // status
- snapuserd_->NotifyIOCompletion(cow_op->new_block);
- return ret;
- }
- // We already have the data in the buffer retrieved from RA thread.
- // Nothing to process further.
- case MERGE_GROUP_STATE::GROUP_MERGE_RA_READY: {
- [[fallthrough]];
- }
- case MERGE_GROUP_STATE::GROUP_MERGE_IN_PROGRESS: {
- return true;
- }
- default: {
- // All other states, fail the I/O viz (GROUP_MERGE_FAILED and GROUP_INVALID)
- return false;
- }
- }
-
- return false;
-}
-
-bool Worker::ProcessCowOp(const CowOperation* cow_op) {
- if (cow_op == nullptr) {
- SNAP_LOG(ERROR) << "ProcessCowOp: Invalid cow_op";
- return false;
- }
-
- switch (cow_op->type) {
- case kCowReplaceOp: {
- return ProcessReplaceOp(cow_op);
- }
-
- case kCowZeroOp: {
- return ProcessZeroOp();
- }
-
- case kCowCopyOp:
- [[fallthrough]];
- case kCowXorOp: {
- return ProcessOrderedOp(cow_op);
- }
-
- default: {
- SNAP_LOG(ERROR) << "Unknown operation-type found: " << cow_op->type;
- }
- }
- 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()) {
- return false;
- }
-
- if (!InitReader()) {
- return false;
- }
-
- return true;
-}
-
-bool Worker::RunThread() {
- SNAP_LOG(INFO) << "Processing snapshot I/O requests....";
-
- if (setpriority(PRIO_PROCESS, gettid(), kNiceValueForMergeThreads)) {
- SNAP_PLOG(ERROR) << "Failed to set priority for TID: " << gettid();
- }
-
- // Start serving IO
- while (true) {
- if (!ProcessIORequest()) {
- break;
- }
- }
-
- CloseFds();
- reader_->CloseCowFd();
-
- 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) {
- size_t payload_size = size;
- void* buf = bufsink_.GetPayloadBufPtr();
- if (header_response) {
- payload_size += sizeof(struct dm_user_header);
- buf = bufsink_.GetBufPtr();
- }
-
- if (!android::base::WriteFully(ctrl_fd_, buf, payload_size)) {
- SNAP_PLOG(ERROR) << "Write to dm-user failed size: " << payload_size;
- return false;
- }
-
- return true;
-}
-
-bool Worker::ReadDataFromBaseDevice(sector_t sector, 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_
- << "at sector :" << sector << " size: " << read_size;
- return false;
- }
-
- return true;
-}
-
-bool Worker::ReadAlignedSector(sector_t sector, size_t sz, bool header_response) {
- struct dm_user_header* header = bufsink_.GetHeaderPtr();
- 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
- // present in the mapping.
- size_t size = std::min(BLOCK_SZ, read_size);
-
- auto it = std::lower_bound(chunk_vec.begin(), chunk_vec.end(),
- std::make_pair(sector, nullptr), SnapshotHandler::compare);
- bool not_found = (it == chunk_vec.end() || it->first != sector);
-
- 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)) {
- SNAP_LOG(ERROR) << "ReadDataFromBaseDevice failed";
- header->type = DM_USER_RESP_ERROR;
- }
-
- ret = size;
- } else {
- // We found the sector in mapping. Check the type of COW OP and
- // process it.
- if (!ProcessCowOp(it->second)) {
- 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;
- }
-
- 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;
- }
- } while (remaining_size > 0 && !io_error);
-
- return true;
-}
-
-int Worker::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)) {
- 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) {
- 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));
- }
-
- bufsink_.ResetBufferOffset();
- return std::min(size, (BLOCK_SZ - skip_sector_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();
- 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),
- SnapshotHandler::compare);
-
- // |-------|-------|-------|
- // 0 1 2 3
- //
- // Block 0 - op 1
- // Block 1 - op 2
- // Block 2 - op 3
- //
- // chunk_vec will have block 0, 1, 2 which maps to relavant COW ops.
- //
- // Each block is 4k bytes. Thus, the last block will span 8 sectors
- // ranging till block 3 (However, block 3 won't be in chunk_vec as
- // it doesn't have any mapping to COW ops. Now, if we get an I/O request for a sector
- // spanning between block 2 and block 3, we need to step back
- // and get hold of the last element.
- //
- // Additionally, we need to make sure that the requested sector is
- // indeed within the range of the final sector. It is perfectly valid
- // to get an I/O request for block 3 and beyond which are not mapped
- // 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
- it = std::prev(chunk_vec.end());
- } else {
- // This can happen when a partition merge is complete but snapshot
- // state in /metadata is not yet deleted; during this window if the
- // device is rebooted, subsequent attempt will mount the snapshot.
- // However, since the merge was completed we wouldn't have any
- // mapping to COW ops thus chunk_vec will be empty. In that case,
- // mark this as merge_complete and route the I/O to the base device.
- merge_complete = true;
- }
- } else if (it->first != sector) {
- if (it != chunk_vec.begin()) {
- --it;
- }
- } else {
- return ReadAlignedSector(sector, size, header_response);
- }
-
- loff_t requested_offset = sector << SECTOR_SHIFT;
-
- loff_t final_offset = 0;
- if (!merge_complete) {
- final_offset = it->first << SECTOR_SHIFT;
- }
-
- // Since a COW op span 4k block size, we need to make sure that the requested
- // offset is within the 4k region. Consider the following case:
- //
- // |-------|-------|-------|
- // 0 1 2 3
- //
- // Block 0 - op 1
- // Block 1 - op 2
- //
- // We have an I/O request for a sector between block 2 and block 3. However,
- // we have mapping to COW ops only for block 0 and block 1. Thus, the
- // 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) &&
- (requested_offset - final_offset) < BLOCK_SZ) {
- // Read the partial un-aligned data
- ret = ReadUnalignedSector(sector, remaining_size, it);
- 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;
- // 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);
- }
- } else {
- // This is all about handling I/O request to be routed to base device
- // as the I/O is not mapped to any of the COW ops.
- loff_t aligned_offset = requested_offset;
- // Align to nearest 4k
- aligned_offset += BLOCK_SZ - 1;
- aligned_offset &= ~(BLOCK_SZ - 1);
- // 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);
- }
-
- total_bytes_read += diff_size;
-
- if (!WriteDmUserPayload(total_bytes_read, header_response)) {
- return false;
- }
-
- 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 true;
-}
-
-bool Worker::RespondIOError(bool header_response) {
- struct dm_user_header* header = bufsink_.GetHeaderPtr();
- header->type = DM_USER_RESP_ERROR;
- // This is an issue with the dm-user interface. There
- // is no way to propagate the I/O error back to dm-user
- // if we have already communicated the header back. Header
- // is responded once at the beginning; however I/O can
- // be processed in chunks. If we encounter an I/O error
- // somewhere in the middle of the processing, we can't communicate
- // this back to dm-user.
- //
- // TODO: Fix the interface
- 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;
-}
-
-bool Worker::DmuserReadRequest() {
- struct dm_user_header* header = bufsink_.GetHeaderPtr();
-
- // Unaligned I/O request
- if (!IsBlockAligned(header->sector << SECTOR_SHIFT)) {
- return ReadUnalignedSector(header->sector, header->len);
- }
-
- return ReadAlignedSector(header->sector, header->len, true);
-}
-
-bool Worker::ProcessIORequest() {
- struct dm_user_header* header = bufsink_.GetHeaderPtr();
-
- if (!ReadDmUserHeader()) {
- return false;
- }
-
- SNAP_LOG(DEBUG) << "Daemon: msg->seq: " << std::dec << header->seq;
- SNAP_LOG(DEBUG) << "Daemon: msg->len: " << std::dec << header->len;
- SNAP_LOG(DEBUG) << "Daemon: msg->sector: " << std::dec << header->sector;
- 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;
- }
-
- 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;
- }
- }
-
- return true;
-}
-
-} // 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 af24286..d2128c5 100644
--- a/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_readahead.cpp
+++ b/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_readahead.cpp
@@ -16,7 +16,10 @@
#include "snapuserd_readahead.h"
+#include <pthread.h>
+
#include "snapuserd_core.h"
+#include "utility.h"
namespace android {
namespace snapshot {
@@ -34,14 +37,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;
}
@@ -66,11 +72,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);
}
@@ -88,10 +95,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
@@ -423,7 +430,7 @@
// will fallback to synchronous I/O.
int ret = io_uring_wait_cqe(ring_.get(), &cqe);
if (ret) {
- SNAP_LOG(ERROR) << "Read-ahead - io_uring_wait_cqe failed: " << ret;
+ SNAP_LOG(ERROR) << "Read-ahead - io_uring_wait_cqe failed: " << strerror(-ret);
status = false;
break;
}
@@ -488,7 +495,7 @@
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) {
- void* buffer = bufsink_.GetPayloadBuffer(BLOCK_SZ);
+ void* buffer = bufsink_.AcquireBuffer(BLOCK_SZ);
if (!buffer) {
SNAP_LOG(ERROR) << "ReadAhead - failed to allocate buffer for block: "
<< xor_op->new_block;
@@ -502,7 +509,6 @@
}
xor_op_index += 1;
- bufsink_.UpdateBufferOffset(BLOCK_SZ);
}
}
block_index += 1;
@@ -589,7 +595,7 @@
// Check if this block is an XOR op
if (xor_op->new_block == new_block) {
// Read the xor'ed data from COW
- void* buffer = bufsink_.GetPayloadBuffer(BLOCK_SZ);
+ void* buffer = bufsink.GetPayloadBuffer(BLOCK_SZ);
if (!buffer) {
SNAP_LOG(ERROR) << "ReadAhead - failed to allocate buffer";
return false;
@@ -687,6 +693,7 @@
// window. If there is a crash during this time frame, merge should resume
// based on the contents of the scratch space.
if (!snapuserd_->WaitForMergeReady()) {
+ SNAP_LOG(ERROR) << "ReadAhead failed to wait for merge ready";
return false;
}
@@ -748,6 +755,10 @@
}
bool ReadAhead::RunThread() {
+ SNAP_LOG(INFO) << "ReadAhead thread started.";
+
+ pthread_setname_np(pthread_self(), "ReadAhead");
+
if (!InitializeFds()) {
return false;
}
@@ -762,10 +773,11 @@
InitializeIouring();
- if (setpriority(PRIO_PROCESS, gettid(), kNiceValueForMergeThreads)) {
- SNAP_PLOG(ERROR) << "Failed to set priority for TID: " << gettid();
+ if (!SetThreadPriority(kNiceValueForMergeThreads)) {
+ SNAP_PLOG(ERROR) << "Failed to set thread priority";
}
+ SNAP_LOG(INFO) << "ReadAhead processing.";
while (!RAIterDone()) {
if (!ReadAheadIOStart()) {
break;
@@ -776,7 +788,7 @@
CloseFds();
reader_->CloseCowFd();
- SNAP_LOG(INFO) << " ReadAhead thread terminating....";
+ SNAP_LOG(INFO) << " ReadAhead thread terminating.";
return true;
}
@@ -803,6 +815,7 @@
if (!reader_->InitForMerge(std::move(cow_fd_))) {
return false;
}
+ header_ = reader_->GetHeader();
return true;
}
diff --git a/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_readahead.h b/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_readahead.h
index 5e94de0..d3ba126 100644
--- a/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_readahead.h
+++ b/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_readahead.h
@@ -85,6 +85,7 @@
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_;
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..13b9a00 100644
--- a/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_server.cpp
+++ b/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_server.cpp
@@ -31,6 +31,7 @@
#include <android-base/scopeguard.h>
#include <android-base/strings.h>
#include <fs_mgr/file_wait.h>
+#include <snapuserd/dm_user_block_server.h>
#include <snapuserd/snapuserd_client.h>
#include "snapuserd_server.h"
@@ -45,28 +46,10 @@
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>();
+ block_server_factory_ = std::make_unique<DmUserBlockServerFactory>();
}
UserSnapshotServer::~UserSnapshotServer() {
@@ -99,12 +82,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 +115,123 @@
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 num_sectors = handler->snapuserd()->GetNumSectors();
+ if (!num_sectors) {
+ 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);
- }
+ auto retval = "success," + std::to_string(num_sectors);
+ 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::UPDATE_VERIFY: {
- std::lock_guard<std::mutex> lock(lock_);
- if (!UpdateVerification(&lock)) {
- return Sendmsg(fd, "fail");
- }
+ if (!handlers_->StartHandler(out[1])) {
+ 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 +300,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 +365,16 @@
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;
- }
+ auto opener = block_server_factory_->CreateOpener(misc_name);
- 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,
+ opener, 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 +471,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..be28541 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,8 @@
#include <vector>
#include <android-base/unique_fd.h>
+#include <snapuserd/block_server.h>
+#include "handler_manager.h"
#include "snapuserd_core.h"
namespace android {
@@ -39,48 +41,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 +48,13 @@
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::unique_ptr<IBlockServerFactory> block_server_factory_;
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 +63,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 +85,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 ef3689f..01fe06f 100644
--- a/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_test.cpp
+++ b/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_test.cpp
@@ -17,7 +17,6 @@
#include <fcntl.h>
#include <linux/fs.h>
-#include <linux/memfd.h>
#include <sys/ioctl.h>
#include <sys/stat.h>
#include <sys/syscall.h>
@@ -37,10 +36,16 @@
#include <libdm/dm.h>
#include <libdm/loop_control.h>
#include <libsnapshot/cow_writer.h>
-#include <snapuserd/snapuserd_client.h>
+#include <snapuserd/dm_user_block_server.h>
#include <storage_literals/storage_literals.h>
-
+#include "handler_manager.h"
+#include "merge_worker.h"
+#include "read_worker.h"
#include "snapuserd_core.h"
+#include "testing/dm_user_harness.h"
+#include "testing/host_harness.h"
+#include "testing/temp_device.h"
+#include "utility.h"
DEFINE_string(force_config, "", "Force testing mode with iouring disabled");
@@ -53,205 +58,54 @@
using namespace std::chrono_literals;
using namespace android::dm;
using namespace std;
+using testing::AssertionFailure;
+using testing::AssertionResult;
+using testing::AssertionSuccess;
-static constexpr char kSnapuserdSocketTest[] = "snapuserdTest";
-
-class Tempdevice {
- public:
- Tempdevice(const std::string& name, const DmTable& table)
- : dm_(DeviceMapper::Instance()), name_(name), valid_(false) {
- valid_ = dm_.CreateDevice(name, table, &path_, std::chrono::seconds(5));
- }
- Tempdevice(Tempdevice&& other) noexcept
- : dm_(other.dm_), name_(other.name_), path_(other.path_), valid_(other.valid_) {
- other.valid_ = false;
- }
- ~Tempdevice() {
- if (valid_) {
- dm_.DeleteDevice(name_);
- }
- }
- bool Destroy() {
- if (!valid_) {
- return false;
- }
- valid_ = false;
- return dm_.DeleteDevice(name_);
- }
- const std::string& path() const { return path_; }
- const std::string& name() const { return name_; }
- bool valid() const { return valid_; }
-
- Tempdevice(const Tempdevice&) = delete;
- Tempdevice& operator=(const Tempdevice&) = delete;
-
- Tempdevice& operator=(Tempdevice&& other) noexcept {
- name_ = other.name_;
- valid_ = other.valid_;
- other.valid_ = false;
- return *this;
- }
-
- private:
- DeviceMapper& dm_;
- std::string name_;
- std::string path_;
- bool valid_;
-};
-
-class SnapuserdTest : public ::testing::Test {
- public:
- bool SetupDefault();
- bool SetupOrderedOps();
- bool SetupOrderedOpsInverted();
- bool SetupCopyOverlap_1();
- bool SetupCopyOverlap_2();
- bool Merge();
- void ValidateMerge();
- void ReadSnapshotDeviceAndValidate();
- void Shutdown();
- void MergeInterrupt();
- void MergeInterruptFixed(int duration);
- void MergeInterruptRandomly(int max_duration);
- void StartMerge();
- void CheckMergeCompletion();
-
- static const uint64_t kSectorSize = 512;
-
+class SnapuserdTestBase : public ::testing::Test {
protected:
- void SetUp() override {}
- void TearDown() override { Shutdown(); }
-
- private:
- void SetupImpl();
-
- void SimulateDaemonRestart();
-
- std::unique_ptr<ICowWriter> CreateCowDeviceInternal();
- void CreateCowDevice();
- void CreateCowDeviceOrderedOps();
- void CreateCowDeviceOrderedOpsInverted();
- void CreateCowDeviceWithCopyOverlap_1();
- void CreateCowDeviceWithCopyOverlap_2();
- bool SetupDaemon();
+ void SetUp() override;
+ void TearDown() override;
void CreateBaseDevice();
- void InitCowDevice();
+ void CreateCowDevice();
void SetDeviceControlName();
- void InitDaemon();
- void CreateDmUserDevice();
- void StartSnapuserdDaemon();
+ std::unique_ptr<ICowWriter> CreateCowDeviceInternal();
- unique_ptr<LoopDevice> base_loop_;
- unique_ptr<Tempdevice> dmuser_dev_;
-
+ std::unique_ptr<ITestHarness> harness_;
+ size_t size_ = 10_MiB;
+ int total_base_size_ = 0;
std::string system_device_ctrl_name_;
std::string system_device_name_;
+ unique_ptr<IBackingDevice> base_dev_;
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_;
- bool setup_ok_ = false;
- bool merge_ok_ = false;
- size_t size_ = 100_MiB;
- int cow_num_sectors_;
- int total_base_size_;
};
-static unique_fd CreateTempFile(const std::string& name, size_t size) {
- unique_fd fd(syscall(__NR_memfd_create, name.c_str(), MFD_ALLOW_SEALING));
- if (fd < 0) {
- return {};
- }
- if (size) {
- if (ftruncate(fd, size) < 0) {
- perror("ftruncate");
- return {};
- }
- if (fcntl(fd, F_ADD_SEALS, F_SEAL_GROW | F_SEAL_SHRINK) < 0) {
- perror("fcntl");
- return {};
- }
- }
- return fd;
+void SnapuserdTestBase::SetUp() {
+#if __ANDROID__
+ harness_ = std::make_unique<DmUserTestHarness>();
+#else
+ harness_ = std::make_unique<HostTestHarness>();
+#endif
}
-void SnapuserdTest::Shutdown() {
- ASSERT_TRUE(dmuser_dev_->Destroy());
+void SnapuserdTestBase::TearDown() {}
- auto misc_device = "/dev/dm-user/" + system_device_ctrl_name_;
- ASSERT_TRUE(client_->WaitForDeviceDelete(system_device_ctrl_name_));
- ASSERT_TRUE(android::fs_mgr::WaitForFileDeleted(misc_device, 10s));
- ASSERT_TRUE(client_->DetachSnapuserd());
-}
-
-bool SnapuserdTest::SetupDefault() {
- SetupImpl();
- return setup_ok_;
-}
-
-bool SnapuserdTest::SetupOrderedOps() {
- CreateBaseDevice();
- CreateCowDeviceOrderedOps();
- return SetupDaemon();
-}
-
-bool SnapuserdTest::SetupOrderedOpsInverted() {
- CreateBaseDevice();
- CreateCowDeviceOrderedOpsInverted();
- return SetupDaemon();
-}
-
-bool SnapuserdTest::SetupCopyOverlap_1() {
- CreateBaseDevice();
- CreateCowDeviceWithCopyOverlap_1();
- return SetupDaemon();
-}
-
-bool SnapuserdTest::SetupCopyOverlap_2() {
- CreateBaseDevice();
- CreateCowDeviceWithCopyOverlap_2();
- return SetupDaemon();
-}
-
-bool SnapuserdTest::SetupDaemon() {
- SetDeviceControlName();
-
- StartSnapuserdDaemon();
-
- CreateDmUserDevice();
- InitCowDevice();
- InitDaemon();
-
- setup_ok_ = true;
-
- 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;
-
+void SnapuserdTestBase::CreateBaseDevice() {
total_base_size_ = (size_ * 5);
- base_fd_ = CreateTempFile("base_device", total_base_size_);
+
+ base_dev_ = harness_->CreateBackingDevice(total_base_size_);
+ ASSERT_NE(base_dev_, nullptr);
+
+ base_fd_.reset(open(base_dev_->GetPath().c_str(), O_RDWR | O_CLOEXEC));
ASSERT_GE(base_fd_, 0);
- rnd_fd.reset(open("/dev/random", O_RDONLY));
- ASSERT_TRUE(rnd_fd > 0);
+ unique_fd rnd_fd(open("/dev/random", O_RDONLY));
+ ASSERT_GE(rnd_fd, 0);
std::unique_ptr<uint8_t[]> random_buffer = std::make_unique<uint8_t[]>(1_MiB);
@@ -261,13 +115,220 @@
}
ASSERT_EQ(lseek(base_fd_, 0, SEEK_SET), 0);
+}
- base_loop_ = std::make_unique<LoopDevice>(base_fd_, 10s);
- ASSERT_TRUE(base_loop_->valid());
+std::unique_ptr<ICowWriter> SnapuserdTestBase::CreateCowDeviceInternal() {
+ cow_system_ = std::make_unique<TemporaryFile>();
+
+ CowOptions options;
+ options.compression = "gz";
+
+ unique_fd fd(cow_system_->fd);
+ cow_system_->fd = -1;
+
+ return CreateCowWriter(kDefaultCowVersion, options, std::move(fd));
+}
+
+void SnapuserdTestBase::CreateCowDevice() {
+ unique_fd rnd_fd;
+ loff_t offset = 0;
+
+ auto writer = CreateCowDeviceInternal();
+ ASSERT_NE(writer, nullptr);
+
+ rnd_fd.reset(open("/dev/random", O_RDONLY));
+ ASSERT_TRUE(rnd_fd > 0);
+
+ std::unique_ptr<uint8_t[]> random_buffer_1_ = std::make_unique<uint8_t[]>(size_);
+
+ // Fill random data
+ for (size_t j = 0; j < (size_ / 1_MiB); j++) {
+ ASSERT_EQ(ReadFullyAtOffset(rnd_fd, (char*)random_buffer_1_.get() + offset, 1_MiB, 0),
+ true);
+
+ offset += 1_MiB;
+ }
+
+ 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;
+
+ uint32_t sequence[num_blocks * 2];
+ // Sequence for Copy ops
+ for (int i = 0; i < num_blocks; i++) {
+ sequence[i] = num_blocks - 1 - i;
+ }
+ // Sequence for Xor ops
+ 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));
+
+ size_t x = num_blocks;
+ while (1) {
+ ASSERT_TRUE(writer->AddCopy(source_blk, blk_src_copy));
+ x -= 1;
+ if (x == 0) {
+ break;
+ }
+ source_blk -= 1;
+ blk_src_copy -= 1;
+ }
+
+ source_blk = num_blocks;
+ blk_src_copy = blk_end_copy;
+
+ 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));
+
+ size_t blk_random2_replace_start = blk_zero_copy_end;
+
+ 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));
+
+ // Flush operations
+ 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);
+ ASSERT_EQ(android::base::ReadFullyAtOffset(base_fd_, orig_buffer_.get(), size_, size_), true);
+ memcpy((char*)orig_buffer_.get() + size_, random_buffer_1_.get(), size_);
+ memcpy((char*)orig_buffer_.get() + (size_ * 2), (void*)zero_buffer.c_str(), size_);
+ memcpy((char*)orig_buffer_.get() + (size_ * 3), random_buffer_1_.get(), size_);
+ ASSERT_EQ(android::base::ReadFullyAtOffset(base_fd_, &orig_buffer_.get()[size_ * 4], size_,
+ size_ + xor_offset),
+ true);
+ for (int i = 0; i < size_; i++) {
+ orig_buffer_.get()[(size_ * 4) + i] =
+ (uint8_t)(orig_buffer_.get()[(size_ * 4) + i] ^ random_buffer_1_.get()[i]);
+ }
+}
+
+void SnapuserdTestBase::SetDeviceControlName() {
+ system_device_name_.clear();
+ system_device_ctrl_name_.clear();
+
+ std::string str(cow_system_->path);
+ std::size_t found = str.find_last_of("/\\");
+ ASSERT_NE(found, std::string::npos);
+ system_device_name_ = str.substr(found + 1);
+
+ system_device_ctrl_name_ = system_device_name_ + "-ctrl";
+}
+
+class SnapuserdTest : public SnapuserdTestBase {
+ public:
+ void SetupDefault();
+ void SetupOrderedOps();
+ void SetupOrderedOpsInverted();
+ void SetupCopyOverlap_1();
+ void SetupCopyOverlap_2();
+ bool Merge();
+ void ValidateMerge();
+ void ReadSnapshotDeviceAndValidate();
+ void Shutdown();
+ void MergeInterrupt();
+ void MergeInterruptFixed(int duration);
+ void MergeInterruptRandomly(int max_duration);
+ bool StartMerge();
+ void CheckMergeCompletion();
+
+ static const uint64_t kSectorSize = 512;
+
+ protected:
+ void SetUp() override;
+ void TearDown() override;
+
+ void SetupImpl();
+
+ void SimulateDaemonRestart();
+
+ void CreateCowDeviceOrderedOps();
+ void CreateCowDeviceOrderedOpsInverted();
+ void CreateCowDeviceWithCopyOverlap_1();
+ void CreateCowDeviceWithCopyOverlap_2();
+ void SetupDaemon();
+ void InitCowDevice();
+ void InitDaemon();
+ void CreateUserDevice();
+
+ unique_ptr<IUserDevice> dmuser_dev_;
+
+ std::unique_ptr<uint8_t[]> merged_buffer_;
+ std::unique_ptr<SnapshotHandlerManager> handlers_;
+ int cow_num_sectors_;
+};
+
+void SnapuserdTest::SetUp() {
+ ASSERT_NO_FATAL_FAILURE(SnapuserdTestBase::SetUp());
+ handlers_ = std::make_unique<SnapshotHandlerManager>();
+}
+
+void SnapuserdTest::TearDown() {
+ SnapuserdTestBase::TearDown();
+ Shutdown();
+}
+
+void SnapuserdTest::Shutdown() {
+ if (dmuser_dev_) {
+ ASSERT_TRUE(dmuser_dev_->Destroy());
+ }
+
+ auto misc_device = "/dev/dm-user/" + system_device_ctrl_name_;
+ ASSERT_TRUE(handlers_->DeleteHandler(system_device_ctrl_name_));
+ ASSERT_TRUE(android::fs_mgr::WaitForFileDeleted(misc_device, 10s));
+ handlers_->TerminateMergeThreads();
+ handlers_->JoinAllThreads();
+ handlers_ = std::make_unique<SnapshotHandlerManager>();
+}
+
+void SnapuserdTest::SetupDefault() {
+ ASSERT_NO_FATAL_FAILURE(SetupImpl());
+}
+
+void SnapuserdTest::SetupOrderedOps() {
+ ASSERT_NO_FATAL_FAILURE(CreateBaseDevice());
+ ASSERT_NO_FATAL_FAILURE(CreateCowDeviceOrderedOps());
+ ASSERT_NO_FATAL_FAILURE(SetupDaemon());
+}
+
+void SnapuserdTest::SetupOrderedOpsInverted() {
+ ASSERT_NO_FATAL_FAILURE(CreateBaseDevice());
+ ASSERT_NO_FATAL_FAILURE(CreateCowDeviceOrderedOpsInverted());
+ ASSERT_NO_FATAL_FAILURE(SetupDaemon());
+}
+
+void SnapuserdTest::SetupCopyOverlap_1() {
+ ASSERT_NO_FATAL_FAILURE(CreateBaseDevice());
+ ASSERT_NO_FATAL_FAILURE(CreateCowDeviceWithCopyOverlap_1());
+ ASSERT_NO_FATAL_FAILURE(SetupDaemon());
+}
+
+void SnapuserdTest::SetupCopyOverlap_2() {
+ ASSERT_NO_FATAL_FAILURE(CreateBaseDevice());
+ ASSERT_NO_FATAL_FAILURE(CreateCowDeviceWithCopyOverlap_2());
+ ASSERT_NO_FATAL_FAILURE(SetupDaemon());
+}
+
+void SnapuserdTest::SetupDaemon() {
+ SetDeviceControlName();
+
+ ASSERT_NO_FATAL_FAILURE(CreateUserDevice());
+ ASSERT_NO_FATAL_FAILURE(InitCowDevice());
+ ASSERT_NO_FATAL_FAILURE(InitDaemon());
}
void SnapuserdTest::ReadSnapshotDeviceAndValidate() {
- unique_fd fd(open(dmuser_dev_->path().c_str(), O_RDONLY));
+ unique_fd fd(open(dmuser_dev_->GetPath().c_str(), O_RDONLY));
ASSERT_GE(fd, 0);
std::unique_ptr<uint8_t[]> snapuserd_buffer = std::make_unique<uint8_t[]>(size_);
@@ -297,19 +358,6 @@
ASSERT_EQ(memcmp(snapuserd_buffer.get(), (char*)orig_buffer_.get() + (size_ * 4), size_), 0);
}
-std::unique_ptr<ICowWriter> SnapuserdTest::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 SnapuserdTest::CreateCowDeviceWithCopyOverlap_2() {
auto writer = CreateCowDeviceInternal();
ASSERT_NE(writer, nullptr);
@@ -506,138 +554,42 @@
}
}
-void SnapuserdTest::CreateCowDevice() {
- unique_fd rnd_fd;
- loff_t offset = 0;
-
- auto writer = CreateCowDeviceInternal();
- ASSERT_NE(writer, nullptr);
-
- rnd_fd.reset(open("/dev/random", O_RDONLY));
- ASSERT_TRUE(rnd_fd > 0);
-
- std::unique_ptr<uint8_t[]> random_buffer_1_ = std::make_unique<uint8_t[]>(size_);
-
- // Fill random data
- for (size_t j = 0; j < (size_ / 1_MiB); j++) {
- ASSERT_EQ(ReadFullyAtOffset(rnd_fd, (char*)random_buffer_1_.get() + offset, 1_MiB, 0),
- true);
-
- offset += 1_MiB;
- }
-
- 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;
-
- uint32_t sequence[num_blocks * 2];
- // Sequence for Copy ops
- for (int i = 0; i < num_blocks; i++) {
- sequence[i] = num_blocks - 1 - i;
- }
- // Sequence for Xor ops
- 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));
-
- size_t x = num_blocks;
- while (1) {
- ASSERT_TRUE(writer->AddCopy(source_blk, blk_src_copy));
- x -= 1;
- if (x == 0) {
- break;
- }
- source_blk -= 1;
- blk_src_copy -= 1;
- }
-
- source_blk = num_blocks;
- blk_src_copy = blk_end_copy;
-
- 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));
-
- size_t blk_random2_replace_start = blk_zero_copy_end;
-
- 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));
-
- // Flush operations
- 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);
- ASSERT_EQ(android::base::ReadFullyAtOffset(base_fd_, orig_buffer_.get(), size_, size_), true);
- memcpy((char*)orig_buffer_.get() + size_, random_buffer_1_.get(), size_);
- memcpy((char*)orig_buffer_.get() + (size_ * 2), (void*)zero_buffer.c_str(), size_);
- memcpy((char*)orig_buffer_.get() + (size_ * 3), random_buffer_1_.get(), size_);
- ASSERT_EQ(android::base::ReadFullyAtOffset(base_fd_, &orig_buffer_.get()[size_ * 4], size_,
- size_ + xor_offset),
- true);
- for (int i = 0; i < size_; i++) {
- orig_buffer_.get()[(size_ * 4) + i] =
- (uint8_t)(orig_buffer_.get()[(size_ * 4) + i] ^ random_buffer_1_.get()[i]);
- }
-}
-
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 factory = harness_->GetBlockServerFactory();
+ auto opener = factory->CreateOpener(system_device_ctrl_name_);
+ auto handler =
+ handlers_->AddHandler(system_device_ctrl_name_, cow_system_->path, base_dev_->GetPath(),
+ base_dev_->GetPath(), opener, 1, use_iouring, false);
+ ASSERT_NE(handler, nullptr);
+ ASSERT_NE(handler->snapuserd(), nullptr);
+#ifdef __ANDROID__
+ ASSERT_NE(handler->snapuserd()->GetNumSectors(), 0);
+#endif
}
-void SnapuserdTest::SetDeviceControlName() {
- system_device_name_.clear();
- system_device_ctrl_name_.clear();
-
- std::string str(cow_system_->path);
- std::size_t found = str.find_last_of("/\\");
- ASSERT_NE(found, std::string::npos);
- system_device_name_ = str.substr(found + 1);
-
- system_device_ctrl_name_ = system_device_name_ + "-ctrl";
-}
-
-void SnapuserdTest::CreateDmUserDevice() {
- unique_fd fd(TEMP_FAILURE_RETRY(open(base_loop_->device().c_str(), O_RDONLY | O_CLOEXEC)));
- ASSERT_TRUE(fd > 0);
-
- uint64_t dev_sz = get_block_device_size(fd.get());
- ASSERT_TRUE(dev_sz > 0);
+void SnapuserdTest::CreateUserDevice() {
+ auto dev_sz = base_dev_->GetSize();
+ ASSERT_NE(dev_sz, 0);
cow_num_sectors_ = dev_sz >> 9;
- DmTable dmuser_table;
- ASSERT_TRUE(dmuser_table.AddTarget(
- std::make_unique<DmTargetUser>(0, cow_num_sectors_, system_device_ctrl_name_)));
- ASSERT_TRUE(dmuser_table.valid());
-
- dmuser_dev_ = std::make_unique<Tempdevice>(system_device_name_, dmuser_table);
- ASSERT_TRUE(dmuser_dev_->valid());
- ASSERT_FALSE(dmuser_dev_->path().empty());
-
- auto misc_device = "/dev/dm-user/" + system_device_ctrl_name_;
- ASSERT_TRUE(android::fs_mgr::WaitForFile(misc_device, 10s));
+ dmuser_dev_ = harness_->CreateUserDevice(system_device_name_, system_device_ctrl_name_,
+ cow_num_sectors_);
+ ASSERT_NE(dmuser_dev_, nullptr);
}
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;
}
@@ -647,30 +599,26 @@
}
void SnapuserdTest::SetupImpl() {
- CreateBaseDevice();
- CreateCowDevice();
+ ASSERT_NO_FATAL_FAILURE(CreateBaseDevice());
+ ASSERT_NO_FATAL_FAILURE(CreateCowDevice());
SetDeviceControlName();
- StartSnapuserdDaemon();
-
- CreateDmUserDevice();
- InitCowDevice();
- InitDaemon();
-
- setup_ok_ = true;
+ ASSERT_NO_FATAL_FAILURE(CreateUserDevice());
+ ASSERT_NO_FATAL_FAILURE(InitCowDevice());
+ ASSERT_NO_FATAL_FAILURE(InitDaemon());
}
bool SnapuserdTest::Merge() {
- StartMerge();
+ if (!StartMerge()) {
+ return false;
+ }
CheckMergeCompletion();
- merge_ok_ = true;
- return merge_ok_;
+ return true;
}
-void SnapuserdTest::StartMerge() {
- bool ok = client_->InitiateMerge(system_device_ctrl_name_);
- ASSERT_TRUE(ok);
+bool SnapuserdTest::StartMerge() {
+ return handlers_->InitiateMerge(system_device_ctrl_name_);
}
void SnapuserdTest::ValidateMerge() {
@@ -681,159 +629,260 @@
}
void SnapuserdTest::SimulateDaemonRestart() {
- Shutdown();
+ ASSERT_NO_FATAL_FAILURE(Shutdown());
std::this_thread::sleep_for(500ms);
SetDeviceControlName();
- StartSnapuserdDaemon();
- CreateDmUserDevice();
- InitCowDevice();
- InitDaemon();
+ ASSERT_NO_FATAL_FAILURE(CreateUserDevice());
+ ASSERT_NO_FATAL_FAILURE(InitCowDevice());
+ ASSERT_NO_FATAL_FAILURE(InitDaemon());
}
void SnapuserdTest::MergeInterruptRandomly(int max_duration) {
std::srand(std::time(nullptr));
- StartMerge();
+ ASSERT_TRUE(StartMerge());
for (int i = 0; i < 20; i++) {
int duration = std::rand() % max_duration;
std::this_thread::sleep_for(std::chrono::milliseconds(duration));
- SimulateDaemonRestart();
- StartMerge();
+ ASSERT_NO_FATAL_FAILURE(SimulateDaemonRestart());
+ ASSERT_TRUE(StartMerge());
}
- SimulateDaemonRestart();
+ ASSERT_NO_FATAL_FAILURE(SimulateDaemonRestart());
ASSERT_TRUE(Merge());
}
void SnapuserdTest::MergeInterruptFixed(int duration) {
- StartMerge();
+ ASSERT_TRUE(StartMerge());
for (int i = 0; i < 25; i++) {
std::this_thread::sleep_for(std::chrono::milliseconds(duration));
- SimulateDaemonRestart();
- StartMerge();
+ ASSERT_NO_FATAL_FAILURE(SimulateDaemonRestart());
+ ASSERT_TRUE(StartMerge());
}
- SimulateDaemonRestart();
+ ASSERT_NO_FATAL_FAILURE(SimulateDaemonRestart());
ASSERT_TRUE(Merge());
}
void SnapuserdTest::MergeInterrupt() {
// Interrupt merge at various intervals
- StartMerge();
+ ASSERT_TRUE(StartMerge());
std::this_thread::sleep_for(250ms);
- SimulateDaemonRestart();
+ ASSERT_NO_FATAL_FAILURE(SimulateDaemonRestart());
- StartMerge();
+ ASSERT_TRUE(StartMerge());
std::this_thread::sleep_for(250ms);
- SimulateDaemonRestart();
+ ASSERT_NO_FATAL_FAILURE(SimulateDaemonRestart());
- StartMerge();
+ ASSERT_TRUE(StartMerge());
std::this_thread::sleep_for(150ms);
- SimulateDaemonRestart();
+ ASSERT_NO_FATAL_FAILURE(SimulateDaemonRestart());
- StartMerge();
+ ASSERT_TRUE(StartMerge());
std::this_thread::sleep_for(100ms);
- SimulateDaemonRestart();
+ ASSERT_NO_FATAL_FAILURE(SimulateDaemonRestart());
- StartMerge();
+ ASSERT_TRUE(StartMerge());
std::this_thread::sleep_for(800ms);
- SimulateDaemonRestart();
+ ASSERT_NO_FATAL_FAILURE(SimulateDaemonRestart());
- StartMerge();
+ ASSERT_TRUE(StartMerge());
std::this_thread::sleep_for(600ms);
- SimulateDaemonRestart();
+ ASSERT_NO_FATAL_FAILURE(SimulateDaemonRestart());
ASSERT_TRUE(Merge());
}
TEST_F(SnapuserdTest, Snapshot_IO_TEST) {
- ASSERT_TRUE(SetupDefault());
+ if (!harness_->HasUserDevice()) {
+ GTEST_SKIP() << "Skipping snapshot read; not supported";
+ }
+ ASSERT_NO_FATAL_FAILURE(SetupDefault());
// I/O before merge
- ReadSnapshotDeviceAndValidate();
+ ASSERT_NO_FATAL_FAILURE(ReadSnapshotDeviceAndValidate());
ASSERT_TRUE(Merge());
ValidateMerge();
// I/O after merge - daemon should read directly
// from base device
- ReadSnapshotDeviceAndValidate();
- Shutdown();
+ ASSERT_NO_FATAL_FAILURE(ReadSnapshotDeviceAndValidate());
}
TEST_F(SnapuserdTest, Snapshot_MERGE_IO_TEST) {
- ASSERT_TRUE(SetupDefault());
+ if (!harness_->HasUserDevice()) {
+ GTEST_SKIP() << "Skipping snapshot read; not supported";
+ }
+ ASSERT_NO_FATAL_FAILURE(SetupDefault());
// Issue I/O before merge begins
std::async(std::launch::async, &SnapuserdTest::ReadSnapshotDeviceAndValidate, this);
// Start the merge
ASSERT_TRUE(Merge());
ValidateMerge();
- Shutdown();
}
TEST_F(SnapuserdTest, Snapshot_MERGE_IO_TEST_1) {
- ASSERT_TRUE(SetupDefault());
+ if (!harness_->HasUserDevice()) {
+ GTEST_SKIP() << "Skipping snapshot read; not supported";
+ }
+ ASSERT_NO_FATAL_FAILURE(SetupDefault());
// Start the merge
- StartMerge();
+ ASSERT_TRUE(StartMerge());
// Issue I/O in parallel when merge is in-progress
std::async(std::launch::async, &SnapuserdTest::ReadSnapshotDeviceAndValidate, this);
CheckMergeCompletion();
ValidateMerge();
- Shutdown();
}
TEST_F(SnapuserdTest, Snapshot_Merge_Resume) {
- ASSERT_TRUE(SetupDefault());
- MergeInterrupt();
+ ASSERT_NO_FATAL_FAILURE(SetupDefault());
+ ASSERT_NO_FATAL_FAILURE(MergeInterrupt());
ValidateMerge();
- Shutdown();
}
TEST_F(SnapuserdTest, Snapshot_COPY_Overlap_TEST_1) {
- ASSERT_TRUE(SetupCopyOverlap_1());
+ ASSERT_NO_FATAL_FAILURE(SetupCopyOverlap_1());
ASSERT_TRUE(Merge());
ValidateMerge();
- Shutdown();
}
TEST_F(SnapuserdTest, Snapshot_COPY_Overlap_TEST_2) {
- ASSERT_TRUE(SetupCopyOverlap_2());
+ ASSERT_NO_FATAL_FAILURE(SetupCopyOverlap_2());
ASSERT_TRUE(Merge());
ValidateMerge();
- Shutdown();
}
TEST_F(SnapuserdTest, Snapshot_COPY_Overlap_Merge_Resume_TEST) {
- ASSERT_TRUE(SetupCopyOverlap_1());
- MergeInterrupt();
+ ASSERT_NO_FATAL_FAILURE(SetupCopyOverlap_1());
+ ASSERT_NO_FATAL_FAILURE(MergeInterrupt());
ValidateMerge();
- Shutdown();
}
TEST_F(SnapuserdTest, Snapshot_Merge_Crash_Fixed_Ordered) {
- ASSERT_TRUE(SetupOrderedOps());
- MergeInterruptFixed(300);
+ ASSERT_NO_FATAL_FAILURE(SetupOrderedOps());
+ ASSERT_NO_FATAL_FAILURE(MergeInterruptFixed(300));
ValidateMerge();
- Shutdown();
}
TEST_F(SnapuserdTest, Snapshot_Merge_Crash_Random_Ordered) {
- ASSERT_TRUE(SetupOrderedOps());
- MergeInterruptRandomly(500);
+ ASSERT_NO_FATAL_FAILURE(SetupOrderedOps());
+ ASSERT_NO_FATAL_FAILURE(MergeInterruptRandomly(500));
ValidateMerge();
- Shutdown();
}
TEST_F(SnapuserdTest, Snapshot_Merge_Crash_Fixed_Inverted) {
- ASSERT_TRUE(SetupOrderedOpsInverted());
- MergeInterruptFixed(50);
+ ASSERT_NO_FATAL_FAILURE(SetupOrderedOpsInverted());
+ ASSERT_NO_FATAL_FAILURE(MergeInterruptFixed(50));
ValidateMerge();
- Shutdown();
}
TEST_F(SnapuserdTest, Snapshot_Merge_Crash_Random_Inverted) {
- ASSERT_TRUE(SetupOrderedOpsInverted());
- MergeInterruptRandomly(50);
+ ASSERT_NO_FATAL_FAILURE(SetupOrderedOpsInverted());
+ ASSERT_NO_FATAL_FAILURE(MergeInterruptRandomly(50));
ValidateMerge();
- Shutdown();
+}
+
+class HandlerTest : public SnapuserdTestBase {
+ protected:
+ void SetUp() override;
+ void TearDown() override;
+
+ AssertionResult ReadSectors(sector_t sector, uint64_t size, void* buffer);
+
+ TestBlockServerFactory factory_;
+ std::shared_ptr<TestBlockServerOpener> opener_;
+ std::shared_ptr<SnapshotHandler> handler_;
+ std::unique_ptr<ReadWorker> read_worker_;
+ TestBlockServer* block_server_;
+ std::future<bool> handler_thread_;
+};
+
+void HandlerTest::SetUp() {
+ ASSERT_NO_FATAL_FAILURE(SnapuserdTestBase::SetUp());
+ ASSERT_NO_FATAL_FAILURE(CreateBaseDevice());
+ ASSERT_NO_FATAL_FAILURE(CreateCowDevice());
+ ASSERT_NO_FATAL_FAILURE(SetDeviceControlName());
+
+ opener_ = factory_.CreateTestOpener(system_device_ctrl_name_);
+ ASSERT_NE(opener_, nullptr);
+
+ handler_ = std::make_shared<SnapshotHandler>(system_device_ctrl_name_, cow_system_->path,
+ base_dev_->GetPath(), base_dev_->GetPath(),
+ opener_, 1, false, false);
+ ASSERT_TRUE(handler_->InitCowDevice());
+ ASSERT_TRUE(handler_->InitializeWorkers());
+
+ read_worker_ = std::make_unique<ReadWorker>(cow_system_->path, base_dev_->GetPath(),
+ system_device_ctrl_name_, base_dev_->GetPath(),
+ handler_->GetSharedPtr(), opener_);
+ ASSERT_TRUE(read_worker_->Init());
+ block_server_ = static_cast<TestBlockServer*>(read_worker_->block_server());
+
+ handler_thread_ = std::async(std::launch::async, &SnapshotHandler::Start, handler_.get());
+}
+
+void HandlerTest::TearDown() {
+ ASSERT_TRUE(factory_.DeleteQueue(system_device_ctrl_name_));
+ ASSERT_TRUE(handler_thread_.get());
+ SnapuserdTestBase::TearDown();
+}
+
+AssertionResult HandlerTest::ReadSectors(sector_t sector, uint64_t size, void* buffer) {
+ if (!read_worker_->RequestSectors(sector, size)) {
+ return AssertionFailure() << "request sectors failed";
+ }
+
+ std::string result = std::move(block_server_->sent_io());
+ if (result.size() != size) {
+ return AssertionFailure() << "size mismatch in result, got " << result.size()
+ << ", expected " << size;
+ }
+
+ memcpy(buffer, result.data(), size);
+ return AssertionSuccess();
+}
+
+// This test mirrors ReadSnapshotDeviceAndValidate.
+TEST_F(HandlerTest, Read) {
+ std::unique_ptr<uint8_t[]> snapuserd_buffer = std::make_unique<uint8_t[]>(size_);
+
+ // COPY
+ loff_t offset = 0;
+ ASSERT_TRUE(ReadSectors(offset / SECTOR_SIZE, size_, snapuserd_buffer.get()));
+ ASSERT_EQ(memcmp(snapuserd_buffer.get(), orig_buffer_.get(), size_), 0);
+
+ // REPLACE
+ offset += size_;
+ ASSERT_TRUE(ReadSectors(offset / SECTOR_SIZE, size_, snapuserd_buffer.get()));
+ ASSERT_EQ(memcmp(snapuserd_buffer.get(), (char*)orig_buffer_.get() + size_, size_), 0);
+
+ // ZERO
+ offset += size_;
+ ASSERT_TRUE(ReadSectors(offset / SECTOR_SIZE, size_, snapuserd_buffer.get()));
+ ASSERT_EQ(memcmp(snapuserd_buffer.get(), (char*)orig_buffer_.get() + (size_ * 2), size_), 0);
+
+ // REPLACE
+ offset += size_;
+ ASSERT_TRUE(ReadSectors(offset / SECTOR_SIZE, size_, snapuserd_buffer.get()));
+ ASSERT_EQ(memcmp(snapuserd_buffer.get(), (char*)orig_buffer_.get() + (size_ * 3), size_), 0);
+
+ // XOR
+ offset += size_;
+ ASSERT_TRUE(ReadSectors(offset / SECTOR_SIZE, size_, snapuserd_buffer.get()));
+ ASSERT_EQ(memcmp(snapuserd_buffer.get(), (char*)orig_buffer_.get() + (size_ * 4), size_), 0);
+}
+
+TEST_F(HandlerTest, ReadUnalignedSector) {
+ std::unique_ptr<uint8_t[]> snapuserd_buffer = std::make_unique<uint8_t[]>(BLOCK_SZ);
+
+ ASSERT_TRUE(ReadSectors(1, BLOCK_SZ, snapuserd_buffer.get()));
+ ASSERT_EQ(memcmp(snapuserd_buffer.get(), orig_buffer_.get() + SECTOR_SIZE, BLOCK_SZ), 0);
+}
+
+TEST_F(HandlerTest, ReadUnalignedSize) {
+ std::unique_ptr<uint8_t[]> snapuserd_buffer = std::make_unique<uint8_t[]>(SECTOR_SIZE);
+
+ ASSERT_TRUE(ReadSectors(0, SECTOR_SIZE, snapuserd_buffer.get()));
+ ASSERT_EQ(memcmp(snapuserd_buffer.get(), orig_buffer_.get(), SECTOR_SIZE), 0);
}
} // namespace snapshot
@@ -844,20 +893,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_transitions.cpp b/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_transitions.cpp
index 28c9f68..f3e0019 100644
--- a/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_transitions.cpp
+++ b/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_transitions.cpp
@@ -189,33 +189,32 @@
cv.notify_all();
}
+static inline bool IsMergeBeginError(MERGE_IO_TRANSITION io_state) {
+ return io_state == MERGE_IO_TRANSITION::READ_AHEAD_FAILURE ||
+ io_state == MERGE_IO_TRANSITION::IO_TERMINATED;
+}
+
// Invoked by Merge thread - Waits on RA thread to resume merging. Will
// be waken up RA thread.
bool SnapshotHandler::WaitForMergeBegin() {
- {
- std::unique_lock<std::mutex> lock(lock_);
- while (!MergeInitiated()) {
- cv.wait(lock);
+ std::unique_lock<std::mutex> lock(lock_);
- if (io_state_ == MERGE_IO_TRANSITION::READ_AHEAD_FAILURE ||
- io_state_ == MERGE_IO_TRANSITION::IO_TERMINATED) {
- return false;
- }
- }
+ cv.wait(lock, [this]() -> bool { return MergeInitiated() || IsMergeBeginError(io_state_); });
- while (!(io_state_ == MERGE_IO_TRANSITION::MERGE_BEGIN ||
- io_state_ == MERGE_IO_TRANSITION::READ_AHEAD_FAILURE ||
- io_state_ == MERGE_IO_TRANSITION::IO_TERMINATED)) {
- cv.wait(lock);
- }
-
- if (io_state_ == MERGE_IO_TRANSITION::READ_AHEAD_FAILURE ||
- io_state_ == MERGE_IO_TRANSITION::IO_TERMINATED) {
- return false;
- }
-
- return true;
+ if (IsMergeBeginError(io_state_)) {
+ SNAP_LOG(ERROR) << "WaitForMergeBegin failed with state: " << io_state_;
+ return false;
}
+
+ cv.wait(lock, [this]() -> bool {
+ return io_state_ == MERGE_IO_TRANSITION::MERGE_BEGIN || IsMergeBeginError(io_state_);
+ });
+
+ if (IsMergeBeginError(io_state_)) {
+ SNAP_LOG(ERROR) << "WaitForMergeBegin failed with state: " << io_state_;
+ return false;
+ }
+ return true;
}
// Invoked by RA thread - Flushes the RA block to scratch space if necessary
@@ -277,6 +276,7 @@
if (io_state_ == MERGE_IO_TRANSITION::MERGE_FAILED ||
io_state_ == MERGE_IO_TRANSITION::MERGE_COMPLETE ||
io_state_ == MERGE_IO_TRANSITION::IO_TERMINATED) {
+ SNAP_LOG(ERROR) << "Wait for merge ready failed: " << io_state_;
return false;
}
return true;
@@ -668,5 +668,26 @@
}
}
+std::ostream& operator<<(std::ostream& os, MERGE_IO_TRANSITION value) {
+ switch (value) {
+ case MERGE_IO_TRANSITION::INVALID:
+ return os << "INVALID";
+ case MERGE_IO_TRANSITION::MERGE_READY:
+ return os << "MERGE_READY";
+ case MERGE_IO_TRANSITION::MERGE_BEGIN:
+ return os << "MERGE_BEGIN";
+ case MERGE_IO_TRANSITION::MERGE_FAILED:
+ return os << "MERGE_FAILED";
+ case MERGE_IO_TRANSITION::MERGE_COMPLETE:
+ return os << "MERGE_COMPLETE";
+ case MERGE_IO_TRANSITION::IO_TERMINATED:
+ return os << "IO_TERMINATED";
+ case MERGE_IO_TRANSITION::READ_AHEAD_FAILURE:
+ return os << "READ_AHEAD_FAILURE";
+ default:
+ return os << "unknown";
+ }
+}
+
} // 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..65208fb
--- /dev/null
+++ b/fs_mgr/libsnapshot/snapuserd/user-space-merge/worker.cpp
@@ -0,0 +1,69 @@
+// 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;
+}
+
+bool Worker::Init() {
+ 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..c89d1b4
--- /dev/null
+++ b/fs_mgr/libsnapshot/snapuserd/user-space-merge/worker.h
@@ -0,0 +1,62 @@
+// 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:
+ bool InitializeFds();
+ bool InitReader();
+ virtual void CloseFds() { base_path_merge_fd_ = {}; }
+
+ std::unique_ptr<CowReader> reader_;
+
+ 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/snapuserd/utility.cpp b/fs_mgr/libsnapshot/snapuserd/utility.cpp
new file mode 100644
index 0000000..a84a7c1
--- /dev/null
+++ b/fs_mgr/libsnapshot/snapuserd/utility.cpp
@@ -0,0 +1,36 @@
+// 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 "utility.h"
+
+#include <sys/resource.h>
+#include <unistd.h>
+
+#include <android-base/file.h>
+
+namespace android {
+namespace snapshot {
+
+using android::base::unique_fd;
+
+bool SetThreadPriority([[maybe_unused]] int priority) {
+#ifdef __ANDROID__
+ return setpriority(PRIO_PROCESS, gettid(), priority) != -1;
+#else
+ return true;
+#endif
+}
+
+} // namespace snapshot
+} // namespace android
diff --git a/fs_mgr/libsnapshot/snapuserd/utility.h b/fs_mgr/libsnapshot/snapuserd/utility.h
new file mode 100644
index 0000000..58ec3e6
--- /dev/null
+++ b/fs_mgr/libsnapshot/snapuserd/utility.h
@@ -0,0 +1,23 @@
+// 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
+
+namespace android {
+namespace snapshot {
+
+bool SetThreadPriority(int priority);
+
+} // namespace snapshot
+} // namespace android
diff --git a/fs_mgr/libsnapshot/tools/Android.bp b/fs_mgr/libsnapshot/tools/Android.bp
new file mode 100644
index 0000000..cfa0cef
--- /dev/null
+++ b/fs_mgr/libsnapshot/tools/Android.bp
@@ -0,0 +1,22 @@
+
+cc_binary {
+ name: "cow_benchmark",
+ host_supported: true,
+ defaults: [
+ "fs_mgr_defaults",
+ "libsnapshot_cow_defaults",
+ ],
+
+ srcs: ["cow_benchmark.cpp"],
+
+ static_libs: [
+ "libsnapshot_cow",
+ ],
+
+ shared_libs: [
+ "libbase",
+ "liblog",
+ ],
+
+ cflags: ["-Werror"],
+}
diff --git a/fs_mgr/libsnapshot/tools/cow_benchmark.cpp b/fs_mgr/libsnapshot/tools/cow_benchmark.cpp
new file mode 100644
index 0000000..da2b879
--- /dev/null
+++ b/fs_mgr/libsnapshot/tools/cow_benchmark.cpp
@@ -0,0 +1,188 @@
+
+#include <memory>
+
+#include <array>
+#include <iostream>
+#include <random>
+
+#include <libsnapshot/cow_compress.h>
+#include <libsnapshot/cow_format.h>
+
+static const uint32_t BLOCK_SZ = 4096;
+static const uint32_t SEED_NUMBER = 10;
+
+namespace android {
+namespace snapshot {
+
+static std::string CompressionToString(CowCompression& compression) {
+ std::string output;
+ switch (compression.algorithm) {
+ case kCowCompressBrotli:
+ output.append("brotli");
+ break;
+ case kCowCompressGz:
+ output.append("gz");
+ break;
+ case kCowCompressLz4:
+ output.append("lz4");
+ break;
+ case kCowCompressZstd:
+ output.append("zstd");
+ break;
+ case kCowCompressNone:
+ return "No Compression";
+ }
+ output.append(" " + std::to_string(compression.compression_level));
+ return output;
+}
+
+void OneShotCompressionTest() {
+ std::cout << "\n-------One Shot Compressor Perf Analysis-------\n";
+
+ std::vector<CowCompression> compression_list = {
+ {kCowCompressLz4, 0}, {kCowCompressBrotli, 1}, {kCowCompressBrotli, 3},
+ {kCowCompressBrotli, 11}, {kCowCompressZstd, 3}, {kCowCompressZstd, 6},
+ {kCowCompressZstd, 9}, {kCowCompressZstd, 22}, {kCowCompressGz, 1},
+ {kCowCompressGz, 3}, {kCowCompressGz, 6}, {kCowCompressGz, 9}};
+ std::vector<std::unique_ptr<ICompressor>> compressors;
+ for (auto i : compression_list) {
+ compressors.emplace_back(ICompressor::Create(i, BLOCK_SZ));
+ }
+
+ // Allocate a buffer of size 8 blocks.
+ std::array<char, 32768> buffer;
+
+ // Generate a random 4k buffer of characters
+ std::default_random_engine gen(SEED_NUMBER);
+ std::uniform_int_distribution<int> distribution(0, 10);
+ for (int i = 0; i < buffer.size(); i++) {
+ buffer[i] = static_cast<char>(distribution(gen));
+ }
+
+ std::vector<std::pair<double, std::string>> latencies;
+ std::vector<std::pair<double, std::string>> ratios;
+
+ for (size_t i = 0; i < compressors.size(); i++) {
+ const auto start = std::chrono::steady_clock::now();
+ std::basic_string<uint8_t> compressed_data =
+ compressors[i]->Compress(buffer.data(), buffer.size());
+ const auto end = std::chrono::steady_clock::now();
+ const auto latency =
+ std::chrono::duration_cast<std::chrono::nanoseconds>(end - start) / 1000.0;
+ const double compression_ratio =
+ static_cast<uint16_t>(compressed_data.size()) * 1.00 / buffer.size();
+
+ std::cout << "Metrics for " << CompressionToString(compression_list[i]) << ": latency -> "
+ << latency.count() << "ms "
+ << " compression ratio ->" << compression_ratio << " \n";
+
+ latencies.emplace_back(
+ std::make_pair(latency.count(), CompressionToString(compression_list[i])));
+ ratios.emplace_back(
+ std::make_pair(compression_ratio, CompressionToString(compression_list[i])));
+ }
+
+ int best_speed = 0;
+ int best_ratio = 0;
+
+ for (size_t i = 1; i < latencies.size(); i++) {
+ if (latencies[i].first < latencies[best_speed].first) {
+ best_speed = i;
+ }
+ if (ratios[i].first < ratios[best_ratio].first) {
+ best_ratio = i;
+ }
+ }
+
+ std::cout << "BEST SPEED: " << latencies[best_speed].first << "ms "
+ << latencies[best_speed].second << "\n";
+ std::cout << "BEST RATIO: " << ratios[best_ratio].first << " " << ratios[best_ratio].second
+ << "\n";
+}
+
+void IncrementalCompressionTest() {
+ std::cout << "\n-------Incremental Compressor Perf Analysis-------\n";
+
+ std::vector<CowCompression> compression_list = {
+ {kCowCompressLz4, 0}, {kCowCompressBrotli, 1}, {kCowCompressBrotli, 3},
+ {kCowCompressBrotli, 11}, {kCowCompressZstd, 3}, {kCowCompressZstd, 6},
+ {kCowCompressZstd, 9}, {kCowCompressZstd, 22}, {kCowCompressGz, 1},
+ {kCowCompressGz, 3}, {kCowCompressGz, 6}, {kCowCompressGz, 9}};
+ std::vector<std::unique_ptr<ICompressor>> compressors;
+ for (auto i : compression_list) {
+ compressors.emplace_back(ICompressor::Create(i, BLOCK_SZ));
+ }
+
+ // Allocate a buffer of size 8 blocks.
+ std::array<char, 32768> buffer;
+
+ // Generate a random 4k buffer of characters
+ std::default_random_engine gen(SEED_NUMBER);
+ std::uniform_int_distribution<int> distribution(0, 10);
+ for (int i = 0; i < buffer.size(); i++) {
+ buffer[i] = static_cast<char>(distribution(gen));
+ }
+
+ std::vector<std::pair<double, std::string>> latencies;
+ std::vector<std::pair<double, std::string>> ratios;
+
+ for (size_t i = 0; i < compressors.size(); i++) {
+ std::vector<std::basic_string<uint8_t>> compressed_data_vec;
+ int num_blocks = buffer.size() / BLOCK_SZ;
+ const uint8_t* iter = reinterpret_cast<const uint8_t*>(buffer.data());
+
+ const auto start = std::chrono::steady_clock::now();
+ while (num_blocks > 0) {
+ std::basic_string<uint8_t> compressed_data = compressors[i]->Compress(iter, BLOCK_SZ);
+ compressed_data_vec.emplace_back(compressed_data);
+ num_blocks--;
+ iter += BLOCK_SZ;
+ }
+
+ const auto end = std::chrono::steady_clock::now();
+ const auto latency =
+ std::chrono::duration_cast<std::chrono::nanoseconds>(end - start) / 1000.0;
+
+ size_t size = 0;
+ for (auto& i : compressed_data_vec) {
+ size += i.size();
+ }
+ const double compression_ratio = size * 1.00 / buffer.size();
+
+ std::cout << "Metrics for " << CompressionToString(compression_list[i]) << ": latency -> "
+ << latency.count() << "ms "
+ << " compression ratio ->" << compression_ratio << " \n";
+
+ latencies.emplace_back(
+ std::make_pair(latency.count(), CompressionToString(compression_list[i])));
+ ratios.emplace_back(
+ std::make_pair(compression_ratio, CompressionToString(compression_list[i])));
+ }
+
+ int best_speed = 0;
+ int best_ratio = 0;
+
+ for (size_t i = 1; i < latencies.size(); i++) {
+ if (latencies[i].first < latencies[best_speed].first) {
+ best_speed = i;
+ }
+ if (ratios[i].first < ratios[best_ratio].first) {
+ best_ratio = i;
+ }
+ }
+
+ std::cout << "BEST SPEED: " << latencies[best_speed].first << "ms "
+ << latencies[best_speed].second << "\n";
+ std::cout << "BEST RATIO: " << ratios[best_ratio].first << " " << ratios[best_ratio].second
+ << "\n";
+}
+
+} // namespace snapshot
+} // namespace android
+
+int main() {
+ android::snapshot::OneShotCompressionTest();
+ android::snapshot::IncrementalCompressionTest();
+
+ return 0;
+}
\ No newline at end of file
diff --git a/fs_mgr/tests/Android.bp b/fs_mgr/tests/Android.bp
index b9bae25..2aeba0a 100644
--- a/fs_mgr/tests/Android.bp
+++ b/fs_mgr/tests/Android.bp
@@ -38,7 +38,8 @@
],
static_libs: [
"libfs_mgr",
- "libfstab",
+ "libgmock",
+ "libgtest",
],
srcs: [
"file_wait_test.cpp",
@@ -109,7 +110,6 @@
],
static_libs: [
"libfs_mgr",
- "libfstab",
"libgmock",
"libgtest",
],
diff --git a/fs_mgr/tests/fs_mgr_test.cpp b/fs_mgr/tests/fs_mgr_test.cpp
index 5f889ca..322bf1b 100644
--- a/fs_mgr/tests/fs_mgr_test.cpp
+++ b/fs_mgr/tests/fs_mgr_test.cpp
@@ -29,11 +29,13 @@
#include <android-base/strings.h>
#include <fs_mgr.h>
#include <fstab/fstab.h>
+#include <gmock/gmock.h>
#include <gtest/gtest.h>
-#include "../fs_mgr_priv_boot_config.h"
+#include "../fs_mgr_priv.h"
using namespace android::fs_mgr;
+using namespace testing;
namespace {
@@ -119,37 +121,42 @@
{"terminator", "truncated"},
};
-const std::string bootconfig =
- "androidboot.bootdevice = \"1d84000.ufshc\"\n"
- "androidboot.boot_devices = \"dev1\", \"dev2,withcomma\", \"dev3\"\n"
- "androidboot.baseband = \"sdy\"\n"
- "androidboot.keymaster = \"1\"\n"
- "androidboot.serialno = \"BLAHBLAHBLAH\"\n"
- "androidboot.slot_suffix = \"_a\"\n"
- "androidboot.hardware.platform = \"sdw813\"\n"
- "androidboot.hardware = \"foo\"\n"
- "androidboot.revision = \"EVT1.0\"\n"
- "androidboot.bootloader = \"burp-0.1-7521\"\n"
- "androidboot.hardware.sku = \"mary\"\n"
- "androidboot.hardware.radio.subtype = \"0\"\n"
- "androidboot.dtbo_idx = \"2\"\n"
- "androidboot.mode = \"normal\"\n"
- "androidboot.hardware.ddr = \"1GB,combuchi,LPDDR4X\"\n"
- "androidboot.ddr_info = \"combuchiandroidboot.ddr_size=2GB\"\n"
- "androidboot.hardware.ufs = \"2GB,combushi\"\n"
- "androidboot.boottime = \"0BLE:58,1BLL:22,1BLE:571,2BLL:105,ODT:0,AVB:123\"\n"
- "androidboot.ramdump = \"disabled\"\n"
- "androidboot.vbmeta.device = \"PARTUUID=aa08f1a4-c7c9-402e-9a66-9707cafa9ceb\"\n"
- "androidboot.vbmeta.avb_version = \"1.1\"\n"
- "androidboot.vbmeta.device_state = \"unlocked\"\n"
- "androidboot.vbmeta.hash_alg = \"sha256\"\n"
- "androidboot.vbmeta.size = \"5248\"\n"
- "androidboot.vbmeta.digest = \""
- "ac13147e959861c20f2a6da97d25fe79e60e902c022a371c5c039d31e7c68860\"\n"
- "androidboot.vbmeta.invalidate_on_error = \"yes\"\n"
- "androidboot.veritymode = \"enforcing\"\n"
- "androidboot.verifiedbootstate = \"orange\"\n"
- "androidboot.space = \"sha256 5248 androidboot.nospace = nope\"\n";
+const std::string bootconfig = R"(
+androidboot.bootdevice = "1d84000.ufshc"
+androidboot.boot_devices = "dev1", "dev2,withcomma", "dev3"
+androidboot.baseband = "sdy"
+androidboot.keymaster = "1"
+androidboot.serialno = "BLAHBLAHBLAH"
+androidboot.slot_suffix = "_a"
+androidboot.hardware.platform = "sdw813"
+androidboot.hardware = "foo"
+androidboot.revision = "EVT1.0"
+androidboot.bootloader = "burp-0.1-7521"
+androidboot.hardware.sku = "mary"
+androidboot.hardware.radio.subtype = "0"
+androidboot.dtbo_idx = "2"
+androidboot.mode = "normal"
+androidboot.hardware.ddr = "1GB,combuchi,LPDDR4X"
+androidboot.ddr_info = "combuchiandroidboot.ddr_size=2GB"
+androidboot.hardware.ufs = "2GB,combushi"
+androidboot.boottime = "0BLE:58,1BLL:22,1BLE:571,2BLL:105,ODT:0,AVB:123"
+androidboot.ramdump = "disabled"
+androidboot.vbmeta.device = "PARTUUID=aa08f1a4-c7c9-402e-9a66-9707cafa9ceb"
+androidboot.vbmeta.avb_version = "1.1"
+androidboot.vbmeta.device_state = "unlocked"
+androidboot.vbmeta.hash_alg = "sha256"
+androidboot.vbmeta.size = "5248"
+androidboot.vbmeta.digest = "ac13147e959861c20f2a6da97d25fe79e60e902c022a371c5c039d31e7c68860"
+androidboot.vbmeta.invalidate_on_error = "yes"
+androidboot.veritymode = "enforcing"
+androidboot.verifiedbootstate = "orange"
+androidboot.space = "sha256 5248 androidboot.nospace = nope"
+just.key
+key.empty.value =
+dessert.value = "ice, cream"
+dessert.list = "ice", "cream"
+ambiguous.list = ", ", ", "
+)";
const std::vector<std::pair<std::string, std::string>> bootconfig_result_space = {
{"androidboot.bootdevice", "1d84000.ufshc"},
@@ -182,6 +189,11 @@
{"androidboot.veritymode", "enforcing"},
{"androidboot.verifiedbootstate", "orange"},
{"androidboot.space", "sha256 5248 androidboot.nospace = nope"},
+ {"just.key", ""},
+ {"key.empty.value", ""},
+ {"dessert.value", "ice, cream"},
+ {"dessert.list", "ice,cream"},
+ {"ambiguous.list", ", ,, "},
};
bool CompareFlags(FstabEntry::FsMgrFlags& lhs, FstabEntry::FsMgrFlags& rhs) {
@@ -212,44 +224,61 @@
} // namespace
-TEST(fs_mgr, fs_mgr_parse_cmdline) {
- EXPECT_EQ(result_space, fs_mgr_parse_cmdline(cmdline));
+TEST(fs_mgr, ImportKernelCmdline) {
+ std::vector<std::pair<std::string, std::string>> result;
+ ImportKernelCmdlineFromString(
+ cmdline, [&](std::string key, std::string value) { result.emplace_back(key, value); });
+ EXPECT_THAT(result, ContainerEq(result_space));
}
-TEST(fs_mgr, fs_mgr_get_boot_config_from_kernel_cmdline) {
+TEST(fs_mgr, GetKernelCmdline) {
std::string content;
- for (const auto& entry : result_space) {
- static constexpr char androidboot[] = "androidboot.";
- if (!android::base::StartsWith(entry.first, androidboot)) continue;
- auto key = entry.first.substr(strlen(androidboot));
- EXPECT_TRUE(fs_mgr_get_boot_config_from_kernel(cmdline, key, &content)) << " for " << key;
- EXPECT_EQ(entry.second, content);
- }
- EXPECT_FALSE(fs_mgr_get_boot_config_from_kernel(cmdline, "vbmeta.avb_versio", &content));
- EXPECT_TRUE(content.empty()) << content;
- EXPECT_FALSE(fs_mgr_get_boot_config_from_kernel(cmdline, "nospace", &content));
- EXPECT_TRUE(content.empty()) << content;
-}
-
-TEST(fs_mgr, fs_mgr_parse_bootconfig) {
- EXPECT_EQ(bootconfig_result_space, fs_mgr_parse_proc_bootconfig(bootconfig));
-}
-
-TEST(fs_mgr, fs_mgr_get_boot_config_from_bootconfig) {
- std::string content;
- for (const auto& entry : bootconfig_result_space) {
- static constexpr char androidboot[] = "androidboot.";
- if (!android::base::StartsWith(entry.first, androidboot)) continue;
- auto key = entry.first.substr(strlen(androidboot));
- EXPECT_TRUE(fs_mgr_get_boot_config_from_bootconfig(bootconfig, key, &content))
- << " for " << key;
- EXPECT_EQ(entry.second, content);
+ for (const auto& [key, value] : result_space) {
+ EXPECT_TRUE(GetKernelCmdlineFromString(cmdline, key, &content)) << " for " << key;
+ EXPECT_EQ(content, value);
}
- EXPECT_FALSE(fs_mgr_get_boot_config_from_bootconfig(bootconfig, "vbmeta.avb_versio", &content));
- EXPECT_TRUE(content.empty()) << content;
- EXPECT_FALSE(fs_mgr_get_boot_config_from_bootconfig(bootconfig, "nospace", &content));
- EXPECT_TRUE(content.empty()) << content;
+ const std::string kUnmodifiedToken = "<UNMODIFIED>";
+ content = kUnmodifiedToken;
+ EXPECT_FALSE(GetKernelCmdlineFromString(cmdline, "", &content));
+ EXPECT_EQ(content, kUnmodifiedToken) << "output parameter shouldn't be overridden";
+
+ content = kUnmodifiedToken;
+ EXPECT_FALSE(GetKernelCmdlineFromString(cmdline, "androidboot.vbmeta.avb_versio", &content));
+ EXPECT_EQ(content, kUnmodifiedToken) << "output parameter shouldn't be overridden";
+
+ content = kUnmodifiedToken;
+ EXPECT_FALSE(GetKernelCmdlineFromString(bootconfig, "androidboot.nospace", &content));
+ EXPECT_EQ(content, kUnmodifiedToken) << "output parameter shouldn't be overridden";
+}
+
+TEST(fs_mgr, ImportBootconfig) {
+ std::vector<std::pair<std::string, std::string>> result;
+ ImportBootconfigFromString(bootconfig, [&](std::string key, std::string value) {
+ result.emplace_back(key, value);
+ });
+ EXPECT_THAT(result, ContainerEq(bootconfig_result_space));
+}
+
+TEST(fs_mgr, GetBootconfig) {
+ std::string content;
+ for (const auto& [key, value] : bootconfig_result_space) {
+ EXPECT_TRUE(GetBootconfigFromString(bootconfig, key, &content)) << " for " << key;
+ EXPECT_EQ(content, value);
+ }
+
+ const std::string kUnmodifiedToken = "<UNMODIFIED>";
+ content = kUnmodifiedToken;
+ EXPECT_FALSE(GetBootconfigFromString(bootconfig, "", &content));
+ EXPECT_EQ(content, kUnmodifiedToken) << "output parameter shouldn't be overridden";
+
+ content = kUnmodifiedToken;
+ EXPECT_FALSE(GetBootconfigFromString(bootconfig, "androidboot.vbmeta.avb_versio", &content));
+ EXPECT_EQ(content, kUnmodifiedToken) << "output parameter shouldn't be overridden";
+
+ content = kUnmodifiedToken;
+ EXPECT_FALSE(GetBootconfigFromString(bootconfig, "androidboot.nospace", &content));
+ EXPECT_EQ(content, kUnmodifiedToken) << "output parameter shouldn't be overridden";
}
TEST(fs_mgr, fs_mgr_read_fstab_file_proc_mounts) {
diff --git a/fs_mgr/tests/vts_fs_test.cpp b/fs_mgr/tests/vts_fs_test.cpp
index 4d771fa..32947b5 100644
--- a/fs_mgr/tests/vts_fs_test.cpp
+++ b/fs_mgr/tests/vts_fs_test.cpp
@@ -23,6 +23,8 @@
#include <gtest/gtest.h>
#include <libdm/dm.h>
+#include "../fs_mgr_priv.h"
+
using testing::Contains;
using testing::Not;
diff --git a/healthd/BatteryMonitor.cpp b/healthd/BatteryMonitor.cpp
index bd7955a..e4cf582 100644
--- a/healthd/BatteryMonitor.cpp
+++ b/healthd/BatteryMonitor.cpp
@@ -361,7 +361,7 @@
void BatteryMonitor::updateValues(void) {
initHealthInfo(mHealthInfo.get());
- if (!mHealthdConfig->batteryPresentPath.isEmpty())
+ if (!mHealthdConfig->batteryPresentPath.empty())
mHealthInfo->batteryPresent = getBooleanField(mHealthdConfig->batteryPresentPath);
else
mHealthInfo->batteryPresent = mBatteryDevicePresent;
@@ -371,43 +371,43 @@
: getIntField(mHealthdConfig->batteryCapacityPath);
mHealthInfo->batteryVoltageMillivolts = getIntField(mHealthdConfig->batteryVoltagePath) / 1000;
- if (!mHealthdConfig->batteryCurrentNowPath.isEmpty())
+ if (!mHealthdConfig->batteryCurrentNowPath.empty())
mHealthInfo->batteryCurrentMicroamps = getIntField(mHealthdConfig->batteryCurrentNowPath);
- if (!mHealthdConfig->batteryFullChargePath.isEmpty())
+ if (!mHealthdConfig->batteryFullChargePath.empty())
mHealthInfo->batteryFullChargeUah = getIntField(mHealthdConfig->batteryFullChargePath);
- if (!mHealthdConfig->batteryCycleCountPath.isEmpty())
+ if (!mHealthdConfig->batteryCycleCountPath.empty())
mHealthInfo->batteryCycleCount = getIntField(mHealthdConfig->batteryCycleCountPath);
- if (!mHealthdConfig->batteryChargeCounterPath.isEmpty())
+ if (!mHealthdConfig->batteryChargeCounterPath.empty())
mHealthInfo->batteryChargeCounterUah =
getIntField(mHealthdConfig->batteryChargeCounterPath);
- if (!mHealthdConfig->batteryCurrentAvgPath.isEmpty())
+ if (!mHealthdConfig->batteryCurrentAvgPath.empty())
mHealthInfo->batteryCurrentAverageMicroamps =
getIntField(mHealthdConfig->batteryCurrentAvgPath);
- if (!mHealthdConfig->batteryChargeTimeToFullNowPath.isEmpty())
+ if (!mHealthdConfig->batteryChargeTimeToFullNowPath.empty())
mHealthInfo->batteryChargeTimeToFullNowSeconds =
getIntField(mHealthdConfig->batteryChargeTimeToFullNowPath);
- if (!mHealthdConfig->batteryFullChargeDesignCapacityUahPath.isEmpty())
+ if (!mHealthdConfig->batteryFullChargeDesignCapacityUahPath.empty())
mHealthInfo->batteryFullChargeDesignCapacityUah =
getIntField(mHealthdConfig->batteryFullChargeDesignCapacityUahPath);
- if (!mHealthdConfig->batteryHealthStatusPath.isEmpty())
+ if (!mHealthdConfig->batteryHealthStatusPath.empty())
mBatteryHealthStatus = getIntField(mHealthdConfig->batteryHealthStatusPath);
- if (!mHealthdConfig->batteryStateOfHealthPath.isEmpty())
+ if (!mHealthdConfig->batteryStateOfHealthPath.empty())
mHealthInfo->batteryHealthData->batteryStateOfHealth =
getIntField(mHealthdConfig->batteryStateOfHealthPath);
- if (!mHealthdConfig->batteryManufacturingDatePath.isEmpty())
+ if (!mHealthdConfig->batteryManufacturingDatePath.empty())
mHealthInfo->batteryHealthData->batteryManufacturingDateSeconds =
getIntField(mHealthdConfig->batteryManufacturingDatePath);
- if (!mHealthdConfig->batteryFirstUsageDatePath.isEmpty())
+ if (!mHealthdConfig->batteryFirstUsageDatePath.empty())
mHealthInfo->batteryHealthData->batteryFirstUsageSeconds =
getIntField(mHealthdConfig->batteryFirstUsageDatePath);
@@ -444,12 +444,10 @@
for (size_t i = 0; i < mChargerNames.size(); i++) {
String8 path;
- path.appendFormat("%s/%s/online", POWER_SUPPLY_SYSFS_PATH,
- mChargerNames[i].string());
+ path.appendFormat("%s/%s/online", POWER_SUPPLY_SYSFS_PATH, mChargerNames[i].c_str());
if (getIntField(path)) {
path.clear();
- path.appendFormat("%s/%s/type", POWER_SUPPLY_SYSFS_PATH,
- mChargerNames[i].string());
+ path.appendFormat("%s/%s/type", POWER_SUPPLY_SYSFS_PATH, mChargerNames[i].c_str());
switch(readPowerSupplyType(path)) {
case ANDROID_POWER_SUPPLY_TYPE_AC:
mHealthInfo->chargerAcOnline = true;
@@ -466,26 +464,24 @@
default:
path.clear();
path.appendFormat("%s/%s/is_dock", POWER_SUPPLY_SYSFS_PATH,
- mChargerNames[i].string());
- if (access(path.string(), R_OK) == 0)
+ mChargerNames[i].c_str());
+ if (access(path.c_str(), R_OK) == 0)
mHealthInfo->chargerDockOnline = true;
else
KLOG_WARNING(LOG_TAG, "%s: Unknown power supply type\n",
- mChargerNames[i].string());
+ mChargerNames[i].c_str());
}
path.clear();
path.appendFormat("%s/%s/current_max", POWER_SUPPLY_SYSFS_PATH,
- mChargerNames[i].string());
- int ChargingCurrent =
- (access(path.string(), R_OK) == 0) ? getIntField(path) : 0;
+ mChargerNames[i].c_str());
+ int ChargingCurrent = (access(path.c_str(), R_OK) == 0) ? getIntField(path) : 0;
path.clear();
path.appendFormat("%s/%s/voltage_max", POWER_SUPPLY_SYSFS_PATH,
- mChargerNames[i].string());
+ mChargerNames[i].c_str());
int ChargingVoltage =
- (access(path.string(), R_OK) == 0) ? getIntField(path) :
- DEFAULT_VBUS_VOLTAGE;
+ (access(path.c_str(), R_OK) == 0) ? getIntField(path) : DEFAULT_VBUS_VOLTAGE;
double power = ((double)ChargingCurrent / MILLION) *
((double)ChargingVoltage / MILLION);
@@ -510,17 +506,17 @@
props.batteryStatus);
len = strlen(dmesgline);
- if (!healthd_config.batteryCurrentNowPath.isEmpty()) {
+ if (!healthd_config.batteryCurrentNowPath.empty()) {
len += snprintf(dmesgline + len, sizeof(dmesgline) - len, " c=%d",
props.batteryCurrentMicroamps);
}
- if (!healthd_config.batteryFullChargePath.isEmpty()) {
+ if (!healthd_config.batteryFullChargePath.empty()) {
len += snprintf(dmesgline + len, sizeof(dmesgline) - len, " fc=%d",
props.batteryFullChargeUah);
}
- if (!healthd_config.batteryCycleCountPath.isEmpty()) {
+ if (!healthd_config.batteryCycleCountPath.empty()) {
len += snprintf(dmesgline + len, sizeof(dmesgline) - len, " cc=%d",
props.batteryCycleCount);
}
@@ -554,7 +550,7 @@
int BatteryMonitor::getChargeStatus() {
BatteryStatus result = BatteryStatus::UNKNOWN;
- if (!mHealthdConfig->batteryStatusPath.isEmpty()) {
+ if (!mHealthdConfig->batteryStatusPath.empty()) {
std::string buf;
if (readFromFile(mHealthdConfig->batteryStatusPath, &buf) > 0)
result = getBatteryStatus(buf.c_str());
@@ -565,7 +561,7 @@
status_t BatteryMonitor::setChargingPolicy(int value) {
status_t ret = NAME_NOT_FOUND;
bool result;
- if (!mHealthdConfig->chargingPolicyPath.isEmpty()) {
+ if (!mHealthdConfig->chargingPolicyPath.empty()) {
result = writeToFile(mHealthdConfig->chargingPolicyPath, value);
if (!result) {
KLOG_WARNING(LOG_TAG, "setChargingPolicy fail\n");
@@ -579,7 +575,7 @@
int BatteryMonitor::getChargingPolicy() {
BatteryChargingPolicy result = BatteryChargingPolicy::DEFAULT;
- if (!mHealthdConfig->chargingPolicyPath.isEmpty()) {
+ if (!mHealthdConfig->chargingPolicyPath.empty()) {
std::string buf;
if (readFromFile(mHealthdConfig->chargingPolicyPath, &buf) > 0)
result = getBatteryChargingPolicy(buf.c_str());
@@ -589,15 +585,15 @@
int BatteryMonitor::getBatteryHealthData(int id) {
if (id == BATTERY_PROP_MANUFACTURING_DATE) {
- if (!mHealthdConfig->batteryManufacturingDatePath.isEmpty())
+ if (!mHealthdConfig->batteryManufacturingDatePath.empty())
return getIntField(mHealthdConfig->batteryManufacturingDatePath);
}
if (id == BATTERY_PROP_FIRST_USAGE_DATE) {
- if (!mHealthdConfig->batteryFirstUsageDatePath.isEmpty())
+ if (!mHealthdConfig->batteryFirstUsageDatePath.empty())
return getIntField(mHealthdConfig->batteryFirstUsageDatePath);
}
if (id == BATTERY_PROP_STATE_OF_HEALTH) {
- if (!mHealthdConfig->batteryStateOfHealthPath.isEmpty())
+ if (!mHealthdConfig->batteryStateOfHealthPath.empty())
return getIntField(mHealthdConfig->batteryStateOfHealthPath);
}
return 0;
@@ -611,7 +607,7 @@
switch(id) {
case BATTERY_PROP_CHARGE_COUNTER:
- if (!mHealthdConfig->batteryChargeCounterPath.isEmpty()) {
+ if (!mHealthdConfig->batteryChargeCounterPath.empty()) {
val->valueInt64 =
getIntField(mHealthdConfig->batteryChargeCounterPath);
ret = OK;
@@ -621,7 +617,7 @@
break;
case BATTERY_PROP_CURRENT_NOW:
- if (!mHealthdConfig->batteryCurrentNowPath.isEmpty()) {
+ if (!mHealthdConfig->batteryCurrentNowPath.empty()) {
val->valueInt64 =
getIntField(mHealthdConfig->batteryCurrentNowPath);
ret = OK;
@@ -631,7 +627,7 @@
break;
case BATTERY_PROP_CURRENT_AVG:
- if (!mHealthdConfig->batteryCurrentAvgPath.isEmpty()) {
+ if (!mHealthdConfig->batteryCurrentAvgPath.empty()) {
val->valueInt64 =
getIntField(mHealthdConfig->batteryCurrentAvgPath);
ret = OK;
@@ -641,7 +637,7 @@
break;
case BATTERY_PROP_CAPACITY:
- if (!mHealthdConfig->batteryCapacityPath.isEmpty()) {
+ if (!mHealthdConfig->batteryCapacityPath.empty()) {
val->valueInt64 =
getIntField(mHealthdConfig->batteryCapacityPath);
ret = OK;
@@ -708,35 +704,35 @@
props.batteryVoltageMillivolts, props.batteryTemperatureTenthsCelsius);
write(fd, vs, strlen(vs));
- if (!mHealthdConfig->batteryCurrentNowPath.isEmpty()) {
+ if (!mHealthdConfig->batteryCurrentNowPath.empty()) {
v = getIntField(mHealthdConfig->batteryCurrentNowPath);
snprintf(vs, sizeof(vs), "current now: %d\n", v);
write(fd, vs, strlen(vs));
}
- if (!mHealthdConfig->batteryCurrentAvgPath.isEmpty()) {
+ if (!mHealthdConfig->batteryCurrentAvgPath.empty()) {
v = getIntField(mHealthdConfig->batteryCurrentAvgPath);
snprintf(vs, sizeof(vs), "current avg: %d\n", v);
write(fd, vs, strlen(vs));
}
- if (!mHealthdConfig->batteryChargeCounterPath.isEmpty()) {
+ if (!mHealthdConfig->batteryChargeCounterPath.empty()) {
v = getIntField(mHealthdConfig->batteryChargeCounterPath);
snprintf(vs, sizeof(vs), "charge counter: %d\n", v);
write(fd, vs, strlen(vs));
}
- if (!mHealthdConfig->batteryCurrentNowPath.isEmpty()) {
+ if (!mHealthdConfig->batteryCurrentNowPath.empty()) {
snprintf(vs, sizeof(vs), "current now: %d\n", props.batteryCurrentMicroamps);
write(fd, vs, strlen(vs));
}
- if (!mHealthdConfig->batteryCycleCountPath.isEmpty()) {
+ if (!mHealthdConfig->batteryCycleCountPath.empty()) {
snprintf(vs, sizeof(vs), "cycle count: %d\n", props.batteryCycleCount);
write(fd, vs, strlen(vs));
}
- if (!mHealthdConfig->batteryFullChargePath.isEmpty()) {
+ if (!mHealthdConfig->batteryFullChargePath.empty()) {
snprintf(vs, sizeof(vs), "Full charge: %d\n", props.batteryFullChargeUah);
write(fd, vs, strlen(vs));
}
@@ -775,8 +771,7 @@
case ANDROID_POWER_SUPPLY_TYPE_DOCK:
path.clear();
path.appendFormat("%s/%s/online", POWER_SUPPLY_SYSFS_PATH, name);
- if (access(path.string(), R_OK) == 0)
- mChargerNames.add(String8(name));
+ if (access(path.c_str(), R_OK) == 0) mChargerNames.add(String8(name));
break;
case ANDROID_POWER_SUPPLY_TYPE_BATTERY:
@@ -787,7 +782,7 @@
if (isScopedPowerSupply(name)) continue;
mBatteryDevicePresent = true;
- if (mHealthdConfig->batteryStatusPath.isEmpty()) {
+ if (mHealthdConfig->batteryStatusPath.empty()) {
path.clear();
path.appendFormat("%s/%s/status", POWER_SUPPLY_SYSFS_PATH,
name);
@@ -795,7 +790,7 @@
mHealthdConfig->batteryStatusPath = path;
}
- if (mHealthdConfig->batteryHealthPath.isEmpty()) {
+ if (mHealthdConfig->batteryHealthPath.empty()) {
path.clear();
path.appendFormat("%s/%s/health", POWER_SUPPLY_SYSFS_PATH,
name);
@@ -803,7 +798,7 @@
mHealthdConfig->batteryHealthPath = path;
}
- if (mHealthdConfig->batteryPresentPath.isEmpty()) {
+ if (mHealthdConfig->batteryPresentPath.empty()) {
path.clear();
path.appendFormat("%s/%s/present", POWER_SUPPLY_SYSFS_PATH,
name);
@@ -811,7 +806,7 @@
mHealthdConfig->batteryPresentPath = path;
}
- if (mHealthdConfig->batteryCapacityPath.isEmpty()) {
+ if (mHealthdConfig->batteryCapacityPath.empty()) {
path.clear();
path.appendFormat("%s/%s/capacity", POWER_SUPPLY_SYSFS_PATH,
name);
@@ -819,7 +814,7 @@
mHealthdConfig->batteryCapacityPath = path;
}
- if (mHealthdConfig->batteryVoltagePath.isEmpty()) {
+ if (mHealthdConfig->batteryVoltagePath.empty()) {
path.clear();
path.appendFormat("%s/%s/voltage_now",
POWER_SUPPLY_SYSFS_PATH, name);
@@ -828,7 +823,7 @@
}
}
- if (mHealthdConfig->batteryFullChargePath.isEmpty()) {
+ if (mHealthdConfig->batteryFullChargePath.empty()) {
path.clear();
path.appendFormat("%s/%s/charge_full",
POWER_SUPPLY_SYSFS_PATH, name);
@@ -836,7 +831,7 @@
mHealthdConfig->batteryFullChargePath = path;
}
- if (mHealthdConfig->batteryCurrentNowPath.isEmpty()) {
+ if (mHealthdConfig->batteryCurrentNowPath.empty()) {
path.clear();
path.appendFormat("%s/%s/current_now",
POWER_SUPPLY_SYSFS_PATH, name);
@@ -844,7 +839,7 @@
mHealthdConfig->batteryCurrentNowPath = path;
}
- if (mHealthdConfig->batteryCycleCountPath.isEmpty()) {
+ if (mHealthdConfig->batteryCycleCountPath.empty()) {
path.clear();
path.appendFormat("%s/%s/cycle_count",
POWER_SUPPLY_SYSFS_PATH, name);
@@ -852,27 +847,27 @@
mHealthdConfig->batteryCycleCountPath = path;
}
- if (mHealthdConfig->batteryCapacityLevelPath.isEmpty()) {
+ if (mHealthdConfig->batteryCapacityLevelPath.empty()) {
path.clear();
path.appendFormat("%s/%s/capacity_level", POWER_SUPPLY_SYSFS_PATH, name);
if (access(path, R_OK) == 0) mHealthdConfig->batteryCapacityLevelPath = path;
}
- if (mHealthdConfig->batteryChargeTimeToFullNowPath.isEmpty()) {
+ if (mHealthdConfig->batteryChargeTimeToFullNowPath.empty()) {
path.clear();
path.appendFormat("%s/%s/time_to_full_now", POWER_SUPPLY_SYSFS_PATH, name);
if (access(path, R_OK) == 0)
mHealthdConfig->batteryChargeTimeToFullNowPath = path;
}
- if (mHealthdConfig->batteryFullChargeDesignCapacityUahPath.isEmpty()) {
+ if (mHealthdConfig->batteryFullChargeDesignCapacityUahPath.empty()) {
path.clear();
path.appendFormat("%s/%s/charge_full_design", POWER_SUPPLY_SYSFS_PATH, name);
if (access(path, R_OK) == 0)
mHealthdConfig->batteryFullChargeDesignCapacityUahPath = path;
}
- if (mHealthdConfig->batteryCurrentAvgPath.isEmpty()) {
+ if (mHealthdConfig->batteryCurrentAvgPath.empty()) {
path.clear();
path.appendFormat("%s/%s/current_avg",
POWER_SUPPLY_SYSFS_PATH, name);
@@ -880,7 +875,7 @@
mHealthdConfig->batteryCurrentAvgPath = path;
}
- if (mHealthdConfig->batteryChargeCounterPath.isEmpty()) {
+ if (mHealthdConfig->batteryChargeCounterPath.empty()) {
path.clear();
path.appendFormat("%s/%s/charge_counter",
POWER_SUPPLY_SYSFS_PATH, name);
@@ -888,7 +883,7 @@
mHealthdConfig->batteryChargeCounterPath = path;
}
- if (mHealthdConfig->batteryTemperaturePath.isEmpty()) {
+ if (mHealthdConfig->batteryTemperaturePath.empty()) {
path.clear();
path.appendFormat("%s/%s/temp", POWER_SUPPLY_SYSFS_PATH,
name);
@@ -897,7 +892,7 @@
}
}
- if (mHealthdConfig->batteryTechnologyPath.isEmpty()) {
+ if (mHealthdConfig->batteryTechnologyPath.empty()) {
path.clear();
path.appendFormat("%s/%s/technology",
POWER_SUPPLY_SYSFS_PATH, name);
@@ -905,7 +900,7 @@
mHealthdConfig->batteryTechnologyPath = path;
}
- if (mHealthdConfig->batteryStateOfHealthPath.isEmpty()) {
+ if (mHealthdConfig->batteryStateOfHealthPath.empty()) {
path.clear();
path.appendFormat("%s/%s/state_of_health", POWER_SUPPLY_SYSFS_PATH, name);
if (access(path, R_OK) == 0) {
@@ -918,32 +913,32 @@
}
}
- if (mHealthdConfig->batteryHealthStatusPath.isEmpty()) {
+ if (mHealthdConfig->batteryHealthStatusPath.empty()) {
path.clear();
path.appendFormat("%s/%s/health_status", POWER_SUPPLY_SYSFS_PATH, name);
if (access(path, R_OK) == 0) mHealthdConfig->batteryHealthStatusPath = path;
}
- if (mHealthdConfig->batteryManufacturingDatePath.isEmpty()) {
+ if (mHealthdConfig->batteryManufacturingDatePath.empty()) {
path.clear();
path.appendFormat("%s/%s/manufacturing_date", POWER_SUPPLY_SYSFS_PATH, name);
if (access(path, R_OK) == 0)
mHealthdConfig->batteryManufacturingDatePath = path;
}
- if (mHealthdConfig->batteryFirstUsageDatePath.isEmpty()) {
+ if (mHealthdConfig->batteryFirstUsageDatePath.empty()) {
path.clear();
path.appendFormat("%s/%s/first_usage_date", POWER_SUPPLY_SYSFS_PATH, name);
if (access(path, R_OK) == 0) mHealthdConfig->batteryFirstUsageDatePath = path;
}
- if (mHealthdConfig->chargingStatePath.isEmpty()) {
+ if (mHealthdConfig->chargingStatePath.empty()) {
path.clear();
path.appendFormat("%s/%s/charging_state", POWER_SUPPLY_SYSFS_PATH, name);
if (access(path, R_OK) == 0) mHealthdConfig->chargingStatePath = path;
}
- if (mHealthdConfig->chargingPolicyPath.isEmpty()) {
+ if (mHealthdConfig->chargingPolicyPath.empty()) {
path.clear();
path.appendFormat("%s/%s/charging_policy", POWER_SUPPLY_SYSFS_PATH, name);
if (access(path, R_OK) == 0) mHealthdConfig->chargingPolicyPath = path;
@@ -958,12 +953,10 @@
// Look for "is_dock" file
path.clear();
path.appendFormat("%s/%s/is_dock", POWER_SUPPLY_SYSFS_PATH, name);
- if (access(path.string(), R_OK) == 0) {
+ if (access(path.c_str(), R_OK) == 0) {
path.clear();
path.appendFormat("%s/%s/online", POWER_SUPPLY_SYSFS_PATH, name);
- if (access(path.string(), R_OK) == 0)
- mChargerNames.add(String8(name));
-
+ if (access(path.c_str(), R_OK) == 0) mChargerNames.add(String8(name));
}
}
}
@@ -975,43 +968,43 @@
hc->periodic_chores_interval_fast = -1;
hc->periodic_chores_interval_slow = -1;
} else {
- if (mHealthdConfig->batteryStatusPath.isEmpty())
+ if (mHealthdConfig->batteryStatusPath.empty())
KLOG_WARNING(LOG_TAG, "BatteryStatusPath not found\n");
- if (mHealthdConfig->batteryHealthPath.isEmpty())
+ if (mHealthdConfig->batteryHealthPath.empty())
KLOG_WARNING(LOG_TAG, "BatteryHealthPath not found\n");
- if (mHealthdConfig->batteryPresentPath.isEmpty())
+ if (mHealthdConfig->batteryPresentPath.empty())
KLOG_WARNING(LOG_TAG, "BatteryPresentPath not found\n");
- if (mHealthdConfig->batteryCapacityPath.isEmpty())
+ if (mHealthdConfig->batteryCapacityPath.empty())
KLOG_WARNING(LOG_TAG, "BatteryCapacityPath not found\n");
- if (mHealthdConfig->batteryVoltagePath.isEmpty())
+ if (mHealthdConfig->batteryVoltagePath.empty())
KLOG_WARNING(LOG_TAG, "BatteryVoltagePath not found\n");
- if (mHealthdConfig->batteryTemperaturePath.isEmpty())
+ if (mHealthdConfig->batteryTemperaturePath.empty())
KLOG_WARNING(LOG_TAG, "BatteryTemperaturePath not found\n");
- if (mHealthdConfig->batteryTechnologyPath.isEmpty())
+ if (mHealthdConfig->batteryTechnologyPath.empty())
KLOG_WARNING(LOG_TAG, "BatteryTechnologyPath not found\n");
- if (mHealthdConfig->batteryCurrentNowPath.isEmpty())
+ if (mHealthdConfig->batteryCurrentNowPath.empty())
KLOG_WARNING(LOG_TAG, "BatteryCurrentNowPath not found\n");
- if (mHealthdConfig->batteryFullChargePath.isEmpty())
+ if (mHealthdConfig->batteryFullChargePath.empty())
KLOG_WARNING(LOG_TAG, "BatteryFullChargePath not found\n");
- if (mHealthdConfig->batteryCycleCountPath.isEmpty())
+ if (mHealthdConfig->batteryCycleCountPath.empty())
KLOG_WARNING(LOG_TAG, "BatteryCycleCountPath not found\n");
- if (mHealthdConfig->batteryCapacityLevelPath.isEmpty())
+ if (mHealthdConfig->batteryCapacityLevelPath.empty())
KLOG_WARNING(LOG_TAG, "batteryCapacityLevelPath not found\n");
- if (mHealthdConfig->batteryChargeTimeToFullNowPath.isEmpty())
+ if (mHealthdConfig->batteryChargeTimeToFullNowPath.empty())
KLOG_WARNING(LOG_TAG, "batteryChargeTimeToFullNowPath. not found\n");
- if (mHealthdConfig->batteryFullChargeDesignCapacityUahPath.isEmpty())
+ if (mHealthdConfig->batteryFullChargeDesignCapacityUahPath.empty())
KLOG_WARNING(LOG_TAG, "batteryFullChargeDesignCapacityUahPath. not found\n");
- if (mHealthdConfig->batteryStateOfHealthPath.isEmpty())
+ if (mHealthdConfig->batteryStateOfHealthPath.empty())
KLOG_WARNING(LOG_TAG, "batteryStateOfHealthPath not found\n");
- if (mHealthdConfig->batteryHealthStatusPath.isEmpty())
+ if (mHealthdConfig->batteryHealthStatusPath.empty())
KLOG_WARNING(LOG_TAG, "batteryHealthStatusPath not found\n");
- if (mHealthdConfig->batteryManufacturingDatePath.isEmpty())
+ if (mHealthdConfig->batteryManufacturingDatePath.empty())
KLOG_WARNING(LOG_TAG, "batteryManufacturingDatePath not found\n");
- if (mHealthdConfig->batteryFirstUsageDatePath.isEmpty())
+ if (mHealthdConfig->batteryFirstUsageDatePath.empty())
KLOG_WARNING(LOG_TAG, "batteryFirstUsageDatePath not found\n");
- if (mHealthdConfig->chargingStatePath.isEmpty())
+ if (mHealthdConfig->chargingStatePath.empty())
KLOG_WARNING(LOG_TAG, "chargingStatePath not found\n");
- if (mHealthdConfig->chargingPolicyPath.isEmpty())
+ if (mHealthdConfig->chargingPolicyPath.empty())
KLOG_WARNING(LOG_TAG, "chargingPolicyPath not found\n");
}
diff --git a/healthd/BatteryMonitor_v1.cpp b/healthd/BatteryMonitor_v1.cpp
index b87c493..686c338 100644
--- a/healthd/BatteryMonitor_v1.cpp
+++ b/healthd/BatteryMonitor_v1.cpp
@@ -301,7 +301,7 @@
void BatteryMonitor::updateValues(void) {
initHealthInfo(mHealthInfo.get());
- if (!mHealthdConfig->batteryPresentPath.isEmpty())
+ if (!mHealthdConfig->batteryPresentPath.empty())
mHealthInfo->batteryPresent = getBooleanField(mHealthdConfig->batteryPresentPath);
else
mHealthInfo->batteryPresent = mBatteryDevicePresent;
@@ -311,28 +311,28 @@
: getIntField(mHealthdConfig->batteryCapacityPath);
mHealthInfo->batteryVoltageMillivolts = getIntField(mHealthdConfig->batteryVoltagePath) / 1000;
- if (!mHealthdConfig->batteryCurrentNowPath.isEmpty())
+ if (!mHealthdConfig->batteryCurrentNowPath.empty())
mHealthInfo->batteryCurrentMicroamps = getIntField(mHealthdConfig->batteryCurrentNowPath);
- if (!mHealthdConfig->batteryFullChargePath.isEmpty())
+ if (!mHealthdConfig->batteryFullChargePath.empty())
mHealthInfo->batteryFullChargeUah = getIntField(mHealthdConfig->batteryFullChargePath);
- if (!mHealthdConfig->batteryCycleCountPath.isEmpty())
+ if (!mHealthdConfig->batteryCycleCountPath.empty())
mHealthInfo->batteryCycleCount = getIntField(mHealthdConfig->batteryCycleCountPath);
- if (!mHealthdConfig->batteryChargeCounterPath.isEmpty())
+ if (!mHealthdConfig->batteryChargeCounterPath.empty())
mHealthInfo->batteryChargeCounterUah =
getIntField(mHealthdConfig->batteryChargeCounterPath);
- if (!mHealthdConfig->batteryCurrentAvgPath.isEmpty())
+ if (!mHealthdConfig->batteryCurrentAvgPath.empty())
mHealthInfo->batteryCurrentAverageMicroamps =
getIntField(mHealthdConfig->batteryCurrentAvgPath);
- if (!mHealthdConfig->batteryChargeTimeToFullNowPath.isEmpty())
+ if (!mHealthdConfig->batteryChargeTimeToFullNowPath.empty())
mHealthInfo->batteryChargeTimeToFullNowSeconds =
getIntField(mHealthdConfig->batteryChargeTimeToFullNowPath);
- if (!mHealthdConfig->batteryFullChargeDesignCapacityUahPath.isEmpty())
+ if (!mHealthdConfig->batteryFullChargeDesignCapacityUahPath.empty())
mHealthInfo->batteryFullChargeDesignCapacityUah =
getIntField(mHealthdConfig->batteryFullChargeDesignCapacityUahPath);
@@ -358,12 +358,10 @@
for (size_t i = 0; i < mChargerNames.size(); i++) {
String8 path;
- path.appendFormat("%s/%s/online", POWER_SUPPLY_SYSFS_PATH,
- mChargerNames[i].string());
+ path.appendFormat("%s/%s/online", POWER_SUPPLY_SYSFS_PATH, mChargerNames[i].c_str());
if (getIntField(path)) {
path.clear();
- path.appendFormat("%s/%s/type", POWER_SUPPLY_SYSFS_PATH,
- mChargerNames[i].string());
+ path.appendFormat("%s/%s/type", POWER_SUPPLY_SYSFS_PATH, mChargerNames[i].c_str());
switch(readPowerSupplyType(path)) {
case ANDROID_POWER_SUPPLY_TYPE_AC:
mHealthInfo->chargerAcOnline = true;
@@ -380,26 +378,24 @@
default:
path.clear();
path.appendFormat("%s/%s/is_dock", POWER_SUPPLY_SYSFS_PATH,
- mChargerNames[i].string());
- if (access(path.string(), R_OK) == 0)
+ mChargerNames[i].c_str());
+ if (access(path.c_str(), R_OK) == 0)
mHealthInfo->chargerDockOnline = true;
else
KLOG_WARNING(LOG_TAG, "%s: Unknown power supply type\n",
- mChargerNames[i].string());
+ mChargerNames[i].c_str());
}
path.clear();
path.appendFormat("%s/%s/current_max", POWER_SUPPLY_SYSFS_PATH,
- mChargerNames[i].string());
- int ChargingCurrent =
- (access(path.string(), R_OK) == 0) ? getIntField(path) : 0;
+ mChargerNames[i].c_str());
+ int ChargingCurrent = (access(path.c_str(), R_OK) == 0) ? getIntField(path) : 0;
path.clear();
path.appendFormat("%s/%s/voltage_max", POWER_SUPPLY_SYSFS_PATH,
- mChargerNames[i].string());
+ mChargerNames[i].c_str());
int ChargingVoltage =
- (access(path.string(), R_OK) == 0) ? getIntField(path) :
- DEFAULT_VBUS_VOLTAGE;
+ (access(path.c_str(), R_OK) == 0) ? getIntField(path) : DEFAULT_VBUS_VOLTAGE;
double power = ((double)ChargingCurrent / MILLION) *
((double)ChargingVoltage / MILLION);
@@ -424,17 +420,17 @@
props.batteryStatus);
len = strlen(dmesgline);
- if (!healthd_config.batteryCurrentNowPath.isEmpty()) {
+ if (!healthd_config.batteryCurrentNowPath.empty()) {
len += snprintf(dmesgline + len, sizeof(dmesgline) - len, " c=%d",
props.batteryCurrentMicroamps);
}
- if (!healthd_config.batteryFullChargePath.isEmpty()) {
+ if (!healthd_config.batteryFullChargePath.empty()) {
len += snprintf(dmesgline + len, sizeof(dmesgline) - len, " fc=%d",
props.batteryFullChargeUah);
}
- if (!healthd_config.batteryCycleCountPath.isEmpty()) {
+ if (!healthd_config.batteryCycleCountPath.empty()) {
len += snprintf(dmesgline + len, sizeof(dmesgline) - len, " cc=%d",
props.batteryCycleCount);
}
@@ -468,7 +464,7 @@
int BatteryMonitor::getChargeStatus() {
BatteryStatus result = BatteryStatus::UNKNOWN;
- if (!mHealthdConfig->batteryStatusPath.isEmpty()) {
+ if (!mHealthdConfig->batteryStatusPath.empty()) {
std::string buf;
if (readFromFile(mHealthdConfig->batteryStatusPath, &buf) > 0)
result = getBatteryStatus(buf.c_str());
@@ -484,7 +480,7 @@
switch(id) {
case BATTERY_PROP_CHARGE_COUNTER:
- if (!mHealthdConfig->batteryChargeCounterPath.isEmpty()) {
+ if (!mHealthdConfig->batteryChargeCounterPath.empty()) {
val->valueInt64 =
getIntField(mHealthdConfig->batteryChargeCounterPath);
ret = OK;
@@ -494,7 +490,7 @@
break;
case BATTERY_PROP_CURRENT_NOW:
- if (!mHealthdConfig->batteryCurrentNowPath.isEmpty()) {
+ if (!mHealthdConfig->batteryCurrentNowPath.empty()) {
val->valueInt64 =
getIntField(mHealthdConfig->batteryCurrentNowPath);
ret = OK;
@@ -504,7 +500,7 @@
break;
case BATTERY_PROP_CURRENT_AVG:
- if (!mHealthdConfig->batteryCurrentAvgPath.isEmpty()) {
+ if (!mHealthdConfig->batteryCurrentAvgPath.empty()) {
val->valueInt64 =
getIntField(mHealthdConfig->batteryCurrentAvgPath);
ret = OK;
@@ -514,7 +510,7 @@
break;
case BATTERY_PROP_CAPACITY:
- if (!mHealthdConfig->batteryCapacityPath.isEmpty()) {
+ if (!mHealthdConfig->batteryCapacityPath.empty()) {
val->valueInt64 =
getIntField(mHealthdConfig->batteryCapacityPath);
ret = OK;
@@ -561,35 +557,35 @@
props.batteryVoltageMillivolts, props.batteryTemperatureTenthsCelsius);
write(fd, vs, strlen(vs));
- if (!mHealthdConfig->batteryCurrentNowPath.isEmpty()) {
+ if (!mHealthdConfig->batteryCurrentNowPath.empty()) {
v = getIntField(mHealthdConfig->batteryCurrentNowPath);
snprintf(vs, sizeof(vs), "current now: %d\n", v);
write(fd, vs, strlen(vs));
}
- if (!mHealthdConfig->batteryCurrentAvgPath.isEmpty()) {
+ if (!mHealthdConfig->batteryCurrentAvgPath.empty()) {
v = getIntField(mHealthdConfig->batteryCurrentAvgPath);
snprintf(vs, sizeof(vs), "current avg: %d\n", v);
write(fd, vs, strlen(vs));
}
- if (!mHealthdConfig->batteryChargeCounterPath.isEmpty()) {
+ if (!mHealthdConfig->batteryChargeCounterPath.empty()) {
v = getIntField(mHealthdConfig->batteryChargeCounterPath);
snprintf(vs, sizeof(vs), "charge counter: %d\n", v);
write(fd, vs, strlen(vs));
}
- if (!mHealthdConfig->batteryCurrentNowPath.isEmpty()) {
+ if (!mHealthdConfig->batteryCurrentNowPath.empty()) {
snprintf(vs, sizeof(vs), "current now: %d\n", props.batteryCurrentMicroamps);
write(fd, vs, strlen(vs));
}
- if (!mHealthdConfig->batteryCycleCountPath.isEmpty()) {
+ if (!mHealthdConfig->batteryCycleCountPath.empty()) {
snprintf(vs, sizeof(vs), "cycle count: %d\n", props.batteryCycleCount);
write(fd, vs, strlen(vs));
}
- if (!mHealthdConfig->batteryFullChargePath.isEmpty()) {
+ if (!mHealthdConfig->batteryFullChargePath.empty()) {
snprintf(vs, sizeof(vs), "Full charge: %d\n", props.batteryFullChargeUah);
write(fd, vs, strlen(vs));
}
@@ -628,8 +624,7 @@
case ANDROID_POWER_SUPPLY_TYPE_DOCK:
path.clear();
path.appendFormat("%s/%s/online", POWER_SUPPLY_SYSFS_PATH, name);
- if (access(path.string(), R_OK) == 0)
- mChargerNames.add(String8(name));
+ if (access(path.c_str(), R_OK) == 0) mChargerNames.add(String8(name));
break;
case ANDROID_POWER_SUPPLY_TYPE_BATTERY:
@@ -640,7 +635,7 @@
if (isScopedPowerSupply(name)) continue;
mBatteryDevicePresent = true;
- if (mHealthdConfig->batteryStatusPath.isEmpty()) {
+ if (mHealthdConfig->batteryStatusPath.empty()) {
path.clear();
path.appendFormat("%s/%s/status", POWER_SUPPLY_SYSFS_PATH,
name);
@@ -648,7 +643,7 @@
mHealthdConfig->batteryStatusPath = path;
}
- if (mHealthdConfig->batteryHealthPath.isEmpty()) {
+ if (mHealthdConfig->batteryHealthPath.empty()) {
path.clear();
path.appendFormat("%s/%s/health", POWER_SUPPLY_SYSFS_PATH,
name);
@@ -656,7 +651,7 @@
mHealthdConfig->batteryHealthPath = path;
}
- if (mHealthdConfig->batteryPresentPath.isEmpty()) {
+ if (mHealthdConfig->batteryPresentPath.empty()) {
path.clear();
path.appendFormat("%s/%s/present", POWER_SUPPLY_SYSFS_PATH,
name);
@@ -664,7 +659,7 @@
mHealthdConfig->batteryPresentPath = path;
}
- if (mHealthdConfig->batteryCapacityPath.isEmpty()) {
+ if (mHealthdConfig->batteryCapacityPath.empty()) {
path.clear();
path.appendFormat("%s/%s/capacity", POWER_SUPPLY_SYSFS_PATH,
name);
@@ -672,7 +667,7 @@
mHealthdConfig->batteryCapacityPath = path;
}
- if (mHealthdConfig->batteryVoltagePath.isEmpty()) {
+ if (mHealthdConfig->batteryVoltagePath.empty()) {
path.clear();
path.appendFormat("%s/%s/voltage_now",
POWER_SUPPLY_SYSFS_PATH, name);
@@ -681,7 +676,7 @@
}
}
- if (mHealthdConfig->batteryFullChargePath.isEmpty()) {
+ if (mHealthdConfig->batteryFullChargePath.empty()) {
path.clear();
path.appendFormat("%s/%s/charge_full",
POWER_SUPPLY_SYSFS_PATH, name);
@@ -689,7 +684,7 @@
mHealthdConfig->batteryFullChargePath = path;
}
- if (mHealthdConfig->batteryCurrentNowPath.isEmpty()) {
+ if (mHealthdConfig->batteryCurrentNowPath.empty()) {
path.clear();
path.appendFormat("%s/%s/current_now",
POWER_SUPPLY_SYSFS_PATH, name);
@@ -697,7 +692,7 @@
mHealthdConfig->batteryCurrentNowPath = path;
}
- if (mHealthdConfig->batteryCycleCountPath.isEmpty()) {
+ if (mHealthdConfig->batteryCycleCountPath.empty()) {
path.clear();
path.appendFormat("%s/%s/cycle_count",
POWER_SUPPLY_SYSFS_PATH, name);
@@ -705,27 +700,27 @@
mHealthdConfig->batteryCycleCountPath = path;
}
- if (mHealthdConfig->batteryCapacityLevelPath.isEmpty()) {
+ if (mHealthdConfig->batteryCapacityLevelPath.empty()) {
path.clear();
path.appendFormat("%s/%s/capacity_level", POWER_SUPPLY_SYSFS_PATH, name);
if (access(path, R_OK) == 0) mHealthdConfig->batteryCapacityLevelPath = path;
}
- if (mHealthdConfig->batteryChargeTimeToFullNowPath.isEmpty()) {
+ if (mHealthdConfig->batteryChargeTimeToFullNowPath.empty()) {
path.clear();
path.appendFormat("%s/%s/time_to_full_now", POWER_SUPPLY_SYSFS_PATH, name);
if (access(path, R_OK) == 0)
mHealthdConfig->batteryChargeTimeToFullNowPath = path;
}
- if (mHealthdConfig->batteryFullChargeDesignCapacityUahPath.isEmpty()) {
+ if (mHealthdConfig->batteryFullChargeDesignCapacityUahPath.empty()) {
path.clear();
path.appendFormat("%s/%s/charge_full_design", POWER_SUPPLY_SYSFS_PATH, name);
if (access(path, R_OK) == 0)
mHealthdConfig->batteryFullChargeDesignCapacityUahPath = path;
}
- if (mHealthdConfig->batteryCurrentAvgPath.isEmpty()) {
+ if (mHealthdConfig->batteryCurrentAvgPath.empty()) {
path.clear();
path.appendFormat("%s/%s/current_avg",
POWER_SUPPLY_SYSFS_PATH, name);
@@ -733,7 +728,7 @@
mHealthdConfig->batteryCurrentAvgPath = path;
}
- if (mHealthdConfig->batteryChargeCounterPath.isEmpty()) {
+ if (mHealthdConfig->batteryChargeCounterPath.empty()) {
path.clear();
path.appendFormat("%s/%s/charge_counter",
POWER_SUPPLY_SYSFS_PATH, name);
@@ -741,7 +736,7 @@
mHealthdConfig->batteryChargeCounterPath = path;
}
- if (mHealthdConfig->batteryTemperaturePath.isEmpty()) {
+ if (mHealthdConfig->batteryTemperaturePath.empty()) {
path.clear();
path.appendFormat("%s/%s/temp", POWER_SUPPLY_SYSFS_PATH,
name);
@@ -750,7 +745,7 @@
}
}
- if (mHealthdConfig->batteryTechnologyPath.isEmpty()) {
+ if (mHealthdConfig->batteryTechnologyPath.empty()) {
path.clear();
path.appendFormat("%s/%s/technology",
POWER_SUPPLY_SYSFS_PATH, name);
@@ -767,12 +762,10 @@
// Look for "is_dock" file
path.clear();
path.appendFormat("%s/%s/is_dock", POWER_SUPPLY_SYSFS_PATH, name);
- if (access(path.string(), R_OK) == 0) {
+ if (access(path.c_str(), R_OK) == 0) {
path.clear();
path.appendFormat("%s/%s/online", POWER_SUPPLY_SYSFS_PATH, name);
- if (access(path.string(), R_OK) == 0)
- mChargerNames.add(String8(name));
-
+ if (access(path.c_str(), R_OK) == 0) mChargerNames.add(String8(name));
}
}
}
@@ -784,31 +777,31 @@
hc->periodic_chores_interval_fast = -1;
hc->periodic_chores_interval_slow = -1;
} else {
- if (mHealthdConfig->batteryStatusPath.isEmpty())
+ if (mHealthdConfig->batteryStatusPath.empty())
KLOG_WARNING(LOG_TAG, "BatteryStatusPath not found\n");
- if (mHealthdConfig->batteryHealthPath.isEmpty())
+ if (mHealthdConfig->batteryHealthPath.empty())
KLOG_WARNING(LOG_TAG, "BatteryHealthPath not found\n");
- if (mHealthdConfig->batteryPresentPath.isEmpty())
+ if (mHealthdConfig->batteryPresentPath.empty())
KLOG_WARNING(LOG_TAG, "BatteryPresentPath not found\n");
- if (mHealthdConfig->batteryCapacityPath.isEmpty())
+ if (mHealthdConfig->batteryCapacityPath.empty())
KLOG_WARNING(LOG_TAG, "BatteryCapacityPath not found\n");
- if (mHealthdConfig->batteryVoltagePath.isEmpty())
+ if (mHealthdConfig->batteryVoltagePath.empty())
KLOG_WARNING(LOG_TAG, "BatteryVoltagePath not found\n");
- if (mHealthdConfig->batteryTemperaturePath.isEmpty())
+ if (mHealthdConfig->batteryTemperaturePath.empty())
KLOG_WARNING(LOG_TAG, "BatteryTemperaturePath not found\n");
- if (mHealthdConfig->batteryTechnologyPath.isEmpty())
+ if (mHealthdConfig->batteryTechnologyPath.empty())
KLOG_WARNING(LOG_TAG, "BatteryTechnologyPath not found\n");
- if (mHealthdConfig->batteryCurrentNowPath.isEmpty())
+ if (mHealthdConfig->batteryCurrentNowPath.empty())
KLOG_WARNING(LOG_TAG, "BatteryCurrentNowPath not found\n");
- if (mHealthdConfig->batteryFullChargePath.isEmpty())
+ if (mHealthdConfig->batteryFullChargePath.empty())
KLOG_WARNING(LOG_TAG, "BatteryFullChargePath not found\n");
- if (mHealthdConfig->batteryCycleCountPath.isEmpty())
+ if (mHealthdConfig->batteryCycleCountPath.empty())
KLOG_WARNING(LOG_TAG, "BatteryCycleCountPath not found\n");
- if (mHealthdConfig->batteryCapacityLevelPath.isEmpty())
+ if (mHealthdConfig->batteryCapacityLevelPath.empty())
KLOG_WARNING(LOG_TAG, "batteryCapacityLevelPath not found\n");
- if (mHealthdConfig->batteryChargeTimeToFullNowPath.isEmpty())
+ if (mHealthdConfig->batteryChargeTimeToFullNowPath.empty())
KLOG_WARNING(LOG_TAG, "batteryChargeTimeToFullNowPath. not found\n");
- if (mHealthdConfig->batteryFullChargeDesignCapacityUahPath.isEmpty())
+ if (mHealthdConfig->batteryFullChargeDesignCapacityUahPath.empty())
KLOG_WARNING(LOG_TAG, "batteryFullChargeDesignCapacityUahPath. not found\n");
}
diff --git a/init/Android.bp b/init/Android.bp
index f62d7b7..d4852d6 100644
--- a/init/Android.bp
+++ b/init/Android.bp
@@ -166,11 +166,9 @@
"libbootloader_message",
"libc++fs",
"libcgrouprc_format",
- "libfsverity_init",
"liblmkd_utils",
"liblz4",
"libzstd",
- "libmini_keyctl_static",
"libmodprobe",
"libprocinfo",
"libprotobuf-cpp-lite",
@@ -214,8 +212,8 @@
visibility: [":__subpackages__"],
}
-cc_library_static {
- name: "libinit",
+cc_defaults {
+ name: "libinit_defaults",
recovery_available: true,
defaults: [
"init_defaults",
@@ -228,7 +226,6 @@
],
whole_static_libs: [
"libcap",
- "libcom.android.sysprop.apex",
"libcom.android.sysprop.init",
],
header_libs: ["bootimg_headers"],
@@ -252,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 {
@@ -270,7 +274,6 @@
recovery_available: true,
stem: "init",
defaults: ["init_defaults"],
- static_libs: ["libinit"],
srcs: ["main.cpp"],
symlinks: ["ueventd"],
target: {
@@ -309,12 +312,14 @@
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"],
- cflags: ["-DMICRODROID"],
+ static_libs: ["libinit.microdroid"],
+ cflags: ["-DMICRODROID=1"],
installable: false,
visibility: ["//packages/modules/Virtualization/microdroid"],
}
@@ -387,6 +392,10 @@
],
static_executable: true,
+ lto: {
+ // b/169004486 ThinLTO breaks x86 static executables.
+ never: true,
+ },
system_shared_libs: [],
cflags: [
@@ -460,7 +469,7 @@
cc_binary {
name: "init_first_stage.microdroid",
defaults: ["init_first_stage_defaults"],
- cflags: ["-DMICRODROID"],
+ cflags: ["-DMICRODROID=1"],
installable: false,
}
@@ -530,6 +539,7 @@
"libprotobuf-cpp-lite",
],
static_libs: [
+ "libfs_mgr",
"libhidl-gen-utils",
],
}
diff --git a/init/README.md b/init/README.md
index 5fced19..11c4e1c 100644
--- a/init/README.md
+++ b/init/README.md
@@ -674,11 +674,12 @@
_options_ include "barrier=1", "noauto\_da\_alloc", "discard", ... as
a comma separated string, e.g. barrier=1,noauto\_da\_alloc
-`perform_apex_config`
+`perform_apex_config [--bootstrap]`
> Performs tasks after APEXes are mounted. For example, creates data directories
for the mounted APEXes, parses config file(s) from them, and updates linker
configurations. Intended to be used only once when apexd notifies the mount
event by setting `apexd.status` to ready.
+ Use --bootstrap when invoking in the bootstrap mount namespace.
`restart [--only-if-running] <service>`
> Stops and restarts a running service, does nothing if the service is currently
diff --git a/init/apex_init_util.cpp b/init/apex_init_util.cpp
index c818f8f..6d17f36 100644
--- a/init/apex_init_util.cpp
+++ b/init/apex_init_util.cpp
@@ -16,13 +16,15 @@
#include "apex_init_util.h"
+#include <dirent.h>
#include <glob.h>
+#include <set>
#include <vector>
#include <android-base/logging.h>
-#include <android-base/result.h>
#include <android-base/properties.h>
+#include <android-base/result.h>
#include <android-base/strings.h>
#include "action_manager.h"
@@ -34,10 +36,13 @@
namespace android {
namespace init {
-static Result<std::vector<std::string>> CollectApexConfigs(const std::string& apex_name) {
+static Result<std::vector<std::string>> CollectRcScriptsFromApex(
+ const std::string& apex_name, const std::set<std::string>& skip_apexes) {
glob_t glob_result;
- std::string glob_pattern = apex_name.empty() ?
- "/apex/*/etc/*rc" : "/apex/" + apex_name + "/etc/*rc";
+ // Pattern uses "*rc" instead of ".rc" because APEXes can have versioned RC files
+ // like foo.34rc.
+ std::string glob_pattern =
+ apex_name.empty() ? "/apex/*/etc/*rc" : "/apex/" + apex_name + "/etc/*rc";
const int ret = glob(glob_pattern.c_str(), GLOB_MARK, nullptr, &glob_result);
if (ret != 0 && ret != GLOB_NOMATCH) {
@@ -47,15 +52,28 @@
std::vector<std::string> configs;
for (size_t i = 0; i < glob_result.gl_pathc; i++) {
std::string path = glob_result.gl_pathv[i];
- // Filter-out /apex/<name>@<ver> paths. The paths are bind-mounted to
- // /apex/<name> paths, so unless we filter them out, we will parse the
- // same file twice.
- std::vector<std::string> paths = android::base::Split(path, "/");
- if (paths.size() >= 3 && paths[2].find('@') != std::string::npos) {
+
+ // Filter out directories
+ if (path.back() == '/') {
continue;
}
- // Filter directories
- if (path.back() == '/') {
+
+ // Get apex name from path.
+ std::vector<std::string> paths = android::base::Split(path, "/");
+ if (paths.size() < 3) {
+ continue;
+ }
+ const std::string& apex_name = paths[2];
+
+ // Filter out /apex/<name>@<ver> paths. The paths are bind-mounted to
+ // /apex/<name> paths, so unless we filter them out, we will parse the
+ // same file twice.
+ if (apex_name.find('@') != std::string::npos) {
+ continue;
+ }
+
+ // Filter out skip_set apexes
+ if (skip_apexes.count(apex_name) > 0) {
continue;
}
configs.push_back(path);
@@ -64,11 +82,41 @@
return configs;
}
-static Result<void> ParseConfigs(const std::vector<std::string>& configs) {
+std::set<std::string> GetApexListFrom(const std::string& apex_dir) {
+ std::set<std::string> apex_list;
+ auto dirp = std::unique_ptr<DIR, int (*)(DIR*)>(opendir(apex_dir.c_str()), closedir);
+ if (!dirp) {
+ return apex_list;
+ }
+ struct dirent* entry;
+ while ((entry = readdir(dirp.get())) != nullptr) {
+ if (entry->d_type != DT_DIR) continue;
+
+ const char* name = entry->d_name;
+ if (name[0] == '.') continue;
+ if (strchr(name, '@') != nullptr) continue;
+ if (strcmp(name, "sharedlibs") == 0) continue;
+ apex_list.insert(name);
+ }
+ return apex_list;
+}
+
+static Result<void> ParseRcScripts(const std::vector<std::string>& files) {
+ if (files.empty()) {
+ return {};
+ }
+ // APEXes can have versioned RC files. These should be filtered based on
+ // SDK version.
+ auto filtered = FilterVersionedConfigs(
+ files, android::base::GetIntProperty("ro.build.version.sdk", INT_MAX));
+ if (filtered.empty()) {
+ return {};
+ }
+
Parser parser =
CreateApexConfigParser(ActionManager::GetInstance(), ServiceList::GetInstance());
std::vector<std::string> errors;
- for (const auto& c : configs) {
+ for (const auto& c : filtered) {
auto result = parser.ParseConfigFile(c);
// We should handle other config files even when there's an error.
if (!result.ok()) {
@@ -81,16 +129,21 @@
return {};
}
-Result<void> ParseApexConfigs(const std::string& apex_name) {
- auto configs = OR_RETURN(CollectApexConfigs(apex_name));
+Result<void> ParseRcScriptsFromApex(const std::string& apex_name) {
+ auto configs = OR_RETURN(CollectRcScriptsFromApex(apex_name, /*skip_apexes=*/{}));
+ return ParseRcScripts(configs);
+}
- if (configs.empty()) {
- return {};
+Result<void> ParseRcScriptsFromAllApexes(bool bootstrap) {
+ std::set<std::string> skip_apexes;
+ if (!bootstrap) {
+ // In case we already loaded config files from bootstrap APEXes, we need to avoid loading
+ // them again. We can get the list of bootstrap APEXes by scanning /bootstrap-apex and
+ // skip them in CollectRcScriptsFromApex.
+ skip_apexes = GetApexListFrom("/bootstrap-apex");
}
-
- auto filtered_configs = FilterVersionedConfigs(configs,
- android::base::GetIntProperty("ro.build.version.sdk", INT_MAX));
- return ParseConfigs(filtered_configs);
+ auto configs = OR_RETURN(CollectRcScriptsFromApex(/*apex_name=*/"", skip_apexes));
+ return ParseRcScripts(configs);
}
} // namespace init
diff --git a/init/apex_init_util.h b/init/apex_init_util.h
index 43f8ad5..75dfee1 100644
--- a/init/apex_init_util.h
+++ b/init/apex_init_util.h
@@ -16,6 +16,7 @@
#pragma once
+#include <set>
#include <string>
#include <vector>
@@ -24,9 +25,14 @@
namespace android {
namespace init {
-// Parse all config files for a given apex.
-// If apex name is empty(""), config files for all apexes will be parsed.
-Result<void> ParseApexConfigs(const std::string& apex_name);
+// Scans apex_dir (/apex) to get the list of active APEXes.
+std::set<std::string> GetApexListFrom(const std::string& apex_dir);
+
+// Parse all RC scripts for a given apex.
+Result<void> ParseRcScriptsFromApex(const std::string& apex_name);
+
+// Parse all RC scripts for all apexes under /apex.
+Result<void> ParseRcScriptsFromAllApexes(bool bootstrap);
} // namespace init
} // namespace android
diff --git a/init/builtins.cpp b/init/builtins.cpp
index 585eca2..a70e866 100644
--- a/init/builtins.cpp
+++ b/init/builtins.cpp
@@ -577,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");
@@ -591,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";
}
@@ -683,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();
}
@@ -764,6 +746,7 @@
static Result<void> do_start(const BuiltinArguments& args) {
Service* svc = ServiceList::GetInstance().FindService(args[1]);
if (!svc) return Error() << "service " << args[1] << " not found";
+ errno = 0;
if (auto result = svc->Start(); !result.ok()) {
return ErrorIgnoreEnoent() << "Could not start service: " << result.error();
}
@@ -1217,7 +1200,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 {};
@@ -1287,37 +1270,33 @@
/*
* Creates a directory under /data/misc/apexdata/ for each APEX.
*/
-static Result<void> create_apex_data_dirs() {
- auto dirp = std::unique_ptr<DIR, int (*)(DIR*)>(opendir("/apex"), closedir);
- if (!dirp) {
- return ErrnoError() << "Unable to open apex directory";
- }
- struct dirent* entry;
- while ((entry = readdir(dirp.get())) != nullptr) {
- if (entry->d_type != DT_DIR) continue;
-
- const char* name = entry->d_name;
- // skip any starting with "."
- if (name[0] == '.') continue;
-
- if (strchr(name, '@') != nullptr) continue;
-
- auto path = "/data/misc/apexdata/" + std::string(name);
+static void create_apex_data_dirs() {
+ for (const auto& name : GetApexListFrom("/apex")) {
+ auto path = "/data/misc/apexdata/" + name;
auto options = MkdirOptions{path, 0771, AID_ROOT, AID_SYSTEM, FscryptAction::kNone, "ref"};
- make_dir_with_options(options);
+ auto result = make_dir_with_options(options);
+ if (!result.ok()) {
+ LOG(ERROR) << result.error();
+ }
}
- return {};
}
static Result<void> do_perform_apex_config(const BuiltinArguments& args) {
- auto create_dirs = create_apex_data_dirs();
- if (!create_dirs.ok()) {
- return create_dirs.error();
+ bool bootstrap = false;
+ if (args.size() == 2) {
+ if (args[1] != "--bootstrap") {
+ return Error() << "Unexpected argument: " << args[1];
+ }
+ bootstrap = true;
}
- auto parse_configs = ParseApexConfigs(/*apex_name=*/"");
- ServiceList::GetInstance().MarkServicesUpdate();
- if (!parse_configs.ok()) {
- return parse_configs.error();
+
+ if (!bootstrap) {
+ create_apex_data_dirs();
+ }
+
+ auto parse_result = ParseRcScriptsFromAllApexes(bootstrap);
+ if (!parse_result.ok()) {
+ return parse_result.error();
}
auto update_linker_config = do_update_linker_config(args);
@@ -1325,6 +1304,9 @@
return update_linker_config.error();
}
+ if (!bootstrap) {
+ ServiceList::GetInstance().StartDelayedServices();
+ }
return {};
}
@@ -1379,7 +1361,7 @@
// mount and umount are run in the same context as mount_all for symmetry.
{"mount_all", {0, kMax, {false, do_mount_all}}},
{"mount", {3, kMax, {false, do_mount}}},
- {"perform_apex_config", {0, 0, {false, do_perform_apex_config}}},
+ {"perform_apex_config", {0, 1, {false, do_perform_apex_config}}},
{"umount", {1, 1, {false, do_umount}}},
{"umount_all", {0, 1, {false, do_umount_all}}},
{"update_linker_config", {0, 0, {false, do_update_linker_config}}},
diff --git a/init/devices.cpp b/init/devices.cpp
index d29ffd6..e76786a 100644
--- a/init/devices.cpp
+++ b/init/devices.cpp
@@ -32,6 +32,7 @@
#include <android-base/logging.h>
#include <android-base/stringprintf.h>
#include <android-base/strings.h>
+#include <fs_mgr.h>
#include <libdm/dm.h>
#include <private/android_filesystem_config.h>
#include <selinux/android.h>
@@ -203,8 +204,8 @@
partition_map.emplace_back(map_pieces[0], map_pieces[1]);
}
};
- ImportKernelCmdline(parser);
- ImportBootconfig(parser);
+ android::fs_mgr::ImportKernelCmdline(parser);
+ android::fs_mgr::ImportBootconfig(parser);
return partition_map;
}();
@@ -568,6 +569,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/first_stage_init.cpp b/init/first_stage_init.cpp
index bff80c5..3239eb7 100644
--- a/init/first_stage_init.cpp
+++ b/init/first_stage_init.cpp
@@ -35,6 +35,7 @@
#include <android-base/chrono_utils.h>
#include <android-base/file.h>
#include <android-base/logging.h>
+#include <android-base/stringprintf.h>
#include <modprobe/modprobe.h>
#include <private/android_filesystem_config.h>
@@ -67,7 +68,7 @@
void FreeRamdisk(DIR* dir, dev_t dev) {
int dfd = dirfd(dir);
- dirent* de;
+ dirent* de = nullptr;
while ((de = readdir(dir)) != nullptr) {
if (de->d_name == "."s || de->d_name == ".."s) {
continue;
@@ -76,7 +77,7 @@
bool is_dir = false;
if (de->d_type == DT_DIR || de->d_type == DT_UNKNOWN) {
- struct stat info;
+ struct stat info {};
if (fstatat(dfd, de->d_name, &info, AT_SYMLINK_NOFOLLOW) != 0) {
continue;
}
@@ -153,6 +154,15 @@
Copy(snapuserd, dst);
}
}
+
+std::string GetPageSizeSuffix() {
+ static const size_t page_size = sysconf(_SC_PAGE_SIZE);
+ if (page_size <= 4096) {
+ return "";
+ }
+ return android::base::StringPrintf("_%zuk", page_size / 1024);
+}
+
} // namespace
std::string GetModuleLoadList(BootMode boot_mode, const std::string& dir_path) {
@@ -171,7 +181,7 @@
}
if (module_load_file != "modules.load") {
- struct stat fileStat;
+ 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)) {
@@ -185,11 +195,11 @@
#define MODULE_BASE_DIR "/lib/modules"
bool LoadKernelModules(BootMode boot_mode, bool want_console, bool want_parallel,
int& modules_loaded) {
- struct utsname uts;
+ struct utsname uts {};
if (uname(&uts)) {
LOG(FATAL) << "Failed to get kernel version.";
}
- int major, minor;
+ int major = 0, minor = 0;
if (sscanf(uts.release, "%d.%d", &major, &minor) != 2) {
LOG(FATAL) << "Failed to parse kernel version " << uts.release;
}
@@ -199,13 +209,21 @@
LOG(INFO) << "Unable to open /lib/modules, skipping module loading.";
return true;
}
- dirent* entry;
+ dirent* entry = nullptr;
std::vector<std::string> module_dirs;
+ const std::string release_specific_module_dir = uts.release + GetPageSizeSuffix();
while ((entry = readdir(base_dir.get()))) {
if (entry->d_type != DT_DIR) {
continue;
}
- int dir_major, dir_minor;
+ if (entry->d_name == release_specific_module_dir) {
+ LOG(INFO) << "Release specific kernel module dir " << release_specific_module_dir
+ << " found, loading modules from here with no fallbacks.";
+ module_dirs.clear();
+ module_dirs.emplace_back(entry->d_name);
+ break;
+ }
+ int dir_major = 0, dir_minor = 0;
if (sscanf(entry->d_name, "%d.%d", &dir_major, &dir_minor) != 2 || dir_major != major ||
dir_minor != minor) {
continue;
@@ -228,6 +246,7 @@
bool retval = m.LoadListedModules(!want_console);
modules_loaded = m.GetModuleCount();
if (modules_loaded > 0) {
+ LOG(INFO) << "Loaded " << modules_loaded << " modules from " << dir_path;
return retval;
}
}
@@ -237,6 +256,7 @@
: m.LoadListedModules(!want_console);
modules_loaded = m.GetModuleCount();
if (modules_loaded > 0) {
+ LOG(INFO) << "Loaded " << modules_loaded << " modules from " << MODULE_BASE_DIR;
return retval;
}
return true;
@@ -257,6 +277,16 @@
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();
@@ -347,12 +377,24 @@
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";
}
- struct stat old_root_info;
+ struct stat old_root_info {};
if (stat("/", &old_root_info) != 0) {
PLOG(ERROR) << "Could not stat(\"/\"), not freeing ramdisk";
old_root_dir.reset();
@@ -381,12 +423,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);
@@ -437,11 +484,26 @@
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;
+ struct stat new_root_info {};
if (stat("/", &new_root_info) != 0) {
PLOG(ERROR) << "Could not stat(\"/\"), not freeing ramdisk";
old_root_dir.reset();
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.cpp b/init/init.cpp
index da63fdc..40e2169 100644
--- a/init/init.cpp
+++ b/init/init.cpp
@@ -487,7 +487,7 @@
}
static Result<void> DoLoadApex(const std::string& apex_name) {
- if (auto result = ParseApexConfigs(apex_name); !result.ok()) {
+ if (auto result = ParseRcScriptsFromApex(apex_name); !result.ok()) {
return result.error();
}
@@ -832,6 +832,12 @@
CHECKCALL(mount("tmpfs", "/apex", "tmpfs", MS_NOEXEC | MS_NOSUID | MS_NODEV,
"mode=0755,uid=0,gid=0"));
+ if (NeedsTwoMountNamespaces()) {
+ // /bootstrap-apex is used to mount "bootstrap" APEXes.
+ CHECKCALL(mount("tmpfs", "/bootstrap-apex", "tmpfs", MS_NOEXEC | MS_NOSUID | MS_NODEV,
+ "mode=0755,uid=0,gid=0"));
+ }
+
// /linkerconfig is used to keep generated linker configuration
CHECKCALL(mount("tmpfs", "/linkerconfig", "tmpfs", MS_NOEXEC | MS_NOSUID | MS_NODEV,
"mode=0755,uid=0,gid=0"));
diff --git a/init/mount_namespace.cpp b/init/mount_namespace.cpp
index fead371..7918f23 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,21 +66,6 @@
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;
- return true;
-}
-
static android::base::unique_fd bootstrap_ns_fd;
static android::base::unique_fd default_ns_fd;
@@ -100,6 +74,15 @@
} // namespace
+// In case we have two sets of APEXes (non-updatable, updatable), we need two separate mount
+// namespaces.
+bool NeedsTwoMountNamespaces() {
+ if (IsRecoveryMode()) return false;
+ // In microdroid, there's only one set of APEXes in built-in directories include block devices.
+ if (IsMicrodroid()) return false;
+ return true;
+}
+
bool SetupMountNamespaces() {
// Set the propagation type of / as shared so that any mounting event (e.g.
// /data) is by default visible to all processes. When private mounting is
@@ -180,6 +163,23 @@
PLOG(ERROR) << "Cannot switch back to bootstrap mount namespace";
return false;
}
+
+ // Some components (e.g. servicemanager) need to access bootstrap
+ // APEXes from the default mount namespace. To achieve that, we bind-mount
+ // /apex to /bootstrap-apex in the bootstrap mount namespace. Since /bootstrap-apex
+ // is "shared", the mounts are visible in the default mount namespace as well.
+ //
+ // The end result will look like:
+ // in the bootstrap mount namespace:
+ // /apex (== /bootstrap-apex)
+ // {bootstrap APEXes from the read-only partition}
+ //
+ // in the default mount namespace:
+ // /bootstrap-apex
+ // {bootstrap APEXes from the read-only partition}
+ // /apex
+ // {APEXes, can be from /data partition}
+ if (!(BindMount("/bootstrap-apex", "/apex"))) return false;
} else {
// Otherwise, default == bootstrap
default_ns_fd.reset(OpenMountNamespace());
@@ -193,7 +193,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/mount_namespace.h b/init/mount_namespace.h
index 5e3dab2..43c5476 100644
--- a/init/mount_namespace.h
+++ b/init/mount_namespace.h
@@ -24,9 +24,12 @@
enum MountNamespace { NS_BOOTSTRAP, NS_DEFAULT };
bool SetupMountNamespaces();
+
base::Result<void> SwitchToMountNamespaceIfNeeded(MountNamespace target_mount_namespace);
base::Result<MountNamespace> GetCurrentMountNamespace();
+bool NeedsTwoMountNamespaces();
+
} // namespace init
} // namespace android
diff --git a/init/property_service.cpp b/init/property_service.cpp
index 4242912..f5de17d 100644
--- a/init/property_service.cpp
+++ b/init/property_service.cpp
@@ -57,12 +57,13 @@
#include <android-base/result.h>
#include <android-base/stringprintf.h>
#include <android-base/strings.h>
-#include <private/android_filesystem_config.h>
+#include <fs_mgr.h>
#include <property_info_parser/property_info_parser.h>
#include <property_info_serializer/property_info_serializer.h>
#include <selinux/android.h>
#include <selinux/label.h>
#include <selinux/selinux.h>
+
#include "debug_ramdisk.h"
#include "epoll.h"
#include "init.h"
@@ -111,12 +112,12 @@
static bool persistent_properties_loaded = false;
+static int property_set_fd = -1;
static int from_init_socket = -1;
static int init_socket = -1;
static bool accept_messages = false;
static std::mutex accept_messages_lock;
static std::thread property_service_thread;
-static std::thread property_service_for_system_thread;
static std::unique_ptr<PersistWriteThread> persist_write_thread;
@@ -394,37 +395,31 @@
return {PROP_ERROR_INVALID_VALUE};
}
- if (name == "sys.powerctl") {
- // No action here - NotifyPropertyChange will trigger the appropriate action, and since this
- // can come to the second thread, we mustn't call out to the __system_property_* functions
- // which support multiple readers but only one mutator.
+ prop_info* pi = (prop_info*)__system_property_find(name.c_str());
+ if (pi != nullptr) {
+ // ro.* properties are actually "write-once".
+ if (StartsWith(name, "ro.")) {
+ *error = "Read-only property was already set";
+ return {PROP_ERROR_READ_ONLY_PROPERTY};
+ }
+
+ __system_property_update(pi, value.c_str(), valuelen);
} else {
- prop_info* pi = (prop_info*)__system_property_find(name.c_str());
- if (pi != nullptr) {
- // ro.* properties are actually "write-once".
- if (StartsWith(name, "ro.")) {
- *error = "Read-only property was already set";
- return {PROP_ERROR_READ_ONLY_PROPERTY};
- }
-
- __system_property_update(pi, value.c_str(), valuelen);
- } else {
- int rc = __system_property_add(name.c_str(), name.size(), value.c_str(), valuelen);
- if (rc < 0) {
- *error = "__system_property_add failed";
- return {PROP_ERROR_SET_FAILED};
- }
+ int rc = __system_property_add(name.c_str(), name.size(), value.c_str(), valuelen);
+ if (rc < 0) {
+ *error = "__system_property_add failed";
+ return {PROP_ERROR_SET_FAILED};
}
+ }
- // Don't write properties to disk until after we have read all default
- // properties to prevent them from being overwritten by default values.
- if (socket && persistent_properties_loaded && StartsWith(name, "persist.")) {
- if (persist_write_thread) {
- persist_write_thread->Write(name, value, std::move(*socket));
- return {};
- }
- WritePersistentProperty(name, value);
+ // Don't write properties to disk until after we have read all default
+ // properties to prevent them from being overwritten by default values.
+ if (socket && persistent_properties_loaded && StartsWith(name, "persist.")) {
+ if (persist_write_thread) {
+ persist_write_thread->Write(name, value, std::move(*socket));
+ return {};
}
+ WritePersistentProperty(name, value);
}
NotifyPropertyChange(name, value);
@@ -585,10 +580,10 @@
return *ret;
}
-static void handle_property_set_fd(int fd) {
+static void handle_property_set_fd() {
static constexpr uint32_t kDefaultSocketTimeout = 2000; /* ms */
- int s = accept4(fd, nullptr, nullptr, SOCK_CLOEXEC);
+ int s = accept4(property_set_fd, nullptr, nullptr, SOCK_CLOEXEC);
if (s == -1) {
return;
}
@@ -1323,7 +1318,8 @@
return;
}
- std::unique_ptr<DIR, int (*)(DIR*)> dir(opendir(get_android_dt_dir().c_str()), closedir);
+ std::unique_ptr<DIR, int (*)(DIR*)> dir(opendir(android::fs_mgr::GetAndroidDtDir().c_str()),
+ closedir);
if (!dir) return;
std::string dt_file;
@@ -1334,7 +1330,7 @@
continue;
}
- std::string file_name = get_android_dt_dir() + dp->d_name;
+ std::string file_name = android::fs_mgr::GetAndroidDtDir() + dp->d_name;
android::base::ReadFileToString(file_name, &dt_file);
std::replace(dt_file.begin(), dt_file.end(), ',', '.');
@@ -1346,7 +1342,7 @@
constexpr auto ANDROIDBOOT_PREFIX = "androidboot."sv;
static void ProcessKernelCmdline() {
- ImportKernelCmdline([&](const std::string& key, const std::string& value) {
+ android::fs_mgr::ImportKernelCmdline([&](const std::string& key, const std::string& value) {
if (StartsWith(key, ANDROIDBOOT_PREFIX)) {
InitPropertySet("ro.boot." + key.substr(ANDROIDBOOT_PREFIX.size()), value);
}
@@ -1355,7 +1351,7 @@
static void ProcessBootconfig() {
- ImportBootconfig([&](const std::string& key, const std::string& value) {
+ android::fs_mgr::ImportBootconfig([&](const std::string& key, const std::string& value) {
if (StartsWith(key, ANDROIDBOOT_PREFIX)) {
InitPropertySet("ro.boot." + key.substr(ANDROIDBOOT_PREFIX.size()), value);
}
@@ -1425,21 +1421,19 @@
}
}
-static void PropertyServiceThread(int fd, bool listen_init) {
+static void PropertyServiceThread() {
Epoll epoll;
if (auto result = epoll.Open(); !result.ok()) {
LOG(FATAL) << result.error();
}
- if (auto result = epoll.RegisterHandler(fd, std::bind(handle_property_set_fd, fd));
+ if (auto result = epoll.RegisterHandler(property_set_fd, handle_property_set_fd);
!result.ok()) {
LOG(FATAL) << result.error();
}
- if (listen_init) {
- if (auto result = epoll.RegisterHandler(init_socket, HandleInitSocket); !result.ok()) {
- LOG(FATAL) << result.error();
- }
+ if (auto result = epoll.RegisterHandler(init_socket, HandleInitSocket); !result.ok()) {
+ LOG(FATAL) << result.error();
}
while (true) {
@@ -1490,23 +1484,6 @@
cv_.notify_all();
}
-void StartThread(const char* name, int mode, int gid, std::thread& t, bool listen_init) {
- int fd = -1;
- if (auto result = CreateSocket(name, SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK,
- /*passcred=*/false, /*should_listen=*/false, mode, /*uid=*/0,
- /*gid=*/gid, /*socketcon=*/{});
- result.ok()) {
- fd = *result;
- } else {
- LOG(FATAL) << "start_property_service socket creation failed: " << result.error();
- }
-
- listen(fd, 8);
-
- auto new_thread = std::thread(PropertyServiceThread, fd, listen_init);
- t.swap(new_thread);
-}
-
void StartPropertyService(int* epoll_socket) {
InitPropertySet("ro.property_service.version", "2");
@@ -1518,9 +1495,19 @@
init_socket = sockets[1];
StartSendingMessages();
- StartThread(PROP_SERVICE_FOR_SYSTEM_NAME, 0660, AID_SYSTEM, property_service_for_system_thread,
- true);
- StartThread(PROP_SERVICE_NAME, 0666, 0, property_service_thread, false);
+ if (auto result = CreateSocket(PROP_SERVICE_NAME, SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK,
+ /*passcred=*/false, /*should_listen=*/false, 0666, /*uid=*/0,
+ /*gid=*/0, /*socketcon=*/{});
+ result.ok()) {
+ property_set_fd = *result;
+ } else {
+ LOG(FATAL) << "start_property_service socket creation failed: " << result.error();
+ }
+
+ listen(property_set_fd, 8);
+
+ auto new_thread = std::thread{PropertyServiceThread};
+ property_service_thread.swap(new_thread);
auto async_persist_writes =
android::base::GetBoolProperty("ro.property_service.async_persist_writes", false);
diff --git a/init/reboot_utils.cpp b/init/reboot_utils.cpp
index e6b868e..547b186 100644
--- a/init/reboot_utils.cpp
+++ b/init/reboot_utils.cpp
@@ -27,6 +27,7 @@
#include <android-base/properties.h>
#include <android-base/strings.h>
#include <cutils/android_reboot.h>
+#include <fs_mgr.h>
#include <unwindstack/AndroidUnwinder.h>
#include "capabilities.h"
@@ -48,13 +49,9 @@
const std::string kInitFatalPanicParamString = "androidboot.init_fatal_panic";
if (cmdline.find(kInitFatalPanicParamString) == std::string::npos) {
- init_fatal_panic = false;
- ImportBootconfig(
- [kInitFatalPanicParamString](const std::string& key, const std::string& value) {
- if (key == kInitFatalPanicParamString && value == "true") {
- init_fatal_panic = true;
- }
- });
+ std::string value;
+ init_fatal_panic = (android::fs_mgr::GetBootconfig(kInitFatalPanicParamString, &value) &&
+ value == "true");
} else {
const std::string kInitFatalPanicString = kInitFatalPanicParamString + "=true";
init_fatal_panic = cmdline.find(kInitFatalPanicString) != std::string::npos;
@@ -68,11 +65,7 @@
const std::string kRebootTargetString = "androidboot.init_fatal_reboot_target";
auto start_pos = cmdline.find(kRebootTargetString);
if (start_pos == std::string::npos) {
- ImportBootconfig([kRebootTargetString](const std::string& key, const std::string& value) {
- if (key == kRebootTargetString) {
- init_fatal_reboot_target = value;
- }
- });
+ android::fs_mgr::GetBootconfig(kRebootTargetString, &init_fatal_reboot_target);
// We already default to bootloader if no setting is provided.
} else {
const std::string kRebootTargetStringPattern = kRebootTargetString + "=";
diff --git a/init/selinux.cpp b/init/selinux.cpp
index a936532..ac102eb 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>
@@ -103,23 +101,14 @@
enum EnforcingStatus { SELINUX_PERMISSIVE, SELINUX_ENFORCING };
EnforcingStatus StatusFromProperty() {
- EnforcingStatus status = SELINUX_ENFORCING;
-
- ImportKernelCmdline([&](const std::string& key, const std::string& value) {
- if (key == "androidboot.selinux" && value == "permissive") {
- status = SELINUX_PERMISSIVE;
- }
- });
-
- if (status == SELINUX_ENFORCING) {
- ImportBootconfig([&](const std::string& key, const std::string& value) {
- if (key == "androidboot.selinux" && value == "permissive") {
- status = SELINUX_PERMISSIVE;
- }
- });
+ std::string value;
+ if (android::fs_mgr::GetKernelCmdline("androidboot.selinux", &value) && value == "permissive") {
+ return SELINUX_PERMISSIVE;
}
-
- return status;
+ if (android::fs_mgr::GetBootconfig("androidboot.selinux", &value) && value == "permissive") {
+ return SELINUX_PERMISSIVE;
+ }
+ return SELINUX_ENFORCING;
}
bool IsEnforcing() {
@@ -300,8 +289,6 @@
}
constexpr const char plat_policy_cil_file[] = "/system/etc/selinux/plat_sepolicy.cil";
-constexpr const char kMicrodroidPrecompiledSepolicy[] =
- "/system/etc/selinux/microdroid_precompiled_sepolicy";
bool IsSplitPolicyDevice() {
return access(plat_policy_cil_file, R_OK) != -1;
@@ -499,25 +486,19 @@
bool OpenMonolithicPolicy(PolicyFile* policy_file) {
static constexpr char kSepolicyFile[] = "/sepolicy";
- // In Microdroid the precompiled sepolicy is located on /system, since there is no vendor code.
- // TODO(b/287206497): refactor once we start conditionally compiling init for Microdroid.
- std::string monolithic_policy_file = access(kMicrodroidPrecompiledSepolicy, R_OK) == 0
- ? kMicrodroidPrecompiledSepolicy
- : kSepolicyFile;
- LOG(INFO) << "Opening SELinux policy from monolithic file " << monolithic_policy_file;
- policy_file->fd.reset(open(monolithic_policy_file.c_str(), O_RDONLY | O_CLOEXEC | O_NOFOLLOW));
+ 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";
return false;
}
- policy_file->path = monolithic_policy_file;
+ policy_file->path = kSepolicyFile;
return true;
}
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";
@@ -621,24 +602,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)) {
@@ -661,18 +624,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";
@@ -705,23 +657,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.
@@ -787,6 +735,14 @@
TEMP_FAILURE_RETRY(send(fd.get(), &request, sizeof(request), 0));
}
+int RestoreconIfExists(const char* path, unsigned int flags) {
+ if (access(path, F_OK) != 0 && errno == ENOENT) {
+ // Avoid error message for path that is expected to not always exist.
+ return 0;
+ }
+ return selinux_android_restorecon(path, flags);
+}
+
} // namespace
void SelinuxRestoreContext() {
@@ -809,14 +765,14 @@
selinux_android_restorecon("/dev/device-mapper", 0);
selinux_android_restorecon("/apex", 0);
-
+ selinux_android_restorecon("/bootstrap-apex", 0);
selinux_android_restorecon("/linkerconfig", 0);
// adb remount, snapshot-based updates, and DSUs all create files during
// first-stage init.
- selinux_android_restorecon(SnapshotManager::GetGlobalRollbackIndicatorPath().c_str(), 0);
- selinux_android_restorecon("/metadata/gsi", SELINUX_ANDROID_RESTORECON_RECURSE |
- SELINUX_ANDROID_RESTORECON_SKIP_SEHASH);
+ RestoreconIfExists(SnapshotManager::GetGlobalRollbackIndicatorPath().c_str(), 0);
+ RestoreconIfExists("/metadata/gsi",
+ SELINUX_ANDROID_RESTORECON_RECURSE | SELINUX_ANDROID_RESTORECON_SKIP_SEHASH);
}
int SelinuxKlogCallback(int type, const char* fmt, ...) {
@@ -858,6 +814,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,
@@ -961,6 +921,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
@@ -976,20 +956,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();
@@ -1001,9 +970,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();
}
@@ -1021,6 +989,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 2945708..5e900ee 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"
@@ -323,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
@@ -421,7 +420,7 @@
}
});
- if (is_updatable() && !ServiceList::GetInstance().IsServicesUpdated()) {
+ if (is_updatable() && !IsDefaultMountNamespaceReady()) {
// Don't delay the service for ExecStart() as the semantic is that
// the caller might depend on the side effect of the execution.
return Error() << "Cannot start an updatable service '" << name_
@@ -582,7 +581,7 @@
}
});
- if (is_updatable() && !ServiceList::GetInstance().IsServicesUpdated()) {
+ if (is_updatable() && !IsDefaultMountNamespaceReady()) {
ServiceList::GetInstance().DelayService(*this);
return Error() << "Cannot start an updatable service '" << name_
<< "' before configs from APEXes are all loaded. "
diff --git a/init/service.h b/init/service.h
index b858eef..9f09cef 100644
--- a/init/service.h
+++ b/init/service.h
@@ -60,7 +60,7 @@
#define SVC_GENTLE_KILL 0x2000 // This service should be stopped with SIGTERM instead of SIGKILL
// Will still be SIGKILLed after timeout period of 200 ms
-#define NR_SVC_SUPP_GIDS 12 // twelve supplementary groups
+#define NR_SVC_SUPP_GIDS 32 // thirty two supplementary groups
namespace android {
namespace init {
diff --git a/init/service_list.cpp b/init/service_list.cpp
index 937d82e..1c56e8a 100644
--- a/init/service_list.cpp
+++ b/init/service_list.cpp
@@ -76,10 +76,7 @@
return post_data_;
}
-void ServiceList::MarkServicesUpdate() {
- services_update_finished_ = true;
-
- // start the delayed services
+void ServiceList::StartDelayedServices() {
for (const auto& name : delayed_service_names_) {
Service* service = FindService(name);
if (service == nullptr) {
@@ -94,7 +91,7 @@
}
void ServiceList::DelayService(const Service& service) {
- if (services_update_finished_) {
+ if (IsDefaultMountNamespaceReady()) {
LOG(ERROR) << "Cannot delay the start of service '" << service.name()
<< "' because all services are already updated. Ignoring.";
return;
diff --git a/init/service_list.h b/init/service_list.h
index f858bc3..44e8453 100644
--- a/init/service_list.h
+++ b/init/service_list.h
@@ -85,14 +85,10 @@
void MarkPostData();
bool IsPostData();
- void MarkServicesUpdate();
- bool IsServicesUpdated() const { return services_update_finished_; }
void DelayService(const Service& service);
+ void StartDelayedServices();
- void ResetState() {
- post_data_ = false;
- services_update_finished_ = false;
- }
+ void ResetState() { post_data_ = false; }
auto size() const { return services_.size(); }
@@ -100,7 +96,6 @@
std::vector<std::unique_ptr<Service>> services_;
bool post_data_ = false;
- bool services_update_finished_ = false;
std::vector<std::string> delayed_service_names_;
};
diff --git a/init/test_kill_services/init_kill_services_test.cpp b/init/test_kill_services/init_kill_services_test.cpp
index dd46064..510ad8a 100644
--- a/init/test_kill_services/init_kill_services_test.cpp
+++ b/init/test_kill_services/init_kill_services_test.cpp
@@ -32,7 +32,7 @@
// 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", 60s))
+ ASSERT_TRUE(WaitForProperty("init.svc.apexd", "stopped", 120s))
<< (system("cat /dev/binderfs/binder_logs/state"), "apexd won't stop");
LOG(INFO) << "hello " << service_name << "!";
diff --git a/init/util.cpp b/init/util.cpp
index bc8ea6e..e760a59 100644
--- a/init/util.cpp
+++ b/init/util.cpp
@@ -42,6 +42,10 @@
#include <cutils/sockets.h>
#include <selinux/android.h>
+#if defined(__ANDROID__)
+#include <fs_mgr.h>
+#endif
+
#ifdef INIT_FULL_SOURCES
#include <android/api-level.h>
#include <sys/system_properties.h>
@@ -60,8 +64,6 @@
namespace android {
namespace init {
-const std::string kDefaultAndroidDtDir("/proc/device-tree/firmware/android/");
-
const std::string kDataDirPrefix("/data/");
void (*trigger_shutdown)(const std::string& command) = nullptr;
@@ -240,33 +242,6 @@
return -1;
}
-void ImportKernelCmdline(const std::function<void(const std::string&, const std::string&)>& fn) {
- std::string cmdline;
- android::base::ReadFileToString("/proc/cmdline", &cmdline);
-
- for (const auto& entry : android::base::Split(android::base::Trim(cmdline), " ")) {
- std::vector<std::string> pieces = android::base::Split(entry, "=");
- if (pieces.size() == 2) {
- fn(pieces[0], pieces[1]);
- }
- }
-}
-
-void ImportBootconfig(const std::function<void(const std::string&, const std::string&)>& fn) {
- std::string bootconfig;
- android::base::ReadFileToString("/proc/bootconfig", &bootconfig);
-
- for (const auto& entry : android::base::Split(bootconfig, "\n")) {
- std::vector<std::string> pieces = android::base::Split(entry, "=");
- if (pieces.size() == 2) {
- // get rid of the extra space between a list of values and remove the quotes.
- std::string value = android::base::StringReplace(pieces[1], "\", \"", ",", true);
- value.erase(std::remove(value.begin(), value.end(), '"'), value.end());
- fn(android::base::Trim(pieces[0]), android::base::Trim(value));
- }
- }
-}
-
bool make_dir(const std::string& path, mode_t mode) {
std::string secontext;
if (SelabelLookupFileContext(path, mode, &secontext) && !secontext.empty()) {
@@ -375,45 +350,18 @@
return dst;
}
-static std::string init_android_dt_dir() {
- // Use the standard procfs-based path by default
- std::string android_dt_dir = kDefaultAndroidDtDir;
- // The platform may specify a custom Android DT path in kernel cmdline
- ImportKernelCmdline([&](const std::string& key, const std::string& value) {
- if (key == "androidboot.android_dt_dir") {
- android_dt_dir = value;
- }
- });
- // ..Or bootconfig
- if (android_dt_dir == kDefaultAndroidDtDir) {
- ImportBootconfig([&](const std::string& key, const std::string& value) {
- if (key == "androidboot.android_dt_dir") {
- android_dt_dir = value;
- }
- });
- }
-
- LOG(INFO) << "Using Android DT directory " << android_dt_dir;
- return android_dt_dir;
-}
-
-// FIXME: The same logic is duplicated in system/core/fs_mgr/
-const std::string& get_android_dt_dir() {
- // Set once and saves time for subsequent calls to this function
- static const std::string kAndroidDtDir = init_android_dt_dir();
- return kAndroidDtDir;
-}
-
// Reads the content of device tree file under the platform's Android DT directory.
// Returns true if the read is success, false otherwise.
bool read_android_dt_file(const std::string& sub_path, std::string* dt_content) {
- const std::string file_name = get_android_dt_dir() + sub_path;
+#if defined(__ANDROID__)
+ const std::string file_name = android::fs_mgr::GetAndroidDtDir() + sub_path;
if (android::base::ReadFileToString(file_name, dt_content)) {
if (!dt_content->empty()) {
dt_content->pop_back(); // Trims the trailing '\0' out.
return true;
}
}
+#endif
return false;
}
@@ -732,11 +680,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..2d02182 100644
--- a/init/util.h
+++ b/init/util.h
@@ -53,16 +53,11 @@
Result<uid_t> DecodeUid(const std::string& name);
bool mkdir_recursive(const std::string& pathname, mode_t mode);
-int wait_for_file(const char *filename, std::chrono::nanoseconds timeout);
-void ImportKernelCmdline(const std::function<void(const std::string&, const std::string&)>&);
-void ImportBootconfig(const std::function<void(const std::string&, const std::string&)>&);
+int wait_for_file(const char* filename, std::chrono::nanoseconds timeout);
bool make_dir(const std::string& path, mode_t mode);
bool is_dir(const char* pathname);
Result<std::string> ExpandProps(const std::string& src);
-// Returns the platform's Android DT directory as specified in the kernel cmdline.
-// If the platform does not configure a custom DT path, returns the standard one (based in procfs).
-const std::string& get_android_dt_dir();
// Reads or compares the content of device tree file under the platform's Android DT directory.
bool read_android_dt_file(const std::string& sub_path, std::string* dt_content);
bool is_android_dt_value_expected(const std::string& sub_path, const std::string& expected_content);
@@ -105,7 +100,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..55a8694 100644
--- a/libcutils/Android.bp
+++ b/libcutils/Android.bp
@@ -162,7 +162,6 @@
"properties.cpp",
"record_stream.cpp",
"strlcpy.c",
- "threads.cpp",
],
target: {
@@ -219,11 +218,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/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/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/fs_config.cpp b/libcutils/fs_config.cpp
index f90a1bc..26ac576 100644
--- a/libcutils/fs_config.cpp
+++ b/libcutils/fs_config.cpp
@@ -214,6 +214,7 @@
#endif
{ 00755, AID_ROOT, AID_ROOT, 0, "first_stage_ramdisk/system/bin/resize2fs" },
{ 00755, AID_ROOT, AID_ROOT, 0, "first_stage_ramdisk/system/bin/snapuserd" },
+ { 00755, AID_ROOT, AID_ROOT, 0, "first_stage_ramdisk/system/bin/snapuserd_ramdisk" },
{ 00755, AID_ROOT, AID_ROOT, 0, "first_stage_ramdisk/system/bin/tune2fs" },
{ 00755, AID_ROOT, AID_ROOT, 0, "first_stage_ramdisk/system/bin/fsck.f2fs" },
// generic defaults
diff --git a/libcutils/include/cutils/threads.h b/libcutils/include/cutils/threads.h
deleted file mode 100644
index 0082c6c..0000000
--- a/libcutils/include/cutils/threads.h
+++ /dev/null
@@ -1,40 +0,0 @@
-/*
- * Copyright (C) 2007 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 <sys/types.h>
-
-#if defined(_WIN32)
-#include <windows.h>
-#else
-#include <pthread.h>
-#endif
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-//
-// Deprecated: use android::base::GetThreadId instead, which doesn't truncate on Mac/Windows.
-//
-#if !defined(__GLIBC__) || __GLIBC__ >= 2 && __GLIBC_MINOR__ < 32
-extern pid_t gettid();
-#endif
-
-#ifdef __cplusplus
-}
-#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/include_outside_system/cutils/threads.h b/libcutils/include_outside_system/cutils/threads.h
deleted file mode 120000
index 99330ff..0000000
--- a/libcutils/include_outside_system/cutils/threads.h
+++ /dev/null
@@ -1 +0,0 @@
-../../include/cutils/threads.h
\ No newline at end of file
diff --git a/libcutils/threads.cpp b/libcutils/threads.cpp
deleted file mode 100644
index 6ece7a3..0000000
--- a/libcutils/threads.cpp
+++ /dev/null
@@ -1,43 +0,0 @@
-/*
-** Copyright (C) 2007, 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 <cutils/threads.h>
-
-#if defined(__APPLE__)
-#include <stdint.h>
-#elif defined(__linux__)
-#include <syscall.h>
-#include <unistd.h>
-#elif defined(_WIN32)
-#include <windows.h>
-#endif
-
-#if defined(__BIONIC__) || defined(__GLIBC__) && __GLIBC_MINOR__ >= 32
-// 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.
-#else
-pid_t gettid() {
-#if defined(__APPLE__)
- uint64_t tid;
- pthread_threadid_np(NULL, &tid);
- return tid;
-#elif defined(__linux__)
- return syscall(__NR_gettid);
-#elif defined(_WIN32)
- return GetCurrentThreadId();
-#endif
-}
-#endif
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/libmodprobe/libmodprobe.cpp b/libmodprobe/libmodprobe.cpp
index 858b955..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;
@@ -524,11 +525,8 @@
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_) {
- if (mod_with_deps.find(module_loaded) != mod_with_deps.end()) {
- mod_with_deps.erase(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_) {
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 1f29040..4506439 100644
--- a/libprocessgroup/processgroup.cpp
+++ b/libprocessgroup/processgroup.cpp
@@ -206,11 +206,11 @@
}
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) {
@@ -223,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/libprocessgroup/task_profiles.cpp b/libprocessgroup/task_profiles.cpp
index 44dba2a..f51b076 100644
--- a/libprocessgroup/task_profiles.cpp
+++ b/libprocessgroup/task_profiles.cpp
@@ -114,9 +114,26 @@
IProfileAttribute::~IProfileAttribute() = default;
-void ProfileAttribute::Reset(const CgroupController& controller, const std::string& file_name) {
+const std::string& ProfileAttribute::file_name() const {
+ if (controller()->version() == 2 && !file_v2_name_.empty()) return file_v2_name_;
+ return file_name_;
+}
+
+void ProfileAttribute::Reset(const CgroupController& controller, const std::string& file_name,
+ const std::string& file_v2_name) {
controller_ = controller;
file_name_ = file_name;
+ file_v2_name_ = file_v2_name;
+}
+
+bool ProfileAttribute::GetPathForProcess(uid_t uid, pid_t pid, std::string* path) const {
+ if (controller()->version() == 2) {
+ // all cgroup v2 attributes use the same process group hierarchy
+ *path = StringPrintf("%s/uid_%u/pid_%d/%s", controller()->path(), uid, pid,
+ file_name().c_str());
+ return true;
+ }
+ return GetPathForTask(pid, path);
}
bool ProfileAttribute::GetPathForTask(int tid, std::string* path) const {
@@ -129,12 +146,11 @@
return true;
}
- const std::string& file_name =
- controller()->version() == 2 && !file_v2_name_.empty() ? file_v2_name_ : file_name_;
if (subgroup.empty()) {
- *path = StringPrintf("%s/%s", controller()->path(), file_name.c_str());
+ *path = StringPrintf("%s/%s", controller()->path(), file_name().c_str());
} else {
- *path = StringPrintf("%s/%s/%s", controller()->path(), subgroup.c_str(), file_name.c_str());
+ *path = StringPrintf("%s/%s/%s", controller()->path(), subgroup.c_str(),
+ file_name().c_str());
}
return true;
}
@@ -144,9 +160,7 @@
return true;
}
- const std::string& file_name =
- controller()->version() == 2 && !file_v2_name_.empty() ? file_v2_name_ : file_name_;
- *path = StringPrintf("%s/uid_%d/%s", controller()->path(), uid, file_name.c_str());
+ *path = StringPrintf("%s/uid_%u/%s", controller()->path(), uid, file_name().c_str());
return true;
}
@@ -205,18 +219,7 @@
#endif
-bool SetAttributeAction::ExecuteForProcess(uid_t, pid_t pid) const {
- return ExecuteForTask(pid);
-}
-
-bool SetAttributeAction::ExecuteForTask(int tid) const {
- std::string path;
-
- if (!attribute_->GetPathForTask(tid, &path)) {
- LOG(ERROR) << "Failed to find cgroup for tid " << tid;
- return false;
- }
-
+bool SetAttributeAction::WriteValueToFile(const std::string& path) const {
if (!WriteStringToFile(value_, path)) {
if (access(path.c_str(), F_OK) < 0) {
if (optional_) {
@@ -236,6 +239,28 @@
return true;
}
+bool SetAttributeAction::ExecuteForProcess(uid_t uid, pid_t pid) const {
+ std::string path;
+
+ if (!attribute_->GetPathForProcess(uid, pid, &path)) {
+ LOG(ERROR) << "Failed to find cgroup for uid " << uid << " pid " << pid;
+ return false;
+ }
+
+ return WriteValueToFile(path);
+}
+
+bool SetAttributeAction::ExecuteForTask(int tid) const {
+ std::string path;
+
+ if (!attribute_->GetPathForTask(tid, &path)) {
+ LOG(ERROR) << "Failed to find cgroup for tid " << tid;
+ return false;
+ }
+
+ return WriteValueToFile(path);
+}
+
bool SetAttributeAction::ExecuteForUID(uid_t uid) const {
std::string path;
@@ -816,7 +841,7 @@
attributes_[name] =
std::make_unique<ProfileAttribute>(controller, file_attr, file_v2_attr);
} else {
- iter->second->Reset(controller, file_attr);
+ iter->second->Reset(controller, file_attr, file_v2_attr);
}
} else {
LOG(WARNING) << "Controller " << controller_name << " is not found";
diff --git a/libprocessgroup/task_profiles.h b/libprocessgroup/task_profiles.h
index a62c5b0..4663f64 100644
--- a/libprocessgroup/task_profiles.h
+++ b/libprocessgroup/task_profiles.h
@@ -32,9 +32,11 @@
class IProfileAttribute {
public:
virtual ~IProfileAttribute() = 0;
- virtual void Reset(const CgroupController& controller, const std::string& file_name) = 0;
+ virtual void Reset(const CgroupController& controller, const std::string& file_name,
+ const std::string& file_v2_name) = 0;
virtual const CgroupController* controller() const = 0;
virtual const std::string& file_name() const = 0;
+ virtual bool GetPathForProcess(uid_t uid, pid_t pid, std::string* path) const = 0;
virtual bool GetPathForTask(int tid, std::string* path) const = 0;
virtual bool GetPathForUID(uid_t uid, std::string* path) const = 0;
};
@@ -50,9 +52,11 @@
~ProfileAttribute() = default;
const CgroupController* controller() const override { return &controller_; }
- const std::string& file_name() const override { return file_name_; }
- void Reset(const CgroupController& controller, const std::string& file_name) override;
+ const std::string& file_name() const override;
+ void Reset(const CgroupController& controller, const std::string& file_name,
+ const std::string& file_v2_name) override;
+ bool GetPathForProcess(uid_t uid, pid_t pid, std::string* path) const override;
bool GetPathForTask(int tid, std::string* path) const override;
bool GetPathForUID(uid_t uid, std::string* path) const override;
@@ -131,6 +135,8 @@
const IProfileAttribute* attribute_;
std::string value_;
bool optional_;
+
+ bool WriteValueToFile(const std::string& path) const;
};
// Set cgroup profile element
diff --git a/libprocessgroup/task_profiles_test.cpp b/libprocessgroup/task_profiles_test.cpp
index eadbe76..99d819a 100644
--- a/libprocessgroup/task_profiles_test.cpp
+++ b/libprocessgroup/task_profiles_test.cpp
@@ -102,7 +102,8 @@
public:
ProfileAttributeMock(const std::string& file_name) : file_name_(file_name) {}
~ProfileAttributeMock() override = default;
- void Reset(const CgroupController& controller, const std::string& file_name) override {
+ void Reset(const CgroupController& controller, const std::string& file_name,
+ const std::string& file_v2_name) override {
CHECK(false);
}
const CgroupController* controller() const override {
@@ -110,6 +111,9 @@
return {};
}
const std::string& file_name() const override { return file_name_; }
+ bool GetPathForProcess(uid_t uid, pid_t pid, std::string* path) const override {
+ return GetPathForTask(pid, path);
+ }
bool GetPathForTask(int tid, std::string* path) const override {
#ifdef __ANDROID__
CHECK(CgroupGetControllerPath(CGROUPV2_CONTROLLER_NAME, path));
@@ -125,9 +129,7 @@
return true;
};
- bool GetPathForUID(uid_t, std::string*) const override {
- return false;
- }
+ bool GetPathForUID(uid_t, std::string*) const override { return false; }
private:
const std::string file_name_;
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/src/NetlinkEvent.cpp b/libsysutils/src/NetlinkEvent.cpp
index cd9db54..55bbe46 100644
--- a/libsysutils/src/NetlinkEvent.cpp
+++ b/libsysutils/src/NetlinkEvent.cpp
@@ -150,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/ProcessCallStack.cpp b/libutils/ProcessCallStack.cpp
index f054de9..4b27bdd 100644
--- a/libutils/ProcessCallStack.cpp
+++ b/libutils/ProcessCallStack.cpp
@@ -205,8 +205,7 @@
}
void ProcessCallStack::printInternal(Printer& printer, Printer& csPrinter) const {
- dumpProcessHeader(printer, getpid(),
- getTimeString(mTimeUpdated).string());
+ dumpProcessHeader(printer, getpid(), getTimeString(mTimeUpdated).c_str());
for (size_t i = 0; i < mThreadMap.size(); ++i) {
pid_t tid = mThreadMap.keyAt(i);
@@ -214,7 +213,7 @@
const String8& threadName = threadInfo.threadName;
printer.printLine("");
- printer.printFormatLine("\"%s\" sysTid=%d", threadName.string(), tid);
+ printer.printFormatLine("\"%s\" sysTid=%d", threadName.c_str(), tid);
threadInfo.callStack.print(csPrinter);
}
diff --git a/libutils/RefBase.cpp b/libutils/RefBase.cpp
index ab122c7..e0a2846 100644
--- a/libutils/RefBase.cpp
+++ b/libutils/RefBase.cpp
@@ -330,7 +330,7 @@
this);
int rc = open(name, O_RDWR | O_CREAT | O_APPEND, 0644);
if (rc >= 0) {
- (void)write(rc, text.string(), text.length());
+ (void)write(rc, text.c_str(), text.length());
close(rc);
ALOGI("STACK TRACE for %p saved in %s", this, name);
}
diff --git a/libutils/String16.cpp b/libutils/String16.cpp
index 68642d8..38d483e 100644
--- a/libutils/String16.cpp
+++ b/libutils/String16.cpp
@@ -26,7 +26,7 @@
static const StaticString16 emptyString(u"");
static inline char16_t* getEmptyString() {
- return const_cast<char16_t*>(emptyString.string());
+ return const_cast<char16_t*>(emptyString.c_str());
}
// ---------------------------------------------------------------------------
@@ -112,10 +112,7 @@
String16::String16(const char16_t* o, size_t len) : mString(allocFromUTF16(o, len)) {}
-String16::String16(const String8& o)
- : mString(allocFromUTF8(o.string(), o.size()))
-{
-}
+String16::String16(const String8& o) : mString(allocFromUTF8(o.c_str(), o.size())) {}
String16::String16(const char* o)
: mString(allocFromUTF8(o, strlen(o)))
@@ -173,7 +170,7 @@
LOG_ALWAYS_FATAL("Not implemented");
}
- return setTo(other.string()+begin, len);
+ return setTo(other.c_str() + begin, len);
}
status_t String16::setTo(const char16_t* other)
@@ -200,7 +197,7 @@
}
status_t String16::append(const String16& other) {
- return append(other.string(), other.size());
+ return append(other.c_str(), other.size());
}
status_t String16::append(const char16_t* chrs, size_t otherLen) {
@@ -286,7 +283,7 @@
{
const size_t ps = prefix.size();
if (ps > size()) return false;
- return strzcmp16(mString, ps, prefix.string(), ps) == 0;
+ return strzcmp16(mString, ps, prefix.c_str(), ps) == 0;
}
bool String16::startsWith(const char16_t* prefix) const
diff --git a/libutils/String16_fuzz.cpp b/libutils/String16_fuzz.cpp
index d7e5ec7..a271aee 100644
--- a/libutils/String16_fuzz.cpp
+++ b/libutils/String16_fuzz.cpp
@@ -25,7 +25,7 @@
// Bytes and size
([](FuzzedDataProvider&, android::String16 str1, android::String16) -> void {
- str1.string();
+ str1.c_str();
}),
([](FuzzedDataProvider&, android::String16 str1, android::String16) -> void {
str1.isStaticString();
@@ -39,7 +39,7 @@
str1.startsWith(str2);
}),
([](FuzzedDataProvider&, android::String16 str1, android::String16 str2) -> void {
- str1.contains(str2.string());
+ str1.contains(str2.c_str());
}),
([](FuzzedDataProvider&, android::String16 str1, android::String16 str2) -> void {
str1.compare(str2);
@@ -52,7 +52,7 @@
([](FuzzedDataProvider& dataProvider, android::String16 str1,
android::String16 str2) -> void {
int pos = dataProvider.ConsumeIntegralInRange<int>(0, str1.size());
- str1.insert(pos, str2.string());
+ str1.insert(pos, str2.c_str());
}),
// Find and replace operations
diff --git a/libutils/String8.cpp b/libutils/String8.cpp
index 82f5cb6..2b72847 100644
--- a/libutils/String8.cpp
+++ b/libutils/String8.cpp
@@ -39,10 +39,6 @@
namespace android {
-// Separator used by resource paths. This is not platform dependent contrary
-// to OS_PATH_SEPARATOR.
-#define RES_PATH_SEPARATOR '/'
-
static inline char* getEmptyString() {
static SharedBuffer* gEmptyStringBuf = [] {
SharedBuffer* buf = SharedBuffer::alloc(1);
@@ -150,10 +146,7 @@
}
}
-String8::String8(const String16& o)
- : mString(allocFromUTF16(o.string(), o.size()))
-{
-}
+String8::String8(const String16& o) : mString(allocFromUTF16(o.c_str(), o.size())) {}
String8::String8(const char16_t* o)
: mString(allocFromUTF16(o, strlen16(o)))
@@ -267,7 +260,7 @@
return OK;
}
- return real_append(other.string(), otherLen);
+ return real_append(other.c_str(), otherLen);
}
status_t String8::append(const char* other)
@@ -393,6 +386,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;
@@ -580,20 +578,4 @@
}
}
-String8& String8::convertToResPath()
-{
-#if OS_PATH_SEPARATOR != RES_PATH_SEPARATOR
- size_t len = length();
- if (len > 0) {
- char * buf = lockBuffer(len);
- for (char * end = buf + len; buf < end; ++buf) {
- if (*buf == OS_PATH_SEPARATOR)
- *buf = RES_PATH_SEPARATOR;
- }
- unlockBuffer(len);
- }
-#endif
- return *this;
-}
-
}; // namespace android
diff --git a/libutils/String8_fuzz.cpp b/libutils/String8_fuzz.cpp
index faf49b6..6f7a54f 100644
--- a/libutils/String8_fuzz.cpp
+++ b/libutils/String8_fuzz.cpp
@@ -34,7 +34,7 @@
str1->bytes();
},
[](FuzzedDataProvider*, android::String8* str1, android::String8*) -> void {
- str1->isEmpty();
+ str1->empty();
},
[](FuzzedDataProvider*, android::String8* str1, android::String8*) -> void {
str1->length();
@@ -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 {
@@ -81,9 +83,6 @@
str1->getPathDir();
},
[](FuzzedDataProvider*, android::String8* str1, android::String8*) -> void {
- str1->convertToResPath();
- },
- [](FuzzedDataProvider*, android::String8* str1, android::String8*) -> void {
std::shared_ptr<android::String8> path_out_str =
std::make_shared<android::String8>();
str1->walkPath(path_out_str.get());
diff --git a/libutils/String8_test.cpp b/libutils/String8_test.cpp
index 1356cd0..9c12cb1 100644
--- a/libutils/String8_test.cpp
+++ b/libutils/String8_test.cpp
@@ -36,7 +36,7 @@
TEST_F(String8Test, Cstr) {
String8 tmp("Hello, world!");
- EXPECT_STREQ(tmp.string(), "Hello, world!");
+ EXPECT_STREQ(tmp.c_str(), "Hello, world!");
}
TEST_F(String8Test, OperatorPlus) {
@@ -45,16 +45,16 @@
// Test adding String8 + const char*
const char* ccsrc2 = "world!";
String8 dst1 = src1 + ccsrc2;
- EXPECT_STREQ(dst1.string(), "Hello, world!");
- EXPECT_STREQ(src1.string(), "Hello, ");
+ EXPECT_STREQ(dst1.c_str(), "Hello, world!");
+ EXPECT_STREQ(src1.c_str(), "Hello, ");
EXPECT_STREQ(ccsrc2, "world!");
// Test adding String8 + String8
String8 ssrc2("world!");
String8 dst2 = src1 + ssrc2;
- EXPECT_STREQ(dst2.string(), "Hello, world!");
- EXPECT_STREQ(src1.string(), "Hello, ");
- EXPECT_STREQ(ssrc2.string(), "world!");
+ EXPECT_STREQ(dst2.c_str(), "Hello, world!");
+ EXPECT_STREQ(src1.c_str(), "Hello, ");
+ EXPECT_STREQ(ssrc2.c_str(), "world!");
}
TEST_F(String8Test, OperatorPlusEquals) {
@@ -63,14 +63,14 @@
// Testing String8 += String8
String8 src2(" is my passport.");
src1 += src2;
- EXPECT_STREQ(src1.string(), "My voice is my passport.");
- EXPECT_STREQ(src2.string(), " is my passport.");
+ EXPECT_STREQ(src1.c_str(), "My voice is my passport.");
+ EXPECT_STREQ(src2.c_str(), " is my passport.");
// Adding const char* to the previous string.
const char* src3 = " Verify me.";
src1 += src3;
- EXPECT_STREQ(src1.string(), "My voice is my passport. Verify me.");
- EXPECT_STREQ(src2.string(), " is my passport.");
+ EXPECT_STREQ(src1.c_str(), "My voice is my passport. Verify me.");
+ EXPECT_STREQ(src2.c_str(), " is my passport.");
EXPECT_STREQ(src3, " Verify me.");
}
@@ -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/Tokenizer.cpp b/libutils/Tokenizer.cpp
index c3ec165..9fc955c 100644
--- a/libutils/Tokenizer.cpp
+++ b/libutils/Tokenizer.cpp
@@ -50,15 +50,15 @@
*outTokenizer = nullptr;
int result = OK;
- int fd = ::open(filename.string(), O_RDONLY);
+ int fd = ::open(filename.c_str(), O_RDONLY);
if (fd < 0) {
result = -errno;
- ALOGE("Error opening file '%s': %s", filename.string(), strerror(errno));
+ ALOGE("Error opening file '%s': %s", filename.c_str(), strerror(errno));
} else {
struct stat stat;
if (fstat(fd, &stat)) {
result = -errno;
- ALOGE("Error getting size of file '%s': %s", filename.string(), strerror(errno));
+ ALOGE("Error getting size of file '%s': %s", filename.c_str(), strerror(errno));
} else {
size_t length = size_t(stat.st_size);
@@ -80,7 +80,7 @@
ssize_t nrd = read(fd, buffer, length);
if (nrd < 0) {
result = -errno;
- ALOGE("Error reading file '%s': %s", filename.string(), strerror(errno));
+ ALOGE("Error reading file '%s': %s", filename.c_str(), strerror(errno));
delete[] buffer;
buffer = nullptr;
} else {
@@ -106,7 +106,7 @@
String8 Tokenizer::getLocation() const {
String8 result;
- result.appendFormat("%s:%d", mFilename.string(), mLineNumber);
+ result.appendFormat("%s:%d", mFilename.c_str(), mLineNumber);
return result;
}
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/abi-dumps/arm64/source-based/libutils.so.lsdump b/libutils/abi-dumps/arm64/source-based/libutils.so.lsdump
index c89af9e..46badde 100644
--- a/libutils/abi-dumps/arm64/source-based/libutils.so.lsdump
+++ b/libutils/abi-dumps/arm64/source-based/libutils.so.lsdump
@@ -725,9 +725,6 @@
"name" : "_ZN7android7String813appendFormatVEPKcSt9__va_list"
},
{
- "name" : "_ZN7android7String816convertToResPathEv"
- },
- {
"name" : "_ZN7android7String85clearEv"
},
{
@@ -6928,19 +6925,6 @@
"source_file" : "system/core/libutils/include/utils/String8.h"
},
{
- "function_name" : "android::String8::convertToResPath",
- "linker_set_key" : "_ZN7android7String816convertToResPathEv",
- "parameters" :
- [
- {
- "is_this_ptr" : true,
- "referenced_type" : "_ZTIPN7android7String8E"
- }
- ],
- "return_type" : "_ZTIRN7android7String8E",
- "source_file" : "system/core/libutils/include/utils/String8.h"
- },
- {
"function_name" : "android::String8::clear",
"linker_set_key" : "_ZN7android7String85clearEv",
"parameters" :
diff --git a/libutils/abi-dumps/arm_arm64/source-based/libutils.so.lsdump b/libutils/abi-dumps/arm_arm64/source-based/libutils.so.lsdump
index f88da15..219c766 100644
--- a/libutils/abi-dumps/arm_arm64/source-based/libutils.so.lsdump
+++ b/libutils/abi-dumps/arm_arm64/source-based/libutils.so.lsdump
@@ -725,9 +725,6 @@
"name" : "_ZN7android7String813appendFormatVEPKcSt9__va_list"
},
{
- "name" : "_ZN7android7String816convertToResPathEv"
- },
- {
"name" : "_ZN7android7String85clearEv"
},
{
@@ -6924,19 +6921,6 @@
"source_file" : "system/core/libutils/include/utils/String8.h"
},
{
- "function_name" : "android::String8::convertToResPath",
- "linker_set_key" : "_ZN7android7String816convertToResPathEv",
- "parameters" :
- [
- {
- "is_this_ptr" : true,
- "referenced_type" : "_ZTIPN7android7String8E"
- }
- ],
- "return_type" : "_ZTIRN7android7String8E",
- "source_file" : "system/core/libutils/include/utils/String8.h"
- },
- {
"function_name" : "android::String8::clear",
"linker_set_key" : "_ZN7android7String85clearEv",
"parameters" :
diff --git a/libutils/include/utils/String16.h b/libutils/include/utils/String16.h
index 3ef56a3..b48b907 100644
--- a/libutils/include/utils/String16.h
+++ b/libutils/include/utils/String16.h
@@ -53,12 +53,17 @@
~String16();
+ inline const char16_t* c_str() const;
inline const char16_t* string() const;
private:
static inline std::string std_string(const String16& str);
public:
size_t size() const;
+ inline bool empty() const;
+
+ inline size_t length() const;
+
void setTo(const String16& other);
status_t setTo(const char16_t* other);
status_t setTo(const char16_t* other, size_t len);
@@ -234,6 +239,11 @@
return compare_type(lhs, rhs) < 0;
}
+inline const char16_t* String16::c_str() const
+{
+ return mString;
+}
+
inline const char16_t* String16::string() const
{
return mString;
@@ -241,7 +251,17 @@
inline std::string String16::std_string(const String16& str)
{
- return std::string(String8(str).string());
+ return std::string(String8(str).c_str());
+}
+
+inline bool String16::empty() const
+{
+ return length() == 0;
+}
+
+inline size_t String16::length() const
+{
+ return size();
}
inline String16& String16::operator=(const String16& other)
diff --git a/libutils/include/utils/String8.h b/libutils/include/utils/String8.h
index 8b2dcf9..ea25c6a 100644
--- a/libutils/include/utils/String8.h
+++ b/libutils/include/utils/String8.h
@@ -52,8 +52,6 @@
explicit String8(const char32_t* o, size_t numChars);
~String8();
- static inline const String8 empty();
-
static String8 format(const char* fmt, ...) __attribute__((format (printf, 1, 2)));
static String8 formatV(const char* fmt, va_list args);
@@ -66,6 +64,7 @@
inline size_t size() const;
inline size_t bytes() const;
+ inline bool empty() const;
inline bool isEmpty() const;
size_t length() const;
@@ -193,23 +192,14 @@
* replaces whatever was there before.
*/
String8& appendPath(const char* leaf);
- String8& appendPath(const String8& leaf) { return appendPath(leaf.string()); }
+ String8& appendPath(const String8& leaf) { return appendPath(leaf.c_str()); }
/*
* Like appendPath(), but does not affect this string. Returns a new one instead.
*/
String8 appendPathCopy(const char* leaf) const
{ String8 p(*this); p.appendPath(leaf); return p; }
- String8 appendPathCopy(const String8& leaf) const { return appendPathCopy(leaf.string()); }
-
- /*
- * Converts all separators in this string to /, the default path separator.
- *
- * If the default OS separator is backslash, this converts all
- * backslashes to slashes, in-place. Otherwise it does nothing.
- * Returns self.
- */
- String8& convertToResPath();
+ String8 appendPathCopy(const String8& leaf) const { return appendPathCopy(leaf.c_str()); }
private:
status_t real_append(const char* other, size_t numChars);
@@ -240,10 +230,6 @@
return compare_type(lhs, rhs) < 0;
}
-inline const String8 String8::empty() {
- return String8();
-}
-
inline const char* String8::c_str() const
{
return mString;
@@ -255,7 +241,7 @@
inline std::string String8::std_string(const String8& str)
{
- return std::string(str.string());
+ return std::string(str.c_str());
}
inline size_t String8::size() const
@@ -263,6 +249,11 @@
return length();
}
+inline bool String8::empty() const
+{
+ return length() == 0;
+}
+
inline bool String8::isEmpty() const
{
return length() == 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/Android.bp b/rootdir/Android.bp
index e98733a..c8a3cd6 100644
--- a/rootdir/Android.bp
+++ b/rootdir/Android.bp
@@ -17,12 +17,30 @@
}
prebuilt_etc {
+ name: "init.boringssl.zygote64_32.rc",
+ src: "init.boringssl.zygote64_32.rc",
+ sub_dir: "init/hw",
+ symlinks: [
+ "init.boringssl.zygote32.rc",
+ "init.boringssl.no_zygote.rc",
+ ],
+}
+
+prebuilt_etc {
+ name: "init.boringssl.zygote64.rc",
+ src: "init.boringssl.zygote64.rc",
+ sub_dir: "init/hw",
+}
+
+prebuilt_etc {
name: "init.rc",
src: "init.rc",
sub_dir: "init/hw",
required: [
"fsverity_init",
"platform-bootclasspath",
+ "init.boringssl.zygote64.rc",
+ "init.boringssl.zygote64_32.rc",
],
}
diff --git a/rootdir/Android.mk b/rootdir/Android.mk
index 3362872..cc6b64a 100644
--- a/rootdir/Android.mk
+++ b/rootdir/Android.mk
@@ -91,27 +91,38 @@
#
# create some directories (some are mount points) and symlinks
LOCAL_POST_INSTALL_CMD := mkdir -p $(addprefix $(TARGET_ROOT_OUT)/, \
- dev proc sys system data data_mirror odm oem acct config storage mnt apex debug_ramdisk \
+ dev proc sys system data data_mirror odm oem acct config storage mnt apex bootstrap-apex debug_ramdisk \
linkerconfig second_stage_resources postinstall $(BOARD_ROOT_EXTRA_FOLDERS)); \
ln -sf /system/bin $(TARGET_ROOT_OUT)/bin; \
ln -sf /system/etc $(TARGET_ROOT_OUT)/etc; \
ln -sf /data/user_de/0/com.android.shell/files/bugreports $(TARGET_ROOT_OUT)/bugreports; \
ln -sfn /sys/kernel/debug $(TARGET_ROOT_OUT)/d; \
ln -sf /storage/self/primary $(TARGET_ROOT_OUT)/sdcard
+
+ALL_ROOTDIR_SYMLINKS := \
+ $(TARGET_ROOT_OUT)/bin \
+ $(TARGET_ROOT_OUT)/etc \
+ $(TARGET_ROOT_OUT)/bugreports \
+ $(TARGET_ROOT_OUT)/d \
+ $(TARGET_ROOT_OUT)/sdcard
+
ifdef BOARD_USES_VENDORIMAGE
LOCAL_POST_INSTALL_CMD += ; mkdir -p $(TARGET_ROOT_OUT)/vendor
else
LOCAL_POST_INSTALL_CMD += ; ln -sf /system/vendor $(TARGET_ROOT_OUT)/vendor
+ ALL_ROOTDIR_SYMLINKS += $(TARGET_ROOT_OUT)/vendor
endif
ifdef BOARD_USES_PRODUCTIMAGE
LOCAL_POST_INSTALL_CMD += ; mkdir -p $(TARGET_ROOT_OUT)/product
else
LOCAL_POST_INSTALL_CMD += ; ln -sf /system/product $(TARGET_ROOT_OUT)/product
+ ALL_ROOTDIR_SYMLINKS += $(TARGET_ROOT_OUT)/product
endif
ifdef BOARD_USES_SYSTEM_EXTIMAGE
LOCAL_POST_INSTALL_CMD += ; mkdir -p $(TARGET_ROOT_OUT)/system_ext
else
LOCAL_POST_INSTALL_CMD += ; ln -sf /system/system_ext $(TARGET_ROOT_OUT)/system_ext
+ ALL_ROOTDIR_SYMLINKS += $(TARGET_ROOT_OUT)/system_ext
endif
ifdef BOARD_USES_METADATA_PARTITION
LOCAL_POST_INSTALL_CMD += ; mkdir -p $(TARGET_ROOT_OUT)/metadata
@@ -134,6 +145,18 @@
LOCAL_POST_INSTALL_CMD += ; ln -sf /vendor/odm/priv-app $(TARGET_ROOT_OUT)/odm/priv-app
LOCAL_POST_INSTALL_CMD += ; ln -sf /vendor/odm/usr $(TARGET_ROOT_OUT)/odm/usr
+ALL_ROOTDIR_SYMLINKS += \
+ $(TARGET_ROOT_OUT)/odm/app \
+ $(TARGET_ROOT_OUT)/odm/bin \
+ $(TARGET_ROOT_OUT)/odm/etc \
+ $(TARGET_ROOT_OUT)/odm/firmware \
+ $(TARGET_ROOT_OUT)/odm/framework \
+ $(TARGET_ROOT_OUT)/odm/lib \
+ $(TARGET_ROOT_OUT)/odm/lib64 \
+ $(TARGET_ROOT_OUT)/odm/overlay \
+ $(TARGET_ROOT_OUT)/odm/priv-app \
+ $(TARGET_ROOT_OUT)/odm/usr
+
# For /vendor_dlkm partition.
LOCAL_POST_INSTALL_CMD += ; mkdir -p $(TARGET_ROOT_OUT)/vendor_dlkm
@@ -144,6 +167,7 @@
# Note that /vendor_dlkm/lib is omitted because vendor DLKMs should be accessed
# via /vendor/lib/modules directly.
LOCAL_POST_INSTALL_CMD += ; ln -sf /vendor/vendor_dlkm/etc $(TARGET_ROOT_OUT)/vendor_dlkm/etc
+ALL_ROOTDIR_SYMLINKS += $(TARGET_ROOT_OUT)/vendor_dlkm/etc
# For /odm_dlkm partition.
LOCAL_POST_INSTALL_CMD += ; mkdir -p $(TARGET_ROOT_OUT)/odm_dlkm
@@ -154,6 +178,7 @@
# Note that /odm_dlkm/lib is omitted because odm DLKMs should be accessed
# via /odm/lib/modules directly.
LOCAL_POST_INSTALL_CMD += ; ln -sf /odm/odm_dlkm/etc $(TARGET_ROOT_OUT)/odm_dlkm/etc
+ALL_ROOTDIR_SYMLINKS += $(TARGET_ROOT_OUT)/odm_dlkm/etc
# For /system_dlkm partition
LOCAL_POST_INSTALL_CMD += ; mkdir -p $(TARGET_ROOT_OUT)/system_dlkm
@@ -162,6 +187,7 @@
LOCAL_POST_INSTALL_CMD += ; mkdir -p $(TARGET_ROOT_OUT)/cache
else
LOCAL_POST_INSTALL_CMD += ; ln -sf /data/cache $(TARGET_ROOT_OUT)/cache
+ ALL_ROOTDIR_SYMLINKS += $(TARGET_ROOT_OUT)/cache
endif
ifdef BOARD_ROOT_EXTRA_SYMLINKS
# BOARD_ROOT_EXTRA_SYMLINKS is a list of <target>:<link_name>.
@@ -169,14 +195,19 @@
$(eval p := $(subst :,$(space),$(s)))\
; mkdir -p $(dir $(TARGET_ROOT_OUT)/$(word 2,$(p))) \
; ln -sf $(word 1,$(p)) $(TARGET_ROOT_OUT)/$(word 2,$(p)))
+ ALL_ROOTDIR_SYMLINKS += $(foreach s,$(BOARD_ROOT_EXTRA_SYMLINKS),$(TARGET_ROOT_OUT)/$(call word-colon,2,$s))
endif
# The init symlink must be a post install command of a file that is to TARGET_ROOT_OUT.
# Since init.environ.rc is required for init and satisfies that requirement, we hijack it to create the symlink.
LOCAL_POST_INSTALL_CMD += ; ln -sf /system/bin/init $(TARGET_ROOT_OUT)/init
+ALL_ROOTDIR_SYMLINKS += $(TARGET_ROOT_OUT)/init
+
+ALL_DEFAULT_INSTALLED_MODULES += $(ALL_ROOTDIR_SYMLINKS)
include $(BUILD_SYSTEM)/base_rules.mk
+$(ALL_ROOTDIR_SYMLINKS): $(LOCAL_BUILT_MODULE)
$(LOCAL_BUILT_MODULE): $(LOCAL_PATH)/init.environ.rc.in
@echo "Generate: $< -> $@"
@mkdir -p $(dir $@)
diff --git a/rootdir/init.boringssl.zygote64.rc b/rootdir/init.boringssl.zygote64.rc
new file mode 100644
index 0000000..3f49fea
--- /dev/null
+++ b/rootdir/init.boringssl.zygote64.rc
@@ -0,0 +1,4 @@
+on init && property:ro.product.cpu.abilist64=*
+ exec_start boringssl_self_test64
+on property:apexd.status=ready && property:ro.product.cpu.abilist64=*
+ exec_start boringssl_self_test_apex64
diff --git a/rootdir/init.boringssl.zygote64_32.rc b/rootdir/init.boringssl.zygote64_32.rc
new file mode 100644
index 0000000..c0be42d
--- /dev/null
+++ b/rootdir/init.boringssl.zygote64_32.rc
@@ -0,0 +1,8 @@
+on init && property:ro.product.cpu.abilist32=*
+ exec_start boringssl_self_test32
+on init && property:ro.product.cpu.abilist64=*
+ exec_start boringssl_self_test64
+on property:apexd.status=ready && property:ro.product.cpu.abilist32=*
+ exec_start boringssl_self_test_apex32
+on property:apexd.status=ready && property:ro.product.cpu.abilist64=*
+ exec_start boringssl_self_test_apex64
diff --git a/rootdir/init.rc b/rootdir/init.rc
index ec89375..2b04495 100644
--- a/rootdir/init.rc
+++ b/rootdir/init.rc
@@ -74,9 +74,7 @@
# become available. Note that this is executed as exec_start to ensure that
# the libraries are available to the processes started after this statement.
exec_start apexd-bootstrap
-
- # Generate linker config based on apex mounted in bootstrap namespace
- update_linker_config
+ perform_apex_config --bootstrap
# These must already exist by the time boringssl_self_test32 / boringssl_self_test64 run.
mkdir /dev/boringssl 0755 root root
@@ -481,14 +479,7 @@
start vndservicemanager
# Run boringssl self test for each ABI. Any failures trigger reboot to firmware.
-on init && property:ro.product.cpu.abilist32=*
- exec_start boringssl_self_test32
-on init && property:ro.product.cpu.abilist64=*
- exec_start boringssl_self_test64
-on property:apexd.status=ready && property:ro.product.cpu.abilist32=*
- exec_start boringssl_self_test_apex32
-on property:apexd.status=ready && property:ro.product.cpu.abilist64=*
- exec_start boringssl_self_test_apex64
+import /system/etc/init/hw/init.boringssl.${ro.zygote}.rc
service boringssl_self_test32 /system/bin/boringssl_self_test32
reboot_on_failure reboot,boringssl-self-check-failed
@@ -553,7 +544,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 +615,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
@@ -1017,7 +1008,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
@@ -1041,13 +1032,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.
@@ -1076,28 +1063,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
@@ -1149,7 +1118,7 @@
write /dev/sys/fs/by-name/userdata/iostat_enable 1
# set readahead multiplier for POSIX_FADV_SEQUENTIAL files
- write /dev/sys/fs/by-name/userdata/seq_file_ra_mul 16
+ write /dev/sys/fs/by-name/userdata/seq_file_ra_mul 128
# limit discard size to 128MB in order to avoid long IO latency
# for filesystem tuning first (dm or sda)
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/trusty/OWNERS b/trusty/OWNERS
index 61b97c6..bf16912 100644
--- a/trusty/OWNERS
+++ b/trusty/OWNERS
@@ -3,6 +3,7 @@
danielangell@google.com
gmar@google.com
marcone@google.com
+mikemcternan@google.com
mmaurer@google.com
ncbray@google.com
swillden@google.com
diff --git a/trusty/keymaster/TrustyKeymaster.cpp b/trusty/keymaster/TrustyKeymaster.cpp
index ac98695..b118a20 100644
--- a/trusty/keymaster/TrustyKeymaster.cpp
+++ b/trusty/keymaster/TrustyKeymaster.cpp
@@ -218,6 +218,11 @@
ForwardCommand(KM_DELETE_ALL_KEYS, request, response);
}
+void TrustyKeymaster::DestroyAttestationIds(const DestroyAttestationIdsRequest& request,
+ DestroyAttestationIdsResponse* response) {
+ ForwardCommand(KM_DESTROY_ATTESTATION_IDS, request, response);
+}
+
void TrustyKeymaster::BeginOperation(const BeginOperationRequest& request,
BeginOperationResponse* response) {
ForwardCommand(KM_BEGIN_OPERATION, request, response);
diff --git a/trusty/keymaster/include/trusty_keymaster/TrustyKeymaster.h b/trusty/keymaster/include/trusty_keymaster/TrustyKeymaster.h
index 60d3f87..c50178b 100644
--- a/trusty/keymaster/include/trusty_keymaster/TrustyKeymaster.h
+++ b/trusty/keymaster/include/trusty_keymaster/TrustyKeymaster.h
@@ -55,6 +55,8 @@
void UpgradeKey(const UpgradeKeyRequest& request, UpgradeKeyResponse* response);
void DeleteKey(const DeleteKeyRequest& request, DeleteKeyResponse* response);
void DeleteAllKeys(const DeleteAllKeysRequest& request, DeleteAllKeysResponse* response);
+ void DestroyAttestationIds(const DestroyAttestationIdsRequest& request,
+ DestroyAttestationIdsResponse* response);
void BeginOperation(const BeginOperationRequest& request, BeginOperationResponse* response);
void UpdateOperation(const UpdateOperationRequest& request, UpdateOperationResponse* response);
void FinishOperation(const FinishOperationRequest& request, FinishOperationResponse* response);
diff --git a/trusty/keymaster/keymint/TrustyKeyMintDevice.cpp b/trusty/keymaster/keymint/TrustyKeyMintDevice.cpp
index b696ff9..fec4c60 100644
--- a/trusty/keymaster/keymint/TrustyKeyMintDevice.cpp
+++ b/trusty/keymaster/keymint/TrustyKeyMintDevice.cpp
@@ -258,7 +258,11 @@
}
ScopedAStatus TrustyKeyMintDevice::destroyAttestationIds() {
- return kmError2ScopedAStatus(KM_ERROR_UNIMPLEMENTED);
+ keymaster::DestroyAttestationIdsRequest request(impl_->message_version());
+ keymaster::DestroyAttestationIdsResponse response(impl_->message_version());
+ impl_->DestroyAttestationIds(request, &response);
+
+ return kmError2ScopedAStatus(response.error);
}
ScopedAStatus TrustyKeyMintDevice::begin(KeyPurpose purpose, const vector<uint8_t>& keyBlob,
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())?;
}
diff --git a/trusty/line-coverage/Android.bp b/trusty/line-coverage/Android.bp
new file mode 100644
index 0000000..36a73aa
--- /dev/null
+++ b/trusty/line-coverage/Android.bp
@@ -0,0 +1,36 @@
+// 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.
+
+package {
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+cc_library {
+ name: "libtrusty_line_coverage",
+ vendor_available: true,
+ srcs: [
+ "coverage.cpp",
+ ],
+ export_include_dirs: [
+ "include",
+ ],
+ shared_libs: [
+ "libbase",
+ "libext2_uuid",
+ "liblog",
+ "libdmabufheap",
+ "libtrusty",
+ ],
+}
+
diff --git a/trusty/line-coverage/coverage.cpp b/trusty/line-coverage/coverage.cpp
new file mode 100644
index 0000000..57b7025
--- /dev/null
+++ b/trusty/line-coverage/coverage.cpp
@@ -0,0 +1,191 @@
+/*
+ * Copyright (C) 2023 The Android Open Sourete 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 "line-coverage"
+
+#include <BufferAllocator/BufferAllocator.h>
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <android-base/unique_fd.h>
+#include <assert.h>
+#include <log/log.h>
+#include <stdio.h>
+#include <sys/mman.h>
+#include <sys/uio.h>
+#include <trusty/line-coverage/coverage.h>
+#include <trusty/line-coverage/tipc.h>
+#include <trusty/tipc.h>
+#include <iostream>
+
+#define LINE_COVERAGE_CLIENT_PORT "com.android.trusty.linecoverage.client"
+
+struct control {
+ /* Written by controller, read by instrumented TA */
+ uint64_t cntrl_flags;
+
+ /* Written by instrumented TA, read by controller */
+ uint64_t oper_flags;
+ uint64_t write_buffer_start_count;
+ uint64_t write_buffer_complete_count;
+};
+
+namespace android {
+namespace trusty {
+namespace line_coverage {
+
+using ::android::base::ErrnoError;
+using ::android::base::Error;
+using ::std::string;
+
+static inline uintptr_t RoundPageUp(uintptr_t val) {
+ return (val + (PAGE_SIZE - 1)) & ~(PAGE_SIZE - 1);
+}
+
+CoverageRecord::CoverageRecord(string tipc_dev, struct uuid* uuid)
+ : tipc_dev_(std::move(tipc_dev)),
+ coverage_srv_fd_(-1),
+ uuid_(*uuid),
+ record_len_(0),
+ shm_(NULL),
+ shm_len_(0) {}
+
+CoverageRecord::~CoverageRecord() {
+ if (shm_) {
+ munmap((void*)shm_, shm_len_);
+ }
+}
+
+volatile void *CoverageRecord::getShm() {
+ if(!IsOpen()) {
+ fprintf(stderr, "Warning! SHM is NULL!\n");
+ }
+ return shm_;
+}
+
+Result<void> CoverageRecord::Rpc(struct line_coverage_client_req* req, \
+ int req_fd, \
+ struct line_coverage_client_resp* resp) {
+ int rc;
+
+ if (req_fd < 0) {
+ rc = write(coverage_srv_fd_, req, sizeof(*req));
+ } else {
+ iovec iov = {
+ .iov_base = req,
+ .iov_len = sizeof(*req),
+ };
+
+ trusty_shm shm = {
+ .fd = req_fd,
+ .transfer = TRUSTY_SHARE,
+ };
+
+ rc = tipc_send(coverage_srv_fd_, &iov, 1, &shm, 1);
+ }
+
+ if (rc != (int)sizeof(*req)) {
+ return ErrnoError() << "failed to send request to coverage server: ";
+ }
+
+ rc = read(coverage_srv_fd_, resp, sizeof(*resp));
+ if (rc != (int)sizeof(*resp)) {
+ return ErrnoError() << "failed to read reply from coverage server: ";
+ }
+
+ if (resp->hdr.cmd != (req->hdr.cmd | LINE_COVERAGE_CLIENT_CMD_RESP_BIT)) {
+ return ErrnoError() << "unknown response cmd: " << resp->hdr.cmd;
+ }
+
+ return {};
+}
+
+Result<void> CoverageRecord::Open(int fd) {
+ struct line_coverage_client_req req;
+ struct line_coverage_client_resp resp;
+
+ if (shm_) {
+ return {}; /* already initialized */
+ }
+
+ coverage_srv_fd_= fd;
+
+ req.hdr.cmd = LINE_COVERAGE_CLIENT_CMD_OPEN;
+ req.open_args.uuid = uuid_;
+ auto ret = Rpc(&req, -1, &resp);
+ if (!ret.ok()) {
+ return Error() << "failed to open coverage client: " << ret.error();
+ }
+ record_len_ = resp.open_args.record_len;
+ shm_len_ = RoundPageUp(record_len_);
+
+ BufferAllocator allocator;
+
+ fd = allocator.Alloc("system", shm_len_);
+ if (fd < 0) {
+ return ErrnoError() << "failed to create dmabuf of size " << shm_len_
+ << " err code: " << fd;
+ }
+ unique_fd dma_buf(fd);
+
+ void* shm = mmap(0, shm_len_, PROT_READ | PROT_WRITE, MAP_SHARED, dma_buf, 0);
+ if (shm == MAP_FAILED) {
+ return ErrnoError() << "failed to map memfd: ";
+ }
+
+ req.hdr.cmd = LINE_COVERAGE_CLIENT_CMD_SHARE_RECORD;
+ req.share_record_args.shm_len = shm_len_;
+ ret = Rpc(&req, dma_buf, &resp);
+ if (!ret.ok()) {
+ return Error() << "failed to send shared memory: " << ret.error();
+ }
+
+ shm_ = shm;
+
+ req.hdr.cmd = LINE_COVERAGE_CLIENT_CMD_OPEN;
+ req.open_args.uuid = uuid_;
+ ret = Rpc(&req, -1, &resp);
+ if (!ret.ok()) {
+ return Error() << "failed to open coverage client: " << ret.error();
+ }
+
+ return {};
+}
+
+bool CoverageRecord::IsOpen() {
+ return shm_;
+}
+
+Result<void> CoverageRecord::SaveFile(const std::string& filename) {
+ if(!IsOpen()) {
+ return ErrnoError() << "Warning! SHM is NULL!";
+ }
+ android::base::unique_fd output_fd(TEMP_FAILURE_RETRY(creat(filename.c_str(), 00644)));
+ if (!output_fd.ok()) {
+ return ErrnoError() << "Could not open output file";
+ }
+
+ uintptr_t* begin = (uintptr_t*)((char *)shm_ + sizeof(struct control));
+ bool ret = WriteFully(output_fd, begin, record_len_);
+ if(!ret) {
+ fprintf(stderr, "Coverage write to file failed\n");
+ }
+
+ return {};
+}
+
+} // namespace line_coverage
+} // namespace trusty
+} // namespace android
diff --git a/trusty/line-coverage/include/trusty/line-coverage/coverage.h b/trusty/line-coverage/include/trusty/line-coverage/coverage.h
new file mode 100644
index 0000000..53901be
--- /dev/null
+++ b/trusty/line-coverage/include/trusty/line-coverage/coverage.h
@@ -0,0 +1,61 @@
+/*
+ * 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 <optional>
+#include <string>
+
+#include <android-base/result.h>
+#include <android-base/unique_fd.h>
+#include <stdint.h>
+#include <trusty/line-coverage/tipc.h>
+
+namespace android {
+namespace trusty {
+namespace line_coverage {
+
+using android::base::Result;
+using android::base::unique_fd;
+
+class CoverageRecord {
+ public:
+ CoverageRecord(std::string tipc_dev, struct uuid* uuid);
+
+ ~CoverageRecord();
+ Result<void> Open(int fd);
+ bool IsOpen();
+ Result<void> SaveFile(const std::string& filename);
+ volatile void* getShm();
+
+ private:
+ Result<void> Rpc(struct line_coverage_client_req* req, \
+ int req_fd, \
+ struct line_coverage_client_resp* resp);
+
+ Result<std::pair<size_t, size_t>> GetRegionBounds(uint32_t region_type);
+
+ std::string tipc_dev_;
+ int coverage_srv_fd_;
+ struct uuid uuid_;
+ size_t record_len_;
+ volatile void* shm_;
+ size_t shm_len_;
+};
+
+} // namespace line_coverage
+} // namespace trusty
+} // namespace android
diff --git a/trusty/line-coverage/include/trusty/line-coverage/tipc.h b/trusty/line-coverage/include/trusty/line-coverage/tipc.h
new file mode 100644
index 0000000..20e855c
--- /dev/null
+++ b/trusty/line-coverage/include/trusty/line-coverage/tipc.h
@@ -0,0 +1,140 @@
+/*
+ * 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.
+ */
+
+/* This file needs to be kept in-sync with its counterpart on Trusty side */
+
+#pragma once
+
+#include <stdint.h>
+#include <trusty/line-coverage/uuid.h>
+
+
+#define LINE_COVERAGE_CLIENT_PORT "com.android.trusty.linecoverage.client"
+
+/**
+ * enum line_coverage_client_cmd - command identifiers for coverage client interface
+ * @LINE_COVERAGE_CLIENT_CMD_RESP_BIT: response bit set as part of response
+ * @LINE_COVERAGE_CLIENT_CMD_SHIFT: number of bits used by response bit
+ * @LINE_COVERAGE_CLIENT_CMD_OPEN: command to open coverage record
+ * @LINE_COVERAGE_CLIENT_CMD_SHARE_RECORD: command to register a shared memory region
+ * where coverage record will be written to
+ */
+enum line_coverage_client_cmd {
+ LINE_COVERAGE_CLIENT_CMD_RESP_BIT = 1U,
+ LINE_COVERAGE_CLIENT_CMD_SHIFT = 1U,
+ LINE_COVERAGE_CLIENT_CMD_OPEN = (1U << LINE_COVERAGE_CLIENT_CMD_SHIFT),
+ LINE_COVERAGE_CLIENT_CMD_SHARE_RECORD = (2U << LINE_COVERAGE_CLIENT_CMD_SHIFT),
+ LINE_COVERAGE_CLIENT_CMD_SEND_LIST = (3U << LINE_COVERAGE_CLIENT_CMD_SHIFT),
+};
+
+/**
+ * struct line_coverage_client_hdr - header for coverage client messages
+ * @cmd: command identifier
+ *
+ * Note that no messages return a status code. Any error on the server side
+ * results in the connection being closed. So, operations can be assumed to be
+ * successful if they return a response.
+ */
+struct line_coverage_client_hdr {
+ uint32_t cmd;
+};
+
+/**
+ * struct line_coverage_client_open_req - arguments for request to open coverage
+ * record
+ * @uuid: UUID of target TA
+ *
+ * There is one coverage record per TA. @uuid is used to identify both the TA
+ * and corresponding coverage record.
+ */
+struct line_coverage_client_open_req {
+ struct uuid uuid;
+};
+
+/**
+ * struct line_coverage_client_open_resp - arguments for response to open coverage
+ * record
+ * @record_len: length of coverage record that will be emitted by target TA
+ *
+ * Shared memory allocated for this coverage record must larger than
+ * @record_len.
+ */
+struct line_coverage_client_open_resp {
+ uint32_t record_len;
+};
+
+/**
+ * struct line_coverage_client_send_list_resp - arguments for response to send list
+ * record
+ * @uuid: UUID of TA that connected to aggregator
+ */
+struct line_coverage_client_send_list_resp {
+ struct uuid uuid;
+};
+
+/**
+ * struct line_coverage_client_send_list_resp - arguments for response to send list
+ * record
+ * @index: index of the list being requested
+ */
+struct line_coverage_client_send_list_req {
+ uint32_t index;
+};
+
+/**
+ * struct line_coverage_client_share_record_req - arguments for request to share
+ * memory for coverage record
+ * @shm_len: length of memory region being shared
+ *
+ * A handle to a memory region must be sent along with this message. This memory
+ * is used to store coverage record.
+ *
+ * Upon success, this memory region can be assumed to be shared between the
+ * client and target TA.
+ */
+struct line_coverage_client_share_record_req {
+ uint32_t shm_len;
+};
+
+/**
+ * struct line_coverage_client_req - structure for a coverage client request
+ * @hdr: message header
+ * @open_args: arguments for %COVERAGE_CLIENT_CMD_OPEN request
+ * @share_record_args: arguments for %COVERAGE_CLIENT_CMD_SHARE_RECORD request
+ * @index: arguments for %COVERAGE_CLIENT_CMD_SHARE_RECORD request
+ */
+struct line_coverage_client_req {
+ struct line_coverage_client_hdr hdr;
+ union {
+ struct line_coverage_client_open_req open_args;
+ struct line_coverage_client_share_record_req share_record_args;
+ struct line_coverage_client_send_list_req send_list_args;
+ };
+};
+
+/**
+ * struct line_coverage_client_resp - structure for a coverage client response
+ * @hdr: message header
+ * @open_args: arguments for %COVERAGE_CLIENT_CMD_OPEN response
+ * @send_list_args: arguments for %COVERAGE_CLIENT_CMD_SHARE_RECORD response
+ */
+struct line_coverage_client_resp {
+ struct line_coverage_client_hdr hdr;
+ union {
+ struct line_coverage_client_open_resp open_args;
+ struct line_coverage_client_send_list_resp send_list_args;
+ };
+};
diff --git a/trusty/line-coverage/include/trusty/line-coverage/uuid.h b/trusty/line-coverage/include/trusty/line-coverage/uuid.h
new file mode 100644
index 0000000..1873980
--- /dev/null
+++ b/trusty/line-coverage/include/trusty/line-coverage/uuid.h
@@ -0,0 +1,37 @@
+/*
+ * 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 <string.h>
+
+#define UUCMP(u1, u2) if (u1 != u2) return u1 < u2
+
+struct uuid {
+ uint32_t time_low;
+ uint16_t time_mid;
+ uint16_t time_hi_and_version;
+ uint8_t clock_seq_and_node[8];
+
+ bool operator<(const struct uuid& rhs) const
+ {
+ UUCMP(time_low, rhs.time_low);
+ UUCMP(time_mid, rhs.time_mid);
+ UUCMP(time_hi_and_version, rhs.time_hi_and_version);
+ return memcmp(clock_seq_and_node, rhs.clock_seq_and_node, 8);
+ }
+};
diff --git a/trusty/storage/proxy/Android.bp b/trusty/storage/proxy/Android.bp
index 2e97ee0..e362b8b 100644
--- a/trusty/storage/proxy/Android.bp
+++ b/trusty/storage/proxy/Android.bp
@@ -33,6 +33,7 @@
shared_libs: [
"libbase",
+ "libbinder_ndk",
"libcutils",
"liblog",
"libhardware_legacy",
diff --git a/trusty/storage/proxy/proxy.c b/trusty/storage/proxy/proxy.c
index c89c5b6..67e935e 100644
--- a/trusty/storage/proxy/proxy.c
+++ b/trusty/storage/proxy/proxy.c
@@ -24,6 +24,7 @@
#include <sys/stat.h>
#include <unistd.h>
+#include <android/binder_process.h>
#include <cutils/android_filesystem_config.h>
#include "checkpoint_handling.h"
@@ -238,6 +239,18 @@
/* parse arguments */
parse_args(argc, argv);
+ /*
+ * Start binder threadpool. At least one extra binder thread is needed to
+ * connect to the wakelock service without relying on polling. If we poll on
+ * the main thread we end up pausing for at least 1s even if the service
+ * starts faster. We set the max thread count to 0 because startThreadPool
+ * "Starts one thread, PLUS those requested in setThreadPoolMaxThreadCount,
+ * PLUS those manually requested in joinThreadPool." We only need a single
+ * binder thread to receive notifications on.
+ */
+ ABinderProcess_setThreadPoolMaxThreadCount(0);
+ ABinderProcess_startThreadPool();
+
/* initialize secure storage directory */
rc = storage_init(ss_data_root);
if (rc < 0) return EXIT_FAILURE;
diff --git a/trusty/storage/proxy/rpmb.c b/trusty/storage/proxy/rpmb.c
index 22a85a7..1f5d107 100644
--- a/trusty/storage/proxy/rpmb.c
+++ b/trusty/storage/proxy/rpmb.c
@@ -399,6 +399,14 @@
bool is_request_write = req->reliable_write_size > 0;
+ /*
+ * Internally this call connects to the suspend service, which will cause
+ * this service to start if not already running. If the binder thread pool
+ * has not been started at this point, this call will block and poll for the
+ * service every 1s. We need to make sure the thread pool is started to
+ * receive an async notification that the service is started to avoid
+ * blocking (see main).
+ */
wl_rc = acquire_wake_lock(PARTIAL_WAKE_LOCK, UFS_WAKE_LOCK_NAME);
if (wl_rc < 0) {
ALOGE("%s: failed to acquire wakelock: %d, %s\n", __func__, wl_rc, strerror(errno));
diff --git a/trusty/utils/coverage-controller/Android.bp b/trusty/utils/coverage-controller/Android.bp
new file mode 100644
index 0000000..e6d30d9
--- /dev/null
+++ b/trusty/utils/coverage-controller/Android.bp
@@ -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.
+
+package {
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+cc_binary {
+ name: "trusty-coverage-controller",
+ vendor: true,
+
+ srcs: ["controller.cpp"],
+ shared_libs: [
+ "libc",
+ "liblog",
+ "libbase",
+ "libdmabufheap",
+ ],
+ static_libs: [
+ "libtrusty",
+ "libtrusty_line_coverage",
+ ],
+}
diff --git a/trusty/utils/coverage-controller/controller.cpp b/trusty/utils/coverage-controller/controller.cpp
new file mode 100644
index 0000000..0047046
--- /dev/null
+++ b/trusty/utils/coverage-controller/controller.cpp
@@ -0,0 +1,185 @@
+/*
+ * 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 <android-base/stringprintf.h>
+#include <array>
+#include <getopt.h>
+#include <inttypes.h>
+#include <memory>
+#include <stdbool.h>
+#include <stdio.h>
+#include <string.h>
+#include <trusty/line-coverage/coverage.h>
+#include <trusty/tipc.h>
+#include <vector>
+
+#include "controller.h"
+
+#define READ_ONCE(x) (*((volatile __typeof__(x) *) &(x)))
+#define WRITE_ONCE(x, val) (*((volatile __typeof__(val) *) &(x)) = (val))
+
+namespace android {
+namespace trusty {
+namespace controller {
+
+using ::android::trusty::line_coverage::CoverageRecord;
+
+void Controller::run(std::string output_dir) {
+ connectCoverageServer();
+ struct control *control;
+ uint64_t complete_cnt = 0, start_cnt = 0, flags;
+
+ while(1) {
+ setUpShm();
+
+ for (int index = 0; index < record_list_.size(); index++) {
+ control = (struct control *)record_list_[index]->getShm();
+ start_cnt = READ_ONCE((control->write_buffer_start_count));
+ complete_cnt = READ_ONCE(control->write_buffer_complete_count);
+ flags = READ_ONCE(control->cntrl_flags);
+
+ if (complete_cnt != counters[index] && start_cnt == complete_cnt) {
+ WRITE_ONCE(control->cntrl_flags, FLAG_NONE);
+ std::string filename;
+ filename = android::base::StringPrintf("/%s.%lu.profraw",
+ uuid_list_[index].c_str(),
+ counters[index]);
+ filename.insert(0, output_dir);
+ android::base::Result<void> res = record_list_[index]->SaveFile(filename);
+ counters[index]++;
+ }
+ if(complete_cnt == counters[index] &&
+ !(flags & FLAG_RUN)) {
+ flags |= FLAG_RUN;
+ WRITE_ONCE(control->cntrl_flags, flags);
+ }
+ }
+ }
+}
+
+void Controller::connectCoverageServer() {
+ coverage_srv_fd = tipc_connect(TIPC_DEV, LINE_COVERAGE_CLIENT_PORT);
+ if (coverage_srv_fd < 0) {
+ fprintf(stderr, \
+ "Error: Failed to connect to Trusty coverage server: %d\n", coverage_srv_fd);
+ return;
+ }
+}
+
+void Controller::setUpShm() {
+ struct line_coverage_client_req req;
+ struct line_coverage_client_resp resp;
+ uint32_t cur_index = record_list_.size();
+ struct uuid zero_uuid = {0, 0, 0, { 0 }};
+ char uuid_str[UUID_STR_SIZE];
+ req.hdr.cmd = LINE_COVERAGE_CLIENT_CMD_SEND_LIST;
+ int rc = write(coverage_srv_fd, &req, sizeof(req));
+ if (rc != (int)sizeof(req)) {
+ fprintf(stderr, "failed to send request to coverage server: %d\n", rc);
+ return;
+ }
+
+ while(1) {
+ rc = read(coverage_srv_fd, &resp, sizeof(resp));
+ if (rc != (int)sizeof(resp)) {
+ fprintf(stderr, "failed to read reply from coverage server:: %d\n", rc);
+ }
+
+ if (resp.hdr.cmd == (req.hdr.cmd | LINE_COVERAGE_CLIENT_CMD_RESP_BIT)) {
+ if (!memcmp(&resp.send_list_args.uuid, &zero_uuid, sizeof(struct uuid))) {
+ break;
+ }
+ if(uuid_set_.find(resp.send_list_args.uuid) == uuid_set_.end()) {
+ uuid_set_.insert(resp.send_list_args.uuid);
+ sprintf(uuid_str,
+ "%08" PRIx32 "-%04" PRIx16 "-%04" PRIx16 "-%02" PRIx8 "%02" PRIx8
+ "-%02" PRIx8 "%02" PRIx8 "%02" PRIx8 "%02" PRIx8 "%02" PRIx8 "%02" PRIx8,
+ resp.send_list_args.uuid.time_low,
+ resp.send_list_args.uuid.time_mid,
+ resp.send_list_args.uuid.time_hi_and_version,
+ resp.send_list_args.uuid.clock_seq_and_node[0],
+ resp.send_list_args.uuid.clock_seq_and_node[1],
+ resp.send_list_args.uuid.clock_seq_and_node[2],
+ resp.send_list_args.uuid.clock_seq_and_node[3],
+ resp.send_list_args.uuid.clock_seq_and_node[4],
+ resp.send_list_args.uuid.clock_seq_and_node[5],
+ resp.send_list_args.uuid.clock_seq_and_node[6],
+ resp.send_list_args.uuid.clock_seq_and_node[7]);
+ uuid_list_.push_back(uuid_str);
+ record_list_.push_back(std::make_unique<CoverageRecord>(TIPC_DEV,
+ &resp.send_list_args.uuid));
+ counters.push_back(0);
+ }
+ }
+ else {
+ fprintf(stderr, "Unknown response header\n");
+ }
+ cur_index++;
+ req.hdr.cmd = LINE_COVERAGE_CLIENT_CMD_SEND_LIST;
+ req.send_list_args.index = cur_index;
+ int rc = write(coverage_srv_fd, &req, sizeof(req));
+ if (rc != (int)sizeof(req)) {
+ fprintf(stderr, "failed to send request to coverage server: %d\n", rc);
+ }
+ }
+
+ for(int ind = 0 ; ind < record_list_.size() ; ind++) {
+ record_list_[ind]->Open(coverage_srv_fd);
+ }
+}
+
+
+} // namespace controller
+} // namespace trusty
+} // namespace android
+
+int main(int argc, char* argv[]) {
+
+ std::string optarg = "";
+ do {
+ int c;
+ c = getopt(argc, argv, "o");
+
+ if (c == -1) {
+ break;
+ }
+
+ switch (c) {
+ case 'o':
+ break;
+ default:
+ fprintf(stderr, "usage: %s -o [output_directory]\n", argv[0]);
+ exit(EXIT_FAILURE);
+ }
+ } while (1);
+
+ if (argc > optind + 1) {
+ fprintf(stderr, "%s: too many arguments\n", argv[0]);
+ exit(EXIT_FAILURE);
+ }
+
+ if (argc > optind) {
+ optarg = argv[optind];
+ }
+ if (optarg.size()==0) {
+ optarg = "data/local/tmp";
+ }
+
+ android::trusty::controller::Controller cur;
+ cur.run(optarg);
+
+ return EXIT_SUCCESS;
+}
\ No newline at end of file
diff --git a/trusty/utils/coverage-controller/controller.h b/trusty/utils/coverage-controller/controller.h
new file mode 100644
index 0000000..f7789bf
--- /dev/null
+++ b/trusty/utils/coverage-controller/controller.h
@@ -0,0 +1,67 @@
+/*
+ * 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 <trusty/line-coverage/coverage.h>
+#include <array>
+#include <memory>
+#include <vector>
+#include <set>
+
+#define TIPC_DEV "/dev/trusty-ipc-dev0"
+#define TEST_SRV_PORT "com.android.trusty.sancov.test.srv"
+#define TEST_SRV_MODULE "srv.syms.elf"
+
+#define UUID_STR_SIZE (37)
+
+#define FLAG_NONE 0x0
+#define FLAG_RUN 0x1
+#define FLAG_TOGGLE_CLEAR 0x2
+
+struct control {
+ /* Written by controller, read by instrumented TA */
+ uint64_t cntrl_flags;
+
+ /* Written by instrumented TA, read by controller */
+ uint64_t oper_flags;
+ uint64_t write_buffer_start_count;
+ uint64_t write_buffer_complete_count;
+};
+
+namespace android {
+namespace trusty {
+namespace controller {
+
+class Controller {
+ public:
+ public:
+ void run(std::string output_dir);
+
+ private:
+ std::vector<std::unique_ptr<line_coverage::CoverageRecord>>record_list_;
+ std::set<struct uuid>uuid_set_;
+ std::vector<std::string>uuid_list_;
+ std::vector<uint64_t> counters;
+ int coverage_srv_fd;
+
+ void connectCoverageServer();
+ void setUpShm();
+};
+
+} // namespace controller
+} // namespace trusty
+} // namespace android