Merge "fastboot: Support casefolding in ext4 partitions."
diff --git a/TEST_MAPPING b/TEST_MAPPING
deleted file mode 100644
index da7fca1..0000000
--- a/TEST_MAPPING
+++ /dev/null
@@ -1,66 +0,0 @@
-{
- "presubmit": [
- {
- "name": "adbd_test"
- },
- {
- "name": "adb_crypto_test"
- },
- {
- "name": "adb_pairing_auth_test"
- },
- {
- "name": "adb_pairing_connection_test"
- },
- {
- "name": "adb_tls_connection_test"
- },
- {
- "name": "CtsFsMgrTestCases"
- },
- {
- "name": "CtsInitTestCases"
- },
- {
- "name": "debuggerd_test"
- },
- {
- "name": "fs_mgr_vendor_overlay_test"
- },
- {
- "name": "init_kill_services_test"
- },
- {
- "name": "libpackagelistparser_test"
- },
- {
- "name": "libcutils_test"
- },
- {
- "name": "libmodprobe_tests"
- },
- {
- "name": "libprocinfo_test"
- },
- {
- "name": "libutils_test"
- },
- {
- "name": "memunreachable_test"
- },
- {
- "name": "memunreachable_unit_test"
- },
- {
- "name": "memunreachable_binder_test"
- },
- {
- "name": "propertyinfoserializer_tests"
- }
- ],
- "imports": [
- {
- "path": "frameworks/base/tests/StagedInstallTest"
- }
- ]
-}
diff --git a/debuggerd/Android.bp b/debuggerd/Android.bp
index d7e8f32..f713ff2 100644
--- a/debuggerd/Android.bp
+++ b/debuggerd/Android.bp
@@ -13,6 +13,7 @@
"-Wno-unused-function",
"-Wno-nullability-completeness",
"-Os",
+ "-fno-finite-loops",
],
local_include_dirs: ["include"],
diff --git a/debuggerd/OWNERS b/debuggerd/OWNERS
index bfeedca..6f7e4a3 100644
--- a/debuggerd/OWNERS
+++ b/debuggerd/OWNERS
@@ -1,2 +1 @@
cferris@google.com
-jmgao@google.com
diff --git a/debuggerd/TEST_MAPPING b/debuggerd/TEST_MAPPING
new file mode 100644
index 0000000..d5327db
--- /dev/null
+++ b/debuggerd/TEST_MAPPING
@@ -0,0 +1,7 @@
+{
+ "presubmit": [
+ {
+ "name": "debuggerd_test"
+ }
+ ]
+}
diff --git a/debuggerd/debuggerd.cpp b/debuggerd/debuggerd.cpp
index 360ea95..e20e8d9 100644
--- a/debuggerd/debuggerd.cpp
+++ b/debuggerd/debuggerd.cpp
@@ -93,8 +93,18 @@
errx(1, "process %d is a zombie", pid);
}
- if (kill(pid, 0) != 0) {
- err(1, "cannot send signal to process %d", pid);
+ // Send a signal to the main thread pid, not a side thread. The signal
+ // handler always sets the crashing tid to the main thread pid when sent this
+ // signal. This is to avoid a problem where the signal is sent to a process,
+ // but happens on a side thread and the intercept mismatches since it
+ // is looking for the main thread pid, not the tid of this random thread.
+ // See b/194346289 for extra details.
+ if (kill(proc_info.pid, 0) != 0) {
+ if (pid == proc_info.pid) {
+ err(1, "cannot send signal to process %d", pid);
+ } else {
+ err(1, "cannot send signal to main thread %d (requested thread %d)", proc_info.pid, pid);
+ }
}
unique_fd piperead, pipewrite;
@@ -103,9 +113,13 @@
}
std::thread redirect_thread = spawn_redirect_thread(std::move(piperead));
- if (!debuggerd_trigger_dump(pid, dump_type, 0, std::move(pipewrite))) {
+ if (!debuggerd_trigger_dump(proc_info.pid, dump_type, 0, std::move(pipewrite))) {
redirect_thread.join();
- errx(1, "failed to dump process %d", pid);
+ if (pid == proc_info.pid) {
+ errx(1, "failed to dump process %d", pid);
+ } else {
+ errx(1, "failed to dump main thread %d (requested thread %d)", proc_info.pid, pid);
+ }
}
redirect_thread.join();
diff --git a/debuggerd/debuggerd_test.cpp b/debuggerd/debuggerd_test.cpp
index abda071..68c9aca 100644
--- a/debuggerd/debuggerd_test.cpp
+++ b/debuggerd/debuggerd_test.cpp
@@ -961,6 +961,44 @@
ASSERT_MATCH(result, R"(Abort message: 'x{4045}')");
}
+TEST_F(CrasherTest, abort_message_newline_trimmed) {
+ int intercept_result;
+ unique_fd output_fd;
+ StartProcess([]() {
+ android_set_abort_message("Message with a newline.\n");
+ abort();
+ });
+ StartIntercept(&output_fd);
+ FinishCrasher();
+ AssertDeath(SIGABRT);
+ FinishIntercept(&intercept_result);
+
+ ASSERT_EQ(1, intercept_result) << "tombstoned reported failure";
+
+ std::string result;
+ ConsumeFd(std::move(output_fd), &result);
+ ASSERT_MATCH(result, R"(Abort message: 'Message with a newline.')");
+}
+
+TEST_F(CrasherTest, abort_message_multiple_newlines_trimmed) {
+ int intercept_result;
+ unique_fd output_fd;
+ StartProcess([]() {
+ android_set_abort_message("Message with multiple newlines.\n\n\n\n\n");
+ abort();
+ });
+ StartIntercept(&output_fd);
+ FinishCrasher();
+ AssertDeath(SIGABRT);
+ FinishIntercept(&intercept_result);
+
+ ASSERT_EQ(1, intercept_result) << "tombstoned reported failure";
+
+ std::string result;
+ ConsumeFd(std::move(output_fd), &result);
+ ASSERT_MATCH(result, R"(Abort message: 'Message with multiple newlines.')");
+}
+
TEST_F(CrasherTest, abort_message_backtrace) {
int intercept_result;
unique_fd output_fd;
@@ -1825,3 +1863,204 @@
ASSERT_TRUE(android::base::ReadFdToString(output_fd, &output));
ASSERT_EQ("foo", output);
}
+
+// Verify that when an intercept is present for the main thread, and the signal
+// is received on a different thread, the intercept still works.
+TEST_F(CrasherTest, intercept_for_main_thread_signal_on_side_thread) {
+ StartProcess([]() {
+ std::thread thread([]() {
+ // Raise the signal on the side thread.
+ raise_debugger_signal(kDebuggerdNativeBacktrace);
+ });
+ thread.join();
+ _exit(0);
+ });
+
+ unique_fd output_fd;
+ StartIntercept(&output_fd, kDebuggerdNativeBacktrace);
+ FinishCrasher();
+ AssertDeath(0);
+
+ int intercept_result;
+ FinishIntercept(&intercept_result);
+ ASSERT_EQ(1, intercept_result) << "tombstoned reported failure";
+
+ std::string result;
+ ConsumeFd(std::move(output_fd), &result);
+ ASSERT_BACKTRACE_FRAME(result, "raise_debugger_signal");
+}
+
+static std::string format_pointer(uintptr_t ptr) {
+#if defined(__LP64__)
+ return android::base::StringPrintf("%08x'%08x", static_cast<uint32_t>(ptr >> 32),
+ static_cast<uint32_t>(ptr & 0xffffffff));
+#else
+ return android::base::StringPrintf("%08x", static_cast<uint32_t>(ptr & 0xffffffff));
+#endif
+}
+
+static std::string format_pointer(void* ptr) {
+ return format_pointer(reinterpret_cast<uintptr_t>(ptr));
+}
+
+static std::string format_full_pointer(uintptr_t ptr) {
+#if defined(__LP64__)
+ return android::base::StringPrintf("%016" PRIx64, ptr);
+#else
+ return android::base::StringPrintf("%08x", ptr);
+#endif
+}
+
+static std::string format_full_pointer(void* ptr) {
+ return format_full_pointer(reinterpret_cast<uintptr_t>(ptr));
+}
+
+__attribute__((__noinline__)) int crash_call(uintptr_t ptr) {
+ int* crash_ptr = reinterpret_cast<int*>(ptr);
+ *crash_ptr = 1;
+ return *crash_ptr;
+}
+
+// Verify that a fault address before the first map is properly handled.
+TEST_F(CrasherTest, fault_address_before_first_map) {
+ StartProcess([]() {
+ ASSERT_EQ(0, crash_call(0x1024));
+ _exit(0);
+ });
+
+ unique_fd output_fd;
+ StartIntercept(&output_fd);
+ FinishCrasher();
+ AssertDeath(SIGSEGV);
+
+ int intercept_result;
+ FinishIntercept(&intercept_result);
+ ASSERT_EQ(1, intercept_result) << "tombstoned reported failure";
+
+ std::string result;
+ ConsumeFd(std::move(output_fd), &result);
+ ASSERT_MATCH(result, R"(signal 11 \(SIGSEGV\), code 1 \(SEGV_MAPERR\), fault addr 0x1024)");
+
+ ASSERT_MATCH(result, R"(\nmemory map \(.*\):\n)");
+
+ std::string match_str = android::base::StringPrintf(
+ R"(memory map .*:\n--->Fault address falls at %s before any mapped regions\n )",
+ format_pointer(0x1024).c_str());
+ ASSERT_MATCH(result, match_str);
+}
+
+// Verify that a fault address after the last map is properly handled.
+TEST_F(CrasherTest, fault_address_after_last_map) {
+ uintptr_t crash_uptr = untag_address(UINTPTR_MAX - 15);
+ StartProcess([crash_uptr]() {
+ ASSERT_EQ(0, crash_call(crash_uptr));
+ _exit(0);
+ });
+
+ unique_fd output_fd;
+ StartIntercept(&output_fd);
+ FinishCrasher();
+ AssertDeath(SIGSEGV);
+
+ int intercept_result;
+ FinishIntercept(&intercept_result);
+ ASSERT_EQ(1, intercept_result) << "tombstoned reported failure";
+
+ std::string result;
+ ConsumeFd(std::move(output_fd), &result);
+
+ std::string match_str = R"(signal 11 \(SIGSEGV\), code 1 \(SEGV_MAPERR\), fault addr )";
+ match_str += android::base::StringPrintf("0x%" PRIxPTR, crash_uptr);
+ ASSERT_MATCH(result, match_str);
+
+ 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.
+ match_str = android::base::StringPrintf(
+ 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);
+}
+
+// Verify that a fault address between maps is properly handled.
+TEST_F(CrasherTest, fault_address_between_maps) {
+ // Create a map before the fork so it will be present in the child.
+ void* start_ptr =
+ mmap(nullptr, 3 * getpagesize(), PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
+ ASSERT_NE(MAP_FAILED, start_ptr);
+ // Unmap the page in the middle.
+ void* middle_ptr =
+ reinterpret_cast<void*>(reinterpret_cast<uintptr_t>(start_ptr) + getpagesize());
+ ASSERT_EQ(0, munmap(middle_ptr, getpagesize()));
+
+ StartProcess([middle_ptr]() {
+ ASSERT_EQ(0, crash_call(reinterpret_cast<uintptr_t>(middle_ptr)));
+ _exit(0);
+ });
+
+ // Unmap the two maps.
+ ASSERT_EQ(0, munmap(start_ptr, getpagesize()));
+ void* end_ptr =
+ reinterpret_cast<void*>(reinterpret_cast<uintptr_t>(start_ptr) + 2 * getpagesize());
+ ASSERT_EQ(0, munmap(end_ptr, getpagesize()));
+
+ unique_fd output_fd;
+ StartIntercept(&output_fd);
+ FinishCrasher();
+ AssertDeath(SIGSEGV);
+
+ int intercept_result;
+ FinishIntercept(&intercept_result);
+ ASSERT_EQ(1, intercept_result) << "tombstoned reported failure";
+
+ std::string result;
+ ConsumeFd(std::move(output_fd), &result);
+
+ std::string match_str = R"(signal 11 \(SIGSEGV\), code 1 \(SEGV_MAPERR\), fault addr )";
+ match_str += android::base::StringPrintf("%p", middle_ptr);
+ ASSERT_MATCH(result, match_str);
+
+ ASSERT_MATCH(result, R"(\nmemory map \(.*\): \(fault address prefixed with --->)\n)");
+
+ match_str = android::base::StringPrintf(
+ R"( %s.*\n--->Fault address falls at %s between mapped regions\n %s)",
+ format_pointer(start_ptr).c_str(), format_pointer(middle_ptr).c_str(),
+ format_pointer(end_ptr).c_str());
+ ASSERT_MATCH(result, match_str);
+}
+
+// Verify that a fault address happens in the correct map.
+TEST_F(CrasherTest, fault_address_in_map) {
+ // Create a map before the fork so it will be present in the child.
+ void* ptr = mmap(nullptr, getpagesize(), 0, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
+ ASSERT_NE(MAP_FAILED, ptr);
+
+ StartProcess([ptr]() {
+ ASSERT_EQ(0, crash_call(reinterpret_cast<uintptr_t>(ptr)));
+ _exit(0);
+ });
+
+ ASSERT_EQ(0, munmap(ptr, getpagesize()));
+
+ unique_fd output_fd;
+ StartIntercept(&output_fd);
+ FinishCrasher();
+ AssertDeath(SIGSEGV);
+
+ int intercept_result;
+ FinishIntercept(&intercept_result);
+ ASSERT_EQ(1, intercept_result) << "tombstoned reported failure";
+
+ std::string result;
+ ConsumeFd(std::move(output_fd), &result);
+
+ std::string match_str = R"(signal 11 \(SIGSEGV\), code 2 \(SEGV_ACCERR\), fault addr )";
+ match_str += android::base::StringPrintf("%p", ptr);
+ ASSERT_MATCH(result, match_str);
+
+ ASSERT_MATCH(result, R"(\nmemory map \(.*\): \(fault address prefixed with --->)\n)");
+
+ match_str = android::base::StringPrintf(R"(\n--->%s.*\n)", format_pointer(ptr).c_str());
+ ASSERT_MATCH(result, match_str);
+}
diff --git a/debuggerd/handler/debuggerd_handler.cpp b/debuggerd/handler/debuggerd_handler.cpp
index 375dbed..35be2bf 100644
--- a/debuggerd/handler/debuggerd_handler.cpp
+++ b/debuggerd/handler/debuggerd_handler.cpp
@@ -155,18 +155,14 @@
* could allocate memory or hold a lock.
*/
static void log_signal_summary(const siginfo_t* info) {
- char thread_name[MAX_TASK_NAME_LEN + 1]; // one more for termination
- if (prctl(PR_GET_NAME, reinterpret_cast<unsigned long>(thread_name), 0, 0, 0) != 0) {
- strcpy(thread_name, "<name unknown>");
- } else {
- // short names are null terminated by prctl, but the man page
- // implies that 16 byte names are not.
- thread_name[MAX_TASK_NAME_LEN] = 0;
+ char main_thread_name[MAX_TASK_NAME_LEN + 1];
+ if (!get_main_thread_name(main_thread_name, sizeof(main_thread_name))) {
+ strncpy(main_thread_name, "<unknown>", sizeof(main_thread_name));
}
if (info->si_signo == BIONIC_SIGNAL_DEBUGGER) {
- async_safe_format_log(ANDROID_LOG_INFO, "libc", "Requested dump for tid %d (%s)", __gettid(),
- thread_name);
+ async_safe_format_log(ANDROID_LOG_INFO, "libc", "Requested dump for pid %d (%s)", __getpid(),
+ main_thread_name);
return;
}
@@ -181,9 +177,13 @@
get_signal_sender(sender_desc, sizeof(sender_desc), info);
}
- char main_thread_name[MAX_TASK_NAME_LEN + 1];
- if (!get_main_thread_name(main_thread_name, sizeof(main_thread_name))) {
- strncpy(main_thread_name, "<unknown>", sizeof(main_thread_name));
+ char thread_name[MAX_TASK_NAME_LEN + 1]; // one more for termination
+ if (prctl(PR_GET_NAME, reinterpret_cast<unsigned long>(thread_name), 0, 0, 0) != 0) {
+ strcpy(thread_name, "<name unknown>");
+ } else {
+ // short names are null terminated by prctl, but the man page
+ // implies that 16 byte names are not.
+ thread_name[MAX_TASK_NAME_LEN] = 0;
}
async_safe_format_log(ANDROID_LOG_FATAL, "libc",
@@ -532,8 +532,13 @@
log_signal_summary(info);
+ // If we got here due to the signal BIONIC_SIGNAL_DEBUGGER, it's possible
+ // this is not the main thread, which can cause the intercept logic to fail
+ // since the intercept is only looking for the main thread. In this case,
+ // setting crashing_tid to pid instead of the current thread's tid avoids
+ // the problem.
debugger_thread_info thread_info = {
- .crashing_tid = __gettid(),
+ .crashing_tid = (signal_number == BIONIC_SIGNAL_DEBUGGER) ? __getpid() : __gettid(),
.pseudothread_tid = -1,
.siginfo = info,
.ucontext = context,
diff --git a/debuggerd/libdebuggerd/tombstone.cpp b/debuggerd/libdebuggerd/tombstone.cpp
index 9c01f15..ff03bcd 100644
--- a/debuggerd/libdebuggerd/tombstone.cpp
+++ b/debuggerd/libdebuggerd/tombstone.cpp
@@ -232,6 +232,12 @@
return;
}
+ // Remove any trailing newlines.
+ size_t index = length;
+ while (index > 0 && (msg[index - 1] == '\0' || msg[index - 1] == '\n')) {
+ --index;
+ }
+ msg[index] = '\0';
_LOG(log, logtype::HEADER, "Abort message: '%s'\n", &msg[0]);
}
diff --git a/debuggerd/libdebuggerd/tombstone_proto.cpp b/debuggerd/libdebuggerd/tombstone_proto.cpp
index ff12017..0c93c90 100644
--- a/debuggerd/libdebuggerd/tombstone_proto.cpp
+++ b/debuggerd/libdebuggerd/tombstone_proto.cpp
@@ -271,6 +271,13 @@
return;
}
+ // Remove any trailing newlines.
+ size_t index = msg.size();
+ while (index > 0 && (msg[index - 1] == '\0' || msg[index - 1] == '\n')) {
+ --index;
+ }
+ msg.resize(index);
+
tombstone->set_abort_message(msg);
}
diff --git a/debuggerd/libdebuggerd/tombstone_proto_to_text.cpp b/debuggerd/libdebuggerd/tombstone_proto_to_text.cpp
index 053299a..681b963 100644
--- a/debuggerd/libdebuggerd/tombstone_proto_to_text.cpp
+++ b/debuggerd/libdebuggerd/tombstone_proto_to_text.cpp
@@ -362,8 +362,13 @@
print_thread_memory_dump(callback, tombstone, thread);
CBS("");
- CBS("memory map (%d %s):", tombstone.memory_mappings().size(),
- tombstone.memory_mappings().size() == 1 ? "entry" : "entries");
+
+ // No memory maps to print.
+ if (tombstone.memory_mappings().empty()) {
+ CBS("No memory maps found");
+ return;
+ }
+
int word_size = pointer_width(tombstone);
const auto format_pointer = [word_size](uint64_t ptr) -> std::string {
if (word_size == 8) {
@@ -375,8 +380,41 @@
return StringPrintf("%0*" PRIx64, word_size * 2, ptr);
};
+ std::string memory_map_header =
+ StringPrintf("memory map (%d %s):", tombstone.memory_mappings().size(),
+ tombstone.memory_mappings().size() == 1 ? "entry" : "entries");
+
+ bool has_fault_address = signal_info.has_fault_address();
+ uint64_t fault_address = untag_address(signal_info.fault_address());
+ bool preamble_printed = false;
+ bool printed_fault_address_marker = false;
for (const auto& map : tombstone.memory_mappings()) {
+ if (!preamble_printed) {
+ preamble_printed = true;
+ if (has_fault_address) {
+ if (fault_address < map.begin_address()) {
+ memory_map_header +=
+ StringPrintf("\n--->Fault address falls at %s before any mapped regions",
+ format_pointer(fault_address).c_str());
+ printed_fault_address_marker = true;
+ } else {
+ memory_map_header += " (fault address prefixed with --->)";
+ }
+ }
+ CBS("%s", memory_map_header.c_str());
+ }
+
std::string line = " ";
+ if (has_fault_address && !printed_fault_address_marker) {
+ if (fault_address < map.begin_address()) {
+ printed_fault_address_marker = true;
+ CBS("--->Fault address falls at %s between mapped regions",
+ format_pointer(fault_address).c_str());
+ } else if (fault_address >= map.begin_address() && fault_address < map.end_address()) {
+ printed_fault_address_marker = true;
+ line = "--->";
+ }
+ }
StringAppendF(&line, "%s-%s", format_pointer(map.begin_address()).c_str(),
format_pointer(map.end_address() - 1).c_str());
StringAppendF(&line, " %s%s%s", map.read() ? "r" : "-", map.write() ? "w" : "-",
@@ -398,6 +436,11 @@
CBS("%s", line.c_str());
}
+
+ if (has_fault_address && !printed_fault_address_marker) {
+ CBS("--->Fault address falls at %s after any mapped regions",
+ format_pointer(fault_address).c_str());
+ }
}
void print_logs(CallbackType callback, const Tombstone& tombstone, int tail) {
diff --git a/debuggerd/tombstoned/tombstoned.cpp b/debuggerd/tombstoned/tombstoned.cpp
index 05d8050..02ded1e 100644
--- a/debuggerd/tombstoned/tombstoned.cpp
+++ b/debuggerd/tombstoned/tombstoned.cpp
@@ -448,8 +448,10 @@
return;
}
- if (crash->output.text.fd == -1) {
- LOG(WARNING) << "missing output fd";
+ if (crash->output.text.fd < 0) {
+ if (crash->output.text.fd == -1) {
+ LOG(WARNING) << "skipping tombstone file creation due to intercept";
+ }
return;
}
diff --git a/diagnose_usb/OWNERS b/diagnose_usb/OWNERS
index 643b448..fcd7757 100644
--- a/diagnose_usb/OWNERS
+++ b/diagnose_usb/OWNERS
@@ -1,2 +1 @@
-jmgao@google.com
yabinc@google.com
diff --git a/fastboot/OWNERS b/fastboot/OWNERS
index a72ee07..58b2a81 100644
--- a/fastboot/OWNERS
+++ b/fastboot/OWNERS
@@ -1,4 +1,3 @@
dvander@google.com
hridya@google.com
enh@google.com
-jmgao@google.com
diff --git a/fastboot/README.md b/fastboot/README.md
index c224448..d3b6c1a 100644
--- a/fastboot/README.md
+++ b/fastboot/README.md
@@ -27,16 +27,16 @@
1. Host sends a command, which is an ascii string in a single
packet no greater than 64 bytes.
-2. Client response with a single packet no greater than 64 bytes.
+2. Client response with a single packet no greater than 256 bytes.
The first four bytes of the response are "OKAY", "FAIL", "DATA",
or "INFO". Additional bytes may contain an (ascii) informative
message.
- a. INFO -> the remaining 60 bytes are an informative message
+ a. INFO -> the remaining 252 bytes are an informative message
(providing progress or diagnostic messages). They should
be displayed and then step #2 repeats
- b. FAIL -> the requested command failed. The remaining 60 bytes
+ b. FAIL -> the requested command failed. The remaining 252 bytes
of the response (if present) provide a textual failure message
to present to the user. Stop.
@@ -53,13 +53,13 @@
until the client has sent or received the number of bytes indicated
in the "DATA" response above.
-4. Client responds with a single packet no greater than 64 bytes.
+4. Client responds with a single packet no greater than 256 bytes.
The first four bytes of the response are "OKAY", "FAIL", or "INFO".
Similar to #2:
- a. INFO -> display the remaining 60 bytes and return to #4
+ a. INFO -> display the remaining 252 bytes and return to #4
- b. FAIL -> display the remaining 60 bytes (if present) as a failure
+ b. FAIL -> display the remaining 252 bytes (if present) as a failure
reason and consider the command failed. Stop.
c. OKAY -> success. Go to #5
diff --git a/fastboot/device/flashing.cpp b/fastboot/device/flashing.cpp
index ee0aa58..9b5d2cd 100644
--- a/fastboot/device/flashing.cpp
+++ b/fastboot/device/flashing.cpp
@@ -187,11 +187,17 @@
", build may be missing broken or missing boot_devices");
}
+ std::string slot_suffix = device->GetCurrentSlot();
+ uint32_t slot_number = SlotNumberForSlotSuffix(slot_suffix);
+
+ std::string other_slot_suffix;
+ if (!slot_suffix.empty()) {
+ other_slot_suffix = (slot_suffix == "_a") ? "_b" : "_a";
+ }
+
// If we are unable to read the existing metadata, then the super partition
// is corrupt. In this case we reflash the whole thing using the provided
// image.
- std::string slot_suffix = device->GetCurrentSlot();
- uint32_t slot_number = SlotNumberForSlotSuffix(slot_suffix);
std::unique_ptr<LpMetadata> old_metadata = ReadMetadata(super_name, slot_number);
if (wipe || !old_metadata) {
if (!FlashPartitionTable(super_name, *new_metadata.get())) {
@@ -203,11 +209,15 @@
}
std::set<std::string> partitions_to_keep;
+ bool virtual_ab = android::base::GetBoolProperty("ro.virtual_ab.enabled", false);
for (const auto& partition : old_metadata->partitions) {
// Preserve partitions in the other slot, but not the current slot.
std::string partition_name = GetPartitionName(partition);
- if (!slot_suffix.empty() && GetPartitionSlotSuffix(partition_name) == slot_suffix) {
- continue;
+ if (!slot_suffix.empty()) {
+ auto part_suffix = GetPartitionSlotSuffix(partition_name);
+ if (part_suffix == slot_suffix || (part_suffix == other_slot_suffix && virtual_ab)) {
+ continue;
+ }
}
std::string group_name = GetPartitionGroupName(old_metadata->groups[partition.group_index]);
// Skip partitions in the COW group
diff --git a/fastboot/fastboot.cpp b/fastboot/fastboot.cpp
index c87ce60..532b524 100644
--- a/fastboot/fastboot.cpp
+++ b/fastboot/fastboot.cpp
@@ -941,7 +941,8 @@
// Tries to locate top-level vbmeta from boot.img footer.
uint64_t footer_offset = buf->sz - AVB_FOOTER_SIZE;
if (0 != data.compare(footer_offset, AVB_FOOTER_MAGIC_LEN, AVB_FOOTER_MAGIC)) {
- die("Failed to find AVB_FOOTER at offset: %" PRId64, footer_offset);
+ die("Failed to find AVB_FOOTER at offset: %" PRId64 ", is BOARD_AVB_ENABLE true?",
+ footer_offset);
}
const AvbFooter* footer = reinterpret_cast<const AvbFooter*>(data.c_str() + footer_offset);
vbmeta_offset = be64toh(footer->vbmeta_offset);
@@ -1021,6 +1022,24 @@
return;
}
+ // If overflows and negative, it should be < buf->sz.
+ int64_t partition_size = static_cast<int64_t>(get_partition_size(partition));
+
+ if (partition_size == buf->sz) {
+ return;
+ }
+ // Some device bootloaders might not implement `fastboot getvar partition-size:boot[_a|_b]`.
+ // In this case, partition_size will be zero.
+ if (partition_size < buf->sz) {
+ fprintf(stderr,
+ "Warning: skip copying boot image avb footer"
+ " (boot partition size: %" PRId64 ", boot image size: %" PRId64 ").\n",
+ partition_size, buf->sz);
+ return;
+ }
+
+ // IMPORTANT: after the following read, we need to reset buf->fd before return (if not die).
+ // Because buf->fd will still be used afterwards.
std::string data;
if (!android::base::ReadFdToString(buf->fd, &data)) {
die("Failed reading from boot");
@@ -1028,17 +1047,9 @@
uint64_t footer_offset = buf->sz - AVB_FOOTER_SIZE;
if (0 != data.compare(footer_offset, AVB_FOOTER_MAGIC_LEN, AVB_FOOTER_MAGIC)) {
+ lseek(buf->fd.get(), 0, SEEK_SET); // IMPORTANT: resets buf->fd before return.
return;
}
- // If overflows and negative, it should be < buf->sz.
- int64_t partition_size = static_cast<int64_t>(get_partition_size(partition));
-
- if (partition_size == buf->sz) {
- return;
- }
- if (partition_size < buf->sz) {
- die("boot partition is smaller than boot image");
- }
unique_fd fd(make_temporary_fd("boot rewriting"));
if (!android::base::WriteStringToFd(data, fd)) {
diff --git a/fastboot/socket.cpp b/fastboot/socket.cpp
index 5a14b63..3096905 100644
--- a/fastboot/socket.cpp
+++ b/fastboot/socket.cpp
@@ -28,6 +28,10 @@
#include "socket.h"
+#ifndef _WIN32
+#include <sys/select.h>
+#endif
+
#include <android-base/errors.h>
#include <android-base/stringprintf.h>
diff --git a/fs_mgr/Android.bp b/fs_mgr/Android.bp
index 3d63a44..7ae526f 100644
--- a/fs_mgr/Android.bp
+++ b/fs_mgr/Android.bp
@@ -15,7 +15,10 @@
//
package {
- default_applicable_licenses: ["system_core_fs_mgr_license"],
+ default_applicable_licenses: [
+ "Android-Apache-2.0",
+ "system_core_fs_mgr_license",
+ ],
}
// Added automatically by a large-scale-change that took the approach of
@@ -36,10 +39,9 @@
name: "system_core_fs_mgr_license",
visibility: [":__subpackages__"],
license_kinds: [
- "SPDX-license-identifier-Apache-2.0",
"SPDX-license-identifier-MIT",
],
- // large-scale-change unable to identify any license_text files
+ license_text: ["NOTICE"],
}
cc_defaults {
@@ -63,6 +65,7 @@
"-D_FILE_OFFSET_BITS=64",
],
srcs: [
+ "blockdev.cpp",
"file_wait.cpp",
"fs_mgr.cpp",
"fs_mgr_format.cpp",
@@ -124,10 +127,19 @@
export_header_lib_headers: [
"libfiemap_headers",
],
- required: [
- "e2freefrag",
- "e2fsdroid",
- ],
+ target: {
+ platform: {
+ required: [
+ "e2freefrag",
+ "e2fsdroid",
+ ],
+ },
+ recovery: {
+ required: [
+ "e2fsdroid.recovery",
+ ],
+ },
+ },
}
// Two variants of libfs_mgr are provided: libfs_mgr and libfs_mgr_binder.
@@ -142,6 +154,7 @@
// It does not have a stable interface.
name: "libfs_mgr",
ramdisk_available: true,
+ vendor_ramdisk_available: true,
recovery_available: true,
defaults: [
"libfs_mgr_defaults",
@@ -167,6 +180,7 @@
name: "libfstab",
vendor_available: true,
ramdisk_available: true,
+ vendor_ramdisk_available: true,
recovery_available: true,
host_supported: true,
defaults: ["fs_mgr_defaults"],
diff --git a/fs_mgr/NOTICE b/fs_mgr/NOTICE
new file mode 100644
index 0000000..3972a40
--- /dev/null
+++ b/fs_mgr/NOTICE
@@ -0,0 +1,21 @@
+Copyright (C) 2016 The Android Open Source Project
+
+Permission is hereby granted, free of charge, to any person
+obtaining a copy of this software and associated documentation
+files (the "Software"), to deal in the Software without
+restriction, including without limitation the rights to use, copy,
+modify, merge, publish, distribute, sublicense, and/or sell copies
+of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/fs_mgr/TEST_MAPPING b/fs_mgr/TEST_MAPPING
index 84709b6..432aa4f 100644
--- a/fs_mgr/TEST_MAPPING
+++ b/fs_mgr/TEST_MAPPING
@@ -1,6 +1,9 @@
{
"presubmit": [
{
+ "name": "CtsFsMgrTestCases"
+ },
+ {
"name": "libdm_test"
},
{
@@ -13,6 +16,9 @@
"name": "fiemap_writer_test"
},
{
+ "name": "fs_mgr_vendor_overlay_test"
+ },
+ {
"name": "vts_libsnapshot_test"
},
{
diff --git a/fs_mgr/blockdev.cpp b/fs_mgr/blockdev.cpp
new file mode 100644
index 0000000..14b217c
--- /dev/null
+++ b/fs_mgr/blockdev.cpp
@@ -0,0 +1,159 @@
+/*
+ * 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 <android-base/file.h>
+#include <android-base/logging.h>
+#include <android-base/stringprintf.h>
+#include <android-base/strings.h>
+
+#include <dirent.h>
+#include <libdm/dm.h>
+#include <sys/stat.h>
+#include <sys/sysmacros.h>
+#include <sys/types.h>
+#include "blockdev.h"
+
+using android::base::Basename;
+using android::base::ErrnoError;
+using android::base::Error;
+using android::base::Result;
+using android::base::ResultError;
+using android::base::StartsWith;
+using android::base::StringPrintf;
+using android::base::unique_fd;
+using android::dm::DeviceMapper;
+
+// Return the parent device of a partition. Converts e.g. "sda26" into "sda".
+static std::string PartitionParent(const std::string& blockdev) {
+ if (blockdev.find('/') != std::string::npos) {
+ LOG(ERROR) << __func__ << ": invalid argument " << blockdev;
+ return blockdev;
+ }
+ auto dir = std::unique_ptr<DIR, decltype(&closedir)>{opendir("/sys/class/block"), closedir};
+ if (!dir) {
+ return blockdev;
+ }
+ for (struct dirent* ent = readdir(dir.get()); ent; ent = readdir(dir.get())) {
+ if (ent->d_name[0] == '.') {
+ continue;
+ }
+ std::string path = StringPrintf("/sys/class/block/%s/%s", ent->d_name, blockdev.c_str());
+ struct stat statbuf;
+ if (stat(path.c_str(), &statbuf) >= 0) {
+ return ent->d_name;
+ }
+ }
+ return blockdev;
+}
+
+// Convert a major:minor pair into a block device name.
+static std::string BlockdevName(dev_t dev) {
+ auto dir = std::unique_ptr<DIR, decltype(&closedir)>{opendir("/dev/block"), closedir};
+ if (!dir) {
+ return {};
+ }
+ for (struct dirent* ent = readdir(dir.get()); ent; ent = readdir(dir.get())) {
+ if (ent->d_name[0] == '.') {
+ continue;
+ }
+ const std::string path = std::string("/dev/block/") + ent->d_name;
+ struct stat statbuf;
+ if (stat(path.c_str(), &statbuf) >= 0 && dev == statbuf.st_rdev) {
+ return ent->d_name;
+ }
+ }
+ return {};
+}
+
+// Trim whitespace from the end of a string.
+static void rtrim(std::string& s) {
+ s.erase(s.find_last_not_of('\n') + 1, s.length());
+}
+
+// For file `file_path`, retrieve the block device backing the filesystem on
+// which the file exists and return the queue depth of the block device.
+static Result<uint32_t> BlockDeviceQueueDepth(const std::string& file_path) {
+ struct stat statbuf;
+ int res = stat(file_path.c_str(), &statbuf);
+ if (res < 0) {
+ return ErrnoError() << "stat(" << file_path << ")";
+ }
+ std::string blockdev = "/dev/block/" + BlockdevName(statbuf.st_dev);
+ LOG(DEBUG) << __func__ << ": " << file_path << " -> " << blockdev;
+ if (blockdev.empty()) {
+ const std::string err_msg =
+ StringPrintf("Failed to convert %u:%u (path %s)", major(statbuf.st_dev),
+ minor(statbuf.st_dev), file_path.c_str());
+ return ResultError(err_msg, 0);
+ }
+ auto& dm = DeviceMapper::Instance();
+ for (;;) {
+ std::optional<std::string> child = dm.GetParentBlockDeviceByPath(blockdev);
+ if (!child) {
+ break;
+ }
+ LOG(DEBUG) << __func__ << ": " << blockdev << " -> " << *child;
+ blockdev = *child;
+ }
+ std::optional<std::string> maybe_blockdev = android::dm::ExtractBlockDeviceName(blockdev);
+ if (!maybe_blockdev) {
+ return ResultError("Failed to remove /dev/block/ prefix from " + blockdev, 0);
+ }
+ blockdev = PartitionParent(*maybe_blockdev);
+ LOG(DEBUG) << __func__ << ": "
+ << "Partition parent: " << blockdev;
+ const std::string nr_tags_path =
+ StringPrintf("/sys/class/block/%s/mq/0/nr_tags", blockdev.c_str());
+ std::string nr_tags;
+ if (!android::base::ReadFileToString(nr_tags_path, &nr_tags)) {
+ return ResultError("Failed to read " + nr_tags_path, 0);
+ }
+ rtrim(nr_tags);
+ LOG(DEBUG) << __func__ << ": " << file_path << " is backed by /dev/" << blockdev
+ << " and that block device supports queue depth " << nr_tags;
+ return strtol(nr_tags.c_str(), NULL, 0);
+}
+
+// Set 'nr_requests' of `loop_device_path` to the queue depth of the block
+// device backing `file_path`.
+Result<void> ConfigureQueueDepth(const std::string& loop_device_path,
+ const std::string& file_path) {
+ if (!StartsWith(loop_device_path, "/dev/")) {
+ return Error() << "Invalid argument " << loop_device_path;
+ }
+
+ const std::string loop_device_name = Basename(loop_device_path);
+
+ const Result<uint32_t> qd = BlockDeviceQueueDepth(file_path);
+ if (!qd.ok()) {
+ LOG(DEBUG) << __func__ << ": "
+ << "BlockDeviceQueueDepth() returned " << qd.error();
+ return ResultError(qd.error());
+ }
+ const std::string nr_requests = StringPrintf("%u", *qd);
+ const std::string sysfs_path =
+ StringPrintf("/sys/class/block/%s/queue/nr_requests", loop_device_name.c_str());
+ unique_fd sysfs_fd(open(sysfs_path.c_str(), O_RDWR | O_CLOEXEC));
+ if (sysfs_fd == -1) {
+ return ErrnoError() << "Failed to open " << sysfs_path;
+ }
+
+ const int res = write(sysfs_fd.get(), nr_requests.data(), nr_requests.length());
+ if (res < 0) {
+ return ErrnoError() << "Failed to write to " << sysfs_path;
+ }
+ return {};
+}
diff --git a/fs_mgr/blockdev.h b/fs_mgr/blockdev.h
new file mode 100644
index 0000000..2c0d68a
--- /dev/null
+++ b/fs_mgr/blockdev.h
@@ -0,0 +1,21 @@
+/*
+ * 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 <android-base/result.h>
+#include <string>
+
+android::base::Result<void> ConfigureQueueDepth(const std::string& loop_device_path,
+ const std::string& file_path);
diff --git a/fs_mgr/file_wait.cpp b/fs_mgr/file_wait.cpp
index cbf6845..af0699b 100644
--- a/fs_mgr/file_wait.cpp
+++ b/fs_mgr/file_wait.cpp
@@ -206,6 +206,9 @@
}
int64_t OneShotInotify::RemainingMs() const {
+ if (relative_timeout_ == std::chrono::milliseconds::max()) {
+ return std::chrono::milliseconds::max().count();
+ }
auto remaining = (std::chrono::steady_clock::now() - start_time_);
auto elapsed = std::chrono::duration_cast<std::chrono::milliseconds>(remaining);
return (relative_timeout_ - elapsed).count();
diff --git a/fs_mgr/fs_mgr.cpp b/fs_mgr/fs_mgr.cpp
index 01c8ad3..5f15aad 100644
--- a/fs_mgr/fs_mgr.cpp
+++ b/fs_mgr/fs_mgr.cpp
@@ -34,11 +34,13 @@
#include <time.h>
#include <unistd.h>
+#include <array>
#include <chrono>
#include <functional>
#include <map>
#include <memory>
#include <string>
+#include <string_view>
#include <thread>
#include <utility>
#include <vector>
@@ -70,6 +72,7 @@
#include <log/log_properties.h>
#include <logwrap/logwrap.h>
+#include "blockdev.h"
#include "fs_mgr_priv.h"
#define KEY_LOC_PROP "ro.crypto.keyfile.userdata"
@@ -101,6 +104,7 @@
using android::base::Realpath;
using android::base::SetProperty;
using android::base::StartsWith;
+using android::base::StringPrintf;
using android::base::Timer;
using android::base::unique_fd;
using android::dm::DeviceMapper;
@@ -790,20 +794,26 @@
int save_errno = 0;
int gc_allowance = 0;
std::string opts;
+ std::string checkpoint_opts;
bool try_f2fs_gc_allowance = is_f2fs(entry.fs_type) && entry.fs_checkpoint_opts.length() > 0;
+ bool try_f2fs_fallback = false;
Timer t;
do {
- if (save_errno == EINVAL && try_f2fs_gc_allowance) {
- PINFO << "Kernel does not support checkpoint=disable:[n]%, trying without.";
+ if (save_errno == EINVAL && (try_f2fs_gc_allowance || try_f2fs_fallback)) {
+ PINFO << "Kernel does not support " << checkpoint_opts << ", trying without.";
try_f2fs_gc_allowance = false;
+ // Attempt without gc allowance before dropping.
+ try_f2fs_fallback = !try_f2fs_fallback;
}
if (try_f2fs_gc_allowance) {
- opts = entry.fs_options + entry.fs_checkpoint_opts + ":" +
- std::to_string(gc_allowance) + "%";
+ checkpoint_opts = entry.fs_checkpoint_opts + ":" + std::to_string(gc_allowance) + "%";
+ } else if (try_f2fs_fallback) {
+ checkpoint_opts = entry.fs_checkpoint_opts;
} else {
- opts = entry.fs_options;
+ checkpoint_opts = "";
}
+ opts = entry.fs_options + checkpoint_opts;
if (save_errno == EAGAIN) {
PINFO << "Retrying mount (source=" << source << ",target=" << target
<< ",type=" << entry.fs_type << ", gc_allowance=" << gc_allowance << "%)=" << ret
@@ -814,7 +824,7 @@
save_errno = errno;
if (try_f2fs_gc_allowance) gc_allowance += 10;
} while ((ret && save_errno == EAGAIN && gc_allowance <= 100) ||
- (ret && save_errno == EINVAL && try_f2fs_gc_allowance));
+ (ret && save_errno == EINVAL && (try_f2fs_gc_allowance || try_f2fs_fallback)));
const char* target_missing = "";
const char* source_missing = "";
if (save_errno == ENOENT) {
@@ -2038,6 +2048,35 @@
return 0;
}
+static bool ConfigureIoScheduler(const std::string& device_path) {
+ if (!StartsWith(device_path, "/dev/")) {
+ LERROR << __func__ << ": invalid argument " << device_path;
+ return false;
+ }
+
+ const std::string iosched_path =
+ StringPrintf("/sys/block/%s/queue/scheduler", Basename(device_path).c_str());
+ unique_fd iosched_fd(open(iosched_path.c_str(), O_RDWR | O_CLOEXEC));
+ if (iosched_fd.get() == -1) {
+ PERROR << __func__ << ": failed to open " << iosched_path;
+ return false;
+ }
+
+ // Kernels before v4.1 only support 'noop'. Kernels [v4.1, v5.0) support
+ // 'noop' and 'none'. Kernels v5.0 and later only support 'none'.
+ static constexpr const std::array<std::string_view, 2> kNoScheduler = {"none", "noop"};
+
+ for (const std::string_view& scheduler : kNoScheduler) {
+ int ret = write(iosched_fd.get(), scheduler.data(), scheduler.size());
+ if (ret > 0) {
+ return true;
+ }
+ }
+
+ PERROR << __func__ << ": failed to write to " << iosched_path;
+ return false;
+}
+
static bool InstallZramDevice(const std::string& device) {
if (!android::base::WriteStringToFile(device, ZRAM_BACK_DEV)) {
PERROR << "Cannot write " << device << " in: " << ZRAM_BACK_DEV;
@@ -2065,22 +2104,26 @@
// Allocate loop device and attach it to file_path.
LoopControl loop_control;
- std::string device;
- if (!loop_control.Attach(target_fd.get(), 5s, &device)) {
+ std::string loop_device;
+ if (!loop_control.Attach(target_fd.get(), 5s, &loop_device)) {
return false;
}
+ ConfigureIoScheduler(loop_device);
+
+ ConfigureQueueDepth(loop_device, "/");
+
// set block size & direct IO
- unique_fd device_fd(TEMP_FAILURE_RETRY(open(device.c_str(), O_RDWR | O_CLOEXEC)));
- if (device_fd.get() == -1) {
- PERROR << "Cannot open " << device;
+ unique_fd loop_fd(TEMP_FAILURE_RETRY(open(loop_device.c_str(), O_RDWR | O_CLOEXEC)));
+ if (loop_fd.get() == -1) {
+ PERROR << "Cannot open " << loop_device;
return false;
}
- if (!LoopControl::EnableDirectIo(device_fd.get())) {
+ if (!LoopControl::EnableDirectIo(loop_fd.get())) {
return false;
}
- return InstallZramDevice(device);
+ return InstallZramDevice(loop_device);
}
bool fs_mgr_swapon_all(const Fstab& fstab) {
@@ -2322,7 +2365,24 @@
return false;
}
- auto options = "lowerdir=" + entry.lowerdir;
+ auto lowerdir = entry.lowerdir;
+ if (entry.fs_mgr_flags.overlayfs_remove_missing_lowerdir) {
+ bool removed_any = false;
+ std::vector<std::string> lowerdirs;
+ for (const auto& dir : android::base::Split(entry.lowerdir, ":")) {
+ if (access(dir.c_str(), F_OK)) {
+ PWARNING << __FUNCTION__ << "(): remove missing lowerdir '" << dir << "'";
+ removed_any = true;
+ } else {
+ lowerdirs.push_back(dir);
+ }
+ }
+ if (removed_any) {
+ lowerdir = android::base::Join(lowerdirs, ":");
+ }
+ }
+
+ auto options = "lowerdir=" + lowerdir;
if (overlayfs_valid_result == OverlayfsValidResult::kOverrideCredsRequired) {
options += ",override_creds=off";
}
diff --git a/fs_mgr/fs_mgr_fstab.cpp b/fs_mgr/fs_mgr_fstab.cpp
index d0c89b9..9b6c3dd 100644
--- a/fs_mgr/fs_mgr_fstab.cpp
+++ b/fs_mgr/fs_mgr_fstab.cpp
@@ -181,6 +181,7 @@
CheckFlag("fsverity", fs_verity);
CheckFlag("metadata_csum", ext_meta_csum);
CheckFlag("fscompress", fs_compress);
+ CheckFlag("overlayfs_remove_missing_lowerdir", overlayfs_remove_missing_lowerdir);
#undef CheckFlag
@@ -413,17 +414,24 @@
return fstab_result;
}
-// Identify path to fstab file. Lookup is based on pattern
-// fstab.<fstab_suffix>, fstab.<hardware>, fstab.<hardware.platform> in
-// folders /odm/etc, vendor/etc, or /.
+// Return the path to the fstab file. There may be multiple fstab files; the
+// one that is returned will be the first that exists of fstab.<fstab_suffix>,
+// fstab.<hardware>, and fstab.<hardware.platform>. The fstab is searched for
+// in /odm/etc/ and /vendor/etc/, as well as in the locations where it may be in
+// the first stage ramdisk during early boot. Previously, the first stage
+// 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() {
for (const char* prop : {"fstab_suffix", "hardware", "hardware.platform"}) {
std::string suffix;
if (!fs_mgr_get_boot_config(prop, &suffix)) continue;
- for (const char* prefix :
- {"/odm/etc/fstab.", "/vendor/etc/fstab.", "/fstab.", "/first_stage_ramdisk/fstab."}) {
+ for (const char* prefix : {// late-boot/post-boot locations
+ "/odm/etc/fstab.", "/vendor/etc/fstab.",
+ // early boot locations
+ "/system/etc/fstab.", "/first_stage_ramdisk/system/etc/fstab.",
+ "/fstab.", "/first_stage_ramdisk/fstab."}) {
std::string fstab_path = prefix + suffix;
if (access(fstab_path.c_str(), F_OK) == 0) {
return fstab_path;
diff --git a/fs_mgr/fs_mgr_overlayfs.cpp b/fs_mgr/fs_mgr_overlayfs.cpp
index 4d32bda..0522ac5 100644
--- a/fs_mgr/fs_mgr_overlayfs.cpp
+++ b/fs_mgr/fs_mgr_overlayfs.cpp
@@ -1133,7 +1133,7 @@
return false;
}
if (!images->BackingImageExists(partition_name)) {
- static constexpr uint64_t kMinimumSize = 16_MiB;
+ static constexpr uint64_t kMinimumSize = 64_MiB;
static constexpr uint64_t kMaximumSize = 2_GiB;
uint64_t size = std::clamp(info.size / 2, kMinimumSize, kMaximumSize);
diff --git a/fs_mgr/fs_mgr_roots.cpp b/fs_mgr/fs_mgr_roots.cpp
index fdaffbe..d275320 100644
--- a/fs_mgr/fs_mgr_roots.cpp
+++ b/fs_mgr/fs_mgr_roots.cpp
@@ -14,6 +14,7 @@
* limitations under the License.
*/
+#include "android-base/file.h"
#include "fs_mgr/roots.h"
#include <sys/mount.h>
@@ -39,18 +40,26 @@
while (true) {
auto entry = GetEntryForMountPoint(fstab, str);
if (entry != nullptr) return entry;
- if (str == "/") break;
- auto slash = str.find_last_of('/');
- if (slash == std::string::npos) break;
- if (slash == 0) {
- str = "/";
- } else {
- str = str.substr(0, slash);
- }
+ str = android::base::Dirname(str);
+ if (!str.compare(".") || !str.compare("/")) break;
}
return nullptr;
}
+std::vector<FstabEntry*> GetEntriesForPath(Fstab* fstab, const std::string& path) {
+ std::vector<FstabEntry*> entries;
+ if (path.empty()) return entries;
+
+ std::string str(path);
+ while (true) {
+ entries = GetEntriesForMountPoint(fstab, str);
+ if (!entries.empty()) return entries;
+ str = android::base::Dirname(str);
+ if (!str.compare(".") || !str.compare("/")) break;
+ }
+ return entries;
+}
+
enum class MountState {
ERROR = -1,
NOT_MOUNTED = 0,
@@ -71,12 +80,7 @@
return MountState::NOT_MOUNTED;
}
-bool EnsurePathMounted(Fstab* fstab, const std::string& path, const std::string& mount_pt) {
- auto rec = GetEntryForPath(fstab, path);
- if (rec == nullptr) {
- LERROR << "unknown volume for path [" << path << "]";
- return false;
- }
+bool TryPathMount(FstabEntry* rec, const std::string& mount_pt) {
if (rec->fs_type == "ramdisk") {
// The ramdisk is always mounted.
return true;
@@ -136,6 +140,21 @@
return true;
}
+bool EnsurePathMounted(Fstab* fstab, const std::string& path, const std::string& mount_point) {
+ auto entries = GetEntriesForPath(fstab, path);
+ if (entries.empty()) {
+ LERROR << "unknown volume for path [" << path << "]";
+ return false;
+ }
+
+ for (auto entry : entries) {
+ if (TryPathMount(entry, mount_point)) return true;
+ }
+
+ LERROR << "Failed to mount for path [" << path << "]";
+ return false;
+}
+
bool EnsurePathUnmounted(Fstab* fstab, const std::string& path) {
auto rec = GetEntryForPath(fstab, path);
if (rec == nullptr) {
diff --git a/fs_mgr/include/fs_mgr/file_wait.h b/fs_mgr/include/fs_mgr/file_wait.h
index 74d160e..294e727 100644
--- a/fs_mgr/include/fs_mgr/file_wait.h
+++ b/fs_mgr/include/fs_mgr/file_wait.h
@@ -23,6 +23,9 @@
// Wait at most |relative_timeout| milliseconds for |path| to exist. dirname(path)
// must already exist. For example, to wait on /dev/block/dm-6, /dev/block must
// be a valid directory.
+//
+// If relative_timeout is std::chrono::milliseconds::max(), then the wait will
+// block indefinitely.
bool WaitForFile(const std::string& path, const std::chrono::milliseconds relative_timeout);
// Wait at most |relative_timeout| milliseconds for |path| to stop existing.
diff --git a/fs_mgr/include_fstab/fstab/fstab.h b/fs_mgr/include_fstab/fstab/fstab.h
index f33768b..9a4ed46 100644
--- a/fs_mgr/include_fstab/fstab/fstab.h
+++ b/fs_mgr/include_fstab/fstab/fstab.h
@@ -86,6 +86,7 @@
bool fs_verity : 1;
bool ext_meta_csum : 1;
bool fs_compress : 1;
+ bool overlayfs_remove_missing_lowerdir : 1;
} fs_mgr_flags = {};
bool is_encryptable() const {
diff --git a/fs_mgr/libdm/dm.cpp b/fs_mgr/libdm/dm.cpp
index a5eda29..4034e30 100644
--- a/fs_mgr/libdm/dm.cpp
+++ b/fs_mgr/libdm/dm.cpp
@@ -578,34 +578,30 @@
return std::string{spec.target_type, sizeof(spec.target_type)};
}
-static bool ExtractBlockDeviceName(const std::string& path, std::string* name) {
+std::optional<std::string> ExtractBlockDeviceName(const std::string& path) {
static constexpr std::string_view kDevBlockPrefix("/dev/block/");
if (android::base::StartsWith(path, kDevBlockPrefix)) {
- *name = path.substr(kDevBlockPrefix.length());
- return true;
+ return path.substr(kDevBlockPrefix.length());
}
- return false;
+ return {};
}
bool DeviceMapper::IsDmBlockDevice(const std::string& path) {
- std::string name;
- if (!ExtractBlockDeviceName(path, &name)) {
- return false;
- }
- return android::base::StartsWith(name, "dm-");
+ std::optional<std::string> name = ExtractBlockDeviceName(path);
+ return name && android::base::StartsWith(*name, "dm-");
}
std::optional<std::string> DeviceMapper::GetDmDeviceNameByPath(const std::string& path) {
- std::string name;
- if (!ExtractBlockDeviceName(path, &name)) {
+ std::optional<std::string> name = ExtractBlockDeviceName(path);
+ if (!name) {
LOG(WARNING) << path << " is not a block device";
return std::nullopt;
}
- if (!android::base::StartsWith(name, "dm-")) {
+ if (!android::base::StartsWith(*name, "dm-")) {
LOG(WARNING) << path << " is not a dm device";
return std::nullopt;
}
- std::string dm_name_file = "/sys/block/" + name + "/dm/name";
+ std::string dm_name_file = "/sys/block/" + *name + "/dm/name";
std::string dm_name;
if (!android::base::ReadFileToString(dm_name_file, &dm_name)) {
PLOG(ERROR) << "Failed to read file " << dm_name_file;
@@ -616,16 +612,16 @@
}
std::optional<std::string> DeviceMapper::GetParentBlockDeviceByPath(const std::string& path) {
- std::string name;
- if (!ExtractBlockDeviceName(path, &name)) {
+ std::optional<std::string> name = ExtractBlockDeviceName(path);
+ if (!name) {
LOG(WARNING) << path << " is not a block device";
return std::nullopt;
}
- if (!android::base::StartsWith(name, "dm-")) {
+ if (!android::base::StartsWith(*name, "dm-")) {
// Reached bottom of the device mapper stack.
return std::nullopt;
}
- auto slaves_dir = "/sys/block/" + name + "/slaves";
+ auto slaves_dir = "/sys/block/" + *name + "/slaves";
auto dir = std::unique_ptr<DIR, decltype(&closedir)>(opendir(slaves_dir.c_str()), closedir);
if (dir == nullptr) {
PLOG(ERROR) << "Failed to open: " << slaves_dir;
@@ -651,5 +647,61 @@
return spec.target_type == "snapshot"s && data == "Overflow"s;
}
+// Find directories in format of "/sys/block/dm-X".
+static int DmNameFilter(const dirent* de) {
+ if (android::base::StartsWith(de->d_name, "dm-")) {
+ return 1;
+ }
+ return 0;
+}
+
+std::map<std::string, std::string> DeviceMapper::FindDmPartitions() {
+ static constexpr auto DM_PATH_PREFIX = "/sys/block/";
+ dirent** namelist;
+ int n = scandir(DM_PATH_PREFIX, &namelist, DmNameFilter, alphasort);
+ if (n == -1) {
+ PLOG(ERROR) << "Failed to scan dir " << DM_PATH_PREFIX;
+ return {};
+ }
+ if (n == 0) {
+ LOG(ERROR) << "No dm block device found.";
+ free(namelist);
+ return {};
+ }
+
+ static constexpr auto DM_PATH_SUFFIX = "/dm/name";
+ static constexpr auto DEV_PATH = "/dev/block/";
+ std::map<std::string, std::string> dm_block_devices;
+ while (n--) {
+ std::string path = DM_PATH_PREFIX + std::string(namelist[n]->d_name) + DM_PATH_SUFFIX;
+ std::string content;
+ if (!android::base::ReadFileToString(path, &content)) {
+ PLOG(WARNING) << "Failed to read " << path;
+ } else {
+ std::string dm_block_name = android::base::Trim(content);
+ // AVB is using 'vroot' for the root block device but we're expecting 'system'.
+ if (dm_block_name == "vroot") {
+ dm_block_name = "system";
+ } else if (android::base::EndsWith(dm_block_name, "-verity")) {
+ auto npos = dm_block_name.rfind("-verity");
+ dm_block_name = dm_block_name.substr(0, npos);
+ } else if (!android::base::GetProperty("ro.boot.avb_version", "").empty()) {
+ // Verified Boot 1.0 doesn't add a -verity suffix. On AVB 2 devices,
+ // if DAP is enabled, then a -verity suffix must be used to
+ // differentiate between dm-linear and dm-verity devices. If we get
+ // here, we're AVB 2 and looking at a non-verity partition.
+ free(namelist[n]);
+ continue;
+ }
+
+ dm_block_devices.emplace(dm_block_name, DEV_PATH + std::string(namelist[n]->d_name));
+ }
+ free(namelist[n]);
+ }
+ free(namelist);
+
+ return dm_block_devices;
+}
+
} // namespace dm
} // namespace android
diff --git a/fs_mgr/libdm/dm_test.cpp b/fs_mgr/libdm/dm_test.cpp
index 8314ec5..541f254 100644
--- a/fs_mgr/libdm/dm_test.cpp
+++ b/fs_mgr/libdm/dm_test.cpp
@@ -684,13 +684,9 @@
TEST(libdm, CreateEmptyDevice) {
DeviceMapper& dm = DeviceMapper::Instance();
ASSERT_TRUE(dm.CreateEmptyDevice("empty-device"));
- auto guard = android::base::make_scope_guard([&]() { dm.DeleteDevice("empty-device", 5s); });
+ auto guard =
+ android::base::make_scope_guard([&]() { dm.DeleteDeviceIfExists("empty-device", 5s); });
// Empty device should be in suspended state.
ASSERT_EQ(DmDeviceState::SUSPENDED, dm.GetState("empty-device"));
-
- std::string path;
- ASSERT_TRUE(dm.WaitForDevice("empty-device", 5s, &path));
- // Path should exist.
- ASSERT_EQ(0, access(path.c_str(), F_OK));
}
diff --git a/fs_mgr/libdm/include/libdm/dm.h b/fs_mgr/libdm/include/libdm/dm.h
index 8fcdf74..7e01b85 100644
--- a/fs_mgr/libdm/include/libdm/dm.h
+++ b/fs_mgr/libdm/include/libdm/dm.h
@@ -17,6 +17,7 @@
#ifndef _LIBDM_DM_H_
#define _LIBDM_DM_H_
+#include <dirent.h>
#include <fcntl.h>
#include <linux/dm-ioctl.h>
#include <linux/kdev_t.h>
@@ -26,6 +27,7 @@
#include <unistd.h>
#include <chrono>
+#include <map>
#include <memory>
#include <optional>
#include <string>
@@ -49,6 +51,10 @@
static constexpr uint64_t kSectorSize = 512;
+// Returns `path` without /dev/block prefix if and only if `path` starts with
+// that prefix.
+std::optional<std::string> ExtractBlockDeviceName(const std::string& path);
+
class DeviceMapper final {
public:
class DmBlockDevice final {
@@ -255,6 +261,12 @@
// * A failure occurred.
std::optional<std::string> GetParentBlockDeviceByPath(const std::string& path);
+ // Iterate the content over "/sys/block/dm-x/dm/name" and find
+ // all the dm-wrapped block devices.
+ //
+ // Returns mapping <partition-name, /dev/block/dm-x>
+ std::map<std::string, std::string> FindDmPartitions();
+
private:
// Maximum possible device mapper targets registered in the kernel.
// This is only used to read the list of targets from kernel so we allocate
diff --git a/fs_mgr/libfiemap/Android.bp b/fs_mgr/libfiemap/Android.bp
index b62e33f..d16b8d6 100644
--- a/fs_mgr/libfiemap/Android.bp
+++ b/fs_mgr/libfiemap/Android.bp
@@ -21,6 +21,7 @@
cc_library_headers {
name: "libfiemap_headers",
ramdisk_available: true,
+ vendor_ramdisk_available: true,
recovery_available: true,
export_include_dirs: ["include"],
}
diff --git a/fs_mgr/libfiemap/fiemap_writer_test.cpp b/fs_mgr/libfiemap/fiemap_writer_test.cpp
index 3c8ab42..b31c78d 100644
--- a/fs_mgr/libfiemap/fiemap_writer_test.cpp
+++ b/fs_mgr/libfiemap/fiemap_writer_test.cpp
@@ -16,6 +16,7 @@
#include <fcntl.h>
#include <inttypes.h>
+#include <linux/limits.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
@@ -299,6 +300,27 @@
ASSERT_EQ(errno, ENOENT);
}
+TEST_F(SplitFiemapTest, CorruptSplit) {
+ unique_fd fd(open(testfile.c_str(), O_RDWR | O_CREAT | O_TRUNC, 0700));
+ ASSERT_GE(fd, 0);
+
+ // Make a giant random string.
+ std::vector<char> data;
+ for (size_t i = 0x1; i < 0x7f; i++) {
+ for (size_t j = 0; j < 100; j++) {
+ data.emplace_back(i);
+ }
+ }
+ ASSERT_GT(data.size(), PATH_MAX);
+
+ data.emplace_back('\n');
+
+ ASSERT_TRUE(android::base::WriteFully(fd, data.data(), data.size()));
+ fd = {};
+
+ ASSERT_TRUE(SplitFiemap::RemoveSplitFiles(testfile));
+}
+
static string ReadSplitFiles(const std::string& base_path, size_t num_files) {
std::string result;
for (int i = 0; i < num_files; i++) {
diff --git a/fs_mgr/libfiemap/split_fiemap_writer.cpp b/fs_mgr/libfiemap/split_fiemap_writer.cpp
index 36bb3df..0df6125 100644
--- a/fs_mgr/libfiemap/split_fiemap_writer.cpp
+++ b/fs_mgr/libfiemap/split_fiemap_writer.cpp
@@ -136,6 +136,7 @@
return FiemapStatus::FromErrno(errno);
}
}
+ fsync(fd.get());
// Unset this bit, so we don't unlink on destruction.
out->creating_ = false;
@@ -192,6 +193,9 @@
std::vector<std::string> files;
if (GetSplitFileList(file_path, &files)) {
for (const auto& file : files) {
+ if (access(file.c_str(), F_OK) != 0 && (errno == ENOENT || errno == ENAMETOOLONG)) {
+ continue;
+ }
ok &= android::base::RemoveFileIfExists(file, message);
}
}
diff --git a/fs_mgr/libfs_avb/Android.bp b/fs_mgr/libfs_avb/Android.bp
index 62493eb..0cbd9db 100644
--- a/fs_mgr/libfs_avb/Android.bp
+++ b/fs_mgr/libfs_avb/Android.bp
@@ -28,6 +28,7 @@
name: "libfs_avb",
defaults: ["fs_mgr_defaults"],
ramdisk_available: true,
+ vendor_ramdisk_available: true,
recovery_available: true,
host_supported: true,
export_include_dirs: ["include"],
diff --git a/fs_mgr/liblp/Android.bp b/fs_mgr/liblp/Android.bp
index 86ca8f3..fc2d8a1 100644
--- a/fs_mgr/liblp/Android.bp
+++ b/fs_mgr/liblp/Android.bp
@@ -31,6 +31,7 @@
name: "liblp",
host_supported: true,
ramdisk_available: true,
+ vendor_ramdisk_available: true,
recovery_available: true,
defaults: ["fs_mgr_defaults"],
cppflags: [
diff --git a/fs_mgr/libsnapshot/Android.bp b/fs_mgr/libsnapshot/Android.bp
index acfaa84..df00f45 100644
--- a/fs_mgr/libsnapshot/Android.bp
+++ b/fs_mgr/libsnapshot/Android.bp
@@ -182,38 +182,6 @@
vendor_ramdisk_available: true,
}
-cc_defaults {
- name: "libsnapshot_snapuserd_defaults",
- defaults: [
- "fs_mgr_defaults",
- ],
- cflags: [
- "-D_FILE_OFFSET_BITS=64",
- "-Wall",
- "-Werror",
- ],
- export_include_dirs: ["include"],
- srcs: [
- "snapuserd_client.cpp",
- ],
-}
-
-cc_library_static {
- name: "libsnapshot_snapuserd",
- defaults: [
- "libsnapshot_snapuserd_defaults",
- ],
- recovery_available: true,
- static_libs: [
- "libcutils_sockets",
- ],
- shared_libs: [
- "libbase",
- "liblog",
- ],
- ramdisk_available: true,
-}
-
cc_library_static {
name: "libsnapshot_test_helpers",
defaults: ["libsnapshot_defaults"],
@@ -412,49 +380,6 @@
require_root: true,
}
-cc_defaults {
- name: "snapuserd_defaults",
- defaults: [
- "fs_mgr_defaults",
- ],
- srcs: [
- "snapuserd_server.cpp",
- "snapuserd.cpp",
- "snapuserd_daemon.cpp",
- "snapuserd_worker.cpp",
- "snapuserd_readahead.cpp",
- ],
-
- cflags: [
- "-Wall",
- "-Werror"
- ],
-
- static_libs: [
- "libbase",
- "libbrotli",
- "libcutils_sockets",
- "libdm",
- "libgflags",
- "liblog",
- "libsnapshot_cow",
- "libz",
- ],
-}
-
-cc_binary {
- name: "snapuserd",
- defaults: ["snapuserd_defaults"],
- init_rc: [
- "snapuserd.rc",
- ],
- static_executable: true,
- system_shared_libs: [],
- ramdisk_available: true,
- vendor_ramdisk_available: true,
- recovery_available: true,
-}
-
cc_test {
name: "cow_api_test",
defaults: [
@@ -556,43 +481,6 @@
},
}
-cc_test {
- name: "cow_snapuserd_test",
- defaults: [
- "fs_mgr_defaults",
- ],
- srcs: [
- "cow_snapuserd_test.cpp",
- "snapuserd.cpp",
- "snapuserd_worker.cpp",
- ],
- cflags: [
- "-Wall",
- "-Werror",
- ],
- shared_libs: [
- "libbase",
- "liblog",
- ],
- static_libs: [
- "libbrotli",
- "libgtest",
- "libsnapshot_cow",
- "libsnapshot_snapuserd",
- "libcutils_sockets",
- "libz",
- "libfs_mgr",
- "libdm",
- ],
- header_libs: [
- "libstorage_literals_headers",
- "libfiemap_headers",
- ],
- test_min_api_level: 30,
- auto_gen_config: true,
- require_root: false,
-}
-
cc_binary {
name: "inspect_cow",
host_supported: true,
@@ -616,3 +504,13 @@
"inspect_cow.cpp",
],
}
+
+python_library_host {
+ name: "snapshot_proto_python",
+ srcs: [
+ "android/snapshot/snapshot.proto",
+ ],
+ proto: {
+ canonical_path_from_root: false,
+ },
+}
diff --git a/fs_mgr/libsnapshot/cow_api_test.cpp b/fs_mgr/libsnapshot/cow_api_test.cpp
index 7f7e40a..6066309 100644
--- a/fs_mgr/libsnapshot/cow_api_test.cpp
+++ b/fs_mgr/libsnapshot/cow_api_test.cpp
@@ -140,6 +140,85 @@
ASSERT_TRUE(iter->Done());
}
+TEST_F(CowTest, ReadWriteXor) {
+ CowOptions options;
+ options.cluster_ops = 0;
+ CowWriter writer(options);
+
+ ASSERT_TRUE(writer.Initialize(cow_->fd));
+
+ std::string data = "This is some data, believe it";
+ data.resize(options.block_size, '\0');
+
+ ASSERT_TRUE(writer.AddCopy(10, 20));
+ ASSERT_TRUE(writer.AddXorBlocks(50, data.data(), data.size(), 24, 10));
+ ASSERT_TRUE(writer.AddZeroBlocks(51, 2));
+ ASSERT_TRUE(writer.Finalize());
+
+ ASSERT_EQ(lseek(cow_->fd, 0, SEEK_SET), 0);
+
+ CowReader reader;
+ CowHeader header;
+ CowFooter footer;
+ ASSERT_TRUE(reader.Parse(cow_->fd));
+ ASSERT_TRUE(reader.GetHeader(&header));
+ ASSERT_TRUE(reader.GetFooter(&footer));
+ ASSERT_EQ(header.magic, kCowMagicNumber);
+ ASSERT_EQ(header.major_version, kCowVersionMajor);
+ ASSERT_EQ(header.minor_version, kCowVersionMinor);
+ ASSERT_EQ(header.block_size, options.block_size);
+ ASSERT_EQ(footer.op.num_ops, 4);
+
+ auto iter = reader.GetOpIter();
+ ASSERT_NE(iter, nullptr);
+ ASSERT_FALSE(iter->Done());
+ auto op = &iter->Get();
+
+ ASSERT_EQ(op->type, kCowCopyOp);
+ ASSERT_EQ(op->compression, kCowCompressNone);
+ ASSERT_EQ(op->data_length, 0);
+ ASSERT_EQ(op->new_block, 10);
+ ASSERT_EQ(op->source, 20);
+
+ StringSink sink;
+
+ iter->Next();
+ ASSERT_FALSE(iter->Done());
+ op = &iter->Get();
+
+ ASSERT_EQ(op->type, kCowXorOp);
+ ASSERT_EQ(op->compression, kCowCompressNone);
+ ASSERT_EQ(op->data_length, 4096);
+ ASSERT_EQ(op->new_block, 50);
+ ASSERT_EQ(op->source, 98314); // 4096 * 24 + 10
+ ASSERT_TRUE(reader.ReadData(*op, &sink));
+ ASSERT_EQ(sink.stream(), data);
+
+ iter->Next();
+ ASSERT_FALSE(iter->Done());
+ op = &iter->Get();
+
+ // Note: the zero operation gets split into two blocks.
+ ASSERT_EQ(op->type, kCowZeroOp);
+ ASSERT_EQ(op->compression, kCowCompressNone);
+ ASSERT_EQ(op->data_length, 0);
+ ASSERT_EQ(op->new_block, 51);
+ ASSERT_EQ(op->source, 0);
+
+ iter->Next();
+ ASSERT_FALSE(iter->Done());
+ op = &iter->Get();
+
+ ASSERT_EQ(op->type, kCowZeroOp);
+ ASSERT_EQ(op->compression, kCowCompressNone);
+ ASSERT_EQ(op->data_length, 0);
+ ASSERT_EQ(op->new_block, 52);
+ ASSERT_EQ(op->source, 0);
+
+ iter->Next();
+ ASSERT_TRUE(iter->Done());
+}
+
TEST_F(CowTest, CompressGz) {
CowOptions options;
options.cluster_ops = 0;
@@ -1013,6 +1092,75 @@
ASSERT_TRUE(iter->Done());
}
+TEST_F(CowTest, MissingSeqOp) {
+ CowOptions options;
+ CowWriter writer(options);
+ const int seq_len = 10;
+ uint32_t sequence[seq_len];
+ for (int i = 0; i < seq_len; i++) {
+ sequence[i] = i + 1;
+ }
+
+ ASSERT_TRUE(writer.Initialize(cow_->fd));
+
+ ASSERT_TRUE(writer.AddSequenceData(seq_len, sequence));
+ ASSERT_TRUE(writer.AddZeroBlocks(1, seq_len - 1));
+ ASSERT_TRUE(writer.Finalize());
+
+ ASSERT_EQ(lseek(cow_->fd, 0, SEEK_SET), 0);
+
+ CowReader reader;
+ ASSERT_FALSE(reader.Parse(cow_->fd));
+}
+
+TEST_F(CowTest, ResumeSeqOp) {
+ CowOptions options;
+ auto writer = std::make_unique<CowWriter>(options);
+ const int seq_len = 10;
+ uint32_t sequence[seq_len];
+ for (int i = 0; i < seq_len; i++) {
+ sequence[i] = i + 1;
+ }
+
+ ASSERT_TRUE(writer->Initialize(cow_->fd));
+
+ ASSERT_TRUE(writer->AddSequenceData(seq_len, sequence));
+ ASSERT_TRUE(writer->AddZeroBlocks(1, seq_len / 2));
+ ASSERT_TRUE(writer->AddLabel(1));
+ ASSERT_TRUE(writer->AddZeroBlocks(1 + seq_len / 2, 1));
+
+ ASSERT_EQ(lseek(cow_->fd, 0, SEEK_SET), 0);
+ auto reader = std::make_unique<CowReader>();
+ ASSERT_TRUE(reader->Parse(cow_->fd, 1));
+ auto itr = reader->GetRevMergeOpIter();
+ ASSERT_TRUE(itr->Done());
+
+ writer = std::make_unique<CowWriter>(options);
+ ASSERT_TRUE(writer->InitializeAppend(cow_->fd, 1));
+ ASSERT_TRUE(writer->AddZeroBlocks(1 + seq_len / 2, seq_len / 2));
+ ASSERT_TRUE(writer->Finalize());
+
+ ASSERT_EQ(lseek(cow_->fd, 0, SEEK_SET), 0);
+
+ reader = std::make_unique<CowReader>();
+ ASSERT_TRUE(reader->Parse(cow_->fd));
+
+ auto iter = reader->GetRevMergeOpIter();
+
+ uint64_t expected_block = 10;
+ while (!iter->Done() && expected_block > 0) {
+ ASSERT_FALSE(iter->Done());
+ const auto& op = iter->Get();
+
+ ASSERT_EQ(op.new_block, expected_block);
+
+ iter->Next();
+ expected_block--;
+ }
+ ASSERT_EQ(expected_block, 0);
+ ASSERT_TRUE(iter->Done());
+}
+
TEST_F(CowTest, RevMergeOpItrTest) {
CowOptions options;
options.cluster_ops = 5;
diff --git a/fs_mgr/libsnapshot/cow_format.cpp b/fs_mgr/libsnapshot/cow_format.cpp
index 3085f80..8e6bec7 100644
--- a/fs_mgr/libsnapshot/cow_format.cpp
+++ b/fs_mgr/libsnapshot/cow_format.cpp
@@ -37,6 +37,8 @@
os << "kCowLabelOp, ";
else if (op.type == kCowClusterOp)
os << "kCowClusterOp ";
+ else if (op.type == kCowXorOp)
+ os << "kCowXorOp ";
else if (op.type == kCowSequenceOp)
os << "kCowSequenceOp ";
else if (op.type == kCowFooterOp)
@@ -61,7 +63,7 @@
int64_t GetNextOpOffset(const CowOperation& op, uint32_t cluster_ops) {
if (op.type == kCowClusterOp) {
return op.source;
- } else if (op.type == kCowReplaceOp && cluster_ops == 0) {
+ } else if ((op.type == kCowReplaceOp || op.type == kCowXorOp) && cluster_ops == 0) {
return op.data_length;
} else {
return 0;
@@ -93,6 +95,7 @@
bool IsOrderedOp(const CowOperation& op) {
switch (op.type) {
case kCowCopyOp:
+ case kCowXorOp:
return true;
default:
return false;
diff --git a/fs_mgr/libsnapshot/cow_reader.cpp b/fs_mgr/libsnapshot/cow_reader.cpp
index b3198a0..773d978 100644
--- a/fs_mgr/libsnapshot/cow_reader.cpp
+++ b/fs_mgr/libsnapshot/cow_reader.cpp
@@ -34,7 +34,11 @@
namespace android {
namespace snapshot {
-CowReader::CowReader() : fd_(-1), header_(), fd_size_(0) {}
+CowReader::CowReader()
+ : fd_(-1),
+ header_(),
+ fd_size_(0),
+ merge_op_blocks_(std::make_shared<std::vector<uint32_t>>()) {}
static void SHA256(const void*, size_t, uint8_t[]) {
#if 0
@@ -45,6 +49,23 @@
#endif
}
+std::unique_ptr<CowReader> CowReader::CloneCowReader() {
+ auto cow = std::make_unique<CowReader>();
+ cow->owned_fd_.reset();
+ cow->header_ = header_;
+ cow->footer_ = footer_;
+ cow->fd_size_ = fd_size_;
+ cow->last_label_ = last_label_;
+ cow->ops_ = ops_;
+ cow->merge_op_blocks_ = merge_op_blocks_;
+ cow->block_map_ = block_map_;
+ cow->num_total_data_ops_ = num_total_data_ops_;
+ cow->num_ordered_ops_to_merge_ = num_ordered_ops_to_merge_;
+ cow->has_seq_ops_ = has_seq_ops_;
+ cow->data_loc_ = data_loc_;
+ return cow;
+}
+
bool CowReader::InitForMerge(android::base::unique_fd&& fd) {
owned_fd_ = std::move(fd);
fd_ = owned_fd_.get();
@@ -133,11 +154,14 @@
if (!ParseOps(label)) {
return false;
}
+ // If we're resuming a write, we're not ready to merge
+ if (label.has_value()) return true;
return PrepMergeOps();
}
bool CowReader::ParseOps(std::optional<uint64_t> label) {
uint64_t pos;
+ auto data_loc = std::make_shared<std::unordered_map<uint64_t, uint64_t>>();
// Skip the scratch space
if (header_.major_version >= 2 && (header_.buffer_size > 0)) {
@@ -157,6 +181,13 @@
// Reading a v1 version of COW which doesn't have buffer_size.
header_.buffer_size = 0;
}
+ uint64_t data_pos = 0;
+
+ if (header_.cluster_ops) {
+ data_pos = pos + header_.cluster_ops * sizeof(CowOperation);
+ } else {
+ data_pos = pos + sizeof(CowOperation);
+ }
auto ops_buffer = std::make_shared<std::vector<CowOperation>>();
uint64_t current_op_num = 0;
@@ -177,7 +208,11 @@
while (current_op_num < ops_buffer->size()) {
auto& current_op = ops_buffer->data()[current_op_num];
current_op_num++;
+ if (current_op.type == kCowXorOp) {
+ data_loc->insert({current_op.new_block, data_pos});
+ }
pos += sizeof(CowOperation) + GetNextOpOffset(current_op, header_.cluster_ops);
+ data_pos += current_op.data_length + GetNextDataOffset(current_op, header_.cluster_ops);
if (current_op.type == kCowClusterOp) {
break;
@@ -268,6 +303,7 @@
ops_ = ops_buffer;
ops_->shrink_to_fit();
+ data_loc_ = data_loc;
return true;
}
@@ -381,10 +417,10 @@
std::set<int, std::greater<int>> other_ops;
auto seq_ops_set = std::unordered_set<uint32_t>();
auto block_map = std::make_shared<std::unordered_map<uint32_t, int>>();
- int num_seqs = 0;
+ size_t num_seqs = 0;
size_t read;
- for (int i = 0; i < ops_->size(); i++) {
+ for (size_t i = 0; i < ops_->size(); i++) {
auto& current_op = ops_->data()[i];
if (current_op.type == kCowSequenceOp) {
@@ -396,7 +432,7 @@
PLOG(ERROR) << "Failed to read sequence op!";
return false;
}
- for (int j = num_seqs; j < num_seqs + seq_len; j++) {
+ for (size_t j = num_seqs; j < num_seqs + seq_len; j++) {
seq_ops_set.insert(merge_op_blocks->data()[j]);
}
num_seqs += seq_len;
@@ -413,10 +449,18 @@
}
block_map->insert({current_op.new_block, i});
}
- if (merge_op_blocks->size() > header_.num_merge_ops)
+ for (auto block : *merge_op_blocks) {
+ if (block_map->count(block) == 0) {
+ LOG(ERROR) << "Invalid Sequence Ops. Could not find Cow Op for new block " << block;
+ return false;
+ }
+ }
+
+ if (merge_op_blocks->size() > header_.num_merge_ops) {
num_ordered_ops_to_merge_ = merge_op_blocks->size() - header_.num_merge_ops;
- else
+ } else {
num_ordered_ops_to_merge_ = 0;
+ }
merge_op_blocks->reserve(merge_op_blocks->size() + other_ops.size());
for (auto block : other_ops) {
merge_op_blocks->emplace_back(block);
@@ -598,7 +642,13 @@
return false;
}
- CowDataStream stream(this, op.source, op.data_length);
+ uint64_t offset;
+ if (op.type == kCowXorOp) {
+ offset = data_loc_->at(op.new_block);
+ } else {
+ offset = op.source;
+ }
+ CowDataStream stream(this, offset, op.data_length);
decompressor->set_stream(&stream);
decompressor->set_sink(sink);
return decompressor->Decompress(header_.block_size);
diff --git a/fs_mgr/libsnapshot/cow_writer.cpp b/fs_mgr/libsnapshot/cow_writer.cpp
index 51505bf..5ce1d3b 100644
--- a/fs_mgr/libsnapshot/cow_writer.cpp
+++ b/fs_mgr/libsnapshot/cow_writer.cpp
@@ -58,10 +58,24 @@
return EmitRawBlocks(new_block_start, data, size);
}
-bool AddXorBlocks(uint32_t /*new_block_start*/, const void* /*data*/, size_t /*size*/,
- uint32_t /*old_block*/, uint16_t /*offset*/) {
- LOG(ERROR) << "AddXorBlocks not yet implemented";
- return false;
+bool ICowWriter::AddXorBlocks(uint32_t new_block_start, const void* data, size_t size,
+ uint32_t old_block, uint16_t offset) {
+ if (size % options_.block_size != 0) {
+ LOG(ERROR) << "AddRawBlocks: size " << size << " is not a multiple of "
+ << options_.block_size;
+ return false;
+ }
+
+ uint64_t num_blocks = size / options_.block_size;
+ uint64_t last_block = new_block_start + num_blocks - 1;
+ if (!ValidateNewBlock(last_block)) {
+ return false;
+ }
+ if (offset >= options_.block_size) {
+ LOG(ERROR) << "AddXorBlocks: offset " << offset << " is not less than "
+ << options_.block_size;
+ }
+ return EmitXorBlocks(new_block_start, data, size, old_block, offset);
}
bool ICowWriter::AddZeroBlocks(uint64_t new_block_start, uint64_t num_blocks) {
@@ -278,13 +292,27 @@
}
bool CowWriter::EmitRawBlocks(uint64_t new_block_start, const void* data, size_t size) {
+ return EmitBlocks(new_block_start, data, size, 0, 0, kCowReplaceOp);
+}
+
+bool CowWriter::EmitXorBlocks(uint32_t new_block_start, const void* data, size_t size,
+ uint32_t old_block, uint16_t offset) {
+ return EmitBlocks(new_block_start, data, size, old_block, offset, kCowXorOp);
+}
+
+bool CowWriter::EmitBlocks(uint64_t new_block_start, const void* data, size_t size,
+ uint64_t old_block, uint16_t offset, uint8_t type) {
const uint8_t* iter = reinterpret_cast<const uint8_t*>(data);
CHECK(!merge_in_progress_);
for (size_t i = 0; i < size / header_.block_size; i++) {
CowOperation op = {};
- op.type = kCowReplaceOp;
op.new_block = new_block_start + i;
- op.source = next_data_pos_;
+ op.type = type;
+ if (type == kCowXorOp) {
+ op.source = (old_block + i) * header_.block_size + offset;
+ } else {
+ op.source = next_data_pos_;
+ }
if (compression_) {
auto data = Compress(iter, header_.block_size);
diff --git a/fs_mgr/libsnapshot/dm_snapshot_internals.h b/fs_mgr/libsnapshot/dm_snapshot_internals.h
index ed77c15..4a36251 100644
--- a/fs_mgr/libsnapshot/dm_snapshot_internals.h
+++ b/fs_mgr/libsnapshot/dm_snapshot_internals.h
@@ -17,8 +17,9 @@
#include <android-base/logging.h>
#include <stdint.h>
+#include <limits>
#include <optional>
-#include <vector>
+#include <unordered_set>
namespace android {
namespace snapshot {
@@ -37,21 +38,16 @@
return;
}
- if (modified_chunks_.size() <= chunk_id) {
- if (modified_chunks_.max_size() <= chunk_id) {
- LOG(ERROR) << "Invalid COW size, chunk_id is too large.";
- valid_ = false;
- return;
- }
- modified_chunks_.resize(chunk_id + 1, false);
- if (modified_chunks_.size() <= chunk_id) {
- LOG(ERROR) << "Invalid COW size, chunk_id is too large.";
- valid_ = false;
- return;
- }
+ if (chunk_id > std::numeric_limits<uint32_t>::max()) {
+ LOG(ERROR) << "Chunk exceeds maximum size: " << chunk_id;
+ valid_ = false;
+ return;
+ }
+ if (modified_chunks_.count(chunk_id) > 0) {
+ return;
}
- modified_chunks_[chunk_id] = true;
+ modified_chunks_.emplace(chunk_id);
}
std::optional<uint64_t> cow_size_bytes() const {
@@ -91,23 +87,16 @@
return std::nullopt;
}
- uint64_t modified_chunks_count = 0;
uint64_t cow_chunks = 0;
- for (const auto& c : modified_chunks_) {
- if (c) {
- ++modified_chunks_count;
- }
- }
-
/* disk header + padding = 1 chunk */
cow_chunks += 1;
/* snapshot modified chunks */
- cow_chunks += modified_chunks_count;
+ cow_chunks += modified_chunks_.size();
/* snapshot chunks index metadata */
- cow_chunks += 1 + modified_chunks_count / exceptions_per_chunk;
+ cow_chunks += 1 + modified_chunks_.size() / exceptions_per_chunk;
return cow_chunks;
}
@@ -150,30 +139,8 @@
/*
* |modified_chunks_| is a container that keeps trace of the modified
* chunks.
- * Multiple options were considered when choosing the most appropriate data
- * structure for this container. Here follows a summary of why vector<bool>
- * has been chosen, taking as a reference a snapshot partition of 4 GiB and
- * chunk size of 4 KiB.
- * - std::set<uint64_t> is very space-efficient for a small number of
- * operations, but if the whole snapshot is changed, it would need to
- * store
- * 4 GiB / 4 KiB * (64 bit / 8) = 8 MiB
- * just for the data, plus the additional data overhead for the red-black
- * tree used for data sorting (if each rb-tree element stores 3 address
- * and the word-aligne color, the total size grows to 32 MiB).
- * - std::bitset<N> is not a good fit because requires a priori knowledge,
- * at compile time, of the bitset size.
- * - std::vector<bool> is a special case of vector, which uses a data
- * compression that allows reducing the space utilization of each element
- * to 1 bit. In detail, this data structure is composed of a resizable
- * array of words, each of them representing a bitmap. On a 64 bit
- * device, modifying the whole 4 GiB snapshot grows this container up to
- * 4 * GiB / 4 KiB / 64 = 64 KiB
- * that, even if is the same space requirement to change a single byte at
- * the highest address of the snapshot, is a very affordable space
- * requirement.
*/
- std::vector<bool> modified_chunks_;
+ std::unordered_set<uint32_t> modified_chunks_;
};
} // namespace snapshot
diff --git a/fs_mgr/libsnapshot/include/libsnapshot/cow_format.h b/fs_mgr/libsnapshot/include/libsnapshot/cow_format.h
index 464046b..c15682a 100644
--- a/fs_mgr/libsnapshot/include/libsnapshot/cow_format.h
+++ b/fs_mgr/libsnapshot/include/libsnapshot/cow_format.h
@@ -138,6 +138,8 @@
// For Label operations, this is the value of the applied label.
//
// For Cluster operations, this is the length of the following data region
+ //
+ // For Xor operations, this is the byte location in the source image.
uint64_t source;
} __attribute__((packed));
@@ -148,6 +150,7 @@
static constexpr uint8_t kCowZeroOp = 3;
static constexpr uint8_t kCowLabelOp = 4;
static constexpr uint8_t kCowClusterOp = 5;
+static constexpr uint8_t kCowXorOp = 6;
static constexpr uint8_t kCowSequenceOp = 7;
static constexpr uint8_t kCowFooterOp = -1;
diff --git a/fs_mgr/libsnapshot/include/libsnapshot/cow_reader.h b/fs_mgr/libsnapshot/include/libsnapshot/cow_reader.h
index 05f7066..0786e82 100644
--- a/fs_mgr/libsnapshot/include/libsnapshot/cow_reader.h
+++ b/fs_mgr/libsnapshot/include/libsnapshot/cow_reader.h
@@ -126,12 +126,19 @@
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
+ // count of the merge sequence before removing already-merged operations.
+ // It may be different than the actual data op count, for example, if there
+ // are duplicate ops in the stream.
uint64_t get_num_total_data_ops() { return num_total_data_ops_; }
uint64_t get_num_ordered_ops_to_merge() { return num_ordered_ops_to_merge_; }
void CloseCowFd() { owned_fd_ = {}; }
+ // Creates a clone of the current CowReader without the file handlers
+ std::unique_ptr<CowReader> CloneCowReader();
+
private:
bool ParseOps(std::optional<uint64_t> label);
bool PrepMergeOps();
@@ -149,6 +156,7 @@
uint64_t num_total_data_ops_;
uint64_t num_ordered_ops_to_merge_;
bool has_seq_ops_;
+ std::shared_ptr<std::unordered_map<uint64_t, uint64_t>> data_loc_;
};
} // namespace snapshot
diff --git a/fs_mgr/libsnapshot/include/libsnapshot/cow_writer.h b/fs_mgr/libsnapshot/include/libsnapshot/cow_writer.h
index 4a807fb..e17b5c6 100644
--- a/fs_mgr/libsnapshot/include/libsnapshot/cow_writer.h
+++ b/fs_mgr/libsnapshot/include/libsnapshot/cow_writer.h
@@ -86,6 +86,8 @@
protected:
virtual bool EmitCopy(uint64_t new_block, uint64_t old_block) = 0;
virtual bool EmitRawBlocks(uint64_t new_block_start, const void* data, size_t size) = 0;
+ virtual bool EmitXorBlocks(uint32_t new_block_start, const void* data, size_t size,
+ uint32_t old_block, uint16_t offset) = 0;
virtual bool EmitZeroBlocks(uint64_t new_block_start, uint64_t num_blocks) = 0;
virtual bool EmitLabel(uint64_t label) = 0;
virtual bool EmitSequenceData(size_t num_ops, const uint32_t* data) = 0;
@@ -122,6 +124,8 @@
protected:
virtual bool EmitCopy(uint64_t new_block, uint64_t old_block) override;
virtual bool EmitRawBlocks(uint64_t new_block_start, const void* data, size_t size) override;
+ virtual bool EmitXorBlocks(uint32_t new_block_start, const void* data, size_t size,
+ uint32_t old_block, uint16_t offset) override;
virtual bool EmitZeroBlocks(uint64_t new_block_start, uint64_t num_blocks) override;
virtual bool EmitLabel(uint64_t label) override;
virtual bool EmitSequenceData(size_t num_ops, const uint32_t* data) override;
@@ -129,6 +133,8 @@
private:
bool EmitCluster();
bool EmitClusterIfNeeded();
+ bool EmitBlocks(uint64_t new_block_start, const void* data, size_t size, uint64_t old_block,
+ uint16_t offset, uint8_t type);
void SetupHeaders();
bool ParseOptions();
bool OpenForWrite();
diff --git a/fs_mgr/libsnapshot/include/libsnapshot/mock_snapshot_writer.h b/fs_mgr/libsnapshot/include/libsnapshot/mock_snapshot_writer.h
new file mode 100644
index 0000000..3655c01
--- /dev/null
+++ b/fs_mgr/libsnapshot/include/libsnapshot/mock_snapshot_writer.h
@@ -0,0 +1,54 @@
+//
+// Copyright (C) 2021 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+#include <gmock/gmock.h>
+#include <libsnapshot/snapshot_writer.h>
+
+namespace android::snapshot {
+
+class MockSnapshotWriter : public ISnapshotWriter {
+ public:
+ using FileDescriptor = ISnapshotWriter::FileDescriptor;
+
+ explicit MockSnapshotWriter(const CowOptions& options) : ISnapshotWriter(options) {}
+ MockSnapshotWriter() : ISnapshotWriter({}) {}
+
+ MOCK_METHOD(bool, Finalize, (), (override));
+
+ // Return number of bytes the cow image occupies on disk.
+ MOCK_METHOD(uint64_t, GetCowSize, (), (override));
+
+ // Returns true if AddCopy() operations are supported.
+ MOCK_METHOD(bool, SupportsCopyOperation, (), (const override));
+
+ MOCK_METHOD(bool, EmitCopy, (uint64_t, uint64_t), (override));
+ MOCK_METHOD(bool, EmitRawBlocks, (uint64_t, const void*, size_t), (override));
+ MOCK_METHOD(bool, EmitXorBlocks, (uint32_t, const void*, size_t, uint32_t, uint16_t),
+ (override));
+ MOCK_METHOD(bool, EmitZeroBlocks, (uint64_t, uint64_t), (override));
+ MOCK_METHOD(bool, EmitLabel, (uint64_t), (override));
+ MOCK_METHOD(bool, EmitSequenceData, (size_t, const uint32_t*), (override));
+
+ // Open the writer in write mode (no append).
+ MOCK_METHOD(bool, Initialize, (), (override));
+
+ // Open the writer in append mode, with the last label to resume
+ // from. See CowWriter::InitializeAppend.
+ MOCK_METHOD(bool, InitializeAppend, (uint64_t label), (override));
+
+ MOCK_METHOD(std::unique_ptr<FileDescriptor>, OpenReader, (), (override));
+};
+} // namespace android::snapshot
diff --git a/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h b/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h
index 15882b3..d9a0cd9 100644
--- a/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h
+++ b/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h
@@ -38,7 +38,7 @@
#include <libsnapshot/auto_device.h>
#include <libsnapshot/return.h>
#include <libsnapshot/snapshot_writer.h>
-#include <libsnapshot/snapuserd_client.h>
+#include <snapuserd/snapuserd_client.h>
#ifndef FRIEND_TEST
#define FRIEND_TEST(test_set_name, individual_test) \
@@ -399,6 +399,7 @@
FRIEND_TEST(SnapshotTest, MergeFailureCode);
FRIEND_TEST(SnapshotTest, NoMergeBeforeReboot);
FRIEND_TEST(SnapshotTest, UpdateBootControlHal);
+ FRIEND_TEST(SnapshotUpdateTest, AddPartition);
FRIEND_TEST(SnapshotUpdateTest, DaemonTransition);
FRIEND_TEST(SnapshotUpdateTest, DataWipeAfterRollback);
FRIEND_TEST(SnapshotUpdateTest, DataWipeRollbackInRecovery);
diff --git a/fs_mgr/libsnapshot/include/libsnapshot/snapshot_writer.h b/fs_mgr/libsnapshot/include/libsnapshot/snapshot_writer.h
index c00dafa..b09e1ae 100644
--- a/fs_mgr/libsnapshot/include/libsnapshot/snapshot_writer.h
+++ b/fs_mgr/libsnapshot/include/libsnapshot/snapshot_writer.h
@@ -74,6 +74,8 @@
protected:
bool EmitCopy(uint64_t new_block, uint64_t old_block) override;
bool EmitRawBlocks(uint64_t new_block_start, const void* data, size_t size) override;
+ bool EmitXorBlocks(uint32_t new_block_start, const void* data, size_t size, uint32_t old_block,
+ uint16_t offset) override;
bool EmitZeroBlocks(uint64_t new_block_start, uint64_t num_blocks) override;
bool EmitLabel(uint64_t label) override;
bool EmitSequenceData(size_t num_ops, const uint32_t* data) override;
@@ -102,6 +104,8 @@
protected:
bool EmitRawBlocks(uint64_t new_block_start, const void* data, size_t size) override;
bool EmitZeroBlocks(uint64_t new_block_start, uint64_t num_blocks) override;
+ bool EmitXorBlocks(uint32_t new_block_start, const void* data, size_t size, uint32_t old_block,
+ uint16_t offset) override;
bool EmitCopy(uint64_t new_block, uint64_t old_block) override;
bool EmitLabel(uint64_t label) override;
bool EmitSequenceData(size_t num_ops, const uint32_t* data) override;
diff --git a/fs_mgr/libsnapshot/scripts/Android.bp b/fs_mgr/libsnapshot/scripts/Android.bp
new file mode 100644
index 0000000..3e09cc3
--- /dev/null
+++ b/fs_mgr/libsnapshot/scripts/Android.bp
@@ -0,0 +1,26 @@
+//
+// 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.
+//
+
+python_binary_host {
+ name: "dump_snapshot_proto",
+ main: "dump_snapshot_proto.py",
+ srcs: [
+ "dump_snapshot_proto.py",
+ ],
+ libs: [
+ "snapshot_proto_python",
+ ],
+}
diff --git a/fs_mgr/libsnapshot/scripts/dump_snapshot_proto.py b/fs_mgr/libsnapshot/scripts/dump_snapshot_proto.py
new file mode 100644
index 0000000..566108d
--- /dev/null
+++ b/fs_mgr/libsnapshot/scripts/dump_snapshot_proto.py
@@ -0,0 +1,39 @@
+# 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.
+
+import argparse
+
+from android.snapshot import snapshot_pb2
+
+def main():
+ parser = argparse.ArgumentParser()
+ parser.add_argument('type', type = str, help = 'Type (snapshot or update)')
+ parser.add_argument('file', type = str, help = 'Input file')
+ args = parser.parse_args()
+
+ with open(args.file, 'rb') as fp:
+ data = fp.read()
+
+ if args.type == 'snapshot':
+ msg = snapshot_pb2.SnapshotStatus()
+ elif args.type == 'update':
+ msg = snapshot_pb2.SnapshotUpdateStatus()
+ else:
+ raise Exception('Unknown proto type')
+
+ msg.ParseFromString(data)
+ print(msg)
+
+if __name__ == '__main__':
+ main()
diff --git a/fs_mgr/libsnapshot/snapshot.cpp b/fs_mgr/libsnapshot/snapshot.cpp
index 0e36da1..52324ba 100644
--- a/fs_mgr/libsnapshot/snapshot.cpp
+++ b/fs_mgr/libsnapshot/snapshot.cpp
@@ -1193,11 +1193,7 @@
return MergeFailureCode::ParseCowConsistencyCheck;
}
- for (auto iter = reader.GetOpIter(); !iter->Done(); iter->Next()) {
- if (!IsMetadataOp(iter->Get())) {
- num_ops++;
- }
- }
+ num_ops = reader.get_num_total_data_ops();
}
// Second pass, try as hard as we can to get the actual number of blocks
@@ -2091,14 +2087,18 @@
if (live_snapshot_status->compression_enabled()) {
// Get the source device (eg the view of the partition from before it was resized).
std::string source_device_path;
- if (!MapSourceDevice(lock, params.GetPartitionName(), remaining_time,
- &source_device_path)) {
- LOG(ERROR) << "Could not map source device for: " << cow_name;
- return false;
- }
+ if (live_snapshot_status->old_partition_size() > 0) {
+ if (!MapSourceDevice(lock, params.GetPartitionName(), remaining_time,
+ &source_device_path)) {
+ LOG(ERROR) << "Could not map source device for: " << cow_name;
+ return false;
+ }
- auto source_device = GetSourceDeviceName(params.GetPartitionName());
- created_devices.EmplaceBack<AutoUnmapDevice>(&dm, source_device);
+ auto source_device = GetSourceDeviceName(params.GetPartitionName());
+ created_devices.EmplaceBack<AutoUnmapDevice>(&dm, source_device);
+ } else {
+ source_device_path = base_path;
+ }
if (!WaitForDevice(source_device_path, remaining_time)) {
return false;
@@ -2534,6 +2534,7 @@
SnapshotUpdateStatus old_status = ReadSnapshotUpdateStatus(lock);
status.set_compression_enabled(old_status.compression_enabled());
status.set_source_build_fingerprint(old_status.source_build_fingerprint());
+ status.set_merge_phase(old_status.merge_phase());
}
return WriteSnapshotUpdateStatus(lock, status);
}
diff --git a/fs_mgr/libsnapshot/snapshot_reader.cpp b/fs_mgr/libsnapshot/snapshot_reader.cpp
index 5ee8e25..6546c2a 100644
--- a/fs_mgr/libsnapshot/snapshot_reader.cpp
+++ b/fs_mgr/libsnapshot/snapshot_reader.cpp
@@ -221,7 +221,7 @@
private:
size_t ignore_start_;
- char discard_[4096];
+ char discard_[BLOCK_SZ];
};
ssize_t CompressedSnapshotReader::ReadBlock(uint64_t chunk, IByteSink* sink, size_t start_offset,
@@ -277,6 +277,29 @@
errno = EIO;
return -1;
}
+ } else if (op->type == kCowXorOp) {
+ borrowed_fd fd = GetSourceFd();
+ if (fd < 0) {
+ // GetSourceFd sets errno.
+ return -1;
+ }
+
+ off64_t offset = op->source + start_offset;
+ char data[BLOCK_SZ];
+ if (!android::base::ReadFullyAtOffset(fd, &data, bytes_to_read, offset)) {
+ PLOG(ERROR) << "read " << *source_device_;
+ // ReadFullyAtOffset sets errno.
+ return -1;
+ }
+ PartialSink partial_sink(buffer, bytes_to_read, start_offset);
+ if (!cow_->ReadData(*op, &partial_sink)) {
+ LOG(ERROR) << "CompressedSnapshotReader failed to read xor op";
+ errno = EIO;
+ return -1;
+ }
+ for (size_t i = 0; i < bytes_to_read; i++) {
+ ((char*)buffer)[i] ^= data[i];
+ }
} else {
LOG(ERROR) << "CompressedSnapshotReader unknown op type: " << uint32_t(op->type);
errno = EINVAL;
diff --git a/fs_mgr/libsnapshot/snapshot_reader_test.cpp b/fs_mgr/libsnapshot/snapshot_reader_test.cpp
index 9373059..078f16e 100644
--- a/fs_mgr/libsnapshot/snapshot_reader_test.cpp
+++ b/fs_mgr/libsnapshot/snapshot_reader_test.cpp
@@ -63,7 +63,9 @@
void WriteCow(ISnapshotWriter* writer) {
std::string new_block = MakeNewBlockString();
+ std::string xor_block = MakeXorBlockString();
+ ASSERT_TRUE(writer->AddXorBlocks(1, xor_block.data(), xor_block.size(), 0, kBlockSize / 2));
ASSERT_TRUE(writer->AddCopy(3, 0));
ASSERT_TRUE(writer->AddRawBlocks(5, new_block.data(), new_block.size()));
ASSERT_TRUE(writer->AddZeroBlocks(7, 2));
@@ -75,7 +77,7 @@
ASSERT_NE(reader, nullptr);
// Test that unchanged blocks are not modified.
- std::unordered_set<size_t> changed_blocks = {3, 5, 7, 8};
+ std::unordered_set<size_t> changed_blocks = {1, 3, 5, 7, 8};
for (size_t i = 0; i < kBlockCount; i++) {
if (changed_blocks.count(i)) {
continue;
@@ -88,6 +90,17 @@
}
// Test that we can read back our modified blocks.
+ std::string data(kBlockSize, 0);
+ std::string offsetblock = base_blocks_[0].substr(kBlockSize / 2, kBlockSize / 2) +
+ base_blocks_[1].substr(0, kBlockSize / 2);
+ ASSERT_EQ(offsetblock.size(), kBlockSize);
+ ASSERT_EQ(reader->Seek(1 * kBlockSize, SEEK_SET), 1 * kBlockSize);
+ ASSERT_EQ(reader->Read(data.data(), data.size()), kBlockSize);
+ for (int i = 0; i < 100; i++) {
+ data[i] = (char)~(data[i]);
+ }
+ ASSERT_EQ(data, offsetblock);
+
std::string block(kBlockSize, 0);
ASSERT_EQ(reader->Seek(3 * kBlockSize, SEEK_SET), 3 * kBlockSize);
ASSERT_EQ(reader->Read(block.data(), block.size()), kBlockSize);
@@ -141,6 +154,12 @@
return new_block;
}
+ std::string MakeXorBlockString() {
+ std::string data(100, -1);
+ data.resize(kBlockSize, 0);
+ return data;
+ }
+
std::unique_ptr<TemporaryFile> base_;
std::unique_ptr<TemporaryFile> cow_;
std::vector<std::string> base_blocks_;
diff --git a/fs_mgr/libsnapshot/snapshot_test.cpp b/fs_mgr/libsnapshot/snapshot_test.cpp
index 6018643..057e5b19a 100644
--- a/fs_mgr/libsnapshot/snapshot_test.cpp
+++ b/fs_mgr/libsnapshot/snapshot_test.cpp
@@ -963,7 +963,7 @@
}
AssertionResult UnmapAll() {
- for (const auto& name : {"sys", "vnd", "prd"}) {
+ for (const auto& name : {"sys", "vnd", "prd", "dlkm"}) {
if (!dm_.DeleteDeviceIfExists(name + "_a"s)) {
return AssertionFailure() << "Cannot unmap " << name << "_a";
}
@@ -1184,6 +1184,53 @@
}
}
+TEST_F(SnapshotUpdateTest, DuplicateOps) {
+ if (!IsCompressionEnabled()) {
+ GTEST_SKIP() << "Compression-only test";
+ }
+
+ // OTA client blindly unmaps all partitions that are possibly mapped.
+ for (const auto& name : {"sys_b", "vnd_b", "prd_b"}) {
+ ASSERT_TRUE(sm->UnmapUpdateSnapshot(name));
+ }
+
+ // Execute the update.
+ ASSERT_TRUE(sm->BeginUpdate());
+ ASSERT_TRUE(sm->CreateUpdateSnapshots(manifest_));
+
+ // Write some data to target partitions.
+ for (const auto& name : {"sys_b", "vnd_b", "prd_b"}) {
+ ASSERT_TRUE(WriteSnapshotAndHash(name));
+ }
+
+ std::vector<PartitionUpdate*> partitions = {sys_, vnd_, prd_};
+ for (auto* partition : partitions) {
+ AddOperation(partition);
+
+ std::unique_ptr<ISnapshotWriter> writer;
+ auto res = MapUpdateSnapshot(partition->partition_name() + "_b", &writer);
+ ASSERT_TRUE(res);
+ ASSERT_TRUE(writer->AddZeroBlocks(0, 1));
+ ASSERT_TRUE(writer->AddZeroBlocks(0, 1));
+ ASSERT_TRUE(writer->Finalize());
+ }
+
+ ASSERT_TRUE(sm->FinishedSnapshotWrites(false));
+
+ // Simulate shutting down the device.
+ ASSERT_TRUE(UnmapAll());
+
+ // After reboot, init does first stage mount.
+ auto init = NewManagerForFirstStageMount("_b");
+ ASSERT_NE(init, nullptr);
+ ASSERT_TRUE(init->NeedSnapshotsInFirstStageMount());
+ ASSERT_TRUE(init->CreateLogicalAndSnapshotPartitions("super", snapshot_timeout_));
+
+ // Initiate the merge and wait for it to be completed.
+ ASSERT_TRUE(init->InitiateMerge());
+ ASSERT_EQ(UpdateState::MergeCompleted, init->ProcessUpdateState());
+}
+
// Test that shrinking and growing partitions at the same time is handled
// correctly in VABC.
TEST_F(SnapshotUpdateTest, SpaceSwapUpdate) {
@@ -2026,6 +2073,70 @@
ASSERT_LT(res.required_size(), 40_MiB);
}
+TEST_F(SnapshotUpdateTest, AddPartition) {
+ // OTA client blindly unmaps all partitions that are possibly mapped.
+ for (const auto& name : {"sys_b", "vnd_b", "prd_b"}) {
+ ASSERT_TRUE(sm->UnmapUpdateSnapshot(name));
+ }
+
+ group_->add_partition_names("dlkm");
+
+ auto dlkm = manifest_.add_partitions();
+ dlkm->set_partition_name("dlkm");
+ dlkm->set_estimate_cow_size(2_MiB);
+ SetSize(dlkm, 3_MiB);
+
+ // Grow all partitions. Set |prd| large enough that |sys| and |vnd|'s COWs
+ // fit in super, but not |prd|.
+ constexpr uint64_t partition_size = 3788_KiB;
+ SetSize(sys_, partition_size);
+ SetSize(vnd_, partition_size);
+ SetSize(prd_, partition_size);
+ SetSize(dlkm, partition_size);
+
+ AddOperationForPartitions({sys_, vnd_, prd_, dlkm});
+
+ // Execute the update.
+ ASSERT_TRUE(sm->BeginUpdate());
+ ASSERT_TRUE(sm->CreateUpdateSnapshots(manifest_));
+
+ // Write some data to target partitions.
+ for (const auto& name : {"sys_b", "vnd_b", "prd_b", "dlkm_b"}) {
+ ASSERT_TRUE(WriteSnapshotAndHash(name));
+ }
+
+ // Assert that source partitions aren't affected.
+ for (const auto& name : {"sys_a", "vnd_a", "prd_a"}) {
+ ASSERT_TRUE(IsPartitionUnchanged(name));
+ }
+
+ ASSERT_TRUE(sm->FinishedSnapshotWrites(false));
+
+ // Simulate shutting down the device.
+ ASSERT_TRUE(UnmapAll());
+
+ // After reboot, init does first stage mount.
+ auto init = NewManagerForFirstStageMount("_b");
+ ASSERT_NE(init, nullptr);
+ ASSERT_TRUE(init->NeedSnapshotsInFirstStageMount());
+ ASSERT_TRUE(init->CreateLogicalAndSnapshotPartitions("super", snapshot_timeout_));
+
+ // Check that the target partitions have the same content.
+ for (const auto& name : {"sys_b", "vnd_b", "prd_b", "dlkm_b"}) {
+ ASSERT_TRUE(IsPartitionUnchanged(name));
+ }
+
+ // Initiate the merge and wait for it to be completed.
+ ASSERT_TRUE(init->InitiateMerge());
+ ASSERT_EQ(UpdateState::MergeCompleted, init->ProcessUpdateState());
+
+ // Check that the target partitions have the same content after the merge.
+ for (const auto& name : {"sys_b", "vnd_b", "prd_b", "dlkm_b"}) {
+ ASSERT_TRUE(IsPartitionUnchanged(name))
+ << "Content of " << name << " changes after the merge";
+ }
+}
+
class AutoKill final {
public:
explicit AutoKill(pid_t pid) : pid_(pid) {}
diff --git a/fs_mgr/libsnapshot/snapshot_writer.cpp b/fs_mgr/libsnapshot/snapshot_writer.cpp
index 34b3e87..3eda08e 100644
--- a/fs_mgr/libsnapshot/snapshot_writer.cpp
+++ b/fs_mgr/libsnapshot/snapshot_writer.cpp
@@ -106,6 +106,11 @@
return cow_->AddRawBlocks(new_block_start, data, size);
}
+bool CompressedSnapshotWriter::EmitXorBlocks(uint32_t new_block_start, const void* data,
+ size_t size, uint32_t old_block, uint16_t offset) {
+ return cow_->AddXorBlocks(new_block_start, data, size, old_block, offset);
+}
+
bool CompressedSnapshotWriter::EmitZeroBlocks(uint64_t new_block_start, uint64_t num_blocks) {
return cow_->AddZeroBlocks(new_block_start, num_blocks);
}
@@ -157,6 +162,11 @@
return true;
}
+bool OnlineKernelSnapshotWriter::EmitXorBlocks(uint32_t, const void*, size_t, uint32_t, uint16_t) {
+ LOG(ERROR) << "EmitXorBlocks not implemented.";
+ return false;
+}
+
bool OnlineKernelSnapshotWriter::EmitZeroBlocks(uint64_t new_block_start, uint64_t num_blocks) {
std::string zeroes(options_.block_size, 0);
for (uint64_t i = 0; i < num_blocks; i++) {
diff --git a/fs_mgr/libsnapshot/snapuserd.rc b/fs_mgr/libsnapshot/snapuserd.rc
deleted file mode 100644
index 4bf34a2..0000000
--- a/fs_mgr/libsnapshot/snapuserd.rc
+++ /dev/null
@@ -1,7 +0,0 @@
-service snapuserd /system/bin/snapuserd
- socket snapuserd stream 0660 system system
- oneshot
- disabled
- user root
- group root system
- seclabel u:r:snapuserd:s0
diff --git a/fs_mgr/libsnapshot/snapuserd/Android.bp b/fs_mgr/libsnapshot/snapuserd/Android.bp
new file mode 100644
index 0000000..47268d4
--- /dev/null
+++ b/fs_mgr/libsnapshot/snapuserd/Android.bp
@@ -0,0 +1,134 @@
+//
+// 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.
+//
+
+package {
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+cc_defaults {
+ name: "libsnapshot_snapuserd_defaults",
+ defaults: [
+ "fs_mgr_defaults",
+ ],
+ cflags: [
+ "-D_FILE_OFFSET_BITS=64",
+ "-Wall",
+ "-Werror",
+ ],
+ export_include_dirs: ["include"],
+ srcs: [
+ "snapuserd_client.cpp",
+ ],
+}
+
+cc_library_static {
+ name: "libsnapshot_snapuserd",
+ defaults: [
+ "libsnapshot_snapuserd_defaults",
+ ],
+ recovery_available: true,
+ static_libs: [
+ "libcutils_sockets",
+ ],
+ shared_libs: [
+ "libbase",
+ "liblog",
+ ],
+ ramdisk_available: true,
+}
+
+cc_defaults {
+ name: "snapuserd_defaults",
+ defaults: [
+ "fs_mgr_defaults",
+ ],
+ srcs: [
+ "snapuserd_server.cpp",
+ "snapuserd.cpp",
+ "snapuserd_daemon.cpp",
+ "snapuserd_worker.cpp",
+ "snapuserd_readahead.cpp",
+ ],
+
+ cflags: [
+ "-Wall",
+ "-Werror"
+ ],
+
+ static_libs: [
+ "libbase",
+ "libbrotli",
+ "libcutils_sockets",
+ "libdm",
+ "libfs_mgr",
+ "libgflags",
+ "liblog",
+ "libsnapshot_cow",
+ "libz",
+ "libext4_utils",
+ ],
+}
+
+cc_binary {
+ name: "snapuserd",
+ defaults: ["snapuserd_defaults"],
+ init_rc: [
+ "snapuserd.rc",
+ ],
+ static_executable: true,
+ system_shared_libs: [],
+ ramdisk_available: true,
+ vendor_ramdisk_available: true,
+ recovery_available: true,
+}
+
+cc_test {
+ name: "cow_snapuserd_test",
+ defaults: [
+ "fs_mgr_defaults",
+ ],
+ srcs: [
+ "cow_snapuserd_test.cpp",
+ "snapuserd.cpp",
+ "snapuserd_worker.cpp",
+ ],
+ cflags: [
+ "-Wall",
+ "-Werror",
+ ],
+ shared_libs: [
+ "libbase",
+ "liblog",
+ ],
+ static_libs: [
+ "libbrotli",
+ "libgtest",
+ "libsnapshot_cow",
+ "libsnapshot_snapuserd",
+ "libcutils_sockets",
+ "libz",
+ "libfs_mgr",
+ "libdm",
+ "libext4_utils",
+ ],
+ header_libs: [
+ "libstorage_literals_headers",
+ "libfiemap_headers",
+ ],
+ test_min_api_level: 30,
+ auto_gen_config: true,
+ require_root: false,
+}
diff --git a/fs_mgr/libsnapshot/snapuserd/OWNERS b/fs_mgr/libsnapshot/snapuserd/OWNERS
new file mode 100644
index 0000000..2df0a2d
--- /dev/null
+++ b/fs_mgr/libsnapshot/snapuserd/OWNERS
@@ -0,0 +1,3 @@
+akailash@google.com
+dvander@google.com
+drosen@google.com
diff --git a/fs_mgr/libsnapshot/cow_snapuserd_test.cpp b/fs_mgr/libsnapshot/snapuserd/cow_snapuserd_test.cpp
similarity index 76%
rename from fs_mgr/libsnapshot/cow_snapuserd_test.cpp
rename to fs_mgr/libsnapshot/snapuserd/cow_snapuserd_test.cpp
index 767cd04..f4aef44 100644
--- a/fs_mgr/libsnapshot/cow_snapuserd_test.cpp
+++ b/fs_mgr/libsnapshot/snapuserd/cow_snapuserd_test.cpp
@@ -33,7 +33,7 @@
#include <libdm/dm.h>
#include <libdm/loop_control.h>
#include <libsnapshot/cow_writer.h>
-#include <libsnapshot/snapuserd_client.h>
+#include <snapuserd/snapuserd_client.h>
#include <storage_literals/storage_literals.h>
#include "snapuserd.h"
@@ -96,6 +96,8 @@
class CowSnapuserdTest final {
public:
bool Setup();
+ bool SetupOrderedOps();
+ bool SetupOrderedOpsInverted();
bool SetupCopyOverlap_1();
bool SetupCopyOverlap_2();
bool Merge();
@@ -103,6 +105,8 @@
void ReadSnapshotDeviceAndValidate();
void Shutdown();
void MergeInterrupt();
+ void MergeInterruptFixed(int duration);
+ void MergeInterruptRandomly(int max_duration);
void ReadDmUserBlockWithoutDaemon();
std::string snapshot_dev() const { return snapshot_dev_->path(); }
@@ -117,6 +121,8 @@
void StartMerge();
void CreateCowDevice();
+ void CreateCowDeviceOrderedOps();
+ void CreateCowDeviceOrderedOpsInverted();
void CreateCowDeviceWithCopyOverlap_1();
void CreateCowDeviceWithCopyOverlap_2();
bool SetupDaemon();
@@ -197,6 +203,18 @@
return setup_ok_;
}
+bool CowSnapuserdTest::SetupOrderedOps() {
+ CreateBaseDevice();
+ CreateCowDeviceOrderedOps();
+ return SetupDaemon();
+}
+
+bool CowSnapuserdTest::SetupOrderedOpsInverted() {
+ CreateBaseDevice();
+ CreateCowDeviceOrderedOpsInverted();
+ return SetupDaemon();
+}
+
bool CowSnapuserdTest::SetupCopyOverlap_1() {
CreateBaseDevice();
CreateCowDeviceWithCopyOverlap_1();
@@ -241,7 +259,7 @@
void CowSnapuserdTest::CreateBaseDevice() {
unique_fd rnd_fd;
- total_base_size_ = (size_ * 4);
+ total_base_size_ = (size_ * 5);
base_fd_ = CreateTempFile("base_device", total_base_size_);
ASSERT_GE(base_fd_, 0);
@@ -286,6 +304,11 @@
offset += size_;
ASSERT_EQ(ReadFullyAtOffset(snapshot_fd, snapuserd_buffer.get(), size_, offset), true);
ASSERT_EQ(memcmp(snapuserd_buffer.get(), (char*)orig_buffer_.get() + (size_ * 3), size_), 0);
+
+ // XOR
+ offset += size_;
+ ASSERT_EQ(ReadFullyAtOffset(snapshot_fd, snapuserd_buffer.get(), size_, offset), true);
+ ASSERT_EQ(memcmp(snapuserd_buffer.get(), (char*)orig_buffer_.get() + (size_ * 4), size_), 0);
}
void CowSnapuserdTest::CreateCowDeviceWithCopyOverlap_2() {
@@ -383,6 +406,130 @@
true);
}
+void CowSnapuserdTest::CreateCowDeviceOrderedOpsInverted() {
+ unique_fd rnd_fd;
+ loff_t offset = 0;
+
+ std::string path = android::base::GetExecutableDirectory();
+ cow_system_ = std::make_unique<TemporaryFile>(path);
+
+ 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;
+ }
+
+ CowOptions options;
+ options.compression = "gz";
+ CowWriter writer(options);
+
+ ASSERT_TRUE(writer.Initialize(cow_system_->fd));
+
+ size_t num_blocks = size_ / options.block_size;
+ size_t blk_end_copy = num_blocks * 3;
+ size_t source_blk = num_blocks - 1;
+ size_t blk_src_copy = blk_end_copy - 1;
+ uint16_t xor_offset = 5;
+
+ 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;
+ }
+
+ for (size_t i = num_blocks; i > 0; i--) {
+ ASSERT_TRUE(writer.AddXorBlocks(num_blocks + i - 1,
+ &random_buffer_1_.get()[options.block_size * (i - 1)],
+ options.block_size, 2 * num_blocks + i - 1, xor_offset));
+ }
+ // Flush operations
+ ASSERT_TRUE(writer.Finalize());
+ // Construct the buffer required for validation
+ orig_buffer_ = std::make_unique<uint8_t[]>(total_base_size_);
+ // Read the entire base device
+ ASSERT_EQ(android::base::ReadFullyAtOffset(base_fd_, orig_buffer_.get(), total_base_size_, 0),
+ true);
+ // Merged Buffer
+ memmove(orig_buffer_.get(), (char*)orig_buffer_.get() + 2 * size_, size_);
+ memmove(orig_buffer_.get() + size_, (char*)orig_buffer_.get() + 2 * size_ + xor_offset, size_);
+ for (int i = 0; i < size_; i++) {
+ orig_buffer_.get()[size_ + i] ^= random_buffer_1_.get()[i];
+ }
+}
+
+void CowSnapuserdTest::CreateCowDeviceOrderedOps() {
+ unique_fd rnd_fd;
+ loff_t offset = 0;
+
+ std::string path = android::base::GetExecutableDirectory();
+ cow_system_ = std::make_unique<TemporaryFile>(path);
+
+ 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;
+ }
+ memset(random_buffer_1_.get(), 0, size_);
+
+ CowOptions options;
+ options.compression = "gz";
+ CowWriter writer(options);
+
+ ASSERT_TRUE(writer.Initialize(cow_system_->fd));
+
+ size_t num_blocks = size_ / options.block_size;
+ size_t x = num_blocks;
+ size_t source_blk = 0;
+ size_t blk_src_copy = 2 * num_blocks;
+ uint16_t xor_offset = 5;
+
+ while (1) {
+ ASSERT_TRUE(writer.AddCopy(source_blk, blk_src_copy));
+
+ x -= 1;
+ if (x == 0) {
+ break;
+ }
+ source_blk += 1;
+ blk_src_copy += 1;
+ }
+
+ ASSERT_TRUE(writer.AddXorBlocks(num_blocks, random_buffer_1_.get(), size_, 2 * 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_);
+ // Read the entire base device
+ ASSERT_EQ(android::base::ReadFullyAtOffset(base_fd_, orig_buffer_.get(), total_base_size_, 0),
+ true);
+ // Merged Buffer
+ memmove(orig_buffer_.get(), (char*)orig_buffer_.get() + 2 * size_, size_);
+ memmove(orig_buffer_.get() + size_, (char*)orig_buffer_.get() + 2 * size_ + xor_offset, size_);
+ for (int i = 0; i < size_; i++) {
+ orig_buffer_.get()[size_ + i] ^= random_buffer_1_.get()[i];
+ }
+}
+
void CowSnapuserdTest::CreateCowDevice() {
unique_fd rnd_fd;
loff_t offset = 0;
@@ -414,6 +561,17 @@
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));
@@ -439,6 +597,11 @@
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
@@ -448,6 +611,13 @@
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 CowSnapuserdTest::InitCowDevice() {
@@ -597,6 +767,7 @@
void CowSnapuserdTest::SimulateDaemonRestart() {
Shutdown();
+ std::this_thread::sleep_for(500ms);
SetDeviceControlName();
StartSnapuserdDaemon();
InitCowDevice();
@@ -605,6 +776,34 @@
CreateSnapshotDevice();
}
+void CowSnapuserdTest::MergeInterruptRandomly(int max_duration) {
+ std::srand(std::time(nullptr));
+ 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();
+ }
+
+ SimulateDaemonRestart();
+ ASSERT_TRUE(Merge());
+}
+
+void CowSnapuserdTest::MergeInterruptFixed(int duration) {
+ StartMerge();
+
+ for (int i = 0; i < 25; i++) {
+ std::this_thread::sleep_for(std::chrono::milliseconds(duration));
+ SimulateDaemonRestart();
+ StartMerge();
+ }
+
+ SimulateDaemonRestart();
+ ASSERT_TRUE(Merge());
+}
+
void CowSnapuserdTest::MergeInterrupt() {
// Interrupt merge at various intervals
StartMerge();
@@ -669,10 +868,9 @@
void* buffer = snapuserd_->GetExceptionBuffer(1);
loff_t offset = 0;
struct disk_exception* de;
- for (int i = 0; i < 12; i++) {
+ for (int i = 11; i >= 0; i--) {
de = reinterpret_cast<struct disk_exception*>((char*)buffer + offset);
ASSERT_EQ(de->old_chunk, i);
- ASSERT_EQ(de->new_chunk, new_chunk);
offset += sizeof(struct disk_exception);
new_chunk += 1;
}
@@ -811,71 +1009,71 @@
de = reinterpret_cast<struct disk_exception*>((char*)buffer + offset);
ASSERT_EQ(de->old_chunk, 100);
- ASSERT_EQ(de->new_chunk, 522);
+ ASSERT_EQ(de->new_chunk, 521);
offset += sizeof(struct disk_exception);
de = reinterpret_cast<struct disk_exception*>((char*)buffer + offset);
ASSERT_EQ(de->old_chunk, 105);
- ASSERT_EQ(de->new_chunk, 524);
+ ASSERT_EQ(de->new_chunk, 522);
offset += sizeof(struct disk_exception);
de = reinterpret_cast<struct disk_exception*>((char*)buffer + offset);
ASSERT_EQ(de->old_chunk, 110);
- ASSERT_EQ(de->new_chunk, 526);
+ ASSERT_EQ(de->new_chunk, 523);
offset += sizeof(struct disk_exception);
// The next 4 operations are batch merged as
// both old and new chunk are contiguous
de = reinterpret_cast<struct disk_exception*>((char*)buffer + offset);
- ASSERT_EQ(de->old_chunk, 50);
- ASSERT_EQ(de->new_chunk, 528);
- offset += sizeof(struct disk_exception);
-
- de = reinterpret_cast<struct disk_exception*>((char*)buffer + offset);
- ASSERT_EQ(de->old_chunk, 51);
- ASSERT_EQ(de->new_chunk, 529);
+ ASSERT_EQ(de->old_chunk, 53);
+ ASSERT_EQ(de->new_chunk, 524);
offset += sizeof(struct disk_exception);
de = reinterpret_cast<struct disk_exception*>((char*)buffer + offset);
ASSERT_EQ(de->old_chunk, 52);
- ASSERT_EQ(de->new_chunk, 530);
+ ASSERT_EQ(de->new_chunk, 525);
offset += sizeof(struct disk_exception);
de = reinterpret_cast<struct disk_exception*>((char*)buffer + offset);
- ASSERT_EQ(de->old_chunk, 53);
- ASSERT_EQ(de->new_chunk, 531);
+ ASSERT_EQ(de->old_chunk, 51);
+ ASSERT_EQ(de->new_chunk, 526);
+ offset += sizeof(struct disk_exception);
+
+ de = reinterpret_cast<struct disk_exception*>((char*)buffer + offset);
+ ASSERT_EQ(de->old_chunk, 50);
+ ASSERT_EQ(de->new_chunk, 527);
offset += sizeof(struct disk_exception);
// This is handling overlap operation with
// two batch merge operations.
de = reinterpret_cast<struct disk_exception*>((char*)buffer + offset);
ASSERT_EQ(de->old_chunk, 18);
- ASSERT_EQ(de->new_chunk, 533);
+ ASSERT_EQ(de->new_chunk, 528);
offset += sizeof(struct disk_exception);
de = reinterpret_cast<struct disk_exception*>((char*)buffer + offset);
ASSERT_EQ(de->old_chunk, 19);
- ASSERT_EQ(de->new_chunk, 534);
+ ASSERT_EQ(de->new_chunk, 529);
offset += sizeof(struct disk_exception);
de = reinterpret_cast<struct disk_exception*>((char*)buffer + offset);
ASSERT_EQ(de->old_chunk, 20);
- ASSERT_EQ(de->new_chunk, 535);
+ ASSERT_EQ(de->new_chunk, 530);
offset += sizeof(struct disk_exception);
de = reinterpret_cast<struct disk_exception*>((char*)buffer + offset);
ASSERT_EQ(de->old_chunk, 21);
- ASSERT_EQ(de->new_chunk, 537);
+ ASSERT_EQ(de->new_chunk, 532);
offset += sizeof(struct disk_exception);
de = reinterpret_cast<struct disk_exception*>((char*)buffer + offset);
ASSERT_EQ(de->old_chunk, 22);
- ASSERT_EQ(de->new_chunk, 538);
+ ASSERT_EQ(de->new_chunk, 533);
offset += sizeof(struct disk_exception);
de = reinterpret_cast<struct disk_exception*>((char*)buffer + offset);
ASSERT_EQ(de->old_chunk, 23);
- ASSERT_EQ(de->new_chunk, 539);
+ ASSERT_EQ(de->new_chunk, 534);
offset += sizeof(struct disk_exception);
// End of metadata
@@ -887,6 +1085,35 @@
}
}
+TEST(Snapuserd_Test, xor_buffer) {
+ std::string data = "Test String";
+ std::string jumbled = {0x0C, 0x2A, 0x21, 0x54, 0x73, 0x27, 0x06, 0x1B, 0x07, 0x09, 0x46};
+ std::string result = "XOR String!";
+
+ BufferSink sink;
+ XorSink xor_sink;
+ sink.Initialize(sizeof(struct dm_user_header) + 10);
+ int buffsize = 5;
+ xor_sink.Initialize(&sink, buffsize);
+
+ void* buff = sink.GetPayloadBuffer(data.length());
+ memcpy(buff, data.data(), data.length());
+
+ size_t actual;
+ size_t count = 0;
+ while (count < data.length()) {
+ void* xor_buff = xor_sink.GetBuffer(10, &actual);
+ ASSERT_EQ(actual, buffsize);
+ ASSERT_NE(xor_buff, nullptr);
+ memcpy(xor_buff, jumbled.data() + count, buffsize);
+ xor_sink.ReturnData(xor_buff, actual);
+ count += actual;
+ }
+
+ std::string answer = reinterpret_cast<char*>(sink.GetPayloadBufPtr());
+ ASSERT_EQ(answer, result);
+}
+
TEST(Snapuserd_Test, Snapshot_Metadata) {
CowSnapuserdMetadataTest harness;
harness.Setup();
@@ -945,6 +1172,38 @@
harness.ReadDmUserBlockWithoutDaemon();
}
+TEST(Snapuserd_Test, Snapshot_Merge_Crash_Fixed_Ordered) {
+ CowSnapuserdTest harness;
+ ASSERT_TRUE(harness.SetupOrderedOps());
+ harness.MergeInterruptFixed(300);
+ harness.ValidateMerge();
+ harness.Shutdown();
+}
+
+TEST(Snapuserd_Test, Snapshot_Merge_Crash_Random_Ordered) {
+ CowSnapuserdTest harness;
+ ASSERT_TRUE(harness.SetupOrderedOps());
+ harness.MergeInterruptRandomly(500);
+ harness.ValidateMerge();
+ harness.Shutdown();
+}
+
+TEST(Snapuserd_Test, Snapshot_Merge_Crash_Fixed_Inverted) {
+ CowSnapuserdTest harness;
+ ASSERT_TRUE(harness.SetupOrderedOpsInverted());
+ harness.MergeInterruptFixed(50);
+ harness.ValidateMerge();
+ harness.Shutdown();
+}
+
+TEST(Snapuserd_Test, Snapshot_Merge_Crash_Random_Inverted) {
+ CowSnapuserdTest harness;
+ ASSERT_TRUE(harness.SetupOrderedOpsInverted());
+ harness.MergeInterruptRandomly(50);
+ harness.ValidateMerge();
+ harness.Shutdown();
+}
+
} // namespace snapshot
} // namespace android
diff --git a/fs_mgr/libsnapshot/include/libsnapshot/snapuserd_client.h b/fs_mgr/libsnapshot/snapuserd/include/snapuserd/snapuserd_client.h
similarity index 92%
rename from fs_mgr/libsnapshot/include/libsnapshot/snapuserd_client.h
rename to fs_mgr/libsnapshot/snapuserd/include/snapuserd/snapuserd_client.h
index 280e857..aeecf41 100644
--- a/fs_mgr/libsnapshot/include/libsnapshot/snapuserd_client.h
+++ b/fs_mgr/libsnapshot/snapuserd/include/snapuserd/snapuserd_client.h
@@ -31,6 +31,7 @@
static constexpr uint32_t PACKET_SIZE = 512;
static constexpr char kSnapuserdSocket[] = "snapuserd";
+static constexpr char kSnapuserdSocketProxy[] = "snapuserd_proxy";
// Ensure that the second-stage daemon for snapuserd is running.
bool EnsureSnapuserdStarted();
@@ -75,6 +76,9 @@
// snapuserd to gracefully exit once all handler threads have terminated.
// This should only be used on first-stage instances of snapuserd.
bool DetachSnapuserd();
+
+ // Returns true if the snapuserd instance supports bridging a socket to second-stage init.
+ bool SupportsSecondStageSocketHandoff();
};
} // namespace snapshot
diff --git a/fs_mgr/libsnapshot/include/libsnapshot/snapuserd_kernel.h b/fs_mgr/libsnapshot/snapuserd/include/snapuserd/snapuserd_kernel.h
similarity index 97%
rename from fs_mgr/libsnapshot/include/libsnapshot/snapuserd_kernel.h
rename to fs_mgr/libsnapshot/snapuserd/include/snapuserd/snapuserd_kernel.h
index 6bb7a39..c592257 100644
--- a/fs_mgr/libsnapshot/include/libsnapshot/snapuserd_kernel.h
+++ b/fs_mgr/libsnapshot/snapuserd/include/snapuserd/snapuserd_kernel.h
@@ -47,8 +47,6 @@
static constexpr uint32_t CHUNK_SIZE = 8;
static constexpr uint32_t CHUNK_SHIFT = (__builtin_ffs(CHUNK_SIZE) - 1);
-#define DIV_ROUND_UP(n, d) (((n) + (d)-1) / (d))
-
// This structure represents the kernel COW header.
// All the below fields should be in Little Endian format.
struct disk_header {
diff --git a/fs_mgr/libsnapshot/snapuserd.cpp b/fs_mgr/libsnapshot/snapuserd/snapuserd.cpp
similarity index 78%
rename from fs_mgr/libsnapshot/snapuserd.cpp
rename to fs_mgr/libsnapshot/snapuserd/snapuserd.cpp
index e578a76..5f4d706 100644
--- a/fs_mgr/libsnapshot/snapuserd.cpp
+++ b/fs_mgr/libsnapshot/snapuserd/snapuserd.cpp
@@ -16,11 +16,23 @@
#include "snapuserd.h"
+#include <dirent.h>
+#include <fcntl.h>
+#include <linux/fs.h>
+#include <unistd.h>
+#include <algorithm>
+
#include <csignal>
#include <optional>
#include <set>
-#include <libsnapshot/snapuserd_client.h>
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <android-base/parseint.h>
+#include <android-base/properties.h>
+#include <android-base/strings.h>
+#include <android-base/unique_fd.h>
+#include <snapuserd/snapuserd_client.h>
namespace android {
namespace snapshot {
@@ -53,6 +65,10 @@
return true;
}
+std::unique_ptr<CowReader> Snapuserd::CloneReaderForWorker() {
+ return reader_->CloneCowReader();
+}
+
bool Snapuserd::CommitMerge(int num_merge_ops) {
struct CowHeader* ch = reinterpret_cast<struct CowHeader*>(mapped_addr_);
ch->num_merge_ops += num_merge_ops;
@@ -64,7 +80,7 @@
int ret = msync(mapped_addr_, BLOCK_SZ, MS_SYNC);
if (ret < 0) {
- PLOG(ERROR) << "msync header failed: " << ret;
+ SNAP_PLOG(ERROR) << "msync header failed: " << ret;
return false;
}
@@ -334,7 +350,7 @@
CowHeader header;
CowOptions options;
bool metadata_found = false;
- int replace_ops = 0, zero_ops = 0, copy_ops = 0;
+ int replace_ops = 0, zero_ops = 0, copy_ops = 0, xor_ops = 0;
SNAP_LOG(DEBUG) << "ReadMetadata: Parsing cow file";
@@ -405,7 +421,6 @@
de->old_chunk = cow_op->new_block;
de->new_chunk = data_chunk_id;
-
// Store operation pointer.
chunk_vec_.push_back(std::make_pair(ChunkToSector(data_chunk_id), cow_op));
num_ops += 1;
@@ -437,14 +452,15 @@
int num_ra_ops_per_iter = ((GetBufferDataSize()) / BLOCK_SZ);
std::optional<chunk_t> prev_id = {};
- std::map<uint64_t, const CowOperation*> map;
+ std::vector<const CowOperation*> vec;
std::set<uint64_t> dest_blocks;
- size_t pending_copy_ops = exceptions_per_area_ - num_ops;
- uint64_t total_copy_ops = reader_->get_num_ordered_ops_to_merge();
+ std::set<uint64_t> source_blocks;
+ size_t pending_ordered_ops = exceptions_per_area_ - num_ops;
+ uint64_t total_ordered_ops = reader_->get_num_ordered_ops_to_merge();
SNAP_LOG(DEBUG) << " Processing copy-ops at Area: " << vec_.size()
<< " Number of replace/zero ops completed in this area: " << num_ops
- << " Pending copy ops for this area: " << pending_copy_ops;
+ << " Pending copy ops for this area: " << pending_ordered_ops;
while (!cowop_rm_iter->Done()) {
do {
@@ -492,103 +508,64 @@
// scratch space and re-construct it thereby there
// is no loss of data.
//
+ // Note that we will follow the same order of COW operations
+ // as present in the COW file. This will make sure that
+ // the merge of operations are done based on the ops present
+ // in the file.
//===========================================================
- //
- // Case 2:
- //
- // Let's say we have three copy operations written to COW file
- // in the following order:
- //
- // op-1: 15 -> 18
- // op-2: 16 -> 19
- // op-3: 17 -> 20
- //
- // As aforementioned, kernel will initiate merge in reverse order.
- // Hence, we will read these ops in reverse order so that all these
- // ops are exectued in the same order as requested. Thus, we will
- // read the metadata in reverse order and for the kernel it will
- // look like:
- //
- // op-3: 17 -> 20
- // op-2: 16 -> 19
- // op-1: 15 -> 18 <-- Merge starts here in the kernel
- //
- // Now, this is problematic as kernel cannot batch merge them.
- //
- // Merge sequence will look like:
- //
- // Merge-1: op-1: 15 -> 18
- // Merge-2: op-2: 16 -> 19
- // Merge-3: op-3: 17 -> 20
- //
- // We have three merge operations.
- //
- // Even though the blocks are contiguous, kernel can batch merge
- // them if the blocks are in descending order. Update engine
- // addresses this issue partially for overlapping operations as
- // we see that op-1 to op-3 and op-4 to op-6 operatiosn are in
- // descending order. However, if the copy operations are not
- // overlapping, update engine cannot write these blocks
- // in descending order. Hence, we will try to address it.
- // Thus, we will send these blocks to the kernel and it will
- // look like:
- //
- // op-3: 15 -> 18
- // op-2: 16 -> 19
- // op-1: 17 -> 20 <-- Merge starts here in the kernel
- //
- // Now with this change, we can batch merge all these three
- // operations. Merge sequence will look like:
- //
- // Merge-1: {op-1: 17 -> 20, op-2: 16 -> 19, op-3: 15 -> 18}
- //
- // Note that we have changed the ordering of merge; However, this
- // is ok as each of these copy operations are independent and there
- // is no overlap.
- //
- //===================================================================
+ uint64_t block_source = cow_op->source;
+ uint64_t block_offset = 0;
+ if (cow_op->type == kCowXorOp) {
+ block_source /= BLOCK_SZ;
+ block_offset = cow_op->source % BLOCK_SZ;
+ }
if (prev_id.has_value()) {
- chunk_t diff = (cow_op->new_block > prev_id.value())
- ? (cow_op->new_block - prev_id.value())
- : (prev_id.value() - cow_op->new_block);
- if (diff != 1) {
- break;
- }
-
- if (dest_blocks.count(cow_op->new_block) || map.count(cow_op->source) > 0) {
+ if (dest_blocks.count(cow_op->new_block) || source_blocks.count(block_source) ||
+ (block_offset > 0 && source_blocks.count(block_source + 1))) {
break;
}
}
metadata_found = true;
- pending_copy_ops -= 1;
- map[cow_op->new_block] = cow_op;
- dest_blocks.insert(cow_op->source);
+ pending_ordered_ops -= 1;
+ vec.push_back(cow_op);
+ dest_blocks.insert(block_source);
+ if (block_offset > 0) {
+ dest_blocks.insert(block_source + 1);
+ }
+ source_blocks.insert(cow_op->new_block);
prev_id = cow_op->new_block;
cowop_rm_iter->Next();
- } while (!cowop_rm_iter->Done() && pending_copy_ops);
+ } while (!cowop_rm_iter->Done() && pending_ordered_ops);
data_chunk_id = GetNextAllocatableChunkId(data_chunk_id);
- SNAP_LOG(DEBUG) << "Batch Merge copy-ops of size: " << map.size()
+ SNAP_LOG(DEBUG) << "Batch Merge copy-ops/xor-ops of size: " << vec.size()
<< " Area: " << vec_.size() << " Area offset: " << offset
- << " Pending-copy-ops in this area: " << pending_copy_ops;
+ << " Pending-ordered-ops in this area: " << pending_ordered_ops;
- for (auto it = map.begin(); it != map.end(); it++) {
+ for (size_t i = 0; i < vec.size(); i++) {
struct disk_exception* de =
reinterpret_cast<struct disk_exception*>((char*)de_ptr.get() + offset);
- de->old_chunk = it->first;
+ const CowOperation* cow_op = vec[i];
+
+ de->old_chunk = cow_op->new_block;
de->new_chunk = data_chunk_id;
// Store operation pointer.
- chunk_vec_.push_back(std::make_pair(ChunkToSector(data_chunk_id), it->second));
+ chunk_vec_.push_back(std::make_pair(ChunkToSector(data_chunk_id), cow_op));
offset += sizeof(struct disk_exception);
num_ops += 1;
- copy_ops++;
+ if (cow_op->type == kCowCopyOp) {
+ copy_ops++;
+ } else { // it->second->type == kCowXorOp
+ xor_ops++;
+ }
+
if (read_ahead_feature_) {
- read_ahead_ops_.push_back(it->second);
+ read_ahead_ops_.push_back(cow_op);
}
SNAP_LOG(DEBUG) << num_ops << ":"
- << " Copy-op: "
+ << " Ordered-op: "
<< " Old-chunk: " << de->old_chunk << " New-chunk: " << de->new_chunk;
if (num_ops == exceptions_per_area_) {
@@ -608,27 +585,28 @@
SNAP_LOG(DEBUG) << "ReadMetadata() completed; Number of Areas: " << vec_.size();
}
- if (!(pending_copy_ops == 0)) {
- SNAP_LOG(ERROR)
- << "Invalid pending_copy_ops: expected: 0 found: " << pending_copy_ops;
+ if (!(pending_ordered_ops == 0)) {
+ SNAP_LOG(ERROR) << "Invalid pending_ordered_ops: expected: 0 found: "
+ << pending_ordered_ops;
return false;
}
- pending_copy_ops = exceptions_per_area_;
+ pending_ordered_ops = exceptions_per_area_;
}
data_chunk_id = GetNextAllocatableChunkId(data_chunk_id);
- total_copy_ops -= 1;
+ total_ordered_ops -= 1;
/*
* Split the number of ops based on the size of read-ahead buffer
* region. We need to ensure that kernel doesn't issue IO on blocks
* which are not read by the read-ahead thread.
*/
- if (read_ahead_feature_ && (total_copy_ops % num_ra_ops_per_iter == 0)) {
+ if (read_ahead_feature_ && (total_ordered_ops % num_ra_ops_per_iter == 0)) {
data_chunk_id = GetNextAllocatableChunkId(data_chunk_id);
}
}
- map.clear();
+ vec.clear();
dest_blocks.clear();
+ source_blocks.clear();
prev_id.reset();
}
@@ -651,8 +629,8 @@
SNAP_LOG(INFO) << "ReadMetadata completed. Final-chunk-id: " << data_chunk_id
<< " Num Sector: " << ChunkToSector(data_chunk_id)
<< " Replace-ops: " << replace_ops << " Zero-ops: " << zero_ops
- << " Copy-ops: " << copy_ops << " Areas: " << vec_.size()
- << " Num-ops-merged: " << header.num_merge_ops
+ << " Copy-ops: " << copy_ops << " Xor-ops: " << xor_ops
+ << " Areas: " << vec_.size() << " Num-ops-merged: " << header.num_merge_ops
<< " Total-data-ops: " << reader_->get_num_total_data_ops();
// Total number of sectors required for creating dm-user device
@@ -712,6 +690,74 @@
return ReadMetadata();
}
+void Snapuserd::ReadBlocksToCache(const std::string& dm_block_device,
+ const std::string& partition_name, off_t offset, size_t size) {
+ android::base::unique_fd fd(TEMP_FAILURE_RETRY(open(dm_block_device.c_str(), O_RDONLY)));
+ if (fd.get() == -1) {
+ SNAP_PLOG(ERROR) << "Error reading " << dm_block_device
+ << " partition-name: " << partition_name;
+ return;
+ }
+
+ size_t remain = size;
+ off_t file_offset = offset;
+ // We pick 4M I/O size based on the fact that the current
+ // update_verifier has a similar I/O size.
+ size_t read_sz = 1024 * BLOCK_SZ;
+ std::vector<uint8_t> buf(read_sz);
+
+ while (remain > 0) {
+ size_t to_read = std::min(remain, read_sz);
+
+ if (!android::base::ReadFullyAtOffset(fd.get(), buf.data(), to_read, file_offset)) {
+ SNAP_PLOG(ERROR) << "Failed to read block from block device: " << dm_block_device
+ << " at offset: " << file_offset
+ << " partition-name: " << partition_name << " total-size: " << size
+ << " remain_size: " << remain;
+ return;
+ }
+
+ file_offset += to_read;
+ remain -= to_read;
+ }
+
+ SNAP_LOG(INFO) << "Finished reading block-device: " << dm_block_device
+ << " partition: " << partition_name << " size: " << size
+ << " offset: " << offset;
+}
+
+void Snapuserd::ReadBlocks(const std::string& partition_name, const std::string& dm_block_device) {
+ SNAP_LOG(DEBUG) << "Reading partition: " << partition_name
+ << " Block-Device: " << dm_block_device;
+
+ uint64_t dev_sz = 0;
+
+ unique_fd fd(TEMP_FAILURE_RETRY(open(dm_block_device.c_str(), O_RDONLY | O_CLOEXEC)));
+ if (fd < 0) {
+ SNAP_LOG(ERROR) << "Cannot open block device";
+ return;
+ }
+
+ dev_sz = get_block_device_size(fd.get());
+ if (!dev_sz) {
+ SNAP_PLOG(ERROR) << "Could not determine block device size: " << dm_block_device;
+ return;
+ }
+
+ int num_threads = 2;
+ size_t num_blocks = dev_sz >> BLOCK_SHIFT;
+ size_t num_blocks_per_thread = num_blocks / num_threads;
+ size_t read_sz_per_thread = num_blocks_per_thread << BLOCK_SHIFT;
+ 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);
+
+ offset += read_sz_per_thread;
+ }
+}
+
/*
* Entry point to launch threads
*/
@@ -740,6 +786,39 @@
std::async(std::launch::async, &WorkerThread::RunThread, worker_threads_[i].get()));
}
+ bool second_stage_init = true;
+
+ // We don't want to read the blocks during first stage init.
+ if (android::base::EndsWith(misc_name_, "-init") || is_socket_present_) {
+ second_stage_init = false;
+ }
+
+ if (second_stage_init) {
+ SNAP_LOG(INFO) << "Reading blocks to cache....";
+ auto& dm = DeviceMapper::Instance();
+ auto dm_block_devices = dm.FindDmPartitions();
+ if (dm_block_devices.empty()) {
+ SNAP_LOG(ERROR) << "No dm-enabled block device is found.";
+ } else {
+ auto parts = android::base::Split(misc_name_, "-");
+ std::string partition_name = parts[0];
+
+ const char* suffix_b = "_b";
+ const char* suffix_a = "_a";
+
+ partition_name.erase(partition_name.find_last_not_of(suffix_b) + 1);
+ partition_name.erase(partition_name.find_last_not_of(suffix_a) + 1);
+
+ if (dm_block_devices.find(partition_name) == dm_block_devices.end()) {
+ SNAP_LOG(ERROR) << "Failed to find dm block device for " << partition_name;
+ } else {
+ ReadBlocks(partition_name, dm_block_devices.at(partition_name));
+ }
+ }
+ } else {
+ SNAP_LOG(INFO) << "Not reading block device into cache";
+ }
+
bool ret = true;
for (auto& t : threads) {
ret = t.get() && ret;
diff --git a/fs_mgr/libsnapshot/snapuserd.h b/fs_mgr/libsnapshot/snapuserd/snapuserd.h
similarity index 90%
rename from fs_mgr/libsnapshot/snapuserd.h
rename to fs_mgr/libsnapshot/snapuserd/snapuserd.h
index 5d86e4f..6388a83 100644
--- a/fs_mgr/libsnapshot/snapuserd.h
+++ b/fs_mgr/libsnapshot/snapuserd/snapuserd.h
@@ -38,10 +38,11 @@
#include <android-base/logging.h>
#include <android-base/stringprintf.h>
#include <android-base/unique_fd.h>
+#include <ext4_utils/ext4_utils.h>
#include <libdm/dm.h>
#include <libsnapshot/cow_reader.h>
#include <libsnapshot/cow_writer.h>
-#include <libsnapshot/snapuserd_kernel.h>
+#include <snapuserd/snapuserd_kernel.h>
namespace android {
namespace snapshot {
@@ -107,6 +108,20 @@
size_t buffer_size_;
};
+class XorSink : public IByteSink {
+ public:
+ void Initialize(BufferSink* sink, size_t size);
+ void Reset();
+ void* GetBuffer(size_t requested, size_t* actual) override;
+ bool ReturnData(void* buffer, size_t len) override;
+
+ private:
+ BufferSink* bufsink_;
+ std::unique_ptr<uint8_t[]> buffer_;
+ size_t buffer_size_;
+ size_t returned_;
+};
+
class Snapuserd;
class ReadAheadThread {
@@ -116,10 +131,10 @@
bool RunThread();
private:
- void InitializeIter();
- bool IterDone();
- void IterNext();
- const CowOperation* GetIterOp();
+ void InitializeRAIter();
+ bool RAIterDone();
+ void RAIterNext();
+ const CowOperation* GetRAOpIter();
void InitializeBuffer();
bool InitializeFds();
@@ -129,7 +144,7 @@
}
bool ReadAheadIOStart();
- void PrepareReadAhead(uint64_t* source_block, int* pending_ops, std::vector<uint64_t>& blocks);
+ void PrepareReadAhead(uint64_t* source_offset, int* pending_ops, std::vector<uint64_t>& blocks);
bool ReconstructDataFromCow();
void CheckOverlap(const CowOperation* cow_op);
@@ -187,7 +202,9 @@
// Processing COW operations
bool ProcessCowOp(const CowOperation* cow_op);
bool ProcessReplaceOp(const CowOperation* cow_op);
+ // Handles Copy and Xor
bool ProcessCopyOp(const CowOperation* cow_op);
+ bool ProcessXorOp(const CowOperation* cow_op);
bool ProcessZeroOp();
bool ReadFromBaseDevice(const CowOperation* cow_op);
@@ -206,6 +223,7 @@
std::unique_ptr<CowReader> reader_;
BufferSink bufsink_;
+ XorSink xorsink_;
std::string cow_device_;
std::string backing_store_device_;
@@ -244,6 +262,7 @@
void* GetExceptionBuffer(size_t i) { return vec_[i].get(); }
bool InitializeWorkers();
+ std::unique_ptr<CowReader> CloneReaderForWorker();
std::shared_ptr<Snapuserd> GetSharedPtr() { return shared_from_this(); }
std::vector<std::pair<sector_t, const CowOperation*>>& GetChunkVec() { return chunk_vec_; }
@@ -284,6 +303,7 @@
// Total number of blocks to be merged in a given read-ahead buffer region
void SetTotalRaBlocksMerged(int x) { total_ra_blocks_merged_ = x; }
int GetTotalRaBlocksMerged() { return total_ra_blocks_merged_; }
+ void SetSocketPresent(bool socket) { is_socket_present_ = socket; }
private:
bool IsChunkIdMetadata(chunk_t chunk);
@@ -296,6 +316,10 @@
bool IsBlockAligned(int read_size) { return ((read_size & (BLOCK_SZ - 1)) == 0); }
struct BufferState* GetBufferState();
+ void ReadBlocks(const std::string& partition_name, const std::string& dm_block_device);
+ void ReadBlocksToCache(const std::string& dm_block_device, const std::string& partition_name,
+ off_t offset, size_t size);
+
std::string cow_device_;
std::string backing_store_device_;
std::string control_device_;
@@ -335,6 +359,7 @@
bool merge_initiated_ = false;
bool attached_ = false;
+ bool is_socket_present_;
};
} // namespace snapshot
diff --git a/fs_mgr/libsnapshot/snapuserd/snapuserd.rc b/fs_mgr/libsnapshot/snapuserd/snapuserd.rc
new file mode 100644
index 0000000..2750096
--- /dev/null
+++ b/fs_mgr/libsnapshot/snapuserd/snapuserd.rc
@@ -0,0 +1,19 @@
+service snapuserd /system/bin/snapuserd
+ socket snapuserd stream 0660 system system
+ oneshot
+ disabled
+ user root
+ group root system
+ seclabel u:r:snapuserd:s0
+
+service snapuserd_proxy /system/bin/snapuserd -socket-handoff
+ socket snapuserd stream 0660 system system
+ socket snapuserd_proxy seqpacket 0660 system root
+ oneshot
+ disabled
+ user root
+ group root system
+ seclabel u:r:snapuserd:s0
+
+on property:init.svc.snapuserd=stopped
+ setprop snapuserd.ready false
diff --git a/fs_mgr/libsnapshot/snapuserd_client.cpp b/fs_mgr/libsnapshot/snapuserd/snapuserd_client.cpp
similarity index 88%
rename from fs_mgr/libsnapshot/snapuserd_client.cpp
rename to fs_mgr/libsnapshot/snapuserd/snapuserd_client.cpp
index 41ab344..1ea05a3 100644
--- a/fs_mgr/libsnapshot/snapuserd_client.cpp
+++ b/fs_mgr/libsnapshot/snapuserd/snapuserd_client.cpp
@@ -33,7 +33,7 @@
#include <android-base/parseint.h>
#include <android-base/properties.h>
#include <android-base/strings.h>
-#include <libsnapshot/snapuserd_client.h>
+#include <snapuserd/snapuserd_client.h>
namespace android {
namespace snapshot {
@@ -42,13 +42,15 @@
using android::base::unique_fd;
bool EnsureSnapuserdStarted() {
- if (android::base::GetProperty("init.svc.snapuserd", "") == "running") {
- return true;
+ if (android::base::GetProperty("init.svc.snapuserd", "") != "running") {
+ android::base::SetProperty("ctl.start", "snapuserd");
+ if (!android::base::WaitForProperty("init.svc.snapuserd", "running", 10s)) {
+ LOG(ERROR) << "Timed out waiting for snapuserd to start.";
+ return false;
+ }
}
-
- android::base::SetProperty("ctl.start", "snapuserd");
- if (!android::base::WaitForProperty("init.svc.snapuserd", "running", 10s)) {
- LOG(ERROR) << "Timed out waiting for snapuserd to start.";
+ if (!android::base::WaitForProperty("snapuserd.ready", "true", 10s)) {
+ LOG(ERROR) << "Timed out waiting for snapuserd to be ready.";
return false;
}
return true;
@@ -141,6 +143,16 @@
return true;
}
+bool SnapuserdClient::SupportsSecondStageSocketHandoff() {
+ std::string msg = "supports,second_stage_socket_handoff";
+ if (!Sendmsg(msg)) {
+ LOG(ERROR) << "Failed to send message " << msg << " to snapuserd";
+ return false;
+ }
+ std::string response = Receivemsg();
+ return response == "success";
+}
+
std::string SnapuserdClient::Receivemsg() {
char msg[PACKET_SIZE];
ssize_t ret = TEMP_FAILURE_RETRY(recv(sockfd_, msg, sizeof(msg), 0));
diff --git a/fs_mgr/libsnapshot/snapuserd_daemon.cpp b/fs_mgr/libsnapshot/snapuserd/snapuserd_daemon.cpp
similarity index 82%
rename from fs_mgr/libsnapshot/snapuserd_daemon.cpp
rename to fs_mgr/libsnapshot/snapuserd/snapuserd_daemon.cpp
index 7fa01b7..e05822e 100644
--- a/fs_mgr/libsnapshot/snapuserd_daemon.cpp
+++ b/fs_mgr/libsnapshot/snapuserd/snapuserd_daemon.cpp
@@ -19,13 +19,15 @@
#include <android-base/logging.h>
#include <android-base/strings.h>
#include <gflags/gflags.h>
-#include <libsnapshot/snapuserd_client.h>
+#include <snapuserd/snapuserd_client.h>
#include "snapuserd_server.h"
DEFINE_string(socket, android::snapshot::kSnapuserdSocket, "Named socket or socket path.");
DEFINE_bool(no_socket, false,
"If true, no socket is used. Each additional argument is an INIT message.");
+DEFINE_bool(socket_handoff, false,
+ "If true, perform a socket hand-off with an existing snapuserd instance, then exit.");
namespace android {
namespace snapshot {
@@ -33,8 +35,28 @@
bool Daemon::StartServer(int argc, char** argv) {
int arg_start = gflags::ParseCommandLineFlags(&argc, &argv, true);
+ sigfillset(&signal_mask_);
+ sigdelset(&signal_mask_, SIGINT);
+ sigdelset(&signal_mask_, SIGTERM);
+ sigdelset(&signal_mask_, SIGUSR1);
+
+ // Masking signals here ensure that after this point, we won't handle INT/TERM
+ // until after we call into ppoll()
+ signal(SIGINT, Daemon::SignalHandler);
+ signal(SIGTERM, Daemon::SignalHandler);
+ signal(SIGPIPE, Daemon::SignalHandler);
+ signal(SIGUSR1, Daemon::SignalHandler);
+
+ MaskAllSignalsExceptIntAndTerm();
+
+ if (FLAGS_socket_handoff) {
+ return server_.RunForSocketHandoff();
+ }
if (!FLAGS_no_socket) {
- return server_.Start(FLAGS_socket);
+ if (!server_.Start(FLAGS_socket)) {
+ return false;
+ }
+ return server_.Run();
}
for (int i = arg_start; i < argc; i++) {
@@ -51,8 +73,7 @@
// Skip the accept() call to avoid spurious log spam. The server will still
// run until all handlers have completed.
- server_.SetTerminating();
- return true;
+ return server_.WaitForSocket();
}
void Daemon::MaskAllSignalsExceptIntAndTerm() {
@@ -61,6 +82,7 @@
sigdelset(&signal_mask, SIGINT);
sigdelset(&signal_mask, SIGTERM);
sigdelset(&signal_mask, SIGPIPE);
+ sigdelset(&signal_mask, SIGUSR1);
if (sigprocmask(SIG_SETMASK, &signal_mask, NULL) != 0) {
PLOG(ERROR) << "Failed to set sigprocmask";
}
@@ -74,28 +96,14 @@
}
}
-void Daemon::Run() {
- sigfillset(&signal_mask_);
- sigdelset(&signal_mask_, SIGINT);
- sigdelset(&signal_mask_, SIGTERM);
-
- // Masking signals here ensure that after this point, we won't handle INT/TERM
- // until after we call into ppoll()
- signal(SIGINT, Daemon::SignalHandler);
- signal(SIGTERM, Daemon::SignalHandler);
- signal(SIGPIPE, Daemon::SignalHandler);
-
- LOG(DEBUG) << "Snapuserd-server: ready to accept connections";
-
- MaskAllSignalsExceptIntAndTerm();
-
- server_.Run();
-}
-
void Daemon::Interrupt() {
server_.Interrupt();
}
+void Daemon::ReceivedSocketSignal() {
+ server_.ReceivedSocketSignal();
+}
+
void Daemon::SignalHandler(int signal) {
LOG(DEBUG) << "Snapuserd received signal: " << signal;
switch (signal) {
@@ -108,6 +116,11 @@
LOG(ERROR) << "Received SIGPIPE signal";
break;
}
+ case SIGUSR1: {
+ LOG(INFO) << "Received SIGUSR1, attaching to proxy socket";
+ Daemon::Instance().ReceivedSocketSignal();
+ break;
+ }
default:
LOG(ERROR) << "Received unknown signal " << signal;
break;
@@ -126,7 +139,5 @@
LOG(ERROR) << "Snapuserd daemon failed to start.";
exit(EXIT_FAILURE);
}
- daemon.Run();
-
return 0;
}
diff --git a/fs_mgr/libsnapshot/snapuserd_daemon.h b/fs_mgr/libsnapshot/snapuserd/snapuserd_daemon.h
similarity index 97%
rename from fs_mgr/libsnapshot/snapuserd_daemon.h
rename to fs_mgr/libsnapshot/snapuserd/snapuserd_daemon.h
index f8afac5..b660ba2 100644
--- a/fs_mgr/libsnapshot/snapuserd_daemon.h
+++ b/fs_mgr/libsnapshot/snapuserd/snapuserd_daemon.h
@@ -36,8 +36,8 @@
}
bool StartServer(int argc, char** argv);
- void Run();
void Interrupt();
+ void ReceivedSocketSignal();
private:
// Signal mask used with ppoll()
diff --git a/fs_mgr/libsnapshot/snapuserd_readahead.cpp b/fs_mgr/libsnapshot/snapuserd/snapuserd_readahead.cpp
similarity index 86%
rename from fs_mgr/libsnapshot/snapuserd_readahead.cpp
rename to fs_mgr/libsnapshot/snapuserd/snapuserd_readahead.cpp
index 16d5919..b868eed 100644
--- a/fs_mgr/libsnapshot/snapuserd_readahead.cpp
+++ b/fs_mgr/libsnapshot/snapuserd/snapuserd_readahead.cpp
@@ -20,7 +20,7 @@
#include <optional>
#include <set>
-#include <libsnapshot/snapuserd_client.h>
+#include <snapuserd/snapuserd_client.h>
namespace android {
namespace snapshot {
@@ -172,24 +172,37 @@
}
void ReadAheadThread::CheckOverlap(const CowOperation* cow_op) {
- if (dest_blocks_.count(cow_op->new_block) || source_blocks_.count(cow_op->source)) {
+ 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;
+ }
+ if (dest_blocks_.count(cow_op->new_block) || source_blocks_.count(source_block) ||
+ (source_offset > 0 && source_blocks_.count(source_block + 1))) {
overlap_ = true;
}
- dest_blocks_.insert(cow_op->source);
+ dest_blocks_.insert(source_block);
+ if (source_offset > 0) {
+ dest_blocks_.insert(source_block + 1);
+ }
source_blocks_.insert(cow_op->new_block);
}
-void ReadAheadThread::PrepareReadAhead(uint64_t* source_block, int* pending_ops,
+void ReadAheadThread::PrepareReadAhead(uint64_t* source_offset, int* pending_ops,
std::vector<uint64_t>& blocks) {
int num_ops = *pending_ops;
int nr_consecutive = 0;
- if (!IterDone() && num_ops) {
- // Get the first block
- const CowOperation* cow_op = GetIterOp();
- *source_block = cow_op->source;
- IterNext();
+ if (!RAIterDone() && num_ops) {
+ // 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;
+ }
+ RAIterNext();
num_ops -= 1;
nr_consecutive = 1;
blocks.push_back(cow_op->new_block);
@@ -201,15 +214,19 @@
/*
* Find number of consecutive blocks working backwards.
*/
- while (!IterDone() && num_ops) {
- const CowOperation* op = GetIterOp();
- if (op->source != (*source_block - nr_consecutive)) {
+ while (!RAIterDone() && num_ops) {
+ const CowOperation* op = GetRAOpIter();
+ uint64_t next_offset = op->source;
+ if (cow_op->type == kCowCopyOp) {
+ next_offset *= BLOCK_SZ;
+ }
+ if (next_offset != (*source_offset - nr_consecutive * BLOCK_SZ)) {
break;
}
nr_consecutive += 1;
num_ops -= 1;
blocks.push_back(op->new_block);
- IterNext();
+ RAIterNext();
if (!overlap_) {
CheckOverlap(op);
@@ -247,12 +264,12 @@
// We are done re-constructing the mapping; however, we need to make sure
// all the COW operations to-be merged are present in the re-constructed
// mapping.
- while (!IterDone()) {
- const CowOperation* op = GetIterOp();
+ while (!RAIterDone()) {
+ const CowOperation* op = GetRAOpIter();
if (read_ahead_buffer_map.find(op->new_block) != read_ahead_buffer_map.end()) {
num_ops -= 1;
snapuserd_->SetFinalBlockMerged(op->new_block);
- IterNext();
+ RAIterNext();
} else {
// Verify that we have covered all the ops which were re-constructed
// from COW device - These are the ops which are being
@@ -312,10 +329,10 @@
source_blocks_.clear();
while (true) {
- uint64_t source_block;
+ uint64_t source_offset;
int linear_blocks;
- PrepareReadAhead(&source_block, &num_ops, blocks);
+ PrepareReadAhead(&source_offset, &num_ops, blocks);
linear_blocks = blocks.size();
if (linear_blocks == 0) {
// No more blocks to read
@@ -324,7 +341,7 @@
}
// Get the first block in the consecutive set of blocks
- source_block = source_block + 1 - linear_blocks;
+ source_offset = source_offset - (linear_blocks - 1) * BLOCK_SZ;
size_t io_size = (linear_blocks * BLOCK_SZ);
num_ops -= linear_blocks;
total_blocks_merged += linear_blocks;
@@ -358,10 +375,12 @@
// Read from the base device consecutive set of blocks in one shot
if (!android::base::ReadFullyAtOffset(backing_store_fd_,
(char*)read_ahead_buffer_ + buffer_offset, io_size,
- source_block * BLOCK_SZ)) {
- SNAP_PLOG(ERROR) << "Copy-op failed. Read from backing store: " << backing_store_device_
- << "at block :" << source_block << " buffer_offset : " << buffer_offset
- << " io_size : " << io_size << " buf-addr : " << read_ahead_buffer_;
+ source_offset)) {
+ SNAP_PLOG(ERROR) << "Ordered-op failed. Read from backing store: "
+ << backing_store_device_ << "at block :" << source_offset / BLOCK_SZ
+ << " offset :" << source_offset % BLOCK_SZ
+ << " buffer_offset : " << buffer_offset << " io_size : " << io_size
+ << " buf-addr : " << read_ahead_buffer_;
snapuserd_->ReadAheadIOFailed();
return false;
@@ -394,10 +413,10 @@
return false;
}
- InitializeIter();
+ InitializeRAIter();
InitializeBuffer();
- while (!IterDone()) {
+ while (!RAIterDone()) {
if (!ReadAheadIOStart()) {
return false;
}
@@ -433,21 +452,21 @@
return true;
}
-void ReadAheadThread::InitializeIter() {
+void ReadAheadThread::InitializeRAIter() {
std::vector<const CowOperation*>& read_ahead_ops = snapuserd_->GetReadAheadOpsVec();
read_ahead_iter_ = read_ahead_ops.rbegin();
}
-bool ReadAheadThread::IterDone() {
+bool ReadAheadThread::RAIterDone() {
std::vector<const CowOperation*>& read_ahead_ops = snapuserd_->GetReadAheadOpsVec();
return read_ahead_iter_ == read_ahead_ops.rend();
}
-void ReadAheadThread::IterNext() {
+void ReadAheadThread::RAIterNext() {
read_ahead_iter_++;
}
-const CowOperation* ReadAheadThread::GetIterOp() {
+const CowOperation* ReadAheadThread::GetRAOpIter() {
return *read_ahead_iter_;
}
diff --git a/fs_mgr/libsnapshot/snapuserd_server.cpp b/fs_mgr/libsnapshot/snapuserd/snapuserd_server.cpp
similarity index 72%
rename from fs_mgr/libsnapshot/snapuserd_server.cpp
rename to fs_mgr/libsnapshot/snapuserd/snapuserd_server.cpp
index 8339690..2f87557 100644
--- a/fs_mgr/libsnapshot/snapuserd_server.cpp
+++ b/fs_mgr/libsnapshot/snapuserd/snapuserd_server.cpp
@@ -25,14 +25,26 @@
#include <sys/types.h>
#include <unistd.h>
+#include <android-base/cmsg.h>
#include <android-base/logging.h>
-
+#include <android-base/properties.h>
+#include <android-base/scopeguard.h>
+#include <fs_mgr/file_wait.h>
+#include <snapuserd/snapuserd_client.h>
#include "snapuserd.h"
#include "snapuserd_server.h"
+#define _REALLY_INCLUDE_SYS__SYSTEM_PROPERTIES_H_
+#include <sys/_system_properties.h>
+
namespace android {
namespace snapshot {
+using namespace std::string_literals;
+
+using android::base::borrowed_fd;
+using android::base::unique_fd;
+
DaemonOperations SnapuserdServer::Resolveop(std::string& input) {
if (input == "init") return DaemonOperations::INIT;
if (input == "start") return DaemonOperations::START;
@@ -40,6 +52,7 @@
if (input == "query") return DaemonOperations::QUERY;
if (input == "delete") return DaemonOperations::DELETE;
if (input == "detach") return DaemonOperations::DETACH;
+ if (input == "supports") return DaemonOperations::SUPPORTS;
return DaemonOperations::INVALID;
}
@@ -193,6 +206,16 @@
terminating_ = true;
return true;
}
+ case DaemonOperations::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");
+ }
+ return Sendmsg(fd, "fail");
+ }
default: {
LOG(ERROR) << "Received unknown message type from client";
Sendmsg(fd, "fail");
@@ -204,6 +227,7 @@
void SnapuserdServer::RunThread(std::shared_ptr<DmUserHandler> handler) {
LOG(INFO) << "Entering thread for handler: " << handler->misc_name();
+ handler->snapuserd()->SetSocketPresent(is_socket_present_);
if (!handler->snapuserd()->Start()) {
LOG(ERROR) << " Failed to launch all worker threads";
}
@@ -245,28 +269,45 @@
}
bool SnapuserdServer::Start(const std::string& socketname) {
+ bool start_listening = true;
+
sockfd_.reset(android_get_control_socket(socketname.c_str()));
- if (sockfd_ >= 0) {
- if (listen(sockfd_.get(), 4) < 0) {
- PLOG(ERROR) << "listen socket failed: " << socketname;
- return false;
- }
- } else {
+ if (sockfd_ < 0) {
sockfd_.reset(socket_local_server(socketname.c_str(), ANDROID_SOCKET_NAMESPACE_RESERVED,
SOCK_STREAM));
if (sockfd_ < 0) {
PLOG(ERROR) << "Failed to create server socket " << socketname;
return false;
}
+ start_listening = false;
+ }
+ return StartWithSocket(start_listening);
+}
+
+bool SnapuserdServer::StartWithSocket(bool start_listening) {
+ if (start_listening && listen(sockfd_.get(), 4) < 0) {
+ PLOG(ERROR) << "listen socket failed";
+ return false;
}
- AddWatchedFd(sockfd_);
+ AddWatchedFd(sockfd_, POLLIN);
+ is_socket_present_ = true;
- LOG(DEBUG) << "Snapuserd server successfully started with socket name " << socketname;
+ // If started in first-stage init, the property service won't be online.
+ if (access("/dev/socket/property_service", F_OK) == 0) {
+ if (!android::base::SetProperty("snapuserd.ready", "true")) {
+ LOG(ERROR) << "Failed to set snapuserd.ready property";
+ return false;
+ }
+ }
+
+ LOG(DEBUG) << "Snapuserd server now accepting connections";
return true;
}
bool SnapuserdServer::Run() {
+ LOG(INFO) << "Now listening on snapuserd socket";
+
while (!IsTerminating()) {
int rv = TEMP_FAILURE_RETRY(poll(watched_fds_.data(), watched_fds_.size(), -1));
if (rv < 0) {
@@ -311,10 +352,10 @@
}
}
-void SnapuserdServer::AddWatchedFd(android::base::borrowed_fd fd) {
+void SnapuserdServer::AddWatchedFd(android::base::borrowed_fd fd, int events) {
struct pollfd p = {};
p.fd = fd.get();
- p.events = POLLIN;
+ p.events = events;
watched_fds_.emplace_back(std::move(p));
}
@@ -325,7 +366,7 @@
return;
}
- AddWatchedFd(fd);
+ AddWatchedFd(fd, POLLIN);
}
bool SnapuserdServer::HandleClient(android::base::borrowed_fd fd, int revents) {
@@ -422,5 +463,97 @@
return true;
}
+bool SnapuserdServer::WaitForSocket() {
+ auto scope_guard = android::base::make_scope_guard([this]() -> void { JoinAllThreads(); });
+
+ auto socket_path = ANDROID_SOCKET_DIR "/"s + kSnapuserdSocketProxy;
+
+ if (!android::fs_mgr::WaitForFile(socket_path, std::chrono::milliseconds::max())) {
+ LOG(ERROR)
+ << "Failed to wait for proxy socket, second-stage snapuserd will fail to connect";
+ return false;
+ }
+
+ // We must re-initialize property service access, since we launched before
+ // second-stage init.
+ __system_properties_init();
+
+ if (!android::base::WaitForProperty("snapuserd.proxy_ready", "true")) {
+ LOG(ERROR)
+ << "Failed to wait for proxy property, second-stage snapuserd will fail to connect";
+ return false;
+ }
+
+ unique_fd fd(socket_local_client(kSnapuserdSocketProxy, ANDROID_SOCKET_NAMESPACE_RESERVED,
+ SOCK_SEQPACKET));
+ if (fd < 0) {
+ PLOG(ERROR) << "Failed to connect to socket proxy";
+ return false;
+ }
+
+ char code[1];
+ std::vector<unique_fd> fds;
+ ssize_t rv = android::base::ReceiveFileDescriptorVector(fd, code, sizeof(code), 1, &fds);
+ if (rv < 0) {
+ PLOG(ERROR) << "Failed to receive server socket over proxy";
+ return false;
+ }
+ if (fds.empty()) {
+ LOG(ERROR) << "Expected at least one file descriptor from proxy";
+ return false;
+ }
+
+ // We don't care if the ACK is received.
+ code[0] = 'a';
+ if (TEMP_FAILURE_RETRY(send(fd, code, sizeof(code), MSG_NOSIGNAL) < 0)) {
+ PLOG(ERROR) << "Failed to send ACK to proxy";
+ return false;
+ }
+
+ sockfd_ = std::move(fds[0]);
+ if (!StartWithSocket(true)) {
+ return false;
+ }
+ return Run();
+}
+
+bool SnapuserdServer::RunForSocketHandoff() {
+ unique_fd proxy_fd(android_get_control_socket(kSnapuserdSocketProxy));
+ if (proxy_fd < 0) {
+ PLOG(FATAL) << "Proxy could not get android control socket " << kSnapuserdSocketProxy;
+ }
+ borrowed_fd server_fd(android_get_control_socket(kSnapuserdSocket));
+ if (server_fd < 0) {
+ PLOG(FATAL) << "Proxy could not get android control socket " << kSnapuserdSocket;
+ }
+
+ if (listen(proxy_fd.get(), 4) < 0) {
+ PLOG(FATAL) << "Proxy listen socket failed";
+ }
+
+ if (!android::base::SetProperty("snapuserd.proxy_ready", "true")) {
+ LOG(FATAL) << "Proxy failed to set ready property";
+ }
+
+ unique_fd client_fd(
+ TEMP_FAILURE_RETRY(accept4(proxy_fd.get(), nullptr, nullptr, SOCK_CLOEXEC)));
+ if (client_fd < 0) {
+ PLOG(FATAL) << "Proxy accept failed";
+ }
+
+ char code[1] = {'a'};
+ std::vector<int> fds = {server_fd.get()};
+ ssize_t rv = android::base::SendFileDescriptorVector(client_fd, code, sizeof(code), fds);
+ if (rv < 0) {
+ PLOG(FATAL) << "Proxy could not send file descriptor to snapuserd";
+ }
+ // Wait for an ACK - results don't matter, we just don't want to risk closing
+ // the proxy socket too early.
+ if (recv(client_fd, code, sizeof(code), 0) < 0) {
+ PLOG(FATAL) << "Proxy could not receive terminating code from snapuserd";
+ }
+ return true;
+}
+
} // namespace snapshot
} // namespace android
diff --git a/fs_mgr/libsnapshot/snapuserd_server.h b/fs_mgr/libsnapshot/snapuserd/snapuserd_server.h
similarity index 92%
rename from fs_mgr/libsnapshot/snapuserd_server.h
rename to fs_mgr/libsnapshot/snapuserd/snapuserd_server.h
index 6699189..3b6ff15 100644
--- a/fs_mgr/libsnapshot/snapuserd_server.h
+++ b/fs_mgr/libsnapshot/snapuserd/snapuserd_server.h
@@ -42,6 +42,7 @@
STOP,
DELETE,
DETACH,
+ SUPPORTS,
INVALID,
};
@@ -93,14 +94,16 @@
private:
android::base::unique_fd sockfd_;
bool terminating_;
+ volatile bool received_socket_signal_ = false;
std::vector<struct pollfd> watched_fds_;
+ bool is_socket_present_ = false;
std::mutex lock_;
using HandlerList = std::vector<std::shared_ptr<DmUserHandler>>;
HandlerList dm_users_;
- void AddWatchedFd(android::base::borrowed_fd fd);
+ void AddWatchedFd(android::base::borrowed_fd fd, int events);
void AcceptClient();
bool HandleClient(android::base::borrowed_fd fd, int revents);
bool Recv(android::base::borrowed_fd fd, std::string* data);
@@ -117,6 +120,7 @@
void RunThread(std::shared_ptr<DmUserHandler> handler);
void JoinAllThreads();
+ bool StartWithSocket(bool start_listening);
// Find a DmUserHandler within a lock.
HandlerList::iterator FindHandler(std::lock_guard<std::mutex>* proof_of_lock,
@@ -129,6 +133,8 @@
bool Start(const std::string& socketname);
bool Run();
void Interrupt();
+ bool RunForSocketHandoff();
+ bool WaitForSocket();
std::shared_ptr<DmUserHandler> AddHandler(const std::string& misc_name,
const std::string& cow_device_path,
@@ -136,6 +142,7 @@
bool StartHandler(const std::shared_ptr<DmUserHandler>& handler);
void SetTerminating() { terminating_ = true; }
+ void ReceivedSocketSignal() { received_socket_signal_ = true; }
};
} // namespace snapshot
diff --git a/fs_mgr/libsnapshot/snapuserd_worker.cpp b/fs_mgr/libsnapshot/snapuserd/snapuserd_worker.cpp
similarity index 92%
rename from fs_mgr/libsnapshot/snapuserd_worker.cpp
rename to fs_mgr/libsnapshot/snapuserd/snapuserd_worker.cpp
index 682f9da..cdf9fe7 100644
--- a/fs_mgr/libsnapshot/snapuserd_worker.cpp
+++ b/fs_mgr/libsnapshot/snapuserd/snapuserd_worker.cpp
@@ -20,7 +20,7 @@
#include <optional>
#include <set>
-#include <libsnapshot/snapuserd_client.h>
+#include <snapuserd/snapuserd_client.h>
namespace android {
namespace snapshot {
@@ -71,6 +71,39 @@
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;
+}
+
WorkerThread::WorkerThread(const std::string& cow_device, const std::string& backing_device,
const std::string& control_device, const std::string& misc_name,
std::shared_ptr<Snapuserd> snapuserd) {
@@ -105,11 +138,11 @@
}
bool WorkerThread::InitReader() {
- reader_ = std::make_unique<CowReader>();
+ reader_ = snapuserd_->CloneReaderForWorker();
+
if (!reader_->InitForMerge(std::move(cow_fd_))) {
return false;
}
-
return true;
}
@@ -150,10 +183,19 @@
}
SNAP_LOG(DEBUG) << " ReadFromBaseDevice...: new-block: " << cow_op->new_block
<< " Source: " << cow_op->source;
- if (!android::base::ReadFullyAtOffset(backing_store_fd_, buffer, BLOCK_SZ,
- cow_op->source * BLOCK_SZ)) {
- SNAP_PLOG(ERROR) << "Copy-op failed. Read from backing store: " << backing_store_device_
- << "at block :" << 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;
}
@@ -188,6 +230,23 @@
return true;
}
+bool WorkerThread::ProcessXorOp(const CowOperation* cow_op) {
+ if (!GetReadAheadPopulatedBuffer(cow_op)) {
+ SNAP_LOG(DEBUG) << " GetReadAheadPopulatedBuffer failed..."
+ << " new_block: " << cow_op->new_block;
+ if (!ReadFromBaseDevice(cow_op)) {
+ return false;
+ }
+ }
+ xorsink_.Reset();
+ if (!reader_->ReadData(*cow_op, &xorsink_)) {
+ SNAP_LOG(ERROR) << "ProcessXorOp failed for block " << cow_op->new_block;
+ return false;
+ }
+
+ return true;
+}
+
bool WorkerThread::ProcessZeroOp() {
// Zero out the entire block
void* buffer = bufsink_.GetPayloadBuffer(BLOCK_SZ);
@@ -219,6 +278,10 @@
return ProcessCopyOp(cow_op);
}
+ case kCowXorOp: {
+ return ProcessXorOp(cow_op);
+ }
+
default: {
SNAP_LOG(ERROR) << "Unknown operation-type found: " << cow_op->type;
}
@@ -470,10 +533,10 @@
}
int WorkerThread::GetNumberOfMergedOps(void* merged_buffer, void* unmerged_buffer, loff_t offset,
- int unmerged_exceptions, bool* copy_op, bool* commit) {
+ int unmerged_exceptions, bool* ordered_op, bool* commit) {
int merged_ops_cur_iter = 0;
std::unordered_map<uint64_t, void*>& read_ahead_buffer_map = snapuserd_->GetReadAheadMap();
- *copy_op = false;
+ *ordered_op = false;
std::vector<std::pair<sector_t, const CowOperation*>>& chunk_vec = snapuserd_->GetChunkVec();
// Find the operations which are merged in this cycle.
@@ -511,9 +574,9 @@
}
const CowOperation* cow_op = it->second;
- if (snapuserd_->IsReadAheadFeaturePresent() && cow_op->type == kCowCopyOp) {
- *copy_op = true;
- // Every single copy operation has to come from read-ahead
+ if (snapuserd_->IsReadAheadFeaturePresent() && IsOrderedOp(*cow_op)) {
+ *ordered_op = true;
+ // Every single ordered operation has to come from read-ahead
// cache.
if (read_ahead_buffer_map.find(cow_op->new_block) == read_ahead_buffer_map.end()) {
SNAP_LOG(ERROR)
@@ -557,7 +620,7 @@
bool WorkerThread::ProcessMergeComplete(chunk_t chunk, void* buffer) {
uint32_t stride = exceptions_per_area_ + 1;
const std::vector<std::unique_ptr<uint8_t[]>>& vec = snapuserd_->GetMetadataVec();
- bool copy_op = false;
+ bool ordered_op = false;
bool commit = false;
// ChunkID to vector index
@@ -582,7 +645,7 @@
}
int merged_ops_cur_iter = GetNumberOfMergedOps(buffer, vec[divresult.quot].get(), offset,
- unmerged_exceptions, ©_op, &commit);
+ unmerged_exceptions, &ordered_op, &commit);
// There should be at least one operation merged in this cycle
if (!(merged_ops_cur_iter > 0)) {
@@ -590,7 +653,7 @@
return false;
}
- if (copy_op) {
+ if (ordered_op) {
if (commit) {
// Push the flushing logic to read-ahead thread so that merge thread
// can make forward progress. Sync will happen in the background
@@ -819,6 +882,7 @@
bool WorkerThread::RunThread() {
InitializeBufsink();
+ xorsink_.Initialize(&bufsink_, BLOCK_SZ);
if (!InitializeFds()) {
return false;
diff --git a/fs_mgr/libstorage_literals/Android.bp b/fs_mgr/libstorage_literals/Android.bp
index fd7ea04..42abd09 100644
--- a/fs_mgr/libstorage_literals/Android.bp
+++ b/fs_mgr/libstorage_literals/Android.bp
@@ -7,6 +7,7 @@
name: "libstorage_literals_headers",
host_supported: true,
ramdisk_available: true,
+ vendor_ramdisk_available: true,
recovery_available: true,
export_include_dirs: ["."],
target: {
diff --git a/gatekeeperd/Android.bp b/gatekeeperd/Android.bp
index 95e814b..a7f0c0e 100644
--- a/gatekeeperd/Android.bp
+++ b/gatekeeperd/Android.bp
@@ -43,8 +43,8 @@
"libhidlbase",
"android.hardware.gatekeeper@1.0",
"libgatekeeper_aidl",
- "android.hardware.security.keymint-V1-ndk_platform",
- "android.security.authorization-ndk_platform",
+ "android.hardware.security.keymint-V1-ndk",
+ "android.security.authorization-ndk",
],
static_libs: ["libscrypt_static"],
diff --git a/init/Android.bp b/init/Android.bp
index 3e8d4e3..5d09687 100644
--- a/init/Android.bp
+++ b/init/Android.bp
@@ -228,17 +228,20 @@
stem: "init",
defaults: ["init_defaults"],
static_libs: ["libinit"],
- required: [
- "e2fsdroid",
- "init.rc",
- "mke2fs",
- "sload_f2fs",
- "make_f2fs",
- "ueventd.rc",
- ],
srcs: ["main.cpp"],
symlinks: ["ueventd"],
target: {
+ platform: {
+ required: [
+ "init.rc",
+ "ueventd.rc",
+ "e2fsdroid",
+ "extra_free_kbytes.sh",
+ "make_f2fs",
+ "mke2fs",
+ "sload_f2fs",
+ ],
+ },
recovery: {
cflags: ["-DRECOVERY"],
exclude_static_libs: [
@@ -248,6 +251,14 @@
"libbinder",
"libutils",
],
+ required: [
+ "init_recovery.rc",
+ "ueventd.rc.recovery",
+ "e2fsdroid.recovery",
+ "make_f2fs.recovery",
+ "mke2fs.recovery",
+ "sload_f2fs.recovery",
+ ],
},
},
visibility: ["//packages/modules/Virtualization/microdroid"],
@@ -542,3 +553,8 @@
},
},
}
+
+sh_binary {
+ name: "extra_free_kbytes.sh",
+ src: "extra_free_kbytes.sh",
+}
diff --git a/init/README.md b/init/README.md
index 4b04628..f447ab2 100644
--- a/init/README.md
+++ b/init/README.md
@@ -277,8 +277,6 @@
CLD_EXITED or an status other than '0', reboot the system with the target specified in
_target_. _target_ takes the same format as the parameter to sys.powerctl. This is particularly
intended to be used with the `exec_start` builtin for any must-have checks during boot.
- A service being stopped by init (e.g. using the `stop` or `class_reset` commands) is not
- considered a failure for the purpose of this setting.
`restart_period <seconds>`
> If a non-oneshot service exits, it will be restarted at its start time plus
diff --git a/init/TEST_MAPPING b/init/TEST_MAPPING
new file mode 100644
index 0000000..03b9eaa
--- /dev/null
+++ b/init/TEST_MAPPING
@@ -0,0 +1,13 @@
+{
+ "presubmit": [
+ {
+ "name": "CtsInitTestCases"
+ },
+ {
+ "name": "init_kill_services_test"
+ },
+ {
+ "name": "MicrodroidHostTestCases"
+ }
+ ]
+}
diff --git a/init/block_dev_initializer.cpp b/init/block_dev_initializer.cpp
index 9c2a7bb..05e00ed 100644
--- a/init/block_dev_initializer.cpp
+++ b/init/block_dev_initializer.cpp
@@ -87,7 +87,13 @@
auto iter = devices->find(name);
if (iter == devices->end()) {
- return ListenerAction::kContinue;
+ auto partition_name = DeviceHandler::GetPartitionNameForDevice(uevent.device_name);
+ if (!partition_name.empty()) {
+ iter = devices->find(partition_name);
+ }
+ if (iter == devices->end()) {
+ return ListenerAction::kContinue;
+ }
}
LOG(VERBOSE) << __PRETTY_FUNCTION__ << ": found partition: " << name;
diff --git a/init/builtins.cpp b/init/builtins.cpp
index 035038f..7633d9c 100644
--- a/init/builtins.cpp
+++ b/init/builtins.cpp
@@ -1305,20 +1305,8 @@
return {};
}
-
-static bool IsApexUpdatable() {
- static bool updatable = android::sysprop::ApexProperties::updatable().value_or(false);
- return updatable;
-}
-
static Result<void> do_update_linker_config(const BuiltinArguments&) {
- // If APEX is not updatable, then all APEX information are already included in the first
- // linker config generation, so there is no need to update linker configuration again.
- if (IsApexUpdatable()) {
- return GenerateLinkerConfiguration();
- }
-
- return {};
+ return GenerateLinkerConfiguration();
}
static Result<void> parse_apex_configs() {
diff --git a/init/devices.cpp b/init/devices.cpp
index 56c6623..d4a3cb9 100644
--- a/init/devices.cpp
+++ b/init/devices.cpp
@@ -46,6 +46,7 @@
using android::base::ReadFileToString;
using android::base::Readlink;
using android::base::Realpath;
+using android::base::Split;
using android::base::StartsWith;
using android::base::StringPrintf;
using android::base::Trim;
@@ -187,6 +188,36 @@
}
}
+std::string DeviceHandler::GetPartitionNameForDevice(const std::string& query_device) {
+ static const auto partition_map = [] {
+ std::vector<std::pair<std::string, std::string>> partition_map;
+ auto parser = [&partition_map](const std::string& key, const std::string& value) {
+ if (key != "androidboot.partition_map") {
+ return;
+ }
+ for (const auto& map : Split(value, ";")) {
+ auto map_pieces = Split(map, ",");
+ if (map_pieces.size() != 2) {
+ LOG(ERROR) << "Expected a comma separated device,partition mapping, but found '"
+ << map << "'";
+ continue;
+ }
+ partition_map.emplace_back(map_pieces[0], map_pieces[1]);
+ }
+ };
+ ImportKernelCmdline(parser);
+ ImportBootconfig(parser);
+ return partition_map;
+ }();
+
+ for (const auto& [device, partition] : partition_map) {
+ if (query_device == device) {
+ return partition;
+ }
+ }
+ return {};
+}
+
// Given a path that may start with a platform device, find the parent platform device by finding a
// parent directory with a 'subsystem' symlink that points to the platform bus.
// If it doesn't start with a platform device, return false
@@ -389,6 +420,10 @@
// If we don't have a partition name but we are a partition on a boot device, create a
// symlink of /dev/block/by-name/<device_name> for symmetry.
links.emplace_back("/dev/block/by-name/" + uevent.device_name);
+ auto partition_name = GetPartitionNameForDevice(uevent.device_name);
+ if (!partition_name.empty()) {
+ links.emplace_back("/dev/block/by-name/" + partition_name);
+ }
}
auto last_slash = uevent.path.rfind('/');
diff --git a/init/devices.h b/init/devices.h
index d70d746..f9f4d79 100644
--- a/init/devices.h
+++ b/init/devices.h
@@ -122,6 +122,12 @@
std::vector<std::string> GetBlockDeviceSymlinks(const Uevent& uevent) const;
+ // `androidboot.partition_map` allows associating a partition name for a raw block device
+ // through a comma separated and semicolon deliminated list. For example,
+ // `androidboot.partition_map=vdb,metadata;vdc,userdata` maps `vdb` to `metadata` and `vdc` to
+ // `userdata`.
+ static std::string GetPartitionNameForDevice(const std::string& device);
+
private:
bool FindPlatformDevice(std::string path, std::string* platform_device_path) const;
std::tuple<mode_t, uid_t, gid_t> GetDevicePermissions(
diff --git a/init/extra_free_kbytes.sh b/init/extra_free_kbytes.sh
new file mode 100755
index 0000000..aeaa912
--- /dev/null
+++ b/init/extra_free_kbytes.sh
@@ -0,0 +1,136 @@
+#!/bin/sh
+
+# Script implements watermark_scale calculation which results in the same low
+# watermark as if extra_free_kbytes tunable were to be used.
+#
+# Usage: extra_free_kbytes.sh <extra_free_kbytes value>
+#
+# extra_free_kbytes is distributed between zones based on
+# zone.managed_pages/vm_total_pages ratio, where vm_total_pages is the sum of
+# zone.managed_pages for all zones (zone.high used in this calculation is 0
+# when this is calculated). Therefore for each zone its share is calculated as:
+#
+# extra_free_pages = extra_free_kbytes / page_size
+# extra_share = extra_free_pages * managed_pages / vm_total_pages
+#
+# This extra_share is added to the low and high watermarks:
+#
+# low = min + max(min / 4, managed_pages * (watermark_scale / 10000)) + extra_share
+# high = min + 2 * max(min / 4, managed_pages * (watermark_scale / 10000)) + extra_share
+#
+# Because Android uses extra_free_kbytes to adjust the low watermark, we ignore
+# the difference in how watermark_scale and extra_free_kbytes affect the high
+# watermark and will match the low watermark only.
+#
+# To eliminate extra_share and compansate the difference with watermark_scale,
+# a new watermark_scale_new is calculated as:
+#
+# (1) max(min / 4, managed_pages * (watermark_scale / 10000)) + extra_share =
+# max(min / 4, managed_pages * (watermark_scale_new / 10000))
+#
+# Two cases to consider:
+# A. managed_pages * (watermark_scale / 10000) > min / 4
+# The formula (1) becomes:
+#
+# managed_pages * (watermark_scale / 10000) + extra_share =
+# managed_pages * (watermark_scale_new / 10000)
+#
+# after simplifying and substituting extra_share formula becomes:
+#
+# (2) watermark_scale_new = watermark_scale + extra_free_pages / vm_total_pages * 10000
+#
+# B. managed_pages * (watermark_scale / 10000) < min / 4
+# The formula (1) becomes:
+#
+# min / 4 + extra_share = max(min / 4, managed_pages * (watermark_scale_new / 10000))
+#
+# after calculating watermark_scale_new, if (managed_pages * (watermark_scale_new / 10000))
+# is still smaller than min / 4 then we can't compensate extra_share with
+# watermark_scale anyway. Therefore calculation becomes:
+#
+# watermark_scale_new = (min / 4 + extra_share) / managed_pages * 10000
+#
+# after simplifying and substituting extra_share formula becomes:
+#
+# (3) watermark_scale_new = (min / 4) * 10000 / managed_pages + extra_free_pages / vm_total_pages * 10000
+#
+# After defining watermark_delta = extra_free_pages / vm_total_pages * 10000:
+#
+# if (managed_pages * (watermark_scale / 10000) > min / 4)
+# watermark_scale_new = watermark_scale + watermark_delta
+# else
+# watermark_scale_new = (min / 4) * 10000 / managed_pages + watermark_delta
+#
+
+if [ "$#" -ne 1 ]
+then
+ echo "Usage: $0 <extra_free_kbytes value>"
+ exit
+fi
+
+extra_free_kbytes=$1
+
+# if extra_free_kbytes knob exists, use it and exit
+if [ -e /proc/sys/vm/extra_free_kbytes ]
+then
+ echo $extra_free_kbytes > /proc/sys/vm/extra_free_kbytes
+ exit
+fi
+
+watermark_scale=`cat /proc/sys/vm/watermark_scale_factor`
+
+# convert extra_free_kbytes to pages
+page_size=$(getconf PAGESIZE)
+page_size_kb=$((page_size/1024))
+extra_free_pg=$((extra_free_kbytes/page_size_kb))
+
+managed=($(grep managed /proc/zoneinfo | awk '{print $2}'))
+length=${#managed[@]}
+min=($(grep "min" /proc/zoneinfo | awk '{print $2}'))
+
+# calculate vm_total_pages.
+# WARNING: if the final low watermark differs from the original, the source of
+# the error is likely vm_total_pages which is impossible to get exact from the
+# userspace. Grep for "Total pages" in the kernel logs to see the actual
+# vm_total_pages and plug it in the calculation to confirm the source of the
+# error. Error caused by this inaccuracy is normally within 1% range.
+vm_total_pages=0
+i=0
+while [ $i -lt $length ]
+do
+ vm_total_pages=$((vm_total_pages + managed[i]))
+ i=$((i+1))
+done
+
+# calculate watermark_scale_new for each zone and choose the max
+max_watermark_scale=0
+i=0
+while [ $i -lt $length ]
+do
+ # skip unmanaged zones
+ if [ ${managed[i]} -eq 0 ]
+ then
+ i=$((i+1))
+ continue
+ fi
+
+ base_margin=$((min[i] / 4))
+ calc_margin=$(echo "${managed[i]} * $watermark_scale / 10000" | bc)
+ # round the value by adding 0.5 and truncating the decimal part
+ watermark_delta=$(echo "x=($extra_free_pg / ($vm_total_pages / 10000) + 0.5); scale = 0; x/1" | bc -l)
+ if [ $calc_margin -gt $base_margin ]
+ then
+ watermark_scale_new=$(echo "$watermark_scale + $watermark_delta" | bc)
+ else
+ watermark_scale_new=$(echo "$base_margin / (${managed[i]} / 10000) + $watermark_delta" | bc)
+ fi
+
+ if [ $max_watermark_scale -lt $watermark_scale_new ]
+ then
+ max_watermark_scale=$watermark_scale_new
+ fi
+
+ i=$((i+1))
+done
+
+echo $max_watermark_scale > /proc/sys/vm/watermark_scale_factor
diff --git a/init/first_stage_console.cpp b/init/first_stage_console.cpp
index e2ea0ab..67cac19 100644
--- a/init/first_stage_console.cpp
+++ b/init/first_stage_console.cpp
@@ -85,7 +85,10 @@
void StartConsole(const std::string& cmdline) {
bool console = KernelConsolePresent(cmdline);
+ // Use a simple sigchld handler -- first_stage_console doesn't need to track or log zombies
+ const struct sigaction chld_act { .sa_handler = SIG_DFL, .sa_flags = SA_NOCLDWAIT };
+ sigaction(SIGCHLD, &chld_act, nullptr);
pid_t pid = fork();
if (pid != 0) {
int status;
diff --git a/init/host_builtin_map.py b/init/host_builtin_map.py
index 6afcb17..41c86ac 100755
--- a/init/host_builtin_map.py
+++ b/init/host_builtin_map.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python
+#!/usr/bin/env python3
"""Generates the builtins map to be used by host_init_verifier.
It copies the builtin function map from builtins.cpp, then replaces do_xxx() functions with the
@@ -39,8 +39,7 @@
match = DO_REGEX.match(line)
if match:
if match.group(1) in check_functions:
- print line.replace('do_', 'check_'),
+ line = line.replace('do_', 'check_')
else:
- print FUNCTION_REGEX.sub('check_stub', line),
- else:
- print line,
+ line = FUNCTION_REGEX.sub('check_stub', line)
+ print(line, end=' ')
diff --git a/init/init.cpp b/init/init.cpp
index a7325ca..bde8e04 100644
--- a/init/init.cpp
+++ b/init/init.cpp
@@ -725,6 +725,40 @@
}
}
+static Result<void> ConnectEarlyStageSnapuserdAction(const BuiltinArguments& args) {
+ auto pid = GetSnapuserdFirstStagePid();
+ if (!pid) {
+ return {};
+ }
+
+ auto info = GetSnapuserdFirstStageInfo();
+ if (auto iter = std::find(info.begin(), info.end(), "socket"s); iter == info.end()) {
+ // snapuserd does not support socket handoff, so exit early.
+ return {};
+ }
+
+ // Socket handoff is supported.
+ auto svc = ServiceList::GetInstance().FindService("snapuserd");
+ if (!svc) {
+ LOG(FATAL) << "Failed to find snapuserd service entry";
+ }
+
+ svc->SetShutdownCritical();
+ svc->SetStartedInFirstStage(*pid);
+
+ svc = ServiceList::GetInstance().FindService("snapuserd_proxy");
+ if (!svc) {
+ LOG(FATAL) << "Failed find snapuserd_proxy service entry, merge will never initiate";
+ }
+ if (!svc->MarkSocketPersistent("snapuserd")) {
+ LOG(FATAL) << "Could not find snapuserd socket in snapuserd_proxy service entry";
+ }
+ if (auto result = svc->Start(); !result.ok()) {
+ LOG(FATAL) << "Could not start snapuserd_proxy: " << result.error();
+ }
+ return {};
+}
+
int SecondStageMain(int argc, char** argv) {
if (REBOOT_BOOTLOADER_ON_PANIC) {
InstallRebootSignalHandlers();
@@ -852,6 +886,7 @@
am.QueueBuiltinAction(SetupCgroupsAction, "SetupCgroups");
am.QueueBuiltinAction(SetKptrRestrictAction, "SetKptrRestrict");
am.QueueBuiltinAction(TestPerfEventSelinuxAction, "TestPerfEventSelinux");
+ am.QueueBuiltinAction(ConnectEarlyStageSnapuserdAction, "ConnectEarlyStageSnapuserd");
am.QueueEventTrigger("early-init");
// Queue an action that waits for coldboot done so we know ueventd has set up all of /dev...
diff --git a/init/mount_namespace.cpp b/init/mount_namespace.cpp
index 2a57808..593e5ae 100644
--- a/init/mount_namespace.cpp
+++ b/init/mount_namespace.cpp
@@ -82,110 +82,21 @@
return updatable;
}
-#ifdef ACTIVATE_FLATTENED_APEX
-
-static Result<void> MountDir(const std::string& path, const std::string& mount_path) {
- if (int ret = mkdir(mount_path.c_str(), 0755); ret != 0 && errno != EEXIST) {
- return ErrnoError() << "Could not create mount point " << mount_path;
- }
- if (mount(path.c_str(), mount_path.c_str(), nullptr, MS_BIND, nullptr) != 0) {
- return ErrnoError() << "Could not bind mount " << path << " to " << mount_path;
- }
- return {};
+static bool IsMicrodroid() {
+ static bool is_microdroid = android::base::GetProperty("ro.hardware", "") == "microdroid";
+ return is_microdroid;
}
-static Result<apex::proto::ApexManifest> GetApexManifest(const std::string& apex_dir) {
- const std::string manifest_path = apex_dir + "/apex_manifest.pb";
- std::string content;
- if (!android::base::ReadFileToString(manifest_path, &content)) {
- return Error() << "Failed to read manifest file: " << manifest_path;
- }
- apex::proto::ApexManifest manifest;
- if (!manifest.ParseFromString(content)) {
- return Error() << "Can't parse manifest file: " << manifest_path;
- }
- return manifest;
-}
-
-template <typename Fn>
-static Result<void> ActivateFlattenedApexesFrom(const std::string& from_dir,
- const std::string& to_dir, Fn on_activate) {
- std::unique_ptr<DIR, decltype(&closedir)> dir(opendir(from_dir.c_str()), closedir);
- if (!dir) {
- return {};
- }
- dirent* entry;
- std::vector<std::string> entries;
-
- while ((entry = readdir(dir.get())) != nullptr) {
- if (entry->d_name[0] == '.') continue;
- if (entry->d_type == DT_DIR) {
- entries.push_back(entry->d_name);
- }
- }
-
- std::sort(entries.begin(), entries.end());
- for (const auto& name : entries) {
- const std::string apex_path = from_dir + "/" + name;
- const auto apex_manifest = GetApexManifest(apex_path);
- if (!apex_manifest.ok()) {
- LOG(ERROR) << apex_path << " is not an APEX directory: " << apex_manifest.error();
- continue;
- }
- const std::string mount_path = to_dir + "/" + apex_manifest->name();
- if (auto result = MountDir(apex_path, mount_path); !result.ok()) {
- return result;
- }
- on_activate(apex_path, *apex_manifest);
- }
- return {};
-}
-
-static bool ActivateFlattenedApexesIfPossible() {
- if (IsRecoveryMode() || IsApexUpdatable()) {
- return true;
- }
-
- const std::string kApexTop = "/apex";
- const std::vector<std::string> kBuiltinDirsForApexes = {
- "/system/apex",
- "/system_ext/apex",
- "/product/apex",
- "/vendor/apex",
- };
-
- std::vector<com::android::apex::ApexInfo> apex_infos;
- auto on_activate = [&](const std::string& apex_path,
- const apex::proto::ApexManifest& apex_manifest) {
- apex_infos.emplace_back(apex_manifest.name(), apex_path, apex_path, apex_manifest.version(),
- apex_manifest.versionname(), /*isFactory=*/true, /*isActive=*/true,
- /* lastUpdateMillis= */ 0);
- };
-
- for (const auto& dir : kBuiltinDirsForApexes) {
- if (auto result = ActivateFlattenedApexesFrom(dir, kApexTop, on_activate); !result.ok()) {
- LOG(ERROR) << result.error();
- return false;
- }
- }
-
- std::ostringstream oss;
- com::android::apex::ApexInfoList apex_info_list(apex_infos);
- com::android::apex::write(oss, apex_info_list);
- const std::string kApexInfoList = kApexTop + "/apex-info-list.xml";
- if (!android::base::WriteStringToFile(oss.str(), kApexInfoList)) {
- PLOG(ERROR) << "Failed to write " << kApexInfoList;
- return false;
- }
- if (selinux_android_restorecon(kApexInfoList.c_str(), 0) != 0) {
- PLOG(ERROR) << "selinux_android_restorecon(" << kApexInfoList << ") failed";
- }
-
+// 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;
}
-#endif // ACTIVATE_FLATTENED_APEX
-
static android::base::unique_fd bootstrap_ns_fd;
static android::base::unique_fd default_ns_fd;
@@ -260,7 +171,7 @@
// number of essential APEXes (e.g. com.android.runtime) are activated.
// In the namespace for post-apexd processes, all APEXes are activated.
bool success = true;
- if (IsApexUpdatable() && !IsRecoveryMode()) {
+ if (NeedsTwoMountNamespaces()) {
// Creating a new namespace by cloning, saving, and switching back to
// the original namespace.
if (unshare(CLONE_NEWNS) == -1) {
@@ -279,9 +190,7 @@
default_ns_fd.reset(OpenMountNamespace());
default_ns_id = GetMountNamespaceId();
}
-#ifdef ACTIVATE_FLATTENED_APEX
- success &= ActivateFlattenedApexesIfPossible();
-#endif
+
LOG(INFO) << "SetupMountNamespaces done";
return success;
}
diff --git a/init/reboot.cpp b/init/reboot.cpp
index 0e788e4..1681627 100644
--- a/init/reboot.cpp
+++ b/init/reboot.cpp
@@ -639,6 +639,7 @@
abort();
}
+ bool do_shutdown_animation = GetBoolProperty("ro.init.shutdown_animation", false);
// watchdogd is a vendor specific component but should be alive to complete shutdown safely.
const std::set<std::string> to_starts{"watchdogd"};
std::set<std::string> stop_first;
@@ -652,6 +653,8 @@
<< "': " << result.error();
}
s->SetShutdownCritical();
+ } else if (do_shutdown_animation) {
+ continue;
} else if (s->IsShutdownCritical()) {
// Start shutdown critical service if not started.
if (auto result = s->Start(); !result.ok()) {
@@ -664,14 +667,13 @@
}
// remaining operations (specifically fsck) may take a substantial duration
- if (cmd == ANDROID_RB_POWEROFF || is_thermal_shutdown) {
+ if (!do_shutdown_animation && (cmd == ANDROID_RB_POWEROFF || is_thermal_shutdown)) {
TurnOffBacklight();
}
Service* boot_anim = ServiceList::GetInstance().FindService("bootanim");
Service* surface_flinger = ServiceList::GetInstance().FindService("surfaceflinger");
if (boot_anim != nullptr && surface_flinger != nullptr && surface_flinger->IsRunning()) {
- bool do_shutdown_animation = GetBoolProperty("ro.init.shutdown_animation", false);
if (do_shutdown_animation) {
SetProperty("service.bootanim.exit", "0");
@@ -1035,6 +1037,20 @@
return;
}
}
+ } else if (reboot_target == "quiescent") {
+ bootloader_message boot = {};
+ if (std::string err; !read_bootloader_message(&boot, &err)) {
+ LOG(ERROR) << "Failed to read bootloader message: " << err;
+ }
+ // Update the boot command field if it's empty, and preserve
+ // the other arguments in the bootloader message.
+ if (!CommandIsPresent(&boot)) {
+ strlcpy(boot.command, "boot-quiescent", sizeof(boot.command));
+ if (std::string err; !write_bootloader_message(boot, &err)) {
+ LOG(ERROR) << "Failed to set bootloader message: " << err;
+ return;
+ }
+ }
} else if (reboot_target == "sideload" || reboot_target == "sideload-auto-reboot" ||
reboot_target == "fastboot") {
std::string arg = reboot_target == "sideload-auto-reboot" ? "sideload_auto_reboot"
diff --git a/init/service.cpp b/init/service.cpp
index 5af81bf..489dd67 100644
--- a/init/service.cpp
+++ b/init/service.cpp
@@ -194,8 +194,6 @@
<< ") process group...";
int max_processes = 0;
int r;
-
- flags_ |= SVC_STOPPING;
if (signal == SIGTERM) {
r = killProcessGroupOnce(proc_attr_.uid, pid_, signal, &max_processes);
} else {
@@ -271,6 +269,9 @@
// Remove any socket resources we may have created.
for (const auto& socket : sockets_) {
+ if (socket.persist) {
+ continue;
+ }
auto path = ANDROID_SOCKET_DIR "/" + socket.name;
unlink(path.c_str());
}
@@ -279,8 +280,7 @@
f(siginfo);
}
- if ((siginfo.si_code != CLD_EXITED || siginfo.si_status != 0) && on_failure_reboot_target_ &&
- !(flags_ & SVC_STOPPING)) {
+ if ((siginfo.si_code != CLD_EXITED || siginfo.si_status != 0) && on_failure_reboot_target_) {
LOG(ERROR) << "Service with 'reboot_on_failure' option failed, shutting down system.";
trigger_shutdown(*on_failure_reboot_target_);
}
@@ -290,7 +290,7 @@
if (flags_ & SVC_TEMPORARY) return;
pid_ = 0;
- flags_ &= ~(SVC_RUNNING | SVC_STOPPING);
+ flags_ &= (~SVC_RUNNING);
start_order_ = 0;
// Oneshot processes go into the disabled state on exit,
@@ -412,10 +412,7 @@
}
bool disabled = (flags_ & (SVC_DISABLED | SVC_RESET));
- // Starting a service removes it from the disabled or reset state and
- // immediately takes it out of the restarting state if it was in there.
- flags_ &= (~(SVC_DISABLED | SVC_RESTARTING | SVC_RESET | SVC_RESTART | SVC_DISABLED_START |
- SVC_STOPPING));
+ ResetFlagsForStart();
// Running processes require no additional work --- if they're in the
// process of exiting, we've ensured that they will immediately restart
@@ -626,6 +623,23 @@
return {};
}
+void Service::SetStartedInFirstStage(pid_t pid) {
+ LOG(INFO) << "adding first-stage service '" << name_ << "'...";
+
+ time_started_ = boot_clock::now(); // not accurate, but doesn't matter here
+ pid_ = pid;
+ flags_ |= SVC_RUNNING;
+ start_order_ = next_start_order_++;
+
+ NotifyStateChange("running");
+}
+
+void Service::ResetFlagsForStart() {
+ // Starting a service removes it from the disabled or reset state and
+ // immediately takes it out of the restarting state if it was in there.
+ flags_ &= ~(SVC_DISABLED | SVC_RESTARTING | SVC_RESET | SVC_RESTART | SVC_DISABLED_START);
+}
+
Result<void> Service::StartIfNotDisabled() {
if (!(flags_ & SVC_DISABLED)) {
return Start();
@@ -796,5 +810,18 @@
nullptr, str_args, false);
}
+// This is used for snapuserd_proxy, which hands off a socket to snapuserd. It's
+// a special case to support the daemon launched in first-stage init. The persist
+// feature is not part of the init language and is only used here.
+bool Service::MarkSocketPersistent(const std::string& socket_name) {
+ for (auto& socket : sockets_) {
+ if (socket.name == socket_name) {
+ socket.persist = true;
+ return true;
+ }
+ }
+ return false;
+}
+
} // namespace init
} // namespace android
diff --git a/init/service.h b/init/service.h
index 89b1f09..ccf6899 100644
--- a/init/service.h
+++ b/init/service.h
@@ -54,7 +54,6 @@
// should not be killed during shutdown
#define SVC_TEMPORARY 0x1000 // This service was started by 'exec' and should be removed from the
// service list once it is reaped.
-#define SVC_STOPPING 0x2000 // service is being stopped by init
#define NR_SVC_SUPP_GIDS 12 // twelve supplementary groups
@@ -100,6 +99,8 @@
void AddReapCallback(std::function<void(const siginfo_t& siginfo)> callback) {
reap_callbacks_.emplace_back(std::move(callback));
}
+ void SetStartedInFirstStage(pid_t pid);
+ bool MarkSocketPersistent(const std::string& socket_name);
size_t CheckAllCommands() const { return onrestart_.CheckAllCommands(); }
static bool is_exec_service_running() { return is_exec_service_running_; }
@@ -145,6 +146,7 @@
void StopOrReset(int how);
void KillProcessGroup(int signal, bool report_oneshot = false);
void SetProcessAttributesAndCaps();
+ void ResetFlagsForStart();
static unsigned long next_start_order_;
static bool is_exec_service_running_;
diff --git a/init/service_utils.h b/init/service_utils.h
index 1e0b4bd..9b65dca 100644
--- a/init/service_utils.h
+++ b/init/service_utils.h
@@ -54,6 +54,7 @@
int perm = 0;
std::string context;
bool passcred = false;
+ bool persist = false;
// Create() creates the named unix domain socket in /dev/socket and returns a Descriptor object.
// It should be called when starting a service, before calling fork(), such that the socket is
diff --git a/init/snapuserd_transition.cpp b/init/snapuserd_transition.cpp
index 40467b7..b8c2fd2 100644
--- a/init/snapuserd_transition.cpp
+++ b/init/snapuserd_transition.cpp
@@ -33,10 +33,10 @@
#include <android-base/unique_fd.h>
#include <cutils/sockets.h>
#include <libsnapshot/snapshot.h>
-#include <libsnapshot/snapuserd_client.h>
#include <private/android_filesystem_config.h>
#include <procinfo/process_map.h>
#include <selinux/android.h>
+#include <snapuserd/snapuserd_client.h>
#include "block_dev_initializer.h"
#include "service_utils.h"
@@ -54,6 +54,7 @@
static constexpr char kSnapuserdPath[] = "/system/bin/snapuserd";
static constexpr char kSnapuserdFirstStagePidVar[] = "FIRST_STAGE_SNAPUSERD_PID";
static constexpr char kSnapuserdFirstStageFdVar[] = "FIRST_STAGE_SNAPUSERD_FD";
+static constexpr char kSnapuserdFirstStageInfoVar[] = "FIRST_STAGE_SNAPUSERD_INFO";
static constexpr char kSnapuserdLabel[] = "u:object_r:snapuserd_exec:s0";
static constexpr char kSnapuserdSocketLabel[] = "u:object_r:snapuserd_socket:s0";
@@ -87,6 +88,14 @@
_exit(127);
}
+ auto client = SnapuserdClient::Connect(android::snapshot::kSnapuserdSocket, 10s);
+ if (!client) {
+ LOG(FATAL) << "Could not connect to first-stage snapuserd";
+ }
+ if (client->SupportsSecondStageSocketHandoff()) {
+ setenv(kSnapuserdFirstStageInfoVar, "socket", 1);
+ }
+
setenv(kSnapuserdFirstStagePidVar, std::to_string(pid).c_str(), 1);
LOG(INFO) << "Relaunched snapuserd with pid: " << pid;
@@ -328,5 +337,13 @@
return GetSnapuserdFirstStagePid().has_value();
}
+std::vector<std::string> GetSnapuserdFirstStageInfo() {
+ const char* pid_str = getenv(kSnapuserdFirstStageInfoVar);
+ if (!pid_str) {
+ return {};
+ }
+ return android::base::Split(pid_str, ",");
+}
+
} // namespace init
} // namespace android
diff --git a/init/snapuserd_transition.h b/init/snapuserd_transition.h
index a5ab652..62aee83 100644
--- a/init/snapuserd_transition.h
+++ b/init/snapuserd_transition.h
@@ -76,6 +76,9 @@
// Return the pid of the first-stage instances of snapuserd, if it was started.
std::optional<pid_t> GetSnapuserdFirstStagePid();
+// Return snapuserd info strings that were set during first-stage init.
+std::vector<std::string> GetSnapuserdFirstStageInfo();
+
// Save an open fd to /system/bin (in the ramdisk) into an environment. This is
// used to later execveat() snapuserd.
void SaveRamdiskPathToSnapuserd();
diff --git a/libcrypto_utils/Android.bp b/libcrypto_utils/Android.bp
index c8a183b..a9f6958 100644
--- a/libcrypto_utils/Android.bp
+++ b/libcrypto_utils/Android.bp
@@ -22,6 +22,7 @@
name: "libcrypto_utils",
vendor_available: true,
ramdisk_available: true,
+ vendor_ramdisk_available: true,
recovery_available: true,
vndk: {
enabled: true,
diff --git a/libcutils/Android.bp b/libcutils/Android.bp
index 68b21c6..0d9f2c7 100644
--- a/libcutils/Android.bp
+++ b/libcutils/Android.bp
@@ -354,18 +354,3 @@
defaults: ["libcutils_test_static_defaults"],
test_config: "KernelLibcutilsTest.xml",
}
-
-rust_bindgen {
- name: "libcutils_bindgen",
- wrapper_src: "rust/cutils.h",
- crate_name: "cutils_bindgen",
- source_stem: "bindings",
- local_include_dirs: ["include"],
- bindgen_flags: [
- "--allowlist-function", "multiuser_get_app_id",
- "--allowlist-function", "multiuser_get_uid",
- "--allowlist-function", "multiuser_get_user_id",
- "--allowlist-var", "AID_KEYSTORE",
- "--allowlist-var", "AID_USER_OFFSET",
- ],
-}
diff --git a/libcutils/TEST_MAPPING b/libcutils/TEST_MAPPING
new file mode 100644
index 0000000..e512ab7
--- /dev/null
+++ b/libcutils/TEST_MAPPING
@@ -0,0 +1,7 @@
+{
+ "presubmit": [
+ {
+ "name": "libcutils_test"
+ }
+ ]
+}
diff --git a/libcutils/include/cutils/trace.h b/libcutils/include/cutils/trace.h
index ef426ff..24c6ae6 100644
--- a/libcutils/include/cutils/trace.h
+++ b/libcutils/include/cutils/trace.h
@@ -75,8 +75,7 @@
#define ATRACE_TAG_AIDL (1<<24)
#define ATRACE_TAG_NNAPI (1<<25)
#define ATRACE_TAG_RRO (1<<26)
-#define ATRACE_TAG_SYSPROP (1<<27)
-#define ATRACE_TAG_LAST ATRACE_TAG_SYSPROP
+#define ATRACE_TAG_LAST ATRACE_TAG_RRO
// Reserved for initialization.
#define ATRACE_TAG_NOT_READY (1ULL<<63)
diff --git a/libcutils/rust/cutils.h b/libcutils/rust/cutils.h
deleted file mode 100644
index 9b78af6..0000000
--- a/libcutils/rust/cutils.h
+++ /dev/null
@@ -1,4 +0,0 @@
-#pragma once
-
-#include <cutils/multiuser.h>
-#include <private/android_filesystem_config.h>
diff --git a/libkeyutils/Android.bp b/libkeyutils/Android.bp
index fbb8049..3af07b4 100644
--- a/libkeyutils/Android.bp
+++ b/libkeyutils/Android.bp
@@ -14,6 +14,7 @@
cflags: ["-Werror"],
defaults: ["linux_bionic_supported"],
ramdisk_available: true,
+ vendor_ramdisk_available: true,
recovery_available: true,
export_include_dirs: ["include/"],
local_include_dirs: ["include/"],
diff --git a/libmodprobe/TEST_MAPPING b/libmodprobe/TEST_MAPPING
new file mode 100644
index 0000000..526b1e4
--- /dev/null
+++ b/libmodprobe/TEST_MAPPING
@@ -0,0 +1,7 @@
+{
+ "presubmit": [
+ {
+ "name": "libmodprobe_tests"
+ }
+ ]
+}
diff --git a/libpackagelistparser/TEST_MAPPING b/libpackagelistparser/TEST_MAPPING
new file mode 100644
index 0000000..51773f9
--- /dev/null
+++ b/libpackagelistparser/TEST_MAPPING
@@ -0,0 +1,7 @@
+{
+ "presubmit": [
+ {
+ "name": "libpackagelistparser_test"
+ }
+ ]
+}
diff --git a/libprocessgroup/cgroup_map.cpp b/libprocessgroup/cgroup_map.cpp
index 5ca0967..0734f25 100644
--- a/libprocessgroup/cgroup_map.cpp
+++ b/libprocessgroup/cgroup_map.cpp
@@ -43,6 +43,7 @@
using android::base::GetBoolProperty;
using android::base::StringPrintf;
using android::base::unique_fd;
+using android::base::WriteStringToFile;
static constexpr const char* CGROUP_PROCS_FILE = "/cgroup.procs";
static constexpr const char* CGROUP_TASKS_FILE = "/tasks";
@@ -202,3 +203,21 @@
return CgroupController(nullptr);
}
+
+int CgroupMap::ActivateControllers(const std::string& path) const {
+ if (__builtin_available(android 30, *)) {
+ auto controller_count = ACgroupFile_getControllerCount();
+ for (uint32_t i = 0; i < controller_count; ++i) {
+ const ACgroupController* controller = ACgroupFile_getController(i);
+ if (ACgroupController_getFlags(controller) &
+ CGROUPRC_CONTROLLER_FLAG_NEEDS_ACTIVATION) {
+ std::string str = std::string("+") + ACgroupController_getName(controller);
+ if (!WriteStringToFile(str, path + "/cgroup.subtree_control")) {
+ return -errno;
+ }
+ }
+ }
+ return 0;
+ }
+ return -ENOSYS;
+}
diff --git a/libprocessgroup/cgroup_map.h b/libprocessgroup/cgroup_map.h
index 427d71b..22d717b 100644
--- a/libprocessgroup/cgroup_map.h
+++ b/libprocessgroup/cgroup_map.h
@@ -62,6 +62,7 @@
static CgroupMap& GetInstance();
CgroupController FindController(const std::string& name) const;
+ int ActivateControllers(const std::string& path) const;
private:
bool loaded_ = false;
diff --git a/libprocessgroup/processgroup.cpp b/libprocessgroup/processgroup.cpp
index c824376..faf945c 100644
--- a/libprocessgroup/processgroup.cpp
+++ b/libprocessgroup/processgroup.cpp
@@ -416,13 +416,15 @@
return KillProcessGroup(uid, initialPid, signal, 0 /*retries*/, max_processes);
}
-static int createProcessGroupInternal(uid_t uid, int initialPid, std::string cgroup) {
+static int createProcessGroupInternal(uid_t uid, int initialPid, std::string cgroup,
+ bool activate_controllers) {
auto uid_path = ConvertUidToPath(cgroup.c_str(), uid);
struct stat cgroup_stat;
mode_t cgroup_mode = 0750;
gid_t cgroup_uid = AID_SYSTEM;
uid_t cgroup_gid = AID_SYSTEM;
+ int ret = 0;
if (stat(cgroup.c_str(), &cgroup_stat) == 1) {
PLOG(ERROR) << "Failed to get stats for " << cgroup;
@@ -436,6 +438,13 @@
PLOG(ERROR) << "Failed to make and chown " << uid_path;
return -errno;
}
+ if (activate_controllers) {
+ ret = CgroupMap::GetInstance().ActivateControllers(uid_path);
+ if (ret) {
+ LOG(ERROR) << "Failed to activate controllers in " << uid_path;
+ return ret;
+ }
+ }
auto uid_pid_path = ConvertUidPidToPath(cgroup.c_str(), uid, initialPid);
@@ -446,7 +455,6 @@
auto uid_pid_procs_file = uid_pid_path + PROCESSGROUP_CGROUP_PROCS_FILE;
- int ret = 0;
if (!WriteStringToFile(std::to_string(initialPid), uid_pid_procs_file)) {
ret = -errno;
PLOG(ERROR) << "Failed to write '" << initialPid << "' to " << uid_pid_procs_file;
@@ -466,14 +474,14 @@
if (isMemoryCgroupSupported() && UsePerAppMemcg()) {
CgroupGetControllerPath("memory", &cgroup);
cgroup += "/apps";
- int ret = createProcessGroupInternal(uid, initialPid, cgroup);
+ int ret = createProcessGroupInternal(uid, initialPid, cgroup, false);
if (ret != 0) {
return ret;
}
}
CgroupGetControllerPath(CGROUPV2_CONTROLLER_NAME, &cgroup);
- return createProcessGroupInternal(uid, initialPid, cgroup);
+ return createProcessGroupInternal(uid, initialPid, cgroup, true);
}
static bool SetProcessGroupValue(int tid, const std::string& attr_name, int64_t value) {
diff --git a/libprocessgroup/profiles/task_profiles.json b/libprocessgroup/profiles/task_profiles.json
index b5fa475..449a505 100644
--- a/libprocessgroup/profiles/task_profiles.json
+++ b/libprocessgroup/profiles/task_profiles.json
@@ -584,7 +584,7 @@
}
}
]
- },
+ }
],
"AggregateProfiles": [
@@ -635,6 +635,10 @@
{
"Name": "CPUSET_SP_RESTRICTED",
"Profiles": [ "ServiceCapacityRestricted", "TimerSlackNormal" ]
+ },
+ {
+ "Name": "Dex2OatBootComplete",
+ "Profiles": [ "SCHED_SP_BACKGROUND" ]
}
]
}
diff --git a/libsparse/Android.bp b/libsparse/Android.bp
index 0b4b640..3f9aeb2 100644
--- a/libsparse/Android.bp
+++ b/libsparse/Android.bp
@@ -85,11 +85,11 @@
srcs: ["simg_dump.py"],
version: {
py2: {
- embedded_launcher: true,
- enabled: true,
+ enabled: false,
},
py3: {
- enabled: false,
+ embedded_launcher: true,
+ enabled: true,
},
},
}
diff --git a/libsparse/simg_dump.py b/libsparse/simg_dump.py
index 82a03ad..b0b3b22 100755
--- a/libsparse/simg_dump.py
+++ b/libsparse/simg_dump.py
@@ -1,4 +1,4 @@
-#! /usr/bin/env python
+#! /usr/bin/env python3
# Copyright (C) 2012 The Android Open Source Project
#
@@ -14,7 +14,6 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-from __future__ import print_function
import csv
import getopt
import hashlib
@@ -47,7 +46,7 @@
opts, args = getopt.getopt(sys.argv[1:],
"vsc:",
["verbose", "showhash", "csvfile"])
- except getopt.GetoptError, e:
+ except getopt.GetoptError as e:
print(e)
usage(me)
for o, a in opts:
diff --git a/libutils/RefBase.cpp b/libutils/RefBase.cpp
index b57e287..0518927 100644
--- a/libutils/RefBase.cpp
+++ b/libutils/RefBase.cpp
@@ -170,7 +170,7 @@
: mStrong(INITIAL_STRONG_VALUE)
, mWeak(0)
, mBase(base)
- , mFlags(0)
+ , mFlags(OBJECT_LIFETIME_STRONG)
{
}
@@ -189,7 +189,7 @@
: mStrong(INITIAL_STRONG_VALUE)
, mWeak(0)
, mBase(base)
- , mFlags(0)
+ , mFlags(OBJECT_LIFETIME_STRONG)
, mStrongRefs(NULL)
, mWeakRefs(NULL)
, mTrackEnabled(!!DEBUG_REFS_ENABLED_BY_DEFAULT)
diff --git a/libutils/String8.cpp b/libutils/String8.cpp
index 195e122..8511da9 100644
--- a/libutils/String8.cpp
+++ b/libutils/String8.cpp
@@ -429,24 +429,17 @@
// ---------------------------------------------------------------------------
// Path functions
-void String8::setPathName(const char* name)
-{
- setPathName(name, strlen(name));
-}
-
-void String8::setPathName(const char* name, size_t len)
-{
- char* buf = lockBuffer(len);
+static void setPathName(String8& s, const char* name) {
+ size_t len = strlen(name);
+ char* buf = s.lockBuffer(len);
memcpy(buf, name, len);
// remove trailing path separator, if present
- if (len > 0 && buf[len-1] == OS_PATH_SEPARATOR)
- len--;
-
+ if (len > 0 && buf[len - 1] == OS_PATH_SEPARATOR) len--;
buf[len] = '\0';
- unlockBuffer(len);
+ s.unlockBuffer(len);
}
String8 String8::getPathLeaf(void) const
@@ -559,7 +552,7 @@
size_t len = length();
if (len == 0) {
// no existing filename, just use the new one
- setPathName(name);
+ setPathName(*this, name);
return *this;
}
@@ -579,7 +572,7 @@
return *this;
} else {
- setPathName(name);
+ setPathName(*this, name);
return *this;
}
}
diff --git a/libutils/String8_fuzz.cpp b/libutils/String8_fuzz.cpp
index a45d675..faf49b6 100644
--- a/libutils/String8_fuzz.cpp
+++ b/libutils/String8_fuzz.cpp
@@ -91,10 +91,6 @@
},
[](FuzzedDataProvider* dataProvider, android::String8* str1,
android::String8*) -> void {
- str1->setPathName(dataProvider->ConsumeBytesWithTerminator<char>(5).data());
- },
- [](FuzzedDataProvider* dataProvider, android::String8* str1,
- android::String8*) -> void {
str1->appendPath(dataProvider->ConsumeBytesWithTerminator<char>(5).data());
},
};
diff --git a/libutils/TEST_MAPPING b/libutils/TEST_MAPPING
new file mode 100644
index 0000000..c8ef45c
--- /dev/null
+++ b/libutils/TEST_MAPPING
@@ -0,0 +1,7 @@
+{
+ "presubmit": [
+ {
+ "name": "libutils_test"
+ }
+ ]
+}
diff --git a/libutils/include/utils/String8.h b/libutils/include/utils/String8.h
index cee5dc6..8b2dcf9 100644
--- a/libutils/include/utils/String8.h
+++ b/libutils/include/utils/String8.h
@@ -137,14 +137,6 @@
*/
/*
- * Set the filename field to a specific value.
- *
- * Normalizes the filename, removing a trailing '/' if present.
- */
- void setPathName(const char* name);
- void setPathName(const char* name, size_t numChars);
-
- /*
* Get just the filename component.
*
* "/tmp/foo/bar.c" --> "bar.c"
diff --git a/property_service/TEST_MAPPING b/property_service/TEST_MAPPING
new file mode 100644
index 0000000..fcdc86a
--- /dev/null
+++ b/property_service/TEST_MAPPING
@@ -0,0 +1,7 @@
+{
+ "presubmit": [
+ {
+ "name": "propertyinfoserializer_tests"
+ }
+ ]
+}
diff --git a/rootdir/init.rc b/rootdir/init.rc
index a8bef42..9371617 100644
--- a/rootdir/init.rc
+++ b/rootdir/init.rc
@@ -591,9 +591,10 @@
# Load trusted keys from dm-verity protected partitions
exec -- /system/bin/fsverity_init --load-verified-keys
+on late-fs && property:ro.product.cpu.abilist64=*
# Set up a tracing instance for system_server to monitor error_report_end events.
# These are sent by kernel tools like KASAN and KFENCE when a memory corruption
- # is detected.
+ # is detected. This is only needed for 64-bit systems.
mkdir /sys/kernel/tracing/instances/bootreceiver 0700 system system
restorecon_recursive /sys/kernel/tracing/instances/bootreceiver
write /sys/kernel/tracing/instances/bootreceiver/buffer_size_kb 1
@@ -750,8 +751,7 @@
mkdir /data/misc/odrefresh 0777 system system
# directory used for on-device signing key blob
mkdir /data/misc/odsign 0700 root root
- # Directory for VirtualizationService temporary image files. Ensure that it is empty.
- exec -- /bin/rm -rf /data/misc/virtualizationservice
+ # Directory for VirtualizationService temporary image files.
mkdir /data/misc/virtualizationservice 0700 virtualizationservice virtualizationservice
mkdir /data/preloads 0775 system system encryption=None
@@ -1138,7 +1138,7 @@
# and chown/chmod does not work for /proc/sys/ entries.
# So proxy writes through init.
on property:sys.sysctl.extra_free_kbytes=*
- write /proc/sys/vm/extra_free_kbytes ${sys.sysctl.extra_free_kbytes}
+ exec -- /system/bin/extra_free_kbytes.sh ${sys.sysctl.extra_free_kbytes}
# Allow users to drop caches
on property:perf.drop_caches=3
diff --git a/toolbox/generate-input.h-labels.py b/toolbox/generate-input.h-labels.py
index c0e9fce..20db638 100755
--- a/toolbox/generate-input.h-labels.py
+++ b/toolbox/generate-input.h-labels.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python
+#!/usr/bin/env python3
#
# Copyright (C) 2015 The Android Open Source Project
#
@@ -16,7 +16,7 @@
#
# pylint: disable=bad-indentation,bad-continuation
-from __future__ import print_function
+
import os
import re
import sys
diff --git a/trusty/confirmationui/fuzz/msg_fuzzer.cpp b/trusty/confirmationui/fuzz/msg_fuzzer.cpp
index 8e4443c..ee55f82 100644
--- a/trusty/confirmationui/fuzz/msg_fuzzer.cpp
+++ b/trusty/confirmationui/fuzz/msg_fuzzer.cpp
@@ -37,7 +37,7 @@
#define CONFIRMATIONUI_MODULE_NAME "confirmationui.syms.elf"
/* A request to render to screen may take a while. */
-const size_t kTimeoutSeconds = 30;
+const size_t kTimeoutSeconds = 60;
/* ConfirmationUI TA's UUID is 7dee2364-c036-425b-b086-df0f6c233c1b */
static struct uuid confirmationui_uuid = {
diff --git a/trusty/keymaster/Android.bp b/trusty/keymaster/Android.bp
index 58dfa94..155363c 100644
--- a/trusty/keymaster/Android.bp
+++ b/trusty/keymaster/Android.bp
@@ -100,14 +100,15 @@
"ipc/trusty_keymaster_ipc.cpp",
"keymint/TrustyKeyMintDevice.cpp",
"keymint/TrustyKeyMintOperation.cpp",
+ "keymint/TrustyRemotelyProvisionedComponentDevice.cpp",
"keymint/TrustySecureClock.cpp",
"keymint/TrustySharedSecret.cpp",
"keymint/service.cpp",
],
shared_libs: [
- "android.hardware.security.keymint-V1-ndk_platform",
- "android.hardware.security.secureclock-V1-ndk_platform",
- "android.hardware.security.sharedsecret-V1-ndk_platform",
+ "android.hardware.security.keymint-V1-ndk",
+ "android.hardware.security.secureclock-V1-ndk",
+ "android.hardware.security.sharedsecret-V1-ndk",
"lib_android_keymaster_keymint_utils",
"libbase",
"libbinder_ndk",
@@ -118,7 +119,6 @@
"libtrusty",
],
required: [
- "RemoteProvisioner",
"android.hardware.hardware_keystore.xml",
],
}
@@ -129,6 +129,27 @@
src: "set_attestation_key/keymaster_soft_attestation_keys.xml",
}
+cc_library {
+ name: "libtrusty_ipc",
+ vendor: true,
+ srcs: ["ipc/trusty_keymaster_ipc.cpp"],
+ local_include_dirs: ["include"],
+ shared_libs: [
+ "libc",
+ "libcrypto",
+ "liblog",
+ "libtrusty",
+ "libhardware",
+ "libkeymaster_messages",
+ "libxml2",
+ ],
+ export_include_dirs: ["include"],
+ cflags: [
+ "-Wall",
+ "-Werror",
+ ],
+}
+
cc_binary {
name: "trusty_keymaster_set_attestation_key",
vendor: true,
diff --git a/trusty/keymaster/TrustyKeymaster.cpp b/trusty/keymaster/TrustyKeymaster.cpp
index ef5fc3f..cdfbd90 100644
--- a/trusty/keymaster/TrustyKeymaster.cpp
+++ b/trusty/keymaster/TrustyKeymaster.cpp
@@ -79,6 +79,16 @@
return -1;
}
+ // Set the vendor patchlevel to value retrieved from system property (which
+ // requires SELinux permission).
+ ConfigureVendorPatchlevelRequest vendor_req(message_version());
+ vendor_req.vendor_patchlevel = GetVendorPatchlevel();
+ ConfigureVendorPatchlevelResponse vendor_rsp = ConfigureVendorPatchlevel(vendor_req);
+ if (vendor_rsp.error != KM_ERROR_OK) {
+ LOG(ERROR) << "Failed to configure keymaster vendor patchlevel: " << vendor_rsp.error;
+ // Don't fail if this message isn't understood.
+ }
+
return 0;
}
@@ -158,6 +168,16 @@
}
}
+void TrustyKeymaster::GenerateRkpKey(const GenerateRkpKeyRequest& request,
+ GenerateRkpKeyResponse* response) {
+ ForwardCommand(KM_GENERATE_RKP_KEY, request, response);
+}
+
+void TrustyKeymaster::GenerateCsr(const GenerateCsrRequest& request,
+ GenerateCsrResponse* response) {
+ ForwardCommand(KM_GENERATE_CSR, request, response);
+}
+
void TrustyKeymaster::GetKeyCharacteristics(const GetKeyCharacteristicsRequest& request,
GetKeyCharacteristicsResponse* response) {
ForwardCommand(KM_GET_KEY_CHARACTERISTICS, request, response);
@@ -252,4 +272,11 @@
return response;
}
+ConfigureVendorPatchlevelResponse TrustyKeymaster::ConfigureVendorPatchlevel(
+ const ConfigureVendorPatchlevelRequest& request) {
+ ConfigureVendorPatchlevelResponse response(message_version());
+ ForwardCommand(KM_CONFIGURE_VENDOR_PATCHLEVEL, request, &response);
+ return response;
+}
+
} // namespace keymaster
diff --git a/trusty/keymaster/include/trusty_keymaster/TrustyKeymaster.h b/trusty/keymaster/include/trusty_keymaster/TrustyKeymaster.h
index 45ebf7f..f80e02f 100644
--- a/trusty/keymaster/include/trusty_keymaster/TrustyKeymaster.h
+++ b/trusty/keymaster/include/trusty_keymaster/TrustyKeymaster.h
@@ -42,6 +42,8 @@
void AddRngEntropy(const AddEntropyRequest& request, AddEntropyResponse* response);
void Configure(const ConfigureRequest& request, ConfigureResponse* response);
void GenerateKey(const GenerateKeyRequest& request, GenerateKeyResponse* response);
+ void GenerateRkpKey(const GenerateRkpKeyRequest& request, GenerateRkpKeyResponse* response);
+ void GenerateCsr(const GenerateCsrRequest& request, GenerateCsrResponse* response);
void GetKeyCharacteristics(const GetKeyCharacteristicsRequest& request,
GetKeyCharacteristicsResponse* response);
void ImportKey(const ImportKeyRequest& request, ImportKeyResponse* response);
@@ -62,6 +64,8 @@
GetVersion2Response GetVersion2(const GetVersion2Request& request);
EarlyBootEndedResponse EarlyBootEnded();
DeviceLockedResponse DeviceLocked(const DeviceLockedRequest& request);
+ ConfigureVendorPatchlevelResponse ConfigureVendorPatchlevel(
+ const ConfigureVendorPatchlevelRequest& request);
uint32_t message_version() const { return message_version_; }
diff --git a/trusty/keymaster/include/trusty_keymaster/TrustyRemotelyProvisionedComponentDevice.h b/trusty/keymaster/include/trusty_keymaster/TrustyRemotelyProvisionedComponentDevice.h
new file mode 100644
index 0000000..d544b51
--- /dev/null
+++ b/trusty/keymaster/include/trusty_keymaster/TrustyRemotelyProvisionedComponentDevice.h
@@ -0,0 +1,53 @@
+/*
+ * Copyright 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.
+ */
+
+#pragma once
+
+#include <aidl/android/hardware/security/keymint/BnRemotelyProvisionedComponent.h>
+#include <aidl/android/hardware/security/keymint/RpcHardwareInfo.h>
+#include <aidl/android/hardware/security/keymint/SecurityLevel.h>
+
+#include <trusty_keymaster/TrustyKeymaster.h>
+
+namespace aidl::android::hardware::security::keymint::trusty {
+
+using ::keymaster::TrustyKeymaster;
+using ::ndk::ScopedAStatus;
+using ::std::shared_ptr;
+
+class TrustyRemotelyProvisionedComponentDevice : public BnRemotelyProvisionedComponent {
+ public:
+ explicit TrustyRemotelyProvisionedComponentDevice(shared_ptr<TrustyKeymaster> impl)
+ : impl_(std::move(impl)) {}
+ virtual ~TrustyRemotelyProvisionedComponentDevice() = default;
+
+ ScopedAStatus getHardwareInfo(RpcHardwareInfo* info) override;
+
+ ScopedAStatus generateEcdsaP256KeyPair(bool testMode, MacedPublicKey* macedPublicKey,
+ std::vector<uint8_t>* privateKeyHandle) override;
+
+ ScopedAStatus generateCertificateRequest(bool testMode,
+ const std::vector<MacedPublicKey>& keysToSign,
+ const std::vector<uint8_t>& endpointEncCertChain,
+ const std::vector<uint8_t>& challenge,
+ DeviceInfo* deviceInfo, ProtectedData* protectedData,
+ std::vector<uint8_t>* keysToSignMac) override;
+
+ private:
+ std::shared_ptr<::keymaster::TrustyKeymaster> impl_;
+};
+
+} // namespace aidl::android::hardware::security::keymint::trusty
diff --git a/trusty/keymaster/include/trusty_keymaster/ipc/keymaster_ipc.h b/trusty/keymaster/include/trusty_keymaster/ipc/keymaster_ipc.h
index a1229a3..fa475ae 100644
--- a/trusty/keymaster/include/trusty_keymaster/ipc/keymaster_ipc.h
+++ b/trusty/keymaster/include/trusty_keymaster/ipc/keymaster_ipc.h
@@ -56,6 +56,9 @@
KM_GET_VERSION_2 = (28 << KEYMASTER_REQ_SHIFT),
KM_EARLY_BOOT_ENDED = (29 << KEYMASTER_REQ_SHIFT),
KM_DEVICE_LOCKED = (30 << KEYMASTER_REQ_SHIFT),
+ KM_GENERATE_RKP_KEY = (31 << KEYMASTER_REQ_SHIFT),
+ KM_GENERATE_CSR = (32 << KEYMASTER_REQ_SHIFT),
+ KM_CONFIGURE_VENDOR_PATCHLEVEL = (33 << KEYMASTER_REQ_SHIFT),
// Bootloader/provisioning calls.
KM_SET_BOOT_PARAMS = (0x1000 << KEYMASTER_REQ_SHIFT),
@@ -69,6 +72,8 @@
KM_SET_PRODUCT_ID = (0x9000 << KEYMASTER_REQ_SHIFT),
KM_CLEAR_ATTESTATION_CERT_CHAIN = (0xa000 << KEYMASTER_REQ_SHIFT),
KM_SET_WRAPPED_ATTESTATION_KEY = (0xb000 << KEYMASTER_REQ_SHIFT),
+ KM_SET_ATTESTATION_IDS = (0xc000 << KEYMASTER_REQ_SHIFT),
+ KM_CONFIGURE_BOOT_PATCHLEVEL = (0xd000 << KEYMASTER_REQ_SHIFT),
};
#ifdef __ANDROID__
diff --git a/trusty/keymaster/ipc/trusty_keymaster_ipc.cpp b/trusty/keymaster/ipc/trusty_keymaster_ipc.cpp
index 0956fe6..2d44009 100644
--- a/trusty/keymaster/ipc/trusty_keymaster_ipc.cpp
+++ b/trusty/keymaster/ipc/trusty_keymaster_ipc.cpp
@@ -25,6 +25,8 @@
#include <unistd.h>
#include <algorithm>
+#include <variant>
+#include <vector>
#include <log/log.h>
#include <trusty/tipc.h>
@@ -46,8 +48,27 @@
return 0;
}
-int trusty_keymaster_call(uint32_t cmd, void* in, uint32_t in_size, uint8_t* out,
- uint32_t* out_size) {
+class VectorEraser {
+ public:
+ VectorEraser(std::vector<uint8_t>* v) : _v(v) {}
+ ~VectorEraser() {
+ if (_v) {
+ std::fill(const_cast<volatile uint8_t*>(_v->data()),
+ const_cast<volatile uint8_t*>(_v->data() + _v->size()), 0);
+ }
+ }
+ void disarm() { _v = nullptr; }
+ VectorEraser(const VectorEraser&) = delete;
+ VectorEraser& operator=(const VectorEraser&) = delete;
+ VectorEraser(VectorEraser&& other) = delete;
+ VectorEraser& operator=(VectorEraser&&) = delete;
+
+ private:
+ std::vector<uint8_t>* _v;
+};
+
+std::variant<int, std::vector<uint8_t>> trusty_keymaster_call_2(uint32_t cmd, void* in,
+ uint32_t in_size) {
if (handle_ < 0) {
ALOGE("not connected\n");
return -EINVAL;
@@ -70,15 +91,38 @@
ALOGE("failed to send cmd (%d) to %s: %s\n", cmd, KEYMASTER_PORT, strerror(errno));
return -errno;
}
- size_t out_max_size = *out_size;
- *out_size = 0;
+
+ std::vector<uint8_t> out(TRUSTY_KEYMASTER_RECV_BUF_SIZE);
+ VectorEraser out_eraser(&out);
+ uint8_t* write_pos = out.data();
+ uint8_t* out_end = out.data() + out.size();
+
struct iovec iov[2];
struct keymaster_message header;
iov[0] = {.iov_base = &header, .iov_len = sizeof(struct keymaster_message)};
while (true) {
- iov[1] = {.iov_base = out + *out_size,
- .iov_len = std::min<uint32_t>(KEYMASTER_MAX_BUFFER_LENGTH,
- out_max_size - *out_size)};
+ if (out_end - write_pos < KEYMASTER_MAX_BUFFER_LENGTH) {
+ // In stead of using std::vector.resize(), allocate a new one to have chance
+ // at zeroing the old buffer.
+ std::vector<uint8_t> new_out(out.size() + KEYMASTER_MAX_BUFFER_LENGTH);
+ // After the swap below this erases the old out buffer.
+ VectorEraser new_out_eraser(&new_out);
+ std::copy(out.data(), write_pos, new_out.begin());
+
+ auto write_offset = write_pos - out.data();
+
+ std::swap(new_out, out);
+
+ write_pos = out.data() + write_offset;
+ out_end = out.data() + out.size();
+ }
+ size_t buffer_size = 0;
+ if (__builtin_sub_overflow(reinterpret_cast<uintptr_t>(out_end),
+ reinterpret_cast<uintptr_t>(write_pos), &buffer_size)) {
+ return -EOVERFLOW;
+ }
+ iov[1] = {.iov_base = write_pos, .iov_len = buffer_size};
+
rc = readv(handle_, iov, 2);
if (rc < 0) {
ALOGE("failed to retrieve response for cmd (%d) to %s: %s\n", cmd, KEYMASTER_PORT,
@@ -95,13 +139,36 @@
ALOGE("invalid command (%d)", header.cmd);
return -EINVAL;
}
- *out_size += ((size_t)rc - sizeof(struct keymaster_message));
+ write_pos += ((size_t)rc - sizeof(struct keymaster_message));
if (header.cmd & KEYMASTER_STOP_BIT) {
break;
}
}
- return rc;
+ out.resize(write_pos - out.data());
+ out_eraser.disarm();
+ return out;
+}
+
+int trusty_keymaster_call(uint32_t cmd, void* in, uint32_t in_size, uint8_t* out,
+ uint32_t* out_size) {
+ auto result = trusty_keymaster_call_2(cmd, in, in_size);
+ if (auto out_buffer = std::get_if<std::vector<uint8_t>>(&result)) {
+ if (out_buffer->size() <= *out_size) {
+ std::copy(out_buffer->begin(), out_buffer->end(), out);
+ std::fill(const_cast<volatile uint8_t*>(&*out_buffer->begin()),
+ const_cast<volatile uint8_t*>(&*out_buffer->end()), 0);
+
+ *out_size = out_buffer->size();
+ return 0;
+ } else {
+ ALOGE("Message was to large (%zu) for the provided buffer (%u)", out_buffer->size(),
+ *out_size);
+ return -EMSGSIZE;
+ }
+ } else {
+ return std::get<int>(result);
+ }
}
void trusty_keymaster_disconnect() {
@@ -155,28 +222,27 @@
req.Serialize(send_buf, send_buf + req_size);
// Send it
- uint8_t recv_buf[TRUSTY_KEYMASTER_RECV_BUF_SIZE];
- keymaster::Eraser recv_buf_eraser(recv_buf, TRUSTY_KEYMASTER_RECV_BUF_SIZE);
- uint32_t rsp_size = TRUSTY_KEYMASTER_RECV_BUF_SIZE;
- int rc = trusty_keymaster_call(command, send_buf, req_size, recv_buf, &rsp_size);
- if (rc < 0) {
+ auto response = trusty_keymaster_call_2(command, send_buf, req_size);
+ if (auto response_buffer = std::get_if<std::vector<uint8_t>>(&response)) {
+ keymaster::Eraser response_buffer_erasor(response_buffer->data(), response_buffer->size());
+ ALOGV("Received %zu byte response\n", response_buffer->size());
+
+ const uint8_t* p = response_buffer->data();
+ if (!rsp->Deserialize(&p, p + response_buffer->size())) {
+ ALOGE("Error deserializing response of size %zu\n", response_buffer->size());
+ return KM_ERROR_UNKNOWN_ERROR;
+ } else if (rsp->error != KM_ERROR_OK) {
+ ALOGE("Response of size %zu contained error code %d\n", response_buffer->size(),
+ (int)rsp->error);
+ }
+ return rsp->error;
+ } else {
+ auto rc = std::get<int>(response);
// Reset the connection on tipc error
trusty_keymaster_disconnect();
trusty_keymaster_connect();
ALOGE("tipc error: %d\n", rc);
// TODO(swillden): Distinguish permanent from transient errors and set error_ appropriately.
return translate_error(rc);
- } else {
- ALOGV("Received %d byte response\n", rsp_size);
}
-
- const uint8_t* p = recv_buf;
- if (!rsp->Deserialize(&p, p + rsp_size)) {
- ALOGE("Error deserializing response of size %d\n", (int)rsp_size);
- return KM_ERROR_UNKNOWN_ERROR;
- } else if (rsp->error != KM_ERROR_OK) {
- ALOGE("Response of size %d contained error code %d\n", (int)rsp_size, (int)rsp->error);
- return rsp->error;
- }
- return rsp->error;
}
diff --git a/trusty/keymaster/keymint/TrustyRemotelyProvisionedComponentDevice.cpp b/trusty/keymaster/keymint/TrustyRemotelyProvisionedComponentDevice.cpp
new file mode 100644
index 0000000..5664829
--- /dev/null
+++ b/trusty/keymaster/keymint/TrustyRemotelyProvisionedComponentDevice.cpp
@@ -0,0 +1,120 @@
+/*
+ * Copyright 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 <trusty_keymaster/TrustyRemotelyProvisionedComponentDevice.h>
+
+#include <assert.h>
+#include <variant>
+
+#include <KeyMintUtils.h>
+#include <keymaster/keymaster_configuration.h>
+
+#include <trusty_keymaster/TrustyKeyMintDevice.h>
+
+namespace aidl::android::hardware::security::keymint::trusty {
+
+using keymaster::GenerateCsrRequest;
+using keymaster::GenerateCsrResponse;
+using keymaster::GenerateRkpKeyRequest;
+using keymaster::GenerateRkpKeyResponse;
+using keymaster::KeymasterBlob;
+using ::std::string;
+using ::std::unique_ptr;
+using ::std::vector;
+using bytevec = ::std::vector<uint8_t>;
+
+namespace {
+
+constexpr auto STATUS_FAILED = IRemotelyProvisionedComponent::STATUS_FAILED;
+
+struct AStatusDeleter {
+ void operator()(AStatus* p) { AStatus_delete(p); }
+};
+
+class Status {
+ public:
+ Status() : status_(AStatus_newOk()) {}
+ Status(int32_t errCode, const std::string& errMsg)
+ : status_(AStatus_fromServiceSpecificErrorWithMessage(errCode, errMsg.c_str())) {}
+ explicit Status(const std::string& errMsg)
+ : status_(AStatus_fromServiceSpecificErrorWithMessage(STATUS_FAILED, errMsg.c_str())) {}
+ explicit Status(AStatus* status) : status_(status ? status : AStatus_newOk()) {}
+
+ Status(Status&&) = default;
+ Status(const Status&) = delete;
+
+ operator ::ndk::ScopedAStatus() && { // NOLINT(google-explicit-constructor)
+ return ndk::ScopedAStatus(status_.release());
+ }
+
+ bool isOk() const { return AStatus_isOk(status_.get()); }
+
+ const char* getMessage() const { return AStatus_getMessage(status_.get()); }
+
+ private:
+ std::unique_ptr<AStatus, AStatusDeleter> status_;
+};
+
+} // namespace
+
+ScopedAStatus TrustyRemotelyProvisionedComponentDevice::getHardwareInfo(RpcHardwareInfo* info) {
+ info->versionNumber = 1;
+ info->rpcAuthorName = "Google";
+ info->supportedEekCurve = RpcHardwareInfo::CURVE_25519;
+ return ScopedAStatus::ok();
+}
+
+ScopedAStatus TrustyRemotelyProvisionedComponentDevice::generateEcdsaP256KeyPair(
+ bool testMode, MacedPublicKey* macedPublicKey, bytevec* privateKeyHandle) {
+ GenerateRkpKeyRequest request(impl_->message_version());
+ request.test_mode = testMode;
+ GenerateRkpKeyResponse response(impl_->message_version());
+ impl_->GenerateRkpKey(request, &response);
+ if (response.error != KM_ERROR_OK) {
+ return Status(-static_cast<int32_t>(response.error), "Failure in key generation.");
+ }
+
+ macedPublicKey->macedKey = km_utils::kmBlob2vector(response.maced_public_key);
+ *privateKeyHandle = km_utils::kmBlob2vector(response.key_blob);
+ return ScopedAStatus::ok();
+}
+
+ScopedAStatus TrustyRemotelyProvisionedComponentDevice::generateCertificateRequest(
+ bool testMode, const vector<MacedPublicKey>& keysToSign,
+ const bytevec& endpointEncCertChain, const bytevec& challenge, DeviceInfo* deviceInfo,
+ ProtectedData* protectedData, bytevec* keysToSignMac) {
+ GenerateCsrRequest request(impl_->message_version());
+ request.test_mode = testMode;
+ request.num_keys = keysToSign.size();
+ request.keys_to_sign_array = new KeymasterBlob[keysToSign.size()];
+ for (size_t i = 0; i < keysToSign.size(); i++) {
+ request.SetKeyToSign(i, keysToSign[i].macedKey.data(), keysToSign[i].macedKey.size());
+ }
+ request.SetEndpointEncCertChain(endpointEncCertChain.data(), endpointEncCertChain.size());
+ request.SetChallenge(challenge.data(), challenge.size());
+ GenerateCsrResponse response(impl_->message_version());
+ impl_->GenerateCsr(request, &response);
+
+ if (response.error != KM_ERROR_OK) {
+ return Status(-static_cast<int32_t>(response.error), "Failure in CSR Generation.");
+ }
+ deviceInfo->deviceInfo = km_utils::kmBlob2vector(response.device_info_blob);
+ protectedData->protectedData = km_utils::kmBlob2vector(response.protected_data_blob);
+ *keysToSignMac = km_utils::kmBlob2vector(response.keys_to_sign_mac);
+ return ScopedAStatus::ok();
+}
+
+} // namespace aidl::android::hardware::security::keymint::trusty
diff --git a/trusty/keymaster/keymint/android.hardware.security.keymint-service.trusty.xml b/trusty/keymaster/keymint/android.hardware.security.keymint-service.trusty.xml
index 0ab3d64..7ca5050 100644
--- a/trusty/keymaster/keymint/android.hardware.security.keymint-service.trusty.xml
+++ b/trusty/keymaster/keymint/android.hardware.security.keymint-service.trusty.xml
@@ -11,4 +11,8 @@
<name>android.hardware.security.sharedsecret</name>
<fqname>ISharedSecret/default</fqname>
</hal>
+ <hal format="aidl">
+ <name>android.hardware.security.keymint</name>
+ <fqname>IRemotelyProvisionedComponent/default</fqname>
+ </hal>
</manifest>
diff --git a/trusty/keymaster/keymint/service.cpp b/trusty/keymaster/keymint/service.cpp
index 8f5f0f8..4060278 100644
--- a/trusty/keymaster/keymint/service.cpp
+++ b/trusty/keymaster/keymint/service.cpp
@@ -20,10 +20,12 @@
#include <android/binder_process.h>
#include <trusty_keymaster/TrustyKeyMintDevice.h>
+#include <trusty_keymaster/TrustyRemotelyProvisionedComponentDevice.h>
#include <trusty_keymaster/TrustySecureClock.h>
#include <trusty_keymaster/TrustySharedSecret.h>
using aidl::android::hardware::security::keymint::trusty::TrustyKeyMintDevice;
+using aidl::android::hardware::security::keymint::trusty::TrustyRemotelyProvisionedComponentDevice;
using aidl::android::hardware::security::secureclock::trusty::TrustySecureClock;
using aidl::android::hardware::security::sharedsecret::trusty::TrustySharedSecret;
@@ -52,7 +54,8 @@
auto keyMint = addService<TrustyKeyMintDevice>(trustyKeymaster);
auto secureClock = addService<TrustySecureClock>(trustyKeymaster);
auto sharedSecret = addService<TrustySharedSecret>(trustyKeymaster);
-
+ auto remotelyProvisionedComponent =
+ addService<TrustyRemotelyProvisionedComponentDevice>(trustyKeymaster);
ABinderProcess_joinThreadPool();
return EXIT_FAILURE; // should not reach
}
diff --git a/trusty/storage/proxy/Android.bp b/trusty/storage/proxy/Android.bp
index a471435..d67089f 100644
--- a/trusty/storage/proxy/Android.bp
+++ b/trusty/storage/proxy/Android.bp
@@ -29,7 +29,10 @@
"proxy.c",
],
- shared_libs: ["liblog"],
+ shared_libs: [
+ "liblog",
+ "libhardware_legacy",
+ ],
header_libs: ["libcutils_headers"],
static_libs: [
diff --git a/trusty/storage/proxy/rpmb.c b/trusty/storage/proxy/rpmb.c
index d1ed649..b59fb67 100644
--- a/trusty/storage/proxy/rpmb.c
+++ b/trusty/storage/proxy/rpmb.c
@@ -29,6 +29,8 @@
#include <linux/major.h>
#include <linux/mmc/ioctl.h>
+#include <hardware_legacy/power.h>
+
#include "ipc.h"
#include "log.h"
#include "rpmb.h"
@@ -100,6 +102,8 @@
static uint8_t read_buf[4096];
static enum dev_type dev_type = UNKNOWN_RPMB;
+static const char* UFS_WAKE_LOCK_NAME = "ufs_seq_wakelock";
+
#ifdef RPMB_DEBUG
static void print_buf(const char* prefix, const uint8_t* buf, size_t size) {
@@ -194,6 +198,7 @@
static int send_ufs_rpmb_req(int sg_fd, const struct storage_rpmb_send_req* req) {
int rc;
+ int wl_rc;
const uint8_t* write_buf = req->payload;
/*
* Meaning of member values are stated on the definition of struct sec_proto_cdb.
@@ -202,6 +207,12 @@
struct sec_proto_cdb out_cdb = {0xB5, 0xEC, 0x00, 0x01, 0x00, 0x00, 0, 0x00, 0x00};
unsigned char sense_buffer[32];
+ 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));
+ return wl_rc;
+ }
+
if (req->reliable_write_size) {
/* Prepare SECURITY PROTOCOL OUT command. */
out_cdb.length = __builtin_bswap32(req->reliable_write_size);
@@ -212,6 +223,7 @@
rc = ioctl(sg_fd, SG_IO, &io_hdr);
if (rc < 0) {
ALOGE("%s: ufs ioctl failed: %d, %s\n", __func__, rc, strerror(errno));
+ goto err_op;
}
write_buf += req->reliable_write_size;
}
@@ -225,6 +237,7 @@
rc = ioctl(sg_fd, SG_IO, &io_hdr);
if (rc < 0) {
ALOGE("%s: ufs ioctl failed: %d, %s\n", __func__, rc, strerror(errno));
+ goto err_op;
}
write_buf += req->write_size;
}
@@ -240,6 +253,13 @@
ALOGE("%s: ufs ioctl failed: %d, %s\n", __func__, rc, strerror(errno));
}
}
+
+err_op:
+ wl_rc = release_wake_lock(UFS_WAKE_LOCK_NAME);
+ if (wl_rc < 0) {
+ ALOGE("%s: failed to release wakelock: %d, %s\n", __func__, wl_rc, strerror(errno));
+ }
+
return rc;
}
diff --git a/trusty/storage/proxy/storage.c b/trusty/storage/proxy/storage.c
index 5b83e21..2fde30f 100644
--- a/trusty/storage/proxy/storage.c
+++ b/trusty/storage/proxy/storage.c
@@ -477,7 +477,6 @@
if (ssdir_fd < 0) {
ALOGE("failed to open ss root dir \"%s\": %s\n",
dirname, strerror(errno));
- return -1;
}
ssdir_name = dirname;
return 0;
diff --git a/trusty/trusty-base.mk b/trusty/trusty-base.mk
index 12521b0..6cd381f 100644
--- a/trusty/trusty-base.mk
+++ b/trusty/trusty-base.mk
@@ -28,5 +28,6 @@
trusty_apploader
PRODUCT_PROPERTY_OVERRIDES += \
+ ro.hardware.keystore_desede=true \
ro.hardware.keystore=trusty \
ro.hardware.gatekeeper=trusty