Merge "Wait for disconnect when rebooting to userspace FB" into main
diff --git a/debuggerd/Android.bp b/debuggerd/Android.bp
index 7d20995..2529516 100644
--- a/debuggerd/Android.bp
+++ b/debuggerd/Android.bp
@@ -21,8 +21,11 @@
local_include_dirs: ["include"],
product_variables: {
debuggable: {
- cflags: ["-UANDROID_DEBUGGABLE", "-DANDROID_DEBUGGABLE=1"],
- }
+ cflags: [
+ "-UANDROID_DEBUGGABLE",
+ "-DANDROID_DEBUGGABLE=1",
+ ],
+ },
},
}
@@ -256,7 +259,7 @@
],
static_libs: [
- "libdexfile_support", // libunwindstack dependency
+ "libdexfile_support", // libunwindstack dependency
"libunwindstack",
"liblzma",
"libbase",
@@ -299,7 +302,7 @@
},
android: {
runtime_libs: [
- "libdexfile", // libdexfile_support dependency
+ "libdexfile", // libdexfile_support dependency
],
},
},
@@ -368,6 +371,10 @@
},
},
+ sanitize: {
+ memtag_heap: true,
+ },
+
shared_libs: [
"libbase",
"libcutils",
@@ -494,7 +501,7 @@
header_libs: [
"bionic_libc_platform_headers",
- "libdebuggerd_common_headers"
+ "libdebuggerd_common_headers",
],
static_libs: [
@@ -552,7 +559,6 @@
},
}
-
// This installs the "other" architecture (so 32-bit on 64-bit device).
prebuilt_etc {
name: "crash_dump.policy_other",
diff --git a/debuggerd/TEST_MAPPING b/debuggerd/TEST_MAPPING
index 8633cb8..61d7155 100644
--- a/debuggerd/TEST_MAPPING
+++ b/debuggerd/TEST_MAPPING
@@ -14,5 +14,10 @@
{
"name": "debuggerd_test"
}
+ ],
+ "postsubmit": [
+ {
+ "name": "CtsCrashDetailHostTestCases"
+ }
]
}
diff --git a/debuggerd/crash_dump.cpp b/debuggerd/crash_dump.cpp
index a23a269..203b485 100644
--- a/debuggerd/crash_dump.cpp
+++ b/debuggerd/crash_dump.cpp
@@ -143,7 +143,7 @@
}
static bool activity_manager_notify(pid_t pid, int signal, const std::string& amfd_data,
- bool recoverable_gwp_asan_crash) {
+ bool recoverable_crash) {
ATRACE_CALL();
android::base::unique_fd amfd(socket_local_client(
"/data/system/ndebugsocket", ANDROID_SOCKET_NAMESPACE_FILESYSTEM, SOCK_STREAM));
@@ -169,7 +169,7 @@
// Activity Manager protocol:
// - 32-bit network-byte-order: pid
// - 32-bit network-byte-order: signal number
- // - byte: recoverable_gwp_asan_crash
+ // - byte: recoverable_crash
// - bytes: raw text of the dump
// - null terminator
@@ -185,10 +185,9 @@
return false;
}
- uint8_t recoverable_gwp_asan_crash_byte = recoverable_gwp_asan_crash ? 1 : 0;
- if (!android::base::WriteFully(amfd, &recoverable_gwp_asan_crash_byte,
- sizeof(recoverable_gwp_asan_crash_byte))) {
- PLOG(ERROR) << "AM recoverable_gwp_asan_crash_byte write failed";
+ uint8_t recoverable_crash_byte = recoverable_crash ? 1 : 0;
+ if (!android::base::WriteFully(amfd, &recoverable_crash_byte, sizeof(recoverable_crash_byte))) {
+ PLOG(ERROR) << "AM recoverable_crash_byte write failed";
return false;
}
@@ -280,11 +279,11 @@
static void ReadCrashInfo(unique_fd& fd, siginfo_t* siginfo,
std::unique_ptr<unwindstack::Regs>* regs, ProcessInfo* process_info,
- bool* recoverable_gwp_asan_crash) {
+ bool* recoverable_crash) {
std::aligned_storage<sizeof(CrashInfo) + 1, alignof(CrashInfo)>::type buf;
CrashInfo* crash_info = reinterpret_cast<CrashInfo*>(&buf);
ssize_t rc = TEMP_FAILURE_RETRY(read(fd.get(), &buf, sizeof(buf)));
- *recoverable_gwp_asan_crash = false;
+ *recoverable_crash = false;
if (rc == -1) {
PLOG(FATAL) << "failed to read target ucontext";
} else {
@@ -321,7 +320,7 @@
process_info->scudo_region_info = crash_info->data.d.scudo_region_info;
process_info->scudo_ring_buffer = crash_info->data.d.scudo_ring_buffer;
process_info->scudo_ring_buffer_size = crash_info->data.d.scudo_ring_buffer_size;
- *recoverable_gwp_asan_crash = crash_info->data.d.recoverable_gwp_asan_crash;
+ *recoverable_crash = crash_info->data.d.recoverable_crash;
process_info->crash_detail_page = crash_info->data.d.crash_detail_page;
FALLTHROUGH_INTENDED;
case 1:
@@ -487,7 +486,7 @@
std::map<pid_t, ThreadInfo> thread_info;
siginfo_t siginfo;
std::string error;
- bool recoverable_gwp_asan_crash = false;
+ bool recoverable_crash = false;
{
ATRACE_NAME("ptrace");
@@ -539,8 +538,7 @@
if (thread == g_target_thread) {
// Read the thread's registers along with the rest of the crash info out of the pipe.
- ReadCrashInfo(input_pipe, &siginfo, &info.registers, &process_info,
- &recoverable_gwp_asan_crash);
+ ReadCrashInfo(input_pipe, &siginfo, &info.registers, &process_info, &recoverable_crash);
info.siginfo = &siginfo;
info.signo = info.siginfo->si_signo;
@@ -670,7 +668,7 @@
if (fatal_signal) {
// Don't try to notify ActivityManager if it just crashed, or we might hang until timeout.
if (thread_info[target_process].thread_name != "system_server") {
- activity_manager_notify(target_process, signo, amfd_data, recoverable_gwp_asan_crash);
+ activity_manager_notify(target_process, signo, amfd_data, recoverable_crash);
}
}
diff --git a/debuggerd/crasher/Android.bp b/debuggerd/crasher/Android.bp
index fe1689c..4c6a400 100644
--- a/debuggerd/crasher/Android.bp
+++ b/debuggerd/crasher/Android.bp
@@ -15,7 +15,7 @@
"-fstack-protector-all",
"-Wno-date-time",
],
- tidy: false, // crasher.cpp tests many memory access errors
+ tidy: false, // crasher.cpp tests many memory access errors
srcs: ["crasher.cpp"],
arch: {
arm: {
diff --git a/debuggerd/debuggerd_test.cpp b/debuggerd/debuggerd_test.cpp
index f396b1d..7c52e6e 100644
--- a/debuggerd/debuggerd_test.cpp
+++ b/debuggerd/debuggerd_test.cpp
@@ -21,6 +21,7 @@
#include <linux/prctl.h>
#include <malloc.h>
#include <pthread.h>
+#include <setjmp.h>
#include <stdlib.h>
#include <sys/capability.h>
#include <sys/mman.h>
@@ -601,6 +602,54 @@
#endif
}
+__attribute__((noinline)) void mte_illegal_setjmp_helper(jmp_buf& jump_buf) {
+ // This frame is at least 8 bytes for storing and restoring the LR before the
+ // setjmp below. So this can never get an empty stack frame, even if we omit
+ // the frame pointer. So, the SP of this is always less (numerically) than the
+ // calling function frame.
+ setjmp(jump_buf);
+}
+
+TEST_F(CrasherTest, DISABLED_mte_illegal_setjmp) {
+ // This setjmp is illegal because it jumps back into a function that already returned.
+ // Quoting man 3 setjmp:
+ // If the function which called setjmp() returns before longjmp() is
+ // called, the behavior is undefined. Some kind of subtle or
+ // unsubtle chaos is sure to result.
+ // https://man7.org/linux/man-pages/man3/longjmp.3.html
+#if defined(__aarch64__)
+ if (!mte_supported()) {
+ GTEST_SKIP() << "Requires MTE";
+ }
+
+ int intercept_result;
+ unique_fd output_fd;
+ StartProcess([&]() {
+ SetTagCheckingLevelSync();
+ jmp_buf jump_buf;
+ mte_illegal_setjmp_helper(jump_buf);
+ longjmp(jump_buf, 1);
+ });
+
+ 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);
+
+ // In our test-case, we have a NEGATIVE stack adjustment, which is being
+ // interpreted as unsigned integer, and thus is "too large".
+ // TODO(fmayer): fix the error message for this
+ ASSERT_MATCH(result, R"(memtag_handle_longjmp: stack adjustment too large)");
+#else
+ GTEST_SKIP() << "Requires aarch64";
+#endif
+}
+
TEST_F(CrasherTest, mte_async) {
#if defined(__aarch64__)
if (!mte_supported()) {
@@ -1825,8 +1874,8 @@
StartProcess([&recoverable]() {
const char* env[] = {"GWP_ASAN_SAMPLE_RATE=1", "GWP_ASAN_PROCESS_SAMPLING=1",
"GWP_ASAN_MAX_ALLOCS=40000", nullptr, nullptr};
- if (recoverable) {
- env[3] = "GWP_ASAN_RECOVERABLE=true";
+ if (!recoverable) {
+ env[3] = "GWP_ASAN_RECOVERABLE=false";
}
std::string test_name = ::testing::UnitTest::GetInstance()->current_test_info()->name();
test_name = std::regex_replace(test_name, std::regex("run_gwp_asan_test"),
@@ -2647,7 +2696,7 @@
match_str += format_full_pointer(crash_uptr);
ASSERT_MATCH(result, match_str);
- ASSERT_MATCH(result, R"(\nmemory map \(.*\): \(fault address prefixed with --->)\n)");
+ ASSERT_MATCH(result, R"(\nmemory map \(.*\): \(fault address prefixed with --->\)\n)");
// Verifies that the fault address error message is at the end of the
// maps section. To do this, the check below looks for the start of the
@@ -2699,7 +2748,7 @@
match_str += format_full_pointer(reinterpret_cast<uintptr_t>(middle_ptr));
ASSERT_MATCH(result, match_str);
- ASSERT_MATCH(result, R"(\nmemory map \(.*\): \(fault address prefixed with --->)\n)");
+ 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)",
@@ -2737,7 +2786,7 @@
match_str += format_full_pointer(reinterpret_cast<uintptr_t>(ptr));
ASSERT_MATCH(result, match_str);
- ASSERT_MATCH(result, R"(\nmemory map \(.*\): \(fault address prefixed with --->)\n)");
+ 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 141723b..e26746b 100644
--- a/debuggerd/handler/debuggerd_handler.cpp
+++ b/debuggerd/handler/debuggerd_handler.cpp
@@ -108,7 +108,7 @@
"persist.device_config.memory_safety_native.permissive.process.%s",
getprogname());
// DO NOT REPLACE this with GetBoolProperty. That uses std::string which allocates, so it is
- // not async-safe (and this functiong gets used in a signal handler).
+ // not async-safe, and this function gets used in a signal handler.
return property_parse_bool("persist.sys.mte.permissive") ||
property_parse_bool("persist.device_config.memory_safety_native.permissive.default") ||
property_parse_bool(process_sysprop_name) ||
@@ -275,10 +275,6 @@
}
}
-static pid_t __fork() {
- return clone(nullptr, nullptr, 0, nullptr);
-}
-
// Double-clone, with CLONE_FILES to share the file descriptor table for kcmp validation.
// Returns 0 in the orphaned child, the pid of the orphan in the original process, or -1 on failure.
static void create_vm_process() {
@@ -396,7 +392,7 @@
ASSERT_SAME_OFFSET(scudo_ring_buffer, scudo_ring_buffer);
ASSERT_SAME_OFFSET(scudo_ring_buffer_size, scudo_ring_buffer_size);
ASSERT_SAME_OFFSET(scudo_stack_depot_size, scudo_stack_depot_size);
- ASSERT_SAME_OFFSET(recoverable_gwp_asan_crash, recoverable_gwp_asan_crash);
+ ASSERT_SAME_OFFSET(recoverable_crash, recoverable_crash);
ASSERT_SAME_OFFSET(crash_detail_page, crash_detail_page);
#undef ASSERT_SAME_OFFSET
@@ -426,7 +422,7 @@
}
// Don't use fork(2) to avoid calling pthread_atfork handlers.
- pid_t crash_dump_pid = __fork();
+ pid_t crash_dump_pid = _Fork();
if (crash_dump_pid == -1) {
async_safe_format_log(ANDROID_LOG_FATAL, "libc",
"failed to fork in debuggerd signal handler: %s", strerror(errno));
@@ -573,6 +569,7 @@
}
gwp_asan_callbacks_t gwp_asan_callbacks = {};
+ bool recoverable_gwp_asan_crash = false;
if (g_callbacks.get_gwp_asan_callbacks != nullptr) {
// GWP-ASan catches use-after-free and heap-buffer-overflow by using PROT_NONE
// guard pages, which lead to SEGV. Normally, debuggerd prints a bug report
@@ -587,10 +584,30 @@
gwp_asan_callbacks.debuggerd_gwp_asan_post_crash_report &&
gwp_asan_callbacks.debuggerd_needs_gwp_asan_recovery(info->si_addr)) {
gwp_asan_callbacks.debuggerd_gwp_asan_pre_crash_report(info->si_addr);
- process_info.recoverable_gwp_asan_crash = true;
+ recoverable_gwp_asan_crash = true;
+ process_info.recoverable_crash = true;
}
}
+ if (info->si_signo == SIGSEGV &&
+ (info->si_code == SEGV_MTESERR || info->si_code == SEGV_MTEAERR) && is_permissive_mte()) {
+ process_info.recoverable_crash = true;
+ // If we are in permissive MTE mode, we do not crash, but instead disable MTE on this thread,
+ // and then let the failing instruction be retried. The second time should work (except
+ // if there is another non-MTE fault).
+ int tagged_addr_ctrl = prctl(PR_GET_TAGGED_ADDR_CTRL, 0, 0, 0, 0);
+ if (tagged_addr_ctrl < 0) {
+ fatal_errno("failed to PR_GET_TAGGED_ADDR_CTRL");
+ }
+ tagged_addr_ctrl = (tagged_addr_ctrl & ~PR_MTE_TCF_MASK) | PR_MTE_TCF_NONE;
+ if (prctl(PR_SET_TAGGED_ADDR_CTRL, tagged_addr_ctrl, 0, 0, 0) < 0) {
+ fatal_errno("failed to PR_SET_TAGGED_ADDR_CTRL");
+ }
+ async_safe_format_log(ANDROID_LOG_ERROR, "libc",
+ "MTE ERROR DETECTED BUT RUNNING IN PERMISSIVE MODE. CONTINUING.");
+ pthread_mutex_unlock(&crash_mutex);
+ }
+
// If sival_int is ~0, it means that the fallback handler has been called
// once before and this function is being called again to dump the stack
// of a specific thread. It is possible that the prctl call might return 1,
@@ -602,7 +619,7 @@
// you can only set NO_NEW_PRIVS to 1, and the effect should be at worst a single missing
// ANR trace.
debuggerd_fallback_handler(info, ucontext, process_info.abort_msg);
- if (no_new_privs && process_info.recoverable_gwp_asan_crash) {
+ if (no_new_privs && recoverable_gwp_asan_crash) {
gwp_asan_callbacks.debuggerd_gwp_asan_post_crash_report(info->si_addr);
return;
}
@@ -679,29 +696,14 @@
// If the signal is fatal, don't unlock the mutex to prevent other crashing threads from
// starting to dump right before our death.
pthread_mutex_unlock(&crash_mutex);
- } else if (process_info.recoverable_gwp_asan_crash) {
- gwp_asan_callbacks.debuggerd_gwp_asan_post_crash_report(info->si_addr);
+ } else if (process_info.recoverable_crash) {
+ if (recoverable_gwp_asan_crash) {
+ gwp_asan_callbacks.debuggerd_gwp_asan_post_crash_report(info->si_addr);
+ }
pthread_mutex_unlock(&crash_mutex);
}
#ifdef __aarch64__
- else if (info->si_signo == SIGSEGV &&
- (info->si_code == SEGV_MTESERR || info->si_code == SEGV_MTEAERR) &&
- is_permissive_mte()) {
- // If we are in permissive MTE mode, we do not crash, but instead disable MTE on this thread,
- // and then let the failing instruction be retried. The second time should work (except
- // if there is another non-MTE fault).
- int tagged_addr_ctrl = prctl(PR_GET_TAGGED_ADDR_CTRL, 0, 0, 0, 0);
- if (tagged_addr_ctrl < 0) {
- fatal_errno("failed to PR_GET_TAGGED_ADDR_CTRL");
- }
- tagged_addr_ctrl = (tagged_addr_ctrl & ~PR_MTE_TCF_MASK) | PR_MTE_TCF_NONE;
- if (prctl(PR_SET_TAGGED_ADDR_CTRL, tagged_addr_ctrl, 0, 0, 0) < 0) {
- fatal_errno("failed to PR_SET_TAGGED_ADDR_CTRL");
- }
- async_safe_format_log(ANDROID_LOG_ERROR, "libc",
- "MTE ERROR DETECTED BUT RUNNING IN PERMISSIVE MODE. CONTINUING.");
- pthread_mutex_unlock(&crash_mutex);
- } else if (info->si_signo == SIGSEGV && info->si_code == SEGV_MTEAERR && getppid() == 1) {
+ else if (info->si_signo == SIGSEGV && info->si_code == SEGV_MTEAERR && getppid() == 1) {
// Back channel to init (see system/core/init/service.cpp) to signal that
// this process crashed due to an ASYNC MTE fault and should be considered
// for upgrade to SYNC mode. We are re-using the ART profiler signal, which
@@ -761,18 +763,7 @@
debuggerd_register_handlers(&action);
}
-// When debuggerd's signal handler is the first handler called, it's great at
-// handling the recoverable GWP-ASan mode. For apps, sigchain (from libart) is
-// always the first signal handler, and so the following function is what
-// sigchain must call before processing the signal. This allows for processing
-// of a potentially recoverable GWP-ASan crash. If the signal requires GWP-ASan
-// recovery, then dump a report (via the regular debuggerd hanndler), and patch
-// up the allocator, and allow the process to continue (indicated by returning
-// 'true'). If the crash has nothing to do with GWP-ASan, or recovery isn't
-// possible, return 'false'.
-bool debuggerd_handle_signal(int signal_number, siginfo_t* info, void* context) {
- if (signal_number != SIGSEGV || !signal_has_si_addr(info)) return false;
-
+bool debuggerd_handle_gwp_asan_signal(int signal_number, siginfo_t* info, void* context) {
if (g_callbacks.get_gwp_asan_callbacks == nullptr) return false;
gwp_asan_callbacks_t gwp_asan_callbacks = g_callbacks.get_gwp_asan_callbacks();
if (gwp_asan_callbacks.debuggerd_needs_gwp_asan_recovery == nullptr ||
@@ -810,3 +801,33 @@
pthread_mutex_unlock(&first_crash_mutex);
return true;
}
+
+// When debuggerd's signal handler is the first handler called, it's great at
+// handling the recoverable GWP-ASan and permissive MTE modes. For apps,
+// sigchain (from libart) is always the first signal handler, and so the
+// following function is what sigchain must call before processing the signal.
+// This allows for processing of a potentially recoverable GWP-ASan or MTE
+// crash. If the signal requires recovery, then dump a report (via the regular
+// debuggerd hanndler), and patch up the allocator (in the case of GWP-ASan) or
+// disable MTE on the thread, and allow the process to continue (indicated by
+// returning 'true'). If the crash has nothing to do with GWP-ASan/MTE, or
+// recovery isn't possible, return 'false'.
+bool debuggerd_handle_signal(int signal_number, siginfo_t* info, void* context) {
+ if (signal_number != SIGSEGV) return false;
+ if (info->si_code == SEGV_MTEAERR || info->si_code == SEGV_MTESERR) {
+ if (!is_permissive_mte()) return false;
+ // Because permissive MTE disables MTE for the entire thread, we're less
+ // worried about getting a whole bunch of crashes in a row. ActivityManager
+ // doesn't like multiple native crashes for an app in a short period of time
+ // (see the comment about recoverable GWP-ASan in
+ // `debuggerd_handle_gwp_asan_signal`), but that shouldn't happen if MTE is
+ // disabled for the entire thread. This might need to be changed if there's
+ // some low-hanging bug that happens across multiple threads in quick
+ // succession.
+ debuggerd_signal_handler(signal_number, info, context);
+ return true;
+ }
+
+ if (!signal_has_si_addr(info)) return false;
+ return debuggerd_handle_gwp_asan_signal(signal_number, info, context);
+}
diff --git a/debuggerd/include/debuggerd/handler.h b/debuggerd/include/debuggerd/handler.h
index c18cf6e..954f049 100644
--- a/debuggerd/include/debuggerd/handler.h
+++ b/debuggerd/include/debuggerd/handler.h
@@ -47,7 +47,7 @@
const char* scudo_ring_buffer;
size_t scudo_ring_buffer_size;
size_t scudo_stack_depot_size;
- bool recoverable_gwp_asan_crash;
+ bool recoverable_crash;
struct crash_detail_page_t* crash_detail_page;
};
diff --git a/debuggerd/proto/Android.bp b/debuggerd/proto/Android.bp
index 7be5d61..7b9e780 100644
--- a/debuggerd/proto/Android.bp
+++ b/debuggerd/proto/Android.bp
@@ -52,4 +52,3 @@
sdk_version: "current",
static_libs: ["libprotobuf-java-lite"],
}
-
diff --git a/debuggerd/protocol.h b/debuggerd/protocol.h
index d3fc15e..9af7377 100644
--- a/debuggerd/protocol.h
+++ b/debuggerd/protocol.h
@@ -100,7 +100,7 @@
uintptr_t scudo_ring_buffer;
size_t scudo_ring_buffer_size;
size_t scudo_stack_depot_size;
- bool recoverable_gwp_asan_crash;
+ bool recoverable_crash;
uintptr_t crash_detail_page;
};
diff --git a/debuggerd/rust/tombstoned_client/Android.bp b/debuggerd/rust/tombstoned_client/Android.bp
index 2007f39..bf19bb7 100644
--- a/debuggerd/rust/tombstoned_client/Android.bp
+++ b/debuggerd/rust/tombstoned_client/Android.bp
@@ -8,7 +8,7 @@
"wrapper.cpp",
],
generated_sources: [
- "libtombstoned_client_rust_bridge_code"
+ "libtombstoned_client_rust_bridge_code",
],
header_libs: [
"libbase_headers",
diff --git a/debuggerd/seccomp_policy/crash_dump.policy.def b/debuggerd/seccomp_policy/crash_dump.policy.def
index 972a575..dc751da 100644
--- a/debuggerd/seccomp_policy/crash_dump.policy.def
+++ b/debuggerd/seccomp_policy/crash_dump.policy.def
@@ -25,8 +25,8 @@
faccessat: 1
recvmsg: 1
recvfrom: 1
-sysinfo: 1
setsockopt: 1
+sysinfo: 1
process_vm_readv: 1
@@ -53,20 +53,29 @@
#if 0
libminijail on vendor partitions older than P does not have constants from <sys/mman.h>.
-Define the values of PROT_READ and PROT_WRITE ourselves to maintain backwards compatibility.
+Define values for PROT_READ, PROT_WRITE and PROT_MTE ourselves to maintain backwards compatibility.
#else
#define PROT_READ 0x1
#define PROT_WRITE 0x2
+#define PROT_MTE 0x20
#endif
madvise: 1
+#if defined(__aarch64__)
+mprotect: arg2 in PROT_READ|PROT_WRITE|PROT_MTE
+#else
mprotect: arg2 in PROT_READ|PROT_WRITE
+#endif
munmap: 1
#if defined(__LP64__)
getuid: 1
fstat: 1
+#if defined(__aarch64__)
+mmap: arg2 in PROT_READ|PROT_WRITE|PROT_MTE
+#else
mmap: arg2 in PROT_READ|PROT_WRITE
+#endif
#else
getuid32: 1
fstat64: 1
diff --git a/debuggerd/test_permissive_mte/Android.bp b/debuggerd/test_permissive_mte/Android.bp
index d3f7520..0ad3243 100644
--- a/debuggerd/test_permissive_mte/Android.bp
+++ b/debuggerd/test_permissive_mte/Android.bp
@@ -17,22 +17,28 @@
}
cc_binary {
- name: "mte_crash",
- tidy: false,
- srcs: ["mte_crash.cpp"],
- sanitize: {
- memtag_heap: true,
- diag: {
- memtag_heap: true,
+ name: "mte_crash",
+ tidy: false,
+ srcs: ["mte_crash.cpp"],
+ sanitize: {
+ memtag_heap: true,
+ diag: {
+ memtag_heap: true,
+ },
},
- },
}
java_test_host {
name: "permissive_mte_test",
libs: ["tradefed"],
- static_libs: ["frameworks-base-hostutils", "cts-install-lib-host"],
- srcs: ["src/**/PermissiveMteTest.java", ":libtombstone_proto-src"],
+ static_libs: [
+ "frameworks-base-hostutils",
+ "cts-install-lib-host",
+ ],
+ srcs: [
+ "src/**/PermissiveMteTest.java",
+ ":libtombstone_proto-src",
+ ],
data: [":mte_crash"],
test_config: "AndroidTest.xml",
test_suites: ["general-tests"],
diff --git a/debuggerd/tombstoned/tombstoned.cpp b/debuggerd/tombstoned/tombstoned.cpp
index cf7904f..fa67d46 100644
--- a/debuggerd/tombstoned/tombstoned.cpp
+++ b/debuggerd/tombstoned/tombstoned.cpp
@@ -61,7 +61,6 @@
struct CrashArtifact {
unique_fd fd;
- std::optional<std::string> temporary_path;
static CrashArtifact devnull() {
CrashArtifact result;
@@ -97,7 +96,7 @@
class CrashQueue {
public:
CrashQueue(const std::string& dir_path, const std::string& file_name_prefix, size_t max_artifacts,
- size_t max_concurrent_dumps, bool supports_proto)
+ size_t max_concurrent_dumps, bool supports_proto, bool world_readable)
: file_name_prefix_(file_name_prefix),
dir_path_(dir_path),
dir_fd_(open(dir_path.c_str(), O_DIRECTORY | O_RDONLY | O_CLOEXEC)),
@@ -105,7 +104,8 @@
next_artifact_(0),
max_concurrent_dumps_(max_concurrent_dumps),
num_concurrent_dumps_(0),
- supports_proto_(supports_proto) {
+ supports_proto_(supports_proto),
+ world_readable_(world_readable) {
if (dir_fd_ == -1) {
PLOG(FATAL) << "failed to open directory: " << dir_path;
}
@@ -128,14 +128,16 @@
static CrashQueue* for_tombstones() {
static CrashQueue queue("/data/tombstones", "tombstone_" /* file_name_prefix */,
GetIntProperty("tombstoned.max_tombstone_count", 32),
- 1 /* max_concurrent_dumps */, true /* supports_proto */);
+ 1 /* max_concurrent_dumps */, true /* supports_proto */,
+ true /* world_readable */);
return &queue;
}
static CrashQueue* for_anrs() {
static CrashQueue queue("/data/anr", "trace_" /* file_name_prefix */,
GetIntProperty("tombstoned.max_anr_count", 64),
- 4 /* max_concurrent_dumps */, false /* supports_proto */);
+ 4 /* max_concurrent_dumps */, false /* supports_proto */,
+ false /* world_readable */);
return &queue;
}
@@ -145,16 +147,15 @@
std::optional<std::string> path;
result.fd.reset(openat(dir_fd_, ".", O_WRONLY | O_APPEND | O_TMPFILE | O_CLOEXEC, 0660));
if (result.fd == -1) {
- // We might not have O_TMPFILE. Try creating with an arbitrary filename instead.
- static size_t counter = 0;
- std::string tmp_filename = StringPrintf(".temporary%zu", counter++);
- result.fd.reset(openat(dir_fd_, tmp_filename.c_str(),
- O_WRONLY | O_APPEND | O_CREAT | O_TRUNC | O_CLOEXEC, 0660));
- if (result.fd == -1) {
- PLOG(FATAL) << "failed to create temporary tombstone in " << dir_path_;
- }
+ PLOG(FATAL) << "failed to create temporary tombstone in " << dir_path_;
+ }
- result.temporary_path = std::move(tmp_filename);
+ if (world_readable_) {
+ // We need to fchmodat after creating to avoid getting the umask applied.
+ std::string fd_path = StringPrintf("/proc/self/fd/%d", result.fd.get());
+ if (fchmodat(dir_fd_, fd_path.c_str(), 0664, 0) != 0) {
+ PLOG(ERROR) << "Failed to make tombstone world-readable";
+ }
}
return std::move(result);
@@ -266,6 +267,7 @@
size_t num_concurrent_dumps_;
bool supports_proto_;
+ bool world_readable_;
std::deque<std::unique_ptr<Crash>> queued_requests_;
@@ -417,6 +419,7 @@
return false;
}
+ // This fd is created inside of dirfd in CrashQueue::create_temporary_file.
std::string fd_path = StringPrintf("/proc/self/fd/%d", fd.get());
rc = linkat(AT_FDCWD, fd_path.c_str(), dirfd.get(), path.c_str(), AT_SYMLINK_FOLLOW);
if (rc != 0) {
@@ -453,17 +456,6 @@
CrashArtifactPaths paths = queue->get_next_artifact_paths();
- if (rename_tombstone_fd(crash->output.text.fd, queue->dir_fd(), paths.text)) {
- if (crash->crash_type == kDebuggerdJavaBacktrace) {
- LOG(ERROR) << "Traces for pid " << crash->crash_pid << " written to: " << paths.text;
- } else {
- // NOTE: Several tools parse this log message to figure out where the
- // tombstone associated with a given native crash was written. Any changes
- // to this message must be carefully considered.
- LOG(ERROR) << "Tombstone written to: " << paths.text;
- }
- }
-
if (crash->output.proto && crash->output.proto->fd != -1) {
if (!paths.proto) {
LOG(ERROR) << "missing path for proto tombstone";
@@ -472,17 +464,14 @@
}
}
- // If we don't have O_TMPFILE, we need to clean up after ourselves.
- if (crash->output.text.temporary_path) {
- rc = unlinkat(queue->dir_fd().get(), crash->output.text.temporary_path->c_str(), 0);
- if (rc != 0) {
- PLOG(ERROR) << "failed to unlink temporary tombstone at " << paths.text;
- }
- }
- if (crash->output.proto && crash->output.proto->temporary_path) {
- rc = unlinkat(queue->dir_fd().get(), crash->output.proto->temporary_path->c_str(), 0);
- if (rc != 0) {
- PLOG(ERROR) << "failed to unlink temporary proto tombstone";
+ if (rename_tombstone_fd(crash->output.text.fd, queue->dir_fd(), paths.text)) {
+ if (crash->crash_type == kDebuggerdJavaBacktrace) {
+ LOG(ERROR) << "Traces for pid " << crash->crash_pid << " written to: " << paths.text;
+ } else {
+ // NOTE: Several tools parse this log message to figure out where the
+ // tombstone associated with a given native crash was written. Any changes
+ // to this message must be carefully considered.
+ LOG(ERROR) << "Tombstone written to: " << paths.text;
}
}
}
diff --git a/fastboot/fuzzer/Android.bp b/fastboot/fuzzer/Android.bp
index a898070..59533fa 100644
--- a/fastboot/fuzzer/Android.bp
+++ b/fastboot/fuzzer/Android.bp
@@ -55,7 +55,10 @@
],
fuzz_config: {
cc: [
- "android-media-fuzzing-reports@google.com",
+ "dvander@google.com",
+ "elsk@google.com",
+ "enh@google.com",
+ "zhangkelvin@google.com",
],
componentid: 533764,
hotlists: [
diff --git a/fs_mgr/TEST_MAPPING b/fs_mgr/TEST_MAPPING
index 1989a5c..32e8b88 100644
--- a/fs_mgr/TEST_MAPPING
+++ b/fs_mgr/TEST_MAPPING
@@ -25,8 +25,6 @@
{
"name": "vab_legacy_tests"
},
- // TODO(b/279009697):
- //{"name": "vabc_legacy_tests"},
{
"name": "cow_api_test"
},
@@ -44,8 +42,6 @@
{
"name": "vab_legacy_tests"
},
- // TODO(b/279009697):
- //{"name": "vabc_legacy_tests"}
{
"name": "snapuserd_test"
}
diff --git a/fs_mgr/libdm/Android.bp b/fs_mgr/libdm/Android.bp
index 5cc0346..c3ca758 100644
--- a/fs_mgr/libdm/Android.bp
+++ b/fs_mgr/libdm/Android.bp
@@ -71,6 +71,9 @@
"libbase",
"liblog",
],
+ header_libs: [
+ "libstorage_literals_headers",
+ ],
srcs: [":libdm_test_srcs"],
auto_gen_config: true,
require_root: true,
diff --git a/fs_mgr/libdm/dm.cpp b/fs_mgr/libdm/dm.cpp
index e261aa3..a963322 100644
--- a/fs_mgr/libdm/dm.cpp
+++ b/fs_mgr/libdm/dm.cpp
@@ -39,6 +39,9 @@
#ifndef DM_DEFERRED_REMOVE
#define DM_DEFERRED_REMOVE (1 << 17)
#endif
+#ifndef DM_IMA_MEASUREMENT_FLAG
+#define DM_IMA_MEASUREMENT_FLAG (1 << 19)
+#endif
namespace android {
namespace dm {
@@ -540,6 +543,10 @@
return GetTable(name, 0, table);
}
+bool DeviceMapper::GetTableStatusIma(const std::string& name, std::vector<TargetInfo>* table) {
+ return GetTable(name, DM_IMA_MEASUREMENT_FLAG, table);
+}
+
bool DeviceMapper::GetTableInfo(const std::string& name, std::vector<TargetInfo>* table) {
return GetTable(name, DM_STATUS_TABLE_FLAG, table);
}
@@ -762,5 +769,25 @@
return true;
}
+bool DeviceMapper::SendMessage(const std::string& name, uint64_t sector,
+ const std::string& message) {
+ std::string ioctl_buffer(sizeof(struct dm_ioctl) + sizeof(struct dm_target_msg), 0);
+ ioctl_buffer += message;
+ ioctl_buffer.push_back('\0');
+
+ struct dm_ioctl* io = reinterpret_cast<struct dm_ioctl*>(&ioctl_buffer[0]);
+ InitIo(io, name);
+ io->data_size = ioctl_buffer.size();
+ io->data_start = sizeof(struct dm_ioctl);
+ struct dm_target_msg* msg =
+ reinterpret_cast<struct dm_target_msg*>(&ioctl_buffer[sizeof(struct dm_ioctl)]);
+ msg->sector = sector;
+ if (ioctl(fd_, DM_TARGET_MSG, io)) {
+ PLOG(ERROR) << "DM_TARGET_MSG failed";
+ return false;
+ }
+ return true;
+}
+
} // namespace dm
} // namespace android
diff --git a/fs_mgr/libdm/dm_target.cpp b/fs_mgr/libdm/dm_target.cpp
index 90d91a0..b5cc9aa 100644
--- a/fs_mgr/libdm/dm_target.cpp
+++ b/fs_mgr/libdm/dm_target.cpp
@@ -61,6 +61,10 @@
return block_device_ + " " + std::to_string(physical_sector_);
}
+std::string DmTargetStripe::GetParameterString() const {
+ return "2 " + std::to_string(chunksize) + " " + block_device0_ + " 0 " + block_device1_ + " 0";
+}
+
DmTargetVerity::DmTargetVerity(uint64_t start, uint64_t length, uint32_t version,
const std::string& block_device, const std::string& hash_device,
uint32_t data_block_size, uint32_t hash_block_size,
@@ -294,5 +298,43 @@
return android::base::Join(argv, " ");
}
+DmTargetThinPool::DmTargetThinPool(uint64_t start, uint64_t length, const std::string& metadata_dev,
+ const std::string& data_dev, uint64_t data_block_size,
+ uint64_t low_water_mark)
+ : DmTarget(start, length),
+ metadata_dev_(metadata_dev),
+ data_dev_(data_dev),
+ data_block_size_(data_block_size),
+ low_water_mark_(low_water_mark) {}
+
+std::string DmTargetThinPool::GetParameterString() const {
+ std::vector<std::string> args{
+ metadata_dev_,
+ data_dev_,
+ std::to_string(data_block_size_),
+ std::to_string(low_water_mark_),
+ };
+ return android::base::Join(args, " ");
+}
+
+bool DmTargetThinPool::Valid() const {
+ // data_block_size: must be between 128 (64KB) and 2097152 (1GB) and a multiple of 128 (64KB)
+ if (data_block_size_ < 128 || data_block_size_ > 2097152) return false;
+ if (data_block_size_ % 128) return false;
+ return true;
+}
+
+DmTargetThin::DmTargetThin(uint64_t start, uint64_t length, const std::string& pool_dev,
+ uint64_t dev_id)
+ : DmTarget(start, length), pool_dev_(pool_dev), dev_id_(dev_id) {}
+
+std::string DmTargetThin::GetParameterString() const {
+ std::vector<std::string> args{
+ pool_dev_,
+ std::to_string(dev_id_),
+ };
+ return android::base::Join(args, " ");
+}
+
} // namespace dm
} // namespace android
diff --git a/fs_mgr/libdm/dm_test.cpp b/fs_mgr/libdm/dm_test.cpp
index a0129c2..b890f47 100644
--- a/fs_mgr/libdm/dm_test.cpp
+++ b/fs_mgr/libdm/dm_test.cpp
@@ -37,12 +37,14 @@
#include <gtest/gtest.h>
#include <libdm/dm.h>
#include <libdm/loop_control.h>
+#include <storage_literals/storage_literals.h>
#include "test_util.h"
#include "utility.h"
using namespace std;
using namespace std::chrono_literals;
using namespace android::dm;
+using namespace android::storage_literals;
using android::base::make_scope_guard;
using android::base::unique_fd;
@@ -181,6 +183,13 @@
ASSERT_EQ(dm.GetState(dev.name()), DmDeviceState::ACTIVE);
}
+TEST_F(DmTest, StripeArgs) {
+ DmTargetStripe target(0, 4096, 1024, "/dev/loop0", "/dev/loop1");
+ ASSERT_EQ(target.name(), "striped");
+ ASSERT_TRUE(target.Valid());
+ ASSERT_EQ(target.GetParameterString(), "2 1024 /dev/loop0 0 /dev/loop1 0");
+}
+
TEST_F(DmTest, DmVerityArgsAvb2) {
std::string device = "/dev/block/platform/soc/1da4000.ufshc/by-name/vendor_a";
std::string algorithm = "sha1";
@@ -766,3 +775,42 @@
ASSERT_EQ(name, test_name_);
ASSERT_FALSE(uuid.empty());
}
+
+TEST_F(DmTest, ThinProvisioning) {
+ if (!DeviceMapper::Instance().GetTargetByName("thin-pool", nullptr)) GTEST_SKIP();
+
+ constexpr uint64_t MetaSize = 2_MiB;
+ constexpr uint64_t DataSize = 64_MiB;
+ constexpr uint64_t ThinSize = 1_TiB;
+
+ // Prepare two loop devices for meta and data devices.
+ TemporaryFile meta;
+ ASSERT_GE(meta.fd, 0);
+ ASSERT_EQ(0, ftruncate64(meta.fd, MetaSize));
+ TemporaryFile data;
+ ASSERT_GE(data.fd, 0);
+ ASSERT_EQ(0, ftruncate64(data.fd, DataSize));
+
+ LoopDevice loop_meta(meta.fd, 10s);
+ ASSERT_TRUE(loop_meta.valid());
+ LoopDevice loop_data(data.fd, 10s);
+ ASSERT_TRUE(loop_data.valid());
+
+ // Create a thin-pool
+ DmTable poolTable;
+ poolTable.Emplace<DmTargetThinPool>(0, DataSize / kSectorSize, loop_meta.device(),
+ loop_data.device(), 128, 0);
+ TempDevice pool("pool", poolTable);
+ ASSERT_TRUE(pool.valid());
+
+ // Create a thin volume
+ uint64_t thin_volume_id = 0;
+ ASSERT_TRUE(DeviceMapper::Instance().SendMessage(
+ "pool", 0, "create_thin " + std::to_string(thin_volume_id)));
+
+ // Use a thin volume to create a 1T device
+ DmTable thinTable;
+ thinTable.Emplace<DmTargetThin>(0, ThinSize / kSectorSize, pool.path(), thin_volume_id);
+ TempDevice thin("thin", thinTable);
+ ASSERT_TRUE(thin.valid());
+}
diff --git a/fs_mgr/libdm/include/libdm/dm.h b/fs_mgr/libdm/include/libdm/dm.h
index 22c475f..43d84f9 100644
--- a/fs_mgr/libdm/include/libdm/dm.h
+++ b/fs_mgr/libdm/include/libdm/dm.h
@@ -78,6 +78,7 @@
virtual bool LoadTable(const std::string& name, const DmTable& table) = 0;
virtual bool GetTableInfo(const std::string& name, std::vector<TargetInfo>* table) = 0;
virtual bool GetTableStatus(const std::string& name, std::vector<TargetInfo>* table) = 0;
+ virtual bool GetTableStatusIma(const std::string& name, std::vector<TargetInfo>* table) = 0;
virtual bool GetDmDevicePathByName(const std::string& name, std::string* path) = 0;
virtual bool GetDeviceString(const std::string& name, std::string* dev) = 0;
virtual bool DeleteDeviceIfExists(const std::string& name) = 0;
@@ -267,6 +268,12 @@
// false.
bool GetTableStatus(const std::string& name, std::vector<TargetInfo>* table) override;
+ // Query the status of a table, given a device name. The output vector will
+ // contain IMA TargetInfo for each target in the table. If the device does
+ // not exist, or there were too many targets, the call will fail and return
+ // false.
+ bool GetTableStatusIma(const std::string& name, std::vector<TargetInfo>* table) override;
+
// Identical to GetTableStatus, except also retrives the active table for the device
// mapper device from the kernel.
bool GetTableInfo(const std::string& name, std::vector<TargetInfo>* table) override;
@@ -300,6 +307,9 @@
bool GetDeviceNameAndUuid(dev_t dev, std::string* name, std::string* uuid);
+ // Send |message| to target, pointed by |name| and |sector|. Use 0 if |sector| is not needed.
+ bool SendMessage(const std::string& name, uint64_t sector, const std::string& message);
+
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/libdm/include/libdm/dm_target.h b/fs_mgr/libdm/include/libdm/dm_target.h
index 09fe200..c49fc5e 100644
--- a/fs_mgr/libdm/include/libdm/dm_target.h
+++ b/fs_mgr/libdm/include/libdm/dm_target.h
@@ -116,6 +116,24 @@
uint64_t physical_sector_;
};
+class DmTargetStripe final : public DmTarget {
+ public:
+ DmTargetStripe(uint64_t start, uint64_t length, uint64_t chunksize,
+ const std::string& block_device0, const std::string& block_device1)
+ : DmTarget(start, length),
+ chunksize(chunksize),
+ block_device0_(block_device0),
+ block_device1_(block_device1) {}
+
+ std::string name() const override { return "striped"; }
+ std::string GetParameterString() const override;
+
+ private:
+ uint64_t chunksize;
+ std::string block_device0_;
+ std::string block_device1_;
+};
+
class DmTargetVerity final : public DmTarget {
public:
DmTargetVerity(uint64_t start, uint64_t length, uint32_t version,
@@ -331,6 +349,35 @@
std::string GetParameterString() const override { return ""; }
};
+class DmTargetThinPool final : public DmTarget {
+ public:
+ DmTargetThinPool(uint64_t start, uint64_t length, const std::string& metadata_dev,
+ const std::string& data_dev, uint64_t data_block_size,
+ uint64_t low_water_mark);
+
+ std::string name() const override { return "thin-pool"; }
+ std::string GetParameterString() const override;
+ bool Valid() const override;
+
+ private:
+ std::string metadata_dev_;
+ std::string data_dev_;
+ uint64_t data_block_size_;
+ uint64_t low_water_mark_;
+};
+
+class DmTargetThin final : public DmTarget {
+ public:
+ DmTargetThin(uint64_t start, uint64_t length, const std::string& pool_dev, uint64_t dev_id);
+
+ std::string name() const override { return "thin"; }
+ std::string GetParameterString() const override;
+
+ private:
+ std::string pool_dev_;
+ uint64_t dev_id_;
+};
+
} // namespace dm
} // namespace android
diff --git a/fs_mgr/liblp/fuzzer/liblp_builder_fuzzer.cpp b/fs_mgr/liblp/fuzzer/liblp_builder_fuzzer.cpp
index 7f09ac8..162c9fc 100644
--- a/fs_mgr/liblp/fuzzer/liblp_builder_fuzzer.cpp
+++ b/fs_mgr/liblp/fuzzer/liblp_builder_fuzzer.cpp
@@ -28,7 +28,7 @@
static constexpr uint64_t kBlockDeviceInfoSize = 1024 * 1024;
static constexpr uint64_t kValidBlockDeviceInfoSize = 8_GiB;
static constexpr uint64_t kValidMaxGroupSize = 40960;
-static constexpr uint64_t kMinBlockDevValue = 0;
+static constexpr uint64_t kMinBlockDevValue = 1;
static constexpr uint64_t kMaxBlockDevValue = 100000;
static constexpr uint64_t kMinSectorValue = 1;
static constexpr uint64_t kMaxSectorValue = 1000000;
@@ -149,12 +149,16 @@
void BuilderFuzzer::setupBuilder(string superBlockDeviceName) {
uint64_t blockDeviceInfoSize =
- mFdp.ConsumeBool() ? mFdp.ConsumeIntegral<uint64_t>() : kValidBlockDeviceInfoSize;
+ mFdp.ConsumeBool()
+ ? mFdp.ConsumeIntegralInRange<uint64_t>(kMinBlockDevValue, kMaxBlockDevValue)
+ : kValidBlockDeviceInfoSize;
uint32_t alignment = mFdp.ConsumeBool() ? mFdp.ConsumeIntegral<uint32_t>() : kValidAlignment;
uint32_t alignmentOffset =
mFdp.ConsumeBool() ? mFdp.ConsumeIntegral<uint32_t>() : kValidAlignmentOffset;
- uint32_t logicalBlockSize =
- mFdp.ConsumeBool() ? mFdp.ConsumeIntegral<uint32_t>() : kValidLogicalBlockSize;
+ uint32_t logicalBlockSize = mFdp.ConsumeBool() ? mFdp.ConsumeIntegralInRange<uint32_t>(
+ kMinBlockDevValue, kMaxBlockDevValue)
+ : kValidLogicalBlockSize;
+
BlockDeviceInfo super(superBlockDeviceName, blockDeviceInfoSize, alignment, alignmentOffset,
logicalBlockSize);
mBlockDevices.push_back(super);
@@ -176,13 +180,16 @@
mFdp.ConsumeBool() ? kDeviceInfoName : mFdp.ConsumeRandomLengthString(kMaxBytes);
BlockDeviceInfo changePartitionDeviceInfo(
changePartitionDeviceInfoName,
- mFdp.ConsumeBool() ? mFdp.ConsumeIntegral<uint64_t>() : kBlockDeviceInfoSize /* size */,
+ mFdp.ConsumeBool()
+ ? mFdp.ConsumeIntegralInRange<uint64_t>(kMinBlockDevValue, kMaxBlockDevValue)
+ : kBlockDeviceInfoSize /* size */,
mFdp.ConsumeBool() ? mFdp.ConsumeIntegral<uint32_t>()
: kZeroAlignmentOffset /* alignment */,
mFdp.ConsumeBool() ? mFdp.ConsumeIntegral<uint32_t>()
: kZeroAlignmentOffset /* alignment_offset */,
- mFdp.ConsumeBool() ? mFdp.ConsumeIntegral<uint32_t>()
- : kValidLogicalBlockSize /* logical_block_size */);
+ mFdp.ConsumeBool()
+ ? mFdp.ConsumeIntegralInRange<uint32_t>(kMinBlockDevValue, kMaxBlockDevValue)
+ : kValidLogicalBlockSize);
mBlockDevices.push_back(changePartitionDeviceInfo);
}
diff --git a/fs_mgr/libsnapshot/Android.bp b/fs_mgr/libsnapshot/Android.bp
index 5ceaf28..914b4a6 100644
--- a/fs_mgr/libsnapshot/Android.bp
+++ b/fs_mgr/libsnapshot/Android.bp
@@ -322,24 +322,6 @@
}
cc_test {
- name: "vabc_legacy_tests",
- defaults: [
- "libsnapshot_test_defaults",
- "libsnapshot_hal_deps",
- ],
- cppflags: [
- "-DLIBSNAPSHOT_TEST_VABC_LEGACY",
- ],
- test_suites: [
- "device-tests",
- ],
- test_options: {
- // Legacy VABC launched in Android S.
- min_shipping_api_level: 31,
- },
-}
-
-cc_test {
name: "vts_ota_config_test",
srcs: [
"vts_ota_config_test.cpp",
diff --git a/fs_mgr/libsnapshot/android/snapshot/snapshot.proto b/fs_mgr/libsnapshot/android/snapshot/snapshot.proto
index f2f7fc1..076a918 100644
--- a/fs_mgr/libsnapshot/android/snapshot/snapshot.proto
+++ b/fs_mgr/libsnapshot/android/snapshot/snapshot.proto
@@ -212,6 +212,9 @@
// io_uring support
bool io_uring_enabled = 10;
+
+ // legacy dm-snapshot based snapuserd
+ bool legacy_snapuserd = 11;
}
// Next: 10
diff --git a/fs_mgr/libsnapshot/include/libsnapshot/cow_compress.h b/fs_mgr/libsnapshot/include/libsnapshot/cow_compress.h
index ac04245..21dc666 100644
--- a/fs_mgr/libsnapshot/include/libsnapshot/cow_compress.h
+++ b/fs_mgr/libsnapshot/include/libsnapshot/cow_compress.h
@@ -17,7 +17,6 @@
#pragma once
#include <memory>
-#include <vector>
#include "libsnapshot/cow_format.h"
namespace android {
@@ -25,27 +24,30 @@
class ICompressor {
public:
- explicit ICompressor(uint32_t compression_level, uint32_t block_size)
+ explicit ICompressor(const int32_t compression_level, const uint32_t block_size)
: compression_level_(compression_level), block_size_(block_size) {}
virtual ~ICompressor() {}
// Factory methods for compression methods.
- static std::unique_ptr<ICompressor> Gz(uint32_t compression_level, const int32_t block_size);
- static std::unique_ptr<ICompressor> Brotli(uint32_t compression_level,
- const int32_t block_size);
- static std::unique_ptr<ICompressor> Lz4(uint32_t compression_level, const int32_t block_size);
- static std::unique_ptr<ICompressor> Zstd(uint32_t compression_level, const int32_t block_size);
+ static std::unique_ptr<ICompressor> Gz(const int32_t compression_level,
+ const uint32_t block_size);
+ static std::unique_ptr<ICompressor> Brotli(const int32_t compression_level,
+ const uint32_t block_size);
+ static std::unique_ptr<ICompressor> Lz4(const int32_t compression_level,
+ const uint32_t block_size);
+ static std::unique_ptr<ICompressor> Zstd(const int32_t compression_level,
+ const uint32_t block_size);
static std::unique_ptr<ICompressor> Create(CowCompression compression,
- const int32_t block_size);
+ const uint32_t block_size);
- uint32_t GetCompressionLevel() const { return compression_level_; }
+ int32_t GetCompressionLevel() const { return compression_level_; }
uint32_t GetBlockSize() const { return block_size_; }
[[nodiscard]] virtual std::vector<uint8_t> Compress(const void* data, size_t length) const = 0;
private:
- uint32_t compression_level_;
- uint32_t block_size_;
+ const int32_t compression_level_;
+ const uint32_t block_size_;
};
} // namespace snapshot
} // namespace android
\ No newline at end of file
diff --git a/fs_mgr/libsnapshot/include/libsnapshot/cow_format.h b/fs_mgr/libsnapshot/include/libsnapshot/cow_format.h
index 6865b19..6a7153d 100644
--- a/fs_mgr/libsnapshot/include/libsnapshot/cow_format.h
+++ b/fs_mgr/libsnapshot/include/libsnapshot/cow_format.h
@@ -293,7 +293,7 @@
};
struct CowCompression {
CowCompressionAlgorithm algorithm = kCowCompressNone;
- uint32_t compression_level = 0;
+ int32_t compression_level = 0;
};
static constexpr uint8_t kCowReadAheadNotStarted = 0;
diff --git a/fs_mgr/libsnapshot/include/libsnapshot/cow_writer.h b/fs_mgr/libsnapshot/include/libsnapshot/cow_writer.h
index 651083f..2c6eefb 100644
--- a/fs_mgr/libsnapshot/include/libsnapshot/cow_writer.h
+++ b/fs_mgr/libsnapshot/include/libsnapshot/cow_writer.h
@@ -45,7 +45,7 @@
std::optional<uint64_t> max_blocks;
// Number of CowOperations in a cluster. 0 for no clustering. Cannot be 1.
- uint32_t cluster_ops = 200;
+ uint32_t cluster_ops = 1024;
bool scratch_space = true;
diff --git a/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h b/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h
index d102863..3ccc3db 100644
--- a/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h
+++ b/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h
@@ -829,6 +829,9 @@
// Set read-ahead size during OTA
void SetReadAheadSize(const std::string& entry_block_device, off64_t size_kb);
+ // Returns true post OTA reboot if legacy snapuserd is required
+ bool IsLegacySnapuserdPostReboot();
+
android::dm::IDeviceMapper& dm_;
std::unique_ptr<IDeviceInfo> device_;
std::string metadata_dir_;
@@ -839,6 +842,7 @@
std::unique_ptr<SnapuserdClient> snapuserd_client_;
std::unique_ptr<LpMetadata> old_partition_metadata_;
std::optional<bool> is_snapshot_userspace_;
+ std::optional<bool> is_legacy_snapuserd_;
};
} // namespace snapshot
diff --git a/fs_mgr/libsnapshot/include_test/libsnapshot/test_helpers.h b/fs_mgr/libsnapshot/include_test/libsnapshot/test_helpers.h
index 5e9f049..90813fe 100644
--- a/fs_mgr/libsnapshot/include_test/libsnapshot/test_helpers.h
+++ b/fs_mgr/libsnapshot/include_test/libsnapshot/test_helpers.h
@@ -155,6 +155,9 @@
virtual bool GetTableStatus(const std::string& name, std::vector<TargetInfo>* table) {
return impl_.GetTableStatus(name, table);
}
+ virtual bool GetTableStatusIma(const std::string& name, std::vector<TargetInfo>* table) {
+ return impl_.GetTableStatusIma(name, table);
+ }
virtual bool GetDmDevicePathByName(const std::string& name, std::string* path) {
return impl_.GetDmDevicePathByName(name, path);
}
diff --git a/fs_mgr/libsnapshot/libsnapshot_cow/cow_compress.cpp b/fs_mgr/libsnapshot/libsnapshot_cow/cow_compress.cpp
index 0205f50..bff5257 100644
--- a/fs_mgr/libsnapshot/libsnapshot_cow/cow_compress.cpp
+++ b/fs_mgr/libsnapshot/libsnapshot_cow/cow_compress.cpp
@@ -17,6 +17,7 @@
#include <sys/types.h>
#include <unistd.h>
+#include <cstdint>
#include <limits>
#include <memory>
#include <queue>
@@ -57,7 +58,7 @@
}
std::unique_ptr<ICompressor> ICompressor::Create(CowCompression compression,
- const int32_t block_size) {
+ const uint32_t block_size) {
switch (compression.algorithm) {
case kCowCompressLz4:
return ICompressor::Lz4(compression.compression_level, block_size);
@@ -101,7 +102,7 @@
class GzCompressor final : public ICompressor {
public:
- GzCompressor(uint32_t compression_level, const uint32_t block_size)
+ GzCompressor(int32_t compression_level, const uint32_t block_size)
: ICompressor(compression_level, block_size){};
std::vector<uint8_t> Compress(const void* data, size_t length) const override {
@@ -122,7 +123,7 @@
class Lz4Compressor final : public ICompressor {
public:
- Lz4Compressor(uint32_t compression_level, const uint32_t block_size)
+ Lz4Compressor(int32_t compression_level, const uint32_t block_size)
: ICompressor(compression_level, block_size){};
std::vector<uint8_t> Compress(const void* data, size_t length) const override {
@@ -154,7 +155,7 @@
class BrotliCompressor final : public ICompressor {
public:
- BrotliCompressor(uint32_t compression_level, const uint32_t block_size)
+ BrotliCompressor(int32_t compression_level, const uint32_t block_size)
: ICompressor(compression_level, block_size){};
std::vector<uint8_t> Compress(const void* data, size_t length) const override {
@@ -180,7 +181,7 @@
class ZstdCompressor final : public ICompressor {
public:
- ZstdCompressor(uint32_t compression_level, const uint32_t block_size)
+ ZstdCompressor(int32_t compression_level, const uint32_t block_size)
: ICompressor(compression_level, block_size),
zstd_context_(ZSTD_createCCtx(), ZSTD_freeCCtx) {
ZSTD_CCtx_setParameter(zstd_context_.get(), ZSTD_c_compressionLevel, compression_level);
@@ -318,22 +319,23 @@
}
}
-std::unique_ptr<ICompressor> ICompressor::Brotli(uint32_t compression_level,
- const int32_t block_size) {
+std::unique_ptr<ICompressor> ICompressor::Brotli(const int32_t compression_level,
+ const uint32_t block_size) {
return std::make_unique<BrotliCompressor>(compression_level, block_size);
}
-std::unique_ptr<ICompressor> ICompressor::Gz(uint32_t compression_level, const int32_t block_size) {
+std::unique_ptr<ICompressor> ICompressor::Gz(const int32_t compression_level,
+ const uint32_t block_size) {
return std::make_unique<GzCompressor>(compression_level, block_size);
}
-std::unique_ptr<ICompressor> ICompressor::Lz4(uint32_t compression_level,
- const int32_t block_size) {
+std::unique_ptr<ICompressor> ICompressor::Lz4(const int32_t compression_level,
+ const uint32_t block_size) {
return std::make_unique<Lz4Compressor>(compression_level, block_size);
}
-std::unique_ptr<ICompressor> ICompressor::Zstd(uint32_t compression_level,
- const int32_t block_size) {
+std::unique_ptr<ICompressor> ICompressor::Zstd(const int32_t compression_level,
+ const uint32_t block_size) {
return std::make_unique<ZstdCompressor>(compression_level, block_size);
}
diff --git a/fs_mgr/libsnapshot/libsnapshot_cow/test_v3.cpp b/fs_mgr/libsnapshot/libsnapshot_cow/test_v3.cpp
index 2021348..4456b26 100644
--- a/fs_mgr/libsnapshot/libsnapshot_cow/test_v3.cpp
+++ b/fs_mgr/libsnapshot/libsnapshot_cow/test_v3.cpp
@@ -209,6 +209,48 @@
ASSERT_EQ(sink, data);
}
+TEST_F(CowTestV3, BigReplaceOp) {
+ CowOptions options;
+ options.op_count_max = 10000;
+ options.batch_write = true;
+ options.cluster_ops = 2048;
+
+ auto writer = CreateCowWriter(3, options, GetCowFd());
+ std::string data = "This is some data, believe it";
+ data.resize(options.block_size * 4096, '\0');
+ for (int i = 0; i < data.size(); i++) {
+ data[i] = static_cast<char>('A' + i / options.block_size);
+ }
+ ASSERT_TRUE(writer->AddRawBlocks(5, data.data(), data.size()));
+ ASSERT_TRUE(writer->Finalize());
+
+ CowReader reader;
+ ASSERT_TRUE(reader.Parse(cow_->fd));
+
+ const auto& header = reader.header_v3();
+ ASSERT_EQ(header.op_count, 4096);
+
+ auto iter = reader.GetOpIter();
+ ASSERT_NE(iter, nullptr);
+ ASSERT_FALSE(iter->AtEnd());
+
+ size_t i = 0;
+
+ while (!iter->AtEnd()) {
+ auto op = iter->Get();
+ std::string sink(options.block_size, '\0');
+ ASSERT_EQ(op->type(), kCowReplaceOp);
+ ASSERT_EQ(op->data_length, options.block_size);
+ ASSERT_EQ(op->new_block, 5 + i);
+ ASSERT_TRUE(ReadData(reader, op, sink.data(), options.block_size));
+ ASSERT_EQ(std::string_view(sink),
+ std::string_view(data).substr(i * options.block_size, options.block_size))
+ << " readback data for " << i << "th block does not match";
+ iter->Next();
+ i++;
+ }
+}
+
TEST_F(CowTestV3, ConsecutiveReplaceOp) {
CowOptions options;
options.op_count_max = 20;
diff --git a/fs_mgr/libsnapshot/libsnapshot_cow/writer_v2.cpp b/fs_mgr/libsnapshot/libsnapshot_cow/writer_v2.cpp
index d0864e0..0993dba 100644
--- a/fs_mgr/libsnapshot/libsnapshot_cow/writer_v2.cpp
+++ b/fs_mgr/libsnapshot/libsnapshot_cow/writer_v2.cpp
@@ -134,7 +134,7 @@
return false;
}
if (parts.size() > 1) {
- if (!android::base::ParseUint(parts[1], &compression_.compression_level)) {
+ if (!android::base::ParseInt(parts[1], &compression_.compression_level)) {
LOG(ERROR) << "failed to parse compression level invalid type: " << parts[1];
return false;
}
diff --git a/fs_mgr/libsnapshot/libsnapshot_cow/writer_v3.cpp b/fs_mgr/libsnapshot/libsnapshot_cow/writer_v3.cpp
index 30c5135..95398e4 100644
--- a/fs_mgr/libsnapshot/libsnapshot_cow/writer_v3.cpp
+++ b/fs_mgr/libsnapshot/libsnapshot_cow/writer_v3.cpp
@@ -149,7 +149,7 @@
}
if (parts.size() > 1) {
- if (!android::base::ParseUint(parts[1], &compression_.compression_level)) {
+ if (!android::base::ParseInt(parts[1], &compression_.compression_level)) {
LOG(ERROR) << "failed to parse compression level invalid type: " << parts[1];
return false;
}
@@ -173,7 +173,7 @@
batch_size_ = std::max<size_t>(options_.cluster_ops, 1);
data_vec_.reserve(batch_size_);
cached_data_.reserve(batch_size_);
- cached_ops_.reserve(batch_size_);
+ cached_ops_.reserve(batch_size_ * kNonDataOpBufferSize);
}
if (batch_size_ > 1) {
@@ -214,15 +214,6 @@
return false;
}
}
-
- // TODO: b/322279333
- // Set compression factor to 4k during estimation.
- // Once COW estimator is ready to support variable
- // block size, this check has to be removed.
- if (IsEstimating()) {
- header_.max_compression_size = header_.block_size;
- }
-
return true;
}
@@ -310,6 +301,14 @@
return true;
}
+size_t CowWriterV3::CachedDataSize() const {
+ size_t size = 0;
+ for (const auto& i : cached_data_) {
+ size += i.size();
+ }
+ return size;
+}
+
bool CowWriterV3::EmitCopy(uint64_t new_block, uint64_t old_block, uint64_t num_blocks) {
if (!CheckOpCount(num_blocks)) {
return false;
@@ -342,7 +341,8 @@
// Allow bigger batch sizes for ops without data. A single CowOperationV3
// struct uses 14 bytes of memory, even if we cache 200 * 16 ops in memory,
// it's only ~44K.
- return cached_data_.size() >= batch_size_ || cached_ops_.size() >= batch_size_ * 16;
+ return CachedDataSize() >= batch_size_ * header_.block_size ||
+ cached_ops_.size() >= batch_size_ * kNonDataOpBufferSize;
}
bool CowWriterV3::ConstructCowOpCompressedBuffers(uint64_t new_block_start, const void* data,
@@ -396,13 +396,13 @@
return false;
}
const auto bytes = reinterpret_cast<const uint8_t*>(data);
- const size_t num_blocks = (size / header_.block_size);
- for (size_t i = 0; i < num_blocks;) {
- const size_t blocks_to_write =
- std::min<size_t>(batch_size_ - cached_data_.size(), num_blocks - i);
-
- if (!ConstructCowOpCompressedBuffers(new_block_start + i, bytes + header_.block_size * i,
- old_block + i, offset, type, blocks_to_write)) {
+ size_t num_blocks = (size / header_.block_size);
+ size_t total_written = 0;
+ while (total_written < num_blocks) {
+ size_t chunk = std::min(num_blocks - total_written, batch_size_);
+ if (!ConstructCowOpCompressedBuffers(new_block_start + total_written,
+ bytes + header_.block_size * total_written,
+ old_block + total_written, offset, type, chunk)) {
return false;
}
@@ -412,8 +412,7 @@
<< ", op type: " << type;
return false;
}
-
- i += blocks_to_write;
+ total_written += chunk;
}
return true;
@@ -481,7 +480,8 @@
header_.sequence_data_count = num_ops;
- // Ensure next_data_pos_ is updated as previously initialized + the newly added sequence buffer.
+ // Ensure next_data_pos_ is updated as previously initialized + the newly added sequence
+ // buffer.
CHECK_EQ(next_data_pos_ + header_.sequence_data_count * sizeof(uint32_t),
GetDataOffset(header_));
next_data_pos_ = GetDataOffset(header_);
@@ -603,41 +603,47 @@
std::vector<CowWriterV3::CompressedBuffer> CowWriterV3::ProcessBlocksWithThreadedCompression(
const size_t num_blocks, const void* data, CowOperationType type) {
const size_t num_threads = num_compress_threads_;
- const size_t blocks_per_thread = DivRoundUp(num_blocks, num_threads);
const uint8_t* iter = reinterpret_cast<const uint8_t*>(data);
+ // We will alternate which thread to send compress work to. E.g. alternate between T1 and T2
+ // until all blocks are processed
std::vector<CompressedBuffer> compressed_vec;
- // Submit the blocks per thread. The retrieval of
- // compressed buffers has to be done in the same order.
- // We should not poll for completed buffers in a different order as the
- // buffers are tightly coupled with block ordering.
- for (size_t i = 0; i < num_threads; i++) {
- CompressWorker* worker = compress_threads_[i].get();
- auto blocks_in_batch = std::min(num_blocks - i * blocks_per_thread, blocks_per_thread);
- // Enqueue the blocks to be compressed for each thread.
- while (blocks_in_batch) {
- CompressedBuffer buffer;
+ int iteration = 0;
+ int blocks_to_compress = static_cast<int>(num_blocks);
+ while (blocks_to_compress) {
+ CompressedBuffer buffer;
+ CompressWorker* worker = compress_threads_[iteration % num_threads].get();
- const size_t compression_factor = GetCompressionFactor(blocks_in_batch, type);
- size_t num_blocks = compression_factor / header_.block_size;
+ const size_t compression_factor = GetCompressionFactor(blocks_to_compress, type);
+ size_t num_blocks = compression_factor / header_.block_size;
- buffer.compression_factor = compression_factor;
- worker->EnqueueCompressBlocks(iter, compression_factor, 1);
- compressed_vec.push_back(std::move(buffer));
- blocks_in_batch -= num_blocks;
- iter += compression_factor;
- }
+ worker->EnqueueCompressBlocks(iter, compression_factor, 1);
+ buffer.compression_factor = compression_factor;
+ compressed_vec.push_back(std::move(buffer));
+
+ iteration++;
+ iter += compression_factor;
+ blocks_to_compress -= num_blocks;
}
- // Fetch compressed buffers from the threads
std::vector<std::vector<uint8_t>> compressed_buf;
+ std::vector<std::vector<std::vector<uint8_t>>> worker_buffers(num_threads);
compressed_buf.clear();
for (size_t i = 0; i < num_threads; i++) {
CompressWorker* worker = compress_threads_[i].get();
- if (!worker->GetCompressedBuffers(&compressed_buf)) {
+ if (!worker->GetCompressedBuffers(&worker_buffers[i])) {
return {};
}
}
+ // compressed_vec | CB 1 | CB 2 | CB 3 | CB 4 | <-compressed buffers
+ // t1 t2 t1 t2 <- processed by these threads
+ // Ordering is important here. We need to retrieve the compressed data in the same order we
+ // processed it and assume that that we submit data beginning with the first thread and then
+ // round robin the consecutive data calls. We need to Fetch compressed buffers from the
+ // threads via the same ordering
+ for (size_t i = 0; i < compressed_vec.size(); i++) {
+ compressed_buf.emplace_back(worker_buffers[i % num_threads][i / num_threads]);
+ }
if (compressed_vec.size() != compressed_buf.size()) {
LOG(ERROR) << "Compressed buffer size: " << compressed_buf.size()
@@ -710,13 +716,28 @@
return false;
}
if (!data.empty()) {
- const auto ret = pwritev(fd_, data.data(), data.size(), next_data_pos_);
- if (ret != total_data_size) {
+ int total_written = 0;
+ int i = 0;
+ while (i < data.size()) {
+ int chunk = std::min(static_cast<int>(data.size() - i), IOV_MAX);
+
+ const auto ret = pwritev(fd_, data.data() + i, chunk, next_data_pos_ + total_written);
+ if (ret < 0) {
+ PLOG(ERROR) << "write failed chunk size of: " << chunk
+ << " at offset: " << next_data_pos_ + total_written << " " << errno;
+ return false;
+ }
+ total_written += ret;
+ i += chunk;
+ }
+ if (total_written != total_data_size) {
PLOG(ERROR) << "write failed for data of size: " << data.size()
- << " at offset: " << next_data_pos_ << " " << ret;
+ << " at offset: " << next_data_pos_ << " " << errno
+ << ", only wrote: " << total_written;
return false;
}
}
+
header_.op_count += ops.size();
next_data_pos_ += total_data_size;
diff --git a/fs_mgr/libsnapshot/libsnapshot_cow/writer_v3.h b/fs_mgr/libsnapshot/libsnapshot_cow/writer_v3.h
index 48eb67b..871ed27 100644
--- a/fs_mgr/libsnapshot/libsnapshot_cow/writer_v3.h
+++ b/fs_mgr/libsnapshot/libsnapshot_cow/writer_v3.h
@@ -28,6 +28,9 @@
namespace snapshot {
using namespace android::storage_literals;
+// This is a multiple on top of the number of data ops that can be stored in our cache at once. This
+// is added so that we can cache more non-data ops as it takes up less space.
+static constexpr uint32_t kNonDataOpBufferSize = 16;
class CowWriterV3 : public CowWriterBase {
public:
@@ -91,7 +94,7 @@
}
return false;
}
-
+ size_t CachedDataSize() const;
bool ReadBackVerification();
bool FlushCacheOps();
void InitWorkers();
diff --git a/fs_mgr/libsnapshot/partition_cow_creator_test.cpp b/fs_mgr/libsnapshot/partition_cow_creator_test.cpp
index cf26a16..a4a2c1a 100644
--- a/fs_mgr/libsnapshot/partition_cow_creator_test.cpp
+++ b/fs_mgr/libsnapshot/partition_cow_creator_test.cpp
@@ -39,6 +39,7 @@
namespace android {
namespace snapshot {
+// @VsrTest = 3.7.6
class PartitionCowCreatorTest : public ::testing::Test {
public:
void SetUp() override {
diff --git a/fs_mgr/libsnapshot/snapshot.cpp b/fs_mgr/libsnapshot/snapshot.cpp
index cbe8285..7ca53ad 100644
--- a/fs_mgr/libsnapshot/snapshot.cpp
+++ b/fs_mgr/libsnapshot/snapshot.cpp
@@ -24,7 +24,6 @@
#include <filesystem>
#include <optional>
#include <thread>
-#include <unordered_set>
#include <android-base/file.h>
#include <android-base/logging.h>
@@ -46,7 +45,6 @@
#include <android/snapshot/snapshot.pb.h>
#include <libsnapshot/snapshot_stats.h>
#include "device_info.h"
-#include "libsnapshot_cow/parser_v2.h"
#include "partition_cow_creator.h"
#include "snapshot_metadata_updater.h"
#include "utility.h"
@@ -265,7 +263,6 @@
auto boot_file = GetSnapshotBootIndicatorPath();
std::string contents;
if (!android::base::ReadFileToString(boot_file, &contents)) {
- PLOG(WARNING) << "Cannot read " << boot_file;
return {};
}
return contents;
@@ -508,8 +505,6 @@
// When snapshots are on current slot, we determine the size
// of block device based on the number of COW operations. We cannot
// use base device as it will be from older image.
- size_t num_ops = 0;
- uint64_t dev_sz = 0;
unique_fd fd(open(cow_file.c_str(), O_RDONLY | O_CLOEXEC));
if (fd < 0) {
PLOG(ERROR) << "Failed to open " << cow_file;
@@ -522,13 +517,18 @@
return false;
}
+ uint64_t dev_sz = 0;
const auto& header = reader.GetHeader();
- if (header.prefix.major_version > 2) {
- LOG(ERROR) << "COW format not supported";
- return false;
+ if (header.prefix.major_version == 2) {
+ const size_t num_ops = reader.get_num_total_data_ops();
+ dev_sz = (num_ops * header.block_size);
+ } else {
+ // create_snapshot will skip in-place copy ops. Hence, fetch this
+ // information directly from v3 header.
+ const auto& v3_header = reader.header_v3();
+ dev_sz = v3_header.op_count_max * v3_header.block_size;
}
- num_ops = reader.get_num_total_data_ops();
- dev_sz = (num_ops * header.block_size);
+
base_sectors = dev_sz >> 9;
} else {
// For userspace snapshots, the size of the base device is taken as the
@@ -2115,6 +2115,53 @@
return update_status.io_uring_enabled();
}
+/*
+ * Please see b/304829384 for more details.
+ *
+ * In Android S, we use dm-snapshot for mounting snapshots and snapshot-merge
+ * process. If the vendor partition continues to be on Android S, then
+ * "snapuserd" binary in first stage ramdisk will be from vendor partition.
+ * Thus, we need to maintain backward compatibility.
+ *
+ * Now, We take a two step approach to maintain the backward compatibility:
+ *
+ * 1: During OTA installation, we will continue to use "user-space" snapshots
+ * for OTA installation as both update-engine and snapuserd binary will be from system partition.
+ * However, during installation, we mark "legacy_snapuserd" in
+ * SnapshotUpdateStatus file to mark that this is a path to support backward compatibility.
+ * Thus, this function will return "false" during OTA installation.
+ *
+ * 2: Post OTA reboot, there are two key steps:
+ * a: During first stage init, "init" and "snapuserd" could be from vendor
+ * partition. This could be from Android S. Thus, the snapshot mount path
+ * will be based off dm-snapshot.
+ *
+ * b: Post selinux transition, "init" and "update-engine" will be "system"
+ * partition. Now, since the snapshots are mounted off dm-snapshot,
+ * update-engine interaction with "snapuserd" should work based off
+ * dm-snapshots.
+ *
+ * TL;DR: update-engine will use the "system" snapuserd for installing new
+ * updates (this is safe as there is no "vendor" snapuserd running during
+ * installation). Post reboot, update-engine will use the legacy path when
+ * communicating with "vendor" snapuserd that was started in first-stage
+ * init. Hence, this function checks:
+ * i: Are we in post OTA reboot
+ * ii: Is the Vendor from Android 12
+ * iii: If both (i) and (ii) are true, then use the dm-snapshot based
+ * approach.
+ *
+ */
+bool SnapshotManager::IsLegacySnapuserdPostReboot() {
+ if (is_legacy_snapuserd_.has_value() && is_legacy_snapuserd_.value() == true) {
+ auto slot = GetCurrentSlot();
+ if (slot == Slot::Target) {
+ return true;
+ }
+ }
+ return false;
+}
+
bool SnapshotManager::UpdateUsesUserSnapshots() {
// This and the following function is constantly
// invoked during snapshot merge. We want to avoid
@@ -2126,7 +2173,12 @@
// during merge phase. Hence, once we know that
// the value is read from disk the very first time,
// it is safe to read successive checks from memory.
+
if (is_snapshot_userspace_.has_value()) {
+ // Check if legacy snapuserd is running post OTA reboot
+ if (IsLegacySnapuserdPostReboot()) {
+ return false;
+ }
return is_snapshot_userspace_.value();
}
@@ -2137,13 +2189,16 @@
}
bool SnapshotManager::UpdateUsesUserSnapshots(LockedFile* lock) {
- // See UpdateUsesUserSnapshots()
- if (is_snapshot_userspace_.has_value()) {
- return is_snapshot_userspace_.value();
+ if (!is_snapshot_userspace_.has_value()) {
+ SnapshotUpdateStatus update_status = ReadSnapshotUpdateStatus(lock);
+ is_snapshot_userspace_ = update_status.userspace_snapshots();
+ is_legacy_snapuserd_ = update_status.legacy_snapuserd();
}
- SnapshotUpdateStatus update_status = ReadSnapshotUpdateStatus(lock);
- is_snapshot_userspace_ = update_status.userspace_snapshots();
+ if (IsLegacySnapuserdPostReboot()) {
+ return false;
+ }
+
return is_snapshot_userspace_.value();
}
@@ -2961,6 +3016,7 @@
status.set_merge_phase(old_status.merge_phase());
status.set_userspace_snapshots(old_status.userspace_snapshots());
status.set_io_uring_enabled(old_status.io_uring_enabled());
+ status.set_legacy_snapuserd(old_status.legacy_snapuserd());
}
return WriteSnapshotUpdateStatus(lock, status);
}
@@ -3207,6 +3263,8 @@
// Deduce supported features.
bool userspace_snapshots = CanUseUserspaceSnapshots();
bool legacy_compression = GetLegacyCompressionEnabledProperty();
+ bool is_legacy_snapuserd = IsVendorFromAndroid12();
+
if (!vabc_disable_reason.empty()) {
if (userspace_snapshots) {
LOG(INFO) << "Userspace snapshots disabled: " << vabc_disable_reason;
@@ -3216,6 +3274,7 @@
}
userspace_snapshots = false;
legacy_compression = false;
+ is_legacy_snapuserd = false;
}
if (legacy_compression || userspace_snapshots) {
@@ -3228,6 +3287,11 @@
}
}
+ if (!userspace_snapshots && is_legacy_snapuserd && legacy_compression) {
+ userspace_snapshots = true;
+ LOG(INFO) << "Vendor from Android 12. Enabling userspace snapshot for OTA install";
+ }
+
const bool using_snapuserd = userspace_snapshots || legacy_compression;
if (!using_snapuserd) {
LOG(INFO) << "Using legacy Virtual A/B (dm-snapshot)";
@@ -3243,7 +3307,7 @@
compression_algorithm = "gz";
}
LOG(INFO) << "using compression algorithm: " << compression_algorithm
- << ", max compressible block size: " << compression_factor;
+ << ", max compressible block size: " << compression_factor;
}
PartitionCowCreator cow_creator{
@@ -3325,6 +3389,11 @@
status.set_io_uring_enabled(true);
LOG(INFO) << "io_uring for snapshots enabled";
}
+
+ if (is_legacy_snapuserd) {
+ LOG(INFO) << "Setting legacy_snapuserd to true";
+ status.set_legacy_snapuserd(true);
+ }
} else if (legacy_compression) {
LOG(INFO) << "Virtual A/B using legacy snapuserd";
} else {
@@ -3332,6 +3401,7 @@
}
is_snapshot_userspace_.emplace(userspace_snapshots);
+ is_legacy_snapuserd_.emplace(is_legacy_snapuserd);
if (!device()->IsTestDevice() && using_snapuserd) {
// Terminate stale daemon if any
diff --git a/fs_mgr/libsnapshot/snapshot_test.cpp b/fs_mgr/libsnapshot/snapshot_test.cpp
index e538d50..80dad17 100644
--- a/fs_mgr/libsnapshot/snapshot_test.cpp
+++ b/fs_mgr/libsnapshot/snapshot_test.cpp
@@ -56,14 +56,12 @@
#if defined(LIBSNAPSHOT_TEST_VAB_LEGACY)
#define DEFAULT_MODE "vab-legacy"
-#elif defined(LIBSNAPSHOT_TEST_VABC_LEGACY)
-#define DEFAULT_MODE "vabc-legacy"
#else
#define DEFAULT_MODE ""
#endif
DEFINE_string(force_mode, DEFAULT_MODE,
- "Force testing older modes (vab-legacy, vabc-legacy) ignoring device config.");
+ "Force testing older modes (vab-legacy) ignoring device config.");
DEFINE_string(force_iouring_disable, "",
"Force testing mode (iouring_disabled) - disable io_uring");
DEFINE_string(compression_method, "gz", "Default compression algorithm.");
@@ -104,6 +102,7 @@
void MountMetadata();
+// @VsrTest = 3.7.6
class SnapshotTest : public ::testing::Test {
public:
SnapshotTest() : dm_(DeviceMapper::Instance()) {}
@@ -139,17 +138,10 @@
void SetupProperties() {
std::unordered_map<std::string, std::string> properties;
- ASSERT_TRUE(android::base::SetProperty("snapuserd.test.dm.snapshots", "0"))
- << "Failed to disable property: virtual_ab.userspace.snapshots.enabled";
ASSERT_TRUE(android::base::SetProperty("snapuserd.test.io_uring.force_disable", "0"))
<< "Failed to set property: snapuserd.test.io_uring.disabled";
- if (FLAGS_force_mode == "vabc-legacy") {
- ASSERT_TRUE(android::base::SetProperty("snapuserd.test.dm.snapshots", "1"))
- << "Failed to disable property: virtual_ab.userspace.snapshots.enabled";
- properties["ro.virtual_ab.compression.enabled"] = "true";
- properties["ro.virtual_ab.userspace.snapshots.enabled"] = "false";
- } else if (FLAGS_force_mode == "vab-legacy") {
+ if (FLAGS_force_mode == "vab-legacy") {
properties["ro.virtual_ab.compression.enabled"] = "false";
properties["ro.virtual_ab.userspace.snapshots.enabled"] = "false";
}
@@ -2891,22 +2883,29 @@
::testing::AddGlobalTestEnvironment(new ::android::snapshot::SnapshotTestEnvironment());
gflags::ParseCommandLineFlags(&argc, &argv, false);
- android::base::SetProperty("ctl.stop", "snapuserd");
+ bool vab_legacy = false;
+ if (FLAGS_force_mode == "vab-legacy") {
+ vab_legacy = true;
+ }
- std::unordered_set<std::string> modes = {"", "vab-legacy", "vabc-legacy"};
+ if (!vab_legacy) {
+ // This is necessary if the configuration we're testing doesn't match the device.
+ android::base::SetProperty("ctl.stop", "snapuserd");
+ android::snapshot::KillSnapuserd();
+ }
+
+ std::unordered_set<std::string> modes = {"", "vab-legacy"};
if (modes.count(FLAGS_force_mode) == 0) {
std::cerr << "Unexpected force_config argument\n";
return 1;
}
- // This is necessary if the configuration we're testing doesn't match the device.
- android::snapshot::KillSnapuserd();
-
int ret = RUN_ALL_TESTS();
- android::base::SetProperty("snapuserd.test.dm.snapshots", "0");
android::base::SetProperty("snapuserd.test.io_uring.force_disable", "0");
- android::snapshot::KillSnapuserd();
+ if (!vab_legacy) {
+ android::snapshot::KillSnapuserd();
+ }
return ret;
}
diff --git a/fs_mgr/libsnapshot/snapshotctl.cpp b/fs_mgr/libsnapshot/snapshotctl.cpp
index 0396a55..192e1d6 100644
--- a/fs_mgr/libsnapshot/snapshotctl.cpp
+++ b/fs_mgr/libsnapshot/snapshotctl.cpp
@@ -15,6 +15,7 @@
//
#include <sysexits.h>
+#include <unistd.h>
#include <chrono>
#include <filesystem>
@@ -53,6 +54,9 @@
using namespace std::chrono_literals;
using namespace std::string_literals;
using namespace android::storage_literals;
+using android::base::LogdLogger;
+using android::base::StderrLogger;
+using android::base::TeeLogger;
using android::fs_mgr::CreateLogicalPartitionParams;
using android::fs_mgr::FindPartition;
using android::fs_mgr::GetPartitionSize;
@@ -79,13 +83,18 @@
" revert-snapshots\n"
" Prepares devices to boot without snapshots on next boot.\n"
" This does not delete the snapshot. It only removes the indicators\n"
- " so that first stage init will not mount from snapshots.\n";
+ " so that first stage init will not mount from snapshots.\n"
+ " apply-update\n"
+ " Apply the incremental OTA update wherein the snapshots are\n"
+ " directly written to COW block device. This will bypass update-engine\n"
+ " and the device will be ready to boot from the target build.\n";
return EX_USAGE;
}
namespace android {
namespace snapshot {
+#ifdef SNAPSHOTCTL_USERDEBUG_OR_ENG
class MapSnapshots {
public:
MapSnapshots(std::string path = "");
@@ -96,14 +105,22 @@
bool DeleteSnapshots();
bool CleanupSnapshot() { return sm_->PrepareDeviceToBootWithoutSnapshot(); }
bool BeginUpdate();
+ bool ApplyUpdate();
private:
std::optional<std::string> GetCowImagePath(std::string& name);
+ bool PrepareUpdate();
bool WriteSnapshotPatch(std::string cow_device, std::string patch);
+ std::string GetGroupName(const android::fs_mgr::LpMetadata& pt,
+ const std::string& partiton_name);
std::unique_ptr<SnapshotManager::LockedFile> lock_;
std::unique_ptr<SnapshotManager> sm_;
std::vector<std::future<bool>> threads_;
std::string snapshot_dir_path_;
+ std::unordered_map<std::string, chromeos_update_engine::DynamicPartitionGroup*> group_map_;
+
+ std::vector<std::string> patchfiles_;
+ chromeos_update_engine::DeltaArchiveManifest manifest_;
};
MapSnapshots::MapSnapshots(std::string path) {
@@ -115,6 +132,178 @@
snapshot_dir_path_ = path + "/";
}
+std::string MapSnapshots::GetGroupName(const android::fs_mgr::LpMetadata& pt,
+ const std::string& partition_name) {
+ std::string group_name;
+ for (const auto& partition : pt.partitions) {
+ std::string name = android::fs_mgr::GetPartitionName(partition);
+ auto suffix = android::fs_mgr::GetPartitionSlotSuffix(name);
+ std::string pname = name.substr(0, name.size() - suffix.size());
+ if (pname == partition_name) {
+ std::string group_name =
+ android::fs_mgr::GetPartitionGroupName(pt.groups[partition.group_index]);
+ return group_name.substr(0, group_name.size() - suffix.size());
+ }
+ }
+ return "";
+}
+
+bool MapSnapshots::PrepareUpdate() {
+ auto source_slot = fs_mgr_get_slot_suffix();
+ auto source_slot_number = SlotNumberForSlotSuffix(source_slot);
+ auto super_source = fs_mgr_get_super_partition_name(source_slot_number);
+
+ // Get current partition information.
+ PartitionOpener opener;
+ auto source_metadata = ReadMetadata(opener, super_source, source_slot_number);
+ if (!source_metadata) {
+ LOG(ERROR) << "Could not read source partition metadata.\n";
+ return false;
+ }
+
+ auto dap = manifest_.mutable_dynamic_partition_metadata();
+ dap->set_snapshot_enabled(true);
+ dap->set_vabc_enabled(true);
+ dap->set_vabc_compression_param("lz4");
+ dap->set_cow_version(3);
+
+ for (const auto& entry : std::filesystem::directory_iterator(snapshot_dir_path_)) {
+ if (android::base::EndsWith(entry.path().generic_string(), ".patch")) {
+ patchfiles_.push_back(android::base::Basename(entry.path().generic_string()));
+ }
+ }
+
+ for (auto& patchfile : patchfiles_) {
+ std::string parsing_file = snapshot_dir_path_ + patchfile;
+ android::base::unique_fd fd(TEMP_FAILURE_RETRY(open(parsing_file.c_str(), O_RDONLY)));
+ if (fd < 0) {
+ LOG(ERROR) << "Failed to open file: " << parsing_file;
+ return false;
+ }
+ uint64_t dev_sz = lseek(fd.get(), 0, SEEK_END);
+ if (!dev_sz) {
+ LOG(ERROR) << "Could not determine block device size: " << parsing_file;
+ return false;
+ }
+
+ const int block_sz = 4_KiB;
+ dev_sz += block_sz - 1;
+ dev_sz &= ~(block_sz - 1);
+
+ auto npos = patchfile.rfind(".patch");
+ auto partition_name = patchfile.substr(0, npos);
+
+ chromeos_update_engine::DynamicPartitionGroup* group = nullptr;
+ std::string group_name = GetGroupName(*source_metadata.get(), partition_name);
+ if (group_map_.find(group_name) != group_map_.end()) {
+ group = group_map_[group_name];
+ } else {
+ group = dap->add_groups();
+ group->set_name(group_name);
+ group_map_[group_name] = group;
+ }
+ group->add_partition_names(partition_name);
+
+ auto pu = manifest_.mutable_partitions()->Add();
+ pu->set_partition_name(partition_name);
+ pu->set_estimate_cow_size(dev_sz);
+
+ CowReader reader;
+ if (!reader.Parse(fd)) {
+ LOG(ERROR) << "COW reader parse failed";
+ return false;
+ }
+
+ uint64_t new_device_size = 0;
+ const auto& header = reader.GetHeader();
+ if (header.prefix.major_version == 2) {
+ size_t num_ops = reader.get_num_total_data_ops();
+ new_device_size = (num_ops * header.block_size);
+ } else {
+ const auto& v3_header = reader.header_v3();
+ new_device_size = v3_header.op_count_max * v3_header.block_size;
+ }
+
+ LOG(INFO) << "Partition: " << partition_name << " Group_name: " << group_name
+ << " size: " << new_device_size << " COW-size: " << dev_sz;
+ pu->mutable_new_partition_info()->set_size(new_device_size);
+ }
+ return true;
+}
+
+bool MapSnapshots::ApplyUpdate() {
+ if (!PrepareUpdate()) {
+ LOG(ERROR) << "PrepareUpdate failed";
+ return false;
+ }
+ if (!sm_->BeginUpdate()) {
+ LOG(ERROR) << "BeginUpdate failed";
+ return false;
+ }
+ if (!sm_->CreateUpdateSnapshots(manifest_)) {
+ LOG(ERROR) << "Could not apply snapshots";
+ return false;
+ }
+
+ LOG(INFO) << "CreateUpdateSnapshots success";
+ if (!sm_->MapAllSnapshots(10s)) {
+ LOG(ERROR) << "MapAllSnapshots failed";
+ return false;
+ }
+
+ LOG(INFO) << "MapAllSnapshots success";
+
+ auto& dm = android::dm::DeviceMapper::Instance();
+ auto target_slot = fs_mgr_get_other_slot_suffix();
+ for (auto& patchfile : patchfiles_) {
+ auto npos = patchfile.rfind(".patch");
+ auto partition_name = patchfile.substr(0, npos) + target_slot;
+ auto cow_device = partition_name + "-cow";
+ std::string cow_path;
+ if (!dm.GetDmDevicePathByName(cow_device, &cow_path)) {
+ LOG(ERROR) << "Failed to cow path";
+ return false;
+ }
+ threads_.emplace_back(std::async(std::launch::async, &MapSnapshots::WriteSnapshotPatch,
+ this, cow_path, patchfile));
+ }
+
+ bool ret = true;
+ for (auto& t : threads_) {
+ ret = t.get() && ret;
+ }
+ if (!ret) {
+ LOG(ERROR) << "Snapshot writes failed";
+ return false;
+ }
+ if (!sm_->UnmapAllSnapshots()) {
+ LOG(ERROR) << "UnmapAllSnapshots failed";
+ return false;
+ }
+
+ LOG(INFO) << "Pre-created snapshots successfully copied";
+ // All snapshots have been written.
+ if (!sm_->FinishedSnapshotWrites(false /* wipe */)) {
+ LOG(ERROR) << "Could not finalize snapshot writes.\n";
+ return false;
+ }
+
+ auto hal = hal::BootControlClient::WaitForService();
+ if (!hal) {
+ LOG(ERROR) << "Could not find IBootControl HAL.\n";
+ return false;
+ }
+ auto target_slot_number = SlotNumberForSlotSuffix(target_slot);
+ auto cr = hal->SetActiveBootSlot(target_slot_number);
+ if (!cr.IsOk()) {
+ LOG(ERROR) << "Could not set active boot slot: " << cr.errMsg;
+ return false;
+ }
+
+ LOG(INFO) << "ApplyUpdate success";
+ return true;
+}
+
bool MapSnapshots::BeginUpdate() {
lock_ = sm_->LockExclusive();
std::vector<std::string> snapshots;
@@ -227,11 +416,10 @@
if (file_offset >= dev_sz) {
break;
}
-
- if (fsync(cfd.get()) < 0) {
- PLOG(ERROR) << "Fsync failed at offset: " << file_offset << " size: " << to_read;
- return false;
- }
+ }
+ if (fsync(cfd.get()) < 0) {
+ PLOG(ERROR) << "Fsync failed";
+ return false;
}
return true;
}
@@ -277,29 +465,31 @@
}
return true;
}
+#endif
bool DumpCmdHandler(int /*argc*/, char** argv) {
- android::base::InitLogging(argv, &android::base::StderrLogger);
+ android::base::InitLogging(argv, TeeLogger(LogdLogger(), &StderrLogger));
return SnapshotManager::New()->Dump(std::cout);
}
bool MapCmdHandler(int, char** argv) {
- android::base::InitLogging(argv, &android::base::StderrLogger);
+ android::base::InitLogging(argv, TeeLogger(LogdLogger(), &StderrLogger));
using namespace std::chrono_literals;
return SnapshotManager::New()->MapAllSnapshots(5000ms);
}
bool UnmapCmdHandler(int, char** argv) {
- android::base::InitLogging(argv, &android::base::StderrLogger);
+ android::base::InitLogging(argv, TeeLogger(LogdLogger(), &StderrLogger));
return SnapshotManager::New()->UnmapAllSnapshots();
}
bool MergeCmdHandler(int /*argc*/, char** argv) {
- android::base::InitLogging(argv, &android::base::StderrLogger);
+ android::base::InitLogging(argv, TeeLogger(LogdLogger(), &StderrLogger));
LOG(WARNING) << "Deprecated. Call update_engine_client --merge instead.";
return false;
}
+#ifdef SNAPSHOTCTL_USERDEBUG_OR_ENG
bool GetVerityPartitions(std::vector<std::string>& partitions) {
auto& dm = android::dm::DeviceMapper::Instance();
auto dm_block_devices = dm.FindDmPartitions();
@@ -367,6 +557,30 @@
return snapshot.DeleteSnapshots();
}
+bool ApplyUpdate(int argc, char** argv) {
+ android::base::InitLogging(argv, &android::base::KernelLogger);
+
+ // Make sure we are root.
+ if (::getuid() != 0) {
+ LOG(ERROR) << "Not running as root. Try \"adb root\" first.";
+ return EXIT_FAILURE;
+ }
+
+ if (argc < 3) {
+ std::cerr << " apply-update <directory location where snapshot patches are present>"
+ " Apply the snapshots to the COW block device\n";
+ return false;
+ }
+
+ std::string path = std::string(argv[2]);
+ MapSnapshots cow(path);
+ if (!cow.ApplyUpdate()) {
+ return false;
+ }
+ LOG(INFO) << "Apply update success. Please reboot the device";
+ return true;
+}
+
bool MapPrecreatedSnapshots(int argc, char** argv) {
android::base::InitLogging(argv, &android::base::KernelLogger);
@@ -428,7 +642,6 @@
return cow.FinishSnapshotWrites();
}
-#ifdef SNAPSHOTCTL_USERDEBUG_OR_ENG
bool CreateTestUpdate(SnapshotManager* sm) {
chromeos_update_engine::DeltaArchiveManifest manifest;
@@ -552,12 +765,13 @@
{"map", MapCmdHandler},
#ifdef SNAPSHOTCTL_USERDEBUG_OR_ENG
{"test-blank-ota", TestOtaHandler},
-#endif
- {"unmap", UnmapCmdHandler},
+ {"apply-update", ApplyUpdate},
{"map-snapshots", MapPrecreatedSnapshots},
{"unmap-snapshots", UnMapPrecreatedSnapshots},
{"delete-snapshots", DeletePrecreatedSnapshots},
{"revert-snapshots", RemovePrecreatedSnapshots},
+#endif
+ {"unmap", UnmapCmdHandler},
// clang-format on
};
diff --git a/fs_mgr/libsnapshot/snapuserd/Android.bp b/fs_mgr/libsnapshot/snapuserd/Android.bp
index bd296a3..649309d 100644
--- a/fs_mgr/libsnapshot/snapuserd/Android.bp
+++ b/fs_mgr/libsnapshot/snapuserd/Android.bp
@@ -59,9 +59,6 @@
],
local_include_dirs: ["include/"],
srcs: [
- "dm-snapshot-merge/snapuserd.cpp",
- "dm-snapshot-merge/snapuserd_readahead.cpp",
- "dm-snapshot-merge/snapuserd_worker.cpp",
"dm_user_block_server.cpp",
"snapuserd_buffer.cpp",
"user-space-merge/handler_manager.cpp",
@@ -109,7 +106,6 @@
"fs_mgr_defaults",
],
srcs: [
- "dm-snapshot-merge/snapuserd_server.cpp",
"snapuserd_daemon.cpp",
"user-space-merge/snapuserd_server.cpp",
],
@@ -190,45 +186,6 @@
symlinks: ["snapuserd"],
}
-cc_test {
- name: "snapuserd_test_legacy",
- defaults: [
- "fs_mgr_defaults",
- "libsnapshot_cow_defaults",
- ],
- srcs: [
- "dm-snapshot-merge/cow_snapuserd_test.cpp",
- "dm-snapshot-merge/snapuserd.cpp",
- "dm-snapshot-merge/snapuserd_worker.cpp",
- "snapuserd_buffer.cpp",
- ],
- shared_libs: [
- "libbase",
- "liblog",
- ],
- static_libs: [
- "libbrotli",
- "libgtest",
- "libsnapshot_cow",
- "libsnapuserd_client",
- "libcutils_sockets",
- "libz",
- "libdm",
- "libext2_uuid",
- "libext4_utils",
- "libfs_mgr_file_wait",
- ],
- header_libs: [
- "libstorage_literals_headers",
- "libfiemap_headers",
- ],
- test_options: {
- min_shipping_api_level: 30,
- },
- auto_gen_config: true,
- require_root: false,
-}
-
cc_defaults {
name: "snapuserd_test_defaults",
defaults: [
diff --git a/fs_mgr/libsnapshot/snapuserd/dm-snapshot-merge/cow_snapuserd_test.cpp b/fs_mgr/libsnapshot/snapuserd/dm-snapshot-merge/cow_snapuserd_test.cpp
deleted file mode 100644
index 737c480..0000000
--- a/fs_mgr/libsnapshot/snapuserd/dm-snapshot-merge/cow_snapuserd_test.cpp
+++ /dev/null
@@ -1,1238 +0,0 @@
-// Copyright (C) 2018 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include <fcntl.h>
-#include <linux/fs.h>
-#include <linux/memfd.h>
-#include <sys/ioctl.h>
-#include <sys/stat.h>
-#include <sys/syscall.h>
-#include <sys/types.h>
-#include <unistd.h>
-
-#include <chrono>
-#include <iostream>
-#include <memory>
-#include <string_view>
-
-#include <android-base/file.h>
-#include <android-base/unique_fd.h>
-#include <fs_mgr/file_wait.h>
-#include <gtest/gtest.h>
-#include <libdm/dm.h>
-#include <libdm/loop_control.h>
-#include <libsnapshot/cow_writer.h>
-#include <snapuserd/snapuserd_buffer.h>
-#include <snapuserd/snapuserd_client.h>
-#include <storage_literals/storage_literals.h>
-
-#include "snapuserd.h"
-
-namespace android {
-namespace snapshot {
-
-using namespace android::storage_literals;
-using android::base::unique_fd;
-using LoopDevice = android::dm::LoopDevice;
-using namespace std::chrono_literals;
-using namespace android::dm;
-using namespace std;
-
-static constexpr char kSnapuserdSocketTest[] = "snapuserdTest";
-
-class TempDevice {
- public:
- TempDevice(const std::string& name, const DmTable& table)
- : dm_(DeviceMapper::Instance()), name_(name), valid_(false) {
- valid_ = dm_.CreateDevice(name, table, &path_, std::chrono::seconds(5));
- }
- TempDevice(TempDevice&& other) noexcept
- : dm_(other.dm_), name_(other.name_), path_(other.path_), valid_(other.valid_) {
- other.valid_ = false;
- }
- ~TempDevice() {
- if (valid_) {
- dm_.DeleteDevice(name_);
- }
- }
- bool Destroy() {
- if (!valid_) {
- return false;
- }
- valid_ = false;
- return dm_.DeleteDevice(name_);
- }
- const std::string& path() const { return path_; }
- const std::string& name() const { return name_; }
- bool valid() const { return valid_; }
-
- TempDevice(const TempDevice&) = delete;
- TempDevice& operator=(const TempDevice&) = delete;
-
- TempDevice& operator=(TempDevice&& other) noexcept {
- name_ = other.name_;
- valid_ = other.valid_;
- other.valid_ = false;
- return *this;
- }
-
- private:
- DeviceMapper& dm_;
- std::string name_;
- std::string path_;
- bool valid_;
-};
-
-class CowSnapuserdTest final {
- public:
- bool Setup();
- bool SetupOrderedOps();
- bool SetupOrderedOpsInverted();
- bool SetupCopyOverlap_1();
- bool SetupCopyOverlap_2();
- bool Merge();
- void ValidateMerge();
- void ReadSnapshotDeviceAndValidate();
- void Shutdown();
- void MergeInterrupt();
- void MergeInterruptFixed(int duration);
- void MergeInterruptRandomly(int max_duration);
- void ReadDmUserBlockWithoutDaemon();
- void ReadLastBlock();
-
- std::string snapshot_dev() const { return snapshot_dev_->path(); }
-
- static const uint64_t kSectorSize = 512;
-
- private:
- void SetupImpl();
-
- void MergeImpl();
- void SimulateDaemonRestart();
- void StartMerge();
-
- std::unique_ptr<ICowWriter> CreateCowDeviceInternal();
- void CreateCowDevice();
- void CreateCowDeviceOrderedOps();
- void CreateCowDeviceOrderedOpsInverted();
- void CreateCowDeviceWithCopyOverlap_1();
- void CreateCowDeviceWithCopyOverlap_2();
- bool SetupDaemon();
- void CreateBaseDevice();
- void InitCowDevice();
- void SetDeviceControlName();
- void InitDaemon();
- void CreateDmUserDevice();
- void StartSnapuserdDaemon();
- void CreateSnapshotDevice();
-
- unique_ptr<LoopDevice> base_loop_;
- unique_ptr<TempDevice> dmuser_dev_;
- unique_ptr<TempDevice> snapshot_dev_;
-
- std::string system_device_ctrl_name_;
- std::string system_device_name_;
-
- unique_fd base_fd_;
- std::unique_ptr<TemporaryFile> cow_system_;
- std::unique_ptr<SnapuserdClient> client_;
- std::unique_ptr<uint8_t[]> orig_buffer_;
- std::unique_ptr<uint8_t[]> merged_buffer_;
- bool setup_ok_ = false;
- bool merge_ok_ = false;
- size_t size_ = 50_MiB;
- int cow_num_sectors_;
- int total_base_size_;
-};
-
-class CowSnapuserdMetadataTest final {
- public:
- void Setup();
- void SetupPartialArea();
- void ValidateMetadata();
- void ValidatePartialFilledArea();
-
- private:
- void InitMetadata();
- std::unique_ptr<ICowWriter> CreateCowDeviceInternal();
- void CreateCowDevice();
- void CreateCowPartialFilledArea();
-
- std::unique_ptr<Snapuserd> snapuserd_;
- std::unique_ptr<TemporaryFile> cow_system_;
- size_t size_ = 1_MiB;
-};
-
-static unique_fd CreateTempFile(const std::string& name, size_t size) {
- unique_fd fd(syscall(__NR_memfd_create, name.c_str(), MFD_ALLOW_SEALING));
- if (fd < 0) {
- return {};
- }
- if (size) {
- if (ftruncate(fd, size) < 0) {
- perror("ftruncate");
- return {};
- }
- if (fcntl(fd, F_ADD_SEALS, F_SEAL_GROW | F_SEAL_SHRINK) < 0) {
- perror("fcntl");
- return {};
- }
- }
- return fd;
-}
-
-void CowSnapuserdTest::Shutdown() {
- ASSERT_TRUE(snapshot_dev_->Destroy());
- ASSERT_TRUE(dmuser_dev_->Destroy());
-
- auto misc_device = "/dev/dm-user/" + system_device_ctrl_name_;
- ASSERT_TRUE(client_->WaitForDeviceDelete(system_device_ctrl_name_));
- ASSERT_TRUE(android::fs_mgr::WaitForFileDeleted(misc_device, 10s));
- ASSERT_TRUE(client_->DetachSnapuserd());
-}
-
-bool CowSnapuserdTest::Setup() {
- SetupImpl();
- return setup_ok_;
-}
-
-bool CowSnapuserdTest::SetupOrderedOps() {
- CreateBaseDevice();
- CreateCowDeviceOrderedOps();
- return SetupDaemon();
-}
-
-bool CowSnapuserdTest::SetupOrderedOpsInverted() {
- CreateBaseDevice();
- CreateCowDeviceOrderedOpsInverted();
- return SetupDaemon();
-}
-
-bool CowSnapuserdTest::SetupCopyOverlap_1() {
- CreateBaseDevice();
- CreateCowDeviceWithCopyOverlap_1();
- return SetupDaemon();
-}
-
-bool CowSnapuserdTest::SetupCopyOverlap_2() {
- CreateBaseDevice();
- CreateCowDeviceWithCopyOverlap_2();
- return SetupDaemon();
-}
-
-bool CowSnapuserdTest::SetupDaemon() {
- SetDeviceControlName();
-
- StartSnapuserdDaemon();
- InitCowDevice();
-
- CreateDmUserDevice();
- InitDaemon();
-
- CreateSnapshotDevice();
- setup_ok_ = true;
-
- return setup_ok_;
-}
-
-void CowSnapuserdTest::StartSnapuserdDaemon() {
- pid_t pid = fork();
- ASSERT_GE(pid, 0);
- if (pid == 0) {
- std::string arg0 = "/system/bin/snapuserd";
- std::string arg1 = "-socket="s + kSnapuserdSocketTest;
- char* const argv[] = {arg0.data(), arg1.data(), nullptr};
- ASSERT_GE(execv(arg0.c_str(), argv), 0);
- } else {
- client_ = SnapuserdClient::Connect(kSnapuserdSocketTest, 10s);
- ASSERT_NE(client_, nullptr);
- }
-}
-
-std::unique_ptr<ICowWriter> CowSnapuserdTest::CreateCowDeviceInternal() {
- std::string path = android::base::GetExecutableDirectory();
- cow_system_ = std::make_unique<TemporaryFile>(path);
-
- CowOptions options;
- options.compression = "gz";
-
- unique_fd fd(cow_system_->fd);
- cow_system_->fd = -1;
-
- return CreateCowWriter(kDefaultCowVersion, options, std::move(fd));
-}
-
-void CowSnapuserdTest::ReadLastBlock() {
- unique_fd rnd_fd;
- total_base_size_ = BLOCK_SZ * 2;
-
- base_fd_ = CreateTempFile("base_device", total_base_size_);
- ASSERT_GE(base_fd_, 0);
-
- rnd_fd.reset(open("/dev/random", O_RDONLY));
- ASSERT_TRUE(rnd_fd > 0);
-
- std::unique_ptr<uint8_t[]> random_buffer = std::make_unique<uint8_t[]>(BLOCK_SZ);
-
- for (size_t j = 0; j < ((total_base_size_) / BLOCK_SZ); j++) {
- ASSERT_EQ(ReadFullyAtOffset(rnd_fd, (char*)random_buffer.get(), BLOCK_SZ, 0), true);
- ASSERT_EQ(android::base::WriteFully(base_fd_, random_buffer.get(), BLOCK_SZ), true);
- }
-
- ASSERT_EQ(lseek(base_fd_, 0, SEEK_SET), 0);
-
- base_loop_ = std::make_unique<LoopDevice>(base_fd_, 10s);
- ASSERT_TRUE(base_loop_->valid());
-
- std::unique_ptr<uint8_t[]> random_buffer_1_ = std::make_unique<uint8_t[]>(total_base_size_);
- loff_t offset = 0;
-
- // Fill random data
- for (size_t j = 0; j < (total_base_size_ / BLOCK_SZ); j++) {
- ASSERT_EQ(ReadFullyAtOffset(rnd_fd, (char*)random_buffer_1_.get() + offset, BLOCK_SZ, 0),
- true);
-
- offset += BLOCK_SZ;
- }
-
- auto writer = CreateCowDeviceInternal();
- ASSERT_NE(writer, nullptr);
-
- ASSERT_TRUE(writer->AddRawBlocks(0, random_buffer_1_.get(), BLOCK_SZ));
- ASSERT_TRUE(writer->AddRawBlocks(1, (char*)random_buffer_1_.get() + BLOCK_SZ, BLOCK_SZ));
-
- ASSERT_TRUE(writer->Finalize());
-
- SetDeviceControlName();
-
- StartSnapuserdDaemon();
- InitCowDevice();
-
- CreateDmUserDevice();
- InitDaemon();
-
- CreateSnapshotDevice();
-
- unique_fd snapshot_fd(open(snapshot_dev_->path().c_str(), O_RDONLY));
- ASSERT_TRUE(snapshot_fd > 0);
-
- std::unique_ptr<uint8_t[]> snapuserd_buffer = std::make_unique<uint8_t[]>(BLOCK_SZ);
-
- offset = 7680;
- ASSERT_EQ(ReadFullyAtOffset(snapshot_fd, snapuserd_buffer.get(), 512, offset), true);
- ASSERT_EQ(memcmp(snapuserd_buffer.get(), (char*)random_buffer_1_.get() + offset, 512), 0);
-}
-
-void CowSnapuserdTest::CreateBaseDevice() {
- unique_fd rnd_fd;
-
- total_base_size_ = (size_ * 5);
- base_fd_ = CreateTempFile("base_device", total_base_size_);
- ASSERT_GE(base_fd_, 0);
-
- rnd_fd.reset(open("/dev/random", O_RDONLY));
- ASSERT_TRUE(rnd_fd > 0);
-
- std::unique_ptr<uint8_t[]> random_buffer = std::make_unique<uint8_t[]>(1_MiB);
-
- for (size_t j = 0; j < ((total_base_size_) / 1_MiB); j++) {
- ASSERT_EQ(ReadFullyAtOffset(rnd_fd, (char*)random_buffer.get(), 1_MiB, 0), true);
- ASSERT_EQ(android::base::WriteFully(base_fd_, random_buffer.get(), 1_MiB), true);
- }
-
- ASSERT_EQ(lseek(base_fd_, 0, SEEK_SET), 0);
-
- base_loop_ = std::make_unique<LoopDevice>(base_fd_, 10s);
- ASSERT_TRUE(base_loop_->valid());
-}
-
-void CowSnapuserdTest::ReadSnapshotDeviceAndValidate() {
- unique_fd snapshot_fd(open(snapshot_dev_->path().c_str(), O_RDONLY));
- ASSERT_TRUE(snapshot_fd > 0);
-
- std::unique_ptr<uint8_t[]> snapuserd_buffer = std::make_unique<uint8_t[]>(size_);
-
- // COPY
- loff_t offset = 0;
- ASSERT_EQ(ReadFullyAtOffset(snapshot_fd, snapuserd_buffer.get(), size_, offset), true);
- ASSERT_EQ(memcmp(snapuserd_buffer.get(), orig_buffer_.get(), size_), 0);
-
- // REPLACE
- offset += size_;
- ASSERT_EQ(ReadFullyAtOffset(snapshot_fd, snapuserd_buffer.get(), size_, offset), true);
- ASSERT_EQ(memcmp(snapuserd_buffer.get(), (char*)orig_buffer_.get() + size_, size_), 0);
-
- // ZERO
- offset += size_;
- ASSERT_EQ(ReadFullyAtOffset(snapshot_fd, snapuserd_buffer.get(), size_, offset), true);
- ASSERT_EQ(memcmp(snapuserd_buffer.get(), (char*)orig_buffer_.get() + (size_ * 2), size_), 0);
-
- // REPLACE
- 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() {
- auto writer = CreateCowDeviceInternal();
- ASSERT_NE(writer, nullptr);
-
- size_t num_blocks = size_ / writer->GetBlockSize();
- size_t x = num_blocks;
- size_t blk_src_copy = 0;
-
- // Create overlapping copy operations
- while (1) {
- ASSERT_TRUE(writer->AddCopy(blk_src_copy, blk_src_copy + 1));
- x -= 1;
- if (x == 1) {
- break;
- }
- blk_src_copy += 1;
- }
-
- // 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 operations required for validation
- int block_size = 4096;
- x = num_blocks;
- loff_t src_offset = block_size;
- loff_t dest_offset = 0;
-
- while (1) {
- memmove((char*)orig_buffer_.get() + dest_offset, (char*)orig_buffer_.get() + src_offset,
- block_size);
- x -= 1;
- if (x == 1) {
- break;
- }
- src_offset += block_size;
- dest_offset += block_size;
- }
-}
-
-void CowSnapuserdTest::CreateCowDeviceWithCopyOverlap_1() {
- auto writer = CreateCowDeviceInternal();
- ASSERT_NE(writer, nullptr);
-
- size_t num_blocks = size_ / writer->GetBlockSize();
- size_t x = num_blocks;
- size_t blk_src_copy = num_blocks - 1;
-
- // Create overlapping copy operations
- while (1) {
- ASSERT_TRUE(writer->AddCopy(blk_src_copy + 1, blk_src_copy));
- x -= 1;
- if (x == 0) {
- ASSERT_EQ(blk_src_copy, 0);
- break;
- }
- blk_src_copy -= 1;
- }
-
- // 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 operations
- ASSERT_EQ(android::base::ReadFullyAtOffset(base_fd_, orig_buffer_.get(), writer->GetBlockSize(),
- 0),
- true);
- ASSERT_EQ(android::base::ReadFullyAtOffset(
- base_fd_, (char*)orig_buffer_.get() + writer->GetBlockSize(), size_, 0),
- true);
-}
-
-void CowSnapuserdTest::CreateCowDeviceOrderedOpsInverted() {
- unique_fd rnd_fd;
- loff_t offset = 0;
-
- auto writer = CreateCowDeviceInternal();
- ASSERT_NE(writer, nullptr);
-
- rnd_fd.reset(open("/dev/random", O_RDONLY));
- ASSERT_TRUE(rnd_fd > 0);
-
- std::unique_ptr<uint8_t[]> random_buffer_1_ = std::make_unique<uint8_t[]>(size_);
-
- // Fill random data
- for (size_t j = 0; j < (size_ / 1_MiB); j++) {
- ASSERT_EQ(ReadFullyAtOffset(rnd_fd, (char*)random_buffer_1_.get() + offset, 1_MiB, 0),
- true);
-
- offset += 1_MiB;
- }
-
- size_t num_blocks = size_ / writer->GetBlockSize();
- size_t blk_end_copy = num_blocks * 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()[writer->GetBlockSize() * (i - 1)],
- writer->GetBlockSize(), 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;
-
- auto writer = CreateCowDeviceInternal();
- ASSERT_NE(writer, nullptr);
-
- rnd_fd.reset(open("/dev/random", O_RDONLY));
- ASSERT_TRUE(rnd_fd > 0);
-
- std::unique_ptr<uint8_t[]> random_buffer_1_ = std::make_unique<uint8_t[]>(size_);
-
- // Fill random data
- for (size_t j = 0; j < (size_ / 1_MiB); j++) {
- ASSERT_EQ(ReadFullyAtOffset(rnd_fd, (char*)random_buffer_1_.get() + offset, 1_MiB, 0),
- true);
-
- offset += 1_MiB;
- }
- memset(random_buffer_1_.get(), 0, size_);
-
- size_t num_blocks = size_ / writer->GetBlockSize();
- size_t x = num_blocks;
- size_t source_blk = 0;
- size_t blk_src_copy = 2 * num_blocks;
- uint16_t xor_offset = 5;
-
- while (1) {
- ASSERT_TRUE(writer->AddCopy(source_blk, blk_src_copy));
-
- 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;
-
- auto writer = CreateCowDeviceInternal();
- ASSERT_NE(writer, nullptr);
-
- rnd_fd.reset(open("/dev/random", O_RDONLY));
- ASSERT_TRUE(rnd_fd > 0);
-
- std::unique_ptr<uint8_t[]> random_buffer_1_ = std::make_unique<uint8_t[]>(size_);
-
- // Fill random data
- for (size_t j = 0; j < (size_ / 1_MiB); j++) {
- ASSERT_EQ(ReadFullyAtOffset(rnd_fd, (char*)random_buffer_1_.get() + offset, 1_MiB, 0),
- true);
-
- offset += 1_MiB;
- }
-
- size_t num_blocks = size_ / writer->GetBlockSize();
- size_t blk_end_copy = num_blocks * 2;
- size_t source_blk = num_blocks - 1;
- size_t blk_src_copy = blk_end_copy - 1;
-
- uint32_t sequence[num_blocks * 2];
- // Sequence for Copy ops
- for (int i = 0; i < num_blocks; i++) {
- sequence[i] = num_blocks - 1 - i;
- }
- // Sequence for Xor ops
- for (int i = 0; i < num_blocks; i++) {
- sequence[num_blocks + i] = 5 * num_blocks - 1 - i;
- }
- ASSERT_TRUE(writer->AddSequenceData(2 * num_blocks, sequence));
-
- size_t x = num_blocks;
- while (1) {
- ASSERT_TRUE(writer->AddCopy(source_blk, blk_src_copy));
- x -= 1;
- if (x == 0) {
- break;
- }
- source_blk -= 1;
- blk_src_copy -= 1;
- }
-
- source_blk = num_blocks;
- blk_src_copy = blk_end_copy;
-
- ASSERT_TRUE(writer->AddRawBlocks(source_blk, random_buffer_1_.get(), size_));
-
- size_t blk_zero_copy_start = source_blk + num_blocks;
- size_t blk_zero_copy_end = blk_zero_copy_start + num_blocks;
-
- ASSERT_TRUE(writer->AddZeroBlocks(blk_zero_copy_start, num_blocks));
-
- size_t blk_random2_replace_start = blk_zero_copy_end;
-
- ASSERT_TRUE(writer->AddRawBlocks(blk_random2_replace_start, random_buffer_1_.get(), size_));
-
- size_t blk_xor_start = blk_random2_replace_start + num_blocks;
- size_t xor_offset = BLOCK_SZ / 2;
- ASSERT_TRUE(writer->AddXorBlocks(blk_xor_start, random_buffer_1_.get(), size_, num_blocks,
- xor_offset));
-
- // Flush operations
- ASSERT_TRUE(writer->Finalize());
- // Construct the buffer required for validation
- orig_buffer_ = std::make_unique<uint8_t[]>(total_base_size_);
- std::string zero_buffer(size_, 0);
- ASSERT_EQ(android::base::ReadFullyAtOffset(base_fd_, orig_buffer_.get(), size_, size_), true);
- memcpy((char*)orig_buffer_.get() + size_, random_buffer_1_.get(), size_);
- memcpy((char*)orig_buffer_.get() + (size_ * 2), (void*)zero_buffer.c_str(), size_);
- memcpy((char*)orig_buffer_.get() + (size_ * 3), random_buffer_1_.get(), size_);
- ASSERT_EQ(android::base::ReadFullyAtOffset(base_fd_, &orig_buffer_.get()[size_ * 4], size_,
- size_ + xor_offset),
- true);
- for (int i = 0; i < size_; i++) {
- orig_buffer_.get()[(size_ * 4) + i] =
- (uint8_t)(orig_buffer_.get()[(size_ * 4) + i] ^ random_buffer_1_.get()[i]);
- }
-}
-
-void CowSnapuserdTest::InitCowDevice() {
- cow_num_sectors_ = client_->InitDmUserCow(system_device_ctrl_name_, cow_system_->path,
- base_loop_->device());
- ASSERT_NE(cow_num_sectors_, 0);
-}
-
-void CowSnapuserdTest::SetDeviceControlName() {
- system_device_name_.clear();
- system_device_ctrl_name_.clear();
-
- std::string str(cow_system_->path);
- std::size_t found = str.find_last_of("/\\");
- ASSERT_NE(found, std::string::npos);
- system_device_name_ = str.substr(found + 1);
-
- system_device_ctrl_name_ = system_device_name_ + "-ctrl";
-}
-
-void CowSnapuserdTest::CreateDmUserDevice() {
- DmTable dmuser_table;
- ASSERT_TRUE(dmuser_table.AddTarget(
- std::make_unique<DmTargetUser>(0, cow_num_sectors_, system_device_ctrl_name_)));
- ASSERT_TRUE(dmuser_table.valid());
-
- dmuser_dev_ = std::make_unique<TempDevice>(system_device_name_, dmuser_table);
- ASSERT_TRUE(dmuser_dev_->valid());
- ASSERT_FALSE(dmuser_dev_->path().empty());
-
- auto misc_device = "/dev/dm-user/" + system_device_ctrl_name_;
- ASSERT_TRUE(android::fs_mgr::WaitForFile(misc_device, 10s));
-}
-
-void CowSnapuserdTest::ReadDmUserBlockWithoutDaemon() {
- DmTable dmuser_table;
- std::string dm_user_name = "dm-test-device";
- unique_fd fd;
-
- // Create a dm-user block device
- ASSERT_TRUE(dmuser_table.AddTarget(std::make_unique<DmTargetUser>(0, 123456, dm_user_name)));
- ASSERT_TRUE(dmuser_table.valid());
-
- dmuser_dev_ = std::make_unique<TempDevice>(dm_user_name, dmuser_table);
- ASSERT_TRUE(dmuser_dev_->valid());
- ASSERT_FALSE(dmuser_dev_->path().empty());
-
- fd.reset(open(dmuser_dev_->path().c_str(), O_RDONLY));
- ASSERT_GE(fd, 0);
-
- std::unique_ptr<uint8_t[]> buffer = std::make_unique<uint8_t[]>(1_MiB);
-
- loff_t offset = 0;
- // Every IO should fail as there is no daemon to process the IO
- for (size_t j = 0; j < 10; j++) {
- ASSERT_EQ(ReadFullyAtOffset(fd, (char*)buffer.get() + offset, BLOCK_SZ, offset), false);
-
- offset += BLOCK_SZ;
- }
-
- fd = {};
- ASSERT_TRUE(dmuser_dev_->Destroy());
-}
-
-void CowSnapuserdTest::InitDaemon() {
- bool ok = client_->AttachDmUser(system_device_ctrl_name_);
- ASSERT_TRUE(ok);
-}
-
-void CowSnapuserdTest::CreateSnapshotDevice() {
- DmTable snap_table;
- ASSERT_TRUE(snap_table.AddTarget(std::make_unique<DmTargetSnapshot>(
- 0, total_base_size_ / kSectorSize, base_loop_->device(), dmuser_dev_->path(),
- SnapshotStorageMode::Persistent, 8)));
- ASSERT_TRUE(snap_table.valid());
-
- snap_table.set_readonly(true);
-
- snapshot_dev_ = std::make_unique<TempDevice>("cowsnapuserd-test-dm-snapshot", snap_table);
- ASSERT_TRUE(snapshot_dev_->valid());
- ASSERT_FALSE(snapshot_dev_->path().empty());
-}
-
-void CowSnapuserdTest::SetupImpl() {
- CreateBaseDevice();
- CreateCowDevice();
-
- SetDeviceControlName();
-
- StartSnapuserdDaemon();
- InitCowDevice();
-
- CreateDmUserDevice();
- InitDaemon();
-
- CreateSnapshotDevice();
- setup_ok_ = true;
-}
-
-bool CowSnapuserdTest::Merge() {
- MergeImpl();
- return merge_ok_;
-}
-
-void CowSnapuserdTest::StartMerge() {
- DmTable merge_table;
- ASSERT_TRUE(merge_table.AddTarget(std::make_unique<DmTargetSnapshot>(
- 0, total_base_size_ / kSectorSize, base_loop_->device(), dmuser_dev_->path(),
- SnapshotStorageMode::Merge, 8)));
- ASSERT_TRUE(merge_table.valid());
- ASSERT_EQ(total_base_size_ / kSectorSize, merge_table.num_sectors());
-
- DeviceMapper& dm = DeviceMapper::Instance();
- ASSERT_TRUE(dm.LoadTableAndActivate("cowsnapuserd-test-dm-snapshot", merge_table));
-}
-
-void CowSnapuserdTest::MergeImpl() {
- StartMerge();
- DeviceMapper& dm = DeviceMapper::Instance();
-
- while (true) {
- vector<DeviceMapper::TargetInfo> status;
- ASSERT_TRUE(dm.GetTableStatus("cowsnapuserd-test-dm-snapshot", &status));
- ASSERT_EQ(status.size(), 1);
- ASSERT_EQ(strncmp(status[0].spec.target_type, "snapshot-merge", strlen("snapshot-merge")),
- 0);
-
- DmTargetSnapshot::Status merge_status;
- ASSERT_TRUE(DmTargetSnapshot::ParseStatusText(status[0].data, &merge_status));
- ASSERT_TRUE(merge_status.error.empty());
- if (merge_status.sectors_allocated == merge_status.metadata_sectors) {
- break;
- }
-
- std::this_thread::sleep_for(250ms);
- }
-
- merge_ok_ = true;
-}
-
-void CowSnapuserdTest::ValidateMerge() {
- merged_buffer_ = std::make_unique<uint8_t[]>(total_base_size_);
- ASSERT_EQ(android::base::ReadFullyAtOffset(base_fd_, merged_buffer_.get(), total_base_size_, 0),
- true);
- ASSERT_EQ(memcmp(merged_buffer_.get(), orig_buffer_.get(), total_base_size_), 0);
-}
-
-void CowSnapuserdTest::SimulateDaemonRestart() {
- Shutdown();
- std::this_thread::sleep_for(500ms);
- SetDeviceControlName();
- StartSnapuserdDaemon();
- InitCowDevice();
- CreateDmUserDevice();
- InitDaemon();
- 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();
- std::this_thread::sleep_for(250ms);
- SimulateDaemonRestart();
-
- StartMerge();
- std::this_thread::sleep_for(250ms);
- SimulateDaemonRestart();
-
- StartMerge();
- std::this_thread::sleep_for(150ms);
- SimulateDaemonRestart();
-
- StartMerge();
- std::this_thread::sleep_for(100ms);
- SimulateDaemonRestart();
-
- StartMerge();
- std::this_thread::sleep_for(800ms);
- SimulateDaemonRestart();
-
- StartMerge();
- std::this_thread::sleep_for(600ms);
- SimulateDaemonRestart();
-
- ASSERT_TRUE(Merge());
-}
-
-std::unique_ptr<ICowWriter> CowSnapuserdMetadataTest::CreateCowDeviceInternal() {
- std::string path = android::base::GetExecutableDirectory();
- cow_system_ = std::make_unique<TemporaryFile>(path);
-
- CowOptions options;
- options.compression = "gz";
-
- unique_fd fd(cow_system_->fd);
- cow_system_->fd = -1;
-
- return CreateCowWriter(kDefaultCowVersion, options, std::move(fd));
-}
-
-void CowSnapuserdMetadataTest::CreateCowPartialFilledArea() {
- auto writer = CreateCowDeviceInternal();
- ASSERT_NE(writer, nullptr);
-
- // Area 0 is completely filled with 256 exceptions
- for (int i = 0; i < 256; i++) {
- ASSERT_TRUE(writer->AddCopy(i, 256 + i));
- }
-
- // Area 1 is partially filled with 2 copy ops and 10 zero ops
- ASSERT_TRUE(writer->AddCopy(500, 1000));
- ASSERT_TRUE(writer->AddCopy(501, 1001));
-
- ASSERT_TRUE(writer->AddZeroBlocks(300, 10));
-
- // Flush operations
- ASSERT_TRUE(writer->Finalize());
-}
-
-void CowSnapuserdMetadataTest::ValidatePartialFilledArea() {
- int area_sz = snapuserd_->GetMetadataAreaSize();
-
- ASSERT_EQ(area_sz, 2);
-
- // Verify the partially filled area
- void* buffer = snapuserd_->GetExceptionBuffer(1);
- loff_t offset = 0;
- struct disk_exception* de;
- for (int i = 11; i >= 0; i--) {
- de = reinterpret_cast<struct disk_exception*>((char*)buffer + offset);
- ASSERT_EQ(de->old_chunk, i);
- offset += sizeof(struct disk_exception);
- }
-
- de = reinterpret_cast<struct disk_exception*>((char*)buffer + offset);
- ASSERT_EQ(de->old_chunk, 0);
- ASSERT_EQ(de->new_chunk, 0);
-}
-
-void CowSnapuserdMetadataTest::SetupPartialArea() {
- CreateCowPartialFilledArea();
- InitMetadata();
-}
-
-void CowSnapuserdMetadataTest::CreateCowDevice() {
- unique_fd rnd_fd;
- loff_t offset = 0;
-
- auto writer = CreateCowDeviceInternal();
- ASSERT_NE(writer, nullptr);
-
- rnd_fd.reset(open("/dev/random", O_RDONLY));
- ASSERT_TRUE(rnd_fd > 0);
-
- std::unique_ptr<uint8_t[]> random_buffer_1_ = std::make_unique<uint8_t[]>(size_);
-
- // Fill random data
- for (size_t j = 0; j < (size_ / 1_MiB); j++) {
- ASSERT_EQ(ReadFullyAtOffset(rnd_fd, (char*)random_buffer_1_.get() + offset, 1_MiB, 0),
- true);
-
- offset += 1_MiB;
- }
-
- size_t num_blocks = size_ / writer->GetBlockSize();
-
- // Overlapping region. This has to be split
- // into two batch operations
- ASSERT_TRUE(writer->AddCopy(23, 20));
- ASSERT_TRUE(writer->AddCopy(22, 19));
- ASSERT_TRUE(writer->AddCopy(21, 18));
- ASSERT_TRUE(writer->AddCopy(20, 17));
- ASSERT_TRUE(writer->AddCopy(19, 16));
- ASSERT_TRUE(writer->AddCopy(18, 15));
-
- // Contiguous region but blocks in ascending order
- // Daemon has to ensure that these blocks are merged
- // in a batch
- ASSERT_TRUE(writer->AddCopy(50, 75));
- ASSERT_TRUE(writer->AddCopy(51, 76));
- ASSERT_TRUE(writer->AddCopy(52, 77));
- ASSERT_TRUE(writer->AddCopy(53, 78));
-
- // Dis-contiguous region
- ASSERT_TRUE(writer->AddCopy(110, 130));
- ASSERT_TRUE(writer->AddCopy(105, 125));
- ASSERT_TRUE(writer->AddCopy(100, 120));
-
- // Overlap
- ASSERT_TRUE(writer->AddCopy(25, 30));
- ASSERT_TRUE(writer->AddCopy(30, 31));
-
- size_t source_blk = num_blocks;
-
- ASSERT_TRUE(writer->AddRawBlocks(source_blk, random_buffer_1_.get(), size_));
-
- size_t blk_zero_copy_start = source_blk + num_blocks;
-
- ASSERT_TRUE(writer->AddZeroBlocks(blk_zero_copy_start, num_blocks));
-
- // Flush operations
- ASSERT_TRUE(writer->Finalize());
-}
-
-void CowSnapuserdMetadataTest::InitMetadata() {
- snapuserd_ = std::make_unique<Snapuserd>("", cow_system_->path, "");
- ASSERT_TRUE(snapuserd_->InitCowDevice());
-}
-
-void CowSnapuserdMetadataTest::Setup() {
- CreateCowDevice();
- InitMetadata();
-}
-
-void CowSnapuserdMetadataTest::ValidateMetadata() {
- int area_sz = snapuserd_->GetMetadataAreaSize();
- ASSERT_EQ(area_sz, 3);
-
- size_t old_chunk;
- size_t new_chunk;
-
- for (int i = 0; i < area_sz; i++) {
- void* buffer = snapuserd_->GetExceptionBuffer(i);
- loff_t offset = 0;
- if (i == 0) {
- old_chunk = 256;
- new_chunk = 2;
- } else if (i == 1) {
- old_chunk = 512;
- new_chunk = 259;
- }
- for (int j = 0; j < 256; j++) {
- struct disk_exception* de =
- reinterpret_cast<struct disk_exception*>((char*)buffer + offset);
-
- if (i != 2) {
- ASSERT_EQ(de->old_chunk, old_chunk);
- ASSERT_EQ(de->new_chunk, new_chunk);
- old_chunk += 1;
- new_chunk += 1;
- } else {
- break;
- }
- offset += sizeof(struct disk_exception);
- }
-
- if (i == 2) {
- // The first 5 copy operation is not batch merged
- // as the sequence is discontiguous
- struct disk_exception* de =
- reinterpret_cast<struct disk_exception*>((char*)buffer + offset);
- ASSERT_EQ(de->old_chunk, 30);
- ASSERT_EQ(de->new_chunk, 518);
- offset += sizeof(struct disk_exception);
-
- de = reinterpret_cast<struct disk_exception*>((char*)buffer + offset);
- ASSERT_EQ(de->old_chunk, 25);
- ASSERT_EQ(de->new_chunk, 520);
- offset += sizeof(struct disk_exception);
-
- de = reinterpret_cast<struct disk_exception*>((char*)buffer + offset);
- ASSERT_EQ(de->old_chunk, 100);
- 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, 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, 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, 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, 525);
- 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, 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, 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, 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, 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, 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, 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, 534);
- offset += sizeof(struct disk_exception);
-
- // End of metadata
- de = reinterpret_cast<struct disk_exception*>((char*)buffer + offset);
- ASSERT_EQ(de->old_chunk, 0);
- ASSERT_EQ(de->new_chunk, 0);
- offset += sizeof(struct disk_exception);
- }
- }
-}
-
-TEST(Snapuserd_Test, Snapshot_Metadata) {
- CowSnapuserdMetadataTest harness;
- harness.Setup();
- harness.ValidateMetadata();
-}
-
-TEST(Snapuserd_Test, Snapshot_Metadata_Overlap) {
- CowSnapuserdMetadataTest harness;
- harness.SetupPartialArea();
- harness.ValidatePartialFilledArea();
-}
-
-TEST(Snapuserd_Test, Snapshot_Merge_Resume) {
- CowSnapuserdTest harness;
- ASSERT_TRUE(harness.Setup());
- harness.MergeInterrupt();
- harness.ValidateMerge();
- harness.Shutdown();
-}
-
-TEST(Snapuserd_Test, Snapshot_IO_TEST) {
- CowSnapuserdTest harness;
- ASSERT_TRUE(harness.Setup());
- harness.ReadSnapshotDeviceAndValidate();
- ASSERT_TRUE(harness.Merge());
- harness.ValidateMerge();
- harness.Shutdown();
-}
-
-TEST(Snapuserd_Test, Snapshot_END_IO_TEST) {
- CowSnapuserdTest harness;
- harness.ReadLastBlock();
- harness.Shutdown();
-}
-
-TEST(Snapuserd_Test, Snapshot_COPY_Overlap_TEST_1) {
- CowSnapuserdTest harness;
- ASSERT_TRUE(harness.SetupCopyOverlap_1());
- ASSERT_TRUE(harness.Merge());
- harness.ValidateMerge();
- harness.Shutdown();
-}
-
-TEST(Snapuserd_Test, Snapshot_COPY_Overlap_TEST_2) {
- CowSnapuserdTest harness;
- ASSERT_TRUE(harness.SetupCopyOverlap_2());
- ASSERT_TRUE(harness.Merge());
- harness.ValidateMerge();
- harness.Shutdown();
-}
-
-TEST(Snapuserd_Test, Snapshot_COPY_Overlap_Merge_Resume_TEST) {
- CowSnapuserdTest harness;
- ASSERT_TRUE(harness.SetupCopyOverlap_1());
- harness.MergeInterrupt();
- harness.ValidateMerge();
- harness.Shutdown();
-}
-
-TEST(Snapuserd_Test, ReadDmUserBlockWithoutDaemon) {
- CowSnapuserdTest harness;
- 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
-
-int main(int argc, char** argv) {
- ::testing::InitGoogleTest(&argc, argv);
- return RUN_ALL_TESTS();
-}
diff --git a/fs_mgr/libsnapshot/snapuserd/dm-snapshot-merge/snapuserd.cpp b/fs_mgr/libsnapshot/snapuserd/dm-snapshot-merge/snapuserd.cpp
deleted file mode 100644
index 93bb0b2..0000000
--- a/fs_mgr/libsnapshot/snapuserd/dm-snapshot-merge/snapuserd.cpp
+++ /dev/null
@@ -1,870 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "snapuserd.h"
-
-#include <dirent.h>
-#include <fcntl.h>
-#include <linux/fs.h>
-#include <unistd.h>
-#include <algorithm>
-
-#include <csignal>
-#include <optional>
-#include <set>
-
-#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 {
-
-using namespace android;
-using namespace android::dm;
-using android::base::unique_fd;
-
-#define SNAP_LOG(level) LOG(level) << misc_name_ << ": "
-#define SNAP_PLOG(level) PLOG(level) << misc_name_ << ": "
-
-Snapuserd::Snapuserd(const std::string& misc_name, const std::string& cow_device,
- const std::string& backing_device) {
- misc_name_ = misc_name;
- cow_device_ = cow_device;
- backing_store_device_ = backing_device;
- control_device_ = "/dev/dm-user/" + misc_name;
-}
-
-bool Snapuserd::InitializeWorkers() {
- for (int i = 0; i < NUM_THREADS_PER_PARTITION; i++) {
- std::unique_ptr<WorkerThread> wt = std::make_unique<WorkerThread>(
- cow_device_, backing_store_device_, control_device_, misc_name_, GetSharedPtr());
-
- worker_threads_.push_back(std::move(wt));
- }
-
- read_ahead_thread_ = std::make_unique<ReadAheadThread>(cow_device_, backing_store_device_,
- misc_name_, GetSharedPtr());
- 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;
-
- if (read_ahead_feature_ && read_ahead_ops_.size() > 0) {
- struct BufferState* ra_state = GetBufferState();
- ra_state->read_ahead_state = kCowReadAheadInProgress;
- }
-
- int ret = msync(mapped_addr_, BLOCK_SZ, MS_SYNC);
- if (ret < 0) {
- SNAP_PLOG(ERROR) << "msync header failed: " << ret;
- return false;
- }
-
- merge_initiated_ = true;
-
- return true;
-}
-
-void Snapuserd::PrepareReadAhead() {
- if (!read_ahead_feature_) {
- return;
- }
-
- struct BufferState* ra_state = GetBufferState();
- // Check if the data has to be re-constructed from COW device
- if (ra_state->read_ahead_state == kCowReadAheadDone) {
- populate_data_from_cow_ = true;
- } else {
- populate_data_from_cow_ = false;
- }
-
- StartReadAhead();
-}
-
-bool Snapuserd::GetRABuffer(std::unique_lock<std::mutex>* lock, uint64_t block, void* buffer) {
- if (!lock->owns_lock()) {
- SNAP_LOG(ERROR) << "GetRABuffer - Lock not held";
- return false;
- }
- std::unordered_map<uint64_t, void*>::iterator it = read_ahead_buffer_map_.find(block);
-
- // This will be true only for IO's generated as part of reading a root
- // filesystem. IO's related to merge should always be in read-ahead cache.
- if (it == read_ahead_buffer_map_.end()) {
- return false;
- }
-
- // Theoretically, we can send the data back from the read-ahead buffer
- // all the way to the kernel without memcpy. However, if the IO is
- // un-aligned, the wrapper function will need to touch the read-ahead
- // buffers and transitions will be bit more complicated.
- memcpy(buffer, it->second, BLOCK_SZ);
- return true;
-}
-
-// ========== State transition functions for read-ahead operations ===========
-
-bool Snapuserd::GetReadAheadPopulatedBuffer(uint64_t block, void* buffer) {
- if (!read_ahead_feature_) {
- return false;
- }
-
- {
- std::unique_lock<std::mutex> lock(lock_);
- if (io_state_ == READ_AHEAD_IO_TRANSITION::READ_AHEAD_FAILURE) {
- return false;
- }
-
- if (io_state_ == READ_AHEAD_IO_TRANSITION::IO_IN_PROGRESS) {
- return GetRABuffer(&lock, block, buffer);
- }
- }
-
- {
- // Read-ahead thread IO is in-progress. Wait for it to complete
- std::unique_lock<std::mutex> lock(lock_);
- while (!(io_state_ == READ_AHEAD_IO_TRANSITION::READ_AHEAD_FAILURE ||
- io_state_ == READ_AHEAD_IO_TRANSITION::IO_IN_PROGRESS)) {
- cv.wait(lock);
- }
-
- return GetRABuffer(&lock, block, buffer);
- }
-}
-
-// This is invoked by read-ahead thread waiting for merge IO's
-// to complete
-bool Snapuserd::WaitForMergeToComplete() {
- {
- std::unique_lock<std::mutex> lock(lock_);
- while (!(io_state_ == READ_AHEAD_IO_TRANSITION::READ_AHEAD_BEGIN ||
- io_state_ == READ_AHEAD_IO_TRANSITION::IO_TERMINATED)) {
- cv.wait(lock);
- }
-
- if (io_state_ == READ_AHEAD_IO_TRANSITION::IO_TERMINATED) {
- return false;
- }
-
- io_state_ = READ_AHEAD_IO_TRANSITION::READ_AHEAD_IN_PROGRESS;
- return true;
- }
-}
-
-// This is invoked during the launch of worker threads. We wait
-// for read-ahead thread to by fully up before worker threads
-// are launched; else we will have a race between worker threads
-// and read-ahead thread specifically during re-construction.
-bool Snapuserd::WaitForReadAheadToStart() {
- {
- std::unique_lock<std::mutex> lock(lock_);
- while (!(io_state_ == READ_AHEAD_IO_TRANSITION::IO_IN_PROGRESS ||
- io_state_ == READ_AHEAD_IO_TRANSITION::READ_AHEAD_FAILURE)) {
- cv.wait(lock);
- }
-
- if (io_state_ == READ_AHEAD_IO_TRANSITION::READ_AHEAD_FAILURE) {
- return false;
- }
-
- return true;
- }
-}
-
-// Invoked by worker threads when a sequence of merge operation
-// is complete notifying read-ahead thread to make forward
-// progress.
-void Snapuserd::StartReadAhead() {
- {
- std::lock_guard<std::mutex> lock(lock_);
- io_state_ = READ_AHEAD_IO_TRANSITION::READ_AHEAD_BEGIN;
- }
-
- cv.notify_one();
-}
-
-void Snapuserd::MergeCompleted() {
- {
- std::lock_guard<std::mutex> lock(lock_);
- io_state_ = READ_AHEAD_IO_TRANSITION::IO_TERMINATED;
- }
-
- cv.notify_one();
-}
-
-bool Snapuserd::ReadAheadIOCompleted(bool sync) {
- if (sync) {
- // Flush the entire buffer region
- int ret = msync(mapped_addr_, total_mapped_addr_length_, MS_SYNC);
- if (ret < 0) {
- PLOG(ERROR) << "msync failed after ReadAheadIOCompleted: " << ret;
- return false;
- }
-
- // Metadata and data are synced. Now, update the state.
- // We need to update the state after flushing data; if there is a crash
- // when read-ahead IO is in progress, the state of data in the COW file
- // is unknown. kCowReadAheadDone acts as a checkpoint wherein the data
- // in the scratch space is good and during next reboot, read-ahead thread
- // can safely re-construct the data.
- struct BufferState* ra_state = GetBufferState();
- ra_state->read_ahead_state = kCowReadAheadDone;
-
- ret = msync(mapped_addr_, BLOCK_SZ, MS_SYNC);
- if (ret < 0) {
- PLOG(ERROR) << "msync failed to flush Readahead completion state...";
- return false;
- }
- }
-
- // Notify the worker threads
- {
- std::lock_guard<std::mutex> lock(lock_);
- io_state_ = READ_AHEAD_IO_TRANSITION::IO_IN_PROGRESS;
- }
-
- cv.notify_all();
- return true;
-}
-
-void Snapuserd::ReadAheadIOFailed() {
- {
- std::lock_guard<std::mutex> lock(lock_);
- io_state_ = READ_AHEAD_IO_TRANSITION::READ_AHEAD_FAILURE;
- }
-
- cv.notify_all();
-}
-
-//========== End of state transition functions ====================
-
-bool Snapuserd::IsChunkIdMetadata(chunk_t chunk) {
- uint32_t stride = exceptions_per_area_ + 1;
- lldiv_t divresult = lldiv(chunk, stride);
-
- return (divresult.rem == NUM_SNAPSHOT_HDR_CHUNKS);
-}
-
-// Find the next free chunk-id to be assigned. Check if the next free
-// chunk-id represents a metadata page. If so, skip it.
-chunk_t Snapuserd::GetNextAllocatableChunkId(chunk_t chunk) {
- chunk_t next_chunk = chunk + 1;
-
- if (IsChunkIdMetadata(next_chunk)) {
- next_chunk += 1;
- }
- return next_chunk;
-}
-
-void Snapuserd::CheckMergeCompletionStatus() {
- if (!merge_initiated_) {
- SNAP_LOG(INFO) << "Merge was not initiated. Total-data-ops: "
- << reader_->get_num_total_data_ops();
- return;
- }
-
- struct CowHeader* ch = reinterpret_cast<struct CowHeader*>(mapped_addr_);
-
- SNAP_LOG(INFO) << "Merge-status: Total-Merged-ops: " << ch->num_merge_ops
- << " Total-data-ops: " << reader_->get_num_total_data_ops();
-}
-
-/*
- * Read the metadata from COW device and
- * construct the metadata as required by the kernel.
- *
- * Please see design on kernel COW format
- *
- * 1: Read the metadata from internal COW device
- * 2: There are 3 COW operations:
- * a: Replace op
- * b: Copy op
- * c: Zero op
- * 3: For each of the 3 operations, op->new_block
- * represents the block number in the base device
- * for which one of the 3 operations have to be applied.
- * This represents the old_chunk in the kernel COW format
- * 4: We need to assign new_chunk for a corresponding old_chunk
- * 5: The algorithm is similar to how kernel assigns chunk number
- * while creating exceptions. However, there are few cases
- * which needs to be addressed here:
- * a: During merge process, kernel scans the metadata page
- * from backwards when merge is initiated. Since, we need
- * to make sure that the merge ordering follows our COW format,
- * we read the COW operation from backwards and populate the
- * metadata so that when kernel starts the merging from backwards,
- * those ops correspond to the beginning of our COW format.
- * b: Kernel can merge successive operations if the two chunk IDs
- * are contiguous. This can be problematic when there is a crash
- * during merge; specifically when the merge operation has dependency.
- * These dependencies can only happen during copy operations.
- *
- * To avoid this problem, we make sure overlap copy operations
- * are not batch merged.
- * 6: Use a monotonically increasing chunk number to assign the
- * new_chunk
- * 7: Each chunk-id represents either
- * a: Metadata page or
- * b: Data page
- * 8: Chunk-id representing a data page is stored in a map.
- * 9: Chunk-id representing a metadata page is converted into a vector
- * index. We store this in vector as kernel requests metadata during
- * two stage:
- * a: When initial dm-snapshot device is created, kernel requests
- * all the metadata and stores it in its internal data-structures.
- * b: During merge, kernel once again requests the same metadata
- * once-again.
- * In both these cases, a quick lookup based on chunk-id is done.
- * 10: When chunk number is incremented, we need to make sure that
- * if the chunk is representing a metadata page and skip.
- * 11: Each 4k page will contain 256 disk exceptions. We call this
- * exceptions_per_area_
- * 12: Kernel will stop issuing metadata IO request when new-chunk ID is 0.
- */
-bool Snapuserd::ReadMetadata() {
- reader_ = std::make_unique<CowReader>();
- CowOptions options;
- bool metadata_found = false;
- int replace_ops = 0, zero_ops = 0, copy_ops = 0;
-
- SNAP_LOG(DEBUG) << "ReadMetadata: Parsing cow file";
-
- if (!reader_->Parse(cow_fd_)) {
- SNAP_LOG(ERROR) << "Failed to parse";
- return false;
- }
-
- const auto& header = reader_->GetHeader();
- if (!(header.block_size == BLOCK_SZ)) {
- SNAP_LOG(ERROR) << "Invalid header block size found: " << header.block_size;
- return false;
- }
-
- SNAP_LOG(DEBUG) << "Merge-ops: " << header.num_merge_ops;
-
- if (!MmapMetadata()) {
- SNAP_LOG(ERROR) << "mmap failed";
- return false;
- }
-
- // Initialize the iterator for reading metadata
- std::unique_ptr<ICowOpIter> cowop_rm_iter = reader_->GetRevMergeOpIter();
-
- exceptions_per_area_ = (CHUNK_SIZE << SECTOR_SHIFT) / sizeof(struct disk_exception);
-
- // Start from chunk number 2. Chunk 0 represents header and chunk 1
- // represents first metadata page.
- chunk_t data_chunk_id = NUM_SNAPSHOT_HDR_CHUNKS + 1;
- size_t num_ops = 0;
-
- loff_t offset = 0;
- std::unique_ptr<uint8_t[]> de_ptr =
- std::make_unique<uint8_t[]>(exceptions_per_area_ * sizeof(struct disk_exception));
-
- // This memset is important. Kernel will stop issuing IO when new-chunk ID
- // is 0. When Area is not filled completely with all 256 exceptions,
- // this memset will ensure that metadata read is completed.
- memset(de_ptr.get(), 0, (exceptions_per_area_ * sizeof(struct disk_exception)));
-
- while (!cowop_rm_iter->AtEnd()) {
- const CowOperation* cow_op = cowop_rm_iter->Get();
- struct disk_exception* de =
- reinterpret_cast<struct disk_exception*>((char*)de_ptr.get() + offset);
-
- metadata_found = true;
- // This loop will handle all the replace and zero ops.
- // We will handle the copy ops later as it requires special
- // handling of assigning chunk-id's. Furthermore, we make
- // sure that replace/zero and copy ops are not batch merged; hence,
- // the bump in the chunk_id before break of this loop
- if (IsOrderedOp(*cow_op)) {
- data_chunk_id = GetNextAllocatableChunkId(data_chunk_id);
- break;
- }
-
- if (cow_op->type() == kCowReplaceOp) {
- replace_ops++;
- } else if (cow_op->type() == kCowZeroOp) {
- zero_ops++;
- }
-
- // Construct the disk-exception
- 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;
- offset += sizeof(struct disk_exception);
- cowop_rm_iter->Next();
-
- SNAP_LOG(DEBUG) << num_ops << ":"
- << " Old-chunk: " << de->old_chunk << " New-chunk: " << de->new_chunk;
-
- if (num_ops == exceptions_per_area_) {
- // Store it in vector at the right index. This maps the chunk-id to
- // vector index.
- vec_.push_back(std::move(de_ptr));
- offset = 0;
- num_ops = 0;
-
- // Create buffer for next area
- de_ptr = std::make_unique<uint8_t[]>(exceptions_per_area_ *
- sizeof(struct disk_exception));
- memset(de_ptr.get(), 0, (exceptions_per_area_ * sizeof(struct disk_exception)));
-
- if (cowop_rm_iter->AtEnd()) {
- vec_.push_back(std::move(de_ptr));
- }
- }
-
- data_chunk_id = GetNextAllocatableChunkId(data_chunk_id);
- }
-
- int num_ra_ops_per_iter = ((GetBufferDataSize()) / BLOCK_SZ);
- std::optional<chunk_t> prev_id = {};
- std::vector<const CowOperation*> vec;
- std::set<uint64_t> dest_blocks;
- 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_ordered_ops;
-
- while (!cowop_rm_iter->AtEnd()) {
- do {
- const CowOperation* cow_op = cowop_rm_iter->Get();
-
- // We have two cases specific cases:
- //
- // =====================================================
- // Case 1: Overlapping copy regions
- //
- // Ex:
- //
- // Source -> Destination
- //
- // 1: 15 -> 18
- // 2: 16 -> 19
- // 3: 17 -> 20
- // 4: 18 -> 21
- // 5: 19 -> 22
- // 6: 20 -> 23
- //
- // We have 6 copy operations to be executed in OTA and there is a overlap. Update-engine
- // will write to COW file as follows:
- //
- // Op-1: 20 -> 23
- // Op-2: 19 -> 22
- // Op-3: 18 -> 21
- // Op-4: 17 -> 20
- // Op-5: 16 -> 19
- // Op-6: 15 -> 18
- //
- // Note that the blocks numbers are contiguous. Hence, all 6 copy
- // operations can be batch merged. However, that will be
- // problematic if we have a crash as block 20, 19, 18 would have
- // been overwritten and hence subsequent recovery may end up with
- // a silent data corruption when op-1, op-2 and op-3 are
- // re-executed.
- //
- // To address the above problem, read-ahead thread will
- // read all the 6 source blocks, cache them in the scratch
- // space of the COW file. During merge, read-ahead
- // thread will serve the blocks from the read-ahead cache.
- // If there is a crash during merge; on subsequent reboot,
- // read-ahead thread will recover the data from the
- // 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.
- //===========================================================
- uint64_t block_source = cow_op->source();
- if (prev_id.has_value()) {
- if (dest_blocks.count(cow_op->new_block) || source_blocks.count(block_source)) {
- break;
- }
- }
- metadata_found = true;
- pending_ordered_ops -= 1;
- vec.push_back(cow_op);
- dest_blocks.insert(block_source);
- source_blocks.insert(cow_op->new_block);
- prev_id = cow_op->new_block;
- cowop_rm_iter->Next();
- } while (!cowop_rm_iter->AtEnd() && pending_ordered_ops);
-
- data_chunk_id = GetNextAllocatableChunkId(data_chunk_id);
- SNAP_LOG(DEBUG) << "Batch Merge copy-ops of size: " << vec.size()
- << " Area: " << vec_.size() << " Area offset: " << offset
- << " Pending-ordered-ops in this area: " << pending_ordered_ops;
-
- for (size_t i = 0; i < vec.size(); i++) {
- struct disk_exception* de =
- reinterpret_cast<struct disk_exception*>((char*)de_ptr.get() + offset);
- 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), cow_op));
- offset += sizeof(struct disk_exception);
- num_ops += 1;
- if (cow_op->type() == kCowCopyOp) {
- copy_ops++;
- }
-
- if (read_ahead_feature_) {
- read_ahead_ops_.push_back(cow_op);
- }
-
- SNAP_LOG(DEBUG) << num_ops << ":"
- << " Ordered-op: "
- << " Old-chunk: " << de->old_chunk << " New-chunk: " << de->new_chunk;
-
- if (num_ops == exceptions_per_area_) {
- // Store it in vector at the right index. This maps the chunk-id to
- // vector index.
- vec_.push_back(std::move(de_ptr));
- num_ops = 0;
- offset = 0;
-
- // Create buffer for next area
- de_ptr = std::make_unique<uint8_t[]>(exceptions_per_area_ *
- sizeof(struct disk_exception));
- memset(de_ptr.get(), 0, (exceptions_per_area_ * sizeof(struct disk_exception)));
-
- if (cowop_rm_iter->AtEnd()) {
- vec_.push_back(std::move(de_ptr));
- SNAP_LOG(DEBUG) << "ReadMetadata() completed; Number of Areas: " << vec_.size();
- }
-
- if (!(pending_ordered_ops == 0)) {
- SNAP_LOG(ERROR) << "Invalid pending_ordered_ops: expected: 0 found: "
- << pending_ordered_ops;
- return false;
- }
- pending_ordered_ops = exceptions_per_area_;
- }
-
- data_chunk_id = GetNextAllocatableChunkId(data_chunk_id);
- 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_ordered_ops % num_ra_ops_per_iter == 0)) {
- data_chunk_id = GetNextAllocatableChunkId(data_chunk_id);
- }
- }
- vec.clear();
- dest_blocks.clear();
- source_blocks.clear();
- prev_id.reset();
- }
-
- // Partially filled area or there is no metadata
- // If there is no metadata, fill with zero so that kernel
- // is aware that merge is completed.
- if (num_ops || !metadata_found) {
- vec_.push_back(std::move(de_ptr));
- SNAP_LOG(DEBUG) << "ReadMetadata() completed. Partially filled area num_ops: " << num_ops
- << "Areas : " << vec_.size();
- }
-
- chunk_vec_.shrink_to_fit();
- vec_.shrink_to_fit();
- read_ahead_ops_.shrink_to_fit();
-
- // Sort the vector based on sectors as we need this during un-aligned access
- std::sort(chunk_vec_.begin(), chunk_vec_.end(), compare);
-
- 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
- << " Total-data-ops: " << reader_->get_num_total_data_ops();
-
- // Total number of sectors required for creating dm-user device
- num_sectors_ = ChunkToSector(data_chunk_id);
- merge_initiated_ = false;
- PrepareReadAhead();
-
- return true;
-}
-
-bool Snapuserd::MmapMetadata() {
- const auto& header = reader_->GetHeader();
-
- if (header.prefix.major_version >= 2 && header.buffer_size > 0) {
- total_mapped_addr_length_ = header.prefix.header_size + BUFFER_REGION_DEFAULT_SIZE;
- read_ahead_feature_ = true;
- } else {
- // mmap the first 4k page - older COW format
- total_mapped_addr_length_ = BLOCK_SZ;
- read_ahead_feature_ = false;
- }
-
- mapped_addr_ = mmap(NULL, total_mapped_addr_length_, PROT_READ | PROT_WRITE, MAP_SHARED,
- cow_fd_.get(), 0);
- if (mapped_addr_ == MAP_FAILED) {
- SNAP_LOG(ERROR) << "mmap metadata failed";
- return false;
- }
-
- return true;
-}
-
-void Snapuserd::UnmapBufferRegion() {
- int ret = munmap(mapped_addr_, total_mapped_addr_length_);
- if (ret < 0) {
- SNAP_PLOG(ERROR) << "munmap failed";
- }
-}
-
-void MyLogger(android::base::LogId, android::base::LogSeverity severity, const char*, const char*,
- unsigned int, const char* message) {
- if (severity == android::base::ERROR) {
- fprintf(stderr, "%s\n", message);
- } else {
- fprintf(stdout, "%s\n", message);
- }
-}
-
-bool Snapuserd::InitCowDevice() {
- cow_fd_.reset(open(cow_device_.c_str(), O_RDWR));
- if (cow_fd_ < 0) {
- SNAP_PLOG(ERROR) << "Open Failed: " << cow_device_;
- return false;
- }
-
- 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++) {
- (void)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
- */
-bool Snapuserd::Start() {
- std::vector<std::future<bool>> threads;
- std::future<bool> ra_thread;
- bool rathread = (read_ahead_feature_ && (read_ahead_ops_.size() > 0));
-
- // Start the read-ahead thread and wait
- // for it as the data has to be re-constructed
- // from COW device.
- if (rathread) {
- ra_thread = std::async(std::launch::async, &ReadAheadThread::RunThread,
- read_ahead_thread_.get());
- if (!WaitForReadAheadToStart()) {
- SNAP_LOG(ERROR) << "Failed to start Read-ahead thread...";
- return false;
- }
-
- SNAP_LOG(INFO) << "Read-ahead thread started...";
- }
-
- // Launch worker threads
- for (int i = 0; i < worker_threads_.size(); i++) {
- threads.emplace_back(
- std::async(std::launch::async, &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;
- }
-
- if (rathread) {
- // Notify the read-ahead thread that all worker threads
- // are done. We need this explicit notification when
- // there is an IO failure or there was a switch
- // of dm-user table; thus, forcing the read-ahead
- // thread to wake up.
- MergeCompleted();
- ret = ret && ra_thread.get();
- }
-
- return ret;
-}
-
-uint64_t Snapuserd::GetBufferMetadataOffset() {
- const auto& header = reader_->GetHeader();
-
- size_t size = header.prefix.header_size + sizeof(BufferState);
- return size;
-}
-
-/*
- * Metadata for read-ahead is 16 bytes. For a 2 MB region, we will
- * end up with 8k (2 PAGE) worth of metadata. Thus, a 2MB buffer
- * region is split into:
- *
- * 1: 8k metadata
- *
- */
-size_t Snapuserd::GetBufferMetadataSize() {
- const auto& header = reader_->GetHeader();
-
- size_t metadata_bytes = (header.buffer_size * sizeof(struct ScratchMetadata)) / BLOCK_SZ;
- return metadata_bytes;
-}
-
-size_t Snapuserd::GetBufferDataOffset() {
- const auto& header = reader_->GetHeader();
-
- return (header.prefix.header_size + GetBufferMetadataSize());
-}
-
-/*
- * (2MB - 8K = 2088960 bytes) will be the buffer region to hold the data.
- */
-size_t Snapuserd::GetBufferDataSize() {
- const auto& header = reader_->GetHeader();
-
- size_t size = header.buffer_size - GetBufferMetadataSize();
- return size;
-}
-
-struct BufferState* Snapuserd::GetBufferState() {
- const auto& header = reader_->GetHeader();
-
- struct BufferState* ra_state =
- reinterpret_cast<struct BufferState*>((char*)mapped_addr_ + header.prefix.header_size);
- return ra_state;
-}
-
-} // namespace snapshot
-} // namespace android
diff --git a/fs_mgr/libsnapshot/snapuserd/dm-snapshot-merge/snapuserd.h b/fs_mgr/libsnapshot/snapuserd/dm-snapshot-merge/snapuserd.h
deleted file mode 100644
index beb6004..0000000
--- a/fs_mgr/libsnapshot/snapuserd/dm-snapshot-merge/snapuserd.h
+++ /dev/null
@@ -1,332 +0,0 @@
-// Copyright (C) 2020 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#pragma once
-
-#include <linux/types.h>
-#include <stdint.h>
-#include <stdlib.h>
-#include <sys/mman.h>
-
-#include <bitset>
-#include <condition_variable>
-#include <csignal>
-#include <cstring>
-#include <future>
-#include <iostream>
-#include <limits>
-#include <map>
-#include <mutex>
-#include <string>
-#include <thread>
-#include <unordered_map>
-#include <unordered_set>
-#include <vector>
-
-#include <android-base/file.h>
-#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 <snapuserd/snapuserd_buffer.h>
-#include <snapuserd/snapuserd_kernel.h>
-
-namespace android {
-namespace snapshot {
-
-using android::base::unique_fd;
-using namespace std::chrono_literals;
-
-static constexpr size_t PAYLOAD_SIZE = (1UL << 20);
-static_assert(PAYLOAD_SIZE >= BLOCK_SZ);
-
-/*
- * With 4 threads, we get optimal performance
- * when update_verifier reads the partition during
- * boot.
- */
-static constexpr int NUM_THREADS_PER_PARTITION = 4;
-
-/*
- * State transitions between worker threads and read-ahead
- * threads.
- *
- * READ_AHEAD_BEGIN: Worker threads initiates the read-ahead
- * thread to begin reading the copy operations
- * for each bounded region.
- *
- * READ_AHEAD_IN_PROGRESS: When read ahead thread is in-flight
- * and reading the copy operations.
- *
- * IO_IN_PROGRESS: Merge operation is in-progress by worker threads.
- *
- * IO_TERMINATED: When all the worker threads are done, request the
- * read-ahead thread to terminate
- *
- * READ_AHEAD_FAILURE: If there are any IO failures when read-ahead
- * thread is reading from COW device.
- *
- * The transition of each states is described in snapuserd_readahead.cpp
- */
-enum class READ_AHEAD_IO_TRANSITION {
- READ_AHEAD_BEGIN,
- READ_AHEAD_IN_PROGRESS,
- IO_IN_PROGRESS,
- IO_TERMINATED,
- READ_AHEAD_FAILURE,
-};
-
-class Snapuserd;
-
-class ReadAheadThread {
- public:
- ReadAheadThread(const std::string& cow_device, const std::string& backing_device,
- const std::string& misc_name, std::shared_ptr<Snapuserd> snapuserd);
- bool RunThread();
-
- private:
- void InitializeRAIter();
- bool RAIterDone();
- void RAIterNext();
- const CowOperation* GetRAOpIter();
- void InitializeBuffer();
-
- bool InitializeFds();
- void CloseFds() {
- cow_fd_ = {};
- backing_store_fd_ = {};
- }
-
- bool ReadAheadIOStart();
- void PrepareReadAhead(uint64_t* source_offset, int* pending_ops, std::vector<uint64_t>& blocks);
- bool ReconstructDataFromCow();
- void CheckOverlap(const CowOperation* cow_op);
-
- void* read_ahead_buffer_;
- void* metadata_buffer_;
- std::vector<const CowOperation*>::reverse_iterator read_ahead_iter_;
- std::string cow_device_;
- std::string backing_store_device_;
- std::string misc_name_;
-
- unique_fd cow_fd_;
- unique_fd backing_store_fd_;
-
- std::shared_ptr<Snapuserd> snapuserd_;
-
- std::unordered_set<uint64_t> dest_blocks_;
- std::unordered_set<uint64_t> source_blocks_;
- bool overlap_;
-};
-
-class WorkerThread {
- public:
- 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);
- bool RunThread();
-
- private:
- // Initialization
- void InitializeBufsink();
- bool InitializeFds();
- bool InitReader();
- void CloseFds() {
- ctrl_fd_ = {};
- backing_store_fd_ = {};
- }
-
- // Functions interacting with dm-user
- bool ReadDmUserHeader();
- bool DmuserReadRequest();
- bool DmuserWriteRequest();
- bool ReadDmUserPayload(void* buffer, size_t size);
- bool WriteDmUserPayload(size_t size, bool header_response);
-
- bool ReadDiskExceptions(chunk_t chunk, size_t size);
- bool ZerofillDiskExceptions(size_t read_size);
- void ConstructKernelCowHeader();
-
- // IO Path
- bool ProcessIORequest();
- int ReadData(sector_t sector, size_t size);
- int ReadUnalignedSector(sector_t sector, size_t size,
- std::vector<std::pair<sector_t, const CowOperation*>>::iterator& it);
-
- // Processing COW operations
- bool ProcessCowOp(const CowOperation* cow_op);
- bool ProcessReplaceOp(const CowOperation* cow_op);
- // Handles Copy
- bool ProcessCopyOp(const CowOperation* cow_op);
- bool ProcessZeroOp();
-
- bool ReadFromBaseDevice(const CowOperation* cow_op);
- bool GetReadAheadPopulatedBuffer(const CowOperation* cow_op);
-
- // Merge related functions
- bool ProcessMergeComplete(chunk_t chunk, void* buffer);
- loff_t GetMergeStartOffset(void* merged_buffer, void* unmerged_buffer,
- int* unmerged_exceptions);
-
- int GetNumberOfMergedOps(void* merged_buffer, void* unmerged_buffer, loff_t offset,
- int unmerged_exceptions, bool* copy_op, bool* commit);
-
- sector_t ChunkToSector(chunk_t chunk) { return chunk << CHUNK_SHIFT; }
- chunk_t SectorToChunk(sector_t sector) { return sector >> CHUNK_SHIFT; }
-
- std::unique_ptr<CowReader> reader_;
- BufferSink bufsink_;
-
- std::string cow_device_;
- std::string backing_store_device_;
- std::string control_device_;
- std::string misc_name_;
-
- unique_fd cow_fd_;
- unique_fd backing_store_fd_;
- unique_fd ctrl_fd_;
-
- std::shared_ptr<Snapuserd> snapuserd_;
- uint32_t exceptions_per_area_;
-};
-
-class Snapuserd : public std::enable_shared_from_this<Snapuserd> {
- public:
- Snapuserd(const std::string& misc_name, const std::string& cow_device,
- const std::string& backing_device);
- bool InitCowDevice();
- bool Start();
- const std::string& GetControlDevicePath() { return control_device_; }
- const std::string& GetMiscName() { return misc_name_; }
- uint64_t GetNumSectors() { return num_sectors_; }
- bool IsAttached() const { return attached_; }
- void AttachControlDevice() { attached_ = true; }
-
- void CheckMergeCompletionStatus();
- bool CommitMerge(int num_merge_ops);
-
- void CloseFds() { cow_fd_ = {}; }
- void FreeResources() {
- worker_threads_.clear();
- read_ahead_thread_ = nullptr;
- }
- size_t GetMetadataAreaSize() { return vec_.size(); }
- 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_; }
- const std::vector<std::unique_ptr<uint8_t[]>>& GetMetadataVec() const { return vec_; }
-
- static bool compare(std::pair<sector_t, const CowOperation*> p1,
- std::pair<sector_t, const CowOperation*> p2) {
- return p1.first < p2.first;
- }
-
- void UnmapBufferRegion();
- bool MmapMetadata();
-
- // Read-ahead related functions
- std::vector<const CowOperation*>& GetReadAheadOpsVec() { return read_ahead_ops_; }
- std::unordered_map<uint64_t, void*>& GetReadAheadMap() { return read_ahead_buffer_map_; }
- void* GetMappedAddr() { return mapped_addr_; }
- bool IsReadAheadFeaturePresent() { return read_ahead_feature_; }
- void PrepareReadAhead();
- void StartReadAhead();
- void MergeCompleted();
- bool ReadAheadIOCompleted(bool sync);
- void ReadAheadIOFailed();
- bool WaitForMergeToComplete();
- bool GetReadAheadPopulatedBuffer(uint64_t block, void* buffer);
- bool ReconstructDataFromCow() { return populate_data_from_cow_; }
- void ReconstructDataFromCowFinish() { populate_data_from_cow_ = false; }
- bool WaitForReadAheadToStart();
-
- uint64_t GetBufferMetadataOffset();
- size_t GetBufferMetadataSize();
- size_t GetBufferDataOffset();
- size_t GetBufferDataSize();
-
- // Final block to be merged in a given read-ahead buffer region
- void SetFinalBlockMerged(uint64_t x) { final_block_merged_ = x; }
- uint64_t GetFinalBlockMerged() { return final_block_merged_; }
- // 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);
- chunk_t GetNextAllocatableChunkId(chunk_t chunk_id);
-
- bool GetRABuffer(std::unique_lock<std::mutex>* lock, uint64_t block, void* buffer);
- bool ReadMetadata();
- sector_t ChunkToSector(chunk_t chunk) { return chunk << CHUNK_SHIFT; }
- chunk_t SectorToChunk(sector_t sector) { return sector >> CHUNK_SHIFT; }
- 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_;
- std::string misc_name_;
-
- unique_fd cow_fd_;
-
- uint32_t exceptions_per_area_;
- uint64_t num_sectors_;
-
- std::unique_ptr<CowReader> reader_;
-
- // Vector of disk exception which is a
- // mapping of old-chunk to new-chunk
- std::vector<std::unique_ptr<uint8_t[]>> vec_;
-
- // chunk_vec stores the pseudo mapping of sector
- // to COW operations.
- std::vector<std::pair<sector_t, const CowOperation*>> chunk_vec_;
-
- std::mutex lock_;
- std::condition_variable cv;
-
- void* mapped_addr_;
- size_t total_mapped_addr_length_;
-
- std::vector<std::unique_ptr<WorkerThread>> worker_threads_;
- // Read-ahead related
- std::unordered_map<uint64_t, void*> read_ahead_buffer_map_;
- std::vector<const CowOperation*> read_ahead_ops_;
- bool populate_data_from_cow_ = false;
- bool read_ahead_feature_;
- uint64_t final_block_merged_;
- int total_ra_blocks_merged_ = 0;
- READ_AHEAD_IO_TRANSITION io_state_;
- std::unique_ptr<ReadAheadThread> read_ahead_thread_;
-
- bool merge_initiated_ = false;
- bool attached_ = false;
- bool is_socket_present_;
-};
-
-} // namespace snapshot
-} // namespace android
diff --git a/fs_mgr/libsnapshot/snapuserd/dm-snapshot-merge/snapuserd_readahead.cpp b/fs_mgr/libsnapshot/snapuserd/dm-snapshot-merge/snapuserd_readahead.cpp
deleted file mode 100644
index f1b9245..0000000
--- a/fs_mgr/libsnapshot/snapuserd/dm-snapshot-merge/snapuserd_readahead.cpp
+++ /dev/null
@@ -1,482 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "snapuserd.h"
-
-#include <csignal>
-#include <optional>
-#include <set>
-
-#include <snapuserd/snapuserd_client.h>
-
-namespace android {
-namespace snapshot {
-
-using namespace android;
-using namespace android::dm;
-using android::base::unique_fd;
-
-#define SNAP_LOG(level) LOG(level) << misc_name_ << ": "
-#define SNAP_PLOG(level) PLOG(level) << misc_name_ << ": "
-
-/*
- * Merging a copy operation involves the following flow:
- *
- * 1: dm-snapshot layer requests merge for a 4k block. dm-user sends the request
- * to the daemon
- * 2: daemon reads the source block
- * 3: daemon copies the source data
- * 4: IO completion sent back to dm-user (a switch from user space to kernel)
- * 5: dm-snapshot merges the data to base device
- * 6: dm-snapshot sends the merge-completion IO to dm-user
- * 7: dm-user re-directs the merge completion IO to daemon (one more switch)
- * 8: daemon updates the COW file about the completed merge request (a write syscall) and followed
- * by a fysnc. 9: Send the IO completion back to dm-user
- *
- * The above sequence is a significant overhead especially when merging one 4k
- * block at a time.
- *
- * Read-ahead layer will optimize the above path by reading the data from base
- * device in the background so that merging thread can retrieve the data from
- * the read-ahead cache. Additionally, syncing of merged data is deferred to
- * read-ahead thread threadby the IO path is not bottlenecked.
- *
- * We create a scratch space of 2MB to store the read-ahead data in the COW
- * device.
- *
- * +-----------------------+
- * | Header (fixed) |
- * +-----------------------+
- * | Scratch space | <-- 2MB
- * +-----------------------+
- *
- * Scratch space is as follows:
- *
- * +-----------------------+
- * | Metadata | <- 4k page
- * +-----------------------+
- * | Metadata | <- 4k page
- * +-----------------------+
- * | |
- * | Read-ahead data |
- * | |
- * +-----------------------+
- *
- * State transitions and communication between read-ahead thread and worker
- * thread during merge:
- * =====================================================================
- *
- * Worker Threads Read-Ahead thread
- * ------------------------------------------------------------------
- *
- * |
- * |
- * --> -----------------READ_AHEAD_BEGIN------------->|
- * | | | READ_AHEAD_IN_PROGRESS
- * | WAIT |
- * | | |
- * | |<-----------------IO_IN_PROGRESS---------------
- * | | |
- * | | IO_IN_PRGRESS WAIT
- * | | |
- * |<--| |
- * | |
- * ------------------IO_TERMINATED--------------->|
- * END
- *
- *
- * ===================================================================
- *
- * Example:
- *
- * We have 6 copy operations to be executed in OTA and there is a overlap. Update-engine
- * will write to COW file as follows:
- *
- * Op-1: 20 -> 23
- * Op-2: 19 -> 22
- * Op-3: 18 -> 21
- * Op-4: 17 -> 20
- * Op-5: 16 -> 19
- * Op-6: 15 -> 18
- *
- * Read-ahead thread will read all the 6 source blocks and store the data in the
- * scratch space. Metadata will contain the destination block numbers. Thus,
- * scratch space will look something like this:
- *
- * +--------------+
- * | Block 23 |
- * | offset - 1 |
- * +--------------+
- * | Block 22 |
- * | offset - 2 |
- * +--------------+
- * | Block 21 |
- * | offset - 3 |
- * +--------------+
- * ...
- * ...
- * +--------------+
- * | Data-Block 20| <-- offset - 1
- * +--------------+
- * | Data-Block 19| <-- offset - 2
- * +--------------+
- * | Data-Block 18| <-- offset - 3
- * +--------------+
- * ...
- * ...
- *
- * ====================================================================
- * IO Path:
- *
- * Read-ahead will serve the data to worker threads during merge only
- * after metadata and data are persisted to the scratch space. Worker
- * threads during merge will always retrieve the data from cache; if the
- * cache is not populated, it will wait for the read-ahead thread to finish.
- * Furthermore, the number of operations merged will by synced to the header
- * only when all the blocks in the read-ahead cache are merged. In the above
- * case, when all 6 operations are merged, COW Header is updated with
- * num_merge_ops = 6.
- *
- * Merge resume after crash:
- *
- * Let's say we have a crash after 5 operations are merged. i.e. after
- * Op-5: 16->19 is completed but before the Op-6 is merged. Thus, COW Header
- * num_merge_ops will be 0 as the all the ops were not merged yet. During next
- * reboot, read-ahead thread will re-construct the data in-memory from the
- * scratch space; when merge resumes, Op-1 will be re-exectued. However,
- * data will be served from read-ahead cache safely even though, block 20
- * was over-written by Op-4.
- *
- */
-
-ReadAheadThread::ReadAheadThread(const std::string& cow_device, const std::string& backing_device,
- const std::string& misc_name,
- std::shared_ptr<Snapuserd> snapuserd) {
- cow_device_ = cow_device;
- backing_store_device_ = backing_device;
- misc_name_ = misc_name;
- snapuserd_ = snapuserd;
-}
-
-void ReadAheadThread::CheckOverlap(const CowOperation* cow_op) {
- uint64_t source_block = cow_op->source();
- if (dest_blocks_.count(cow_op->new_block) || source_blocks_.count(source_block)) {
- overlap_ = true;
- }
-
- dest_blocks_.insert(source_block);
- source_blocks_.insert(cow_op->new_block);
-}
-
-void ReadAheadThread::PrepareReadAhead(uint64_t* source_offset, int* pending_ops,
- std::vector<uint64_t>& blocks) {
- int num_ops = *pending_ops;
- int nr_consecutive = 0;
- CHECK_NE(source_offset, nullptr);
-
- if (!RAIterDone() && num_ops) {
- // Get the first block with offset
- const CowOperation* cow_op = GetRAOpIter();
- CHECK_NE(cow_op, nullptr);
- *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);
-
- if (!overlap_) {
- CheckOverlap(cow_op);
- }
-
- /*
- * Find number of consecutive blocks working backwards.
- */
- while (!RAIterDone() && num_ops) {
- const CowOperation* op = GetRAOpIter();
- CHECK_NE(op, nullptr);
- uint64_t next_offset = op->source();
- if (op->type() == kCowCopyOp) {
- next_offset *= BLOCK_SZ;
- }
- if (next_offset + nr_consecutive * BLOCK_SZ != *source_offset) {
- break;
- }
- nr_consecutive += 1;
- num_ops -= 1;
- blocks.push_back(op->new_block);
- RAIterNext();
-
- if (!overlap_) {
- CheckOverlap(op);
- }
- }
- }
-}
-
-bool ReadAheadThread::ReconstructDataFromCow() {
- std::unordered_map<uint64_t, void*>& read_ahead_buffer_map = snapuserd_->GetReadAheadMap();
- read_ahead_buffer_map.clear();
- loff_t metadata_offset = 0;
- loff_t start_data_offset = snapuserd_->GetBufferDataOffset();
- int num_ops = 0;
- int total_blocks_merged = 0;
-
- // This memcpy is important as metadata_buffer_ will be an unaligned address and will fault
- // on 32-bit systems
- std::unique_ptr<uint8_t[]> metadata_buffer =
- std::make_unique<uint8_t[]>(snapuserd_->GetBufferMetadataSize());
- memcpy(metadata_buffer.get(), metadata_buffer_, snapuserd_->GetBufferMetadataSize());
-
- while (true) {
- struct ScratchMetadata* bm = reinterpret_cast<struct ScratchMetadata*>(
- (char*)metadata_buffer.get() + metadata_offset);
-
- // Done reading metadata
- if (bm->new_block == 0 && bm->file_offset == 0) {
- break;
- }
-
- loff_t buffer_offset = bm->file_offset - start_data_offset;
- void* bufptr = static_cast<void*>((char*)read_ahead_buffer_ + buffer_offset);
- read_ahead_buffer_map[bm->new_block] = bufptr;
- num_ops += 1;
- total_blocks_merged += 1;
-
- metadata_offset += sizeof(struct ScratchMetadata);
- }
-
- // 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 (!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);
- RAIterNext();
- } else {
- // Verify that we have covered all the ops which were re-constructed
- // from COW device - These are the ops which are being
- // re-constructed after crash.
- if (!(num_ops == 0)) {
- SNAP_LOG(ERROR) << "ReconstructDataFromCow failed. Not all ops recoverd "
- << " Pending ops: " << num_ops;
- snapuserd_->ReadAheadIOFailed();
- return false;
- }
- break;
- }
- }
-
- snapuserd_->SetTotalRaBlocksMerged(total_blocks_merged);
-
- snapuserd_->ReconstructDataFromCowFinish();
-
- if (!snapuserd_->ReadAheadIOCompleted(true)) {
- SNAP_LOG(ERROR) << "ReadAheadIOCompleted failed...";
- snapuserd_->ReadAheadIOFailed();
- return false;
- }
-
- SNAP_LOG(INFO) << "ReconstructDataFromCow success";
- return true;
-}
-
-bool ReadAheadThread::ReadAheadIOStart() {
- // Check if the data has to be constructed from the COW file.
- // This will be true only once during boot up after a crash
- // during merge.
- if (snapuserd_->ReconstructDataFromCow()) {
- return ReconstructDataFromCow();
- }
-
- std::unordered_map<uint64_t, void*>& read_ahead_buffer_map = snapuserd_->GetReadAheadMap();
- read_ahead_buffer_map.clear();
-
- int num_ops = (snapuserd_->GetBufferDataSize()) / BLOCK_SZ;
- loff_t metadata_offset = 0;
-
- struct ScratchMetadata* bm =
- reinterpret_cast<struct ScratchMetadata*>((char*)metadata_buffer_ + metadata_offset);
-
- bm->new_block = 0;
- bm->file_offset = 0;
-
- std::vector<uint64_t> blocks;
-
- loff_t buffer_offset = 0;
- loff_t offset = 0;
- loff_t file_offset = snapuserd_->GetBufferDataOffset();
- int total_blocks_merged = 0;
- overlap_ = false;
- dest_blocks_.clear();
- source_blocks_.clear();
-
- while (true) {
- uint64_t source_offset;
- int linear_blocks;
-
- PrepareReadAhead(&source_offset, &num_ops, blocks);
- linear_blocks = blocks.size();
- if (linear_blocks == 0) {
- // No more blocks to read
- SNAP_LOG(DEBUG) << " Read-ahead completed....";
- break;
- }
-
- // Get the first block in the consecutive set of 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;
-
- // Mark the block number as the one which will
- // be the final block to be merged in this entire region.
- // Read-ahead thread will get
- // notified when this block is merged to make
- // forward progress
- snapuserd_->SetFinalBlockMerged(blocks.back());
-
- while (linear_blocks) {
- uint64_t new_block = blocks.back();
- blocks.pop_back();
- // Assign the mapping
- void* bufptr = static_cast<void*>((char*)read_ahead_buffer_ + offset);
- read_ahead_buffer_map[new_block] = bufptr;
- offset += BLOCK_SZ;
-
- bm = reinterpret_cast<struct ScratchMetadata*>((char*)metadata_buffer_ +
- metadata_offset);
- bm->new_block = new_block;
- bm->file_offset = file_offset;
-
- metadata_offset += sizeof(struct ScratchMetadata);
- file_offset += BLOCK_SZ;
-
- linear_blocks -= 1;
- }
-
- // 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_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;
- }
-
- // This is important - explicitly set the contents to zero. This is used
- // when re-constructing the data after crash. This indicates end of
- // reading metadata contents when re-constructing the data
- bm = reinterpret_cast<struct ScratchMetadata*>((char*)metadata_buffer_ + metadata_offset);
- bm->new_block = 0;
- bm->file_offset = 0;
-
- buffer_offset += io_size;
- }
-
- snapuserd_->SetTotalRaBlocksMerged(total_blocks_merged);
-
- // Flush the data only if we have a overlapping blocks in the region
- if (!snapuserd_->ReadAheadIOCompleted(overlap_)) {
- SNAP_LOG(ERROR) << "ReadAheadIOCompleted failed...";
- snapuserd_->ReadAheadIOFailed();
- return false;
- }
-
- return true;
-}
-
-bool ReadAheadThread::RunThread() {
- if (!InitializeFds()) {
- return false;
- }
-
- InitializeRAIter();
- InitializeBuffer();
-
- while (!RAIterDone()) {
- if (!ReadAheadIOStart()) {
- return false;
- }
-
- bool status = snapuserd_->WaitForMergeToComplete();
-
- if (status && !snapuserd_->CommitMerge(snapuserd_->GetTotalRaBlocksMerged())) {
- return false;
- }
-
- if (!status) break;
- }
-
- CloseFds();
- SNAP_LOG(INFO) << " ReadAhead thread terminating....";
- return true;
-}
-
-// Initialization
-bool ReadAheadThread::InitializeFds() {
- backing_store_fd_.reset(open(backing_store_device_.c_str(), O_RDONLY));
- if (backing_store_fd_ < 0) {
- SNAP_PLOG(ERROR) << "Open Failed: " << backing_store_device_;
- return false;
- }
-
- cow_fd_.reset(open(cow_device_.c_str(), O_RDWR));
- if (cow_fd_ < 0) {
- SNAP_PLOG(ERROR) << "Open Failed: " << cow_device_;
- return false;
- }
-
- return true;
-}
-
-void ReadAheadThread::InitializeRAIter() {
- std::vector<const CowOperation*>& read_ahead_ops = snapuserd_->GetReadAheadOpsVec();
- read_ahead_iter_ = read_ahead_ops.rbegin();
-}
-
-bool ReadAheadThread::RAIterDone() {
- std::vector<const CowOperation*>& read_ahead_ops = snapuserd_->GetReadAheadOpsVec();
- return read_ahead_iter_ == read_ahead_ops.rend();
-}
-
-void ReadAheadThread::RAIterNext() {
- read_ahead_iter_++;
-}
-
-const CowOperation* ReadAheadThread::GetRAOpIter() {
- return *read_ahead_iter_;
-}
-
-void ReadAheadThread::InitializeBuffer() {
- void* mapped_addr = snapuserd_->GetMappedAddr();
- // Map the scratch space region into memory
- metadata_buffer_ =
- static_cast<void*>((char*)mapped_addr + snapuserd_->GetBufferMetadataOffset());
- read_ahead_buffer_ = static_cast<void*>((char*)mapped_addr + snapuserd_->GetBufferDataOffset());
-}
-
-} // namespace snapshot
-} // namespace android
diff --git a/fs_mgr/libsnapshot/snapuserd/dm-snapshot-merge/snapuserd_server.cpp b/fs_mgr/libsnapshot/snapuserd/dm-snapshot-merge/snapuserd_server.cpp
deleted file mode 100644
index 577b09d..0000000
--- a/fs_mgr/libsnapshot/snapuserd/dm-snapshot-merge/snapuserd_server.cpp
+++ /dev/null
@@ -1,559 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <arpa/inet.h>
-#include <cutils/sockets.h>
-#include <errno.h>
-#include <netinet/in.h>
-#include <stdint.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <sys/socket.h>
-#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_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;
- if (input == "stop") return DaemonOperations::STOP;
- 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;
-}
-
-SnapuserdServer::~SnapuserdServer() {
- // Close any client sockets that were added via AcceptClient().
- for (size_t i = 1; i < watched_fds_.size(); i++) {
- close(watched_fds_[i].fd);
- }
-}
-
-std::string SnapuserdServer::GetDaemonStatus() {
- std::string msg = "";
-
- if (IsTerminating())
- msg = "passive";
- else
- msg = "active";
-
- return msg;
-}
-
-void SnapuserdServer::Parsemsg(std::string const& msg, const char delim,
- std::vector<std::string>& out) {
- std::stringstream ss(msg);
- std::string s;
-
- while (std::getline(ss, s, delim)) {
- out.push_back(s);
- }
-}
-
-void SnapuserdServer::ShutdownThreads() {
- StopThreads();
- JoinAllThreads();
-}
-
-DmUserHandler::DmUserHandler(std::shared_ptr<Snapuserd> snapuserd)
- : snapuserd_(snapuserd), misc_name_(snapuserd_->GetMiscName()) {}
-
-bool SnapuserdServer::Sendmsg(android::base::borrowed_fd fd, const std::string& msg) {
- ssize_t ret = TEMP_FAILURE_RETRY(send(fd.get(), msg.data(), msg.size(), MSG_NOSIGNAL));
- if (ret < 0) {
- PLOG(ERROR) << "Snapuserd:server: send() failed";
- return false;
- }
-
- if (ret < msg.size()) {
- LOG(ERROR) << "Partial send; expected " << msg.size() << " bytes, sent " << ret;
- return false;
- }
- return true;
-}
-
-bool SnapuserdServer::Recv(android::base::borrowed_fd fd, std::string* data) {
- char msg[MAX_PACKET_SIZE];
- ssize_t rv = TEMP_FAILURE_RETRY(recv(fd.get(), msg, sizeof(msg), 0));
- if (rv < 0) {
- PLOG(ERROR) << "recv failed";
- return false;
- }
- *data = std::string(msg, rv);
- return true;
-}
-
-bool SnapuserdServer::Receivemsg(android::base::borrowed_fd fd, const std::string& str) {
- const char delim = ',';
-
- std::vector<std::string> out;
- Parsemsg(str, delim, out);
- DaemonOperations op = Resolveop(out[0]);
-
- switch (op) {
- case DaemonOperations::INIT: {
- // Message format:
- // init,<misc_name>,<cow_device_path>,<backing_device>
- //
- // Reads the metadata and send the number of sectors
- if (out.size() != 4) {
- LOG(ERROR) << "Malformed init message, " << out.size() << " parts";
- return Sendmsg(fd, "fail");
- }
-
- auto handler = AddHandler(out[1], out[2], out[3]);
- if (!handler) {
- return Sendmsg(fd, "fail");
- }
-
- auto retval = "success," + std::to_string(handler->snapuserd()->GetNumSectors());
- return Sendmsg(fd, retval);
- }
- case DaemonOperations::START: {
- // Message format:
- // start,<misc_name>
- //
- // Start the new thread which binds to dm-user misc device
- if (out.size() != 2) {
- LOG(ERROR) << "Malformed start message, " << out.size() << " parts";
- return Sendmsg(fd, "fail");
- }
-
- std::lock_guard<std::mutex> lock(lock_);
- auto iter = FindHandler(&lock, out[1]);
- if (iter == dm_users_.end()) {
- LOG(ERROR) << "Could not find handler: " << out[1];
- return Sendmsg(fd, "fail");
- }
- if (!(*iter)->snapuserd() || (*iter)->snapuserd()->IsAttached()) {
- LOG(ERROR) << "Tried to re-attach control device: " << out[1];
- return Sendmsg(fd, "fail");
- }
- if (!StartHandler(*iter)) {
- return Sendmsg(fd, "fail");
- }
- return Sendmsg(fd, "success");
- }
- case DaemonOperations::STOP: {
- // Message format: stop
- //
- // Stop all the threads gracefully and then shutdown the
- // main thread
- SetTerminating();
- ShutdownThreads();
- return true;
- }
- case DaemonOperations::QUERY: {
- // Message format: query
- //
- // As part of transition, Second stage daemon will be
- // created before terminating the first stage daemon. Hence,
- // for a brief period client may have to distiguish between
- // first stage daemon and second stage daemon.
- //
- // Second stage daemon is marked as active and hence will
- // be ready to receive control message.
- return Sendmsg(fd, GetDaemonStatus());
- }
- case DaemonOperations::DELETE: {
- // Message format:
- // delete,<misc_name>
- if (out.size() != 2) {
- LOG(ERROR) << "Malformed delete message, " << out.size() << " parts";
- return Sendmsg(fd, "fail");
- }
- if (!RemoveAndJoinHandler(out[1])) {
- return Sendmsg(fd, "fail");
- }
- return Sendmsg(fd, "success");
- }
- case DaemonOperations::DETACH: {
- 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");
- return false;
- }
- }
-}
-
-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";
- }
-
- handler->snapuserd()->CloseFds();
- handler->snapuserd()->CheckMergeCompletionStatus();
- handler->snapuserd()->UnmapBufferRegion();
-
- auto misc_name = handler->misc_name();
- LOG(INFO) << "Handler thread about to exit: " << misc_name;
-
- {
- std::lock_guard<std::mutex> lock(lock_);
- auto iter = FindHandler(&lock, handler->misc_name());
- if (iter == dm_users_.end()) {
- // RemoveAndJoinHandler() already removed us from the list, and is
- // now waiting on a join(), so just return. Additionally, release
- // all the resources held by snapuserd object which are shared
- // by worker threads. This should be done when the last reference
- // of "handler" is released; but we will explicitly release here
- // to make sure snapuserd object is freed as it is the biggest
- // consumer of memory in the daemon.
- handler->FreeResources();
- LOG(INFO) << "Exiting handler thread to allow for join: " << misc_name;
- return;
- }
-
- LOG(INFO) << "Exiting handler thread and freeing resources: " << misc_name;
-
- if (handler->snapuserd()->IsAttached()) {
- handler->thread().detach();
- }
-
- // Important: free resources within the lock. This ensures that if
- // WaitForDelete() is called, the handler is either in the list, or
- // it's not and its resources are guaranteed to be freed.
- handler->FreeResources();
- }
-}
-
-bool SnapuserdServer::Start(const std::string& socketname) {
- bool start_listening = true;
-
- sockfd_.reset(android_get_control_socket(socketname.c_str()));
- 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_, POLLIN);
- is_socket_present_ = true;
-
- // 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) {
- PLOG(ERROR) << "poll failed";
- return false;
- }
- if (!rv) {
- continue;
- }
-
- if (watched_fds_[0].revents) {
- AcceptClient();
- }
-
- auto iter = watched_fds_.begin() + 1;
- while (iter != watched_fds_.end()) {
- if (iter->revents && !HandleClient(iter->fd, iter->revents)) {
- close(iter->fd);
- iter = watched_fds_.erase(iter);
- } else {
- iter++;
- }
- }
- }
-
- JoinAllThreads();
- return true;
-}
-
-void SnapuserdServer::JoinAllThreads() {
- // Acquire the thread list within the lock.
- std::vector<std::shared_ptr<DmUserHandler>> dm_users;
- {
- std::lock_guard<std::mutex> guard(lock_);
- dm_users = std::move(dm_users_);
- }
-
- for (auto& client : dm_users) {
- auto& th = client->thread();
-
- if (th.joinable()) th.join();
- }
-}
-
-void SnapuserdServer::AddWatchedFd(android::base::borrowed_fd fd, int events) {
- struct pollfd p = {};
- p.fd = fd.get();
- p.events = events;
- watched_fds_.emplace_back(std::move(p));
-}
-
-void SnapuserdServer::AcceptClient() {
- int fd = TEMP_FAILURE_RETRY(accept4(sockfd_.get(), nullptr, nullptr, SOCK_CLOEXEC));
- if (fd < 0) {
- PLOG(ERROR) << "accept4 failed";
- return;
- }
-
- AddWatchedFd(fd, POLLIN);
-}
-
-bool SnapuserdServer::HandleClient(android::base::borrowed_fd fd, int revents) {
- if (revents & POLLHUP) {
- LOG(DEBUG) << "Snapuserd client disconnected";
- return false;
- }
-
- std::string str;
- if (!Recv(fd, &str)) {
- return false;
- }
- if (!Receivemsg(fd, str)) {
- LOG(ERROR) << "Encountered error handling client message, revents: " << revents;
- return false;
- }
- return true;
-}
-
-void SnapuserdServer::Interrupt() {
- // Force close the socket so poll() fails.
- sockfd_ = {};
- SetTerminating();
-}
-
-std::shared_ptr<DmUserHandler> SnapuserdServer::AddHandler(const std::string& misc_name,
- const std::string& cow_device_path,
- const std::string& backing_device) {
- auto snapuserd = std::make_shared<Snapuserd>(misc_name, cow_device_path, backing_device);
- if (!snapuserd->InitCowDevice()) {
- LOG(ERROR) << "Failed to initialize Snapuserd";
- return nullptr;
- }
-
- if (!snapuserd->InitializeWorkers()) {
- LOG(ERROR) << "Failed to initialize workers";
- return nullptr;
- }
-
- auto handler = std::make_shared<DmUserHandler>(snapuserd);
- {
- std::lock_guard<std::mutex> lock(lock_);
- if (FindHandler(&lock, misc_name) != dm_users_.end()) {
- LOG(ERROR) << "Handler already exists: " << misc_name;
- return nullptr;
- }
- dm_users_.push_back(handler);
- }
- return handler;
-}
-
-bool SnapuserdServer::StartHandler(const std::shared_ptr<DmUserHandler>& handler) {
- if (handler->snapuserd()->IsAttached()) {
- LOG(ERROR) << "Handler already attached";
- return false;
- }
-
- handler->snapuserd()->AttachControlDevice();
-
- handler->thread() = std::thread(std::bind(&SnapuserdServer::RunThread, this, handler));
- return true;
-}
-
-auto SnapuserdServer::FindHandler(std::lock_guard<std::mutex>* proof_of_lock,
- const std::string& misc_name) -> HandlerList::iterator {
- CHECK(proof_of_lock);
-
- for (auto iter = dm_users_.begin(); iter != dm_users_.end(); iter++) {
- if ((*iter)->misc_name() == misc_name) {
- return iter;
- }
- }
- return dm_users_.end();
-}
-
-bool SnapuserdServer::RemoveAndJoinHandler(const std::string& misc_name) {
- std::shared_ptr<DmUserHandler> handler;
- {
- std::lock_guard<std::mutex> lock(lock_);
-
- auto iter = FindHandler(&lock, misc_name);
- if (iter == dm_users_.end()) {
- // Client already deleted.
- return true;
- }
- handler = std::move(*iter);
- dm_users_.erase(iter);
- }
-
- auto& th = handler->thread();
- if (th.joinable()) {
- th.join();
- }
- return true;
-}
-
-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/dm-snapshot-merge/snapuserd_server.h b/fs_mgr/libsnapshot/snapuserd/dm-snapshot-merge/snapuserd_server.h
deleted file mode 100644
index 3b6ff15..0000000
--- a/fs_mgr/libsnapshot/snapuserd/dm-snapshot-merge/snapuserd_server.h
+++ /dev/null
@@ -1,149 +0,0 @@
-// Copyright (C) 2020 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#pragma once
-
-#include <poll.h>
-
-#include <cstdio>
-#include <cstring>
-#include <functional>
-#include <future>
-#include <iostream>
-#include <mutex>
-#include <sstream>
-#include <string>
-#include <thread>
-#include <vector>
-
-#include <android-base/unique_fd.h>
-#include "snapuserd.h"
-
-namespace android {
-namespace snapshot {
-
-static constexpr uint32_t MAX_PACKET_SIZE = 512;
-
-enum class DaemonOperations {
- INIT,
- START,
- QUERY,
- STOP,
- DELETE,
- DETACH,
- SUPPORTS,
- INVALID,
-};
-
-class DmUserHandler {
- public:
- explicit DmUserHandler(std::shared_ptr<Snapuserd> snapuserd);
-
- void FreeResources() {
- // Each worker thread holds a reference to snapuserd.
- // Clear them so that all the resources
- // held by snapuserd is released
- if (snapuserd_) {
- snapuserd_->FreeResources();
- snapuserd_ = nullptr;
- }
- }
- const std::shared_ptr<Snapuserd>& snapuserd() const { return snapuserd_; }
- std::thread& thread() { return thread_; }
-
- const std::string& misc_name() const { return misc_name_; }
-
- private:
- std::thread thread_;
- std::shared_ptr<Snapuserd> snapuserd_;
- std::string misc_name_;
-};
-
-class Stoppable {
- std::promise<void> exitSignal_;
- std::future<void> futureObj_;
-
- public:
- Stoppable() : futureObj_(exitSignal_.get_future()) {}
-
- virtual ~Stoppable() {}
-
- bool StopRequested() {
- // checks if value in future object is available
- if (futureObj_.wait_for(std::chrono::milliseconds(0)) == std::future_status::timeout) {
- return false;
- }
- return true;
- }
- // Request the thread to stop by setting value in promise object
- void StopThreads() { exitSignal_.set_value(); }
-};
-
-class SnapuserdServer : public Stoppable {
- 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, int events);
- void AcceptClient();
- bool HandleClient(android::base::borrowed_fd fd, int revents);
- bool Recv(android::base::borrowed_fd fd, std::string* data);
- bool Sendmsg(android::base::borrowed_fd fd, const std::string& msg);
- bool Receivemsg(android::base::borrowed_fd fd, const std::string& str);
-
- void ShutdownThreads();
- bool RemoveAndJoinHandler(const std::string& control_device);
- DaemonOperations Resolveop(std::string& input);
- std::string GetDaemonStatus();
- void Parsemsg(std::string const& msg, const char delim, std::vector<std::string>& out);
-
- bool IsTerminating() { return terminating_; }
-
- void RunThread(std::shared_ptr<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,
- const std::string& misc_name);
-
- public:
- SnapuserdServer() { terminating_ = false; }
- ~SnapuserdServer();
-
- 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,
- const std::string& backing_device);
- bool StartHandler(const std::shared_ptr<DmUserHandler>& handler);
-
- void SetTerminating() { terminating_ = true; }
- void ReceivedSocketSignal() { received_socket_signal_ = true; }
-};
-
-} // namespace snapshot
-} // namespace android
diff --git a/fs_mgr/libsnapshot/snapuserd/dm-snapshot-merge/snapuserd_worker.cpp b/fs_mgr/libsnapshot/snapuserd/dm-snapshot-merge/snapuserd_worker.cpp
deleted file mode 100644
index 1f5d568..0000000
--- a/fs_mgr/libsnapshot/snapuserd/dm-snapshot-merge/snapuserd_worker.cpp
+++ /dev/null
@@ -1,869 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "snapuserd.h"
-
-#include <csignal>
-#include <optional>
-#include <set>
-
-#include <snapuserd/snapuserd_client.h>
-
-namespace android {
-namespace snapshot {
-
-using namespace android;
-using namespace android::dm;
-using android::base::unique_fd;
-
-#define SNAP_LOG(level) LOG(level) << misc_name_ << ": "
-#define SNAP_PLOG(level) PLOG(level) << misc_name_ << ": "
-
-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) {
- cow_device_ = cow_device;
- backing_store_device_ = backing_device;
- control_device_ = control_device;
- misc_name_ = misc_name;
- snapuserd_ = snapuserd;
- exceptions_per_area_ = (CHUNK_SIZE << SECTOR_SHIFT) / sizeof(struct disk_exception);
-}
-
-bool WorkerThread::InitializeFds() {
- backing_store_fd_.reset(open(backing_store_device_.c_str(), O_RDONLY));
- if (backing_store_fd_ < 0) {
- SNAP_PLOG(ERROR) << "Open Failed: " << backing_store_device_;
- return false;
- }
-
- cow_fd_.reset(open(cow_device_.c_str(), O_RDWR));
- if (cow_fd_ < 0) {
- SNAP_PLOG(ERROR) << "Open Failed: " << cow_device_;
- return false;
- }
-
- ctrl_fd_.reset(open(control_device_.c_str(), O_RDWR));
- if (ctrl_fd_ < 0) {
- SNAP_PLOG(ERROR) << "Unable to open " << control_device_;
- return false;
- }
-
- return true;
-}
-
-bool WorkerThread::InitReader() {
- reader_ = snapuserd_->CloneReaderForWorker();
-
- if (!reader_->InitForMerge(std::move(cow_fd_))) {
- return false;
- }
- return true;
-}
-
-// Construct kernel COW header in memory
-// This header will be in sector 0. The IO
-// request will always be 4k. After constructing
-// the header, zero out the remaining block.
-void WorkerThread::ConstructKernelCowHeader() {
- void* buffer = bufsink_.GetPayloadBuffer(BLOCK_SZ);
-
- memset(buffer, 0, BLOCK_SZ);
-
- struct disk_header* dh = reinterpret_cast<struct disk_header*>(buffer);
-
- dh->magic = SNAP_MAGIC;
- dh->valid = SNAPSHOT_VALID;
- dh->version = SNAPSHOT_DISK_VERSION;
- dh->chunk_size = CHUNK_SIZE;
-}
-
-// Start the replace operation. This will read the
-// internal COW format and if the block is compressed,
-// it will be de-compressed.
-bool WorkerThread::ProcessReplaceOp(const CowOperation* cow_op) {
- void* buffer = bufsink_.GetPayloadBuffer(BLOCK_SZ);
- if (!buffer) {
- SNAP_LOG(ERROR) << "No space in buffer sink";
- return false;
- }
- ssize_t rv = reader_->ReadData(cow_op, buffer, BLOCK_SZ);
- if (rv != BLOCK_SZ) {
- SNAP_LOG(ERROR) << "ProcessReplaceOp failed for block " << cow_op->new_block
- << ", return = " << rv << ", COW operation = " << *cow_op;
- return false;
- }
- return true;
-}
-
-bool WorkerThread::ReadFromBaseDevice(const CowOperation* cow_op) {
- void* buffer = bufsink_.GetPayloadBuffer(BLOCK_SZ);
- if (buffer == nullptr) {
- SNAP_LOG(ERROR) << "ReadFromBaseDevice: Failed to get payload buffer";
- return false;
- }
- uint64_t offset;
- if (!reader_->GetSourceOffset(cow_op, &offset)) {
- SNAP_LOG(ERROR) << "ReadFromBaseDevice: Failed to get source offset";
- return false;
- }
- SNAP_LOG(DEBUG) << " ReadFromBaseDevice...: new-block: " << cow_op->new_block
- << " Source: " << offset;
- if (!android::base::ReadFullyAtOffset(backing_store_fd_, buffer, BLOCK_SZ, offset)) {
- SNAP_PLOG(ERROR) << "Copy op failed. Read from backing store: " << backing_store_device_
- << "at block :" << offset / BLOCK_SZ << " offset:" << offset % BLOCK_SZ;
- return false;
- }
-
- return true;
-}
-
-bool WorkerThread::GetReadAheadPopulatedBuffer(const CowOperation* cow_op) {
- void* buffer = bufsink_.GetPayloadBuffer(BLOCK_SZ);
- if (buffer == nullptr) {
- SNAP_LOG(ERROR) << "GetReadAheadPopulatedBuffer: Failed to get payload buffer";
- return false;
- }
-
- if (!snapuserd_->GetReadAheadPopulatedBuffer(cow_op->new_block, buffer)) {
- return false;
- }
-
- return true;
-}
-
-// Start the copy operation. This will read the backing
-// block device which is represented by cow_op->source.
-bool WorkerThread::ProcessCopyOp(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;
- }
- }
-
- return true;
-}
-
-bool WorkerThread::ProcessZeroOp() {
- // Zero out the entire block
- void* buffer = bufsink_.GetPayloadBuffer(BLOCK_SZ);
- if (buffer == nullptr) {
- SNAP_LOG(ERROR) << "ProcessZeroOp: Failed to get payload buffer";
- return false;
- }
-
- memset(buffer, 0, BLOCK_SZ);
- return true;
-}
-
-bool WorkerThread::ProcessCowOp(const CowOperation* cow_op) {
- if (cow_op == nullptr) {
- SNAP_LOG(ERROR) << "ProcessCowOp: Invalid cow_op";
- return false;
- }
-
- switch (cow_op->type()) {
- case kCowReplaceOp: {
- return ProcessReplaceOp(cow_op);
- }
-
- case kCowZeroOp: {
- return ProcessZeroOp();
- }
-
- case kCowCopyOp: {
- return ProcessCopyOp(cow_op);
- }
-
- default: {
- SNAP_LOG(ERROR) << "Unsupported operation-type found: "
- << static_cast<uint8_t>(cow_op->type());
- }
- }
- return false;
-}
-
-int WorkerThread::ReadUnalignedSector(
- sector_t sector, size_t size,
- std::vector<std::pair<sector_t, const CowOperation*>>::iterator& it) {
- size_t skip_sector_size = 0;
-
- SNAP_LOG(DEBUG) << "ReadUnalignedSector: sector " << sector << " size: " << size
- << " Aligned sector: " << it->first;
-
- if (!ProcessCowOp(it->second)) {
- SNAP_LOG(ERROR) << "ReadUnalignedSector: " << sector << " failed of size: " << size
- << " Aligned sector: " << it->first;
- return -1;
- }
-
- int num_sectors_skip = sector - it->first;
-
- if (num_sectors_skip > 0) {
- skip_sector_size = num_sectors_skip << SECTOR_SHIFT;
- char* buffer = reinterpret_cast<char*>(bufsink_.GetBufPtr());
- struct dm_user_message* msg = (struct dm_user_message*)(&(buffer[0]));
-
- if (skip_sector_size == BLOCK_SZ) {
- SNAP_LOG(ERROR) << "Invalid un-aligned IO request at sector: " << sector
- << " Base-sector: " << it->first;
- return -1;
- }
-
- memmove(msg->payload.buf, (char*)msg->payload.buf + skip_sector_size,
- (BLOCK_SZ - skip_sector_size));
- }
-
- bufsink_.ResetBufferOffset();
- return std::min(size, (BLOCK_SZ - skip_sector_size));
-}
-
-/*
- * Read the data for a given COW Operation.
- *
- * Kernel can issue IO at a sector granularity.
- * Hence, an IO may end up with reading partial
- * data from a COW operation or we may also
- * end up with interspersed request between
- * two COW operations.
- *
- */
-int WorkerThread::ReadData(sector_t sector, size_t size) {
- std::vector<std::pair<sector_t, const CowOperation*>>& chunk_vec = snapuserd_->GetChunkVec();
- std::vector<std::pair<sector_t, const CowOperation*>>::iterator it;
- /*
- * chunk_map stores COW operation at 4k granularity.
- * If the requested IO with the sector falls on the 4k
- * boundary, then we can read the COW op directly without
- * any issue.
- *
- * However, if the requested sector is not 4K aligned,
- * then we will have the find the nearest COW operation
- * and chop the 4K block to fetch the requested sector.
- */
- it = std::lower_bound(chunk_vec.begin(), chunk_vec.end(), std::make_pair(sector, nullptr),
- Snapuserd::compare);
-
- bool read_end_of_device = false;
- if (it == chunk_vec.end()) {
- // |-------|-------|-------|
- // 0 1 2 3
- //
- // Block 0 - op 1
- // Block 1 - op 2
- // Block 2 - op 3
- //
- // chunk_vec will have block 0, 1, 2 which maps to relavant COW ops.
- //
- // Each block is 4k bytes. Thus, the last block will span 8 sectors
- // ranging till block 3 (However, block 3 won't be in chunk_vec as
- // it doesn't have any mapping to COW ops. Now, if we get an I/O request for a sector
- // spanning between block 2 and block 3, we need to step back
- // and get hold of the last element.
- //
- // Additionally, dm-snapshot makes sure that I/O request beyond block 3
- // will not be routed to the daemon. Hence, it is safe to assume that
- // if a sector is not available in the chunk_vec, the I/O falls in the
- // end of region.
- it = std::prev(chunk_vec.end());
- read_end_of_device = true;
- }
-
- // We didn't find the required sector; hence find the previous sector
- // as lower_bound will gives us the value greater than
- // the requested sector
- if (it->first != sector) {
- if (it != chunk_vec.begin() && !read_end_of_device) {
- --it;
- }
-
- /*
- * If the IO is spanned between two COW operations,
- * split the IO into two parts:
- *
- * 1: Read the first part from the single COW op
- * 2: Read the second part from the next COW op.
- *
- * Ex: Let's say we have a 1024 Bytes IO request.
- *
- * 0 COW OP-1 4096 COW OP-2 8192
- * |******************|*******************|
- * |*****|*****|
- * 3584 4608
- * <- 1024B - >
- *
- * We have two COW operations which are 4k blocks.
- * The IO is requested for 1024 Bytes which are spanned
- * between two COW operations. We will split this IO
- * into two parts:
- *
- * 1: IO of size 512B from offset 3584 bytes (COW OP-1)
- * 2: IO of size 512B from offset 4096 bytes (COW OP-2)
- */
- return ReadUnalignedSector(sector, size, it);
- }
-
- int num_ops = DIV_ROUND_UP(size, BLOCK_SZ);
- sector_t read_sector = sector;
- while (num_ops) {
- // We have to make sure that the reads are
- // sequential; there shouldn't be a data
- // request merged with a metadata IO.
- if (it->first != read_sector) {
- SNAP_LOG(ERROR) << "Invalid IO request: read_sector: " << read_sector
- << " cow-op sector: " << it->first;
- return -1;
- } else if (!ProcessCowOp(it->second)) {
- return -1;
- }
- num_ops -= 1;
- read_sector += (BLOCK_SZ >> SECTOR_SHIFT);
-
- it++;
-
- if (it == chunk_vec.end() && num_ops) {
- SNAP_LOG(ERROR) << "Invalid IO request at sector " << sector
- << " COW ops completed; pending read-request: " << num_ops;
- return -1;
- }
- // Update the buffer offset
- bufsink_.UpdateBufferOffset(BLOCK_SZ);
- }
-
- // Reset the buffer offset
- bufsink_.ResetBufferOffset();
- return size;
-}
-
-/*
- * dm-snap does prefetch reads while reading disk-exceptions.
- * By default, prefetch value is set to 12; this means that
- * dm-snap will issue 12 areas wherein each area is a 4k page
- * of disk-exceptions.
- *
- * If during prefetch, if the chunk-id seen is beyond the
- * actual number of metadata page, fill the buffer with zero.
- * When dm-snap starts parsing the buffer, it will stop
- * reading metadata page once the buffer content is zero.
- */
-bool WorkerThread::ZerofillDiskExceptions(size_t read_size) {
- size_t size = exceptions_per_area_ * sizeof(struct disk_exception);
-
- if (read_size > size) {
- return false;
- }
-
- void* buffer = bufsink_.GetPayloadBuffer(size);
- if (buffer == nullptr) {
- SNAP_LOG(ERROR) << "ZerofillDiskExceptions: Failed to get payload buffer";
- return false;
- }
-
- memset(buffer, 0, size);
- return true;
-}
-
-/*
- * A disk exception is a simple mapping of old_chunk to new_chunk.
- * When dm-snapshot device is created, kernel requests these mapping.
- *
- * Each disk exception is of size 16 bytes. Thus a single 4k page can
- * have:
- *
- * exceptions_per_area_ = 4096/16 = 256. This entire 4k page
- * is considered a metadata page and it is represented by chunk ID.
- *
- * Convert the chunk ID to index into the vector which gives us
- * the metadata page.
- */
-bool WorkerThread::ReadDiskExceptions(chunk_t chunk, size_t read_size) {
- uint32_t stride = exceptions_per_area_ + 1;
- size_t size;
- const std::vector<std::unique_ptr<uint8_t[]>>& vec = snapuserd_->GetMetadataVec();
-
- // ChunkID to vector index
- lldiv_t divresult = lldiv(chunk, stride);
-
- if (divresult.quot < vec.size()) {
- size = exceptions_per_area_ * sizeof(struct disk_exception);
-
- if (read_size != size) {
- SNAP_LOG(ERROR) << "ReadDiskExceptions: read_size: " << read_size
- << " does not match with size: " << size;
- return false;
- }
-
- void* buffer = bufsink_.GetPayloadBuffer(size);
- if (buffer == nullptr) {
- SNAP_LOG(ERROR) << "ReadDiskExceptions: Failed to get payload buffer of size: " << size;
- return false;
- }
-
- memcpy(buffer, vec[divresult.quot].get(), size);
- } else {
- return ZerofillDiskExceptions(read_size);
- }
-
- return true;
-}
-
-loff_t WorkerThread::GetMergeStartOffset(void* merged_buffer, void* unmerged_buffer,
- int* unmerged_exceptions) {
- loff_t offset = 0;
- *unmerged_exceptions = 0;
-
- while (*unmerged_exceptions <= exceptions_per_area_) {
- struct disk_exception* merged_de =
- reinterpret_cast<struct disk_exception*>((char*)merged_buffer + offset);
- struct disk_exception* cow_de =
- reinterpret_cast<struct disk_exception*>((char*)unmerged_buffer + offset);
-
- // Unmerged op by the kernel
- if (merged_de->old_chunk != 0 || merged_de->new_chunk != 0) {
- if (!(merged_de->old_chunk == cow_de->old_chunk)) {
- SNAP_LOG(ERROR) << "GetMergeStartOffset: merged_de->old_chunk: "
- << merged_de->old_chunk
- << "cow_de->old_chunk: " << cow_de->old_chunk;
- return -1;
- }
-
- if (!(merged_de->new_chunk == cow_de->new_chunk)) {
- SNAP_LOG(ERROR) << "GetMergeStartOffset: merged_de->new_chunk: "
- << merged_de->new_chunk
- << "cow_de->new_chunk: " << cow_de->new_chunk;
- return -1;
- }
-
- offset += sizeof(struct disk_exception);
- *unmerged_exceptions += 1;
- continue;
- }
-
- break;
- }
-
- SNAP_LOG(DEBUG) << "Unmerged_Exceptions: " << *unmerged_exceptions << " Offset: " << offset;
- return offset;
-}
-
-int WorkerThread::GetNumberOfMergedOps(void* merged_buffer, void* unmerged_buffer, loff_t offset,
- 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();
- *ordered_op = false;
- std::vector<std::pair<sector_t, const CowOperation*>>& chunk_vec = snapuserd_->GetChunkVec();
-
- // Find the operations which are merged in this cycle.
- while ((unmerged_exceptions + merged_ops_cur_iter) < exceptions_per_area_) {
- struct disk_exception* merged_de =
- reinterpret_cast<struct disk_exception*>((char*)merged_buffer + offset);
- struct disk_exception* cow_de =
- reinterpret_cast<struct disk_exception*>((char*)unmerged_buffer + offset);
-
- if (!(merged_de->new_chunk == 0)) {
- SNAP_LOG(ERROR) << "GetNumberOfMergedOps: Invalid new-chunk: " << merged_de->new_chunk;
- return -1;
- }
-
- if (!(merged_de->old_chunk == 0)) {
- SNAP_LOG(ERROR) << "GetNumberOfMergedOps: Invalid old-chunk: " << merged_de->old_chunk;
- return -1;
- }
-
- if (cow_de->new_chunk != 0) {
- merged_ops_cur_iter += 1;
- offset += sizeof(struct disk_exception);
- auto it = std::lower_bound(chunk_vec.begin(), chunk_vec.end(),
- std::make_pair(ChunkToSector(cow_de->new_chunk), nullptr),
- Snapuserd::compare);
-
- if (!(it != chunk_vec.end())) {
- SNAP_LOG(ERROR) << "Sector not found: " << ChunkToSector(cow_de->new_chunk);
- return -1;
- }
-
- if (!(it->first == ChunkToSector(cow_de->new_chunk))) {
- SNAP_LOG(ERROR) << "Invalid sector: " << ChunkToSector(cow_de->new_chunk);
- return -1;
- }
- const CowOperation* cow_op = it->second;
-
- 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)
- << " Block: " << cow_op->new_block << " not found in read-ahead cache"
- << " Op: " << *cow_op;
- return -1;
- }
- // If this is a final block merged in the read-ahead buffer
- // region, notify the read-ahead thread to make forward
- // progress
- if (cow_op->new_block == snapuserd_->GetFinalBlockMerged()) {
- *commit = true;
- }
- }
-
- // zero out to indicate that operation is merged.
- cow_de->old_chunk = 0;
- cow_de->new_chunk = 0;
- } else if (cow_de->old_chunk == 0) {
- // Already merged op in previous iteration or
- // This could also represent a partially filled area.
- //
- // If the op was merged in previous cycle, we don't have
- // to count them.
- break;
- } else {
- SNAP_LOG(ERROR) << "Error in merge operation. Found invalid metadata: "
- << " merged_de-old-chunk: " << merged_de->old_chunk
- << " merged_de-new-chunk: " << merged_de->new_chunk
- << " cow_de-old-chunk: " << cow_de->old_chunk
- << " cow_de-new-chunk: " << cow_de->new_chunk
- << " unmerged_exceptions: " << unmerged_exceptions
- << " merged_ops_cur_iter: " << merged_ops_cur_iter
- << " offset: " << offset;
- return -1;
- }
- }
- return merged_ops_cur_iter;
-}
-
-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 ordered_op = false;
- bool commit = false;
-
- // ChunkID to vector index
- lldiv_t divresult = lldiv(chunk, stride);
-
- if (!(divresult.quot < vec.size())) {
- SNAP_LOG(ERROR) << "ProcessMergeComplete: Invalid chunk: " << chunk
- << " Metadata-Index: " << divresult.quot << " Area-size: " << vec.size();
- return false;
- }
-
- SNAP_LOG(DEBUG) << "ProcessMergeComplete: chunk: " << chunk
- << " Metadata-Index: " << divresult.quot;
-
- int unmerged_exceptions = 0;
- loff_t offset = GetMergeStartOffset(buffer, vec[divresult.quot].get(), &unmerged_exceptions);
-
- if (offset < 0) {
- SNAP_LOG(ERROR) << "GetMergeStartOffset failed: unmerged_exceptions: "
- << unmerged_exceptions;
- return false;
- }
-
- int merged_ops_cur_iter = GetNumberOfMergedOps(buffer, vec[divresult.quot].get(), offset,
- unmerged_exceptions, &ordered_op, &commit);
-
- // There should be at least one operation merged in this cycle
- if (!(merged_ops_cur_iter > 0)) {
- SNAP_LOG(ERROR) << "Merge operation failed: " << merged_ops_cur_iter;
- return false;
- }
-
- 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
- snapuserd_->StartReadAhead();
- }
- } else {
- // Non-copy ops and all ops in older COW format
- if (!snapuserd_->CommitMerge(merged_ops_cur_iter)) {
- SNAP_LOG(ERROR) << "CommitMerge failed...";
- return false;
- }
- }
-
- SNAP_LOG(DEBUG) << "Merge success: " << merged_ops_cur_iter << "chunk: " << chunk;
- return true;
-}
-
-// Read Header from dm-user misc device. This gives
-// us the sector number for which IO is issued by dm-snapshot device
-bool WorkerThread::ReadDmUserHeader() {
- if (!android::base::ReadFully(ctrl_fd_, bufsink_.GetBufPtr(), sizeof(struct dm_user_header))) {
- if (errno != ENOTBLK) {
- SNAP_PLOG(ERROR) << "Control-read failed";
- }
-
- return false;
- }
-
- return true;
-}
-
-// Send the payload/data back to dm-user misc device.
-bool WorkerThread::WriteDmUserPayload(size_t size, bool header_response) {
- size_t payload_size = size;
- void* buf = bufsink_.GetPayloadBufPtr();
- if (header_response) {
- payload_size += sizeof(struct dm_user_header);
- buf = bufsink_.GetBufPtr();
- }
-
- if (!android::base::WriteFully(ctrl_fd_, buf, payload_size)) {
- SNAP_PLOG(ERROR) << "Write to dm-user failed size: " << payload_size;
- return false;
- }
-
- return true;
-}
-
-bool WorkerThread::ReadDmUserPayload(void* buffer, size_t size) {
- if (!android::base::ReadFully(ctrl_fd_, buffer, size)) {
- SNAP_PLOG(ERROR) << "ReadDmUserPayload failed size: " << size;
- return false;
- }
-
- return true;
-}
-
-bool WorkerThread::DmuserWriteRequest() {
- struct dm_user_header* header = bufsink_.GetHeaderPtr();
-
- // device mapper has the capability to allow
- // targets to flush the cache when writes are completed. This
- // is controlled by each target by a flag "flush_supported".
- // This flag is set by dm-user. When flush is supported,
- // a number of zero-length bio's will be submitted to
- // the target for the purpose of flushing cache. It is the
- // responsibility of the target driver - which is dm-user in this
- // case, to remap these bio's to the underlying device. Since,
- // there is no underlying device for dm-user, this zero length
- // bio's gets routed to daemon.
- //
- // Flush operations are generated post merge by dm-snap by having
- // REQ_PREFLUSH flag set. Snapuser daemon doesn't have anything
- // to flush per se; hence, just respond back with a success message.
- if (header->sector == 0) {
- if (!(header->len == 0)) {
- SNAP_LOG(ERROR) << "Invalid header length received from sector 0: " << header->len;
- header->type = DM_USER_RESP_ERROR;
- } else {
- header->type = DM_USER_RESP_SUCCESS;
- }
-
- if (!WriteDmUserPayload(0, true)) {
- return false;
- }
- return true;
- }
-
- std::vector<std::pair<sector_t, const CowOperation*>>& chunk_vec = snapuserd_->GetChunkVec();
- size_t remaining_size = header->len;
- size_t read_size = std::min(PAYLOAD_SIZE, remaining_size);
-
- chunk_t chunk = SectorToChunk(header->sector);
- auto it = std::lower_bound(chunk_vec.begin(), chunk_vec.end(),
- std::make_pair(header->sector, nullptr), Snapuserd::compare);
-
- bool not_found = (it == chunk_vec.end() || it->first != header->sector);
-
- if (not_found) {
- void* buffer = bufsink_.GetPayloadBuffer(read_size);
- if (buffer == nullptr) {
- SNAP_LOG(ERROR) << "DmuserWriteRequest: Failed to get payload buffer of size: "
- << read_size;
- header->type = DM_USER_RESP_ERROR;
- } else {
- header->type = DM_USER_RESP_SUCCESS;
-
- if (!ReadDmUserPayload(buffer, read_size)) {
- SNAP_LOG(ERROR) << "ReadDmUserPayload failed for chunk id: " << chunk
- << "Sector: " << header->sector;
- header->type = DM_USER_RESP_ERROR;
- }
-
- if (header->type == DM_USER_RESP_SUCCESS && !ProcessMergeComplete(chunk, buffer)) {
- SNAP_LOG(ERROR) << "ProcessMergeComplete failed for chunk id: " << chunk
- << "Sector: " << header->sector;
- header->type = DM_USER_RESP_ERROR;
- }
- }
- } else {
- SNAP_LOG(ERROR) << "DmuserWriteRequest: Invalid sector received: header->sector";
- header->type = DM_USER_RESP_ERROR;
- }
-
- if (!WriteDmUserPayload(0, true)) {
- return false;
- }
-
- return true;
-}
-
-bool WorkerThread::DmuserReadRequest() {
- struct dm_user_header* header = bufsink_.GetHeaderPtr();
- size_t remaining_size = header->len;
- loff_t offset = 0;
- sector_t sector = header->sector;
- std::vector<std::pair<sector_t, const CowOperation*>>& chunk_vec = snapuserd_->GetChunkVec();
- bool header_response = true;
- do {
- size_t read_size = std::min(PAYLOAD_SIZE, remaining_size);
-
- int ret = read_size;
- header->type = DM_USER_RESP_SUCCESS;
- chunk_t chunk = SectorToChunk(header->sector);
-
- // Request to sector 0 is always for kernel
- // representation of COW header. This IO should be only
- // once during dm-snapshot device creation. We should
- // never see multiple IO requests. Additionally this IO
- // will always be a single 4k.
- if (header->sector == 0) {
- if (read_size == BLOCK_SZ) {
- ConstructKernelCowHeader();
- SNAP_LOG(DEBUG) << "Kernel header constructed";
- } else {
- SNAP_LOG(ERROR) << "Invalid read_size: " << read_size << " for sector 0";
- header->type = DM_USER_RESP_ERROR;
- }
- } else {
- auto it = std::lower_bound(chunk_vec.begin(), chunk_vec.end(),
- std::make_pair(header->sector, nullptr), Snapuserd::compare);
- bool not_found = (it == chunk_vec.end() || it->first != header->sector);
- if (!offset && (read_size == BLOCK_SZ) && not_found) {
- if (!ReadDiskExceptions(chunk, read_size)) {
- SNAP_LOG(ERROR) << "ReadDiskExceptions failed for chunk id: " << chunk
- << "Sector: " << header->sector;
- header->type = DM_USER_RESP_ERROR;
- } else {
- SNAP_LOG(DEBUG) << "ReadDiskExceptions success for chunk id: " << chunk
- << "Sector: " << header->sector;
- }
- } else {
- chunk_t num_sectors_read = (offset >> SECTOR_SHIFT);
-
- ret = ReadData(sector + num_sectors_read, read_size);
- if (ret < 0) {
- SNAP_LOG(ERROR) << "ReadData failed for chunk id: " << chunk
- << " Sector: " << (sector + num_sectors_read)
- << " size: " << read_size << " header-len: " << header->len;
- header->type = DM_USER_RESP_ERROR;
- } else {
- SNAP_LOG(DEBUG) << "ReadData success for chunk id: " << chunk
- << "Sector: " << header->sector;
- }
- }
- }
-
- // Just return the header if it is an error
- if (header->type == DM_USER_RESP_ERROR) {
- SNAP_LOG(ERROR) << "IO read request failed...";
- ret = 0;
- }
-
- if (!header_response) {
- CHECK(header->type == DM_USER_RESP_SUCCESS)
- << " failed for sector: " << sector << " header->len: " << header->len
- << " remaining_size: " << remaining_size;
- }
-
- // Daemon will not be terminated if there is any error. We will
- // just send the error back to dm-user.
- if (!WriteDmUserPayload(ret, header_response)) {
- return false;
- }
-
- if (header->type == DM_USER_RESP_ERROR) {
- break;
- }
-
- remaining_size -= ret;
- offset += ret;
- header_response = false;
- } while (remaining_size > 0);
-
- return true;
-}
-
-void WorkerThread::InitializeBufsink() {
- // Allocate the buffer which is used to communicate between
- // daemon and dm-user. The buffer comprises of header and a fixed payload.
- // If the dm-user requests a big IO, the IO will be broken into chunks
- // of PAYLOAD_SIZE.
- size_t buf_size = sizeof(struct dm_user_header) + PAYLOAD_SIZE;
- bufsink_.Initialize(buf_size);
-}
-
-bool WorkerThread::RunThread() {
- InitializeBufsink();
-
- if (!InitializeFds()) {
- return false;
- }
-
- if (!InitReader()) {
- return false;
- }
-
- // Start serving IO
- while (true) {
- if (!ProcessIORequest()) {
- break;
- }
- }
-
- CloseFds();
- reader_->CloseCowFd();
-
- return true;
-}
-
-bool WorkerThread::ProcessIORequest() {
- struct dm_user_header* header = bufsink_.GetHeaderPtr();
-
- if (!ReadDmUserHeader()) {
- return false;
- }
-
- SNAP_LOG(DEBUG) << "Daemon: msg->seq: " << std::dec << header->seq;
- SNAP_LOG(DEBUG) << "Daemon: msg->len: " << std::dec << header->len;
- SNAP_LOG(DEBUG) << "Daemon: msg->sector: " << std::dec << header->sector;
- SNAP_LOG(DEBUG) << "Daemon: msg->type: " << std::dec << header->type;
- SNAP_LOG(DEBUG) << "Daemon: msg->flags: " << std::dec << header->flags;
-
- switch (header->type) {
- case DM_USER_REQ_MAP_READ: {
- if (!DmuserReadRequest()) {
- return false;
- }
- break;
- }
-
- case DM_USER_REQ_MAP_WRITE: {
- if (!DmuserWriteRequest()) {
- return false;
- }
- break;
- }
- }
-
- return true;
-}
-
-} // namespace snapshot
-} // namespace android
diff --git a/fs_mgr/libsnapshot/snapuserd/snapuserd_daemon.cpp b/fs_mgr/libsnapshot/snapuserd/snapuserd_daemon.cpp
index 36dad33..0ebe543 100644
--- a/fs_mgr/libsnapshot/snapuserd/snapuserd_daemon.cpp
+++ b/fs_mgr/libsnapshot/snapuserd/snapuserd_daemon.cpp
@@ -38,20 +38,20 @@
const std::string vendor_release =
android::base::GetProperty("ro.vendor.build.version.release_or_codename", UNKNOWN);
- // No user-space snapshots if vendor partition is on Android 12
+ // If the vendor is on Android S, install process will forcefully take the
+ // userspace snapshots path.
+ //
+ // We will not reach here post OTA reboot as the binary will be from vendor
+ // ramdisk which is on Android S.
if (vendor_release.find("12") != std::string::npos) {
- LOG(INFO) << "Userspace snapshots disabled as vendor partition is on Android: "
+ LOG(INFO) << "Userspace snapshots enabled as vendor partition is on Android: "
<< vendor_release;
- return false;
+ return true;
}
return android::base::GetBoolProperty("ro.virtual_ab.userspace.snapshots.enabled", false);
}
-bool Daemon::IsDmSnapshotTestingEnabled() {
- return android::base::GetBoolProperty("snapuserd.test.dm.snapshots", false);
-}
-
bool Daemon::StartDaemon(int argc, char** argv) {
int arg_start = gflags::ParseCommandLineFlags(&argc, &argv, true);
@@ -65,16 +65,16 @@
// stage init and hence use the command line flags to get the information.
bool user_snapshots = FLAGS_user_snapshot;
if (!user_snapshots) {
- user_snapshots = (!IsDmSnapshotTestingEnabled() && IsUserspaceSnapshotsEnabled());
+ user_snapshots = IsUserspaceSnapshotsEnabled();
}
if (user_snapshots) {
LOG(INFO) << "Starting daemon for user-space snapshots.....";
return StartServerForUserspaceSnapshots(arg_start, argc, argv);
} else {
- LOG(INFO) << "Starting daemon for dm-snapshots.....";
- return StartServerForDmSnapshot(arg_start, argc, argv);
+ LOG(ERROR) << "Userspace snapshots not enabled. No support for legacy snapshots";
}
+ return false;
}
bool Daemon::StartServerForUserspaceSnapshots(int arg_start, int argc, char** argv) {
@@ -130,48 +130,6 @@
return user_server_.WaitForSocket();
}
-bool Daemon::StartServerForDmSnapshot(int arg_start, int argc, char** argv) {
- 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) {
- if (!server_.Start(FLAGS_socket)) {
- return false;
- }
- return server_.Run();
- }
-
- for (int i = arg_start; i < argc; i++) {
- auto parts = android::base::Split(argv[i], ",");
- if (parts.size() != 3) {
- LOG(ERROR) << "Malformed message, expected three sub-arguments.";
- return false;
- }
- auto handler = server_.AddHandler(parts[0], parts[1], parts[2]);
- if (!handler || !server_.StartHandler(handler)) {
- return false;
- }
- }
-
- // Skip the accept() call to avoid spurious log spam. The server will still
- // run until all handlers have completed.
- return server_.WaitForSocket();
-}
-
void Daemon::MaskAllSignalsExceptIntAndTerm() {
sigset_t signal_mask;
sigfillset(&signal_mask);
@@ -198,16 +156,12 @@
// and verify it through a temp variable.
if (user_server_.IsServerRunning()) {
user_server_.Interrupt();
- } else {
- server_.Interrupt();
}
}
void Daemon::ReceivedSocketSignal() {
if (user_server_.IsServerRunning()) {
user_server_.ReceivedSocketSignal();
- } else {
- server_.ReceivedSocketSignal();
}
}
diff --git a/fs_mgr/libsnapshot/snapuserd/snapuserd_daemon.h b/fs_mgr/libsnapshot/snapuserd/snapuserd_daemon.h
index cf3b917..303e394 100644
--- a/fs_mgr/libsnapshot/snapuserd/snapuserd_daemon.h
+++ b/fs_mgr/libsnapshot/snapuserd/snapuserd_daemon.h
@@ -19,7 +19,6 @@
#include <string>
#include <vector>
-#include "dm-snapshot-merge/snapuserd_server.h"
#include "user-space-merge/snapuserd_server.h"
namespace android {
@@ -36,12 +35,10 @@
return instance;
}
- bool StartServerForDmSnapshot(int arg_start, int argc, char** argv);
bool StartServerForUserspaceSnapshots(int arg_start, int argc, char** argv);
void Interrupt();
void ReceivedSocketSignal();
bool IsUserspaceSnapshotsEnabled();
- bool IsDmSnapshotTestingEnabled();
bool StartDaemon(int argc, char** argv);
private:
@@ -51,7 +48,6 @@
Daemon(Daemon const&) = delete;
void operator=(Daemon const&) = delete;
- SnapuserdServer server_;
UserSnapshotServer user_server_;
void MaskAllSignalsExceptIntAndTerm();
void MaskAllSignals();
diff --git a/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_test.cpp b/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_test.cpp
index 76b44b4..d05df82 100644
--- a/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_test.cpp
+++ b/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_test.cpp
@@ -126,7 +126,8 @@
}
std::unique_ptr<ICowWriter> SnapuserdTestBase::CreateCowDeviceInternal() {
- cow_system_ = std::make_unique<TemporaryFile>();
+ std::string path = android::base::GetExecutableDirectory();
+ cow_system_ = std::make_unique<TemporaryFile>(path);
CowOptions options;
options.compression = "gz";
@@ -147,7 +148,8 @@
options.batch_write = true;
options.compression_factor = params.block_size;
- cow_system_ = std::make_unique<TemporaryFile>();
+ std::string path = android::base::GetExecutableDirectory();
+ cow_system_ = std::make_unique<TemporaryFile>(path);
unique_fd fd(cow_system_->fd);
cow_system_->fd = -1;
@@ -989,6 +991,7 @@
void SnapuserdVariableBlockSizeTest::CreateV3CowDeviceForVariableBlockSize() {
auto writer = CreateV3Cow();
+ ASSERT_NE(writer, nullptr);
size_t total_data_to_write = size_;
size_t total_blocks_to_write = total_data_to_write / BLOCK_SZ;
@@ -1337,6 +1340,7 @@
void HandlerTestV3::SetUpV3Cow() {
auto writer = CreateV3Cow();
+ ASSERT_NE(writer, nullptr);
size_t total_data_to_write = size_;
size_t total_blocks_to_write = total_data_to_write / BLOCK_SZ;
diff --git a/fs_mgr/libsnapshot/utility.cpp b/fs_mgr/libsnapshot/utility.cpp
index 1ffa89c..fe2d95c 100644
--- a/fs_mgr/libsnapshot/utility.cpp
+++ b/fs_mgr/libsnapshot/utility.cpp
@@ -230,11 +230,7 @@
return fetcher->GetBoolProperty("ro.virtual_ab.userspace.snapshots.enabled", false);
}
-bool CanUseUserspaceSnapshots() {
- if (!GetUserspaceSnapshotsEnabledProperty()) {
- return false;
- }
-
+bool IsVendorFromAndroid12() {
auto fetcher = IPropertyFetcher::GetInstance();
const std::string UNKNOWN = "unknown";
@@ -243,8 +239,15 @@
// No user-space snapshots if vendor partition is on Android 12
if (vendor_release.find("12") != std::string::npos) {
- LOG(INFO) << "Userspace snapshots disabled as vendor partition is on Android: "
- << vendor_release;
+ return true;
+ }
+
+ return false;
+}
+
+bool CanUseUserspaceSnapshots() {
+ if (!GetUserspaceSnapshotsEnabledProperty()) {
+ LOG(INFO) << "Virtual A/B - Userspace snapshots disabled";
return false;
}
diff --git a/fs_mgr/libsnapshot/utility.h b/fs_mgr/libsnapshot/utility.h
index 370f3c4..f956a05 100644
--- a/fs_mgr/libsnapshot/utility.h
+++ b/fs_mgr/libsnapshot/utility.h
@@ -136,6 +136,7 @@
bool CanUseUserspaceSnapshots();
bool IsDmSnapshotTestingEnabled();
+bool IsVendorFromAndroid12();
// Swap the suffix of a partition name.
std::string GetOtherPartitionName(const std::string& name);
diff --git a/fs_mgr/libsnapshot/vts_ota_config_test.cpp b/fs_mgr/libsnapshot/vts_ota_config_test.cpp
index d387eb3..b5618c4 100755
--- a/fs_mgr/libsnapshot/vts_ota_config_test.cpp
+++ b/fs_mgr/libsnapshot/vts_ota_config_test.cpp
@@ -21,6 +21,7 @@
return android::base::GetIntProperty("ro.vendor.api_level", -1);
}
+// @VsrTest = 3.7.6
TEST(VAB, Enabled) {
if (!android::base::GetBoolProperty("ro.build.ab_update", false) && (GetVsrLevel() < __ANDROID_API_T__)) {
GTEST_SKIP();
diff --git a/fs_mgr/tests/adb-remount-test.sh b/fs_mgr/tests/adb-remount-test.sh
index 7ac7a16..526c761 100755
--- a/fs_mgr/tests/adb-remount-test.sh
+++ b/fs_mgr/tests/adb-remount-test.sh
@@ -1081,7 +1081,9 @@
LOG RUN "Testing adb disable-verity -R"
T=$(adb_date)
-adb_su disable-verity -R >&2 ||
+adb_su disable-verity -R >&2
+err=${?}
+[[ ${err} -eq 0 || ${err} -eq 255 ]] ||
die -t "${T}" "disable-verity -R failed"
sleep 2
adb_wait "${ADB_WAIT}" ||
@@ -1192,7 +1194,9 @@
LOG RUN "Testing adb remount -R"
T=$(adb_date)
-adb_su remount -R </dev/null >&2 ||
+adb_su remount -R </dev/null >&2
+err=${?}
+[[ ${err} -eq 0 || ${err} -eq 255 ]] ||
die -t "${T}" "adb remount -R failed"
sleep 2
adb_wait "${ADB_WAIT}" ||
diff --git a/fs_mgr/tests/vts_fs_test.cpp b/fs_mgr/tests/vts_fs_test.cpp
index 9503072..f55cadb 100644
--- a/fs_mgr/tests/vts_fs_test.cpp
+++ b/fs_mgr/tests/vts_fs_test.cpp
@@ -74,6 +74,7 @@
ASSERT_EQ(access("/sys/fs/erofs", F_OK), 0);
}
+// @VsrTest = 3.7.10
TEST(fs, PartitionTypes) {
// Requirements only apply to Android 13+, 5.10+ devices.
int vsr_level = GetVsrLevel();
diff --git a/fs_mgr/tools/dmctl.cpp b/fs_mgr/tools/dmctl.cpp
index 7273087..00f8038 100644
--- a/fs_mgr/tools/dmctl.cpp
+++ b/fs_mgr/tools/dmctl.cpp
@@ -50,8 +50,10 @@
std::cerr << " create <dm-name> [-ro] <targets...>" << std::endl;
std::cerr << " delete <dm-name>" << std::endl;
std::cerr << " list <devices | targets> [-v]" << std::endl;
+ std::cerr << " message <dm-name> <sector> <message>" << std::endl;
std::cerr << " getpath <dm-name>" << std::endl;
std::cerr << " getuuid <dm-name>" << std::endl;
+ std::cerr << " ima <dm-name>" << std::endl;
std::cerr << " info <dm-name>" << std::endl;
std::cerr << " replace <dm-name> <targets...>" << std::endl;
std::cerr << " status <dm-name>" << std::endl;
@@ -115,6 +117,21 @@
std::string block_device = NextArg();
return std::make_unique<DmTargetAndroidVerity>(start_sector, num_sectors, keyid,
block_device);
+ } else if (target_type == "striped") {
+ if (!HasArgs(3)) {
+ std::cerr << "Expected \"striped\" <block_device0> <block_device1> <chunksize>"
+ << std::endl;
+ return nullptr;
+ }
+ std::string block_device0 = NextArg();
+ std::string block_device1 = NextArg();
+ uint64_t chunk_size;
+ if (!android::base::ParseUint(NextArg(), &chunk_size)) {
+ std::cerr << "Expected start sector, got: " << PreviousArg() << std::endl;
+ return nullptr;
+ }
+ return std::make_unique<DmTargetStripe>(start_sector, num_sectors, chunk_size,
+ block_device0, block_device1);
} else if (target_type == "bow") {
if (!HasArgs(1)) {
std::cerr << "Expected \"bow\" <block_device>" << std::endl;
@@ -187,6 +204,46 @@
return std::make_unique<DmTargetUser>(start_sector, num_sectors, control_device);
} else if (target_type == "error") {
return std::make_unique<DmTargetError>(start_sector, num_sectors);
+ } else if (target_type == "thin-pool") {
+ if (!HasArgs(4)) {
+ std::cerr << "Expected \"thin-pool\" <metadata dev> <data dev> <data block size> "
+ "<low water mark> <feature args>"
+ << std::endl;
+ return nullptr;
+ }
+
+ std::string metadata_dev = NextArg();
+ std::string data_dev = NextArg();
+ std::string data_block_size_str = NextArg();
+ std::string low_water_mark_str = NextArg();
+
+ uint64_t data_block_size;
+ if (!android::base::ParseUint(data_block_size_str, &data_block_size)) {
+ std::cerr << "Data block size must be an unsigned integer.\n";
+ return nullptr;
+ }
+ uint64_t low_water_mark;
+ if (!android::base::ParseUint(low_water_mark_str, &low_water_mark)) {
+ std::cerr << "Low water mark must be an unsigned integer.\n";
+ return nullptr;
+ }
+ return std::make_unique<DmTargetThinPool>(start_sector, num_sectors, metadata_dev,
+ data_dev, data_block_size, low_water_mark);
+ } else if (target_type == "thin") {
+ if (!HasArgs(2)) {
+ std::cerr << "Expected \"thin\" <pool dev> <dev id>" << std::endl;
+ return nullptr;
+ }
+
+ std::string pool_dev = NextArg();
+ std::string dev_id_str = NextArg();
+
+ uint64_t dev_id;
+ if (!android::base::ParseUint(dev_id_str, &dev_id)) {
+ std::cerr << "Dev id must be an unsigned integer.\n";
+ return nullptr;
+ }
+ return std::make_unique<DmTargetThin>(start_sector, num_sectors, pool_dev, dev_id);
} else {
std::cerr << "Unrecognized target type: " << target_type << std::endl;
return nullptr;
@@ -401,6 +458,24 @@
return -EINVAL;
}
+static int DmMessageCmdHandler(int argc, char** argv) {
+ if (argc != 3) {
+ std::cerr << "Usage: dmctl message <name> <sector> <message>" << std::endl;
+ return -EINVAL;
+ }
+ uint64_t sector;
+ if (!android::base::ParseUint(argv[1], §or)) {
+ std::cerr << "Invalid argument for sector: " << argv[1] << std::endl;
+ return -EINVAL;
+ }
+ DeviceMapper& dm = DeviceMapper::Instance();
+ if (!dm.SendMessage(argv[0], sector, argv[2])) {
+ std::cerr << "Could not send message to " << argv[0] << std::endl;
+ return -EINVAL;
+ }
+ return 0;
+}
+
static int HelpCmdHandler(int /* argc */, char** /* argv */) {
Usage();
return 0;
@@ -493,7 +568,14 @@
<< std::endl;
return -EINVAL;
}
+ } else if (mode == "ima") {
+ if (!dm.GetTableStatusIma(argv[0], &table)) {
+ std::cerr << "Could not query table status of device \"" << argv[0] << "\"."
+ << std::endl;
+ return -EINVAL;
+ }
}
+
std::cout << "Targets in the device-mapper table for " << argv[0] << ":" << std::endl;
for (const auto& target : table) {
std::cout << target.spec.sector_start << "-"
@@ -515,6 +597,10 @@
return DumpTable("status", argc, argv);
}
+static int ImaCmdHandler(int argc, char** argv) {
+ return DumpTable("ima", argc, argv);
+}
+
static int ResumeCmdHandler(int argc, char** argv) {
if (argc != 1) {
std::cerr << "Invalid arguments, see \'dmctl help\'" << std::endl;
@@ -549,12 +635,14 @@
{"delete", DmDeleteCmdHandler},
{"replace", DmReplaceCmdHandler},
{"list", DmListCmdHandler},
+ {"message", DmMessageCmdHandler},
{"help", HelpCmdHandler},
{"getpath", GetPathCmdHandler},
{"getuuid", GetUuidCmdHandler},
{"info", InfoCmdHandler},
{"table", TableCmdHandler},
{"status", StatusCmdHandler},
+ {"ima", ImaCmdHandler},
{"resume", ResumeCmdHandler},
{"suspend", SuspendCmdHandler},
// clang-format on
diff --git a/init/Android.bp b/init/Android.bp
index 2d16f60..ff82f7f 100644
--- a/init/Android.bp
+++ b/init/Android.bp
@@ -162,6 +162,7 @@
},
static_libs: [
"libavb",
+ "libavf_cc_flags",
"libbootloader_message",
"libc++fs",
"libcgrouprc_format",
@@ -254,7 +255,10 @@
cc_library_static {
name: "libinit.microdroid",
- defaults: ["libinit_defaults"],
+ defaults: [
+ "avf_build_flags_cc",
+ "libinit_defaults",
+ ],
cflags: ["-DMICRODROID=1"],
}
@@ -309,11 +313,15 @@
name: "init_second_stage",
defaults: ["init_second_stage_defaults"],
static_libs: ["libinit"],
+ visibility: ["//visibility:any_system_partition"],
}
cc_binary {
name: "init_second_stage.microdroid",
- defaults: ["init_second_stage_defaults"],
+ defaults: [
+ "avf_build_flags_cc",
+ "init_second_stage_defaults",
+ ],
static_libs: ["libinit.microdroid"],
cflags: ["-DMICRODROID=1"],
installable: false,
@@ -359,6 +367,7 @@
static_libs: [
"libc++fs",
"libfs_avb",
+ "libavf_cc_flags",
"libfs_mgr",
"libfec",
"libfec_rs",
@@ -459,7 +468,10 @@
cc_binary {
name: "init_first_stage.microdroid",
- defaults: ["init_first_stage_defaults"],
+ defaults: [
+ "avf_build_flags_cc",
+ "init_first_stage_defaults",
+ ],
cflags: ["-DMICRODROID=1"],
installable: false,
}
@@ -597,7 +609,6 @@
},
generated_headers: [
"generated_stub_builtin_function_map",
- "generated_android_ids",
],
target: {
android: {
@@ -613,6 +624,9 @@
name: "host_init_verifier",
defaults: ["init_host_defaults"],
srcs: ["host_init_verifier.cpp"] + init_common_sources + init_host_sources,
+ generated_headers: [
+ "generated_android_ids",
+ ],
}
cc_library_host_static {
diff --git a/init/block_dev_initializer.cpp b/init/block_dev_initializer.cpp
index 05e00ed..a686d05 100644
--- a/init/block_dev_initializer.cpp
+++ b/init/block_dev_initializer.cpp
@@ -132,11 +132,19 @@
bool BlockDevInitializer::InitDmDevice(const std::string& device) {
const std::string device_name(basename(device.c_str()));
const std::string syspath = "/sys/block/" + device_name;
+ return InitDevice(syspath, device_name);
+}
+
+bool BlockDevInitializer::InitPlatformDevice(const std::string& dev_name) {
+ return InitDevice("/sys/devices/platform", dev_name);
+}
+
+bool BlockDevInitializer::InitDevice(const std::string& syspath, const std::string& device_name) {
bool found = false;
- auto uevent_callback = [&device_name, &device, this, &found](const Uevent& uevent) {
+ auto uevent_callback = [&device_name, this, &found](const Uevent& uevent) {
if (uevent.device_name == device_name) {
- LOG(VERBOSE) << "Creating device-mapper device : " << device;
+ LOG(VERBOSE) << "Creating device : " << device_name;
device_handler_->HandleUevent(uevent);
found = true;
return ListenerAction::kStop;
@@ -146,13 +154,13 @@
uevent_listener_.RegenerateUeventsForPath(syspath, uevent_callback);
if (!found) {
- LOG(INFO) << "dm device '" << device << "' not found in /sys, waiting for its uevent";
+ LOG(INFO) << "device '" << device_name << "' not found in /sys, waiting for its uevent";
Timer t;
uevent_listener_.Poll(uevent_callback, 10s);
- LOG(INFO) << "wait for dm device '" << device << "' returned after " << t;
+ LOG(INFO) << "wait for device '" << device_name << "' returned after " << t;
}
if (!found) {
- LOG(ERROR) << "dm device '" << device << "' not found after polling timeout";
+ LOG(ERROR) << "device '" << device_name << "' not found after polling timeout";
return false;
}
return true;
diff --git a/init/block_dev_initializer.h b/init/block_dev_initializer.h
index ec39ce0..d5b1f60 100644
--- a/init/block_dev_initializer.h
+++ b/init/block_dev_initializer.h
@@ -24,6 +24,7 @@
namespace android {
namespace init {
+// TODO: should this be renamed to FirstStageDevInitialize?
class BlockDevInitializer final {
public:
BlockDevInitializer();
@@ -32,11 +33,13 @@
bool InitDmUser(const std::string& name);
bool InitDevices(std::set<std::string> devices);
bool InitDmDevice(const std::string& device);
+ bool InitPlatformDevice(const std::string& device);
private:
ListenerAction HandleUevent(const Uevent& uevent, std::set<std::string>* devices);
bool InitMiscDevice(const std::string& name);
+ bool InitDevice(const std::string& syspath, const std::string& device);
std::unique_ptr<DeviceHandler> device_handler_;
UeventListener uevent_listener_;
diff --git a/init/first_stage_console.cpp b/init/first_stage_console.cpp
index 0076764..f6f9329 100644
--- a/init/first_stage_console.cpp
+++ b/init/first_stage_console.cpp
@@ -16,6 +16,7 @@
#include "first_stage_console.h"
+#include <spawn.h>
#include <stdio.h>
#include <sys/stat.h>
#include <sys/sysmacros.h>
@@ -65,20 +66,20 @@
return true;
}
-static void RunScript() {
- LOG(INFO) << "Attempting to run /first_stage.sh...";
- pid_t pid = fork();
- if (pid != 0) {
- int status;
- waitpid(pid, &status, 0);
- LOG(INFO) << "/first_stage.sh exited with status " << status;
- return;
- }
- const char* path = "/system/bin/sh";
- const char* args[] = {path, "/first_stage.sh", nullptr};
- int rv = execv(path, const_cast<char**>(args));
- LOG(ERROR) << "unable to execv /first_stage.sh, returned " << rv << " errno " << errno;
- _exit(127);
+static pid_t SpawnImage(const char* file) {
+ const char* argv[] = {file, NULL};
+ const char* envp[] = {NULL};
+
+ char* const* argvp = const_cast<char* const*>(argv);
+ char* const* envpp = const_cast<char* const*>(envp);
+
+ pid_t pid;
+ errno = posix_spawn(&pid, argv[0], NULL, NULL, argvp, envpp);
+ if (!errno) return pid;
+
+ PLOG(ERROR) << "Failed to spawn '" << file << "'";
+
+ return (pid_t)0;
}
namespace android {
@@ -94,19 +95,21 @@
sigaction(SIGCHLD, &chld_act, nullptr);
pid_t pid = fork();
if (pid != 0) {
- int status;
- waitpid(pid, &status, 0);
- LOG(ERROR) << "console shell exited with status " << status;
+ wait(NULL);
+ LOG(ERROR) << "console shell exited";
return;
}
if (console) console = SetupConsole();
- RunScript();
+
+ LOG(INFO) << "Attempting to run /first_stage.sh...";
+ if (SpawnImage("/first_stage.sh")) {
+ wait(NULL);
+ LOG(INFO) << "/first_stage.sh exited";
+ }
+
if (console) {
- const char* path = "/system/bin/sh";
- const char* args[] = {path, nullptr};
- int rv = execv(path, const_cast<char**>(args));
- LOG(ERROR) << "unable to execv, returned " << rv << " errno " << errno;
+ if (SpawnImage("/system/bin/sh")) wait(NULL);
}
_exit(127);
}
diff --git a/init/first_stage_init.cpp b/init/first_stage_init.cpp
index c4d0f75..bfe636b 100644
--- a/init/first_stage_init.cpp
+++ b/init/first_stage_init.cpp
@@ -37,6 +37,7 @@
#include <android-base/file.h>
#include <android-base/logging.h>
#include <android-base/stringprintf.h>
+#include <android/avf_cc_flags.h>
#include <modprobe/modprobe.h>
#include <private/android_filesystem_config.h>
@@ -385,7 +386,12 @@
// /second_stage_resources is used to preserve files from first to second
// stage init
CHECKCALL(mount("tmpfs", kSecondStageRes, "tmpfs", MS_NOEXEC | MS_NOSUID | MS_NODEV,
- "mode=0755,uid=0,gid=0"))
+ "mode=0755,uid=0,gid=0"));
+
+ if (IsMicrodroid() && android::virtualization::IsOpenDiceChangesFlagEnabled()) {
+ CHECKCALL(mount("tmpfs", "/microdroid_resources", "tmpfs", MS_NOEXEC | MS_NOSUID | MS_NODEV,
+ "mode=0750,uid=0,gid=0"));
+ }
#undef CHECKCALL
SetStdioToDevNull(argv);
@@ -402,18 +408,6 @@
LOG(INFO) << "init first stage started!";
- // We only allow /vendor partition in debuggable Microdrod until it is verified during boot.
- // TODO(b/285855436): remove this check.
- if (IsMicrodroid()) {
- bool mount_vendor =
- cmdline.find("androidboot.microdroid.mount_vendor=1") != std::string::npos;
- bool debuggable =
- bootconfig.find("androidboot.microdroid.debuggable = \"1\"") != std::string::npos;
- if (mount_vendor && !debuggable) {
- LOG(FATAL) << "Attempted to mount /vendor partition for non-debuggable Microdroid VM";
- }
- }
-
auto old_root_dir = std::unique_ptr<DIR, decltype(&closedir)>{opendir("/"), closedir};
if (!old_root_dir) {
PLOG(ERROR) << "Could not opendir(\"/\"), not freeing ramdisk";
diff --git a/init/first_stage_mount.cpp b/init/first_stage_mount.cpp
index c0b9281..ae216c6 100644
--- a/init/first_stage_mount.cpp
+++ b/init/first_stage_mount.cpp
@@ -16,6 +16,7 @@
#include "first_stage_mount.h"
+#include <signal.h>
#include <stdlib.h>
#include <sys/mount.h>
#include <unistd.h>
@@ -33,6 +34,7 @@
#include <android-base/logging.h>
#include <android-base/stringprintf.h>
#include <android-base/strings.h>
+#include <android/avf_cc_flags.h>
#include <fs_avb/fs_avb.h>
#include <fs_mgr.h>
#include <fs_mgr_dm_linear.h>
@@ -272,6 +274,11 @@
return true;
}
+// TODO: should this be in a library in packages/modules/Virtualization first_stage_init links?
+static bool IsMicrodroidStrictBoot() {
+ return access("/proc/device-tree/chosen/avf,strict-boot", F_OK) == 0;
+}
+
bool FirstStageMountVBootV2::InitDevices() {
std::set<std::string> devices;
GetSuperDeviceName(&devices);
@@ -283,6 +290,14 @@
return false;
}
+ if (IsMicrodroid() && android::virtualization::IsOpenDiceChangesFlagEnabled()) {
+ if (IsMicrodroidStrictBoot()) {
+ if (!block_dev_init_.InitPlatformDevice("open-dice0")) {
+ return false;
+ }
+ }
+ }
+
if (IsDmLinearEnabled()) {
auto super_symlink = "/dev/block/by-name/"s + super_partition_name_;
if (!android::base::Realpath(super_symlink, &super_path_)) {
@@ -381,9 +396,10 @@
use_snapuserd_ = sm->IsSnapuserdRequired();
if (use_snapuserd_) {
if (sm->UpdateUsesUserSnapshots()) {
- LaunchFirstStageSnapuserd(SnapshotDriver::DM_USER);
+ LaunchFirstStageSnapuserd();
} else {
- LaunchFirstStageSnapuserd(SnapshotDriver::DM_SNAPSHOT);
+ LOG(FATAL) << "legacy virtual-ab is no longer supported";
+ return false;
}
}
@@ -527,9 +543,48 @@
return true;
}
+static bool MaybeDeriveMicrodroidVendorDiceNode(Fstab* fstab) {
+ std::optional<std::string> microdroid_vendor_block_dev;
+ for (auto entry = fstab->begin(); entry != fstab->end(); entry++) {
+ if (entry->mount_point == "/vendor") {
+ microdroid_vendor_block_dev.emplace(entry->blk_device);
+ break;
+ }
+ }
+ if (!microdroid_vendor_block_dev.has_value()) {
+ LOG(VERBOSE) << "No microdroid vendor partition to mount";
+ return true;
+ }
+ // clang-format off
+ const std::array<const char*, 7> args = {
+ "/system/bin/derive_microdroid_vendor_dice_node",
+ "--dice-driver", "/dev/open-dice0",
+ "--microdroid-vendor-disk-image", microdroid_vendor_block_dev->data(),
+ "--output", "/microdroid_resources/dice_chain.raw",
+ };
+ // clang-format-on
+ // ForkExecveAndWaitForCompletion calls waitpid to wait for the fork-ed process to finish.
+ // The first_stage_console adds SA_NOCLDWAIT flag to the SIGCHLD handler, which means that
+ // waitpid will always return -ECHLD. Here we re-register a default handler, so that waitpid
+ // works.
+ LOG(INFO) << "Deriving dice node for microdroid vendor partition";
+ signal(SIGCHLD, SIG_DFL);
+ if (!ForkExecveAndWaitForCompletion(args[0], (char**)args.data())) {
+ LOG(ERROR) << "Failed to derive microdroid vendor dice node";
+ return false;
+ }
+ return true;
+}
+
bool FirstStageMountVBootV2::MountPartitions() {
if (!TrySwitchSystemAsRoot()) return false;
+ if (IsMicrodroid() && android::virtualization::IsOpenDiceChangesFlagEnabled()) {
+ if (!MaybeDeriveMicrodroidVendorDiceNode(&fstab_)) {
+ return false;
+ }
+ }
+
if (!SkipMountingPartitions(&fstab_, true /* verbose */)) return false;
for (auto current = fstab_.begin(); current != fstab_.end();) {
diff --git a/init/persistent_properties.cpp b/init/persistent_properties.cpp
index 6f8a4de..1d17e3c 100644
--- a/init/persistent_properties.cpp
+++ b/init/persistent_properties.cpp
@@ -23,6 +23,7 @@
#include <sys/types.h>
#include <memory>
+#include <unordered_map>
#include <android-base/file.h>
#include <android-base/logging.h>
@@ -235,6 +236,9 @@
persistent_properties->mutable_properties()->end(),
[&name](const auto& record) { return record.name() == name; });
if (it != persistent_properties->mutable_properties()->end()) {
+ if (it->value() == value) {
+ return;
+ }
it->set_name(name);
it->set_value(value);
} else {
diff --git a/init/persistent_properties_test.cpp b/init/persistent_properties_test.cpp
index 5763050..97865d7 100644
--- a/init/persistent_properties_test.cpp
+++ b/init/persistent_properties_test.cpp
@@ -17,6 +17,9 @@
#include "persistent_properties.h"
#include <errno.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
#include <vector>
@@ -155,6 +158,31 @@
EXPECT_FALSE(it == read_back_properties.properties().end());
}
+TEST(persistent_properties, NopUpdateDoesntWriteFile) {
+ TemporaryFile tf;
+ ASSERT_TRUE(tf.fd != -1);
+ persistent_property_filename = tf.path;
+
+ auto last_modified = [&tf]() -> time_t {
+ struct stat buf;
+ EXPECT_EQ(fstat(tf.fd, &buf), 0);
+ return buf.st_mtime;
+ };
+
+ std::vector<std::pair<std::string, std::string>> persistent_properties = {
+ {"persist.sys.locale", "en-US"},
+ {"persist.sys.timezone", "America/Los_Angeles"},
+ };
+ ASSERT_RESULT_OK(
+ WritePersistentPropertyFile(VectorToPersistentProperties(persistent_properties)));
+
+ time_t t = last_modified();
+ sleep(2);
+ WritePersistentProperty("persist.sys.locale", "en-US");
+ // Ensure that the file was not modified
+ ASSERT_EQ(last_modified(), t);
+}
+
TEST(persistent_properties, RejectNonPersistProperty) {
TemporaryFile tf;
ASSERT_TRUE(tf.fd != -1);
diff --git a/init/property_service.cpp b/init/property_service.cpp
index bc4ef42..58a0a7f 100644
--- a/init/property_service.cpp
+++ b/init/property_service.cpp
@@ -58,6 +58,7 @@
#include <android-base/stringprintf.h>
#include <android-base/strings.h>
#include <fs_mgr.h>
+#include <private/android_filesystem_config.h>
#include <property_info_parser/property_info_parser.h>
#include <property_info_serializer/property_info_serializer.h>
#include <selinux/android.h>
@@ -117,12 +118,13 @@
static bool persistent_properties_loaded = false;
-static int property_set_fd = -1;
static int from_init_socket = -1;
static int init_socket = -1;
static bool accept_messages = false;
static std::mutex accept_messages_lock;
+static std::mutex selinux_check_access_lock;
static std::thread property_service_thread;
+static std::thread property_service_for_system_thread;
static std::unique_ptr<PersistWriteThread> persist_write_thread;
@@ -167,6 +169,7 @@
ucred cr = {.pid = 0, .uid = 0, .gid = 0};
audit_data.cr = &cr;
+ auto lock = std::lock_guard{selinux_check_access_lock};
return selinux_check_access(source_context.c_str(), target_context, "file", "read",
&audit_data) == 0;
}
@@ -182,10 +185,9 @@
audit_data.name = name.c_str();
audit_data.cr = &cr;
- bool has_access = (selinux_check_access(source_context, target_context, "property_service",
- "set", &audit_data) == 0);
-
- return has_access;
+ auto lock = std::lock_guard{selinux_check_access_lock};
+ return selinux_check_access(source_context, target_context, "property_service", "set",
+ &audit_data) == 0;
}
void NotifyPropertyChange(const std::string& name, const std::string& value) {
@@ -400,30 +402,38 @@
return {PROP_ERROR_INVALID_VALUE};
}
- prop_info* pi = (prop_info*)__system_property_find(name.c_str());
- if (pi != nullptr) {
- // ro.* properties are actually "write-once".
- if (StartsWith(name, "ro.")) {
- *error = "Read-only property was already set";
- return {PROP_ERROR_READ_ONLY_PROPERTY};
- }
-
- __system_property_update(pi, value.c_str(), valuelen);
+ if (name == "sys.powerctl") {
+ // No action here - NotifyPropertyChange will trigger the appropriate action, and since this
+ // can come to the second thread, we mustn't call out to the __system_property_* functions
+ // which support multiple readers but only one mutator.
} else {
- int rc = __system_property_add(name.c_str(), name.size(), value.c_str(), valuelen);
- if (rc < 0) {
- *error = "__system_property_add failed";
- return {PROP_ERROR_SET_FAILED};
- }
- }
+ prop_info* pi = (prop_info*)__system_property_find(name.c_str());
+ if (pi != nullptr) {
+ // ro.* properties are actually "write-once".
+ if (StartsWith(name, "ro.")) {
+ *error = "Read-only property was already set";
+ return {PROP_ERROR_READ_ONLY_PROPERTY};
+ }
- bool need_persist = StartsWith(name, "persist.") || StartsWith(name, "next_boot.");
- if (socket && persistent_properties_loaded && need_persist) {
- if (persist_write_thread) {
- persist_write_thread->Write(name, value, std::move(*socket));
- return {};
+ __system_property_update(pi, value.c_str(), valuelen);
+ } else {
+ int rc = __system_property_add(name.c_str(), name.size(), value.c_str(), valuelen);
+ if (rc < 0) {
+ *error = "__system_property_add failed";
+ return {PROP_ERROR_SET_FAILED};
+ }
}
- WritePersistentProperty(name, value);
+
+ // Don't write properties to disk until after we have read all default
+ // properties to prevent them from being overwritten by default values.
+ bool need_persist = StartsWith(name, "persist.") || StartsWith(name, "next_boot.");
+ if (socket && persistent_properties_loaded && need_persist) {
+ if (persist_write_thread) {
+ persist_write_thread->Write(name, value, std::move(*socket));
+ return {};
+ }
+ WritePersistentProperty(name, value);
+ }
}
NotifyPropertyChange(name, value);
@@ -584,10 +594,10 @@
return *ret;
}
-static void handle_property_set_fd() {
+static void handle_property_set_fd(int fd) {
static constexpr uint32_t kDefaultSocketTimeout = 2000; /* ms */
- int s = accept4(property_set_fd, nullptr, nullptr, SOCK_CLOEXEC);
+ int s = accept4(fd, nullptr, nullptr, SOCK_CLOEXEC);
if (s == -1) {
return;
}
@@ -1090,6 +1100,12 @@
// required to support.
constexpr auto VENDOR_API_LEVEL_PROP = "ro.vendor.api_level";
+ if (__system_property_find(VENDOR_API_LEVEL_PROP) != nullptr) {
+ // The device already have ro.vendor.api_level in its vendor/build.prop.
+ // Skip initializing the ro.vendor.api_level property.
+ return;
+ }
+
auto vendor_api_level = GetIntProperty("ro.board.first_api_level", __ANDROID_VENDOR_API_MAX__);
if (vendor_api_level != __ANDROID_VENDOR_API_MAX__) {
// Update the vendor_api_level with "ro.board.api_level" only if both "ro.board.api_level"
@@ -1295,7 +1311,7 @@
mkdir(APPCOMPAT_OVERRIDE_PROP_FOLDERNAME, S_IRWXU | S_IXGRP | S_IXOTH);
if (!WriteStringToFile(serialized_contexts, APPCOMPAT_OVERRIDE_PROP_TREE_FILE, 0444, 0, 0,
false)) {
- PLOG(ERROR) << "Unable to write vendor overrides to file";
+ PLOG(ERROR) << "Unable to write appcompat override property infos to file";
}
selinux_android_restorecon(APPCOMPAT_OVERRIDE_PROP_TREE_FILE, 0);
}
@@ -1432,19 +1448,21 @@
}
}
-static void PropertyServiceThread() {
+static void PropertyServiceThread(int fd, bool listen_init) {
Epoll epoll;
if (auto result = epoll.Open(); !result.ok()) {
LOG(FATAL) << result.error();
}
- if (auto result = epoll.RegisterHandler(property_set_fd, handle_property_set_fd);
+ if (auto result = epoll.RegisterHandler(fd, std::bind(handle_property_set_fd, fd));
!result.ok()) {
LOG(FATAL) << result.error();
}
- if (auto result = epoll.RegisterHandler(init_socket, HandleInitSocket); !result.ok()) {
- LOG(FATAL) << result.error();
+ if (listen_init) {
+ if (auto result = epoll.RegisterHandler(init_socket, HandleInitSocket); !result.ok()) {
+ LOG(FATAL) << result.error();
+ }
}
while (true) {
@@ -1493,6 +1511,23 @@
cv_.notify_all();
}
+void StartThread(const char* name, int mode, int gid, std::thread& t, bool listen_init) {
+ int fd = -1;
+ if (auto result = CreateSocket(name, SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK,
+ /*passcred=*/false, /*should_listen=*/false, mode, /*uid=*/0,
+ /*gid=*/gid, /*socketcon=*/{});
+ result.ok()) {
+ fd = *result;
+ } else {
+ LOG(FATAL) << "start_property_service socket creation failed: " << result.error();
+ }
+
+ listen(fd, 8);
+
+ auto new_thread = std::thread(PropertyServiceThread, fd, listen_init);
+ t.swap(new_thread);
+}
+
void StartPropertyService(int* epoll_socket) {
InitPropertySet("ro.property_service.version", "2");
@@ -1504,19 +1539,9 @@
init_socket = sockets[1];
StartSendingMessages();
- if (auto result = CreateSocket(PROP_SERVICE_NAME, SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK,
- /*passcred=*/false, /*should_listen=*/false, 0666, /*uid=*/0,
- /*gid=*/0, /*socketcon=*/{});
- result.ok()) {
- property_set_fd = *result;
- } else {
- LOG(FATAL) << "start_property_service socket creation failed: " << result.error();
- }
-
- listen(property_set_fd, 8);
-
- auto new_thread = std::thread{PropertyServiceThread};
- property_service_thread.swap(new_thread);
+ StartThread(PROP_SERVICE_FOR_SYSTEM_NAME, 0660, AID_SYSTEM, property_service_for_system_thread,
+ true);
+ StartThread(PROP_SERVICE_NAME, 0666, 0, property_service_thread, false);
auto async_persist_writes =
android::base::GetBoolProperty("ro.property_service.async_persist_writes", false);
diff --git a/init/reboot.cpp b/init/reboot.cpp
index 1a26c4d..150f8f4 100644
--- a/init/reboot.cpp
+++ b/init/reboot.cpp
@@ -1083,7 +1083,8 @@
return;
}
}
- } else if (reboot_target == "quiescent") {
+ } else if (std::find(cmd_params.begin(), cmd_params.end(), "quiescent")
+ != cmd_params.end()) { // Quiescent can be either subreason or details.
bootloader_message boot = {};
if (std::string err; !read_bootloader_message(&boot, &err)) {
LOG(ERROR) << "Failed to read bootloader message: " << err;
diff --git a/init/selinux.cpp b/init/selinux.cpp
index 1f211dd..c2d9b8d 100644
--- a/init/selinux.cpp
+++ b/init/selinux.cpp
@@ -66,6 +66,7 @@
#include <android-base/result.h>
#include <android-base/strings.h>
#include <android-base/unique_fd.h>
+#include <android/avf_cc_flags.h>
#include <fs_avb/fs_avb.h>
#include <fs_mgr.h>
#include <libgsi/libgsi.h>
@@ -111,97 +112,6 @@
return true;
}
-// Forks, executes the provided program in the child, and waits for the completion in the parent.
-// Child's stderr is captured and logged using LOG(ERROR).
-bool ForkExecveAndWaitForCompletion(const char* filename, char* const argv[]) {
- // Create a pipe used for redirecting child process's output.
- // * pipe_fds[0] is the FD the parent will use for reading.
- // * pipe_fds[1] is the FD the child will use for writing.
- int pipe_fds[2];
- if (pipe(pipe_fds) == -1) {
- PLOG(ERROR) << "Failed to create pipe";
- return false;
- }
-
- pid_t child_pid = fork();
- if (child_pid == -1) {
- PLOG(ERROR) << "Failed to fork for " << filename;
- return false;
- }
-
- if (child_pid == 0) {
- // fork succeeded -- this is executing in the child process
-
- // Close the pipe FD not used by this process
- close(pipe_fds[0]);
-
- // Redirect stderr to the pipe FD provided by the parent
- if (TEMP_FAILURE_RETRY(dup2(pipe_fds[1], STDERR_FILENO)) == -1) {
- PLOG(ERROR) << "Failed to redirect stderr of " << filename;
- _exit(127);
- return false;
- }
- close(pipe_fds[1]);
-
- if (execv(filename, argv) == -1) {
- PLOG(ERROR) << "Failed to execve " << filename;
- return false;
- }
- // Unreachable because execve will have succeeded and replaced this code
- // with child process's code.
- _exit(127);
- return false;
- } else {
- // fork succeeded -- this is executing in the original/parent process
-
- // Close the pipe FD not used by this process
- close(pipe_fds[1]);
-
- // Log the redirected output of the child process.
- // It's unfortunate that there's no standard way to obtain an istream for a file descriptor.
- // As a result, we're buffering all output and logging it in one go at the end of the
- // invocation, instead of logging it as it comes in.
- const int child_out_fd = pipe_fds[0];
- std::string child_output;
- if (!android::base::ReadFdToString(child_out_fd, &child_output)) {
- PLOG(ERROR) << "Failed to capture full output of " << filename;
- }
- close(child_out_fd);
- if (!child_output.empty()) {
- // Log captured output, line by line, because LOG expects to be invoked for each line
- std::istringstream in(child_output);
- std::string line;
- while (std::getline(in, line)) {
- LOG(ERROR) << filename << ": " << line;
- }
- }
-
- // Wait for child to terminate
- int status;
- if (TEMP_FAILURE_RETRY(waitpid(child_pid, &status, 0)) != child_pid) {
- PLOG(ERROR) << "Failed to wait for " << filename;
- return false;
- }
-
- if (WIFEXITED(status)) {
- int status_code = WEXITSTATUS(status);
- if (status_code == 0) {
- return true;
- } else {
- LOG(ERROR) << filename << " exited with status " << status_code;
- }
- } else if (WIFSIGNALED(status)) {
- LOG(ERROR) << filename << " killed by signal " << WTERMSIG(status);
- } else if (WIFSTOPPED(status)) {
- LOG(ERROR) << filename << " stopped by signal " << WSTOPSIG(status);
- } else {
- LOG(ERROR) << "waitpid for " << filename << " returned unexpected status: " << status;
- }
-
- return false;
- }
-}
-
bool ReadFirstLine(const char* file, std::string* line) {
line->clear();
@@ -793,6 +703,15 @@
SelinuxSetEnforcement();
+ if (IsMicrodroid() && android::virtualization::IsOpenDiceChangesFlagEnabled()) {
+ // We run restorecon of /microdroid_resources while we are still in kernel context to avoid
+ // granting init `tmpfs:file relabelfrom` capability.
+ const int flags = SELINUX_ANDROID_RESTORECON_RECURSE;
+ if (selinux_android_restorecon("/microdroid_resources", flags) == -1) {
+ PLOG(FATAL) << "restorecon of /microdroid_resources failed";
+ }
+ }
+
// We're in the kernel domain and want to transition to the init domain. File systems that
// store SELabels in their xattrs, such as ext4 do not need an explicit restorecon here,
// but other file systems do. In particular, this is needed for ramdisks such as the
diff --git a/init/service.cpp b/init/service.cpp
index eb24dd5..31308a0 100644
--- a/init/service.cpp
+++ b/init/service.cpp
@@ -355,20 +355,35 @@
// If we crash > 4 times in 'fatal_crash_window_' minutes or before boot_completed,
// reboot into bootloader or set crashing property
boot_clock::time_point now = boot_clock::now();
+ constexpr const char native_watchdog_reboot_time[] = "persist.init.svc.last_fatal_reboot_epoch";
+ uint64_t throttle_window =
+ std::chrono::duration_cast<std::chrono::seconds>(std::chrono::hours(24)).count();
if (((flags_ & SVC_CRITICAL) || is_process_updatable) && !(flags_ & SVC_RESTART) &&
!was_last_exit_ok_) {
bool boot_completed = GetBoolProperty("sys.boot_completed", false);
if (now < time_crashed_ + fatal_crash_window_ || !boot_completed) {
if (++crash_count_ > 4) {
- auto exit_reason = boot_completed ?
- "in " + std::to_string(fatal_crash_window_.count()) + " minutes" :
- "before boot completed";
+ auto exit_reason =
+ boot_completed
+ ? "in " + std::to_string(fatal_crash_window_.count()) + " minutes"
+ : "before boot completed";
if (flags_ & SVC_CRITICAL) {
if (!GetBoolProperty("init.svc_debug.no_fatal." + name_, false)) {
- // Aborts into `fatal_reboot_target_'.
- SetFatalRebootTarget(fatal_reboot_target_);
- LOG(FATAL) << "critical process '" << name_ << "' exited 4 times "
- << exit_reason;
+ uint64_t epoch_time =
+ std::chrono::duration_cast<std::chrono::seconds>(
+ std::chrono::system_clock::now().time_since_epoch())
+ .count();
+ // Do not reboot again If it was already initiated in the last 24hrs
+ if (epoch_time - GetIntProperty(native_watchdog_reboot_time, 0) >
+ throttle_window) {
+ SetProperty(native_watchdog_reboot_time, std::to_string(epoch_time));
+ // Aborts into `fatal_reboot_target_'.
+ SetFatalRebootTarget(fatal_reboot_target_);
+ LOG(FATAL) << "critical process '" << name_ << "' exited 4 times "
+ << exit_reason;
+ } else {
+ LOG(INFO) << "Reboot already performed in last 24hrs because of crash.";
+ }
}
} else {
LOG(ERROR) << "process with updatable components '" << name_
diff --git a/init/snapuserd_transition.cpp b/init/snapuserd_transition.cpp
index 3a78343..9e3ff41 100644
--- a/init/snapuserd_transition.cpp
+++ b/init/snapuserd_transition.cpp
@@ -62,7 +62,7 @@
static constexpr char kSnapuserdLabel[] = "u:object_r:snapuserd_exec:s0";
static constexpr char kSnapuserdSocketLabel[] = "u:object_r:snapuserd_socket:s0";
-void LaunchFirstStageSnapuserd(SnapshotDriver driver) {
+void LaunchFirstStageSnapuserd() {
SocketDescriptor socket_desc;
socket_desc.name = android::snapshot::kSnapuserdSocket;
socket_desc.type = SOCK_STREAM;
@@ -85,22 +85,13 @@
if (pid == 0) {
socket->Publish();
- if (driver == SnapshotDriver::DM_USER) {
- char arg0[] = "/system/bin/snapuserd";
- char arg1[] = "-user_snapshot";
- char* const argv[] = {arg0, arg1, nullptr};
- if (execv(arg0, argv) < 0) {
- PLOG(FATAL) << "Cannot launch snapuserd; execv failed";
- }
- _exit(127);
- } else {
- char arg0[] = "/system/bin/snapuserd";
- char* const argv[] = {arg0, nullptr};
- if (execv(arg0, argv) < 0) {
- PLOG(FATAL) << "Cannot launch snapuserd; execv failed";
- }
- _exit(127);
+ char arg0[] = "/system/bin/snapuserd";
+ char arg1[] = "-user_snapshot";
+ char* const argv[] = {arg0, arg1, nullptr};
+ if (execv(arg0, argv) < 0) {
+ PLOG(FATAL) << "Cannot launch snapuserd; execv failed";
}
+ _exit(127);
}
auto client = SnapuserdClient::Connect(android::snapshot::kSnapuserdSocket, 10s);
@@ -195,22 +186,20 @@
return;
}
auto start = reinterpret_cast<const void*>(map.start);
- auto len = map.end - map.start;
+ uint64_t len = android::procinfo::MappedFileSize(map);
if (!len) {
return;
}
+
if (mlock(start, len) < 0) {
- LOG(ERROR) << "mlock failed, " << start << " for " << len << " bytes.";
+ PLOG(ERROR) << "\"" << map.name << "\": mlock(" << start << ", " << len
+ << ") failed: pgoff = " << map.pgoff;
ok = false;
}
};
if (!android::procinfo::ReadProcessMaps(getpid(), callback) || !ok) {
- LOG(FATAL) << "Could not process /proc/" << getpid() << "/maps file for init, "
- << "falling back to mlockall().";
- if (mlockall(MCL_CURRENT) < 0) {
- LOG(FATAL) << "mlockall failed";
- }
+ LOG(FATAL) << "Could not process /proc/" << getpid() << "/maps file for init";
}
}
diff --git a/init/snapuserd_transition.h b/init/snapuserd_transition.h
index 557d105..e86e8da 100644
--- a/init/snapuserd_transition.h
+++ b/init/snapuserd_transition.h
@@ -29,13 +29,8 @@
namespace android {
namespace init {
-enum class SnapshotDriver {
- DM_SNAPSHOT,
- DM_USER,
-};
-
// Fork and exec a new copy of snapuserd.
-void LaunchFirstStageSnapuserd(SnapshotDriver driver);
+void LaunchFirstStageSnapuserd();
class SnapuserdSelinuxHelper final {
using SnapshotManager = android::snapshot::SnapshotManager;
diff --git a/init/test_kill_services/Android.bp b/init/test_kill_services/Android.bp
index 37361a8..ada87d8 100644
--- a/init/test_kill_services/Android.bp
+++ b/init/test_kill_services/Android.bp
@@ -10,7 +10,10 @@
cc_test {
name: "init_kill_services_test",
srcs: ["init_kill_services_test.cpp"],
- shared_libs: ["libbase"],
+ shared_libs: [
+ "libbase",
+ "libhidlbase",
+ ],
test_suites: ["general-tests"],
// TODO(b/153565474): switch back to auto-generation
diff --git a/init/test_kill_services/init_kill_services_test.cpp b/init/test_kill_services/init_kill_services_test.cpp
index 510ad8a..3af92bb 100644
--- a/init/test_kill_services/init_kill_services_test.cpp
+++ b/init/test_kill_services/init_kill_services_test.cpp
@@ -18,15 +18,20 @@
#include <android-base/logging.h>
#include <android-base/properties.h>
+#include <hidl/ServiceManagement.h>
#include <iostream>
using ::android::base::GetProperty;
using ::android::base::SetProperty;
using ::android::base::WaitForProperty;
+using ::android::hardware::isHidlSupported;
using std::literals::chrono_literals::operator""s;
void ExpectKillingServiceRecovers(const std::string& service_name) {
+ if (!isHidlSupported() && service_name == "hwservicemanager") {
+ GTEST_SKIP() << "No HIDL support on device so hwservicemanager will not be running";
+ }
LOG(INFO) << "before we say hi to " << service_name << ", I can't have apexd around!";
// b/280514080 - servicemanager will restart apexd, and apexd will restart the
diff --git a/init/util.cpp b/init/util.cpp
index e760a59..e5efc7d 100644
--- a/init/util.cpp
+++ b/init/util.cpp
@@ -27,6 +27,7 @@
#include <string.h>
#include <sys/socket.h>
#include <sys/un.h>
+#include <sys/wait.h>
#include <time.h>
#include <unistd.h>
@@ -747,5 +748,96 @@
return filtered_configs;
}
+// Forks, executes the provided program in the child, and waits for the completion in the parent.
+// Child's stderr is captured and logged using LOG(ERROR).
+bool ForkExecveAndWaitForCompletion(const char* filename, char* const argv[]) {
+ // Create a pipe used for redirecting child process's output.
+ // * pipe_fds[0] is the FD the parent will use for reading.
+ // * pipe_fds[1] is the FD the child will use for writing.
+ int pipe_fds[2];
+ if (pipe(pipe_fds) == -1) {
+ PLOG(ERROR) << "Failed to create pipe";
+ return false;
+ }
+
+ pid_t child_pid = fork();
+ if (child_pid == -1) {
+ PLOG(ERROR) << "Failed to fork for " << filename;
+ return false;
+ }
+
+ if (child_pid == 0) {
+ // fork succeeded -- this is executing in the child process
+
+ // Close the pipe FD not used by this process
+ close(pipe_fds[0]);
+
+ // Redirect stderr to the pipe FD provided by the parent
+ if (TEMP_FAILURE_RETRY(dup2(pipe_fds[1], STDERR_FILENO)) == -1) {
+ PLOG(ERROR) << "Failed to redirect stderr of " << filename;
+ _exit(127);
+ return false;
+ }
+ close(pipe_fds[1]);
+
+ if (execv(filename, argv) == -1) {
+ PLOG(ERROR) << "Failed to execve " << filename;
+ return false;
+ }
+ // Unreachable because execve will have succeeded and replaced this code
+ // with child process's code.
+ _exit(127);
+ return false;
+ } else {
+ // fork succeeded -- this is executing in the original/parent process
+
+ // Close the pipe FD not used by this process
+ close(pipe_fds[1]);
+
+ // Log the redirected output of the child process.
+ // It's unfortunate that there's no standard way to obtain an istream for a file descriptor.
+ // As a result, we're buffering all output and logging it in one go at the end of the
+ // invocation, instead of logging it as it comes in.
+ const int child_out_fd = pipe_fds[0];
+ std::string child_output;
+ if (!android::base::ReadFdToString(child_out_fd, &child_output)) {
+ PLOG(ERROR) << "Failed to capture full output of " << filename;
+ }
+ close(child_out_fd);
+ if (!child_output.empty()) {
+ // Log captured output, line by line, because LOG expects to be invoked for each line
+ std::istringstream in(child_output);
+ std::string line;
+ while (std::getline(in, line)) {
+ LOG(ERROR) << filename << ": " << line;
+ }
+ }
+
+ // Wait for child to terminate
+ int status;
+ if (TEMP_FAILURE_RETRY(waitpid(child_pid, &status, 0)) != child_pid) {
+ PLOG(ERROR) << "Failed to wait for " << filename;
+ return false;
+ }
+
+ if (WIFEXITED(status)) {
+ int status_code = WEXITSTATUS(status);
+ if (status_code == 0) {
+ return true;
+ } else {
+ LOG(ERROR) << filename << " exited with status " << status_code;
+ }
+ } else if (WIFSIGNALED(status)) {
+ LOG(ERROR) << filename << " killed by signal " << WTERMSIG(status);
+ } else if (WIFSTOPPED(status)) {
+ LOG(ERROR) << filename << " stopped by signal " << WSTOPSIG(status);
+ } else {
+ LOG(ERROR) << "waitpid for " << filename << " returned unexpected status: " << status;
+ }
+
+ return false;
+ }
+}
+
} // namespace init
} // namespace android
diff --git a/init/util.h b/init/util.h
index 2d02182..aa24123 100644
--- a/init/util.h
+++ b/init/util.h
@@ -117,5 +117,10 @@
// (.rc == .0rc for ranking purposes)
std::vector<std::string> FilterVersionedConfigs(const std::vector<std::string>& configs,
int active_sdk);
+
+// Forks, executes the provided program in the child, and waits for the completion in the parent.
+// Child's stderr is captured and logged using LOG(ERROR).
+bool ForkExecveAndWaitForCompletion(const char* filename, char* const argv[]);
+
} // namespace init
} // namespace android
diff --git a/libcutils/Android.bp b/libcutils/Android.bp
index 8ae7d9e..b7752d9 100644
--- a/libcutils/Android.bp
+++ b/libcutils/Android.bp
@@ -116,27 +116,6 @@
},
}
-cc_test {
- name: "libcutils_sockets_test",
- test_suites: ["device-tests"],
- static_libs: ["libbase", "libcutils_sockets"],
- cflags: [
- "-Wall",
- "-Wextra",
- "-Werror",
- ],
-
- srcs: ["sockets_test.cpp"],
- target: {
- android: {
- srcs: [
- "android_get_control_file_test.cpp",
- "android_get_control_socket_test.cpp",
- ],
- },
- },
-}
-
// some files must not be compiled when building against Mingw
// they correspond to features not used by our host development tools
// which are also hard or even impossible to port to native Win32
@@ -347,7 +326,10 @@
cc_test {
name: "KernelLibcutilsTest",
- test_suites: ["general-tests", "vts"],
+ test_suites: [
+ "general-tests",
+ "vts",
+ ],
defaults: ["libcutils_test_static_defaults"],
test_config: "KernelLibcutilsTest.xml",
}
diff --git a/libcutils/abi-dumps/arm64/source-based/libcutils.so.lsdump b/libcutils/abi-dumps/arm64/source-based/libcutils.so.lsdump
index 333e61c..67c7514 100644
--- a/libcutils/abi-dumps/arm64/source-based/libcutils.so.lsdump
+++ b/libcutils/abi-dumps/arm64/source-based/libcutils.so.lsdump
@@ -290,6 +290,9 @@
"name" : "fs_write_atomic_int"
},
{
+ "name" : "get_fs_config"
+ },
+ {
"name" : "hashmapCreate"
},
{
@@ -1274,6 +1277,27 @@
"source_file" : "system/core/libcutils/include/cutils/fs.h"
},
{
+ "function_name" : "get_fs_config",
+ "linker_set_key" : "get_fs_config",
+ "parameters" :
+ [
+ {
+ "referenced_type" : "_ZTIPKc"
+ },
+ {
+ "referenced_type" : "_ZTIb"
+ },
+ {
+ "referenced_type" : "_ZTIPKc"
+ },
+ {
+ "referenced_type" : "_ZTIP9fs_config"
+ }
+ ],
+ "return_type" : "_ZTIb",
+ "source_file" : "system/core/libcutils/include/private/fs_config.h"
+ },
+ {
"function_name" : "hashmapCreate",
"linker_set_key" : "hashmapCreate",
"parameters" :
@@ -2392,6 +2416,15 @@
},
{
"alignment" : 8,
+ "linker_set_key" : "_ZTIP9fs_config",
+ "name" : "fs_config *",
+ "referenced_type" : "_ZTI9fs_config",
+ "self_type" : "_ZTIP9fs_config",
+ "size" : 8,
+ "source_file" : "system/core/libcutils/include/private/fs_config.h"
+ },
+ {
+ "alignment" : 8,
"linker_set_key" : "_ZTIP9str_parms",
"name" : "str_parms *",
"referenced_type" : "_ZTI9str_parms",
@@ -2684,6 +2717,37 @@
"self_type" : "_ZTI5cnode",
"size" : 40,
"source_file" : "system/core/libcutils/include/cutils/config_utils.h"
+ },
+ {
+ "alignment" : 8,
+ "fields" :
+ [
+ {
+ "field_name" : "uid",
+ "referenced_type" : "_ZTIj"
+ },
+ {
+ "field_name" : "gid",
+ "field_offset" : 32,
+ "referenced_type" : "_ZTIj"
+ },
+ {
+ "field_name" : "mode",
+ "field_offset" : 64,
+ "referenced_type" : "_ZTIj"
+ },
+ {
+ "field_name" : "capabilities",
+ "field_offset" : 128,
+ "referenced_type" : "_ZTIm"
+ }
+ ],
+ "linker_set_key" : "_ZTI9fs_config",
+ "name" : "fs_config",
+ "referenced_type" : "_ZTI9fs_config",
+ "self_type" : "_ZTI9fs_config",
+ "size" : 24,
+ "source_file" : "system/core/libcutils/include/private/fs_config.h"
}
],
"rvalue_reference_types" : []
diff --git a/libcutils/abi-dumps/arm_arm64/source-based/libcutils.so.lsdump b/libcutils/abi-dumps/arm_arm64/source-based/libcutils.so.lsdump
index f612fb9..f75240c 100644
--- a/libcutils/abi-dumps/arm_arm64/source-based/libcutils.so.lsdump
+++ b/libcutils/abi-dumps/arm_arm64/source-based/libcutils.so.lsdump
@@ -300,6 +300,9 @@
"name" : "fs_write_atomic_int"
},
{
+ "name" : "get_fs_config"
+ },
+ {
"name" : "hashmapCreate"
},
{
@@ -1284,6 +1287,27 @@
"source_file" : "system/core/libcutils/include/cutils/fs.h"
},
{
+ "function_name" : "get_fs_config",
+ "linker_set_key" : "get_fs_config",
+ "parameters" :
+ [
+ {
+ "referenced_type" : "_ZTIPKc"
+ },
+ {
+ "referenced_type" : "_ZTIb"
+ },
+ {
+ "referenced_type" : "_ZTIPKc"
+ },
+ {
+ "referenced_type" : "_ZTIP9fs_config"
+ }
+ ],
+ "return_type" : "_ZTIb",
+ "source_file" : "system/core/libcutils/include/private/fs_config.h"
+ },
+ {
"function_name" : "hashmapCreate",
"linker_set_key" : "hashmapCreate",
"parameters" :
@@ -2402,6 +2426,15 @@
},
{
"alignment" : 4,
+ "linker_set_key" : "_ZTIP9fs_config",
+ "name" : "fs_config *",
+ "referenced_type" : "_ZTI9fs_config",
+ "self_type" : "_ZTIP9fs_config",
+ "size" : 4,
+ "source_file" : "system/core/libcutils/include/private/fs_config.h"
+ },
+ {
+ "alignment" : 4,
"linker_set_key" : "_ZTIP9str_parms",
"name" : "str_parms *",
"referenced_type" : "_ZTI9str_parms",
@@ -2694,6 +2727,37 @@
"self_type" : "_ZTI5cnode",
"size" : 20,
"source_file" : "system/core/libcutils/include/cutils/config_utils.h"
+ },
+ {
+ "alignment" : 8,
+ "fields" :
+ [
+ {
+ "field_name" : "uid",
+ "referenced_type" : "_ZTIj"
+ },
+ {
+ "field_name" : "gid",
+ "field_offset" : 32,
+ "referenced_type" : "_ZTIj"
+ },
+ {
+ "field_name" : "mode",
+ "field_offset" : 64,
+ "referenced_type" : "_ZTIt"
+ },
+ {
+ "field_name" : "capabilities",
+ "field_offset" : 128,
+ "referenced_type" : "_ZTIy"
+ }
+ ],
+ "linker_set_key" : "_ZTI9fs_config",
+ "name" : "fs_config",
+ "referenced_type" : "_ZTI9fs_config",
+ "self_type" : "_ZTI9fs_config",
+ "size" : 24,
+ "source_file" : "system/core/libcutils/include/private/fs_config.h"
}
],
"rvalue_reference_types" : []
diff --git a/libcutils/ashmem-dev.cpp b/libcutils/ashmem-dev.cpp
index 410dbfd..46b8ef2 100644
--- a/libcutils/ashmem-dev.cpp
+++ b/libcutils/ashmem-dev.cpp
@@ -301,6 +301,12 @@
return -1;
}
+ // forbid size changes to match ashmem behaviour
+ if (fcntl(fd, F_ADD_SEALS, F_SEAL_GROW | F_SEAL_SHRINK) == -1) {
+ ALOGE("memfd_create(%s, %zd) F_ADD_SEALS failed: %m", name, size);
+ return -1;
+ }
+
if (debug_log) {
ALOGE("memfd_create(%s, %zd) success. fd=%d\n", name, size, fd.get());
}
@@ -352,14 +358,29 @@
}
static int memfd_set_prot_region(int fd, int prot) {
- /* Only proceed if an fd needs to be write-protected */
+ int seals = fcntl(fd, F_GET_SEALS);
+ if (seals == -1) {
+ ALOGE("memfd_set_prot_region(%d, %d): F_GET_SEALS failed: %s\n", fd, prot, strerror(errno));
+ return -1;
+ }
+
if (prot & PROT_WRITE) {
+ /* Now we want the buffer to be read-write, let's check if the buffer
+ * has been previously marked as read-only before, if so return error
+ */
+ if (seals & F_SEAL_FUTURE_WRITE) {
+ ALOGE("memfd_set_prot_region(%d, %d): region is write protected\n", fd, prot);
+ errno = EINVAL; // inline with ashmem error code, if already in
+ // read-only mode
+ return -1;
+ }
return 0;
}
- if (fcntl(fd, F_ADD_SEALS, F_SEAL_FUTURE_WRITE) == -1) {
- ALOGE("memfd_set_prot_region(%d, %d): F_SEAL_FUTURE_WRITE seal failed: %s\n", fd, prot,
- strerror(errno));
+ /* We would only allow read-only for any future file operations */
+ if (fcntl(fd, F_ADD_SEALS, F_SEAL_FUTURE_WRITE | F_SEAL_SEAL) == -1) {
+ ALOGE("memfd_set_prot_region(%d, %d): F_SEAL_FUTURE_WRITE | F_SEAL_SEAL seal failed: %s\n",
+ fd, prot, strerror(errno));
return -1;
}
diff --git a/libcutils/fs_config.cpp b/libcutils/fs_config.cpp
index 919be2f..5efe209 100644
--- a/libcutils/fs_config.cpp
+++ b/libcutils/fs_config.cpp
@@ -91,7 +91,7 @@
{ 00751, AID_ROOT, AID_SHELL, 0, "vendor/bin" },
{ 00751, AID_ROOT, AID_SHELL, 0, "vendor/apex/*/bin" },
{ 00755, AID_ROOT, AID_SHELL, 0, "vendor" },
- { 00755, AID_ROOT, AID_ROOT, 0, 0 },
+ {},
// clang-format on
};
#ifndef __ANDROID_VNDK__
@@ -218,17 +218,32 @@
{ 00640, AID_ROOT, AID_SHELL, 0, "fstab.*" },
{ 00750, AID_ROOT, AID_SHELL, 0, "init*" },
{ 00755, AID_ROOT, AID_SHELL, 0, "odm/bin/*" },
+ { 00644, AID_ROOT, AID_ROOT, 0, "odm/framework/*" },
+ { 00644, AID_ROOT, AID_ROOT, 0, "odm/app/*" },
+ { 00644, AID_ROOT, AID_ROOT, 0, "odm/priv-app/*" },
{ 00755, AID_ROOT, AID_SHELL, 0, "product/bin/*" },
{ 00755, AID_ROOT, AID_SHELL, 0, "product/apex/*bin/*" },
+ { 00644, AID_ROOT, AID_ROOT, 0, "product/framework/*" },
+ { 00644, AID_ROOT, AID_ROOT, 0, "product/app/*" },
+ { 00644, AID_ROOT, AID_ROOT, 0, "product/priv-app/*" },
{ 00755, AID_ROOT, AID_SHELL, 0, "system/bin/*" },
{ 00755, AID_ROOT, AID_SHELL, 0, "system/xbin/*" },
{ 00755, AID_ROOT, AID_SHELL, 0, "system/apex/*/bin/*" },
+ { 00644, AID_ROOT, AID_ROOT, 0, "system/framework/*" },
+ { 00644, AID_ROOT, AID_ROOT, 0, "system/app/*" },
+ { 00644, AID_ROOT, AID_ROOT, 0, "system/priv-app/*" },
{ 00755, AID_ROOT, AID_SHELL, 0, "system_ext/bin/*" },
{ 00755, AID_ROOT, AID_SHELL, 0, "system_ext/apex/*/bin/*" },
+ { 00644, AID_ROOT, AID_ROOT, 0, "system_ext/framework/*" },
+ { 00644, AID_ROOT, AID_ROOT, 0, "system_ext/app/*" },
+ { 00644, AID_ROOT, AID_ROOT, 0, "system_ext/priv-app/*" },
{ 00755, AID_ROOT, AID_SHELL, 0, "vendor/bin/*" },
{ 00755, AID_ROOT, AID_SHELL, 0, "vendor/apex/*bin/*" },
{ 00755, AID_ROOT, AID_SHELL, 0, "vendor/xbin/*" },
- { 00644, AID_ROOT, AID_ROOT, 0, 0 },
+ { 00644, AID_ROOT, AID_ROOT, 0, "vendor/framework/*" },
+ { 00644, AID_ROOT, AID_ROOT, 0, "vendor/app/*" },
+ { 00644, AID_ROOT, AID_ROOT, 0, "vendor/priv-app/*" },
+ {},
// clang-format on
};
#ifndef __ANDROID_VNDK__
@@ -318,8 +333,8 @@
auto __for_testing_only__fs_config_cmp = fs_config_cmp;
#endif
-void fs_config(const char* path, int dir, const char* target_out_path, unsigned* uid, unsigned* gid,
- unsigned* mode, uint64_t* capabilities) {
+bool get_fs_config(const char* path, bool dir, const char* target_out_path,
+ struct fs_config* fs_conf) {
const struct fs_path_config* pc;
size_t which, plen;
@@ -362,11 +377,11 @@
if (fs_config_cmp(dir, prefix, len, path, plen)) {
free(prefix);
close(fd);
- *uid = header.uid;
- *gid = header.gid;
- *mode = (*mode & (~07777)) | header.mode;
- *capabilities = header.capabilities;
- return;
+ fs_conf->uid = header.uid;
+ fs_conf->gid = header.gid;
+ fs_conf->mode = header.mode;
+ fs_conf->capabilities = header.capabilities;
+ return true;
}
free(prefix);
}
@@ -375,11 +390,28 @@
for (pc = dir ? android_dirs : android_files; pc->prefix; pc++) {
if (fs_config_cmp(dir, pc->prefix, strlen(pc->prefix), path, plen)) {
- break;
+ fs_conf->uid = pc->uid;
+ fs_conf->gid = pc->gid;
+ fs_conf->mode = pc->mode;
+ fs_conf->capabilities = pc->capabilities;
+ return true;
}
}
- *uid = pc->uid;
- *gid = pc->gid;
- *mode = (*mode & (~07777)) | pc->mode;
- *capabilities = pc->capabilities;
+ return false;
+}
+
+void fs_config(const char* path, int dir, const char* target_out_path, unsigned* uid, unsigned* gid,
+ unsigned* mode, uint64_t* capabilities) {
+ struct fs_config conf;
+ if (get_fs_config(path, dir, target_out_path, &conf)) {
+ *uid = conf.uid;
+ *gid = conf.gid;
+ *mode = (*mode & S_IFMT) | conf.mode;
+ *capabilities = conf.capabilities;
+ } else {
+ *uid = AID_ROOT;
+ *gid = AID_ROOT;
+ *mode = (*mode & S_IFMT) | (dir ? 0755 : 0644);
+ *capabilities = 0;
+ }
}
diff --git a/libcutils/include/private/fs_config.h b/libcutils/include/private/fs_config.h
index 45f46e5..9a727bc 100644
--- a/libcutils/include/private/fs_config.h
+++ b/libcutils/include/private/fs_config.h
@@ -21,8 +21,11 @@
#pragma once
+#include <fcntl.h>
+#include <stdbool.h>
#include <stdint.h>
#include <sys/cdefs.h>
+#include <unistd.h>
#include <linux/capability.h>
@@ -30,17 +33,27 @@
__BEGIN_DECLS
-/*
- * Used in:
- * build/tools/fs_config/fs_config.c
- * build/tools/fs_get_stats/fs_get_stats.c
- * system/extras/ext4_utils/make_ext4fs_main.c
- * external/squashfs-tools/squashfs-tools/android.c
- * system/core/cpio/mkbootfs.c
- * system/core/adb/file_sync_service.cpp
- * system/extras/ext4_utils/canned_fs_config.c
- */
+/* This API is deprecated. New users should call get_fs_config. */
void fs_config(const char* path, int dir, const char* target_out_path, unsigned* uid, unsigned* gid,
unsigned* mode, uint64_t* capabilities);
+struct fs_config {
+ uid_t uid;
+ gid_t gid;
+ mode_t mode;
+ uint64_t capabilities;
+};
+
+/*
+ * If a file system configuration was found for the specified path, store it to *conf.
+ * Returns whether a file system configuration was found.
+ *
+ * dir: Whether path refers to a directory.
+ * target_out_path: Path to the base directory to read the file system configuration from, or a null
+ * pointer to use the root directory as the base. Host code should pass $ANDROID_PRODUCT_OUT or
+ * equivalent, and device code should pass a null pointer.
+ */
+bool get_fs_config(const char* path, bool dir, const char* target_out_path,
+ struct fs_config* conf);
+
__END_DECLS
diff --git a/libmodprobe/Android.bp b/libmodprobe/Android.bp
index 1d94a96..12906cc 100644
--- a/libmodprobe/Android.bp
+++ b/libmodprobe/Android.bp
@@ -10,6 +10,7 @@
vendor_available: true,
ramdisk_available: true,
recovery_available: true,
+ vendor_ramdisk_available: true,
host_supported: true,
srcs: [
"libmodprobe.cpp",
diff --git a/libmodprobe/include/modprobe/modprobe.h b/libmodprobe/include/modprobe/modprobe.h
index 5d79d6a..d7a90c4 100644
--- a/libmodprobe/include/modprobe/modprobe.h
+++ b/libmodprobe/include/modprobe/modprobe.h
@@ -41,6 +41,7 @@
std::vector<std::string>* post_dependencies);
void ResetModuleCount() { module_count_ = 0; }
int GetModuleCount() { return module_count_; }
+ bool IsBlocklisted(const std::string& module_name);
private:
std::string MakeCanonical(const std::string& module_path);
@@ -52,7 +53,6 @@
void AddOption(const std::string& module_name, const std::string& option_name,
const std::string& value);
std::string GetKernelCmdline();
- bool IsBlocklisted(const std::string& module_name);
bool ParseDepCallback(const std::string& base_path, const std::vector<std::string>& args);
bool ParseAliasCallback(const std::vector<std::string>& args);
diff --git a/libutils/CallStack.cpp b/libutils/CallStack.cpp
index fe827eb..029f4a7 100644
--- a/libutils/CallStack.cpp
+++ b/libutils/CallStack.cpp
@@ -89,7 +89,8 @@
// The following four functions may be used via weak symbol references from libutils.
// Clients assume that if any of these symbols are available, then deleteStack() is.
-#ifdef WEAKS_AVAILABLE
+// Apple and Windows does not support this, so only compile on other platforms.
+#if !defined(__APPLE__) && !defined(_WIN32)
CallStack::CallStackUPtr CallStack::getCurrentInternal(int ignoreDepth) {
CallStack::CallStackUPtr stack(new CallStack());
@@ -110,6 +111,6 @@
delete stack;
}
-#endif // WEAKS_AVAILABLE
+#endif // !defined(__APPLE__) && !defined(_WIN32)
}; // namespace android
diff --git a/libutils/CallStack_test.cpp b/libutils/CallStack_test.cpp
index 2cfaf61..7afc2c3 100644
--- a/libutils/CallStack_test.cpp
+++ b/libutils/CallStack_test.cpp
@@ -18,10 +18,16 @@
#include <thread>
+#include <android-base/test_utils.h>
#include <android-base/threads.h>
#include <gtest/gtest.h>
#include <utils/CallStack.h>
+#if defined(__ANDROID__)
+#include <log/log.h>
+#include <log/log_read.h>
+#endif
+
__attribute__((__noinline__)) extern "C" void CurrentCaller(android::String8& backtrace) {
android::CallStack cs;
cs.update();
@@ -61,3 +67,38 @@
ASSERT_NE(-1, cs.toString().find("(ThreadBusyWait")) << "Full backtrace:\n" << cs.toString();
}
+
+#if defined(__ANDROID__)
+TEST(CallStackTest, log_stack) {
+ android::CallStack::logStack("callstack_test");
+ auto logger_list = android_logger_list_open(android_name_to_log_id("main"),
+ ANDROID_LOG_NONBLOCK, 1000, getpid());
+ ASSERT_NE(nullptr, logger_list);
+ std::string log;
+ while (true) {
+ log_msg log_msg;
+ auto ret = android_logger_list_read(logger_list, &log_msg);
+ if (ret == -EAGAIN) {
+ break;
+ }
+ ASSERT_GT(ret, 0);
+ if (log_msg.msg() == nullptr) {
+ continue;
+ }
+ // First get the tag.
+ char* msg = &log_msg.msg()[1];
+ if (std::string(msg) != "callstack_test") {
+ continue;
+ }
+ // Now move past the tag.
+ msg = &msg[strlen(msg) + 1];
+ log += msg;
+ log += '\n';
+ }
+ ASSERT_NE("", log) << "No messages found in the log from the test.";
+ // Look for a backtrace line such as:
+ // #00 pc 00000000000536e4 libutils_test (testing::Test::Run()+436)
+ ASSERT_MATCH(log, "#\\d+ pc \\d+");
+ android_logger_list_close(logger_list);
+}
+#endif
diff --git a/libutils/binder/RefBase_test.cpp b/libutils/binder/RefBase_test.cpp
index d675598..65d40a2 100644
--- a/libutils/binder/RefBase_test.cpp
+++ b/libutils/binder/RefBase_test.cpp
@@ -300,8 +300,8 @@
std::atomic<int>* mDeleteCount;
};
-static sp<Bar> buffer;
-static std::atomic<bool> bufferFull(false);
+[[clang::no_destroy]] static constinit sp<Bar> buffer;
+static constinit std::atomic<bool> bufferFull(false);
// Wait until bufferFull has value val.
static inline void waitFor(bool val) {
@@ -380,8 +380,8 @@
} // Otherwise this is slow and probably pointless on a uniprocessor.
}
-static wp<Bar> wpBuffer;
-static std::atomic<bool> wpBufferFull(false);
+[[clang::no_destroy]] static constinit wp<Bar> wpBuffer;
+static constinit std::atomic<bool> wpBufferFull(false);
// Wait until wpBufferFull has value val.
static inline void wpWaitFor(bool val) {
diff --git a/libutils/binder/include/utils/RefBase.h b/libutils/binder/include/utils/RefBase.h
index 5e3fa7d..f03e1be 100644
--- a/libutils/binder/include/utils/RefBase.h
+++ b/libutils/binder/include/utils/RefBase.h
@@ -404,7 +404,7 @@
public:
typedef typename RefBase::weakref_type weakref_type;
- inline wp() : m_ptr(nullptr), m_refs(nullptr) { }
+ inline constexpr wp() : m_ptr(nullptr), m_refs(nullptr) { }
// if nullptr, returns nullptr
//
diff --git a/libutils/binder/include/utils/StrongPointer.h b/libutils/binder/include/utils/StrongPointer.h
index 43c00c9..fb9b8e8 100644
--- a/libutils/binder/include/utils/StrongPointer.h
+++ b/libutils/binder/include/utils/StrongPointer.h
@@ -30,7 +30,7 @@
template<typename T>
class sp {
public:
- inline sp() : m_ptr(nullptr) { }
+ inline constexpr sp() : m_ptr(nullptr) { }
// The old way of using sp<> was like this. This is bad because it relies
// on implicit conversion to sp<>, which we would like to remove (if an
diff --git a/libutils/include/utils/CallStack.h b/libutils/include/utils/CallStack.h
index 54d559b..0239b68 100644
--- a/libutils/include/utils/CallStack.h
+++ b/libutils/include/utils/CallStack.h
@@ -14,8 +14,7 @@
* limitations under the License.
*/
-#ifndef ANDROID_CALLSTACK_H
-#define ANDROID_CALLSTACK_H
+#pragma once
#include <memory>
@@ -27,14 +26,14 @@
#include <sys/types.h>
#if !defined(__APPLE__) && !defined(_WIN32)
-# define WEAKS_AVAILABLE 1
+# define CALLSTACK_WEAKS_AVAILABLE 1
#endif
#ifndef CALLSTACK_WEAK
-# ifdef WEAKS_AVAILABLE
+# ifdef CALLSTACK_WEAKS_AVAILABLE
# define CALLSTACK_WEAK __attribute__((weak))
-# else // !WEAKS_AVAILABLE
+# else // !CALLSTACK_WEAKS_AVAILABLE
# define CALLSTACK_WEAK
-# endif // !WEAKS_AVAILABLE
+# endif // !CALLSTACK_WEAKS_AVAILABLE
#endif // CALLSTACK_WEAK predefined
#ifndef CALLSTACK_ALWAYS_INLINE
@@ -91,7 +90,7 @@
//
// DO NOT USE THESE. They will disappear.
struct StackDeleter {
-#ifdef WEAKS_AVAILABLE
+#ifdef CALLSTACK_WEAKS_AVAILABLE
void operator()(CallStack* stack) {
deleteStack(stack);
}
@@ -103,7 +102,7 @@
typedef std::unique_ptr<CallStack, StackDeleter> CallStackUPtr;
// Return current call stack if possible, nullptr otherwise.
-#ifdef WEAKS_AVAILABLE
+#ifdef CALLSTACK_WEAKS_AVAILABLE
static CallStackUPtr CALLSTACK_ALWAYS_INLINE getCurrent(int32_t ignoreDepth = 1) {
if (reinterpret_cast<uintptr_t>(getCurrentInternal) == 0) {
ALOGW("CallStack::getCurrentInternal not linked, returning null");
@@ -112,13 +111,13 @@
return getCurrentInternal(ignoreDepth);
}
}
-#else // !WEAKS_AVAILABLE
+#else // !CALLSTACK_WEAKS_AVAILABLE
static CallStackUPtr CALLSTACK_ALWAYS_INLINE getCurrent(int32_t = 1) {
return CallStackUPtr(nullptr);
}
-#endif // !WEAKS_AVAILABLE
+#endif // !CALLSTACK_WEAKS_AVAILABLE
-#ifdef WEAKS_AVAILABLE
+#ifdef CALLSTACK_WEAKS_AVAILABLE
static void CALLSTACK_ALWAYS_INLINE logStack(const char* logtag,
CallStack* stack = getCurrent().get(),
android_LogPriority priority = ANDROID_LOG_DEBUG) {
@@ -135,9 +134,9 @@
android_LogPriority = ANDROID_LOG_DEBUG) {
ALOG(LOG_WARN, logtag, "CallStack::logStackInternal not linked");
}
-#endif // !WEAKS_AVAILABLE
+#endif // !CALLSTACK_WEAKS_AVAILABLE
-#ifdef WEAKS_AVAILABLE
+#ifdef CALLSTACK_WEAKS_AVAILABLE
static String8 CALLSTACK_ALWAYS_INLINE
stackToString(const char* prefix = nullptr, const CallStack* stack = getCurrent().get()) {
if (reinterpret_cast<uintptr_t>(stackToStringInternal) != 0 && stack != nullptr) {
@@ -146,15 +145,15 @@
return String8::format("%s<CallStack package not linked>", (prefix ? prefix : ""));
}
}
-#else // !WEAKS_AVAILABLE
+#else // !CALLSTACK_WEAKS_AVAILABLE
static String8 CALLSTACK_ALWAYS_INLINE stackToString(const char* prefix = nullptr,
const CallStack* = getCurrent().get()) {
return String8::format("%s<CallStack package not linked>", (prefix ? prefix : ""));
}
-#endif // !WEAKS_AVAILABLE
+#endif // !CALLSTACK_WEAKS_AVAILABLE
private:
-#ifdef WEAKS_AVAILABLE
+#ifdef CALLSTACK_WEAKS_AVAILABLE
static CallStackUPtr CALLSTACK_WEAK getCurrentInternal(int32_t ignoreDepth);
static void CALLSTACK_WEAK logStackInternal(const char* logtag, const CallStack* stack,
android_LogPriority priority);
@@ -162,13 +161,13 @@
// The deleter is only invoked on non-null pointers. Hence it will never be
// invoked if CallStack is not linked.
static void CALLSTACK_WEAK deleteStack(CallStack* stack);
-#endif // WEAKS_AVAILABLE
+#endif // CALLSTACK_WEAKS_AVAILABLE
Vector<String8> mFrameLines;
};
} // namespace android
+#undef CALLSTACK_WEAKS_AVAILABLE
+#undef CALLSTACK_WEAK
#undef CALLSTACK_ALWAYS_INLINE
-
-#endif // ANDROID_CALLSTACK_H
diff --git a/libvendorsupport/Android.bp b/libvendorsupport/Android.bp
index b4457b1..e87959e 100644
--- a/libvendorsupport/Android.bp
+++ b/libvendorsupport/Android.bp
@@ -34,3 +34,32 @@
"liblog",
],
}
+
+cc_library_headers {
+ name: "libvendorsupport_llndk_headers",
+ host_supported: true,
+ vendor_available: true,
+ recovery_available: true,
+ ramdisk_available: true,
+ vendor_ramdisk_available: true,
+ native_bridge_supported: true,
+
+ export_include_dirs: ["include_llndk"],
+ llndk: {
+ llndk_headers: true,
+ },
+
+ apex_available: [
+ "//apex_available:platform",
+ "//apex_available:anyapex",
+ ],
+ min_sdk_version: "apex_inherit",
+
+ system_shared_libs: [],
+ stl: "none",
+
+ // This header library is used for libc and must be available to any sdk
+ // versions.
+ // Setting sdk_version to the lowest version allows the dependencies.
+ sdk_version: "1",
+}
diff --git a/libvendorsupport/include_llndk/android/llndk-versioning.h b/libvendorsupport/include_llndk/android/llndk-versioning.h
new file mode 100644
index 0000000..cf82fb7
--- /dev/null
+++ b/libvendorsupport/include_llndk/android/llndk-versioning.h
@@ -0,0 +1,45 @@
+// Copyright (C) 2024 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
+
+// LLNDK (https://source.android.com/docs/core/architecture/vndk/build-system#ll-ndk) is similar to
+// NDK, but uses its own versioning of YYYYMM format for vendor builds. The LLNDK symbols are
+// enabled when the vendor api level is equal to or newer than the ro.board.api_level. These symbols
+// must be annotated in map.txt files with the `# llndk=YYYYMM` annotation. They also must be marked
+// with `__INTRODUCED_IN_LLNDK(YYYYMM)` in the header files. It leaves a no-op annotation for ABI
+// analysis.
+#if !defined(__INTRODUCED_IN_LLNDK)
+#define __INTRODUCED_IN_LLNDK(vendor_api_level) \
+ __attribute__((annotate("introduced_in_llndk=" #vendor_api_level)))
+#endif
+
+#if defined(__ANDROID_VENDOR__)
+
+// Use this macro as an `if` statement to call an API that are available to both NDK and LLNDK.
+// This returns true for the vendor modules if the vendor_api_level is less than or equal to the
+// ro.board.api_level.
+#define API_LEVEL_AT_LEAST(sdk_api_level, vendor_api_level) \
+ constexpr(__ANDROID_VENDOR_API__ >= vendor_api_level)
+
+#else // __ANDROID_VENDOR__
+
+// For non-vendor modules, API_LEVEL_AT_LEAST is replaced with __builtin_available(sdk_api_level) to
+// guard the API for __INTRODUCED_IN.
+#if !defined(API_LEVEL_AT_LEAST)
+#define API_LEVEL_AT_LEAST(sdk_api_level, vendor_api_level) \
+ (__builtin_available(android sdk_api_level, *))
+#endif
+
+#endif // __ANDROID_VENDOR__
diff --git a/rootdir/Android.bp b/rootdir/Android.bp
index c8a3cd6..6a3484e 100644
--- a/rootdir/Android.bp
+++ b/rootdir/Android.bp
@@ -71,3 +71,33 @@
src: "adb_debug.prop",
debug_ramdisk: true,
}
+
+prebuilt_etc {
+ name: "init.zygote64.rc",
+ src: "init.zygote64.rc",
+ sub_dir: "init/hw",
+}
+
+prebuilt_etc {
+ name: "init.zygote32.rc",
+ src: "init.zygote32.rc",
+ sub_dir: "init/hw",
+}
+
+prebuilt_etc {
+ name: "init.zygote64_32.rc",
+ src: "init.zygote64_32.rc",
+ sub_dir: "init/hw",
+}
+
+prebuilt_etc {
+ name: "init.usb.rc",
+ src: "init.usb.rc",
+ sub_dir: "init/hw",
+}
+
+prebuilt_etc {
+ name: "init.usb.configfs.rc",
+ src: "init.usb.configfs.rc",
+ sub_dir: "init/hw",
+}
diff --git a/rootdir/Android.mk b/rootdir/Android.mk
index 7444f96..593bb1e 100644
--- a/rootdir/Android.mk
+++ b/rootdir/Android.mk
@@ -223,13 +223,6 @@
$(hide) sed -i -e 's?%EXPORT_GLOBAL_HWASAN_OPTIONS%?$(EXPORT_GLOBAL_HWASAN_OPTIONS)?g' $@
$(hide) sed -i -e 's?%EXPORT_GLOBAL_SCUDO_ALLOCATION_RING_BUFFER_SIZE%?$(EXPORT_GLOBAL_SCUDO_ALLOCATION_RING_BUFFER_SIZE)?g' $@
-# Append PLATFORM_VNDK_VERSION to base name.
-define append_vndk_version
-$(strip \
- $(basename $(1)).$(PLATFORM_VNDK_VERSION)$(suffix $(1)) \
-)
-endef
-
#######################################
# sanitizer.libraries.txt
include $(CLEAR_VARS)
diff --git a/rootdir/etc/linker.config.json b/rootdir/etc/linker.config.json
index d72ac66..8b3542f 100644
--- a/rootdir/etc/linker.config.json
+++ b/rootdir/etc/linker.config.json
@@ -9,8 +9,6 @@
"libnativehelper.so",
"libnativeloader.so",
"libsigchain.so",
- // TODO(b/122876336): Remove libpac.so once it's migrated to Webview
- "libpac.so",
// TODO(b/120786417 or b/134659294): libicuuc.so
// and libicui18n.so are kept for app compat.
"libicui18n.so",
@@ -32,6 +30,8 @@
],
"provideLibs": [
"libaptX_encoder.so",
- "libaptXHD_encoder.so"
+ "libaptXHD_encoder.so",
+ "libEGL.so",
+ "libGLESv2.so"
]
}
diff --git a/rootdir/init.rc b/rootdir/init.rc
index f3b2d03..1af46c1 100644
--- a/rootdir/init.rc
+++ b/rootdir/init.rc
@@ -247,6 +247,7 @@
write /dev/blkio/background/blkio.bfq.weight 10
write /dev/blkio/blkio.group_idle 0
write /dev/blkio/background/blkio.group_idle 0
+ write /dev/blkio/blkio.prio.class promote-to-rt
write /dev/blkio/background/blkio.prio.class restrict-to-be
restorecon_recursive /mnt
@@ -303,6 +304,9 @@
mkdir /mnt/runtime/full 0755 root root
mkdir /mnt/runtime/full/self 0755 root root
+ # For Pre-reboot Dexopt
+ mkdir /mnt/pre_reboot_dexopt 0755 artd artd
+
# Symlink to keep legacy apps working in multi-user world
symlink /storage/self/primary /mnt/sdcard
symlink /mnt/user/0/primary /mnt/runtime/default/self/primary
@@ -641,9 +645,13 @@
mkdir /metadata/staged-install 0770 root system
- mkdir /metadata/aconfig 0750 root system
- mkdir /metadata/aconfig/flags 0750 root system
- mkdir /metadata/aconfig/boot 0754 root system
+ mkdir /metadata/aconfig 0775 root system
+ mkdir /metadata/aconfig/flags 0770 root system
+ mkdir /metadata/aconfig/boot 0775 root system
+
+ mkdir /metadata/aconfig_test_missions 0775 root system
+ exec_start aconfigd-init
+ start aconfigd
on late-fs
# Ensure that tracefs has the correct permissions.
@@ -709,7 +717,7 @@
# Start tombstoned early to be able to store tombstones.
mkdir /data/anr 0775 system system encryption=Require
- mkdir /data/tombstones 0771 system system encryption=Require
+ mkdir /data/tombstones 0775 system system encryption=Require
mkdir /data/vendor/tombstones 0771 root root
mkdir /data/vendor/tombstones/wifi 0771 wifi wifi
start tombstoned
@@ -853,6 +861,8 @@
mkdir /data/misc/odsign 0710 root system
# directory used for odsign metrics
mkdir /data/misc/odsign/metrics 0770 root system
+ # directory used for connectivity blob store.
+ mkdir /data/misc/connectivityblobdb 0770 system system
# Directory for VirtualizationService temporary image files.
# Delete any stale files owned by the old virtualizationservice uid (b/230056726).
@@ -1023,7 +1033,7 @@
perform_apex_config
# Create directories for boot animation.
- mkdir /data/misc/bootanim 0755 system system encryption=DeleteIfNecessary
+ mkdir /data/misc/bootanim 0755 system system
exec_start derive_sdk
@@ -1358,3 +1368,16 @@
write /sys/kernel/mm/lru_gen/enabled 5
on property:persist.device_config.mglru_native.lru_gen_config=all
write /sys/kernel/mm/lru_gen/enabled 7
+
+# Allow other processes to run `snapshotctl` through `init`. This requires
+# `set_prop` permission on `snapshotctl_prop`.
+on property:sys.snapshotctl.map=requested
+ # "root" is needed to talk to gsid and pass its check on uid.
+ # "system" is needed to write to "/dev/socket/snapuserd" to talk to
+ # snapuserd.
+ exec - root root system -- /system/bin/snapshotctl map
+ setprop sys.snapshotctl.map "finished"
+
+on property:sys.snapshotctl.unmap=requested
+ exec - root root system -- /system/bin/snapshotctl unmap
+ setprop sys.snapshotctl.unmap "finished"
diff --git a/toolbox/Android.bp b/toolbox/Android.bp
index 8594ec4..120cc6e 100644
--- a/toolbox/Android.bp
+++ b/toolbox/Android.bp
@@ -69,6 +69,7 @@
name: "toolbox",
defaults: ["toolbox_binary_defaults"],
recovery_available: true,
+ vendor_ramdisk_available: true,
}
cc_binary {
diff --git a/toolbox/OWNERS b/toolbox/OWNERS
index 5e2c581..898ddce 100644
--- a/toolbox/OWNERS
+++ b/toolbox/OWNERS
@@ -1,2 +1,3 @@
include platform/system/core:/janitors/OWNERS
per-file modprobe.c=willmcvicker@google.com,dvander@google.com
+per-file getevent.c=file:platform/frameworks/base:/INPUT_OWNERS
diff --git a/toolbox/getevent.c b/toolbox/getevent.c
index f65bb20..7b896e9 100644
--- a/toolbox/getevent.c
+++ b/toolbox/getevent.c
@@ -441,7 +441,7 @@
if(res < (int)sizeof(*event)) {
if(errno == EINTR)
return 0;
- fprintf(stderr, "could not get event, %s\n", strerror(errno));
+ fprintf(stderr, "could not get inotify events, %s\n", strerror(errno));
return 1;
}
//printf("got %d bytes of event information\n", res);
@@ -664,7 +664,7 @@
if(ufds[i].revents & POLLIN) {
res = read(ufds[i].fd, &event, sizeof(event));
if(res < (int)sizeof(event)) {
- fprintf(stderr, "could not get event\n");
+ fprintf(stderr, "could not get evdev event, %s\n", strerror(errno));
return 1;
}
if(get_time) {
diff --git a/toolbox/modprobe.cpp b/toolbox/modprobe.cpp
index 17d4e31..45dd9b8 100644
--- a/toolbox/modprobe.cpp
+++ b/toolbox/modprobe.cpp
@@ -112,6 +112,7 @@
android::base::SetMinimumLogSeverity(android::base::INFO);
std::vector<std::string> modules;
+ std::string modules_load_file;
std::string module_parameters;
std::string mods;
std::vector<std::string> mod_dirs;
@@ -119,7 +120,7 @@
bool blocklist = false;
int rv = EXIT_SUCCESS;
- int opt;
+ int opt, fd;
int option_index = 0;
// NB: We have non-standard short options -l and -D to make it easier for
// OEMs to transition from toybox.
@@ -144,16 +145,19 @@
// is supported here by default, ignore flag if no argument.
check_mode();
if (optarg == NULL) break;
- if (!android::base::ReadFileToString(optarg, &mods)) {
+
+ // Since libmodprobe doesn't fail when the modules load file
+ // doesn't exist, let's check that here so that we don't
+ // silently fail.
+ fd = open(optarg, O_RDONLY | O_CLOEXEC | O_BINARY);
+ if (fd == -1) {
PLOG(ERROR) << "Failed to open " << optarg;
- rv = EXIT_FAILURE;
+ return EXIT_FAILURE;
}
- for (auto mod : android::base::Split(stripComments(mods), "\n")) {
- mod = android::base::Trim(mod);
- if (mod == "") continue;
- if (std::find(modules.begin(), modules.end(), mod) != modules.end()) continue;
- modules.emplace_back(mod);
- }
+ close(fd);
+
+ mod_dirs.emplace_back(android::base::Dirname(optarg));
+ modules_load_file = android::base::Basename(optarg);
break;
case 'b':
blocklist = true;
@@ -233,30 +237,39 @@
LOG(DEBUG) << "mode is " << mode;
LOG(DEBUG) << "mod_dirs is: " << android::base::Join(mod_dirs, " ");
LOG(DEBUG) << "modules is: " << android::base::Join(modules, " ");
+ LOG(DEBUG) << "modules load file is: " << modules_load_file;
LOG(DEBUG) << "module parameters is: " << android::base::Join(module_parameters, " ");
if (modules.empty()) {
if (mode == ListModulesMode) {
// emulate toybox modprobe list with no pattern (list all)
modules.emplace_back("*");
- } else {
+ } else if (modules_load_file.empty()) {
LOG(ERROR) << "No modules given.";
print_usage();
return EXIT_FAILURE;
}
}
- if (parameter_count && modules.size() > 1) {
+ if (parameter_count && (modules.size() > 1 || !modules_load_file.empty())) {
LOG(ERROR) << "Only one module may be loaded when specifying module parameters.";
print_usage();
return EXIT_FAILURE;
}
- Modprobe m(mod_dirs, "modules.load", blocklist);
+ Modprobe m(mod_dirs, modules_load_file.empty() ? "modules.load" : modules_load_file, blocklist);
+ if (mode == AddModulesMode && !modules_load_file.empty()) {
+ if (!m.LoadListedModules(false)) {
+ PLOG(ERROR) << "Failed to load all the modules from " << modules_load_file;
+ return EXIT_FAILURE;
+ }
+ /* Fall-through to load modules provided on the command line (if any)*/
+ }
for (const auto& module : modules) {
switch (mode) {
case AddModulesMode:
if (!m.LoadWithAliases(module, true, module_parameters)) {
+ if (m.IsBlocklisted(module)) continue;
PLOG(ERROR) << "Failed to load module " << module;
rv = EXIT_FAILURE;
}
diff --git a/trusty/keymint/src/keymint_hal_main.rs b/trusty/keymint/src/keymint_hal_main.rs
index eda986a..ef0c598 100644
--- a/trusty/keymint/src/keymint_hal_main.rs
+++ b/trusty/keymint/src/keymint_hal_main.rs
@@ -82,7 +82,7 @@
}
fn main() {
- if let Err(e) = inner_main() {
+ if let Err(HalServiceError(e)) = inner_main() {
panic!("HAL service failed: {:?}", e);
}
}
diff --git a/trusty/libtrusty/tipc-test/tipc_test.c b/trusty/libtrusty/tipc-test/tipc_test.c
index dabe118..3cf0c05 100644
--- a/trusty/libtrusty/tipc-test/tipc_test.c
+++ b/trusty/libtrusty/tipc-test/tipc_test.c
@@ -37,7 +37,7 @@
#define BENCH_RESULT_TPL \
"{" \
" \"schema_version\": 3," \
-" \"suite_name\": \"crypto\"," \
+" \"suite_name\": \"tipc\"," \
" \"bench_name\": \"%s\"," \
" \"results\": [" \
" {" \
@@ -1041,7 +1041,7 @@
}
avg /= params->bench;
- fprintf(stderr, BENCH_RESULT_TPL, params->test_name, min, max, avg, cold, min, max, avg, cold);
+ printf(BENCH_RESULT_TPL, params->test_name, min, max, avg, cold, min, max, avg, cold);
return rc;
}
diff --git a/trusty/metrics/include/trusty/metrics/tipc.h b/trusty/metrics/include/trusty/metrics/tipc.h
index b29628e..e2cf57b 100644
--- a/trusty/metrics/include/trusty/metrics/tipc.h
+++ b/trusty/metrics/include/trusty/metrics/tipc.h
@@ -39,14 +39,17 @@
* repository. They must be kept in sync.
*/
-#define METRICS_PORT "com.android.trusty.metrics"
+#define METRICS_PORT "com.android.trusty.metrics.consumer"
+
+#define UUID_STR_SIZE (37)
/**
* enum metrics_cmd - command identifiers for metrics interface
- * @METRICS_CMD_RESP_BIT: message is a response
- * @METRICS_CMD_REQ_SHIFT: number of bits used by @METRICS_CMD_RESP_BIT
- * @METRICS_CMD_REPORT_EVENT_DROP: report gaps in the event stream
- * @METRICS_CMD_REPORT_CRASH: report an app crash event
+ * @METRICS_CMD_RESP_BIT: message is a response
+ * @METRICS_CMD_REQ_SHIFT: number of bits used by @METRICS_CMD_RESP_BIT
+ * @METRICS_CMD_REPORT_EVENT_DROP: report gaps in the event stream
+ * @METRICS_CMD_REPORT_CRASH: report an app crash event
+ * @METRICS_CMD_REPORT_STORAGE_ERROR: report trusty storage error
*/
enum metrics_cmd {
METRICS_CMD_RESP_BIT = 1,
@@ -54,6 +57,7 @@
METRICS_CMD_REPORT_EVENT_DROP = (1 << METRICS_CMD_REQ_SHIFT),
METRICS_CMD_REPORT_CRASH = (2 << METRICS_CMD_REQ_SHIFT),
+ METRICS_CMD_REPORT_STORAGE_ERROR = (3 << METRICS_CMD_REQ_SHIFT),
};
/**
@@ -90,17 +94,70 @@
/**
* struct metrics_report_crash_req - arguments of %METRICS_CMD_REPORT_CRASH
* requests
- * @app_id_len: length of app ID that follows this structure
+ * @app_id: uuid of the app that crashed
* @crash_reason: architecture-specific code representing the reason for the
* crash
*/
struct metrics_report_crash_req {
- uint32_t app_id_len;
+ char app_id[UUID_STR_SIZE];
uint32_t crash_reason;
} __attribute__((__packed__));
-#define METRICS_MAX_APP_ID_LEN 256
+enum TrustyStorageErrorType {
+ TRUSTY_STORAGE_ERROR_UNKNOWN = 0,
+ TRUSTY_STORAGE_ERROR_SUPERBLOCK_INVALID = 1,
+ TRUSTY_STORAGE_ERROR_BLOCK_MAC_MISMATCH = 2,
+ TRUSTY_STORAGE_ERROR_BLOCK_HEADER_INVALID = 3,
+ TRUSTY_STORAGE_ERROR_RPMB_COUNTER_MISMATCH = 4,
+ TRUSTY_STORAGE_ERROR_RPMB_COUNTER_MISMATCH_RECOVERED = 5,
+ TRUSTY_STORAGE_ERROR_RPMB_COUNTER_READ_FAILURE = 6,
+ TRUSTY_STORAGE_ERROR_RPMB_MAC_MISMATCH = 7,
+ TRUSTY_STORAGE_ERROR_RPMB_ADDR_MISMATCH = 8,
+ TRUSTY_STORAGE_ERROR_RPMB_FAILURE_RESPONSE = 9,
+ TRUSTY_STORAGE_ERROR_RPMB_UNKNOWN = 10,
+ TRUSTY_STORAGE_ERROR_RPMB_SCSI_ERROR = 11,
+ TRUSTY_STORAGE_ERROR_IO_ERROR = 12,
+ TRUSTY_STORAGE_ERROR_PROXY_COMMUNICATION_FAILURE = 13,
+};
-#define METRICS_MAX_MSG_SIZE \
- (sizeof(struct metrics_req) + sizeof(struct metrics_report_crash_req) + \
- METRICS_MAX_APP_ID_LEN)
+enum TrustyFileSystem {
+ TRUSTY_FS_UNKNOWN = 0,
+ TRUSTY_FS_TP = 1,
+ TRUSTY_FS_TD = 2,
+ TRUSTY_FS_TDP = 3,
+ TRUSTY_FS_TDEA = 4,
+ TRUSTY_FS_NSP = 5,
+};
+
+enum TrustyBlockType {
+ TRUSTY_BLOCKTYPE_UNKNOWN = 0,
+ TRUSTY_BLOCKTYPE_FILES_ROOT = 1,
+ TRUSTY_BLOCKTYPE_FREE_ROOT = 2,
+ TRUSTY_BLOCKTYPE_FILES_INTERNAL = 3,
+ TRUSTY_BLOCKTYPE_FREE_INTERNAL = 4,
+ TRUSTY_BLOCKTYPE_FILE_ENTRY = 5,
+ TRUSTY_BLOCKTYPE_FILE_BLOCK_MAP = 6,
+ TRUSTY_BLOCKTYPE_FILE_DATA = 7,
+ TRUSTY_BLOCKTYPE_CHECKPOINT_ROOT = 8,
+ TRUSTY_BLOCKTYPE_CHECKPOINT_FILES_ROOT = 9,
+ TRUSTY_BLOCKTYPE_CHECKPOINT_FREE_ROOT = 10,
+};
+
+struct metrics_report_storage_error_req {
+ enum TrustyStorageErrorType error;
+ char app_id[UUID_STR_SIZE];
+ char client_app_id[UUID_STR_SIZE];
+ uint32_t write;
+ enum TrustyFileSystem file_system;
+ uint64_t file_path_hash;
+ enum TrustyBlockType block_type;
+ uint64_t repair_counter;
+} __attribute__((__packed__));
+
+struct metrics_msg {
+ struct metrics_req req;
+ union {
+ struct metrics_report_crash_req crash_args;
+ struct metrics_report_storage_error_req storage_args;
+ };
+} __attribute__((__packed__));
\ No newline at end of file
diff --git a/trusty/metrics/metrics.cpp b/trusty/metrics/metrics.cpp
index 3ac128a..d2f0b0a 100644
--- a/trusty/metrics/metrics.cpp
+++ b/trusty/metrics/metrics.cpp
@@ -78,9 +78,8 @@
return Error() << "connection to Metrics TA has not been initialized yet";
}
- uint8_t msg[METRICS_MAX_MSG_SIZE];
-
- auto rc = read(metrics_fd_, msg, sizeof(msg));
+ struct metrics_msg metrics_msg;
+ int rc = read(metrics_fd_, &metrics_msg, sizeof(metrics_msg));
if (rc < 0) {
return ErrnoError() << "failed to read metrics message";
}
@@ -89,23 +88,14 @@
if (msg_len < sizeof(metrics_req)) {
return Error() << "message too small: " << rc;
}
- auto req = reinterpret_cast<metrics_req*>(msg);
- size_t offset = sizeof(metrics_req);
+ uint32_t cmd = metrics_msg.req.cmd;
uint32_t status = METRICS_NO_ERROR;
- switch (req->cmd) {
+ switch (cmd) {
case METRICS_CMD_REPORT_CRASH: {
- if (msg_len < offset + sizeof(metrics_report_crash_req)) {
- return Error() << "message too small: " << rc;
- }
- auto crash_args = reinterpret_cast<metrics_report_crash_req*>(msg + offset);
- offset += sizeof(metrics_report_crash_req);
-
- if (msg_len < offset + crash_args->app_id_len) {
- return Error() << "message too small: " << rc;
- }
- auto app_id_ptr = reinterpret_cast<char*>(msg + offset);
- std::string app_id(app_id_ptr, crash_args->app_id_len);
+ struct metrics_report_crash_req crash_args = metrics_msg.crash_args;
+ auto app_id_ptr = crash_args.app_id;
+ std::string app_id(app_id_ptr, UUID_STR_SIZE);
HandleCrash(app_id);
break;
@@ -121,7 +111,7 @@
}
metrics_resp resp = {
- .cmd = req->cmd | METRICS_CMD_RESP_BIT,
+ .cmd = cmd | METRICS_CMD_RESP_BIT,
.status = status,
};
diff --git a/trusty/secretkeeper/src/hal_main.rs b/trusty/secretkeeper/src/hal_main.rs
index df30493..b31db13 100644
--- a/trusty/secretkeeper/src/hal_main.rs
+++ b/trusty/secretkeeper/src/hal_main.rs
@@ -91,7 +91,7 @@
}
fn main() {
- if let Err(e) = inner_main() {
+ if let Err(HalServiceError(e)) = inner_main() {
panic!("HAL service failed: {:?}", e);
}
}
diff --git a/trusty/utils/rpmb_dev/Android.bp b/trusty/utils/rpmb_dev/Android.bp
index a270087..5e9caaf 100644
--- a/trusty/utils/rpmb_dev/Android.bp
+++ b/trusty/utils/rpmb_dev/Android.bp
@@ -24,6 +24,7 @@
],
shared_libs: [
"libc",
+ "libcutils",
"liblog",
"libcrypto",
],
diff --git a/trusty/utils/rpmb_dev/rpmb_dev.c b/trusty/utils/rpmb_dev/rpmb_dev.c
index 0a9e6a1..cee3037 100644
--- a/trusty/utils/rpmb_dev/rpmb_dev.c
+++ b/trusty/utils/rpmb_dev/rpmb_dev.c
@@ -19,6 +19,7 @@
#include "rpmb_protocol.h"
#include <assert.h>
+#include <cutils/sockets.h>
#include <errno.h>
#include <fcntl.h>
#include <getopt.h>
@@ -613,20 +614,24 @@
return EXIT_FAILURE;
}
- cmdres_sock = socket(AF_UNIX, SOCK_STREAM, 0);
+ cmdres_sock = android_get_control_socket(socket_path);
if (cmdres_sock < 0) {
- ALOGE("rpmb_dev: Failed to create command/response socket: %s\n", strerror(errno));
- return EXIT_FAILURE;
- }
+ ALOGW("android_get_control_socket(%s) failed, fall back to create it\n", socket_path);
+ cmdres_sock = socket(AF_UNIX, SOCK_STREAM, 0);
+ if (cmdres_sock < 0) {
+ ALOGE("rpmb_dev: Failed to create command/response socket: %s\n", strerror(errno));
+ return EXIT_FAILURE;
+ }
- cmdres_sockaddr.sun_family = AF_UNIX;
- strncpy(cmdres_sockaddr.sun_path, socket_path, sizeof(cmdres_sockaddr.sun_path));
+ cmdres_sockaddr.sun_family = AF_UNIX;
+ strncpy(cmdres_sockaddr.sun_path, socket_path, sizeof(cmdres_sockaddr.sun_path));
- ret = bind(cmdres_sock, (struct sockaddr*)&cmdres_sockaddr, sizeof(struct sockaddr_un));
- if (ret < 0) {
- ALOGE("rpmb_dev: Failed to bind command/response socket: %s: %s\n", socket_path,
- strerror(errno));
- return EXIT_FAILURE;
+ ret = bind(cmdres_sock, (struct sockaddr*)&cmdres_sockaddr, sizeof(struct sockaddr_un));
+ if (ret < 0) {
+ ALOGE("rpmb_dev: Failed to bind command/response socket: %s: %s\n", socket_path,
+ strerror(errno));
+ return EXIT_FAILURE;
+ }
}
ret = listen(cmdres_sock, 1);
diff --git a/trusty/utils/rpmb_dev/rpmb_dev.rc b/trusty/utils/rpmb_dev/rpmb_dev.rc
index 9e203b8..33ec0f2 100644
--- a/trusty/utils/rpmb_dev/rpmb_dev.rc
+++ b/trusty/utils/rpmb_dev/rpmb_dev.rc
@@ -1,29 +1,34 @@
# RPMB Mock
-on post-fs-data
- mkdir /data/vendor/ss
- chown root system /data/vendor/ss
- chmod 0770 /data/vendor/ss
- rm /data/vendor/ss/rpmb_sock
- start rpmb_mock_init
+on post-fs
+ mkdir /mnt/vendor/persist/ss 0770 root system
+ exec_start rpmb_mock_init
start rpmb_mock
+on post-fs-data
+ mkdir /data/vendor/ss 0770 root system
+ symlink /mnt/vendor/persist/ss /data/vendor/ss/persist
+ chown root system /data/vendor/ss/persist
+ chmod 0770 /data/vendor/ss/persist
+
# Storage proxy
- start storageproxyd
+ restart storageproxyd
-service storageproxyd /vendor/bin/storageproxyd -d /dev/trusty-ipc-dev0 \
- -r /data/vendor/ss/rpmb_sock -p /data/vendor/ss -t sock
- class main
- disabled
- user root
+service storageproxyd /vendor/bin/storageproxyd -d ${ro.hardware.trusty_ipc_dev:-/dev/trusty-ipc-dev0} \
+ -r /dev/socket/rpmb_mock -p /data/vendor/ss -t sock
+ class early_hal
+ user system
+ group system
-service rpmb_mock_init /vendor/bin/rpmb_dev --dev /data/vendor/ss/RPMB_DATA --init --size 2048
+service rpmb_mock_init /vendor/bin/rpmb_dev --dev /mnt/vendor/persist/ss/RPMB_DATA --init --size 2048
disabled
user system
group system
oneshot
-service rpmb_mock /vendor/bin/rpmb_dev --dev /data/vendor/ss/RPMB_DATA --sock /data/vendor/ss/rpmb_sock
+service rpmb_mock /vendor/bin/rpmb_dev --dev /mnt/vendor/persist/ss/RPMB_DATA \
+ --sock rpmb_mock
class main
disabled
user system
group system
+ socket rpmb_mock stream 660 system system