Android 15.0.0 Release 6 (AP4A.241205.013)
-----BEGIN PGP SIGNATURE----- iF0EABECAB0WIQRDQNE1cO+UXoOBCWTorT+BmrEOeAUCZ1IsswAKCRDorT+BmrEO eHLxAJ9VFRJgjolHUwxeYIHRrAxp7WFw0wCeIiUvtF763IeQx6Ri6gz3/i1V9mY= =uE+H -----END PGP SIGNATURE----- gpgsig -----BEGIN PGP SIGNATURE----- iQJLBAABCgA1FiEEHrBYPudH862glXQBzJUERRm+ZmkFAmdYsU0XHG1rYmVzdGFz QGxpbmVhZ2Vvcy5vcmcACgkQzJUERRm+ZmlzEhAAkyT+qSieZv1roFs6MW0sBnjP 60eSCsj/eVetsK91ExBdm+NPHmpFG1XUcwxxiWzlPweIYA+eaECdoP9qngwxH/fy 7m6lxzVx2C9JbSCRWuBmyFWfsm7l+cjDoO8a5QnummBNobhV6/z680+CPzhsXXp5 wQ8cRYLlZEwSMGlgW5KufhbEQISZK1rxWGcx7C0MwoAZybm0V7bcv9ot9XWVZdBI 0uvpZEAYuLqMTTOxd1HNZBKA+cMmWLE+0ALfydGqdHxTkpDXY17Ek4/R3H7KTcy0 mhp6rLQHMKn/atDUsYGvDp/wGs+PWHl9QPXprwj9g9XBNRaAcw/ANi+I/Gc17Qsc X/5DeC0ycGBljhjnl7ZoXAPwLyN+tYZi+ekwBs0E4+uQCLG5AMSLGZHGHcZafXB1 s0pR1u85BxC/7CoVB22J5utjsLdJT0G8bIgfyrKVVIA9iIe9zO/rsMN+9kffrQ9W xPohc1XyVrsQ2b6xk/PyqbAI5mk7+IKKhxhX+Vv2Fczp2OCPuefa1aS1lIv4bZBL rRPlVyodLWsEqxGNhiCo5Hh24uufJGuBTL2w6Rn5/UkqUkvUQZbsRNTg7WQIfcWh sNvuNNxpgsilXFJC0/aoLE557MjCWq4eolPLnyrz3yR3jPcAa269bMuiMXKsVeEd PvjxgQawPY8QkE2woe0= =R9aC -----END PGP SIGNATURE----- Merge tag 'android-15.0.0_r6' into staging/lineage-22.0_merge-android-15.0.0_r6 Android 15.0.0 Release 6 (AP4A.241205.013) # -----BEGIN PGP SIGNATURE----- # # iF0EABECAB0WIQRDQNE1cO+UXoOBCWTorT+BmrEOeAUCZ1IsswAKCRDorT+BmrEO # eHLxAJ9VFRJgjolHUwxeYIHRrAxp7WFw0wCeIiUvtF763IeQx6Ri6gz3/i1V9mY= # =uE+H # -----END PGP SIGNATURE----- # gpg: Signature made Fri Dec 6 00:44:03 2024 EET # gpg: using DSA key 4340D13570EF945E83810964E8AD3F819AB10E78 # gpg: Good signature from "The Android Open Source Project <initial-contribution@android.com>" [marginal] # gpg: initial-contribution@android.com: Verified 2481 signatures in the past # 3 years. Encrypted 4 messages in the past 2 years. # gpg: WARNING: This key is not certified with sufficiently trusted signatures! # gpg: It is not certain that the signature belongs to the owner. # Primary key fingerprint: 4340 D135 70EF 945E 8381 0964 E8AD 3F81 9AB1 0E78 # By Akilesh Kailash (13) and others # Via Automerger Merge Worker (317) and others * tag 'android-15.0.0_r6': (158 commits) trusty: storage: proxy: FS_READY property setting on vendor only Fix the trigger name for loading bpf programs. start netd earlier Replace base::RandInt with std::uniform_int_distribution trusty: keymint: rename trusty_ipc_dev property Move the `dist` target of `mke2fs` to `build/core/tasks` Remove define of SA_EXPOSE_TAGBITS. Add input event profile to mitigate input latency of input threads Remove usage of base/string/* in libfs_avb Add getFdStateDebug to access Looper's callbacks libsnapshot: CHECK -> CHECK_EQ Mount /mnt/vm earlier Define linker.config.json as a filegroup Remove usage of base/logging.h in libfs_avb debuggerd: recognize jumps to non-executable memory. Support vendor partition in non-debuggable pVMs Remind the reader that they'll need to modify CTS too. Rename system/core/rootdir/Android.mk to create_root_structure.mk trusty: keymint/gatekeeper: Pass device name from init scripts Remove unused variable. ... Conflicts: fs_mgr/libsnapshot/include/libsnapshot/snapshot.h fs_mgr/libsnapshot/snapshot.cpp init/Android.bp init/fuzzer/Android.bp Change-Id: I29c07b3ac76940cb2b82726e98d2beb643b3e6e4
This commit is contained in:
commit
ffe39e16d3
212 changed files with 4742 additions and 5106 deletions
|
|
@ -499,7 +499,7 @@ int32_t BootReasonStrToEnum(const std::string& boot_reason) {
|
|||
}
|
||||
|
||||
// Canonical list of supported primary reboot reasons.
|
||||
const std::vector<const std::string> knownReasons = {
|
||||
const std::vector<std::string> knownReasons = {
|
||||
// clang-format off
|
||||
// kernel
|
||||
"watchdog",
|
||||
|
|
|
|||
|
|
@ -359,6 +359,7 @@ cc_test {
|
|||
"libdebuggerd/test/dump_memory_test.cpp",
|
||||
"libdebuggerd/test/elf_fake.cpp",
|
||||
"libdebuggerd/test/log_fake.cpp",
|
||||
"libdebuggerd/test/mte_stack_record_test.cpp",
|
||||
"libdebuggerd/test/open_files_list_test.cpp",
|
||||
"libdebuggerd/test/tombstone_proto_to_text_test.cpp",
|
||||
],
|
||||
|
|
|
|||
|
|
@ -138,7 +138,7 @@ bool debuggerd_trigger_dump(pid_t tid, DebuggerdDumpType dump_type, unsigned int
|
|||
|
||||
auto remaining = end - std::chrono::steady_clock::now();
|
||||
if (remaining < decltype(remaining)::zero()) {
|
||||
log_error(output_fd, 0, "timeout expired");
|
||||
log_error(output_fd, 0, "timeout expired (update_timeout)");
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
@ -254,7 +254,7 @@ bool debuggerd_trigger_dump(pid_t tid, DebuggerdDumpType dump_type, unsigned int
|
|||
if (timeout_ms <= 0) {
|
||||
remaining_ms = -1;
|
||||
} else if (remaining_ms < 0) {
|
||||
log_error(output_fd, 0, "timeout expired");
|
||||
log_error(output_fd, 0, "timeout expired before poll");
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
@ -271,7 +271,7 @@ bool debuggerd_trigger_dump(pid_t tid, DebuggerdDumpType dump_type, unsigned int
|
|||
return false;
|
||||
}
|
||||
} else if (rc == 0) {
|
||||
log_error(output_fd, 0, "timeout expired");
|
||||
log_error(output_fd, 0, "poll timeout expired");
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -662,6 +662,15 @@ int main(int argc, char** argv) {
|
|||
info.pac_enabled_keys = -1;
|
||||
}
|
||||
|
||||
#if defined(__aarch64__)
|
||||
struct iovec tls_iov = {
|
||||
&info.tls,
|
||||
sizeof(info.tls),
|
||||
};
|
||||
if (ptrace(PTRACE_GETREGSET, thread, NT_ARM_TLS, reinterpret_cast<void*>(&tls_iov)) == -1) {
|
||||
info.tls = 0;
|
||||
}
|
||||
#endif
|
||||
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_crash);
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@
|
|||
#include <dlfcn.h>
|
||||
#include <err.h>
|
||||
#include <fcntl.h>
|
||||
#include <inttypes.h>
|
||||
#include <linux/prctl.h>
|
||||
#include <malloc.h>
|
||||
#include <pthread.h>
|
||||
|
|
@ -69,7 +70,6 @@
|
|||
#include "crash_test.h"
|
||||
#include "debuggerd/handler.h"
|
||||
#include "gtest/gtest.h"
|
||||
#include "libdebuggerd/utility.h"
|
||||
#include "protocol.h"
|
||||
#include "tombstoned/tombstoned.h"
|
||||
#include "util.h"
|
||||
|
|
@ -741,6 +741,8 @@ TEST_F(CrasherTest, mte_multiple_causes) {
|
|||
}
|
||||
|
||||
#if defined(__aarch64__)
|
||||
constexpr size_t kTagGranuleSize = 16;
|
||||
|
||||
static uintptr_t CreateTagMapping() {
|
||||
// Some of the MTE tag dump tests assert that there is an inaccessible page to the left and right
|
||||
// of the PROT_MTE page, so map three pages and set the two guard pages to PROT_NONE.
|
||||
|
|
@ -1771,6 +1773,75 @@ TEST_F(CrasherTest, seccomp_crash_logcat) {
|
|||
AssertDeath(SIGABRT);
|
||||
}
|
||||
|
||||
extern "C" void malloc_enable();
|
||||
extern "C" void malloc_disable();
|
||||
|
||||
TEST_F(CrasherTest, seccomp_tombstone_no_allocation) {
|
||||
int intercept_result;
|
||||
unique_fd output_fd;
|
||||
|
||||
static const auto dump_type = kDebuggerdTombstone;
|
||||
StartProcess(
|
||||
[]() {
|
||||
std::thread a(foo);
|
||||
std::thread b(bar);
|
||||
|
||||
std::this_thread::sleep_for(100ms);
|
||||
|
||||
// Disable allocations to verify that nothing in the fallback
|
||||
// signal handler does an allocation.
|
||||
malloc_disable();
|
||||
raise_debugger_signal(dump_type);
|
||||
_exit(0);
|
||||
},
|
||||
&seccomp_fork);
|
||||
|
||||
StartIntercept(&output_fd, dump_type);
|
||||
FinishCrasher();
|
||||
AssertDeath(0);
|
||||
FinishIntercept(&intercept_result);
|
||||
ASSERT_EQ(1, intercept_result) << "tombstoned reported failure";
|
||||
|
||||
std::string result;
|
||||
ConsumeFd(std::move(output_fd), &result);
|
||||
ASSERT_BACKTRACE_FRAME(result, "raise_debugger_signal");
|
||||
ASSERT_BACKTRACE_FRAME(result, "foo");
|
||||
ASSERT_BACKTRACE_FRAME(result, "bar");
|
||||
}
|
||||
|
||||
TEST_F(CrasherTest, seccomp_backtrace_no_allocation) {
|
||||
int intercept_result;
|
||||
unique_fd output_fd;
|
||||
|
||||
static const auto dump_type = kDebuggerdNativeBacktrace;
|
||||
StartProcess(
|
||||
[]() {
|
||||
std::thread a(foo);
|
||||
std::thread b(bar);
|
||||
|
||||
std::this_thread::sleep_for(100ms);
|
||||
|
||||
// Disable allocations to verify that nothing in the fallback
|
||||
// signal handler does an allocation.
|
||||
malloc_disable();
|
||||
raise_debugger_signal(dump_type);
|
||||
_exit(0);
|
||||
},
|
||||
&seccomp_fork);
|
||||
|
||||
StartIntercept(&output_fd, dump_type);
|
||||
FinishCrasher();
|
||||
AssertDeath(0);
|
||||
FinishIntercept(&intercept_result);
|
||||
ASSERT_EQ(1, intercept_result) << "tombstoned reported failure";
|
||||
|
||||
std::string result;
|
||||
ConsumeFd(std::move(output_fd), &result);
|
||||
ASSERT_BACKTRACE_FRAME(result, "raise_debugger_signal");
|
||||
ASSERT_BACKTRACE_FRAME(result, "foo");
|
||||
ASSERT_BACKTRACE_FRAME(result, "bar");
|
||||
}
|
||||
|
||||
TEST_F(CrasherTest, competing_tracer) {
|
||||
int intercept_result;
|
||||
unique_fd output_fd;
|
||||
|
|
|
|||
|
|
@ -48,50 +48,69 @@ using android::base::unique_fd;
|
|||
extern "C" bool __linker_enable_fallback_allocator();
|
||||
extern "C" void __linker_disable_fallback_allocator();
|
||||
|
||||
// This is incredibly sketchy to do inside of a signal handler, especially when libbacktrace
|
||||
// uses the C++ standard library throughout, but this code runs in the linker, so we'll be using
|
||||
// the linker's malloc instead of the libc one. Switch it out for a replacement, just in case.
|
||||
//
|
||||
// This isn't the default method of dumping because it can fail in cases such as address space
|
||||
// exhaustion.
|
||||
// This file implements a fallback path for processes that do not allow the
|
||||
// normal fork and exec of crash_dump to handle crashes/unwinds.
|
||||
// The issue is that all of this happens from within a signal handler, which
|
||||
// can cause problems since this code uses the linker allocator which is not
|
||||
// thread safe. In order to avoid any problems allocating, the code calls
|
||||
// a function to switch to use a fallback allocator in the linker that will
|
||||
// only be used for the current thread. All of the libunwindstack code does
|
||||
// allocations using C++ stl, but should be fine since the code runs in the
|
||||
// linker and should use the fallback handler.
|
||||
|
||||
// This method can still fail if the virtual space is exhausted on a 32 bit
|
||||
// process or mmap failing due to hitting the maximum number of maps (65535
|
||||
// total maps) on a 64 bit process.
|
||||
|
||||
// Class to handle automatically turning on and off the fallback allocator.
|
||||
class ScopedUseFallbackAllocator {
|
||||
public:
|
||||
ScopedUseFallbackAllocator() { Enable(); }
|
||||
|
||||
~ScopedUseFallbackAllocator() { Disable(); }
|
||||
|
||||
bool Enable() {
|
||||
if (!enabled_) {
|
||||
enabled_ = __linker_enable_fallback_allocator();
|
||||
if (!enabled_) {
|
||||
async_safe_format_log(ANDROID_LOG_ERROR, "libc",
|
||||
"Unable to enable fallback allocator, already in use.");
|
||||
}
|
||||
}
|
||||
return enabled_;
|
||||
}
|
||||
|
||||
void Disable() {
|
||||
if (enabled_) {
|
||||
__linker_disable_fallback_allocator();
|
||||
enabled_ = false;
|
||||
}
|
||||
}
|
||||
|
||||
bool enabled() { return enabled_; }
|
||||
|
||||
private:
|
||||
bool enabled_ = false;
|
||||
};
|
||||
|
||||
static void debuggerd_fallback_trace(int output_fd, ucontext_t* ucontext) {
|
||||
if (!__linker_enable_fallback_allocator()) {
|
||||
async_safe_format_log(ANDROID_LOG_ERROR, "libc", "fallback allocator already in use");
|
||||
return;
|
||||
}
|
||||
std::unique_ptr<unwindstack::Regs> regs;
|
||||
|
||||
{
|
||||
std::unique_ptr<unwindstack::Regs> regs;
|
||||
ThreadInfo thread;
|
||||
thread.pid = getpid();
|
||||
thread.tid = gettid();
|
||||
thread.thread_name = get_thread_name(gettid());
|
||||
thread.registers.reset(
|
||||
unwindstack::Regs::CreateFromUcontext(unwindstack::Regs::CurrentArch(), ucontext));
|
||||
|
||||
ThreadInfo thread;
|
||||
thread.pid = getpid();
|
||||
thread.tid = gettid();
|
||||
thread.thread_name = get_thread_name(gettid());
|
||||
thread.registers.reset(
|
||||
unwindstack::Regs::CreateFromUcontext(unwindstack::Regs::CurrentArch(), ucontext));
|
||||
|
||||
// Do not use the thread cache here because it will call pthread_key_create
|
||||
// which doesn't work in linker code. See b/189803009.
|
||||
// Use a normal cached object because the thread is stopped, and there
|
||||
// is no chance of data changing between reads.
|
||||
auto process_memory = unwindstack::Memory::CreateProcessMemoryCached(getpid());
|
||||
// TODO: Create this once and store it in a global?
|
||||
unwindstack::AndroidLocalUnwinder unwinder(process_memory);
|
||||
dump_backtrace_thread(output_fd, &unwinder, thread);
|
||||
}
|
||||
__linker_disable_fallback_allocator();
|
||||
}
|
||||
|
||||
static void debuggerd_fallback_tombstone(int output_fd, int proto_fd, ucontext_t* ucontext,
|
||||
siginfo_t* siginfo, void* abort_message) {
|
||||
if (!__linker_enable_fallback_allocator()) {
|
||||
async_safe_format_log(ANDROID_LOG_ERROR, "libc", "fallback allocator already in use");
|
||||
return;
|
||||
}
|
||||
|
||||
engrave_tombstone_ucontext(output_fd, proto_fd, reinterpret_cast<uintptr_t>(abort_message),
|
||||
siginfo, ucontext);
|
||||
__linker_disable_fallback_allocator();
|
||||
// Do not use the thread cache here because it will call pthread_key_create
|
||||
// which doesn't work in linker code. See b/189803009.
|
||||
// Use a normal cached object because the thread is stopped, and there
|
||||
// is no chance of data changing between reads.
|
||||
auto process_memory = unwindstack::Memory::CreateProcessMemoryCached(getpid());
|
||||
// TODO: Create this once and store it in a global?
|
||||
unwindstack::AndroidLocalUnwinder unwinder(process_memory);
|
||||
dump_backtrace_thread(output_fd, &unwinder, thread);
|
||||
}
|
||||
|
||||
static bool forward_output(int src_fd, int dst_fd, pid_t expected_tid) {
|
||||
|
|
@ -154,6 +173,11 @@ static std::pair<pid_t, int> unpack_thread_fd(uint64_t value) {
|
|||
}
|
||||
|
||||
static void trace_handler(siginfo_t* info, ucontext_t* ucontext) {
|
||||
ScopedUseFallbackAllocator allocator;
|
||||
if (!allocator.enabled()) {
|
||||
return;
|
||||
}
|
||||
|
||||
static std::atomic<uint64_t> trace_output(pack_thread_fd(-1, -1));
|
||||
|
||||
if (info->si_value.sival_ptr == kDebuggerdFallbackSivalPtrRequestDump) {
|
||||
|
|
@ -181,6 +205,11 @@ static void trace_handler(siginfo_t* info, ucontext_t* ucontext) {
|
|||
async_safe_format_log(ANDROID_LOG_ERROR, "libc", "failed to write to output fd");
|
||||
}
|
||||
|
||||
// Stop using the fallback allocator before the close. This will prevent
|
||||
// a race condition where the thread backtracing all of the threads tries
|
||||
// to re-acquire the fallback allocator.
|
||||
allocator.Disable();
|
||||
|
||||
close(fd);
|
||||
return;
|
||||
}
|
||||
|
|
@ -210,10 +239,15 @@ static void trace_handler(siginfo_t* info, ucontext_t* ucontext) {
|
|||
|
||||
// Send a signal to all of our siblings, asking them to dump their stack.
|
||||
pid_t current_tid = gettid();
|
||||
if (!iterate_tids(current_tid, [&output_fd, ¤t_tid](pid_t tid) {
|
||||
if (!iterate_tids(current_tid, [&allocator, &output_fd, ¤t_tid](pid_t tid) {
|
||||
if (current_tid == tid) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!allocator.enabled()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Use a pipe, to be able to detect situations where the thread gracefully exits before
|
||||
// receiving our signal.
|
||||
unique_fd pipe_read, pipe_write;
|
||||
|
|
@ -233,22 +267,29 @@ static void trace_handler(siginfo_t* info, ucontext_t* ucontext) {
|
|||
return;
|
||||
}
|
||||
|
||||
// Disable our use of the fallback allocator while the target thread
|
||||
// is getting the backtrace.
|
||||
allocator.Disable();
|
||||
|
||||
siginfo_t siginfo = {};
|
||||
siginfo.si_code = SI_QUEUE;
|
||||
siginfo.si_value.sival_ptr = kDebuggerdFallbackSivalPtrRequestDump;
|
||||
siginfo.si_pid = getpid();
|
||||
siginfo.si_uid = getuid();
|
||||
|
||||
if (syscall(__NR_rt_tgsigqueueinfo, getpid(), tid, BIONIC_SIGNAL_DEBUGGER, &siginfo) != 0) {
|
||||
if (syscall(__NR_rt_tgsigqueueinfo, getpid(), tid, BIONIC_SIGNAL_DEBUGGER, &siginfo) == 0) {
|
||||
if (!forward_output(pipe_read.get(), output_fd.get(), tid)) {
|
||||
async_safe_format_log(ANDROID_LOG_ERROR, "libc",
|
||||
"timeout expired while waiting for thread %d to dump", tid);
|
||||
}
|
||||
} else {
|
||||
async_safe_format_log(ANDROID_LOG_ERROR, "libc", "failed to send trace signal to %d: %s",
|
||||
tid, strerror(errno));
|
||||
return;
|
||||
}
|
||||
|
||||
bool success = forward_output(pipe_read.get(), output_fd.get(), tid);
|
||||
if (!success) {
|
||||
async_safe_format_log(ANDROID_LOG_ERROR, "libc",
|
||||
"timeout expired while waiting for thread %d to dump", tid);
|
||||
// The thread should be finished now, so try and re-enable the fallback allocator.
|
||||
if (!allocator.Enable()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Regardless of whether the poll succeeds, check to see if the thread took fd ownership.
|
||||
|
|
@ -260,14 +301,15 @@ static void trace_handler(siginfo_t* info, ucontext_t* ucontext) {
|
|||
close(fd);
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
})) {
|
||||
async_safe_format_log(ANDROID_LOG_ERROR, "libc", "failed to open /proc/%d/task: %s",
|
||||
current_tid, strerror(errno));
|
||||
}
|
||||
|
||||
dump_backtrace_footer(output_fd.get());
|
||||
if (allocator.enabled()) {
|
||||
dump_backtrace_footer(output_fd.get());
|
||||
}
|
||||
|
||||
tombstoned_notify_completion(tombstone_socket.get());
|
||||
}
|
||||
|
||||
|
|
@ -295,7 +337,13 @@ static void crash_handler(siginfo_t* info, ucontext_t* ucontext, void* abort_mes
|
|||
unique_fd tombstone_socket, output_fd, proto_fd;
|
||||
bool tombstoned_connected = tombstoned_connect(getpid(), &tombstone_socket, &output_fd, &proto_fd,
|
||||
kDebuggerdTombstoneProto);
|
||||
debuggerd_fallback_tombstone(output_fd.get(), proto_fd.get(), ucontext, info, abort_message);
|
||||
{
|
||||
ScopedUseFallbackAllocator allocator;
|
||||
if (allocator.enabled()) {
|
||||
engrave_tombstone_ucontext(output_fd.get(), proto_fd.get(),
|
||||
reinterpret_cast<uintptr_t>(abort_message), info, ucontext);
|
||||
}
|
||||
}
|
||||
if (tombstoned_connected) {
|
||||
tombstoned_notify_completion(tombstone_socket.get());
|
||||
}
|
||||
|
|
|
|||
|
|
@ -36,10 +36,12 @@
|
|||
#include <sys/uio.h>
|
||||
#include <sys/un.h>
|
||||
#include <sys/wait.h>
|
||||
#include <time.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <android-base/macros.h>
|
||||
#include <android-base/parsebool.h>
|
||||
#include <android-base/parseint.h>
|
||||
#include <android-base/properties.h>
|
||||
#include <android-base/unique_fd.h>
|
||||
#include <async_safe/log.h>
|
||||
|
|
@ -115,6 +117,59 @@ static bool is_permissive_mte() {
|
|||
(permissive_env && ParseBool(permissive_env) == ParseBoolResult::kTrue);
|
||||
}
|
||||
|
||||
static bool parse_uint_with_error_reporting(const char* s, const char* name, int* v) {
|
||||
if (android::base::ParseInt(s, v) && *v >= 0) {
|
||||
return true;
|
||||
}
|
||||
async_safe_format_log(ANDROID_LOG_ERROR, "libc", "invalid %s: %s", name, s);
|
||||
return false;
|
||||
}
|
||||
|
||||
// We cannot use base::GetIntProperty, because that internally uses
|
||||
// std::string, which allocates.
|
||||
static bool property_parse_int(const char* name, int* out) {
|
||||
const prop_info* pi = __system_property_find(name);
|
||||
if (!pi) return false;
|
||||
struct cookie_t {
|
||||
int* out;
|
||||
bool empty;
|
||||
} cookie{out, true};
|
||||
__system_property_read_callback(
|
||||
pi,
|
||||
[](void* raw_cookie, const char* name, const char* value, uint32_t) {
|
||||
// Property is set to empty value, ignoring.
|
||||
if (!*value) return;
|
||||
cookie_t* cookie = reinterpret_cast<cookie_t*>(raw_cookie);
|
||||
if (parse_uint_with_error_reporting(value, name, cookie->out)) cookie->empty = false;
|
||||
},
|
||||
&cookie);
|
||||
return !cookie.empty;
|
||||
}
|
||||
|
||||
static int permissive_mte_renable_timer() {
|
||||
if (char* env = getenv("MTE_PERMISSIVE_REENABLE_TIME_CPUMS")) {
|
||||
int v;
|
||||
if (parse_uint_with_error_reporting(env, "MTE_PERMISSIVE_REENABLE_TIME_CPUMS", &v)) return v;
|
||||
}
|
||||
|
||||
char process_sysprop_name[512];
|
||||
async_safe_format_buffer(process_sysprop_name, sizeof(process_sysprop_name),
|
||||
"persist.sys.mte.permissive_reenable_timer.process.%s", getprogname());
|
||||
int v;
|
||||
if (property_parse_int(process_sysprop_name, &v)) return v;
|
||||
if (property_parse_int("persist.sys.mte.permissive_reenable_timer.default", &v)) return v;
|
||||
char process_deviceconf_sysprop_name[512];
|
||||
async_safe_format_buffer(
|
||||
process_deviceconf_sysprop_name, sizeof(process_deviceconf_sysprop_name),
|
||||
"persist.device_config.memory_safety_native.permissive_reenable_timer.process.%s",
|
||||
getprogname());
|
||||
if (property_parse_int(process_deviceconf_sysprop_name, &v)) return v;
|
||||
if (property_parse_int(
|
||||
"persist.device_config.memory_safety_native.permissive_reenable_timer.default", &v))
|
||||
return v;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline void futex_wait(volatile void* ftx, int value) {
|
||||
syscall(__NR_futex, ftx, FUTEX_WAIT, value, nullptr, nullptr, 0);
|
||||
}
|
||||
|
|
@ -599,12 +654,40 @@ static void debuggerd_signal_handler(int signal_number, siginfo_t* info, void* c
|
|||
if (tagged_addr_ctrl < 0) {
|
||||
fatal_errno("failed to PR_GET_TAGGED_ADDR_CTRL");
|
||||
}
|
||||
int previous = tagged_addr_ctrl & PR_MTE_TCF_MASK;
|
||||
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.");
|
||||
if (int reenable_timer = permissive_mte_renable_timer()) {
|
||||
async_safe_format_log(ANDROID_LOG_ERROR, "libc",
|
||||
"MTE ERROR DETECTED BUT RUNNING IN PERMISSIVE MODE. CONTINUING WITH "
|
||||
"MTE DISABLED FOR %d MS OF CPU TIME.",
|
||||
reenable_timer);
|
||||
timer_t timerid{};
|
||||
struct sigevent sev {};
|
||||
sev.sigev_signo = BIONIC_ENABLE_MTE;
|
||||
sev.sigev_notify = SIGEV_THREAD_ID;
|
||||
sev.sigev_value.sival_int = previous;
|
||||
sev.sigev_notify_thread_id = __gettid();
|
||||
// This MUST be CLOCK_THREAD_CPUTIME_ID. If we used CLOCK_MONOTONIC we could get stuck
|
||||
// in an endless loop of re-running the same instruction, calling this signal handler,
|
||||
// and re-enabling MTE before we had a chance to re-run the instruction.
|
||||
if (timer_create(CLOCK_THREAD_CPUTIME_ID, &sev, &timerid) == -1) {
|
||||
fatal_errno("timer_create() failed");
|
||||
}
|
||||
struct itimerspec its {};
|
||||
its.it_value.tv_sec = reenable_timer / 1000;
|
||||
its.it_value.tv_nsec = (reenable_timer % 1000) * 1000000;
|
||||
|
||||
if (timer_settime(timerid, 0, &its, nullptr) == -1) {
|
||||
fatal_errno("timer_settime() failed");
|
||||
}
|
||||
} else {
|
||||
async_safe_format_log(
|
||||
ANDROID_LOG_ERROR, "libc",
|
||||
"MTE ERROR DETECTED BUT RUNNING IN PERMISSIVE MODE. CONTINUING WITH MTE DISABLED.");
|
||||
}
|
||||
pthread_mutex_unlock(&crash_mutex);
|
||||
}
|
||||
|
||||
|
|
@ -755,7 +838,6 @@ void debuggerd_init(debuggerd_callbacks_t* callbacks) {
|
|||
// Use the alternate signal stack if available so we can catch stack overflows.
|
||||
action.sa_flags |= SA_ONSTACK;
|
||||
|
||||
#define SA_EXPOSE_TAGBITS 0x00000800
|
||||
// Request that the kernel set tag bits in the fault address. This is necessary for diagnosing MTE
|
||||
// faults.
|
||||
action.sa_flags |= SA_EXPOSE_TAGBITS;
|
||||
|
|
|
|||
|
|
@ -73,5 +73,8 @@ bool tombstone_proto_to_text(
|
|||
|
||||
void fill_in_backtrace_frame(BacktraceFrame* f, const unwindstack::FrameData& frame);
|
||||
void set_human_readable_cause(Cause* cause, uint64_t fault_addr);
|
||||
|
||||
#if defined(__aarch64__)
|
||||
void dump_stack_history(unwindstack::AndroidUnwinder* unwinder, uintptr_t target_tls,
|
||||
StackHistoryBuffer& shb_ob, bool nounwind = false);
|
||||
#endif
|
||||
#endif // _DEBUGGERD_TOMBSTONE_H
|
||||
|
|
|
|||
|
|
@ -41,6 +41,9 @@ struct ThreadInfo {
|
|||
siginfo_t* siginfo = nullptr;
|
||||
|
||||
std::unique_ptr<unwindstack::Regs> guest_registers;
|
||||
#if defined(__aarch64__)
|
||||
uintptr_t tls; // This is currently used for MTE stack history buffer.
|
||||
#endif
|
||||
};
|
||||
|
||||
// This struct is written into a pipe from inside the crashing process.
|
||||
|
|
|
|||
|
|
@ -1,65 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2015 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef _DEBUGGERD_TEST_HOST_SIGNAL_FIXUP_H
|
||||
#define _DEBUGGERD_TEST_HOST_SIGNAL_FIXUP_H
|
||||
|
||||
#include <signal.h>
|
||||
|
||||
#if !defined(__BIONIC__)
|
||||
|
||||
// In order to compile parts of debuggerd for the host, we need to
|
||||
// define these values.
|
||||
|
||||
#if !defined(NSIGILL)
|
||||
#define NSIGILL ILL_BADSTK
|
||||
#endif
|
||||
|
||||
#if !defined(BUS_MCEERR_AR)
|
||||
#define BUS_MCEERR_AR 4
|
||||
#endif
|
||||
#if !defined(BUS_MCEERR_AO)
|
||||
#define BUS_MCEERR_AO 5
|
||||
#endif
|
||||
#if !defined(NSIGBUS)
|
||||
#define NSIGBUS BUS_MCEERR_AO
|
||||
#endif
|
||||
|
||||
#if !defined(NSIGFPE)
|
||||
#define NSIGFPE FPE_FLTSUB
|
||||
#endif
|
||||
|
||||
#if !defined(NSIGSEGV)
|
||||
#define NSIGSEGV SEGV_ACCERR
|
||||
#endif
|
||||
|
||||
#if !defined(TRAP_BRANCH)
|
||||
#define TRAP_BRANCH 3
|
||||
#endif
|
||||
#if !defined(TRAP_HWBKPT)
|
||||
#define TRAP_HWBKPT 4
|
||||
#endif
|
||||
#if !defined(NSIGTRAP)
|
||||
#define NSIGTRAP TRAP_HWBKPT
|
||||
#endif
|
||||
|
||||
#if !defined(SI_DETHREAD)
|
||||
#define SI_DETHREAD (-7)
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
#endif // _DEBUGGERD_TEST_HOST_SIGNAL_FIXUP_H
|
||||
157
debuggerd/libdebuggerd/test/mte_stack_record_test.cpp
Normal file
157
debuggerd/libdebuggerd/test/mte_stack_record_test.cpp
Normal file
|
|
@ -0,0 +1,157 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
#if defined(__aarch64__)
|
||||
|
||||
#include <stdint.h>
|
||||
#include <sys/mman.h>
|
||||
|
||||
#include <optional>
|
||||
|
||||
#include "bionic/mte.h"
|
||||
#include "bionic/page.h"
|
||||
#include "unwindstack/AndroidUnwinder.h"
|
||||
#include "unwindstack/Memory.h"
|
||||
|
||||
#include <android-base/test_utils.h>
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
#include "libdebuggerd/tombstone.h"
|
||||
|
||||
struct ScopedUnmap {
|
||||
void* ptr;
|
||||
size_t size;
|
||||
~ScopedUnmap() { munmap(ptr, size); }
|
||||
};
|
||||
|
||||
class MteStackHistoryTest : public ::testing::TestWithParam<int> {
|
||||
void SetUp() override {
|
||||
#if !defined(__aarch64__)
|
||||
GTEST_SKIP();
|
||||
#endif
|
||||
SKIP_WITH_HWASAN;
|
||||
unwinder.emplace();
|
||||
unwindstack::ErrorData E;
|
||||
ASSERT_TRUE(unwinder->Initialize(E));
|
||||
}
|
||||
|
||||
protected:
|
||||
// std::optional so we don't construct it for the SKIP cases.
|
||||
std::optional<unwindstack::AndroidLocalUnwinder> unwinder;
|
||||
};
|
||||
|
||||
TEST(MteStackHistoryUnwindTest, TestOne) {
|
||||
#if !defined(__aarch64__)
|
||||
GTEST_SKIP();
|
||||
#endif
|
||||
SKIP_WITH_HWASAN;
|
||||
size_t size = stack_mte_ringbuffer_size(0);
|
||||
char* data = static_cast<char*>(stack_mte_ringbuffer_allocate(0, nullptr));
|
||||
ScopedUnmap s{data, size};
|
||||
|
||||
uintptr_t taggedfp = (1ULL << 56) | 1;
|
||||
uintptr_t pc = reinterpret_cast<uintptr_t>(&memcpy);
|
||||
memcpy(data, &pc, sizeof(pc));
|
||||
memcpy(data + 8, &taggedfp, sizeof(taggedfp));
|
||||
|
||||
// The MTE TLS is at TLS - 3, so we allocate 3 placeholders.
|
||||
void* tls[4] = {data + 16};
|
||||
|
||||
unwindstack::AndroidLocalUnwinder unwinder;
|
||||
unwindstack::ErrorData E;
|
||||
unwinder.Initialize(E);
|
||||
StackHistoryBuffer shb;
|
||||
dump_stack_history(&unwinder, reinterpret_cast<uintptr_t>(&tls[3]), shb, /* nounwind= */ false);
|
||||
ASSERT_EQ(shb.entries_size(), 1);
|
||||
const StackHistoryBufferEntry& e = shb.entries(0);
|
||||
EXPECT_EQ(e.addr().pc(), pc);
|
||||
EXPECT_EQ(e.addr().file_name(), "/apex/com.android.runtime/lib64/bionic/libc.so");
|
||||
EXPECT_EQ(e.fp(), 1ULL);
|
||||
EXPECT_EQ(e.tag(), 1ULL);
|
||||
}
|
||||
|
||||
TEST_P(MteStackHistoryTest, TestEmpty) {
|
||||
int size_cls = GetParam();
|
||||
size_t size = stack_mte_ringbuffer_size(size_cls);
|
||||
void* data = stack_mte_ringbuffer_allocate(size_cls, nullptr);
|
||||
ScopedUnmap s{data, size};
|
||||
// The MTE TLS is at TLS - 3, so we allocate 3 placeholders.
|
||||
void* tls[4] = {data};
|
||||
|
||||
StackHistoryBuffer shb;
|
||||
dump_stack_history(&*unwinder, reinterpret_cast<uintptr_t>(&tls[3]), shb, /* nounwind= */ true);
|
||||
EXPECT_EQ(shb.entries_size(), 0);
|
||||
}
|
||||
|
||||
TEST_P(MteStackHistoryTest, TestFull) {
|
||||
int size_cls = GetParam();
|
||||
size_t size = stack_mte_ringbuffer_size(size_cls);
|
||||
char* data = static_cast<char*>(stack_mte_ringbuffer_allocate(size_cls, nullptr));
|
||||
ScopedUnmap s{data, size};
|
||||
uintptr_t itr = 1;
|
||||
for (char* d = data; d < &data[size]; d += 16) {
|
||||
uintptr_t taggedfp = ((itr & 15) << 56) | itr;
|
||||
uintptr_t pc = itr;
|
||||
memcpy(d, &pc, sizeof(pc));
|
||||
memcpy(d + 8, &taggedfp, sizeof(taggedfp));
|
||||
++itr;
|
||||
}
|
||||
// The MTE TLS is at TLS - 3, so we allocate 3 placeholders.
|
||||
// Because the buffer is full, and we point at one past the last inserted element,
|
||||
// due to wrap-around we point at the beginning of the buffer.
|
||||
void* tls[4] = {data};
|
||||
|
||||
StackHistoryBuffer shb;
|
||||
dump_stack_history(&*unwinder, reinterpret_cast<uintptr_t>(&tls[3]), shb, /* nounwind= */ true);
|
||||
EXPECT_EQ(static_cast<size_t>(shb.entries_size()), size / 16);
|
||||
for (const auto& entry : shb.entries()) {
|
||||
EXPECT_EQ(entry.addr().pc(), --itr);
|
||||
EXPECT_EQ(entry.addr().pc(), entry.fp());
|
||||
EXPECT_EQ(entry.addr().pc() & 15, entry.tag());
|
||||
}
|
||||
}
|
||||
|
||||
TEST_P(MteStackHistoryTest, TestHalfFull) {
|
||||
int size_cls = GetParam();
|
||||
size_t size = stack_mte_ringbuffer_size(size_cls);
|
||||
size_t half_size = size / 2;
|
||||
|
||||
char* data = static_cast<char*>(stack_mte_ringbuffer_allocate(size_cls, nullptr));
|
||||
ScopedUnmap s{data, size};
|
||||
|
||||
uintptr_t itr = 1;
|
||||
for (char* d = data; d < &data[half_size]; d += 16) {
|
||||
uintptr_t taggedfp = ((itr & 15) << 56) | itr;
|
||||
uintptr_t pc = itr;
|
||||
memcpy(d, &pc, sizeof(pc));
|
||||
memcpy(d + 8, &taggedfp, sizeof(taggedfp));
|
||||
++itr;
|
||||
}
|
||||
// The MTE TLS is at TLS - 3, so we allocate 3 placeholders.
|
||||
void* tls[4] = {&data[half_size]};
|
||||
|
||||
StackHistoryBuffer shb;
|
||||
dump_stack_history(&*unwinder, reinterpret_cast<uintptr_t>(&tls[3]), shb, /* nounwind= */ true);
|
||||
EXPECT_EQ(static_cast<size_t>(shb.entries_size()), half_size / 16);
|
||||
for (const auto& entry : shb.entries()) {
|
||||
EXPECT_EQ(entry.addr().pc(), --itr);
|
||||
EXPECT_EQ(entry.addr().pc(), entry.fp());
|
||||
EXPECT_EQ(entry.addr().pc() & 15, entry.tag());
|
||||
}
|
||||
}
|
||||
|
||||
INSTANTIATE_TEST_SUITE_P(MteStackHistoryTestInstance, MteStackHistoryTest, testing::Range(0, 8));
|
||||
|
||||
#endif
|
||||
|
|
@ -134,3 +134,31 @@ TEST_F(TombstoneProtoToTextTest, crash_detail_bytes) {
|
|||
ProtoToString();
|
||||
EXPECT_MATCH(text_, R"(CRASH_DETAIL_NAME: 'helloworld\\1\\255\\3')");
|
||||
}
|
||||
|
||||
TEST_F(TombstoneProtoToTextTest, stack_record) {
|
||||
auto* cause = tombstone_->add_causes();
|
||||
cause->set_human_readable("stack tag-mismatch on thread 123");
|
||||
auto* stack = tombstone_->mutable_stack_history_buffer();
|
||||
stack->set_tid(123);
|
||||
{
|
||||
auto* shb_entry = stack->add_entries();
|
||||
shb_entry->set_fp(0x1);
|
||||
shb_entry->set_tag(0xb);
|
||||
auto* addr = shb_entry->mutable_addr();
|
||||
addr->set_rel_pc(0x567);
|
||||
addr->set_file_name("foo.so");
|
||||
addr->set_build_id("ABC123");
|
||||
}
|
||||
{
|
||||
auto* shb_entry = stack->add_entries();
|
||||
shb_entry->set_fp(0x2);
|
||||
shb_entry->set_tag(0xc);
|
||||
auto* addr = shb_entry->mutable_addr();
|
||||
addr->set_rel_pc(0x678);
|
||||
addr->set_file_name("bar.so");
|
||||
}
|
||||
ProtoToString();
|
||||
EXPECT_MATCH(text_, "stack tag-mismatch on thread 123");
|
||||
EXPECT_MATCH(text_, "stack_record fp:0x1 tag:0xb pc:foo\\.so\\+0x567 \\(BuildId: ABC123\\)");
|
||||
EXPECT_MATCH(text_, "stack_record fp:0x2 tag:0xc pc:bar\\.so\\+0x678");
|
||||
}
|
||||
|
|
|
|||
|
|
@ -27,6 +27,7 @@
|
|||
#include <inttypes.h>
|
||||
#include <signal.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/mman.h>
|
||||
|
|
@ -49,9 +50,11 @@
|
|||
|
||||
#include <android/log.h>
|
||||
#include <android/set_abort_message.h>
|
||||
#include <bionic/macros.h>
|
||||
#include <bionic/reserved_signals.h>
|
||||
#include <bionic/crash_detail_internal.h>
|
||||
#include <bionic/macros.h>
|
||||
#include <bionic/mte.h>
|
||||
#include <bionic/reserved_signals.h>
|
||||
#include <bionic/tls_defines.h>
|
||||
#include <log/log.h>
|
||||
#include <log/log_read.h>
|
||||
#include <log/logprint.h>
|
||||
|
|
@ -202,8 +205,117 @@ void set_human_readable_cause(Cause* cause, uint64_t fault_addr) {
|
|||
error_type_str, diff, byte_suffix, location_str, heap_object.size(), heap_object.address()));
|
||||
}
|
||||
|
||||
#if defined(__aarch64__)
|
||||
void dump_stack_history(unwindstack::AndroidUnwinder* unwinder, uintptr_t target_tls,
|
||||
StackHistoryBuffer& shb_obj, bool nounwind) {
|
||||
auto process_memory = unwinder->GetProcessMemory();
|
||||
target_tls += sizeof(void*) * TLS_SLOT_STACK_MTE;
|
||||
uintptr_t stack_mte;
|
||||
if (!process_memory->ReadFully(target_tls, &stack_mte, sizeof(stack_mte))) {
|
||||
async_safe_format_log(ANDROID_LOG_ERROR, LOG_TAG,
|
||||
"dump_stack_history: failed to read TLS_SLOT_STACK_MTE: %m");
|
||||
return;
|
||||
}
|
||||
if (stack_mte == 0) {
|
||||
async_safe_format_log(ANDROID_LOG_DEBUG, LOG_TAG,
|
||||
"dump_stack_history: stack history buffer is null");
|
||||
return;
|
||||
}
|
||||
uintptr_t untagged_stack_mte = untag_address(stack_mte);
|
||||
uintptr_t buf_size = stack_mte_ringbuffer_size_from_pointer(stack_mte);
|
||||
if (buf_size == 0) {
|
||||
async_safe_format_log(ANDROID_LOG_ERROR, LOG_TAG, "dump_stack_history: empty size");
|
||||
return;
|
||||
}
|
||||
uintptr_t buf_start = untagged_stack_mte & ~(buf_size - 1ULL);
|
||||
std::vector<char> buf(buf_size);
|
||||
if (!process_memory->ReadFully(buf_start, buf.data(), buf.size())) {
|
||||
async_safe_format_log(ANDROID_LOG_ERROR, LOG_TAG,
|
||||
"dump_stack_history: failed to read stack history: %m");
|
||||
return;
|
||||
}
|
||||
uintptr_t original_off = untagged_stack_mte - buf_start;
|
||||
if (original_off % 16 || original_off > buf_size) {
|
||||
async_safe_format_log(ANDROID_LOG_ERROR, LOG_TAG,
|
||||
"dump_stack_history: invalid offset: %" PRIuPTR, original_off);
|
||||
return;
|
||||
}
|
||||
|
||||
// The original_off is the next slot that would have been written, so the last
|
||||
// slot that was written is the previous one.
|
||||
for (uintptr_t idx = 16; idx <= buf_size; idx += 16) {
|
||||
int64_t off = original_off - idx;
|
||||
if (off < 0) off += buf_size;
|
||||
uintptr_t pc, taggedfp;
|
||||
memcpy(&pc, &(buf[off]), sizeof(pc));
|
||||
memcpy(&taggedfp, &(buf[off + sizeof(pc)]), sizeof(taggedfp));
|
||||
|
||||
if (pc == 0) break;
|
||||
uintptr_t fp = untag_address(taggedfp);
|
||||
uintptr_t tag = taggedfp >> 56;
|
||||
|
||||
unwindstack::FrameData frame_data;
|
||||
|
||||
if (nounwind) {
|
||||
frame_data.pc = pc;
|
||||
} else {
|
||||
// +4 is to counteract the "pc adjustment" in BuildFrameFromPcOnly.
|
||||
// BuildFrameFromPcOnly assumes we are unwinding, so it needs to correct for that
|
||||
// the PC is the return address. That is not the case here.
|
||||
// It doesn't really matter, because either should be in the correct function, but
|
||||
// this is more correct (and consistent with the nounwind case).
|
||||
frame_data = unwinder->BuildFrameFromPcOnly(pc);
|
||||
frame_data.pc += 4;
|
||||
frame_data.rel_pc += 4;
|
||||
}
|
||||
|
||||
StackHistoryBufferEntry* entry = shb_obj.add_entries();
|
||||
fill_in_backtrace_frame(entry->mutable_addr(), frame_data);
|
||||
entry->set_fp(fp);
|
||||
entry->set_tag(tag);
|
||||
}
|
||||
}
|
||||
|
||||
static pid_t get_containing_thread(unwindstack::MapInfo* map_info, pid_t main_tid) {
|
||||
if (map_info == nullptr) return 0;
|
||||
|
||||
std::string name = map_info->name();
|
||||
if (name == "[stack]") {
|
||||
return main_tid;
|
||||
}
|
||||
int tid;
|
||||
if (sscanf(name.c_str(), "[anon:stack_and_tls:%d", &tid) != 1) {
|
||||
return 0;
|
||||
}
|
||||
return tid;
|
||||
}
|
||||
|
||||
static std::optional<std::string> maybe_stack_mte_cause(
|
||||
Tombstone* tombstone, unwindstack::AndroidUnwinder* unwinder, const ThreadInfo& target_thread,
|
||||
[[maybe_unused]] const std::map<pid_t, ThreadInfo>& threads, uint64_t fault_addr) {
|
||||
unwindstack::Maps* maps = unwinder->GetMaps();
|
||||
auto map_info = maps->Find(untag_address(fault_addr));
|
||||
pid_t tid = get_containing_thread(map_info.get(), target_thread.tid);
|
||||
if (!tid) {
|
||||
return std::nullopt;
|
||||
}
|
||||
auto it = threads.find(tid);
|
||||
if (it != threads.end()) {
|
||||
StackHistoryBuffer* shb = tombstone->mutable_stack_history_buffer();
|
||||
shb->set_tid(tid);
|
||||
dump_stack_history(unwinder, it->second.tls, *shb);
|
||||
} else {
|
||||
async_safe_format_log(ANDROID_LOG_ERROR, LOG_TAG,
|
||||
"dump_probable_cause: unknown target thread %d", tid);
|
||||
}
|
||||
return StringPrintf("stack tag-mismatch on thread %u", tid);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
static void dump_probable_cause(Tombstone* tombstone, unwindstack::AndroidUnwinder* unwinder,
|
||||
const ProcessInfo& process_info, const ThreadInfo& target_thread) {
|
||||
const ProcessInfo& process_info, const ThreadInfo& target_thread,
|
||||
[[maybe_unused]] const std::map<pid_t, ThreadInfo>& threads) {
|
||||
#if defined(USE_SCUDO)
|
||||
ScudoCrashData scudo_crash_data(unwinder->GetProcessMemory().get(), process_info);
|
||||
if (scudo_crash_data.CrashIsMine()) {
|
||||
|
|
@ -244,10 +356,21 @@ static void dump_probable_cause(Tombstone* tombstone, unwindstack::AndroidUnwind
|
|||
auto map_info = maps->Find(fault_addr);
|
||||
if (map_info != nullptr && map_info->flags() == PROT_EXEC) {
|
||||
cause = "execute-only (no-read) memory access error; likely due to data in .text.";
|
||||
} else if (fault_addr == target_thread.registers->pc() &&
|
||||
map_info != nullptr && (map_info->flags() & PROT_EXEC) == 0) {
|
||||
cause = "trying to execute non-executable memory.";
|
||||
} else {
|
||||
cause = get_stack_overflow_cause(fault_addr, target_thread.registers->sp(), maps);
|
||||
}
|
||||
} else if (si->si_signo == SIGSYS && si->si_code == SYS_SECCOMP) {
|
||||
}
|
||||
#if defined(__aarch64__) && defined(SEGV_MTESERR)
|
||||
else if (si->si_signo == SIGSEGV && si->si_code == SEGV_MTESERR) {
|
||||
// If this was a heap MTE crash, it would have been handled by scudo. Checking whether it
|
||||
// is a stack one.
|
||||
cause = maybe_stack_mte_cause(tombstone, unwinder, target_thread, threads, fault_addr);
|
||||
}
|
||||
#endif
|
||||
else if (si->si_signo == SIGSYS && si->si_code == SYS_SECCOMP) {
|
||||
cause = StringPrintf("seccomp prevented call to disallowed %s system call %d", ABI_STRING,
|
||||
si->si_syscall);
|
||||
}
|
||||
|
|
@ -788,7 +911,7 @@ void engrave_tombstone_proto(Tombstone* tombstone, unwindstack::AndroidUnwinder*
|
|||
}
|
||||
}
|
||||
|
||||
dump_probable_cause(&result, unwinder, process_info, target_thread);
|
||||
dump_probable_cause(&result, unwinder, process_info, target_thread, threads);
|
||||
|
||||
dump_mappings(&result, unwinder->GetMaps(), unwinder->GetProcessMemory());
|
||||
|
||||
|
|
|
|||
|
|
@ -511,6 +511,19 @@ static void print_main_thread(CallbackType callback, const Tombstone& tombstone,
|
|||
"order of likelihood.");
|
||||
}
|
||||
|
||||
if (tombstone.has_stack_history_buffer()) {
|
||||
for (const StackHistoryBufferEntry& shbe : tombstone.stack_history_buffer().entries()) {
|
||||
std::string stack_record_str = StringPrintf(
|
||||
"stack_record fp:0x%" PRIx64 " tag:0x%" PRIx64 " pc:%s+0x%" PRIx64, shbe.fp(), shbe.tag(),
|
||||
shbe.addr().file_name().c_str(), shbe.addr().rel_pc());
|
||||
if (!shbe.addr().build_id().empty()) {
|
||||
StringAppendF(&stack_record_str, " (BuildId: %s)", shbe.addr().build_id().c_str());
|
||||
}
|
||||
|
||||
CBL("%s", stack_record_str.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
for (const Cause& cause : tombstone.causes()) {
|
||||
if (tombstone.causes_size() > 1) {
|
||||
CBS("");
|
||||
|
|
|
|||
|
|
@ -22,6 +22,21 @@ message CrashDetail {
|
|||
reserved 3 to 999;
|
||||
}
|
||||
|
||||
message StackHistoryBufferEntry {
|
||||
BacktraceFrame addr = 1;
|
||||
uint64 fp = 2;
|
||||
uint64 tag = 3;
|
||||
|
||||
reserved 4 to 999;
|
||||
}
|
||||
|
||||
message StackHistoryBuffer {
|
||||
uint64 tid = 1;
|
||||
repeated StackHistoryBufferEntry entries = 2;
|
||||
|
||||
reserved 3 to 999;
|
||||
}
|
||||
|
||||
message Tombstone {
|
||||
Architecture arch = 1;
|
||||
Architecture guest_arch = 24;
|
||||
|
|
@ -54,7 +69,9 @@ message Tombstone {
|
|||
uint32 page_size = 22;
|
||||
bool has_been_16kb_mode = 23;
|
||||
|
||||
reserved 26 to 999;
|
||||
StackHistoryBuffer stack_history_buffer = 26;
|
||||
|
||||
reserved 27 to 999;
|
||||
}
|
||||
|
||||
enum Architecture {
|
||||
|
|
|
|||
|
|
@ -20,5 +20,14 @@
|
|||
int main(int, char**) {
|
||||
volatile char* f = (char*)malloc(1);
|
||||
printf("%c\n", f[17]);
|
||||
#ifdef __aarch64__
|
||||
if (getenv("MTE_PERMISSIVE_REENABLE_TIME_CPUMS")) {
|
||||
// Burn some cycles because the MTE_PERMISSIVE_REENABLE_TIME_CPUMS is based on CPU clock.
|
||||
for (int i = 0; i < 1000000000; ++i) {
|
||||
asm("isb");
|
||||
}
|
||||
printf("%c\n", f[17]);
|
||||
}
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -97,6 +97,34 @@ public class PermissiveMteTest extends BaseHostJUnit4Test {
|
|||
}
|
||||
assertThat(numberTombstones).isEqualTo(1);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testReenableCrash() throws Exception {
|
||||
CommandResult result =
|
||||
getDevice().executeShellV2Command("MTE_PERMISSIVE=1 MTE_PERMISSIVE_REENABLE_TIME_CPUMS=1 "
|
||||
+ "/data/local/tmp/mte_crash testReenableCrash "
|
||||
+ mUUID);
|
||||
assertThat(result.getExitCode()).isEqualTo(0);
|
||||
int numberTombstones = 0;
|
||||
String[] tombstones = getDevice().getChildren("/data/tombstones");
|
||||
for (String tombstone : tombstones) {
|
||||
if (!tombstone.endsWith(".pb")) {
|
||||
continue;
|
||||
}
|
||||
String tombstonePath = "/data/tombstones/" + tombstone;
|
||||
Tombstone tombstoneProto = parseTombstone(tombstonePath);
|
||||
if (!tombstoneProto.getCommandLineList().stream().anyMatch(x -> x.contains(mUUID))) {
|
||||
continue;
|
||||
}
|
||||
if (!tombstoneProto.getCommandLineList().stream().anyMatch(
|
||||
x -> x.contains("testReenableCrash"))) {
|
||||
continue;
|
||||
}
|
||||
numberTombstones++;
|
||||
}
|
||||
assertThat(numberTombstones).isEqualTo(2);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCrashProperty() throws Exception {
|
||||
String prevValue = getDevice().getProperty("persist.sys.mte.permissive");
|
||||
|
|
|
|||
|
|
@ -158,7 +158,7 @@ class CrashQueue {
|
|||
}
|
||||
}
|
||||
|
||||
return std::move(result);
|
||||
return result;
|
||||
}
|
||||
|
||||
std::optional<CrashOutput> get_output(DebuggerdDumpType dump_type) {
|
||||
|
|
|
|||
|
|
@ -186,6 +186,7 @@ cc_binary {
|
|||
"libprotobuf-cpp-lite",
|
||||
"libsparse",
|
||||
"libutils",
|
||||
"libselinux",
|
||||
],
|
||||
|
||||
static_libs: [
|
||||
|
|
|
|||
|
|
@ -1,25 +0,0 @@
|
|||
# Copyright (C) 2007 Google Inc.
|
||||
#
|
||||
# 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.
|
||||
|
||||
LOCAL_PATH:= $(call my-dir)
|
||||
|
||||
#
|
||||
# Package fastboot-related executables.
|
||||
#
|
||||
|
||||
my_dist_files := $(HOST_OUT_EXECUTABLES)/mke2fs
|
||||
my_dist_files += $(HOST_OUT_EXECUTABLES)/make_f2fs
|
||||
my_dist_files += $(HOST_OUT_EXECUTABLES)/make_f2fs_casefold
|
||||
$(call dist-for-goals,dist_files sdk,$(my_dist_files))
|
||||
my_dist_files :=
|
||||
|
|
@ -90,7 +90,7 @@ std::string TransportSniffer::CreateTrace() {
|
|||
// and be printed as a string, or just a raw byte-buffer
|
||||
const auto msg = [&ret, no_print](const std::vector<char>& buf) {
|
||||
ret += android::base::StringPrintf("(%lu bytes): ", buf.size());
|
||||
std::vector<const char>::iterator iter = buf.end();
|
||||
std::vector<char>::const_iterator iter = buf.end();
|
||||
const unsigned max_chars = 50;
|
||||
if (buf.size() > max_chars) {
|
||||
iter = buf.begin() + max_chars;
|
||||
|
|
|
|||
|
|
@ -163,7 +163,7 @@ struct TestFileHandle {
|
|||
protected:
|
||||
// |rel_path| is the relative path under test data directory.
|
||||
TestFileHandle(const std::filesystem::path& rel_path)
|
||||
: abs_path_(std::move(std::filesystem::path(GetExecutableDirectory()) / rel_path)) {}
|
||||
: abs_path_(std::filesystem::path(GetExecutableDirectory()) / rel_path) {}
|
||||
// Given |read_fd|, the readonly fd on the test file, return an fd that's suitable for client
|
||||
// to use. Implementation is responsible for managing the lifetime of the returned fd.
|
||||
virtual android::base::Result<borrowed_fd> Transform(const std::filesystem::path& abs_path,
|
||||
|
|
|
|||
|
|
@ -33,6 +33,9 @@
|
|||
}
|
||||
],
|
||||
"kernel-presubmit": [
|
||||
{
|
||||
"name": "adb-remount-sh"
|
||||
},
|
||||
{
|
||||
"name": "libdm_test"
|
||||
},
|
||||
|
|
|
|||
|
|
@ -28,6 +28,7 @@
|
|||
#include <sys/ioctl.h>
|
||||
#include <sys/mount.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/statvfs.h>
|
||||
#include <sys/swap.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/utsname.h>
|
||||
|
|
@ -37,10 +38,8 @@
|
|||
|
||||
#include <array>
|
||||
#include <chrono>
|
||||
#include <functional>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <numeric>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <thread>
|
||||
|
|
@ -65,6 +64,7 @@
|
|||
#include <fs_mgr/file_wait.h>
|
||||
#include <fs_mgr_overlayfs.h>
|
||||
#include <fscrypt/fscrypt.h>
|
||||
#include <fstab/fstab.h>
|
||||
#include <libdm/dm.h>
|
||||
#include <libdm/loop_control.h>
|
||||
#include <liblp/metadata_format.h>
|
||||
|
|
@ -81,7 +81,7 @@
|
|||
#define F2FS_FSCK_BIN "/system/bin/fsck.f2fs"
|
||||
#define MKSWAP_BIN "/system/bin/mkswap"
|
||||
#define TUNE2FS_BIN "/system/bin/tune2fs"
|
||||
#define RESIZE2FS_BIN "/system/bin/resize2fs"
|
||||
#define RESIZE2FS_BIN "/system/bin/resize2fs"
|
||||
|
||||
#ifdef __ANDROID_RECOVERY__
|
||||
#define FSCK_LOG_FILE "/dev/null"
|
||||
|
|
@ -141,8 +141,8 @@ enum FsStatFlags {
|
|||
static void log_fs_stat(const std::string& blk_device, int fs_stat) {
|
||||
std::string msg =
|
||||
android::base::StringPrintf("\nfs_stat,%s,0x%x\n", blk_device.c_str(), fs_stat);
|
||||
android::base::unique_fd fd(TEMP_FAILURE_RETRY(open(FSCK_LOG_FILE, O_WRONLY | O_CLOEXEC |
|
||||
O_APPEND | O_CREAT, 0664)));
|
||||
android::base::unique_fd fd(TEMP_FAILURE_RETRY(
|
||||
open(FSCK_LOG_FILE, O_WRONLY | O_CLOEXEC | O_APPEND | O_CREAT, 0664)));
|
||||
if (fd == -1 || !android::base::WriteStringToFd(msg, fd)) {
|
||||
LWARNING << __FUNCTION__ << "() cannot log " << msg;
|
||||
}
|
||||
|
|
@ -219,10 +219,6 @@ static void check_fs(const std::string& blk_device, const std::string& fs_type,
|
|||
*/
|
||||
if (!(*fs_stat & FS_STAT_FULL_MOUNT_FAILED)) { // already tried if full mount failed
|
||||
errno = 0;
|
||||
if (fs_type == "ext4") {
|
||||
// This option is only valid with ext4
|
||||
tmpmnt_opts += ",nomblk_io_submit";
|
||||
}
|
||||
ret = mount(blk_device.c_str(), target.c_str(), fs_type.c_str(), tmpmnt_flags,
|
||||
tmpmnt_opts.c_str());
|
||||
PINFO << __FUNCTION__ << "(): mount(" << blk_device << "," << target << "," << fs_type
|
||||
|
|
@ -600,7 +596,7 @@ static void tune_metadata_csum(const std::string& blk_device, const FstabEntry&
|
|||
|
||||
// Must give `-T now` to prevent last_fsck_time from growing too large,
|
||||
// otherwise, tune2fs won't enable metadata_csum.
|
||||
const char* tune2fs_args[] = {TUNE2FS_BIN, "-O", "metadata_csum,64bit,extent",
|
||||
const char* tune2fs_args[] = {TUNE2FS_BIN, "-O", "metadata_csum,64bit,extent",
|
||||
"-T", "now", blk_device.c_str()};
|
||||
const char* resize2fs_args[] = {RESIZE2FS_BIN, "-b", blk_device.c_str()};
|
||||
|
||||
|
|
@ -936,7 +932,8 @@ static bool should_use_metadata_encryption(const FstabEntry& entry) {
|
|||
// attempted_idx: On return, will indicate which fstab entry
|
||||
// succeeded. In case of failure, it will be the start_idx.
|
||||
// Sets errno to match the 1st mount failure on failure.
|
||||
static bool mount_with_alternatives(Fstab& fstab, int start_idx, int* end_idx, int* attempted_idx) {
|
||||
static bool mount_with_alternatives(Fstab& fstab, int start_idx, bool interrupted, int* end_idx,
|
||||
int* attempted_idx) {
|
||||
unsigned long i;
|
||||
int mount_errno = 0;
|
||||
bool mounted = false;
|
||||
|
|
@ -955,6 +952,13 @@ static bool mount_with_alternatives(Fstab& fstab, int start_idx, int* end_idx, i
|
|||
continue;
|
||||
}
|
||||
|
||||
if (interrupted) {
|
||||
LINFO << __FUNCTION__ << "(): skipping fstab mountpoint=" << fstab[i].mount_point
|
||||
<< " rec[" << i << "].fs_type=" << fstab[i].fs_type
|
||||
<< " (previously interrupted during encryption step)";
|
||||
continue;
|
||||
}
|
||||
|
||||
// fstab[start_idx].blk_device is already updated to /dev/dm-<N> by
|
||||
// AVB related functions. Copy it from start_idx to the current index i.
|
||||
if ((i != start_idx) && fstab[i].fs_mgr_flags.logical &&
|
||||
|
|
@ -1422,19 +1426,59 @@ static bool IsMountPointMounted(const std::string& mount_point) {
|
|||
return GetEntryForMountPoint(&fstab, mount_point) != nullptr;
|
||||
}
|
||||
|
||||
std::string fs_mgr_metadata_encryption_in_progress_file_name(const FstabEntry& entry) {
|
||||
return entry.metadata_key_dir + "/in_progress";
|
||||
}
|
||||
|
||||
bool WasMetadataEncryptionInterrupted(const FstabEntry& entry) {
|
||||
if (!should_use_metadata_encryption(entry)) return false;
|
||||
return access(fs_mgr_metadata_encryption_in_progress_file_name(entry).c_str(), R_OK) == 0;
|
||||
}
|
||||
|
||||
static FstabEntry* LocateFormattableEntry(FstabEntry* const begin, FstabEntry* const end) {
|
||||
if (begin == end) {
|
||||
return nullptr;
|
||||
}
|
||||
const bool dev_option_enabled =
|
||||
android::base::GetBoolProperty("ro.product.build.16k_page.enabled", false);
|
||||
FstabEntry* f2fs_entry = nullptr;
|
||||
for (auto iter = begin; iter != end && iter->blk_device == begin->blk_device; iter++) {
|
||||
if (iter->fs_mgr_flags.formattable) {
|
||||
if (getpagesize() != 4096 && is_f2fs(iter->fs_type) && dev_option_enabled) {
|
||||
f2fs_entry = iter;
|
||||
continue;
|
||||
}
|
||||
if (f2fs_entry) {
|
||||
LOG(INFO) << "Skipping F2FS format for block device " << iter->blk_device << " @ "
|
||||
<< iter->mount_point
|
||||
<< " in non-4K mode for dev option enabled devices, "
|
||||
"as these devices need to toggle between 4K/16K mode, and F2FS does "
|
||||
"not support page_size != block_size configuration.";
|
||||
}
|
||||
return iter;
|
||||
}
|
||||
}
|
||||
if (f2fs_entry) {
|
||||
LOG(INFO) << "Using F2FS for " << f2fs_entry->blk_device << " @ " << f2fs_entry->mount_point
|
||||
<< " even though we are in non-4K mode. Device might require a data wipe after "
|
||||
"going back to 4K mode, as F2FS does not support page_size != block_size";
|
||||
}
|
||||
return f2fs_entry;
|
||||
}
|
||||
|
||||
// When multiple fstab records share the same mount_point, it will try to mount each
|
||||
// one in turn, and ignore any duplicates after a first successful mount.
|
||||
// Returns -1 on error, and FS_MGR_MNTALL_* otherwise.
|
||||
MountAllResult fs_mgr_mount_all(Fstab* fstab, int mount_mode) {
|
||||
int fs_mgr_mount_all(Fstab* fstab, int mount_mode) {
|
||||
int encryptable = FS_MGR_MNTALL_DEV_NOT_ENCRYPTABLE;
|
||||
int error_count = 0;
|
||||
CheckpointManager checkpoint_manager;
|
||||
AvbUniquePtr avb_handle(nullptr);
|
||||
bool wiped = false;
|
||||
|
||||
bool userdata_mounted = false;
|
||||
|
||||
if (fstab->empty()) {
|
||||
return {FS_MGR_MNTALL_FAIL, userdata_mounted};
|
||||
return FS_MGR_MNTALL_FAIL;
|
||||
}
|
||||
|
||||
bool scratch_can_be_mounted = true;
|
||||
|
|
@ -1513,7 +1557,7 @@ MountAllResult fs_mgr_mount_all(Fstab* fstab, int mount_mode) {
|
|||
if (!avb_handle) {
|
||||
LERROR << "Failed to open AvbHandle";
|
||||
set_type_property(encryptable);
|
||||
return {FS_MGR_MNTALL_FAIL, userdata_mounted};
|
||||
return FS_MGR_MNTALL_FAIL;
|
||||
}
|
||||
}
|
||||
if (avb_handle->SetUpAvbHashtree(¤t_entry, true /* wait_for_verity_dev */) ==
|
||||
|
|
@ -1532,11 +1576,13 @@ MountAllResult fs_mgr_mount_all(Fstab* fstab, int mount_mode) {
|
|||
}
|
||||
}
|
||||
|
||||
int last_idx_inspected;
|
||||
int top_idx = i;
|
||||
int last_idx_inspected = -1;
|
||||
const int top_idx = i;
|
||||
int attempted_idx = -1;
|
||||
|
||||
bool mret = mount_with_alternatives(*fstab, i, &last_idx_inspected, &attempted_idx);
|
||||
bool encryption_interrupted = WasMetadataEncryptionInterrupted(current_entry);
|
||||
bool mret = mount_with_alternatives(*fstab, i, encryption_interrupted, &last_idx_inspected,
|
||||
&attempted_idx);
|
||||
auto& attempted_entry = (*fstab)[attempted_idx];
|
||||
i = last_idx_inspected;
|
||||
int mount_errno = errno;
|
||||
|
|
@ -1547,7 +1593,7 @@ MountAllResult fs_mgr_mount_all(Fstab* fstab, int mount_mode) {
|
|||
|
||||
if (status == FS_MGR_MNTALL_FAIL) {
|
||||
// Fatal error - no point continuing.
|
||||
return {status, userdata_mounted};
|
||||
return status;
|
||||
}
|
||||
|
||||
if (status != FS_MGR_MNTALL_DEV_NOT_ENCRYPTABLE) {
|
||||
|
|
@ -1562,11 +1608,12 @@ MountAllResult fs_mgr_mount_all(Fstab* fstab, int mount_mode) {
|
|||
attempted_entry.mount_point, wiped ? "true" : "false",
|
||||
attempted_entry.fs_type,
|
||||
attempted_entry.fs_mgr_flags.is_zoned ? "true" : "false",
|
||||
std::to_string(attempted_entry.length),
|
||||
android::base::Join(attempted_entry.user_devices, ' ')},
|
||||
nullptr)) {
|
||||
LERROR << "Encryption failed";
|
||||
set_type_property(encryptable);
|
||||
return {FS_MGR_MNTALL_FAIL, userdata_mounted};
|
||||
return FS_MGR_MNTALL_FAIL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1580,17 +1627,23 @@ MountAllResult fs_mgr_mount_all(Fstab* fstab, int mount_mode) {
|
|||
// Success! Go get the next one.
|
||||
continue;
|
||||
}
|
||||
|
||||
auto formattable_entry =
|
||||
LocateFormattableEntry(fstab->data() + top_idx, fstab->data() + fstab->size());
|
||||
// Mounting failed, understand why and retry.
|
||||
wiped = partition_wiped(current_entry.blk_device.c_str());
|
||||
if (mount_errno != EBUSY && mount_errno != EACCES &&
|
||||
current_entry.fs_mgr_flags.formattable && wiped) {
|
||||
current_entry.fs_mgr_flags.formattable && (wiped || encryption_interrupted)) {
|
||||
// current_entry and attempted_entry point at the same partition, but sometimes
|
||||
// at two different lines in the fstab. Use current_entry for formatting
|
||||
// as that is the preferred one.
|
||||
LERROR << __FUNCTION__ << "(): " << realpath(current_entry.blk_device)
|
||||
<< " is wiped and " << current_entry.mount_point << " " << current_entry.fs_type
|
||||
<< " is formattable. Format it.";
|
||||
if (wiped)
|
||||
LERROR << __FUNCTION__ << "(): " << realpath(current_entry.blk_device)
|
||||
<< " is wiped and " << current_entry.mount_point << " "
|
||||
<< current_entry.fs_type << " is formattable. Format it.";
|
||||
if (encryption_interrupted)
|
||||
LERROR << __FUNCTION__ << "(): " << realpath(current_entry.blk_device)
|
||||
<< " was interrupted during encryption and " << current_entry.mount_point
|
||||
<< " " << current_entry.fs_type << " is formattable. Format it.";
|
||||
|
||||
checkpoint_manager.Revert(¤t_entry);
|
||||
|
||||
|
|
@ -1603,11 +1656,12 @@ MountAllResult fs_mgr_mount_all(Fstab* fstab, int mount_mode) {
|
|||
encryptable = FS_MGR_MNTALL_DEV_IS_METADATA_ENCRYPTED;
|
||||
set_type_property(encryptable);
|
||||
|
||||
if (!call_vdc({"cryptfs", "encryptFstab", current_entry.blk_device,
|
||||
current_entry.mount_point, "true" /* shouldFormat */,
|
||||
current_entry.fs_type,
|
||||
current_entry.fs_mgr_flags.is_zoned ? "true" : "false",
|
||||
android::base::Join(current_entry.user_devices, ' ')},
|
||||
if (!call_vdc({"cryptfs", "encryptFstab", formattable_entry->blk_device,
|
||||
formattable_entry->mount_point, "true" /* shouldFormat */,
|
||||
formattable_entry->fs_type,
|
||||
formattable_entry->fs_mgr_flags.is_zoned ? "true" : "false",
|
||||
std::to_string(formattable_entry->length),
|
||||
android::base::Join(formattable_entry->user_devices, ' ')},
|
||||
nullptr)) {
|
||||
LERROR << "Encryption failed";
|
||||
} else {
|
||||
|
|
@ -1616,7 +1670,7 @@ MountAllResult fs_mgr_mount_all(Fstab* fstab, int mount_mode) {
|
|||
}
|
||||
}
|
||||
|
||||
if (fs_mgr_do_format(current_entry) == 0) {
|
||||
if (fs_mgr_do_format(*formattable_entry) == 0) {
|
||||
// Let's replay the mount actions.
|
||||
i = top_idx - 1;
|
||||
continue;
|
||||
|
|
@ -1629,7 +1683,7 @@ MountAllResult fs_mgr_mount_all(Fstab* fstab, int mount_mode) {
|
|||
}
|
||||
|
||||
// mount(2) returned an error, handle the encryptable/formattable case.
|
||||
if (mount_errno != EBUSY && mount_errno != EACCES &&
|
||||
if (mount_errno != EBUSY && mount_errno != EACCES && !encryption_interrupted &&
|
||||
should_use_metadata_encryption(attempted_entry)) {
|
||||
if (!call_vdc({"cryptfs", "mountFstab", attempted_entry.blk_device,
|
||||
attempted_entry.mount_point,
|
||||
|
|
@ -1647,13 +1701,13 @@ MountAllResult fs_mgr_mount_all(Fstab* fstab, int mount_mode) {
|
|||
// Use StringPrintf to output "(null)" instead.
|
||||
if (attempted_entry.fs_mgr_flags.no_fail) {
|
||||
PERROR << android::base::StringPrintf(
|
||||
"Ignoring failure to mount an un-encryptable or wiped "
|
||||
"Ignoring failure to mount an un-encryptable, interrupted, or wiped "
|
||||
"partition on %s at %s options: %s",
|
||||
attempted_entry.blk_device.c_str(), attempted_entry.mount_point.c_str(),
|
||||
attempted_entry.fs_options.c_str());
|
||||
} else {
|
||||
PERROR << android::base::StringPrintf(
|
||||
"Failed to mount an un-encryptable or wiped partition "
|
||||
"Failed to mount an un-encryptable, interrupted, or wiped partition "
|
||||
"on %s at %s options: %s",
|
||||
attempted_entry.blk_device.c_str(), attempted_entry.mount_point.c_str(),
|
||||
attempted_entry.fs_options.c_str());
|
||||
|
|
@ -1679,9 +1733,9 @@ MountAllResult fs_mgr_mount_all(Fstab* fstab, int mount_mode) {
|
|||
set_type_property(encryptable);
|
||||
|
||||
if (error_count) {
|
||||
return {FS_MGR_MNTALL_FAIL, userdata_mounted};
|
||||
return FS_MGR_MNTALL_FAIL;
|
||||
} else {
|
||||
return {encryptable, userdata_mounted};
|
||||
return encryptable;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1718,190 +1772,6 @@ int fs_mgr_umount_all(android::fs_mgr::Fstab* fstab) {
|
|||
return ret;
|
||||
}
|
||||
|
||||
static std::chrono::milliseconds GetMillisProperty(const std::string& name,
|
||||
std::chrono::milliseconds default_value) {
|
||||
auto value = GetUintProperty(name, static_cast<uint64_t>(default_value.count()));
|
||||
return std::chrono::milliseconds(std::move(value));
|
||||
}
|
||||
|
||||
static bool fs_mgr_unmount_all_data_mounts(const std::string& data_block_device) {
|
||||
LINFO << __FUNCTION__ << "(): about to umount everything on top of " << data_block_device;
|
||||
Timer t;
|
||||
auto timeout = GetMillisProperty("init.userspace_reboot.userdata_remount.timeoutmillis", 5s);
|
||||
while (true) {
|
||||
bool umount_done = true;
|
||||
Fstab proc_mounts;
|
||||
if (!ReadFstabFromFile("/proc/mounts", &proc_mounts)) {
|
||||
LERROR << __FUNCTION__ << "(): Can't read /proc/mounts";
|
||||
return false;
|
||||
}
|
||||
// Now proceed with other bind mounts on top of /data.
|
||||
for (const auto& entry : proc_mounts) {
|
||||
std::string block_device;
|
||||
if (StartsWith(entry.blk_device, "/dev/block") &&
|
||||
!Realpath(entry.blk_device, &block_device)) {
|
||||
PWARNING << __FUNCTION__ << "(): failed to realpath " << entry.blk_device;
|
||||
block_device = entry.blk_device;
|
||||
}
|
||||
if (data_block_device == block_device) {
|
||||
if (umount2(entry.mount_point.c_str(), 0) != 0) {
|
||||
PERROR << __FUNCTION__ << "(): Failed to umount " << entry.mount_point;
|
||||
umount_done = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (umount_done) {
|
||||
LINFO << __FUNCTION__ << "(): Unmounting /data took " << t;
|
||||
return true;
|
||||
}
|
||||
if (t.duration() > timeout) {
|
||||
LERROR << __FUNCTION__ << "(): Timed out unmounting all mounts on "
|
||||
<< data_block_device;
|
||||
Fstab remaining_mounts;
|
||||
if (!ReadFstabFromFile("/proc/mounts", &remaining_mounts)) {
|
||||
LERROR << __FUNCTION__ << "(): Can't read /proc/mounts";
|
||||
} else {
|
||||
LERROR << __FUNCTION__ << "(): Following mounts remaining";
|
||||
for (const auto& e : remaining_mounts) {
|
||||
LERROR << __FUNCTION__ << "(): mount point: " << e.mount_point
|
||||
<< " block device: " << e.blk_device;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
std::this_thread::sleep_for(50ms);
|
||||
}
|
||||
}
|
||||
|
||||
static bool UnwindDmDeviceStack(const std::string& block_device,
|
||||
std::vector<std::string>* dm_stack) {
|
||||
if (!StartsWith(block_device, "/dev/block/")) {
|
||||
LWARNING << block_device << " is not a block device";
|
||||
return false;
|
||||
}
|
||||
std::string current = block_device;
|
||||
DeviceMapper& dm = DeviceMapper::Instance();
|
||||
while (true) {
|
||||
dm_stack->push_back(current);
|
||||
if (!dm.IsDmBlockDevice(current)) {
|
||||
break;
|
||||
}
|
||||
auto parent = dm.GetParentBlockDeviceByPath(current);
|
||||
if (!parent) {
|
||||
return false;
|
||||
}
|
||||
current = *parent;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
FstabEntry* fs_mgr_get_mounted_entry_for_userdata(Fstab* fstab,
|
||||
const std::string& data_block_device) {
|
||||
std::vector<std::string> dm_stack;
|
||||
if (!UnwindDmDeviceStack(data_block_device, &dm_stack)) {
|
||||
LERROR << "Failed to unwind dm-device stack for " << data_block_device;
|
||||
return nullptr;
|
||||
}
|
||||
for (auto& entry : *fstab) {
|
||||
if (entry.mount_point != "/data") {
|
||||
continue;
|
||||
}
|
||||
std::string block_device;
|
||||
if (entry.fs_mgr_flags.logical) {
|
||||
if (!fs_mgr_update_logical_partition(&entry)) {
|
||||
LERROR << "Failed to update logic partition " << entry.blk_device;
|
||||
continue;
|
||||
}
|
||||
block_device = entry.blk_device;
|
||||
} else if (!Realpath(entry.blk_device, &block_device)) {
|
||||
PWARNING << "Failed to realpath " << entry.blk_device;
|
||||
block_device = entry.blk_device;
|
||||
}
|
||||
if (std::find(dm_stack.begin(), dm_stack.end(), block_device) != dm_stack.end()) {
|
||||
return &entry;
|
||||
}
|
||||
}
|
||||
LERROR << "Didn't find entry that was used to mount /data onto " << data_block_device;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// TODO(b/143970043): return different error codes based on which step failed.
|
||||
int fs_mgr_remount_userdata_into_checkpointing(Fstab* fstab) {
|
||||
Fstab proc_mounts;
|
||||
if (!ReadFstabFromFile("/proc/mounts", &proc_mounts)) {
|
||||
LERROR << "Can't read /proc/mounts";
|
||||
return -1;
|
||||
}
|
||||
auto mounted_entry = GetEntryForMountPoint(&proc_mounts, "/data");
|
||||
if (mounted_entry == nullptr) {
|
||||
LERROR << "/data is not mounted";
|
||||
return -1;
|
||||
}
|
||||
std::string block_device;
|
||||
if (!Realpath(mounted_entry->blk_device, &block_device)) {
|
||||
PERROR << "Failed to realpath " << mounted_entry->blk_device;
|
||||
return -1;
|
||||
}
|
||||
auto fstab_entry = fs_mgr_get_mounted_entry_for_userdata(fstab, block_device);
|
||||
if (fstab_entry == nullptr) {
|
||||
LERROR << "Can't find /data in fstab";
|
||||
return -1;
|
||||
}
|
||||
bool force_umount = GetBoolProperty("sys.init.userdata_remount.force_umount", false);
|
||||
if (force_umount) {
|
||||
LINFO << "Will force an umount of userdata even if it's not required";
|
||||
}
|
||||
if (!force_umount && !SupportsCheckpoint(fstab_entry)) {
|
||||
LINFO << "Userdata doesn't support checkpointing. Nothing to do";
|
||||
return 0;
|
||||
}
|
||||
CheckpointManager checkpoint_manager;
|
||||
if (!force_umount && !checkpoint_manager.NeedsCheckpoint()) {
|
||||
LINFO << "Checkpointing not needed. Don't remount";
|
||||
return 0;
|
||||
}
|
||||
if (!force_umount && fstab_entry->fs_mgr_flags.checkpoint_fs) {
|
||||
// Userdata is f2fs, simply remount it.
|
||||
if (!checkpoint_manager.Update(fstab_entry)) {
|
||||
LERROR << "Failed to remount userdata in checkpointing mode";
|
||||
return -1;
|
||||
}
|
||||
if (mount(block_device.c_str(), fstab_entry->mount_point.c_str(), "none",
|
||||
MS_REMOUNT | fstab_entry->flags, fstab_entry->fs_options.c_str()) != 0) {
|
||||
PERROR << "Failed to remount userdata in checkpointing mode";
|
||||
return -1;
|
||||
}
|
||||
} else {
|
||||
LINFO << "Unmounting /data before remounting into checkpointing mode";
|
||||
if (!fs_mgr_unmount_all_data_mounts(block_device)) {
|
||||
LERROR << "Failed to umount /data";
|
||||
return -1;
|
||||
}
|
||||
DeviceMapper& dm = DeviceMapper::Instance();
|
||||
while (dm.IsDmBlockDevice(block_device)) {
|
||||
auto next_device = dm.GetParentBlockDeviceByPath(block_device);
|
||||
auto name = dm.GetDmDeviceNameByPath(block_device);
|
||||
if (!name) {
|
||||
LERROR << "Failed to get dm-name for " << block_device;
|
||||
return -1;
|
||||
}
|
||||
LINFO << "Deleting " << block_device << " named " << *name;
|
||||
if (!dm.DeleteDevice(*name, 3s)) {
|
||||
return -1;
|
||||
}
|
||||
if (!next_device) {
|
||||
LERROR << "Failed to find parent device for " << block_device;
|
||||
}
|
||||
block_device = *next_device;
|
||||
}
|
||||
LINFO << "Remounting /data";
|
||||
// TODO(b/143970043): remove this hack after fs_mgr_mount_all is refactored.
|
||||
auto result = fs_mgr_mount_all(fstab, MOUNT_MODE_ONLY_USERDATA);
|
||||
return result.code == FS_MGR_MNTALL_FAIL ? -1 : 0;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
// wrapper to __mount() and expects a fully prepared fstab_rec,
|
||||
// unlike fs_mgr_do_mount which does more things with avb / verity etc.
|
||||
int fs_mgr_do_mount_one(const FstabEntry& entry, const std::string& alt_mount_point) {
|
||||
|
|
@ -1916,12 +1786,12 @@ int fs_mgr_do_mount_one(const FstabEntry& entry, const std::string& alt_mount_po
|
|||
int ret = prepare_fs_for_mount(entry.blk_device, entry, mount_point);
|
||||
// Wiped case doesn't require to try __mount below.
|
||||
if (ret & FS_STAT_INVALID_MAGIC) {
|
||||
return FS_MGR_DOMNT_FAILED;
|
||||
return FS_MGR_DOMNT_FAILED;
|
||||
}
|
||||
|
||||
ret = __mount(entry.blk_device, mount_point, entry);
|
||||
if (ret) {
|
||||
ret = (errno == EBUSY) ? FS_MGR_DOMNT_BUSY : FS_MGR_DOMNT_FAILED;
|
||||
ret = (errno == EBUSY) ? FS_MGR_DOMNT_BUSY : FS_MGR_DOMNT_FAILED;
|
||||
}
|
||||
|
||||
return ret;
|
||||
|
|
@ -2073,11 +1943,45 @@ static bool InstallZramDevice(const std::string& device) {
|
|||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
* Zram backing device can be created as long as /data has at least `size`
|
||||
* free space, though we may want to leave some extra space for the remaining
|
||||
* boot process and other system activities.
|
||||
*/
|
||||
static bool ZramBackingDeviceSizeAvailable(off64_t size) {
|
||||
constexpr const char* data_path = "/data";
|
||||
uint64_t min_free_mb =
|
||||
android::base::GetUintProperty<uint64_t>("ro.zram_backing_device_min_free_mb", 0);
|
||||
|
||||
// No min_free property. Skip the available size check.
|
||||
if (min_free_mb == 0) return true;
|
||||
|
||||
struct statvfs vst;
|
||||
if (statvfs(data_path, &vst) < 0) {
|
||||
PERROR << "Cannot check available space: " << data_path;
|
||||
return false;
|
||||
}
|
||||
|
||||
uint64_t size_free = static_cast<uint64_t>(vst.f_bfree) * vst.f_frsize;
|
||||
uint64_t size_required = size + (min_free_mb * 1024 * 1024);
|
||||
if (size_required > size_free) {
|
||||
PERROR << "Free space is not enough for zram backing device: " << size_required << " > "
|
||||
<< size_free;
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool PrepareZramBackingDevice(off64_t size) {
|
||||
|
||||
constexpr const char* file_path = "/data/per_boot/zram_swap";
|
||||
if (size == 0) return true;
|
||||
|
||||
// Check available space
|
||||
if (!ZramBackingDeviceSizeAvailable(size)) {
|
||||
PERROR << "No space for target path: " << file_path;
|
||||
return false;
|
||||
}
|
||||
// Prepare target path
|
||||
unique_fd target_fd(TEMP_FAILURE_RETRY(open(file_path, O_RDWR | O_CREAT | O_CLOEXEC, 0600)));
|
||||
if (target_fd.get() == -1) {
|
||||
|
|
@ -2086,6 +1990,7 @@ static bool PrepareZramBackingDevice(off64_t size) {
|
|||
}
|
||||
if (fallocate(target_fd.get(), 0, 0, size) < 0) {
|
||||
PERROR << "Cannot truncate target path: " << file_path;
|
||||
unlink(file_path);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -74,7 +74,7 @@ bool fs_mgr_is_dsu_running() {
|
|||
return android::gsi::IsGsiRunning();
|
||||
}
|
||||
|
||||
std::vector<const std::string> OverlayMountPoints() {
|
||||
std::vector<std::string> OverlayMountPoints() {
|
||||
// Never fallback to legacy cache mount point if within a DSU system,
|
||||
// because running a DSU system implies the device supports dynamic
|
||||
// partitions, which means legacy cache mustn't be used.
|
||||
|
|
@ -412,6 +412,8 @@ static bool fs_mgr_overlayfs_mount_one(const FstabEntry& fstab_entry) {
|
|||
bool retval = true;
|
||||
bool move_dir_shared = true;
|
||||
bool parent_shared = true;
|
||||
bool parent_have_parent = false;
|
||||
bool parent_made_private = false;
|
||||
bool root_shared = true;
|
||||
bool root_made_private = false;
|
||||
|
||||
|
|
@ -443,6 +445,10 @@ static bool fs_mgr_overlayfs_mount_one(const FstabEntry& fstab_entry) {
|
|||
if (entry.mount_point == "/") {
|
||||
root_shared = entry.shared_flag;
|
||||
}
|
||||
// Ignore "/" as we don't overlay "/" directly.
|
||||
if (entry.mount_point != "/") {
|
||||
parent_have_parent |= android::base::StartsWith(mount_point, entry.mount_point + "/");
|
||||
}
|
||||
}
|
||||
|
||||
// Precondition is that kMoveMountTempDir is MS_PRIVATE, otherwise don't try to move any
|
||||
|
|
@ -453,11 +459,13 @@ static bool fs_mgr_overlayfs_mount_one(const FstabEntry& fstab_entry) {
|
|||
|
||||
// Need to make the original mountpoint MS_PRIVATE, so that the overlayfs can be MS_MOVE.
|
||||
// This could happen if its parent mount is remounted later.
|
||||
if (!fs_mgr_overlayfs_set_shared_mount(mount_point, false)) {
|
||||
// If failed to set "/system" mount type, it might be due to "/system" not being a valid
|
||||
// mountpoint after switch root. Retry with "/" in this case.
|
||||
if (errno == EINVAL && mount_point == "/system") {
|
||||
root_made_private = fs_mgr_overlayfs_set_shared_mount("/", false);
|
||||
if (parent_have_parent) {
|
||||
parent_made_private |= fs_mgr_overlayfs_set_shared_mount(mount_point, false);
|
||||
if (!parent_made_private && errno == EINVAL && mount_point == "/system") {
|
||||
// If failed to set "/system" mount type, it might be due to "/system" not being a valid
|
||||
// mountpoint after switch root. Retry with "/" in this case.
|
||||
parent_made_private |= fs_mgr_overlayfs_set_shared_mount("/", false);
|
||||
root_made_private |= parent_made_private;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -496,6 +504,15 @@ static bool fs_mgr_overlayfs_mount_one(const FstabEntry& fstab_entry) {
|
|||
continue;
|
||||
}
|
||||
}
|
||||
if (!parent_made_private) {
|
||||
parent_made_private |= fs_mgr_overlayfs_set_shared_mount(mount_point, false);
|
||||
if (!parent_made_private && errno == EINVAL && mount_point == "/system") {
|
||||
// If failed to set "/system" mount type, it might be due to "/system" not being a
|
||||
// valid mountpoint after switch root. Retry with "/" in this case.
|
||||
parent_made_private |= fs_mgr_overlayfs_set_shared_mount("/", false);
|
||||
root_made_private |= parent_made_private;
|
||||
}
|
||||
}
|
||||
|
||||
if (new_entry.shared_flag) {
|
||||
new_entry.shared_flag = fs_mgr_overlayfs_set_shared_mount(new_entry.mount_point, false);
|
||||
|
|
@ -524,7 +541,7 @@ static bool fs_mgr_overlayfs_mount_one(const FstabEntry& fstab_entry) {
|
|||
rmdir(entry.dir.c_str());
|
||||
}
|
||||
// If the original (overridden) mount was MS_SHARED, then set the overlayfs mount to MS_SHARED.
|
||||
if (parent_shared) {
|
||||
if (parent_shared && parent_made_private) {
|
||||
fs_mgr_overlayfs_set_shared_mount(mount_point, true);
|
||||
}
|
||||
if (root_shared && root_made_private) {
|
||||
|
|
|
|||
|
|
@ -54,7 +54,7 @@ const std::string fs_mgr_mount_point(const std::string& mount_point);
|
|||
bool OverlayfsSetupAllowed(bool verbose = false);
|
||||
bool MountScratch(const std::string& device_path, bool readonly = false);
|
||||
bool fs_mgr_overlayfs_umount_scratch();
|
||||
std::vector<const std::string> OverlayMountPoints();
|
||||
std::vector<std::string> OverlayMountPoints();
|
||||
bool fs_mgr_overlayfs_already_mounted(const std::string& mount_point, bool overlay_only = true);
|
||||
bool fs_mgr_wants_overlayfs(android::fs_mgr::FstabEntry* entry);
|
||||
android::fs_mgr::Fstab fs_mgr_overlayfs_candidate_list(const android::fs_mgr::Fstab& fstab);
|
||||
|
|
|
|||
|
|
@ -80,10 +80,8 @@
|
|||
|
||||
using namespace std::chrono_literals;
|
||||
|
||||
bool fs_mgr_set_blk_ro(const std::string& blockdev, bool readonly = true);
|
||||
bool fs_mgr_is_device_unlocked();
|
||||
|
||||
bool fs_mgr_is_ext4(const std::string& blk_device);
|
||||
bool fs_mgr_is_f2fs(const std::string& blk_device);
|
||||
|
||||
bool fs_mgr_filesystem_available(const std::string& filesystem);
|
||||
|
|
|
|||
|
|
@ -171,11 +171,12 @@ bool VerifyCheckpointing() {
|
|||
}
|
||||
if (show_help) {
|
||||
show_help = false;
|
||||
std::cerr << "WARNING: Userdata checkpoint is in progress. To force end checkpointing, "
|
||||
"call 'vdc checkpoint commitChanges'. This can lead to data corruption if "
|
||||
"rolled back."
|
||||
std::cerr << "WARNING: Userdata checkpoint is in progress. "
|
||||
"To forcibly end checkpointing, "
|
||||
"call 'vdc checkpoint commitChanges'. "
|
||||
"This can lead to data corruption if rolled back."
|
||||
<< std::endl;
|
||||
LOG(INFO) << "Waiting for checkpoint to complete and then continue remount.";
|
||||
LOG(INFO) << "Waiting for checkpoint to complete before remounting...";
|
||||
}
|
||||
std::this_thread::sleep_for(4s);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -36,7 +36,7 @@ namespace {
|
|||
|
||||
// The order of the list means the priority to show the files in the directory.
|
||||
// The last one has the highest priority.
|
||||
const std::vector<const std::string> kVendorOverlaySourceDirs = {
|
||||
const std::vector<std::string> kVendorOverlaySourceDirs = {
|
||||
"/system/vendor_overlay/",
|
||||
"/product/vendor_overlay/",
|
||||
};
|
||||
|
|
|
|||
|
|
@ -58,13 +58,8 @@ enum mount_mode {
|
|||
#define FS_MGR_MNTALL_DEV_NEEDS_RECOVERY 4
|
||||
#define FS_MGR_MNTALL_DEV_NOT_ENCRYPTABLE 0
|
||||
#define FS_MGR_MNTALL_FAIL (-1)
|
||||
|
||||
struct MountAllResult {
|
||||
// One of the FS_MGR_MNTALL_* returned code defined above.
|
||||
int code;
|
||||
// Whether userdata was mounted as a result of |fs_mgr_mount_all| call.
|
||||
bool userdata_mounted;
|
||||
};
|
||||
// fs_mgr_mount_all() updates fstab entries that reference device-mapper.
|
||||
int fs_mgr_mount_all(android::fs_mgr::Fstab* fstab, int mount_mode);
|
||||
|
||||
struct HashtreeInfo {
|
||||
// The hash algorithm used to build the merkle tree.
|
||||
|
|
@ -75,13 +70,6 @@ struct HashtreeInfo {
|
|||
bool check_at_most_once;
|
||||
};
|
||||
|
||||
// fs_mgr_mount_all() updates fstab entries that reference device-mapper.
|
||||
// Returns a |MountAllResult|. The first element is one of the FS_MNG_MNTALL_* return codes
|
||||
// defined above, and the second element tells whether this call to fs_mgr_mount_all was responsible
|
||||
// for mounting userdata. Later is required for init to correctly enqueue fs-related events as part
|
||||
// of userdata remount during userspace reboot.
|
||||
MountAllResult fs_mgr_mount_all(android::fs_mgr::Fstab* fstab, int mount_mode);
|
||||
|
||||
#define FS_MGR_DOMNT_FAILED (-1)
|
||||
#define FS_MGR_DOMNT_BUSY (-2)
|
||||
#define FS_MGR_DOMNT_SUCCESS 0
|
||||
|
|
@ -116,6 +104,12 @@ int fs_mgr_setup_verity(android::fs_mgr::FstabEntry* fstab, bool wait_for_verity
|
|||
// returned. Otherwise, it will use the current slot.
|
||||
std::string fs_mgr_get_super_partition_name(int slot = -1);
|
||||
|
||||
// Set readonly for the block device
|
||||
bool fs_mgr_set_blk_ro(const std::string& blockdev, bool readonly = true);
|
||||
|
||||
// Check if the block device has ext4 filesystem
|
||||
bool fs_mgr_is_ext4(const std::string& blk_device);
|
||||
|
||||
enum FsMgrUmountStatus : int {
|
||||
SUCCESS = 0,
|
||||
ERROR_UNKNOWN = 1 << 0,
|
||||
|
|
@ -127,11 +121,6 @@ enum FsMgrUmountStatus : int {
|
|||
// it destroys verity devices from device mapper after the device is unmounted.
|
||||
int fs_mgr_umount_all(android::fs_mgr::Fstab* fstab);
|
||||
|
||||
// Finds a entry in |fstab| that was used to mount a /data on |data_block_device|.
|
||||
android::fs_mgr::FstabEntry* fs_mgr_get_mounted_entry_for_userdata(
|
||||
android::fs_mgr::Fstab* fstab, const std::string& data_block_device);
|
||||
int fs_mgr_remount_userdata_into_checkpointing(android::fs_mgr::Fstab* fstab);
|
||||
|
||||
// Finds the dm_bow device on which this block device is stacked, or returns
|
||||
// empty string
|
||||
std::string fs_mgr_find_bow_device(const std::string& block_device);
|
||||
|
|
@ -144,3 +133,7 @@ bool fs_mgr_create_canonical_mount_point(const std::string& mount_point);
|
|||
// Unlike fs_mgr_overlayfs, mount overlayfs without upperdir and workdir, so the
|
||||
// filesystem cannot be remount read-write.
|
||||
bool fs_mgr_mount_overlayfs_fstab_entry(const android::fs_mgr::FstabEntry& entry);
|
||||
|
||||
// File name used to track if encryption was interrupted, leading to a known bad fs state
|
||||
std::string fs_mgr_metadata_encryption_in_progress_file_name(
|
||||
const android::fs_mgr::FstabEntry& entry);
|
||||
|
|
|
|||
|
|
@ -29,6 +29,8 @@ namespace fs_mgr {
|
|||
// first match or nullptr.
|
||||
FstabEntry* GetEntryForPath(Fstab* fstab, const std::string& path);
|
||||
|
||||
std::vector<FstabEntry*> GetEntriesForPath(Fstab* fstab, const std::string& path);
|
||||
|
||||
// Make sure that the volume 'path' is on is mounted.
|
||||
// * If 'mount_point' is nullptr, use mount point in fstab. Caller can call
|
||||
// fs_mgr_ensure_path_unmounted() with the same 'path' argument to unmount.
|
||||
|
|
|
|||
|
|
@ -60,6 +60,7 @@ static_assert(sizeof(off_t) == sizeof(uint64_t));
|
|||
static inline void cleanup(const std::string& file_path, bool created) {
|
||||
if (created) {
|
||||
unlink(file_path.c_str());
|
||||
sync();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -34,6 +34,7 @@
|
|||
|
||||
#include <string>
|
||||
|
||||
#include <android-base/logging.h>
|
||||
#include <android-base/macros.h>
|
||||
#include <android-base/strings.h>
|
||||
#include <android-base/unique_fd.h>
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@
|
|||
#include <array>
|
||||
#include <sstream>
|
||||
|
||||
#include <android-base/logging.h>
|
||||
#include <android-base/file.h>
|
||||
#include <android-base/strings.h>
|
||||
#include <android-base/unique_fd.h>
|
||||
|
|
|
|||
|
|
@ -28,6 +28,7 @@
|
|||
#include <vector>
|
||||
|
||||
#include <android-base/file.h>
|
||||
#include <android-base/logging.h>
|
||||
#include <android-base/parseint.h>
|
||||
#include <android-base/stringprintf.h>
|
||||
#include <android-base/strings.h>
|
||||
|
|
|
|||
|
|
@ -20,6 +20,7 @@
|
|||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include <android-base/logging.h>
|
||||
#include <android-base/strings.h>
|
||||
#include <fstab/fstab.h>
|
||||
#include <libavb/libavb.h>
|
||||
|
|
|
|||
|
|
@ -16,10 +16,11 @@
|
|||
|
||||
#include <endian.h>
|
||||
|
||||
#include <random>
|
||||
|
||||
#include <android-base/strings.h>
|
||||
#include <android-base/unique_fd.h>
|
||||
#include <base/files/file_util.h>
|
||||
#include <base/rand_util.h>
|
||||
#include <base/strings/string_util.h>
|
||||
#include <libavb/libavb.h>
|
||||
|
||||
#include "avb_util.h"
|
||||
|
|
@ -70,7 +71,7 @@ void AvbUtilTest::SetVBMetaFlags(const base::FilePath& image_path, uint32_t flag
|
|||
|
||||
std::string image_file_name = image_path.RemoveExtension().BaseName().value();
|
||||
bool is_vbmeta_partition =
|
||||
base::StartsWith(image_file_name, "vbmeta", base::CompareCase::INSENSITIVE_ASCII);
|
||||
android::base::StartsWithIgnoreCase(image_file_name, "vbmeta");
|
||||
|
||||
android::base::unique_fd fd(open(image_path.value().c_str(), O_RDWR | O_CLOEXEC));
|
||||
EXPECT_TRUE(fd > 0);
|
||||
|
|
@ -603,7 +604,7 @@ bool AvbUtilTest::CompareVBMeta(const base::FilePath& avb_image_path,
|
|||
std::string image_file_name = avb_image_path.RemoveExtension().BaseName().value();
|
||||
|
||||
base::FilePath extracted_vbmeta_path;
|
||||
if (base::StartsWith(image_file_name, "vbmeta", base::CompareCase::INSENSITIVE_ASCII)) {
|
||||
if (android::base::StartsWithIgnoreCase(image_file_name, "vbmeta")) {
|
||||
extracted_vbmeta_path = avb_image_path; // no need to extract if it's a vbmeta image.
|
||||
} else {
|
||||
extracted_vbmeta_path = ExtractVBMetaImage(avb_image_path, image_file_name + "-vbmeta.img");
|
||||
|
|
@ -727,7 +728,10 @@ void AvbUtilTest::ModifyFile(const base::FilePath& file_path, size_t offset, ssi
|
|||
|
||||
// Introduces a new modification.
|
||||
if (length > 0) {
|
||||
int modify_location = base::RandInt(offset, offset + length - 1);
|
||||
// mersenne_twister_engine seeded with the default seed source.
|
||||
static std::mt19937 gen(std::random_device{}());
|
||||
std::uniform_int_distribution<> rand_distribution(offset, offset + length - 1);
|
||||
int modify_location = rand_distribution(gen);
|
||||
file_content[modify_location] ^= 0x80;
|
||||
last_file_path = file_path.value();
|
||||
last_modified_location = modify_location;
|
||||
|
|
|
|||
|
|
@ -20,7 +20,6 @@
|
|||
|
||||
#include <android-base/file.h>
|
||||
#include <base/files/file_util.h>
|
||||
#include <base/strings/string_util.h>
|
||||
|
||||
namespace fs_avb_host_test {
|
||||
|
||||
|
|
|
|||
|
|
@ -18,8 +18,8 @@
|
|||
#include <stdlib.h>
|
||||
|
||||
#include <android-base/file.h>
|
||||
#include <android-base/strings.h>
|
||||
#include <base/files/file_util.h>
|
||||
#include <base/strings/string_util.h>
|
||||
#include <fs_avb/fs_avb.h>
|
||||
#include <libavb/libavb.h>
|
||||
|
||||
|
|
@ -49,7 +49,7 @@ void PublicFsAvbTest::ModifyVBMetaHeaderFlags(const base::FilePath& vbmeta_image
|
|||
|
||||
// Only support modifying the flags in vbmeta*.img.
|
||||
std::string image_file_name = vbmeta_image_path.RemoveExtension().BaseName().value();
|
||||
ASSERT_TRUE(base::StartsWith(image_file_name, "vbmeta", base::CompareCase::INSENSITIVE_ASCII));
|
||||
ASSERT_TRUE(android::base::StartsWithIgnoreCase(image_file_name, "vbmeta"));
|
||||
|
||||
android::base::unique_fd fd(open(vbmeta_image_path.value().c_str(), O_RDWR | O_CLOEXEC));
|
||||
EXPECT_TRUE(fd > 0);
|
||||
|
|
|
|||
|
|
@ -19,8 +19,9 @@
|
|||
#include <stdlib.h>
|
||||
|
||||
#include <android-base/file.h>
|
||||
#include <android-base/strings.h>
|
||||
#include <android-base/stringprintf.h>
|
||||
#include <base/files/file_util.h>
|
||||
#include <base/strings/string_util.h>
|
||||
|
||||
namespace fs_avb_host_test {
|
||||
|
||||
|
|
@ -64,9 +65,7 @@ std::string BaseFsAvbTest::CalcVBMetaDigest(const std::string& file_name,
|
|||
std::string vbmeta_digest_data;
|
||||
EXPECT_TRUE(base::ReadFileToString(vbmeta_digest_path, &vbmeta_digest_data));
|
||||
// Returns the trimmed digest.
|
||||
std::string trimmed_digest_data;
|
||||
base::TrimString(vbmeta_digest_data, " \t\n", &trimmed_digest_data);
|
||||
return trimmed_digest_data;
|
||||
return android::base::Trim(vbmeta_digest_data);
|
||||
}
|
||||
|
||||
base::FilePath BaseFsAvbTest::GenerateVBMetaImage(
|
||||
|
|
@ -91,7 +90,7 @@ base::FilePath BaseFsAvbTest::GenerateVBMetaImage(
|
|||
// --chain_partitions
|
||||
std::string chain_partition_options;
|
||||
for (const auto& partition : chain_partitions) {
|
||||
chain_partition_options += base::StringPrintf(
|
||||
chain_partition_options += android::base::StringPrintf(
|
||||
" --chain_partition %s:%u:%s", partition.partition_name.c_str(),
|
||||
partition.rollback_index_location, partition.key_blob_path.value().c_str());
|
||||
}
|
||||
|
|
|
|||
|
|
@ -26,9 +26,9 @@
|
|||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include <android-base/stringprintf.h>
|
||||
#include <android-base/unique_fd.h>
|
||||
#include <base/files/file_path.h>
|
||||
#include <base/strings/stringprintf.h>
|
||||
#include <fs_avb/types.h>
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
|
|
@ -37,7 +37,7 @@
|
|||
// the command exits normally with exit status |expected_exit_status|.
|
||||
#define EXPECT_COMMAND(expected_exit_status, command_format, ...) \
|
||||
do { \
|
||||
int rc = system(base::StringPrintf(command_format, ##__VA_ARGS__).c_str()); \
|
||||
int rc = system(android::base::StringPrintf(command_format, ##__VA_ARGS__).c_str()); \
|
||||
EXPECT_TRUE(WIFEXITED(rc)); \
|
||||
EXPECT_EQ(WEXITSTATUS(rc), expected_exit_status); \
|
||||
} while (0);
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@
|
|||
|
||||
#include <thread>
|
||||
|
||||
#include <android-base/logging.h>
|
||||
#include <android-base/stringprintf.h>
|
||||
#include <android-base/unique_fd.h>
|
||||
#include <linux/fs.h>
|
||||
|
|
|
|||
|
|
@ -20,12 +20,6 @@
|
|||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#ifdef HOST_TEST
|
||||
#include <base/logging.h>
|
||||
#else
|
||||
#include <android-base/logging.h>
|
||||
#endif
|
||||
|
||||
#include <android-base/result.h>
|
||||
|
||||
using android::base::ErrnoError;
|
||||
|
|
|
|||
|
|
@ -39,6 +39,10 @@
|
|||
#include "fstab_priv.h"
|
||||
#include "logging_macros.h"
|
||||
|
||||
#if !defined(MS_LAZYTIME)
|
||||
#define MS_LAZYTIME (1 << 25)
|
||||
#endif
|
||||
|
||||
using android::base::EndsWith;
|
||||
using android::base::ParseByteCount;
|
||||
using android::base::ParseInt;
|
||||
|
|
@ -74,6 +78,7 @@ FlagList kMountFlagsList[] = {
|
|||
{"private", MS_PRIVATE},
|
||||
{"slave", MS_SLAVE},
|
||||
{"shared", MS_SHARED},
|
||||
{"lazytime", MS_LAZYTIME},
|
||||
{"defaults", 0},
|
||||
};
|
||||
|
||||
|
|
@ -521,6 +526,24 @@ std::vector<FstabPtrEntryType*> GetEntriesByPred(FstabPtr fstab, const Pred& pre
|
|||
|
||||
} // namespace
|
||||
|
||||
// Return the path to the recovery fstab file. There may be multiple fstab files;
|
||||
// the one that is returned will be the first that exists of recovery.fstab.<fstab_suffix>,
|
||||
// recovery.fstab.<hardware>, and recovery.fstab.<hardware.platform>.
|
||||
std::string GetRecoveryFstabPath() {
|
||||
for (const char* prop : {"fstab_suffix", "hardware", "hardware.platform"}) {
|
||||
std::string suffix;
|
||||
|
||||
if (!fs_mgr_get_boot_config(prop, &suffix)) continue;
|
||||
|
||||
std::string fstab_path = "/etc/recovery.fstab." + suffix;
|
||||
if (access(fstab_path.c_str(), F_OK) == 0) {
|
||||
return fstab_path;
|
||||
}
|
||||
}
|
||||
|
||||
return "/etc/recovery.fstab";
|
||||
}
|
||||
|
||||
// Return the path to the fstab file. There may be multiple fstab files; the
|
||||
// one that is returned will be the first that exists of fstab.<fstab_suffix>,
|
||||
// fstab.<hardware>, and fstab.<hardware.platform>. The fstab is searched for
|
||||
|
|
@ -530,7 +553,7 @@ std::vector<FstabPtrEntryType*> GetEntriesByPred(FstabPtr fstab, const Pred& pre
|
|||
// the system/etc directory is supported too and is the preferred location.
|
||||
std::string GetFstabPath() {
|
||||
if (InRecovery()) {
|
||||
return "/etc/recovery.fstab";
|
||||
return GetRecoveryFstabPath();
|
||||
}
|
||||
for (const char* prop : {"fstab_suffix", "hardware", "hardware.platform"}) {
|
||||
std::string suffix;
|
||||
|
|
|
|||
|
|
@ -39,9 +39,6 @@ std::string GetFstabPath();
|
|||
void ImportBootconfigFromString(const std::string& bootconfig,
|
||||
const std::function<void(std::string, std::string)>& fn);
|
||||
|
||||
bool GetBootconfigFromString(const std::string& bootconfig, const std::string& key,
|
||||
std::string* out);
|
||||
|
||||
void ImportKernelCmdlineFromString(const std::string& cmdline,
|
||||
const std::function<void(std::string, std::string)>& fn);
|
||||
|
||||
|
|
|
|||
|
|
@ -154,5 +154,8 @@ bool GetKernelCmdline(const std::string& key, std::string* out);
|
|||
// Return the "other" slot for the given slot suffix.
|
||||
std::string OtherSlotSuffix(const std::string& suffix);
|
||||
|
||||
bool GetBootconfigFromString(const std::string& bootconfig, const std::string& key,
|
||||
std::string* out);
|
||||
|
||||
} // namespace fs_mgr
|
||||
} // namespace android
|
||||
|
|
|
|||
|
|
@ -91,6 +91,7 @@ filegroup {
|
|||
"partition_cow_creator.cpp",
|
||||
"return.cpp",
|
||||
"utility.cpp",
|
||||
"scratch_super.cpp",
|
||||
],
|
||||
}
|
||||
|
||||
|
|
@ -110,6 +111,9 @@ cc_library_static {
|
|||
static_libs: [
|
||||
"libfs_mgr_binder",
|
||||
],
|
||||
whole_static_libs: [
|
||||
"libselinux",
|
||||
],
|
||||
}
|
||||
|
||||
cc_library {
|
||||
|
|
@ -128,6 +132,9 @@ cc_library {
|
|||
static_libs: [
|
||||
"libsnapshot_cow",
|
||||
],
|
||||
whole_static_libs: [
|
||||
"libselinux",
|
||||
],
|
||||
}
|
||||
|
||||
cc_library_static {
|
||||
|
|
@ -142,6 +149,7 @@ cc_library_static {
|
|||
],
|
||||
static_libs: [
|
||||
"libfs_mgr",
|
||||
"libselinux",
|
||||
],
|
||||
}
|
||||
|
||||
|
|
@ -159,6 +167,9 @@ cc_library_static {
|
|||
static_libs: [
|
||||
"libfs_mgr",
|
||||
],
|
||||
whole_static_libs: [
|
||||
"libselinux",
|
||||
],
|
||||
}
|
||||
|
||||
cc_defaults {
|
||||
|
|
@ -241,6 +252,7 @@ cc_library_static {
|
|||
"libfs_mgr",
|
||||
"libgmock",
|
||||
"libgtest",
|
||||
"libselinux",
|
||||
],
|
||||
}
|
||||
|
||||
|
|
@ -283,7 +295,6 @@ cc_defaults {
|
|||
],
|
||||
auto_gen_config: true,
|
||||
require_root: true,
|
||||
compile_multilib: "first",
|
||||
}
|
||||
|
||||
cc_test {
|
||||
|
|
@ -294,8 +305,17 @@ cc_test {
|
|||
],
|
||||
test_suites: [
|
||||
"vts",
|
||||
"device-tests",
|
||||
"general-tests",
|
||||
],
|
||||
compile_multilib: "both",
|
||||
multilib: {
|
||||
lib32: {
|
||||
suffix: "32",
|
||||
},
|
||||
lib64: {
|
||||
suffix: "64",
|
||||
},
|
||||
},
|
||||
test_options: {
|
||||
min_shipping_api_level: 30,
|
||||
},
|
||||
|
|
@ -311,11 +331,18 @@ cc_test {
|
|||
"-DLIBSNAPSHOT_TEST_VAB_LEGACY",
|
||||
],
|
||||
test_suites: [
|
||||
"device-tests",
|
||||
"general-tests",
|
||||
],
|
||||
compile_multilib: "64",
|
||||
test_options: {
|
||||
// Legacy VAB launched in Android R.
|
||||
min_shipping_api_level: 30,
|
||||
test_runner_options: [
|
||||
{
|
||||
name: "force-no-test-error",
|
||||
value: "false",
|
||||
},
|
||||
],
|
||||
},
|
||||
}
|
||||
|
||||
|
|
@ -345,6 +372,7 @@ cc_binary {
|
|||
],
|
||||
srcs: [
|
||||
"snapshotctl.cpp",
|
||||
"scratch_super.cpp",
|
||||
],
|
||||
static_libs: [
|
||||
"libbrotli",
|
||||
|
|
@ -414,7 +442,7 @@ cc_test {
|
|||
"libstorage_literals_headers",
|
||||
],
|
||||
test_suites: [
|
||||
"device-tests",
|
||||
"general-tests",
|
||||
],
|
||||
test_options: {
|
||||
min_shipping_api_level: 30,
|
||||
|
|
|
|||
|
|
@ -229,6 +229,10 @@ message SnapshotUpdateStatus {
|
|||
|
||||
// Enable direct reads from source device
|
||||
bool o_direct = 12;
|
||||
|
||||
// Number of cow operations to be merged at once
|
||||
uint32 cow_op_merge_size = 13;
|
||||
|
||||
}
|
||||
|
||||
// Next: 10
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@
|
|||
// limitations under the License.
|
||||
|
||||
#include "device_info.h"
|
||||
#include "scratch_super.h"
|
||||
|
||||
#include <android-base/logging.h>
|
||||
#include <fs_mgr.h>
|
||||
|
|
@ -37,8 +38,24 @@ constexpr bool kIsRecovery = true;
|
|||
constexpr bool kIsRecovery = false;
|
||||
#endif
|
||||
|
||||
DeviceInfo::DeviceInfo() {
|
||||
std::string scratch_device = android::snapshot::GetScratchOtaMetadataPartition();
|
||||
if (!scratch_device.empty()) {
|
||||
std::string scratch_metadata =
|
||||
android::snapshot::MapScratchOtaMetadataPartition(scratch_device);
|
||||
if (!scratch_metadata.empty()) {
|
||||
SetMetadataDir(scratch_metadata);
|
||||
SetTempMetadata();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::string DeviceInfo::GetMetadataDir() const {
|
||||
return "/metadata/ota"s;
|
||||
return metadata_dir_;
|
||||
}
|
||||
|
||||
void DeviceInfo::SetMetadataDir(const std::string& value) {
|
||||
metadata_dir_ = value;
|
||||
}
|
||||
|
||||
std::string DeviceInfo::GetSlotSuffix() const {
|
||||
|
|
@ -104,6 +121,24 @@ bool DeviceInfo::IsFirstStageInit() const {
|
|||
return first_stage_init_;
|
||||
}
|
||||
|
||||
bool DeviceInfo::SetActiveBootSlot([[maybe_unused]] unsigned int slot) {
|
||||
#ifdef LIBSNAPSHOT_USE_HAL
|
||||
if (!EnsureBootHal()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
CommandResult result = boot_control_->SetActiveBootSlot(slot);
|
||||
if (!result.success) {
|
||||
LOG(ERROR) << "Error setting slot " << slot << " active: " << result.errMsg;
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
#else
|
||||
LOG(ERROR) << "HAL support not enabled.";
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
bool DeviceInfo::SetSlotAsUnbootable([[maybe_unused]] unsigned int slot) {
|
||||
#ifdef LIBSNAPSHOT_USE_HAL
|
||||
if (!EnsureBootHal()) {
|
||||
|
|
|
|||
|
|
@ -29,6 +29,7 @@ class DeviceInfo final : public SnapshotManager::IDeviceInfo {
|
|||
using MergeStatus = ::aidl::android::hardware::boot::MergeStatus;
|
||||
|
||||
public:
|
||||
DeviceInfo();
|
||||
std::string GetMetadataDir() const override;
|
||||
std::string GetSlotSuffix() const override;
|
||||
std::string GetOtherSlotSuffix() const override;
|
||||
|
|
@ -36,19 +37,25 @@ class DeviceInfo final : public SnapshotManager::IDeviceInfo {
|
|||
std::string GetSuperDevice(uint32_t slot) const override;
|
||||
bool IsOverlayfsSetup() const override;
|
||||
bool SetBootControlMergeStatus(MergeStatus status) override;
|
||||
bool SetActiveBootSlot(unsigned int slot) override;
|
||||
bool SetSlotAsUnbootable(unsigned int slot) override;
|
||||
bool IsRecovery() const override;
|
||||
std::unique_ptr<IImageManager> OpenImageManager() const override;
|
||||
bool IsFirstStageInit() const override;
|
||||
android::dm::IDeviceMapper& GetDeviceMapper() override;
|
||||
|
||||
void SetMetadataDir(const std::string& value);
|
||||
void set_first_stage_init(bool value) { first_stage_init_ = value; }
|
||||
bool IsTempMetadata() const override { return temp_metadata_; }
|
||||
void SetTempMetadata() { temp_metadata_ = true; }
|
||||
|
||||
private:
|
||||
bool EnsureBootHal();
|
||||
|
||||
android::fs_mgr::PartitionOpener opener_;
|
||||
bool first_stage_init_ = false;
|
||||
// Default value
|
||||
std::string metadata_dir_ = "/metadata/ota";
|
||||
bool temp_metadata_ = false;
|
||||
#ifdef LIBSNAPSHOT_USE_HAL
|
||||
std::unique_ptr<::android::hal::BootControlClient> boot_control_;
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -29,6 +29,7 @@ class MockDeviceInfo : public SnapshotManager::IDeviceInfo {
|
|||
MOCK_METHOD(const android::fs_mgr::IPartitionOpener&, GetPartitionOpener, (), (const));
|
||||
MOCK_METHOD(bool, IsOverlayfsSetup, (), (const, override));
|
||||
MOCK_METHOD(bool, SetBootControlMergeStatus, (MergeStatus status), (override));
|
||||
MOCK_METHOD(bool, SetActiveBootSlot, (unsigned int slot), (override));
|
||||
MOCK_METHOD(bool, SetSlotAsUnbootable, (unsigned int slot), (override));
|
||||
MOCK_METHOD(bool, IsRecovery, (), (const, override));
|
||||
MOCK_METHOD(bool, IsFirstStageInit, (), (const, override));
|
||||
|
|
|
|||
|
|
@ -104,12 +104,14 @@ class ISnapshotManager {
|
|||
virtual const android::fs_mgr::IPartitionOpener& GetPartitionOpener() const = 0;
|
||||
virtual bool IsOverlayfsSetup() const = 0;
|
||||
virtual bool SetBootControlMergeStatus(MergeStatus status) = 0;
|
||||
virtual bool SetActiveBootSlot(unsigned int slot) = 0;
|
||||
virtual bool SetSlotAsUnbootable(unsigned int slot) = 0;
|
||||
virtual bool IsRecovery() const = 0;
|
||||
virtual bool IsTestDevice() const { return false; }
|
||||
virtual bool IsFirstStageInit() const = 0;
|
||||
virtual std::unique_ptr<IImageManager> OpenImageManager() const = 0;
|
||||
virtual android::dm::IDeviceMapper& GetDeviceMapper() = 0;
|
||||
virtual bool IsTempMetadata() const = 0;
|
||||
|
||||
// Helper method for implementing OpenImageManager.
|
||||
std::unique_ptr<IImageManager> OpenImageManager(const std::string& gsid_dir) const;
|
||||
|
|
@ -328,6 +330,10 @@ class SnapshotManager final : public ISnapshotManager {
|
|||
// might be needed to perform first-stage mounts.
|
||||
static bool IsSnapshotManagerNeeded();
|
||||
|
||||
// Map the temp OTA metadata partition from super
|
||||
static bool MapTempOtaMetadataPartitionIfNeeded(
|
||||
const std::function<bool(const std::string&)>& init);
|
||||
|
||||
// Helper function for second stage init to restorecon on the rollback indicator.
|
||||
static std::string GetGlobalRollbackIndicatorPath();
|
||||
|
||||
|
|
@ -675,6 +681,8 @@ class SnapshotManager final : public ISnapshotManager {
|
|||
std::string GetBootSnapshotsWithoutSlotSwitchPath();
|
||||
std::string GetSnapuserdFromSystemPath();
|
||||
|
||||
bool HasForwardMergeIndicator();
|
||||
|
||||
const LpMetadata* ReadOldPartitionMetadata(LockedFile* lock);
|
||||
|
||||
bool MapAllPartitions(LockedFile* lock, const std::string& super_device, uint32_t slot,
|
||||
|
|
@ -785,11 +793,8 @@ class SnapshotManager final : public ISnapshotManager {
|
|||
bool UpdateForwardMergeIndicator(bool wipe);
|
||||
|
||||
// Helper for HandleImminentDataWipe.
|
||||
// Call ProcessUpdateState and handle states with special rules before data wipe. Specifically,
|
||||
// if |allow_forward_merge| and allow-forward-merge indicator exists, initiate merge if
|
||||
// necessary.
|
||||
UpdateState ProcessUpdateStateOnDataWipe(bool allow_forward_merge,
|
||||
const std::function<bool()>& callback);
|
||||
// Call ProcessUpdateState and handle states with special rules before data wipe.
|
||||
UpdateState ProcessUpdateStateOnDataWipe(const std::function<bool()>& callback);
|
||||
|
||||
// Return device string of a mapped image, or if it is not available, the mapped image path.
|
||||
bool GetMappedImageDeviceStringOrPath(const std::string& device_name,
|
||||
|
|
@ -831,6 +836,8 @@ class SnapshotManager final : public ISnapshotManager {
|
|||
// Check if direct reads are enabled for the source image
|
||||
bool UpdateUsesODirect(LockedFile* lock);
|
||||
|
||||
// Get value of maximum cow op merge size
|
||||
uint32_t GetUpdateCowOpMergeSize(LockedFile* lock);
|
||||
// Wrapper around libdm, with diagnostics.
|
||||
bool DeleteDeviceIfExists(const std::string& name,
|
||||
const std::chrono::milliseconds& timeout_ms = {});
|
||||
|
|
@ -846,7 +853,6 @@ class SnapshotManager final : public ISnapshotManager {
|
|||
std::string metadata_dir_;
|
||||
std::unique_ptr<IImageManager> images_;
|
||||
bool use_first_stage_snapuserd_ = false;
|
||||
bool in_factory_data_reset_ = false;
|
||||
std::function<bool(const std::string&)> uevent_regen_callback_;
|
||||
std::unique_ptr<SnapuserdClient> snapuserd_client_;
|
||||
std::unique_ptr<LpMetadata> old_partition_metadata_;
|
||||
|
|
|
|||
|
|
@ -30,6 +30,8 @@
|
|||
#include <storage_literals/storage_literals.h>
|
||||
#include <update_engine/update_metadata.pb.h>
|
||||
|
||||
#include "utility.h"
|
||||
|
||||
namespace android {
|
||||
namespace snapshot {
|
||||
|
||||
|
|
@ -77,7 +79,7 @@ class TestDeviceInfo : public SnapshotManager::IDeviceInfo {
|
|||
: TestDeviceInfo(fake_super) {
|
||||
set_slot_suffix(slot_suffix);
|
||||
}
|
||||
std::string GetMetadataDir() const override { return "/metadata/ota/test"s; }
|
||||
std::string GetMetadataDir() const override { return metadata_dir_; }
|
||||
std::string GetSlotSuffix() const override { return slot_suffix_; }
|
||||
std::string GetOtherSlotSuffix() const override { return slot_suffix_ == "_a" ? "_b" : "_a"; }
|
||||
std::string GetSuperDevice([[maybe_unused]] uint32_t slot) const override { return "super"; }
|
||||
|
|
@ -90,6 +92,7 @@ class TestDeviceInfo : public SnapshotManager::IDeviceInfo {
|
|||
}
|
||||
bool IsOverlayfsSetup() const override { return false; }
|
||||
bool IsRecovery() const override { return recovery_; }
|
||||
bool SetActiveBootSlot([[maybe_unused]] unsigned int slot) override { return true; }
|
||||
bool SetSlotAsUnbootable(unsigned int slot) override {
|
||||
unbootable_slots_.insert(slot);
|
||||
return true;
|
||||
|
|
@ -117,6 +120,7 @@ class TestDeviceInfo : public SnapshotManager::IDeviceInfo {
|
|||
void set_dm(android::dm::IDeviceMapper* dm) { dm_ = dm; }
|
||||
|
||||
MergeStatus merge_status() const { return merge_status_; }
|
||||
bool IsTempMetadata() const override { return temp_metadata_; }
|
||||
|
||||
private:
|
||||
std::string slot_suffix_ = "_a";
|
||||
|
|
@ -126,6 +130,8 @@ class TestDeviceInfo : public SnapshotManager::IDeviceInfo {
|
|||
bool first_stage_init_ = false;
|
||||
std::unordered_set<uint32_t> unbootable_slots_;
|
||||
android::dm::IDeviceMapper* dm_ = nullptr;
|
||||
std::string metadata_dir_ = "/metadata/ota/test";
|
||||
bool temp_metadata_ = false;
|
||||
};
|
||||
|
||||
class DeviceMapperWrapper : public android::dm::IDeviceMapper {
|
||||
|
|
@ -234,5 +240,21 @@ bool IsVirtualAbEnabled();
|
|||
|
||||
#define RETURN_IF_NON_VIRTUAL_AB() RETURN_IF_NON_VIRTUAL_AB_MSG("")
|
||||
|
||||
#define SKIP_IF_VENDOR_ON_ANDROID_S() \
|
||||
do { \
|
||||
if (IsVendorFromAndroid12()) \
|
||||
GTEST_SKIP() << "Skip test as Vendor partition is on Android S"; \
|
||||
} while (0)
|
||||
|
||||
#define RETURN_IF_VENDOR_ON_ANDROID_S_MSG(msg) \
|
||||
do { \
|
||||
if (IsVendorFromAndroid12()) { \
|
||||
std::cerr << (msg); \
|
||||
return; \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#define RETURN_IF_VENDOR_ON_ANDROID_S() RETURN_IF_VENDOR_ON_ANDROID_S_MSG("")
|
||||
|
||||
} // namespace snapshot
|
||||
} // namespace android
|
||||
|
|
|
|||
|
|
@ -82,7 +82,7 @@ class CowWriterV3 : public CowWriterBase {
|
|||
CowOperationType type);
|
||||
size_t GetCompressionFactor(const size_t blocks_to_compress, CowOperationType type) const;
|
||||
|
||||
constexpr bool IsBlockAligned(const size_t size) {
|
||||
constexpr bool IsBlockAligned(const uint64_t size) {
|
||||
// These are the only block size supported. Block size beyond 256k
|
||||
// may impact random read performance post OTA boot.
|
||||
const size_t values[] = {4_KiB, 8_KiB, 16_KiB, 32_KiB, 64_KiB, 128_KiB, 256_KiB};
|
||||
|
|
|
|||
|
|
@ -250,8 +250,8 @@ TEST_F(PartitionCowCreatorTest, CompressionEnabled) {
|
|||
.target_partition = system_b,
|
||||
.current_metadata = builder_a.get(),
|
||||
.current_suffix = "_a",
|
||||
.using_snapuserd = true,
|
||||
.update = &update};
|
||||
.update = &update,
|
||||
.using_snapuserd = true};
|
||||
|
||||
auto ret = creator.Run();
|
||||
ASSERT_TRUE(ret.has_value());
|
||||
|
|
@ -276,8 +276,8 @@ TEST_F(PartitionCowCreatorTest, CompressionWithNoManifest) {
|
|||
.target_partition = system_b,
|
||||
.current_metadata = builder_a.get(),
|
||||
.current_suffix = "_a",
|
||||
.using_snapuserd = true,
|
||||
.update = nullptr};
|
||||
.update = nullptr,
|
||||
.using_snapuserd = true};
|
||||
|
||||
auto ret = creator.Run();
|
||||
ASSERT_FALSE(ret.has_value());
|
||||
|
|
|
|||
416
fs_mgr/libsnapshot/scratch_super.cpp
Normal file
416
fs_mgr/libsnapshot/scratch_super.cpp
Normal file
|
|
@ -0,0 +1,416 @@
|
|||
// 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.
|
||||
|
||||
#include <dirent.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <linux/fs.h>
|
||||
#include <selinux/selinux.h>
|
||||
#include <stdlib.h>
|
||||
#include <sys/mount.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/statvfs.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/vfs.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <android-base/file.h>
|
||||
#include <android-base/logging.h>
|
||||
#include <android-base/macros.h>
|
||||
#include <android-base/properties.h>
|
||||
#include <android-base/scopeguard.h>
|
||||
#include <android-base/strings.h>
|
||||
#include <android-base/unique_fd.h>
|
||||
#include <ext4_utils/ext4_utils.h>
|
||||
|
||||
#include <libsnapshot/snapshot.h>
|
||||
|
||||
#include <fs_mgr.h>
|
||||
#include <fs_mgr_dm_linear.h>
|
||||
#include <fstab/fstab.h>
|
||||
#include <liblp/builder.h>
|
||||
#include <storage_literals/storage_literals.h>
|
||||
#include <algorithm>
|
||||
#include <filesystem>
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "device_info.h"
|
||||
#include "scratch_super.h"
|
||||
|
||||
using namespace std::literals;
|
||||
using namespace android::dm;
|
||||
using namespace android::fs_mgr;
|
||||
using namespace android::storage_literals;
|
||||
|
||||
namespace android {
|
||||
namespace snapshot {
|
||||
|
||||
static bool UmountScratch() {
|
||||
auto ota_dir = std::string(kOtaMetadataMount) + "/" + "ota";
|
||||
std::error_code ec;
|
||||
|
||||
if (std::filesystem::remove_all(ota_dir, ec) == static_cast<std::uintmax_t>(-1)) {
|
||||
LOG(ERROR) << "Failed to remove OTA directory: " << ec.message();
|
||||
return false;
|
||||
}
|
||||
|
||||
if (umount(kOtaMetadataMount) != 0) {
|
||||
PLOG(ERROR) << "UmountScratch failed";
|
||||
return false;
|
||||
}
|
||||
|
||||
LOG(INFO) << "umount scratch_super success";
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CleanupScratchOtaMetadataIfPresent(const ISnapshotManager::IDeviceInfo* info) {
|
||||
if (!UmountScratch()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
std::unique_ptr<MetadataBuilder> builder;
|
||||
const auto partition_name = android::base::Basename(kOtaMetadataMount);
|
||||
const std::vector<int> slots = {0, 1};
|
||||
|
||||
if (info == nullptr) {
|
||||
info = new android::snapshot::DeviceInfo();
|
||||
}
|
||||
|
||||
std::string super_device;
|
||||
if (info->IsTestDevice()) {
|
||||
super_device = "super";
|
||||
} else {
|
||||
super_device = kPhysicalDevice + fs_mgr_get_super_partition_name();
|
||||
}
|
||||
const auto& opener = info->GetPartitionOpener();
|
||||
std::string slot_suffix = info->GetSlotSuffix();
|
||||
// Walk both the slots and clean up metadata related to scratch space from
|
||||
// both the slots.
|
||||
for (auto slot : slots) {
|
||||
std::unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(opener, super_device, slot);
|
||||
if (!builder) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (builder->FindPartition(partition_name) != nullptr) {
|
||||
builder->RemovePartition(partition_name);
|
||||
auto metadata = builder->Export();
|
||||
if (!metadata) {
|
||||
return false;
|
||||
}
|
||||
if (!UpdatePartitionTable(info->GetPartitionOpener(), super_device, *metadata.get(),
|
||||
slot)) {
|
||||
LOG(ERROR) << "UpdatePartitionTable failed for slot: " << slot;
|
||||
return false;
|
||||
}
|
||||
if (DestroyLogicalPartition(partition_name)) {
|
||||
LOG(INFO) << "CleanupScratchOtaMetadata success for slot: " << slot;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool SetupOTADirs() {
|
||||
if (setfscreatecon(android::snapshot::kOtaMetadataFileContext)) {
|
||||
PLOG(ERROR) << "setfscreatecon failed: " << android::snapshot::kOtaMetadataFileContext;
|
||||
return false;
|
||||
}
|
||||
const auto ota_dir = std::string(kOtaMetadataMount) + "/" + "ota";
|
||||
if (mkdir(ota_dir.c_str(), 0755) != 0 && errno != EEXIST) {
|
||||
PLOG(ERROR) << "mkdir " << ota_dir;
|
||||
return false;
|
||||
}
|
||||
|
||||
const auto snapshot_dir = ota_dir + "/" + "snapshots";
|
||||
if (mkdir(snapshot_dir.c_str(), 0755) != 0 && errno != EEXIST) {
|
||||
PLOG(ERROR) << "mkdir " << snapshot_dir;
|
||||
return false;
|
||||
}
|
||||
if (setfscreatecon(nullptr)) {
|
||||
PLOG(ERROR) << "setfscreatecon null";
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool MountScratch(const std::string& device_path) {
|
||||
if (access(device_path.c_str(), R_OK | W_OK)) {
|
||||
LOG(ERROR) << "Path does not exist or is not readwrite: " << device_path;
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string filesystem_candidate;
|
||||
if (fs_mgr_is_ext4(device_path)) {
|
||||
filesystem_candidate = "ext4";
|
||||
} else {
|
||||
LOG(ERROR) << "Scratch partition is not ext4";
|
||||
return false;
|
||||
}
|
||||
if (setfscreatecon(android::snapshot::kOtaMetadataFileContext)) {
|
||||
PLOG(ERROR) << "setfscreatecon failed: " << android::snapshot::kOtaMetadataFileContext;
|
||||
return false;
|
||||
}
|
||||
if (mkdir(kOtaMetadataMount, 0755) && (errno != EEXIST)) {
|
||||
PLOG(ERROR) << "create " << kOtaMetadataMount;
|
||||
return false;
|
||||
}
|
||||
|
||||
android::fs_mgr::FstabEntry entry;
|
||||
entry.blk_device = device_path;
|
||||
entry.mount_point = kOtaMetadataMount;
|
||||
entry.flags = MS_NOATIME;
|
||||
entry.flags |= MS_SYNCHRONOUS;
|
||||
entry.fs_options = "nodiscard";
|
||||
fs_mgr_set_blk_ro(device_path, false);
|
||||
entry.fs_mgr_flags.check = true;
|
||||
|
||||
bool mounted = false;
|
||||
entry.fs_type = filesystem_candidate.c_str();
|
||||
if (fs_mgr_do_mount_one(entry) == 0) {
|
||||
mounted = true;
|
||||
}
|
||||
|
||||
if (setfscreatecon(nullptr)) {
|
||||
PLOG(ERROR) << "setfscreatecon null";
|
||||
return false;
|
||||
}
|
||||
if (!mounted) {
|
||||
rmdir(kOtaMetadataMount);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool MakeScratchFilesystem(const std::string& scratch_device) {
|
||||
std::string fs_type;
|
||||
std::string command;
|
||||
if (!access(kMkExt4, X_OK)) {
|
||||
fs_type = "ext4";
|
||||
command = kMkExt4 + " -F -b 4096 -t ext4 -m 0 -O has_journal -M "s + kOtaMetadataMount;
|
||||
} else {
|
||||
LOG(ERROR) << "No supported mkfs command or filesystem driver available, supported "
|
||||
"filesystems "
|
||||
"are: f2fs, ext4";
|
||||
return false;
|
||||
}
|
||||
command += " " + scratch_device + " >/dev/null 2>/dev/null </dev/null";
|
||||
fs_mgr_set_blk_ro(scratch_device, false);
|
||||
auto ret = system(command.c_str());
|
||||
if (ret) {
|
||||
LOG(ERROR) << "make " << fs_type << " filesystem on " << scratch_device
|
||||
<< " return=" << ret;
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool CreateDynamicScratch(const ISnapshotManager::IDeviceInfo* info,
|
||||
std::string* scratch_device) {
|
||||
const auto partition_name = android::base::Basename(kOtaMetadataMount);
|
||||
auto& dm = DeviceMapper::Instance();
|
||||
if (info == nullptr) {
|
||||
info = new android::snapshot::DeviceInfo();
|
||||
}
|
||||
|
||||
std::string super_device;
|
||||
if (info->IsTestDevice()) {
|
||||
super_device = "super";
|
||||
} else {
|
||||
super_device = kPhysicalDevice + fs_mgr_get_super_partition_name();
|
||||
}
|
||||
|
||||
bool partition_exists = dm.GetState(partition_name) != DmDeviceState::INVALID;
|
||||
if (partition_exists) {
|
||||
LOG(ERROR) << "Partition already exists: " << partition_name;
|
||||
return false;
|
||||
}
|
||||
|
||||
const auto& opener = info->GetPartitionOpener();
|
||||
std::string slot_suffix = info->GetSlotSuffix();
|
||||
int slot = SlotNumberForSlotSuffix(slot_suffix);
|
||||
std::unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(opener, super_device, slot);
|
||||
|
||||
if (!builder) {
|
||||
LOG(ERROR) << "open " << super_device << " failed";
|
||||
return false;
|
||||
}
|
||||
|
||||
auto partition = builder->FindPartition(partition_name);
|
||||
partition_exists = partition != nullptr;
|
||||
if (partition_exists) {
|
||||
LOG(ERROR) << "Partition exists in super metadata";
|
||||
return false;
|
||||
}
|
||||
|
||||
partition = builder->AddPartition(partition_name, LP_PARTITION_ATTR_NONE);
|
||||
if (!partition) {
|
||||
LOG(ERROR) << "AddPartition failed " << partition_name;
|
||||
return false;
|
||||
}
|
||||
|
||||
auto free_space = builder->AllocatableSpace() - builder->UsedSpace();
|
||||
if (free_space < kOtaMetadataPartitionSize) {
|
||||
LOG(ERROR) << "No space in super partition. Free space: " << free_space
|
||||
<< " Requested space: " << kOtaMetadataPartitionSize;
|
||||
return false;
|
||||
}
|
||||
|
||||
LOG(INFO) << "CreateDynamicScratch: free_space: " << free_space
|
||||
<< " scratch_size: " << kOtaMetadataPartitionSize << " slot_number: " << slot;
|
||||
|
||||
if (!builder->ResizePartition(partition, kOtaMetadataPartitionSize)) {
|
||||
LOG(ERROR) << "ResizePartition failed: " << partition_name << " free_space: " << free_space
|
||||
<< " scratch_size: " << kOtaMetadataPartitionSize;
|
||||
return false;
|
||||
}
|
||||
|
||||
auto metadata = builder->Export();
|
||||
CreateLogicalPartitionParams params;
|
||||
|
||||
if (!metadata ||
|
||||
!UpdatePartitionTable(info->GetPartitionOpener(), super_device, *metadata.get(), slot)) {
|
||||
LOG(ERROR) << "UpdatePartitionTable failed: " << partition_name;
|
||||
return false;
|
||||
}
|
||||
params = {
|
||||
.block_device = super_device,
|
||||
.metadata_slot = slot,
|
||||
.partition_name = partition_name,
|
||||
.force_writable = true,
|
||||
.timeout_ms = 10s,
|
||||
.partition_opener = &info->GetPartitionOpener(),
|
||||
};
|
||||
|
||||
if (!CreateLogicalPartition(params, scratch_device)) {
|
||||
LOG(ERROR) << "CreateLogicalPartition failed";
|
||||
return false;
|
||||
}
|
||||
|
||||
LOG(INFO) << "Scratch device created successfully: " << *scratch_device << " slot: " << slot;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool IsScratchOtaMetadataOnSuper() {
|
||||
auto partition_name = android::base::Basename(kOtaMetadataMount);
|
||||
auto source_slot = fs_mgr_get_slot_suffix();
|
||||
auto source_slot_number = SlotNumberForSlotSuffix(source_slot);
|
||||
|
||||
const auto super_device =
|
||||
kPhysicalDevice + fs_mgr_get_super_partition_name(!source_slot_number);
|
||||
|
||||
auto metadata = android::fs_mgr::ReadMetadata(super_device, !source_slot_number);
|
||||
if (!metadata) {
|
||||
return false;
|
||||
}
|
||||
auto partition = android::fs_mgr::FindPartition(*metadata.get(), partition_name);
|
||||
if (!partition) {
|
||||
return false;
|
||||
}
|
||||
|
||||
auto& dm = DeviceMapper::Instance();
|
||||
if (dm.GetState(partition_name) == DmDeviceState::ACTIVE) {
|
||||
LOG(INFO) << "Partition: " << partition_name << " is active";
|
||||
return true;
|
||||
}
|
||||
|
||||
CreateLogicalPartitionParams params = {
|
||||
.block_device = super_device,
|
||||
.metadata = metadata.get(),
|
||||
.partition = partition,
|
||||
};
|
||||
|
||||
std::string scratch_path;
|
||||
if (!CreateLogicalPartition(params, &scratch_path)) {
|
||||
LOG(ERROR) << "Could not create logical partition: " << partition_name;
|
||||
return false;
|
||||
}
|
||||
LOG(INFO) << "Scratch device: " << scratch_path << " created successfully";
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
std::string GetScratchOtaMetadataPartition() {
|
||||
std::string device;
|
||||
auto& dm = DeviceMapper::Instance();
|
||||
auto partition_name = android::base::Basename(kOtaMetadataMount);
|
||||
|
||||
bool invalid_partition = (dm.GetState(partition_name) == DmDeviceState::INVALID);
|
||||
if (!invalid_partition && dm.GetDmDevicePathByName(partition_name, &device)) {
|
||||
return device;
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
static bool ScratchAlreadyMounted(const std::string& mount_point) {
|
||||
android::fs_mgr::Fstab fstab;
|
||||
if (!ReadFstabFromProcMounts(&fstab)) {
|
||||
return false;
|
||||
}
|
||||
for (const auto& entry : GetEntriesForMountPoint(&fstab, mount_point)) {
|
||||
if (entry->fs_type == "ext4") {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string MapScratchOtaMetadataPartition(const std::string& scratch_device) {
|
||||
if (!ScratchAlreadyMounted(kOtaMetadataMount)) {
|
||||
if (!MountScratch(scratch_device)) {
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
auto ota_dir = std::string(kOtaMetadataMount) + "/" + "ota";
|
||||
if (access(ota_dir.c_str(), F_OK) != 0) {
|
||||
return "";
|
||||
}
|
||||
return ota_dir;
|
||||
}
|
||||
|
||||
// Entry point to create a scratch device on super partition
|
||||
// This will create a 1MB space in super. The space will be
|
||||
// from the current active slot. Ext4 filesystem will be created
|
||||
// on this scratch device and all the OTA related directories
|
||||
// will be created.
|
||||
bool CreateScratchOtaMetadataOnSuper(const ISnapshotManager::IDeviceInfo* info) {
|
||||
std::string scratch_device;
|
||||
|
||||
if (!CreateDynamicScratch(info, &scratch_device)) {
|
||||
LOG(ERROR) << "CreateDynamicScratch failed";
|
||||
return false;
|
||||
}
|
||||
if (!MakeScratchFilesystem(scratch_device)) {
|
||||
LOG(ERROR) << "MakeScratchFilesystem failed";
|
||||
return false;
|
||||
}
|
||||
if (!MountScratch(scratch_device)) {
|
||||
LOG(ERROR) << "MountScratch failed";
|
||||
return false;
|
||||
}
|
||||
if (!SetupOTADirs()) {
|
||||
LOG(ERROR) << "SetupOTADirs failed";
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace snapshot
|
||||
} // namespace android
|
||||
33
fs_mgr/libsnapshot/scratch_super.h
Normal file
33
fs_mgr/libsnapshot/scratch_super.h
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
// 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
|
||||
|
||||
namespace android {
|
||||
namespace snapshot {
|
||||
|
||||
constexpr char kMkExt4[] = "/system/bin/mke2fs";
|
||||
constexpr char kOtaMetadataFileContext[] = "u:object_r:ota_metadata_file:s0";
|
||||
constexpr char kOtaMetadataMount[] = "/mnt/scratch_ota_metadata_super";
|
||||
const size_t kOtaMetadataPartitionSize = uint64_t(1 * 1024 * 1024);
|
||||
constexpr char kPhysicalDevice[] = "/dev/block/by-name/";
|
||||
|
||||
bool IsScratchOtaMetadataOnSuper();
|
||||
std::string GetScratchOtaMetadataPartition();
|
||||
std::string MapScratchOtaMetadataPartition(const std::string& device);
|
||||
bool CreateScratchOtaMetadataOnSuper(const ISnapshotManager::IDeviceInfo* info = nullptr);
|
||||
bool CleanupScratchOtaMetadataIfPresent(const ISnapshotManager::IDeviceInfo* info = nullptr);
|
||||
|
||||
} // namespace snapshot
|
||||
} // namespace android
|
||||
|
|
@ -22,6 +22,7 @@
|
|||
#include <sys/unistd.h>
|
||||
#include <sys/xattr.h>
|
||||
|
||||
#include <chrono>
|
||||
#include <filesystem>
|
||||
#include <optional>
|
||||
#include <thread>
|
||||
|
|
@ -47,6 +48,7 @@
|
|||
#include <libsnapshot/snapshot_stats.h>
|
||||
#include "device_info.h"
|
||||
#include "partition_cow_creator.h"
|
||||
#include "scratch_super.h"
|
||||
#include "snapshot_metadata_updater.h"
|
||||
#include "utility.h"
|
||||
|
||||
|
|
@ -116,7 +118,11 @@ std::unique_ptr<SnapshotManager> SnapshotManager::New(IDeviceInfo* info) {
|
|||
info = new DeviceInfo();
|
||||
}
|
||||
|
||||
return std::unique_ptr<SnapshotManager>(new SnapshotManager(info));
|
||||
auto sm = std::unique_ptr<SnapshotManager>(new SnapshotManager(info));
|
||||
if (info->IsTempMetadata()) {
|
||||
LOG(INFO) << "Using temp metadata from super";
|
||||
}
|
||||
return sm;
|
||||
}
|
||||
|
||||
std::unique_ptr<SnapshotManager> SnapshotManager::NewForFirstStageMount(IDeviceInfo* info) {
|
||||
|
|
@ -1109,6 +1115,13 @@ UpdateState SnapshotManager::ProcessUpdateState(const std::function<bool()>& cal
|
|||
if (result.state == UpdateState::MergeFailed) {
|
||||
AcknowledgeMergeFailure(result.failure_code);
|
||||
}
|
||||
|
||||
if (result.state == UpdateState::MergeCompleted) {
|
||||
if (device_->IsTempMetadata()) {
|
||||
CleanupScratchOtaMetadataIfPresent();
|
||||
}
|
||||
}
|
||||
|
||||
if (result.state != UpdateState::Merging) {
|
||||
// Either there is no merge, or the merge was finished, so no need
|
||||
// to keep waiting.
|
||||
|
|
@ -1708,6 +1721,10 @@ bool SnapshotManager::PerformInitTransition(InitTransition transition,
|
|||
if (UpdateUsesODirect(lock.get())) {
|
||||
snapuserd_argv->emplace_back("-o_direct");
|
||||
}
|
||||
uint cow_op_merge_size = GetUpdateCowOpMergeSize(lock.get());
|
||||
if (cow_op_merge_size != 0) {
|
||||
snapuserd_argv->emplace_back("-cow_op_merge_size=" + std::to_string(cow_op_merge_size));
|
||||
}
|
||||
}
|
||||
|
||||
size_t num_cows = 0;
|
||||
|
|
@ -2130,6 +2147,11 @@ bool SnapshotManager::UpdateUsesODirect(LockedFile* lock) {
|
|||
return update_status.o_direct();
|
||||
}
|
||||
|
||||
uint32_t SnapshotManager::GetUpdateCowOpMergeSize(LockedFile* lock) {
|
||||
SnapshotUpdateStatus update_status = ReadSnapshotUpdateStatus(lock);
|
||||
return update_status.cow_op_merge_size();
|
||||
}
|
||||
|
||||
bool SnapshotManager::MarkSnapuserdFromSystem() {
|
||||
auto path = GetSnapuserdFromSystemPath();
|
||||
|
||||
|
|
@ -2300,7 +2322,27 @@ bool SnapshotManager::ListSnapshots(LockedFile* lock, std::vector<std::string>*
|
|||
}
|
||||
|
||||
bool SnapshotManager::IsSnapshotManagerNeeded() {
|
||||
return access(kBootIndicatorPath, F_OK) == 0;
|
||||
if (access(kBootIndicatorPath, F_OK) == 0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (IsScratchOtaMetadataOnSuper()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool SnapshotManager::MapTempOtaMetadataPartitionIfNeeded(
|
||||
const std::function<bool(const std::string&)>& init) {
|
||||
auto device = android::snapshot::GetScratchOtaMetadataPartition();
|
||||
if (!device.empty()) {
|
||||
init(device);
|
||||
if (android::snapshot::MapScratchOtaMetadataPartition(device).empty()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
std::string SnapshotManager::GetGlobalRollbackIndicatorPath() {
|
||||
|
|
@ -2387,6 +2429,12 @@ bool SnapshotManager::MapAllPartitions(LockedFile* lock, const std::string& supe
|
|||
continue;
|
||||
}
|
||||
|
||||
if (GetPartitionName(partition) ==
|
||||
android::base::Basename(android::snapshot::kOtaMetadataMount)) {
|
||||
LOG(INFO) << "Partition: " << GetPartitionName(partition) << " skipping";
|
||||
continue;
|
||||
}
|
||||
|
||||
CreateLogicalPartitionParams params = {
|
||||
.block_device = super_device,
|
||||
.metadata = metadata.get(),
|
||||
|
|
@ -2892,10 +2940,12 @@ bool SnapshotManager::UnmapAllSnapshots() {
|
|||
}
|
||||
|
||||
bool SnapshotManager::UnmapAllSnapshots(LockedFile* lock) {
|
||||
LOG(INFO) << "Lock acquired for " << __FUNCTION__;
|
||||
std::vector<std::string> snapshots;
|
||||
if (!ListSnapshots(lock, &snapshots)) {
|
||||
return false;
|
||||
}
|
||||
LOG(INFO) << "Found " << snapshots.size() << " partitions with snapshots";
|
||||
|
||||
for (const auto& snapshot : snapshots) {
|
||||
if (!UnmapPartitionWithSnapshot(lock, snapshot)) {
|
||||
|
|
@ -2903,6 +2953,7 @@ bool SnapshotManager::UnmapAllSnapshots(LockedFile* lock) {
|
|||
return false;
|
||||
}
|
||||
}
|
||||
LOG(INFO) << "Unmapped " << snapshots.size() << " partitions with snapshots";
|
||||
|
||||
// Terminate the daemon and release the snapuserd_client_ object.
|
||||
// If we need to re-connect with the daemon, EnsureSnapuserdConnected()
|
||||
|
|
@ -2918,6 +2969,7 @@ bool SnapshotManager::UnmapAllSnapshots(LockedFile* lock) {
|
|||
|
||||
auto SnapshotManager::OpenFile(const std::string& file,
|
||||
int lock_flags) -> std::unique_ptr<LockedFile> {
|
||||
const auto start = std::chrono::system_clock::now();
|
||||
unique_fd fd(open(file.c_str(), O_RDONLY | O_CLOEXEC | O_NOFOLLOW));
|
||||
if (fd < 0) {
|
||||
PLOG(ERROR) << "Open failed: " << file;
|
||||
|
|
@ -2930,6 +2982,11 @@ auto SnapshotManager::OpenFile(const std::string& file,
|
|||
// For simplicity, we want to CHECK that lock_mode == LOCK_EX, in some
|
||||
// calls, so strip extra flags.
|
||||
int lock_mode = lock_flags & (LOCK_EX | LOCK_SH);
|
||||
const auto end = std::chrono::system_clock::now();
|
||||
const auto duration_ms = std::chrono::duration_cast<std::chrono::milliseconds>(end - start);
|
||||
if (duration_ms >= 1000ms) {
|
||||
LOG(INFO) << "Taking lock on " << file << " took " << duration_ms.count() << "ms";
|
||||
}
|
||||
return std::make_unique<LockedFile>(file, std::move(fd), lock_mode);
|
||||
}
|
||||
|
||||
|
|
@ -3082,6 +3139,7 @@ bool SnapshotManager::WriteUpdateState(LockedFile* lock, UpdateState state,
|
|||
status.set_io_uring_enabled(old_status.io_uring_enabled());
|
||||
status.set_legacy_snapuserd(old_status.legacy_snapuserd());
|
||||
status.set_o_direct(old_status.o_direct());
|
||||
status.set_cow_op_merge_size(old_status.cow_op_merge_size());
|
||||
}
|
||||
return WriteSnapshotUpdateStatus(lock, status);
|
||||
}
|
||||
|
|
@ -3464,6 +3522,8 @@ Return SnapshotManager::CreateUpdateSnapshots(const DeltaArchiveManifest& manife
|
|||
status.set_legacy_snapuserd(true);
|
||||
LOG(INFO) << "Setting legacy_snapuserd to true";
|
||||
}
|
||||
status.set_cow_op_merge_size(
|
||||
android::base::GetUintProperty<uint32_t>("ro.virtual_ab.cow_op_merge_size", 0));
|
||||
} else if (legacy_compression) {
|
||||
LOG(INFO) << "Virtual A/B using legacy snapuserd";
|
||||
} else {
|
||||
|
|
@ -3899,6 +3959,7 @@ bool SnapshotManager::Dump(std::ostream& os) {
|
|||
ss << "Using userspace snapshots: " << update_status.userspace_snapshots() << std::endl;
|
||||
ss << "Using io_uring: " << update_status.io_uring_enabled() << std::endl;
|
||||
ss << "Using o_direct: " << update_status.o_direct() << std::endl;
|
||||
ss << "Cow op merge size (0 for uncapped): " << update_status.cow_op_merge_size() << std::endl;
|
||||
ss << "Using XOR compression: " << GetXorCompressionEnabledProperty() << std::endl;
|
||||
ss << "Current slot: " << device_->GetSlotSuffix() << std::endl;
|
||||
ss << "Boot indicator: booting from " << GetCurrentSlot() << " slot" << std::endl;
|
||||
|
|
@ -3982,44 +4043,90 @@ bool SnapshotManager::HandleImminentDataWipe(const std::function<void()>& callba
|
|||
// We allow the wipe to continue, because if we can't mount /metadata,
|
||||
// it is unlikely the device would have booted anyway. If there is no
|
||||
// metadata partition, then the device predates Virtual A/B.
|
||||
LOG(INFO) << "/metadata not found; allowing wipe.";
|
||||
return true;
|
||||
}
|
||||
|
||||
// Check this early, so we don't accidentally start trying to populate
|
||||
// the state file in recovery. Note we don't call GetUpdateState since
|
||||
// we want errors in acquiring the lock to be propagated, instead of
|
||||
// returning UpdateState::None.
|
||||
auto state_file = GetStateFilePath();
|
||||
if (access(state_file.c_str(), F_OK) != 0 && errno == ENOENT) {
|
||||
return true;
|
||||
}
|
||||
|
||||
auto slot_number = SlotNumberForSlotSuffix(device_->GetSlotSuffix());
|
||||
auto super_path = device_->GetSuperDevice(slot_number);
|
||||
if (!CreateLogicalAndSnapshotPartitions(super_path, 20s)) {
|
||||
LOG(ERROR) << "Unable to map partitions to complete merge.";
|
||||
return false;
|
||||
}
|
||||
|
||||
auto process_callback = [&]() -> bool {
|
||||
if (callback) {
|
||||
callback();
|
||||
// This could happen if /metadata mounted but there is no filesystem
|
||||
// structure. Weird, but we have to assume there's no OTA pending, and
|
||||
// thus we let the wipe proceed.
|
||||
UpdateState state;
|
||||
{
|
||||
auto lock = LockExclusive();
|
||||
if (!lock) {
|
||||
LOG(ERROR) << "Unable to determine update state; allowing wipe.";
|
||||
return true;
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
in_factory_data_reset_ = true;
|
||||
UpdateState state =
|
||||
ProcessUpdateStateOnDataWipe(true /* allow_forward_merge */, process_callback);
|
||||
in_factory_data_reset_ = false;
|
||||
|
||||
if (state == UpdateState::MergeFailed) {
|
||||
return false;
|
||||
state = ReadUpdateState(lock.get());
|
||||
LOG(INFO) << "Update state before wipe: " << state << "; slot: " << GetCurrentSlot()
|
||||
<< "; suffix: " << device_->GetSlotSuffix();
|
||||
}
|
||||
|
||||
// Nothing should be depending on partitions now, so unmap them all.
|
||||
if (!UnmapAllPartitionsInRecovery()) {
|
||||
LOG(ERROR) << "Unable to unmap all partitions; fastboot may fail to flash.";
|
||||
bool try_merge = false;
|
||||
switch (state) {
|
||||
case UpdateState::None:
|
||||
case UpdateState::Initiated:
|
||||
LOG(INFO) << "Wipe is not impacted by update state; allowing wipe.";
|
||||
break;
|
||||
case UpdateState::Unverified:
|
||||
if (GetCurrentSlot() != Slot::Target) {
|
||||
LOG(INFO) << "Wipe is not impacted by rolled back update; allowing wipe";
|
||||
break;
|
||||
}
|
||||
if (!HasForwardMergeIndicator()) {
|
||||
auto slot_number = SlotNumberForSlotSuffix(device_->GetSlotSuffix());
|
||||
auto other_slot_number = SlotNumberForSlotSuffix(device_->GetOtherSlotSuffix());
|
||||
|
||||
// We're not allowed to forward merge, so forcefully rollback the
|
||||
// slot switch.
|
||||
LOG(INFO) << "Allowing wipe due to lack of forward merge indicator; reverting to "
|
||||
"old slot since update will be deleted.";
|
||||
device_->SetSlotAsUnbootable(slot_number);
|
||||
device_->SetActiveBootSlot(other_slot_number);
|
||||
break;
|
||||
}
|
||||
|
||||
// Forward merge indicator means we have to mount snapshots and try to merge.
|
||||
LOG(INFO) << "Forward merge indicator is present.";
|
||||
try_merge = true;
|
||||
break;
|
||||
case UpdateState::Merging:
|
||||
case UpdateState::MergeFailed:
|
||||
try_merge = true;
|
||||
break;
|
||||
case UpdateState::MergeNeedsReboot:
|
||||
case UpdateState::Cancelled:
|
||||
LOG(INFO) << "Unexpected update state in recovery; allowing wipe.";
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (try_merge) {
|
||||
auto slot_number = SlotNumberForSlotSuffix(device_->GetSlotSuffix());
|
||||
auto super_path = device_->GetSuperDevice(slot_number);
|
||||
if (!CreateLogicalAndSnapshotPartitions(super_path, 20s)) {
|
||||
LOG(ERROR) << "Unable to map partitions to complete merge.";
|
||||
return false;
|
||||
}
|
||||
|
||||
auto process_callback = [&]() -> bool {
|
||||
if (callback) {
|
||||
callback();
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
state = ProcessUpdateStateOnDataWipe(process_callback);
|
||||
if (state == UpdateState::MergeFailed) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Nothing should be depending on partitions now, so unmap them all.
|
||||
if (!UnmapAllPartitionsInRecovery()) {
|
||||
LOG(ERROR) << "Unable to unmap all partitions; fastboot may fail to flash.";
|
||||
}
|
||||
}
|
||||
|
||||
if (state != UpdateState::None) {
|
||||
|
|
@ -4065,58 +4172,40 @@ bool SnapshotManager::FinishMergeInRecovery() {
|
|||
return true;
|
||||
}
|
||||
|
||||
UpdateState SnapshotManager::ProcessUpdateStateOnDataWipe(bool allow_forward_merge,
|
||||
const std::function<bool()>& callback) {
|
||||
auto slot_number = SlotNumberForSlotSuffix(device_->GetSlotSuffix());
|
||||
UpdateState state = ProcessUpdateState(callback);
|
||||
LOG(INFO) << "Update state in recovery: " << state;
|
||||
switch (state) {
|
||||
case UpdateState::MergeFailed:
|
||||
LOG(ERROR) << "Unrecoverable merge failure detected.";
|
||||
return state;
|
||||
case UpdateState::Unverified: {
|
||||
// If an OTA was just applied but has not yet started merging:
|
||||
//
|
||||
// - if forward merge is allowed, initiate merge and call
|
||||
// ProcessUpdateState again.
|
||||
//
|
||||
// - if forward merge is not allowed, we
|
||||
// have no choice but to revert slots, because the current slot will
|
||||
// immediately become unbootable. Rather than wait for the device
|
||||
// to reboot N times until a rollback, we proactively disable the
|
||||
// new slot instead.
|
||||
//
|
||||
// Since the rollback is inevitable, we don't treat a HAL failure
|
||||
// as an error here.
|
||||
auto slot = GetCurrentSlot();
|
||||
if (slot == Slot::Target) {
|
||||
if (allow_forward_merge &&
|
||||
access(GetForwardMergeIndicatorPath().c_str(), F_OK) == 0) {
|
||||
LOG(INFO) << "Forward merge allowed, initiating merge now.";
|
||||
|
||||
if (!InitiateMerge()) {
|
||||
LOG(ERROR) << "Failed to initiate merge on data wipe.";
|
||||
return UpdateState::MergeFailed;
|
||||
}
|
||||
return ProcessUpdateStateOnDataWipe(false /* allow_forward_merge */, callback);
|
||||
UpdateState SnapshotManager::ProcessUpdateStateOnDataWipe(const std::function<bool()>& callback) {
|
||||
while (true) {
|
||||
UpdateState state = ProcessUpdateState(callback);
|
||||
LOG(INFO) << "Processed updated state in recovery: " << state;
|
||||
switch (state) {
|
||||
case UpdateState::MergeFailed:
|
||||
LOG(ERROR) << "Unrecoverable merge failure detected.";
|
||||
return state;
|
||||
case UpdateState::Unverified: {
|
||||
// Unverified was already handled earlier, in HandleImminentDataWipe,
|
||||
// but it will fall through here if a forward merge is required.
|
||||
//
|
||||
// If InitiateMerge fails, we early return. If it succeeds, then we
|
||||
// are guaranteed that the next call to ProcessUpdateState will not
|
||||
// return Unverified.
|
||||
if (!InitiateMerge()) {
|
||||
LOG(ERROR) << "Failed to initiate merge on data wipe.";
|
||||
return UpdateState::MergeFailed;
|
||||
}
|
||||
|
||||
LOG(ERROR) << "Reverting to old slot since update will be deleted.";
|
||||
device_->SetSlotAsUnbootable(slot_number);
|
||||
} else {
|
||||
LOG(INFO) << "Booting from " << slot << " slot, no action is taken.";
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
case UpdateState::MergeNeedsReboot:
|
||||
// We shouldn't get here, because nothing is depending on
|
||||
// logical partitions.
|
||||
LOG(ERROR) << "Unexpected merge-needs-reboot state in recovery.";
|
||||
return state;
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
case UpdateState::MergeNeedsReboot:
|
||||
// We shouldn't get here, because nothing is depending on
|
||||
// logical partitions.
|
||||
LOG(ERROR) << "Unexpected merge-needs-reboot state in recovery.";
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return state;
|
||||
}
|
||||
|
||||
bool SnapshotManager::HasForwardMergeIndicator() {
|
||||
return access(GetForwardMergeIndicatorPath().c_str(), F_OK) == 0;
|
||||
}
|
||||
|
||||
bool SnapshotManager::EnsureNoOverflowSnapshot(LockedFile* lock) {
|
||||
|
|
|
|||
|
|
@ -25,7 +25,7 @@ namespace snapshot {
|
|||
|
||||
SnapshotMergeStats* SnapshotMergeStats::GetInstance(SnapshotManager& parent) {
|
||||
static SnapshotMergeStats g_instance(parent.GetMergeStateFilePath());
|
||||
CHECK(g_instance.path_ == parent.GetMergeStateFilePath());
|
||||
CHECK_EQ(g_instance.path_, parent.GetMergeStateFilePath());
|
||||
return &g_instance;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -47,6 +47,7 @@
|
|||
#include <android/snapshot/snapshot.pb.h>
|
||||
#include <libsnapshot/test_helpers.h>
|
||||
#include "partition_cow_creator.h"
|
||||
#include "scratch_super.h"
|
||||
#include "utility.h"
|
||||
|
||||
// Mock classes are not used. Header included to ensure mocked class definition aligns with the
|
||||
|
|
@ -122,6 +123,7 @@ class SnapshotTest : public ::testing::Test {
|
|||
LOG(INFO) << "Starting test: " << test_name_;
|
||||
|
||||
SKIP_IF_NON_VIRTUAL_AB();
|
||||
SKIP_IF_VENDOR_ON_ANDROID_S();
|
||||
|
||||
SetupProperties();
|
||||
if (!DeviceSupportsMode()) {
|
||||
|
|
@ -168,6 +170,7 @@ class SnapshotTest : public ::testing::Test {
|
|||
|
||||
void TearDown() override {
|
||||
RETURN_IF_NON_VIRTUAL_AB();
|
||||
RETURN_IF_VENDOR_ON_ANDROID_S();
|
||||
|
||||
LOG(INFO) << "Tearing down SnapshotTest test: " << test_name_;
|
||||
|
||||
|
|
@ -1015,6 +1018,7 @@ class SnapshotUpdateTest : public SnapshotTest {
|
|||
public:
|
||||
void SetUp() override {
|
||||
SKIP_IF_NON_VIRTUAL_AB();
|
||||
SKIP_IF_VENDOR_ON_ANDROID_S();
|
||||
|
||||
SnapshotTest::SetUp();
|
||||
if (!image_manager_) {
|
||||
|
|
@ -1097,6 +1101,7 @@ class SnapshotUpdateTest : public SnapshotTest {
|
|||
}
|
||||
void TearDown() override {
|
||||
RETURN_IF_NON_VIRTUAL_AB();
|
||||
RETURN_IF_VENDOR_ON_ANDROID_S();
|
||||
|
||||
LOG(INFO) << "Tearing down SnapshotUpdateTest test: " << test_name_;
|
||||
|
||||
|
|
@ -1338,6 +1343,15 @@ class SnapshotUpdateTest : public SnapshotTest {
|
|||
DynamicPartitionGroup* group_ = nullptr;
|
||||
};
|
||||
|
||||
TEST_F(SnapshotUpdateTest, SuperOtaMetadataTest) {
|
||||
auto info = new TestDeviceInfo(fake_super);
|
||||
ASSERT_TRUE(CreateScratchOtaMetadataOnSuper(info));
|
||||
std::string scratch_device = GetScratchOtaMetadataPartition();
|
||||
ASSERT_NE(scratch_device, "");
|
||||
ASSERT_NE(MapScratchOtaMetadataPartition(scratch_device), "");
|
||||
ASSERT_TRUE(CleanupScratchOtaMetadataIfPresent(info));
|
||||
}
|
||||
|
||||
// Test full update flow executed by update_engine. Some partitions uses super empty space,
|
||||
// some uses images, and some uses both.
|
||||
// Also test UnmapUpdateSnapshot unmaps everything.
|
||||
|
|
@ -1978,6 +1992,8 @@ TEST_F(MetadataMountedTest, Android) {
|
|||
}
|
||||
|
||||
TEST_F(MetadataMountedTest, Recovery) {
|
||||
GTEST_SKIP() << "b/350715463";
|
||||
|
||||
test_device->set_recovery(true);
|
||||
metadata_dir_ = test_device->GetMetadataDir();
|
||||
|
||||
|
|
@ -2096,10 +2112,10 @@ TEST_F(SnapshotUpdateTest, DataWipeRollbackInRecovery) {
|
|||
test_device->set_recovery(true);
|
||||
auto new_sm = NewManagerForFirstStageMount(test_device);
|
||||
|
||||
EXPECT_EQ(new_sm->GetUpdateState(), UpdateState::Unverified);
|
||||
ASSERT_TRUE(new_sm->HandleImminentDataWipe());
|
||||
// Manually mount metadata so that we can call GetUpdateState() below.
|
||||
MountMetadata();
|
||||
EXPECT_EQ(new_sm->GetUpdateState(), UpdateState::None);
|
||||
EXPECT_TRUE(test_device->IsSlotUnbootable(1));
|
||||
EXPECT_FALSE(test_device->IsSlotUnbootable(0));
|
||||
}
|
||||
|
|
@ -2121,6 +2137,7 @@ TEST_F(SnapshotUpdateTest, DataWipeAfterRollback) {
|
|||
test_device->set_recovery(true);
|
||||
auto new_sm = NewManagerForFirstStageMount(test_device);
|
||||
|
||||
EXPECT_EQ(new_sm->GetUpdateState(), UpdateState::Unverified);
|
||||
ASSERT_TRUE(new_sm->HandleImminentDataWipe());
|
||||
EXPECT_EQ(new_sm->GetUpdateState(), UpdateState::None);
|
||||
EXPECT_FALSE(test_device->IsSlotUnbootable(0));
|
||||
|
|
@ -2129,10 +2146,6 @@ TEST_F(SnapshotUpdateTest, DataWipeAfterRollback) {
|
|||
|
||||
// Test update package that requests data wipe.
|
||||
TEST_F(SnapshotUpdateTest, DataWipeRequiredInPackage) {
|
||||
if (ShouldSkipLegacyMerging()) {
|
||||
GTEST_SKIP() << "Skipping legacy merge in test";
|
||||
}
|
||||
|
||||
AddOperationForPartitions();
|
||||
// Execute the update.
|
||||
ASSERT_TRUE(sm->BeginUpdate());
|
||||
|
|
@ -2151,6 +2164,7 @@ TEST_F(SnapshotUpdateTest, DataWipeRequiredInPackage) {
|
|||
test_device->set_recovery(true);
|
||||
auto new_sm = NewManagerForFirstStageMount(test_device);
|
||||
|
||||
EXPECT_EQ(new_sm->GetUpdateState(), UpdateState::Unverified);
|
||||
ASSERT_TRUE(new_sm->HandleImminentDataWipe());
|
||||
// Manually mount metadata so that we can call GetUpdateState() below.
|
||||
MountMetadata();
|
||||
|
|
@ -2172,10 +2186,6 @@ TEST_F(SnapshotUpdateTest, DataWipeRequiredInPackage) {
|
|||
|
||||
// Test update package that requests data wipe.
|
||||
TEST_F(SnapshotUpdateTest, DataWipeWithStaleSnapshots) {
|
||||
if (ShouldSkipLegacyMerging()) {
|
||||
GTEST_SKIP() << "Skipping legacy merge in test";
|
||||
}
|
||||
|
||||
AddOperationForPartitions();
|
||||
|
||||
// Execute the update.
|
||||
|
|
@ -2216,6 +2226,7 @@ TEST_F(SnapshotUpdateTest, DataWipeWithStaleSnapshots) {
|
|||
test_device->set_recovery(true);
|
||||
auto new_sm = NewManagerForFirstStageMount(test_device);
|
||||
|
||||
EXPECT_EQ(new_sm->GetUpdateState(), UpdateState::Unverified);
|
||||
ASSERT_TRUE(new_sm->HandleImminentDataWipe());
|
||||
// Manually mount metadata so that we can call GetUpdateState() below.
|
||||
MountMetadata();
|
||||
|
|
@ -2659,6 +2670,7 @@ TEST_F(SnapshotTest, FlagCheck) {
|
|||
status.set_o_direct(true);
|
||||
status.set_io_uring_enabled(true);
|
||||
status.set_userspace_snapshots(true);
|
||||
status.set_cow_op_merge_size(16);
|
||||
|
||||
sm->WriteSnapshotUpdateStatus(lock_.get(), status);
|
||||
// Ensure a connection to the second-stage daemon, but use the first-stage
|
||||
|
|
@ -2680,6 +2692,8 @@ TEST_F(SnapshotTest, FlagCheck) {
|
|||
snapuserd_argv.end());
|
||||
ASSERT_TRUE(std::find(snapuserd_argv.begin(), snapuserd_argv.end(), "-user_snapshot") !=
|
||||
snapuserd_argv.end());
|
||||
ASSERT_TRUE(std::find(snapuserd_argv.begin(), snapuserd_argv.end(), "-cow_op_merge_size=16") !=
|
||||
snapuserd_argv.end());
|
||||
}
|
||||
|
||||
class FlashAfterUpdateTest : public SnapshotUpdateTest,
|
||||
|
|
@ -2885,6 +2899,8 @@ void SnapshotTestEnvironment::SetUp() {
|
|||
|
||||
void SnapshotTestEnvironment::TearDown() {
|
||||
RETURN_IF_NON_VIRTUAL_AB();
|
||||
RETURN_IF_VENDOR_ON_ANDROID_S();
|
||||
|
||||
if (super_images_ != nullptr) {
|
||||
DeleteBackingImage(super_images_.get(), "fake-super");
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,7 +16,6 @@
|
|||
|
||||
#include <sysexits.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <chrono>
|
||||
#include <filesystem>
|
||||
#include <fstream>
|
||||
|
|
@ -46,6 +45,7 @@
|
|||
#include <storage_literals/storage_literals.h>
|
||||
|
||||
#include "partition_cow_creator.h"
|
||||
#include "scratch_super.h"
|
||||
|
||||
#ifdef SNAPSHOTCTL_USERDEBUG_OR_ENG
|
||||
#include <BootControlClient.h>
|
||||
|
|
@ -57,6 +57,8 @@ using namespace android::storage_literals;
|
|||
using android::base::LogdLogger;
|
||||
using android::base::StderrLogger;
|
||||
using android::base::TeeLogger;
|
||||
using namespace android::dm;
|
||||
using namespace android::fs_mgr;
|
||||
using android::fs_mgr::CreateLogicalPartitionParams;
|
||||
using android::fs_mgr::FindPartition;
|
||||
using android::fs_mgr::GetPartitionSize;
|
||||
|
|
@ -97,7 +99,7 @@ namespace snapshot {
|
|||
#ifdef SNAPSHOTCTL_USERDEBUG_OR_ENG
|
||||
class MapSnapshots {
|
||||
public:
|
||||
MapSnapshots(std::string path = "");
|
||||
MapSnapshots(std::string path = "", bool metadata_super = false);
|
||||
bool CreateSnapshotDevice(std::string& partition_name, std::string& patch);
|
||||
bool InitiateThreadedSnapshotWrite(std::string& pname, std::string& snapshot_patch);
|
||||
bool FinishSnapshotWrites();
|
||||
|
|
@ -122,15 +124,12 @@ class MapSnapshots {
|
|||
|
||||
std::vector<std::string> patchfiles_;
|
||||
chromeos_update_engine::DeltaArchiveManifest manifest_;
|
||||
bool metadata_super_ = false;
|
||||
};
|
||||
|
||||
MapSnapshots::MapSnapshots(std::string path) {
|
||||
sm_ = SnapshotManager::New();
|
||||
if (!sm_) {
|
||||
std::cout << "Failed to create snapshotmanager";
|
||||
exit(1);
|
||||
}
|
||||
MapSnapshots::MapSnapshots(std::string path, bool metadata_super) {
|
||||
snapshot_dir_path_ = path + "/";
|
||||
metadata_super_ = metadata_super;
|
||||
}
|
||||
|
||||
std::string MapSnapshots::GetGroupName(const android::fs_mgr::LpMetadata& pt,
|
||||
|
|
@ -150,6 +149,12 @@ std::string MapSnapshots::GetGroupName(const android::fs_mgr::LpMetadata& pt,
|
|||
}
|
||||
|
||||
bool MapSnapshots::PrepareUpdate() {
|
||||
if (metadata_super_ && !CreateScratchOtaMetadataOnSuper()) {
|
||||
LOG(ERROR) << "Failed to create OTA metadata on super";
|
||||
return false;
|
||||
}
|
||||
sm_ = SnapshotManager::New();
|
||||
|
||||
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);
|
||||
|
|
@ -234,7 +239,17 @@ bool MapSnapshots::PrepareUpdate() {
|
|||
|
||||
bool MapSnapshots::GetCowDevicePath(std::string partition_name, std::string* cow_path) {
|
||||
auto& dm = android::dm::DeviceMapper::Instance();
|
||||
std::string cow_device = partition_name + "-cow";
|
||||
|
||||
std::string cow_device = partition_name + "-cow-img";
|
||||
if (metadata_super_) {
|
||||
// If COW device exists on /data, then data wipe cannot be done.
|
||||
if (dm.GetDmDevicePathByName(cow_device, cow_path)) {
|
||||
LOG(ERROR) << "COW device exists on /data: " << *cow_path;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
cow_device = partition_name + "-cow";
|
||||
if (dm.GetDmDevicePathByName(cow_device, cow_path)) {
|
||||
return true;
|
||||
}
|
||||
|
|
@ -321,6 +336,12 @@ bool MapSnapshots::ApplyUpdate() {
|
|||
}
|
||||
|
||||
bool MapSnapshots::BeginUpdate() {
|
||||
if (metadata_super_ && !CreateScratchOtaMetadataOnSuper()) {
|
||||
LOG(ERROR) << "Failed to create OTA metadata on super";
|
||||
return false;
|
||||
}
|
||||
sm_ = SnapshotManager::New();
|
||||
|
||||
lock_ = sm_->LockExclusive();
|
||||
std::vector<std::string> snapshots;
|
||||
sm_->ListSnapshots(lock_.get(), &snapshots);
|
||||
|
|
@ -470,10 +491,12 @@ bool MapSnapshots::FinishSnapshotWrites() {
|
|||
}
|
||||
|
||||
bool MapSnapshots::UnmapCowImagePath(std::string& name) {
|
||||
sm_ = SnapshotManager::New();
|
||||
return sm_->UnmapCowImage(name);
|
||||
}
|
||||
|
||||
bool MapSnapshots::DeleteSnapshots() {
|
||||
sm_ = SnapshotManager::New();
|
||||
lock_ = sm_->LockExclusive();
|
||||
if (!sm_->RemoveAllUpdateState(lock_.get())) {
|
||||
LOG(ERROR) << "Remove All Update State failed";
|
||||
|
|
@ -583,13 +606,19 @@ bool ApplyUpdate(int argc, char** argv) {
|
|||
}
|
||||
|
||||
if (argc < 3) {
|
||||
std::cerr << " apply-update <directory location where snapshot patches are present>"
|
||||
std::cerr << " apply-update <directory location where snapshot patches are present> {-w}"
|
||||
" Apply the snapshots to the COW block device\n";
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string path = std::string(argv[2]);
|
||||
MapSnapshots cow(path);
|
||||
bool metadata_on_super = false;
|
||||
if (argc == 4) {
|
||||
if (std::string(argv[3]) == "-w") {
|
||||
metadata_on_super = true;
|
||||
}
|
||||
}
|
||||
MapSnapshots cow(path, metadata_on_super);
|
||||
if (!cow.ApplyUpdate()) {
|
||||
return false;
|
||||
}
|
||||
|
|
@ -607,7 +636,7 @@ bool MapPrecreatedSnapshots(int argc, char** argv) {
|
|||
}
|
||||
|
||||
if (argc < 3) {
|
||||
std::cerr << " map-snapshots <directory location where snapshot patches are present>"
|
||||
std::cerr << " map-snapshots <directory location where snapshot patches are present> {-w}"
|
||||
" Map all snapshots based on patches present in the directory\n";
|
||||
return false;
|
||||
}
|
||||
|
|
@ -638,7 +667,14 @@ bool MapPrecreatedSnapshots(int argc, char** argv) {
|
|||
}
|
||||
}
|
||||
|
||||
MapSnapshots cow(path);
|
||||
bool metadata_on_super = false;
|
||||
if (argc == 4) {
|
||||
if (std::string(argv[3]) == "-w") {
|
||||
metadata_on_super = true;
|
||||
}
|
||||
}
|
||||
|
||||
MapSnapshots cow(path, metadata_on_super);
|
||||
if (!cow.BeginUpdate()) {
|
||||
LOG(ERROR) << "BeginUpdate failed";
|
||||
return false;
|
||||
|
|
|
|||
|
|
@ -42,6 +42,7 @@ cc_library_static {
|
|||
static_libs: [
|
||||
"libcutils_sockets",
|
||||
"libfs_mgr_file_wait",
|
||||
"libdm",
|
||||
],
|
||||
shared_libs: [
|
||||
"libbase",
|
||||
|
|
@ -169,7 +170,7 @@ cc_binary {
|
|||
recovery_available: true,
|
||||
}
|
||||
|
||||
// This target will install to /system/bin/snapuserd_ramdisk
|
||||
// This target will install to /system/bin/snapuserd_ramdisk
|
||||
// It will also create a symblink on /system/bin/snapuserd that point to
|
||||
// /system/bin/snapuserd_ramdisk .
|
||||
// This way, init can check if generic ramdisk copy exists.
|
||||
|
|
@ -238,9 +239,19 @@ cc_defaults {
|
|||
test_options: {
|
||||
min_shipping_api_level: 30,
|
||||
},
|
||||
|
||||
compile_multilib: "both",
|
||||
multilib: {
|
||||
lib32: {
|
||||
suffix: "32",
|
||||
},
|
||||
lib64: {
|
||||
suffix: "64",
|
||||
},
|
||||
},
|
||||
|
||||
auto_gen_config: true,
|
||||
require_root: true,
|
||||
compile_multilib: "first",
|
||||
}
|
||||
|
||||
cc_test {
|
||||
|
|
@ -248,8 +259,16 @@ cc_test {
|
|||
defaults: ["snapuserd_test_defaults"],
|
||||
host_supported: true,
|
||||
test_suites: [
|
||||
"device-tests",
|
||||
"general-tests",
|
||||
],
|
||||
test_options: {
|
||||
test_runner_options: [
|
||||
{
|
||||
name: "force-no-test-error",
|
||||
value: "false",
|
||||
},
|
||||
],
|
||||
},
|
||||
}
|
||||
|
||||
// vts tests cannot be host_supported.
|
||||
|
|
@ -259,6 +278,10 @@ cc_test {
|
|||
test_suites: [
|
||||
"vts",
|
||||
],
|
||||
test_options: {
|
||||
// VABC mandatory in Android T per VSR.
|
||||
min_shipping_api_level: 32,
|
||||
},
|
||||
}
|
||||
|
||||
cc_binary_host {
|
||||
|
|
|
|||
|
|
@ -27,11 +27,12 @@ using android::base::unique_fd;
|
|||
DmUserBlockServer::DmUserBlockServer(const std::string& misc_name, unique_fd&& ctrl_fd,
|
||||
Delegate* delegate, size_t buffer_size)
|
||||
: misc_name_(misc_name), ctrl_fd_(std::move(ctrl_fd)), delegate_(delegate) {
|
||||
buffer_.Initialize(buffer_size);
|
||||
buffer_.Initialize(sizeof(struct dm_user_header), buffer_size);
|
||||
}
|
||||
|
||||
bool DmUserBlockServer::ProcessRequests() {
|
||||
struct dm_user_header* header = buffer_.GetHeaderPtr();
|
||||
struct dm_user_header* header =
|
||||
reinterpret_cast<struct dm_user_header*>(buffer_.GetHeaderPtr());
|
||||
if (!android::base::ReadFully(ctrl_fd_, header, sizeof(*header))) {
|
||||
if (errno != ENOTBLK) {
|
||||
SNAP_PLOG(ERROR) << "Control-read failed";
|
||||
|
|
@ -90,7 +91,8 @@ bool DmUserBlockServer::SendBufferedIo() {
|
|||
}
|
||||
|
||||
void DmUserBlockServer::SendError() {
|
||||
struct dm_user_header* header = buffer_.GetHeaderPtr();
|
||||
struct dm_user_header* header =
|
||||
reinterpret_cast<struct dm_user_header*>(buffer_.GetHeaderPtr());
|
||||
header->type = DM_USER_RESP_ERROR;
|
||||
// This is an issue with the dm-user interface. There
|
||||
// is no way to propagate the I/O error back to dm-user
|
||||
|
|
|
|||
|
|
@ -20,6 +20,7 @@
|
|||
|
||||
#include <snapuserd/block_server.h>
|
||||
#include <snapuserd/snapuserd_buffer.h>
|
||||
#include <snapuserd/snapuserd_kernel.h>
|
||||
|
||||
namespace android {
|
||||
namespace snapshot {
|
||||
|
|
|
|||
|
|
@ -27,13 +27,17 @@ namespace snapshot {
|
|||
|
||||
class BufferSink final {
|
||||
public:
|
||||
void Initialize(size_t size);
|
||||
// Do not reserve any space of header by default
|
||||
void Initialize(size_t size) { return Initialize(0, size); };
|
||||
// This allows to set const header_size_ to be used if caller needs it
|
||||
// for example, while working with dm_user
|
||||
void Initialize(size_t header_size, size_t size);
|
||||
void* GetBufPtr() { return buffer_.get(); }
|
||||
void Clear() { memset(GetBufPtr(), 0, buffer_size_); }
|
||||
void* GetPayloadBuffer(size_t size);
|
||||
void* GetBuffer(size_t requested, size_t* actual);
|
||||
void UpdateBufferOffset(size_t size) { buffer_offset_ += size; }
|
||||
struct dm_user_header* GetHeaderPtr();
|
||||
void* GetHeaderPtr();
|
||||
void ResetBufferOffset() { buffer_offset_ = 0; }
|
||||
void* GetPayloadBufPtr();
|
||||
loff_t GetPayloadBytesWritten() { return buffer_offset_; }
|
||||
|
|
@ -56,6 +60,7 @@ class BufferSink final {
|
|||
std::unique_ptr<uint8_t[]> buffer_;
|
||||
loff_t buffer_offset_;
|
||||
size_t buffer_size_;
|
||||
size_t header_size_;
|
||||
};
|
||||
|
||||
} // namespace snapshot
|
||||
|
|
|
|||
|
|
@ -92,15 +92,5 @@ struct dm_user_header {
|
|||
__u64 len;
|
||||
} __attribute__((packed));
|
||||
|
||||
struct dm_user_payload {
|
||||
__u8 buf[];
|
||||
};
|
||||
|
||||
// Message comprising both header and payload
|
||||
struct dm_user_message {
|
||||
struct dm_user_header header;
|
||||
struct dm_user_payload payload;
|
||||
};
|
||||
|
||||
} // namespace snapshot
|
||||
} // namespace android
|
||||
|
|
|
|||
|
|
@ -22,8 +22,9 @@
|
|||
namespace android {
|
||||
namespace snapshot {
|
||||
|
||||
void BufferSink::Initialize(size_t size) {
|
||||
buffer_size_ = size + sizeof(struct dm_user_header);
|
||||
void BufferSink::Initialize(size_t header_size, size_t size) {
|
||||
header_size_ = header_size;
|
||||
buffer_size_ = size + header_size;
|
||||
buffer_offset_ = 0;
|
||||
buffer_ = std::make_unique<uint8_t[]>(buffer_size_);
|
||||
}
|
||||
|
|
@ -41,11 +42,11 @@ void* BufferSink::AcquireBuffer(size_t size, size_t to_write) {
|
|||
|
||||
void* BufferSink::GetPayloadBuffer(size_t size) {
|
||||
char* buffer = reinterpret_cast<char*>(GetBufPtr());
|
||||
struct dm_user_message* msg = (struct dm_user_message*)(&(buffer[0]));
|
||||
if ((buffer_size_ - buffer_offset_ - sizeof(msg->header)) < size) {
|
||||
|
||||
if ((buffer_size_ - buffer_offset_ - header_size_) < size) {
|
||||
return nullptr;
|
||||
}
|
||||
return (char*)msg->payload.buf + buffer_offset_;
|
||||
return (char*)(&buffer[0] + header_size_ + buffer_offset_);
|
||||
}
|
||||
|
||||
void* BufferSink::GetBuffer(size_t requested, size_t* actual) {
|
||||
|
|
@ -58,19 +59,18 @@ void* BufferSink::GetBuffer(size_t requested, size_t* actual) {
|
|||
return buf;
|
||||
}
|
||||
|
||||
struct dm_user_header* BufferSink::GetHeaderPtr() {
|
||||
if (!(sizeof(struct dm_user_header) <= buffer_size_)) {
|
||||
void* BufferSink::GetHeaderPtr() {
|
||||
// If no sufficient space or header not reserved
|
||||
if (!(header_size_ <= buffer_size_) || !header_size_) {
|
||||
return nullptr;
|
||||
}
|
||||
char* buf = reinterpret_cast<char*>(GetBufPtr());
|
||||
struct dm_user_header* header = (struct dm_user_header*)(&(buf[0]));
|
||||
return header;
|
||||
return (void*)(&(buf[0]));
|
||||
}
|
||||
|
||||
void* BufferSink::GetPayloadBufPtr() {
|
||||
char* buffer = reinterpret_cast<char*>(GetBufPtr());
|
||||
struct dm_user_message* msg = reinterpret_cast<struct dm_user_message*>(&(buffer[0]));
|
||||
return msg->payload.buf;
|
||||
return &buffer[header_size_];
|
||||
}
|
||||
|
||||
} // namespace snapshot
|
||||
|
|
|
|||
|
|
@ -35,6 +35,7 @@
|
|||
#include <android-base/properties.h>
|
||||
#include <android-base/strings.h>
|
||||
#include <fs_mgr/file_wait.h>
|
||||
#include <libdm/dm.h>
|
||||
#include <snapuserd/snapuserd_client.h>
|
||||
|
||||
namespace android {
|
||||
|
|
@ -333,7 +334,21 @@ bool SnapuserdClient::QueryUpdateVerification() {
|
|||
}
|
||||
|
||||
std::string SnapuserdClient::GetDaemonAliveIndicatorPath() {
|
||||
return "/metadata/ota/" + std::string(kDaemonAliveIndicator);
|
||||
std::string metadata_dir;
|
||||
std::string temp_metadata_mnt = "/mnt/scratch_ota_metadata_super";
|
||||
|
||||
auto& dm = ::android::dm::DeviceMapper::Instance();
|
||||
auto partition_name = android::base::Basename(temp_metadata_mnt);
|
||||
|
||||
bool invalid_partition = (dm.GetState(partition_name) == dm::DmDeviceState::INVALID);
|
||||
std::string temp_device;
|
||||
if (!invalid_partition && dm.GetDmDevicePathByName(partition_name, &temp_device)) {
|
||||
metadata_dir = temp_metadata_mnt + "/" + "ota/";
|
||||
} else {
|
||||
metadata_dir = "/metadata/ota/";
|
||||
}
|
||||
|
||||
return metadata_dir + std::string(kDaemonAliveIndicator);
|
||||
}
|
||||
|
||||
bool SnapuserdClient::IsTransitionedDaemonReady() {
|
||||
|
|
|
|||
|
|
@ -30,6 +30,7 @@ DEFINE_bool(socket_handoff, false,
|
|||
DEFINE_bool(user_snapshot, false, "If true, user-space snapshots are used");
|
||||
DEFINE_bool(io_uring, false, "If true, io_uring feature is enabled");
|
||||
DEFINE_bool(o_direct, false, "If true, enable direct reads on source device");
|
||||
DEFINE_int32(cow_op_merge_size, 0, "number of operations to be processed at once");
|
||||
|
||||
namespace android {
|
||||
namespace snapshot {
|
||||
|
|
@ -106,7 +107,6 @@ bool Daemon::StartServerForUserspaceSnapshots(int arg_start, int argc, char** ar
|
|||
}
|
||||
return user_server_.Run();
|
||||
}
|
||||
|
||||
for (int i = arg_start; i < argc; i++) {
|
||||
auto parts = android::base::Split(argv[i], ",");
|
||||
|
||||
|
|
@ -114,8 +114,8 @@ bool Daemon::StartServerForUserspaceSnapshots(int arg_start, int argc, char** ar
|
|||
LOG(ERROR) << "Malformed message, expected at least four sub-arguments.";
|
||||
return false;
|
||||
}
|
||||
auto handler =
|
||||
user_server_.AddHandler(parts[0], parts[1], parts[2], parts[3], FLAGS_o_direct);
|
||||
auto handler = user_server_.AddHandler(parts[0], parts[1], parts[2], parts[3],
|
||||
FLAGS_o_direct, FLAGS_cow_op_merge_size);
|
||||
if (!handler || !user_server_.StartHandler(parts[0])) {
|
||||
return false;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -41,7 +41,7 @@ Extractor::Extractor(const std::string& base_path, const std::string& cow_path)
|
|||
bool Extractor::Init() {
|
||||
auto opener = factory_.CreateTestOpener(control_name_);
|
||||
handler_ = std::make_shared<SnapshotHandler>(control_name_, cow_path_, base_path_, base_path_,
|
||||
opener, 1, false, false, false);
|
||||
opener, 1, false, false, false, 0);
|
||||
if (!handler_->InitCowDevice()) {
|
||||
return false;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -53,10 +53,10 @@ std::shared_ptr<HandlerThread> SnapshotHandlerManager::AddHandler(
|
|||
const std::string& misc_name, const std::string& cow_device_path,
|
||||
const std::string& backing_device, const std::string& base_path_merge,
|
||||
std::shared_ptr<IBlockServerOpener> opener, int num_worker_threads, bool use_iouring,
|
||||
bool o_direct) {
|
||||
bool o_direct, uint32_t cow_op_merge_size) {
|
||||
auto snapuserd = std::make_shared<SnapshotHandler>(
|
||||
misc_name, cow_device_path, backing_device, base_path_merge, opener, num_worker_threads,
|
||||
use_iouring, perform_verification_, o_direct);
|
||||
use_iouring, perform_verification_, o_direct, cow_op_merge_size);
|
||||
if (!snapuserd->InitCowDevice()) {
|
||||
LOG(ERROR) << "Failed to initialize Snapuserd";
|
||||
return nullptr;
|
||||
|
|
|
|||
|
|
@ -52,13 +52,11 @@ class ISnapshotHandlerManager {
|
|||
virtual ~ISnapshotHandlerManager() {}
|
||||
|
||||
// Add a new snapshot handler but do not start serving requests yet.
|
||||
virtual std::shared_ptr<HandlerThread> AddHandler(const std::string& misc_name,
|
||||
const std::string& cow_device_path,
|
||||
const std::string& backing_device,
|
||||
const std::string& base_path_merge,
|
||||
std::shared_ptr<IBlockServerOpener> opener,
|
||||
int num_worker_threads, bool use_iouring,
|
||||
bool o_direct) = 0;
|
||||
virtual std::shared_ptr<HandlerThread> AddHandler(
|
||||
const std::string& misc_name, const std::string& cow_device_path,
|
||||
const std::string& backing_device, const std::string& base_path_merge,
|
||||
std::shared_ptr<IBlockServerOpener> opener, int num_worker_threads, bool use_iouring,
|
||||
bool o_direct, uint32_t cow_op_merge_size) = 0;
|
||||
|
||||
// Start serving requests on a snapshot handler.
|
||||
virtual bool StartHandler(const std::string& misc_name) = 0;
|
||||
|
|
@ -98,7 +96,7 @@ class SnapshotHandlerManager final : public ISnapshotHandlerManager {
|
|||
const std::string& base_path_merge,
|
||||
std::shared_ptr<IBlockServerOpener> opener,
|
||||
int num_worker_threads, bool use_iouring,
|
||||
bool o_direct) override;
|
||||
bool o_direct, uint32_t cow_op_merge_size) override;
|
||||
bool StartHandler(const std::string& misc_name) override;
|
||||
bool DeleteHandler(const std::string& misc_name) override;
|
||||
bool InitiateMerge(const std::string& misc_name) override;
|
||||
|
|
|
|||
|
|
@ -17,6 +17,8 @@
|
|||
#include <libsnapshot/cow_format.h>
|
||||
#include <pthread.h>
|
||||
|
||||
#include <android-base/properties.h>
|
||||
|
||||
#include "merge_worker.h"
|
||||
#include "snapuserd_core.h"
|
||||
#include "utility.h"
|
||||
|
|
@ -30,12 +32,18 @@ using android::base::unique_fd;
|
|||
|
||||
MergeWorker::MergeWorker(const std::string& cow_device, const std::string& misc_name,
|
||||
const std::string& base_path_merge,
|
||||
std::shared_ptr<SnapshotHandler> snapuserd)
|
||||
: Worker(cow_device, misc_name, base_path_merge, snapuserd) {}
|
||||
std::shared_ptr<SnapshotHandler> snapuserd, uint32_t cow_op_merge_size)
|
||||
: Worker(cow_device, misc_name, base_path_merge, snapuserd),
|
||||
cow_op_merge_size_(cow_op_merge_size) {}
|
||||
|
||||
int MergeWorker::PrepareMerge(uint64_t* source_offset, int* pending_ops,
|
||||
std::vector<const CowOperation*>* replace_zero_vec) {
|
||||
int num_ops = *pending_ops;
|
||||
// 0 indicates ro.virtual_ab.cow_op_merge_size was not set in the build
|
||||
if (cow_op_merge_size_ != 0) {
|
||||
num_ops = std::min(cow_op_merge_size_, static_cast<uint32_t>(*pending_ops));
|
||||
}
|
||||
|
||||
int nr_consecutive = 0;
|
||||
bool checkOrderedOp = (replace_zero_vec == nullptr);
|
||||
size_t num_blocks = 1;
|
||||
|
|
@ -179,8 +187,8 @@ bool MergeWorker::MergeReplaceZeroOps() {
|
|||
bufsink_.ResetBufferOffset();
|
||||
|
||||
if (snapuserd_->IsIOTerminated()) {
|
||||
SNAP_LOG(ERROR)
|
||||
<< "MergeReplaceZeroOps: MergeWorker threads terminated - shutting down merge";
|
||||
SNAP_LOG(ERROR) << "MergeReplaceZeroOps: MergeWorker threads terminated - shutting "
|
||||
"down merge";
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
@ -577,8 +585,10 @@ bool MergeWorker::Run() {
|
|||
SNAP_LOG(ERROR) << "Merge terminated early...";
|
||||
return true;
|
||||
}
|
||||
auto merge_thread_priority = android::base::GetUintProperty<uint32_t>(
|
||||
"ro.virtual_ab.merge_thread_priority", ANDROID_PRIORITY_BACKGROUND);
|
||||
|
||||
if (!SetThreadPriority(ANDROID_PRIORITY_BACKGROUND)) {
|
||||
if (!SetThreadPriority(merge_thread_priority)) {
|
||||
SNAP_PLOG(ERROR) << "Failed to set thread priority";
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -23,7 +23,8 @@ namespace snapshot {
|
|||
class MergeWorker : public Worker {
|
||||
public:
|
||||
MergeWorker(const std::string& cow_device, const std::string& misc_name,
|
||||
const std::string& base_path_merge, std::shared_ptr<SnapshotHandler> snapuserd);
|
||||
const std::string& base_path_merge, std::shared_ptr<SnapshotHandler> snapuserd,
|
||||
uint32_t cow_op_merge_size);
|
||||
bool Run();
|
||||
|
||||
private:
|
||||
|
|
@ -53,6 +54,7 @@ class MergeWorker : public Worker {
|
|||
// syscalls and fallback to synchronous I/O, we
|
||||
// don't want huge queue depth
|
||||
int queue_depth_ = 8;
|
||||
uint32_t cow_op_merge_size_ = 0;
|
||||
};
|
||||
|
||||
} // namespace snapshot
|
||||
|
|
|
|||
|
|
@ -14,11 +14,14 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include <android-base/properties.h>
|
||||
|
||||
#include <libsnapshot/cow_format.h>
|
||||
#include <pthread.h>
|
||||
|
||||
#include "read_worker.h"
|
||||
#include "snapuserd_core.h"
|
||||
#include "user-space-merge/worker.h"
|
||||
#include "utility.h"
|
||||
|
||||
namespace android {
|
||||
|
|
@ -259,8 +262,10 @@ bool ReadWorker::Run() {
|
|||
SNAP_LOG(INFO) << "Processing snapshot I/O requests....";
|
||||
|
||||
pthread_setname_np(pthread_self(), "ReadWorker");
|
||||
auto worker_thread_priority = android::base::GetUintProperty<uint32_t>(
|
||||
"ro.virtual_ab.worker_thread_priority", ANDROID_PRIORITY_NORMAL);
|
||||
|
||||
if (!SetThreadPriority(ANDROID_PRIORITY_NORMAL)) {
|
||||
if (!SetThreadPriority(worker_thread_priority)) {
|
||||
SNAP_PLOG(ERROR) << "Failed to set thread priority";
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -57,7 +57,7 @@ class ReadWorker : public Worker, public IBlockServer::Delegate {
|
|||
bool ReadFromSourceDevice(const CowOperation* cow_op, void* buffer);
|
||||
bool ReadDataFromBaseDevice(sector_t sector, void* buffer, size_t read_size);
|
||||
|
||||
constexpr bool IsBlockAligned(size_t size) { return ((size & (BLOCK_SZ - 1)) == 0); }
|
||||
constexpr bool IsBlockAligned(uint64_t size) { return ((size & (BLOCK_SZ - 1)) == 0); }
|
||||
constexpr sector_t ChunkToSector(chunk_t chunk) { return chunk << CHUNK_SHIFT; }
|
||||
constexpr chunk_t SectorToChunk(sector_t sector) { return sector >> CHUNK_SHIFT; }
|
||||
|
||||
|
|
|
|||
|
|
@ -36,7 +36,8 @@ using android::base::unique_fd;
|
|||
SnapshotHandler::SnapshotHandler(std::string misc_name, std::string cow_device,
|
||||
std::string backing_device, std::string base_path_merge,
|
||||
std::shared_ptr<IBlockServerOpener> opener, int num_worker_threads,
|
||||
bool use_iouring, bool perform_verification, bool o_direct) {
|
||||
bool use_iouring, bool perform_verification, bool o_direct,
|
||||
uint32_t cow_op_merge_size) {
|
||||
misc_name_ = std::move(misc_name);
|
||||
cow_device_ = std::move(cow_device);
|
||||
backing_store_device_ = std::move(backing_device);
|
||||
|
|
@ -46,6 +47,7 @@ SnapshotHandler::SnapshotHandler(std::string misc_name, std::string cow_device,
|
|||
is_io_uring_enabled_ = use_iouring;
|
||||
perform_verification_ = perform_verification;
|
||||
o_direct_ = o_direct;
|
||||
cow_op_merge_size_ = cow_op_merge_size;
|
||||
}
|
||||
|
||||
bool SnapshotHandler::InitializeWorkers() {
|
||||
|
|
@ -60,12 +62,11 @@ bool SnapshotHandler::InitializeWorkers() {
|
|||
|
||||
worker_threads_.push_back(std::move(wt));
|
||||
}
|
||||
|
||||
merge_thread_ = std::make_unique<MergeWorker>(cow_device_, misc_name_, base_path_merge_,
|
||||
GetSharedPtr());
|
||||
GetSharedPtr(), cow_op_merge_size_);
|
||||
|
||||
read_ahead_thread_ = std::make_unique<ReadAhead>(cow_device_, backing_store_device_, misc_name_,
|
||||
GetSharedPtr());
|
||||
GetSharedPtr(), cow_op_merge_size_);
|
||||
|
||||
update_verify_ = std::make_unique<UpdateVerify>(misc_name_);
|
||||
|
||||
|
|
|
|||
|
|
@ -104,7 +104,8 @@ class SnapshotHandler : public std::enable_shared_from_this<SnapshotHandler> {
|
|||
public:
|
||||
SnapshotHandler(std::string misc_name, std::string cow_device, std::string backing_device,
|
||||
std::string base_path_merge, std::shared_ptr<IBlockServerOpener> opener,
|
||||
int num_workers, bool use_iouring, bool perform_verification, bool o_direct);
|
||||
int num_workers, bool use_iouring, bool perform_verification, bool o_direct,
|
||||
uint32_t cow_op_merge_size);
|
||||
bool InitCowDevice();
|
||||
bool Start();
|
||||
|
||||
|
|
@ -247,7 +248,7 @@ class SnapshotHandler : public std::enable_shared_from_this<SnapshotHandler> {
|
|||
bool resume_merge_ = false;
|
||||
bool merge_complete_ = false;
|
||||
bool o_direct_ = false;
|
||||
|
||||
uint32_t cow_op_merge_size_ = 0;
|
||||
std::unique_ptr<UpdateVerify> update_verify_;
|
||||
std::shared_ptr<IBlockServerOpener> block_server_opener_;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@
|
|||
|
||||
#include <pthread.h>
|
||||
|
||||
#include "android-base/properties.h"
|
||||
#include "snapuserd_core.h"
|
||||
#include "utility.h"
|
||||
|
||||
|
|
@ -29,11 +30,13 @@ using namespace android::dm;
|
|||
using android::base::unique_fd;
|
||||
|
||||
ReadAhead::ReadAhead(const std::string& cow_device, const std::string& backing_device,
|
||||
const std::string& misc_name, std::shared_ptr<SnapshotHandler> snapuserd) {
|
||||
const std::string& misc_name, std::shared_ptr<SnapshotHandler> snapuserd,
|
||||
uint32_t cow_op_merge_size) {
|
||||
cow_device_ = cow_device;
|
||||
backing_store_device_ = backing_device;
|
||||
misc_name_ = misc_name;
|
||||
snapuserd_ = snapuserd;
|
||||
cow_op_merge_size_ = cow_op_merge_size;
|
||||
}
|
||||
|
||||
void ReadAhead::CheckOverlap(const CowOperation* cow_op) {
|
||||
|
|
@ -62,8 +65,11 @@ int ReadAhead::PrepareNextReadAhead(uint64_t* source_offset, int* pending_ops,
|
|||
std::vector<uint64_t>& blocks,
|
||||
std::vector<const CowOperation*>& xor_op_vec) {
|
||||
int num_ops = *pending_ops;
|
||||
int nr_consecutive = 0;
|
||||
if (cow_op_merge_size_ != 0) {
|
||||
num_ops = std::min(static_cast<int>(cow_op_merge_size_), *pending_ops);
|
||||
}
|
||||
|
||||
int nr_consecutive = 0;
|
||||
bool is_ops_present = (!RAIterDone() && num_ops);
|
||||
|
||||
if (!is_ops_present) {
|
||||
|
|
|
|||
|
|
@ -35,7 +35,8 @@ class SnapshotHandler;
|
|||
class ReadAhead {
|
||||
public:
|
||||
ReadAhead(const std::string& cow_device, const std::string& backing_device,
|
||||
const std::string& misc_name, std::shared_ptr<SnapshotHandler> snapuserd);
|
||||
const std::string& misc_name, std::shared_ptr<SnapshotHandler> snapuserd,
|
||||
uint32_t cow_op_merge_size);
|
||||
bool RunThread();
|
||||
|
||||
private:
|
||||
|
|
@ -106,6 +107,7 @@ class ReadAhead {
|
|||
// syscalls and fallback to synchronous I/O, we
|
||||
// don't want huge queue depth
|
||||
int queue_depth_ = 8;
|
||||
uint32_t cow_op_merge_size_;
|
||||
std::unique_ptr<struct io_uring> ring_;
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@
|
|||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/system_properties.h>
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
|
||||
|
|
@ -35,9 +36,6 @@
|
|||
#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 {
|
||||
|
||||
|
|
@ -347,7 +345,8 @@ std::shared_ptr<HandlerThread> UserSnapshotServer::AddHandler(const std::string&
|
|||
const std::string& cow_device_path,
|
||||
const std::string& backing_device,
|
||||
const std::string& base_path_merge,
|
||||
const bool o_direct) {
|
||||
const bool o_direct,
|
||||
uint32_t cow_op_merge_size) {
|
||||
// We will need multiple worker threads only during
|
||||
// device boot after OTA. For all other purposes,
|
||||
// one thread is sufficient. We don't want to consume
|
||||
|
|
@ -369,7 +368,8 @@ std::shared_ptr<HandlerThread> UserSnapshotServer::AddHandler(const std::string&
|
|||
auto opener = block_server_factory_->CreateOpener(misc_name);
|
||||
|
||||
return handlers_->AddHandler(misc_name, cow_device_path, backing_device, base_path_merge,
|
||||
opener, num_worker_threads, io_uring_enabled_, o_direct);
|
||||
opener, num_worker_threads, io_uring_enabled_, o_direct,
|
||||
cow_op_merge_size);
|
||||
}
|
||||
|
||||
bool UserSnapshotServer::WaitForSocket() {
|
||||
|
|
|
|||
|
|
@ -87,7 +87,8 @@ class UserSnapshotServer {
|
|||
const std::string& cow_device_path,
|
||||
const std::string& backing_device,
|
||||
const std::string& base_path_merge,
|
||||
bool o_direct = false);
|
||||
bool o_direct = false,
|
||||
uint32_t cow_op_merge_size = 0);
|
||||
bool StartHandler(const std::string& misc_name);
|
||||
|
||||
void SetTerminating() { terminating_ = true; }
|
||||
|
|
|
|||
|
|
@ -67,6 +67,7 @@ struct TestParam {
|
|||
std::string compression;
|
||||
int block_size;
|
||||
int num_threads;
|
||||
uint32_t cow_op_merge_size;
|
||||
};
|
||||
|
||||
class SnapuserdTestBase : public ::testing::TestWithParam<TestParam> {
|
||||
|
|
@ -79,6 +80,8 @@ class SnapuserdTestBase : public ::testing::TestWithParam<TestParam> {
|
|||
std::unique_ptr<ICowWriter> CreateCowDeviceInternal();
|
||||
std::unique_ptr<ICowWriter> CreateV3Cow();
|
||||
|
||||
unique_fd GetCowFd() { return unique_fd{dup(cow_system_->fd)}; }
|
||||
|
||||
std::unique_ptr<ITestHarness> harness_;
|
||||
size_t size_ = 10_MiB;
|
||||
int total_base_size_ = 0;
|
||||
|
|
@ -101,7 +104,9 @@ void SnapuserdTestBase::SetUp() {
|
|||
#endif
|
||||
}
|
||||
|
||||
void SnapuserdTestBase::TearDown() {}
|
||||
void SnapuserdTestBase::TearDown() {
|
||||
cow_system_ = nullptr;
|
||||
}
|
||||
|
||||
void SnapuserdTestBase::CreateBaseDevice() {
|
||||
total_base_size_ = (size_ * 5);
|
||||
|
|
@ -132,10 +137,7 @@ std::unique_ptr<ICowWriter> SnapuserdTestBase::CreateCowDeviceInternal() {
|
|||
CowOptions options;
|
||||
options.compression = "gz";
|
||||
|
||||
unique_fd fd(cow_system_->fd);
|
||||
cow_system_->fd = -1;
|
||||
|
||||
return CreateCowWriter(2, options, std::move(fd));
|
||||
return CreateCowWriter(2, options, GetCowFd());
|
||||
}
|
||||
|
||||
std::unique_ptr<ICowWriter> SnapuserdTestBase::CreateV3Cow() {
|
||||
|
|
@ -151,10 +153,7 @@ std::unique_ptr<ICowWriter> SnapuserdTestBase::CreateV3Cow() {
|
|||
std::string path = android::base::GetExecutableDirectory();
|
||||
cow_system_ = std::make_unique<TemporaryFile>(path);
|
||||
|
||||
unique_fd fd(cow_system_->fd);
|
||||
cow_system_->fd = -1;
|
||||
|
||||
return CreateCowWriter(3, options, std::move(fd));
|
||||
return CreateCowWriter(3, options, GetCowFd());
|
||||
}
|
||||
|
||||
void SnapuserdTestBase::CreateCowDevice() {
|
||||
|
|
@ -710,9 +709,9 @@ void SnapuserdTest::InitCowDevice() {
|
|||
auto opener = factory->CreateOpener(system_device_ctrl_name_);
|
||||
handlers_->DisableVerification();
|
||||
const TestParam params = GetParam();
|
||||
auto handler = handlers_->AddHandler(system_device_ctrl_name_, cow_system_->path,
|
||||
base_dev_->GetPath(), base_dev_->GetPath(), opener, 1,
|
||||
params.io_uring, params.o_direct);
|
||||
auto handler = handlers_->AddHandler(
|
||||
system_device_ctrl_name_, cow_system_->path, base_dev_->GetPath(), base_dev_->GetPath(),
|
||||
opener, 1, params.io_uring, params.o_direct, params.cow_op_merge_size);
|
||||
ASSERT_NE(handler, nullptr);
|
||||
ASSERT_NE(handler->snapuserd(), nullptr);
|
||||
#ifdef __ANDROID__
|
||||
|
|
@ -1229,9 +1228,9 @@ void HandlerTest::InitializeDevice() {
|
|||
ASSERT_NE(opener_, nullptr);
|
||||
|
||||
const TestParam params = GetParam();
|
||||
handler_ = std::make_shared<SnapshotHandler>(system_device_ctrl_name_, cow_system_->path,
|
||||
base_dev_->GetPath(), base_dev_->GetPath(),
|
||||
opener_, 1, false, false, params.o_direct);
|
||||
handler_ = std::make_shared<SnapshotHandler>(
|
||||
system_device_ctrl_name_, cow_system_->path, base_dev_->GetPath(), base_dev_->GetPath(),
|
||||
opener_, 1, false, false, params.o_direct, params.cow_op_merge_size);
|
||||
ASSERT_TRUE(handler_->InitCowDevice());
|
||||
ASSERT_TRUE(handler_->InitializeWorkers());
|
||||
|
||||
|
|
@ -1509,6 +1508,7 @@ std::vector<TestParam> GetVariableBlockTestConfigs() {
|
|||
param.num_threads = thread;
|
||||
param.io_uring = io_uring;
|
||||
param.o_direct = false;
|
||||
param.cow_op_merge_size = 0;
|
||||
testParams.push_back(std::move(param));
|
||||
}
|
||||
}
|
||||
|
|
@ -1530,6 +1530,14 @@ INSTANTIATE_TEST_SUITE_P(Io, HandlerTest, ::testing::ValuesIn(GetTestConfigs()))
|
|||
int main(int argc, char** argv) {
|
||||
::testing::InitGoogleTest(&argc, argv);
|
||||
|
||||
#ifdef __ANDROID__
|
||||
if (!android::snapshot::CanUseUserspaceSnapshots() ||
|
||||
android::snapshot::IsVendorFromAndroid12()) {
|
||||
std::cerr << "snapuserd_test not supported on this device\n";
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
gflags::ParseCommandLineFlags(&argc, &argv, false);
|
||||
|
||||
return RUN_ALL_TESTS();
|
||||
|
|
|
|||
|
|
@ -14,11 +14,14 @@
|
|||
|
||||
#include "utility.h"
|
||||
|
||||
#include <android-base/properties.h>
|
||||
#include <sys/resource.h>
|
||||
#include <sys/utsname.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <android-base/file.h>
|
||||
#include <android-base/logging.h>
|
||||
#include <libdm/dm.h>
|
||||
#include <processgroup/processgroup.h>
|
||||
|
||||
#include <private/android_filesystem_config.h>
|
||||
|
|
@ -27,6 +30,7 @@ namespace android {
|
|||
namespace snapshot {
|
||||
|
||||
using android::base::unique_fd;
|
||||
using android::dm::DeviceMapper;
|
||||
|
||||
bool SetThreadPriority([[maybe_unused]] int priority) {
|
||||
#ifdef __ANDROID__
|
||||
|
|
@ -61,5 +65,38 @@ bool KernelSupportsIoUring() {
|
|||
return major > 5 || (major == 5 && minor >= 6);
|
||||
}
|
||||
|
||||
bool GetUserspaceSnapshotsEnabledProperty() {
|
||||
return android::base::GetBoolProperty("ro.virtual_ab.userspace.snapshots.enabled", false);
|
||||
}
|
||||
|
||||
bool KernelSupportsCompressedSnapshots() {
|
||||
auto& dm = DeviceMapper::Instance();
|
||||
return dm.GetTargetByName("user", nullptr);
|
||||
}
|
||||
|
||||
bool IsVendorFromAndroid12() {
|
||||
const std::string UNKNOWN = "unknown";
|
||||
const std::string vendor_release =
|
||||
android::base::GetProperty("ro.vendor.build.version.release_or_codename", UNKNOWN);
|
||||
|
||||
if (vendor_release.find("12") != std::string::npos) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool CanUseUserspaceSnapshots() {
|
||||
if (!GetUserspaceSnapshotsEnabledProperty()) {
|
||||
LOG(INFO) << "Virtual A/B - Userspace snapshots disabled";
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!KernelSupportsCompressedSnapshots()) {
|
||||
LOG(ERROR) << "Userspace snapshots requested, but no kernel support is available.";
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace snapshot
|
||||
} // namespace android
|
||||
|
|
|
|||
|
|
@ -24,5 +24,10 @@ bool SetThreadPriority(int priority);
|
|||
bool SetProfiles(std::initializer_list<std::string_view> profiles);
|
||||
bool KernelSupportsIoUring();
|
||||
|
||||
bool GetUserspaceSnapshotsEnabledProperty();
|
||||
bool KernelSupportsCompressedSnapshots();
|
||||
bool CanUseUserspaceSnapshots();
|
||||
bool IsVendorFromAndroid12();
|
||||
|
||||
} // namespace snapshot
|
||||
} // namespace android
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@
|
|||
<option name="config-descriptor:metadata" key="parameter" value="not_instant_app" />
|
||||
<option name="config-descriptor:metadata" key="parameter" value="multi_abi" />
|
||||
<option name="config-descriptor:metadata" key="parameter" value="secondary_user" />
|
||||
<option name="config-descriptor:metadata" key="parameter" value="secondary_user_on_secondary_display" />
|
||||
<target_preparer class="com.android.compatibility.common.tradefed.targetprep.FilePusher">
|
||||
<option name="cleanup" value="true" />
|
||||
<option name="push" value="CtsFsMgrTestCases->/data/local/tmp/CtsFsMgrTestCases" />
|
||||
|
|
|
|||
|
|
@ -37,6 +37,10 @@
|
|||
using namespace android::fs_mgr;
|
||||
using namespace testing;
|
||||
|
||||
#if !defined(MS_LAZYTIME)
|
||||
#define MS_LAZYTIME (1 << 25)
|
||||
#endif
|
||||
|
||||
namespace {
|
||||
|
||||
const std::string cmdline =
|
||||
|
|
@ -329,6 +333,7 @@ TEST(fs_mgr, fs_mgr_read_fstab_file_proc_mounts) {
|
|||
{"private", MS_PRIVATE},
|
||||
{"slave", MS_SLAVE},
|
||||
{"shared", MS_SHARED},
|
||||
{"lazytime", MS_LAZYTIME},
|
||||
{"defaults", 0},
|
||||
{0, 0},
|
||||
};
|
||||
|
|
@ -1062,23 +1067,6 @@ TEST(fs_mgr, DefaultFstabContainsUserdata) {
|
|||
<< "Default fstab doesn't contain /data entry";
|
||||
}
|
||||
|
||||
TEST(fs_mgr, UserdataMountedFromDefaultFstab) {
|
||||
if (getuid() != 0) {
|
||||
GTEST_SKIP() << "Must be run as root.";
|
||||
return;
|
||||
}
|
||||
Fstab fstab;
|
||||
ASSERT_TRUE(ReadDefaultFstab(&fstab)) << "Failed to read default fstab";
|
||||
Fstab proc_mounts;
|
||||
ASSERT_TRUE(ReadFstabFromFile("/proc/mounts", &proc_mounts)) << "Failed to read /proc/mounts";
|
||||
auto mounted_entry = GetEntryForMountPoint(&proc_mounts, "/data");
|
||||
ASSERT_NE(mounted_entry, nullptr) << "/data is not mounted";
|
||||
std::string block_device;
|
||||
ASSERT_TRUE(android::base::Realpath(mounted_entry->blk_device, &block_device));
|
||||
ASSERT_NE(nullptr, fs_mgr_get_mounted_entry_for_userdata(&fstab, block_device))
|
||||
<< "/data wasn't mounted from default fstab";
|
||||
}
|
||||
|
||||
TEST(fs_mgr, ReadFstabFromFile_FsMgrOptions_Readahead_Size_KB) {
|
||||
TemporaryFile tf;
|
||||
ASSERT_TRUE(tf.fd != -1);
|
||||
|
|
|
|||
|
|
@ -306,8 +306,8 @@ void Charger::UpdateScreenState(int64_t now) {
|
|||
|
||||
if (!batt_anim_.run || now < next_screen_transition_) return;
|
||||
|
||||
// If battery level is not ready, keep checking in the defined time
|
||||
if (health_info_.battery_level == 0 && health_info_.battery_status == BatteryStatus::UNKNOWN) {
|
||||
// If battery status is not ready, keep checking in the defined time
|
||||
if (health_info_.battery_status == BatteryStatus::UNKNOWN) {
|
||||
if (wait_batt_level_timestamp_ == 0) {
|
||||
// Set max delay time and skip drawing screen
|
||||
wait_batt_level_timestamp_ = now + MAX_BATT_LEVEL_WAIT_TIME;
|
||||
|
|
@ -317,7 +317,7 @@ void Charger::UpdateScreenState(int64_t now) {
|
|||
// Do nothing, keep waiting
|
||||
return;
|
||||
}
|
||||
// If timeout and battery level is still not ready, draw unknown battery
|
||||
// If timeout and battery status is still not ready, draw unknown battery
|
||||
}
|
||||
|
||||
if (healthd_draw_ == nullptr) return;
|
||||
|
|
|
|||
|
|
@ -38,7 +38,6 @@ init_common_sources = [
|
|||
"capabilities.cpp",
|
||||
"epoll.cpp",
|
||||
"import_parser.cpp",
|
||||
"interface_utils.cpp",
|
||||
"interprocess_fifo.cpp",
|
||||
"keychords.cpp",
|
||||
"parser.cpp",
|
||||
|
|
@ -85,10 +84,6 @@ init_device_sources = [
|
|||
"ueventd.cpp",
|
||||
"ueventd_parser.cpp",
|
||||
]
|
||||
init_host_sources = [
|
||||
"check_builtins.cpp",
|
||||
"host_import_parser.cpp",
|
||||
]
|
||||
|
||||
cc_library_static {
|
||||
name: "vendor_init",
|
||||
|
|
@ -170,7 +165,7 @@ libinit_cc_defaults {
|
|||
},
|
||||
release_write_appcompat_override_system_properties: {
|
||||
cflags: ["-DWRITE_APPCOMPAT_OVERRIDE_SYSTEM_PROPERTIES"],
|
||||
}
|
||||
},
|
||||
},
|
||||
static_libs: [
|
||||
"libavb",
|
||||
|
|
@ -198,7 +193,6 @@ libinit_cc_defaults {
|
|||
"libext4_utils",
|
||||
"libfs_mgr",
|
||||
"libgsi",
|
||||
"libhidl-gen-utils",
|
||||
"libkeyutils",
|
||||
"liblog",
|
||||
"liblogwrap",
|
||||
|
|
@ -236,7 +230,6 @@ cc_defaults {
|
|||
],
|
||||
whole_static_libs: [
|
||||
"libcap",
|
||||
"libcom.android.sysprop.init",
|
||||
],
|
||||
header_libs: ["bootimg_headers"],
|
||||
proto: {
|
||||
|
|
@ -340,7 +333,7 @@ cc_binary {
|
|||
static_libs: ["libinit.microdroid"],
|
||||
cflags: ["-DMICRODROID=1"],
|
||||
no_full_install: true,
|
||||
visibility: ["//packages/modules/Virtualization/microdroid"],
|
||||
visibility: ["//packages/modules/Virtualization/build/microdroid"],
|
||||
}
|
||||
|
||||
soong_config_module_type {
|
||||
|
|
@ -348,7 +341,7 @@ soong_config_module_type {
|
|||
module_type: "cc_defaults",
|
||||
config_namespace: "ANDROID",
|
||||
bool_variables: ["BOARD_USES_RECOVERY_AS_BOOT"],
|
||||
properties: ["installable"],
|
||||
properties: ["no_full_install"],
|
||||
}
|
||||
|
||||
// Do not install init_first_stage even with mma if we're system-as-root.
|
||||
|
|
@ -357,7 +350,7 @@ init_first_stage_cc_defaults {
|
|||
name: "init_first_stage_defaults",
|
||||
soong_config_variables: {
|
||||
BOARD_USES_RECOVERY_AS_BOOT: {
|
||||
installable: false,
|
||||
no_full_install: true,
|
||||
},
|
||||
},
|
||||
|
||||
|
|
@ -618,8 +611,6 @@ cc_defaults {
|
|||
whole_static_libs: ["libcap"],
|
||||
shared_libs: [
|
||||
"libcutils",
|
||||
"libhidl-gen-utils",
|
||||
"libhidlmetadata",
|
||||
"liblog",
|
||||
"libprocessgroup",
|
||||
"libprotobuf-cpp-lite",
|
||||
|
|
@ -627,9 +618,6 @@ cc_defaults {
|
|||
proto: {
|
||||
type: "lite",
|
||||
},
|
||||
generated_headers: [
|
||||
"generated_stub_builtin_function_map",
|
||||
],
|
||||
target: {
|
||||
android: {
|
||||
enabled: false,
|
||||
|
|
@ -648,17 +636,43 @@ cc_defaults {
|
|||
cc_binary {
|
||||
name: "host_init_verifier",
|
||||
defaults: ["init_host_defaults"],
|
||||
srcs: ["host_init_verifier.cpp"] + init_common_sources + init_host_sources,
|
||||
srcs: [
|
||||
"check_builtins.cpp",
|
||||
"host_import_parser.cpp",
|
||||
"host_init_verifier.cpp",
|
||||
"interface_utils.cpp",
|
||||
] + init_common_sources,
|
||||
generated_headers: [
|
||||
"generated_android_ids",
|
||||
"generated_stub_builtin_function_map",
|
||||
],
|
||||
shared_libs: [
|
||||
"libhidl-gen-utils",
|
||||
"libhidlmetadata",
|
||||
],
|
||||
}
|
||||
|
||||
genrule {
|
||||
name: "noop_builtin_function_map",
|
||||
tool_files: ["host_builtin_map.py"],
|
||||
out: ["noop_builtin_function_map.h"],
|
||||
srcs: [
|
||||
"builtins.cpp",
|
||||
"noop_builtins.cpp",
|
||||
],
|
||||
cmd: "$(location host_builtin_map.py) --builtins $(location builtins.cpp) --check_builtins $(location noop_builtins.cpp) > $(out)",
|
||||
}
|
||||
|
||||
cc_library_host_static {
|
||||
name: "libinit_host",
|
||||
defaults: ["init_host_defaults"],
|
||||
srcs: init_common_sources + init_host_sources,
|
||||
srcs: [
|
||||
"noop_builtins.cpp",
|
||||
] + init_common_sources,
|
||||
export_include_dirs: ["."],
|
||||
generated_headers: [
|
||||
"noop_builtin_function_map",
|
||||
],
|
||||
proto: {
|
||||
export_proto_headers: true,
|
||||
},
|
||||
|
|
@ -673,3 +687,11 @@ sh_binary {
|
|||
src: "extra_free_kbytes.sh",
|
||||
filename_from_src: true,
|
||||
}
|
||||
|
||||
phony {
|
||||
name: "init_vendor",
|
||||
required: select(soong_config_variable("ANDROID", "BOARD_USES_RECOVERY_AS_BOOT"), {
|
||||
true: [],
|
||||
default: ["init_first_stage"],
|
||||
}),
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,16 +0,0 @@
|
|||
# Copyright 2005 The Android Open Source Project
|
||||
|
||||
LOCAL_PATH:= $(call my-dir)
|
||||
|
||||
include $(CLEAR_VARS)
|
||||
|
||||
LOCAL_MODULE := init_vendor
|
||||
LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0
|
||||
LOCAL_LICENSE_CONDITIONS := notice
|
||||
LOCAL_NOTICE_FILE := $(LOCAL_PATH)/NOTICE
|
||||
ifneq ($(BOARD_USES_RECOVERY_AS_BOOT),true)
|
||||
LOCAL_REQUIRED_MODULES := \
|
||||
init_first_stage \
|
||||
|
||||
endif # BOARD_USES_RECOVERY_AS_BOOT
|
||||
include $(BUILD_PHONY_PACKAGE)
|
||||
|
|
@ -499,9 +499,12 @@ have been omitted.
|
|||
4. `late-fs` - Mount partitions marked as latemounted.
|
||||
5. `post-fs-data` - Mount and configure `/data`; set up encryption. `/metadata` is
|
||||
reformatted here if it couldn't mount in first-stage init.
|
||||
6. `zygote-start` - Start the zygote.
|
||||
7. `early-boot` - After zygote has started.
|
||||
8. `boot` - After `early-boot` actions have completed.
|
||||
6. `post-fs-data-checkpointed` - Triggered when vold has completed committing a checkpoint
|
||||
after an OTA update. Not triggered if checkpointing is not needed or supported.
|
||||
7. `bpf-progs-loaded` - Starts things that want to start ASAP but need eBPF (incl. netd)
|
||||
8. `zygote-start` - Start the zygote.
|
||||
9. `early-boot` - After zygote has started.
|
||||
10. `boot` - After `early-boot` actions have completed.
|
||||
|
||||
Commands
|
||||
--------
|
||||
|
|
@ -634,7 +637,7 @@ provides the `aidl_lazy_test_1` interface.
|
|||
Properties are expanded within _level_.
|
||||
|
||||
`mark_post_data`
|
||||
> Used to mark the point right after /data is mounted.
|
||||
> (This action is deprecated and no-op.)
|
||||
|
||||
`mkdir <path> [<mode>] [<owner>] [<group>] [encryption=<action>] [key=<key>]`
|
||||
> Create a directory at _path_, optionally with the given mode, owner, and
|
||||
|
|
@ -743,6 +746,9 @@ provides the `aidl_lazy_test_1` interface.
|
|||
fstab.${ro.hardware} or fstab.${ro.hardware.platform} will be scanned for
|
||||
under /odm/etc, /vendor/etc, or / at runtime, in that order.
|
||||
|
||||
`swapoff <path>`
|
||||
> Stops swapping to the file or block device specified by path.
|
||||
|
||||
`symlink <target> <path>`
|
||||
> Create a symbolic link at _path_ with the value _target_
|
||||
|
||||
|
|
@ -786,7 +792,6 @@ provides the `aidl_lazy_test_1` interface.
|
|||
If the file does not exist, it will be created. If it does exist,
|
||||
it will be truncated. Properties are expanded within _content_.
|
||||
|
||||
|
||||
Imports
|
||||
-------
|
||||
`import <path>`
|
||||
|
|
|
|||
|
|
@ -139,6 +139,10 @@ bool BlockDevInitializer::InitPlatformDevice(const std::string& dev_name) {
|
|||
return InitDevice("/sys/devices/platform", dev_name);
|
||||
}
|
||||
|
||||
bool BlockDevInitializer::InitHvcDevice(const std::string& dev_name) {
|
||||
return InitDevice("/sys/devices/virtual/tty", dev_name);
|
||||
}
|
||||
|
||||
bool BlockDevInitializer::InitDevice(const std::string& syspath, const std::string& device_name) {
|
||||
bool found = false;
|
||||
|
||||
|
|
|
|||
|
|
@ -34,6 +34,7 @@ class BlockDevInitializer final {
|
|||
bool InitDevices(std::set<std::string> devices);
|
||||
bool InitDmDevice(const std::string& device);
|
||||
bool InitPlatformDevice(const std::string& device);
|
||||
bool InitHvcDevice(const std::string& device);
|
||||
|
||||
private:
|
||||
ListenerAction HandleUevent(const Uevent& uevent, std::set<std::string>* devices);
|
||||
|
|
|
|||
|
|
@ -36,6 +36,7 @@
|
|||
#include <sys/resource.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/swap.h>
|
||||
#include <sys/syscall.h>
|
||||
#include <sys/system_properties.h>
|
||||
#include <sys/time.h>
|
||||
|
|
@ -46,7 +47,6 @@
|
|||
#include <map>
|
||||
#include <memory>
|
||||
|
||||
#include <InitProperties.sysprop.h>
|
||||
#include <android-base/chrono_utils.h>
|
||||
#include <android-base/file.h>
|
||||
#include <android-base/logging.h>
|
||||
|
|
@ -609,8 +609,6 @@ static Result<void> queue_fs_event(int code) {
|
|||
return Error() << "Invalid code: " << code;
|
||||
}
|
||||
|
||||
static int initial_mount_fstab_return_code = -1;
|
||||
|
||||
/* <= Q: mount_all <fstab> [ <path> ]* [--<options>]*
|
||||
* >= R: mount_all [ <fstab> ] [--<options>]*
|
||||
*
|
||||
|
|
@ -651,19 +649,10 @@ static Result<void> do_mount_all(const BuiltinArguments& args) {
|
|||
import_late(mount_all->rc_paths);
|
||||
}
|
||||
|
||||
if (mount_fstab_result.userdata_mounted) {
|
||||
// This call to fs_mgr_mount_all mounted userdata. Keep the result in
|
||||
// order for userspace reboot to correctly remount userdata.
|
||||
LOG(INFO) << "Userdata mounted using "
|
||||
<< (mount_all->fstab_path.empty() ? "(default fstab)" : mount_all->fstab_path)
|
||||
<< " result : " << mount_fstab_result.code;
|
||||
initial_mount_fstab_return_code = mount_fstab_result.code;
|
||||
}
|
||||
|
||||
if (queue_event) {
|
||||
/* queue_fs_event will queue event based on mount_fstab return code
|
||||
* and return processed return code*/
|
||||
auto queue_fs_result = queue_fs_event(mount_fstab_result.code);
|
||||
auto queue_fs_result = queue_fs_event(mount_fstab_result);
|
||||
if (!queue_fs_result.ok()) {
|
||||
return Error() << "queue_fs_event() failed: " << queue_fs_result.error();
|
||||
}
|
||||
|
|
@ -1151,29 +1140,19 @@ static Result<void> ExecWithFunctionOnFailure(const std::vector<std::string>& ar
|
|||
}
|
||||
|
||||
static Result<void> ExecVdcRebootOnFailure(const std::string& vdc_arg) {
|
||||
bool should_reboot_into_recovery = true;
|
||||
auto reboot_reason = vdc_arg + "_failed";
|
||||
if (android::sysprop::InitProperties::userspace_reboot_in_progress().value_or(false)) {
|
||||
should_reboot_into_recovery = false;
|
||||
reboot_reason = "userspace_failed," + vdc_arg;
|
||||
}
|
||||
|
||||
auto reboot = [reboot_reason, should_reboot_into_recovery](const std::string& message) {
|
||||
auto reboot = [reboot_reason](const std::string& message) {
|
||||
// TODO (b/122850122): support this in gsi
|
||||
if (should_reboot_into_recovery) {
|
||||
if (IsFbeEnabled() && !android::gsi::IsGsiRunning()) {
|
||||
LOG(ERROR) << message << ": Rebooting into recovery, reason: " << reboot_reason;
|
||||
if (auto result = reboot_into_recovery(
|
||||
{"--prompt_and_wipe_data", "--reason="s + reboot_reason});
|
||||
!result.ok()) {
|
||||
LOG(FATAL) << "Could not reboot into recovery: " << result.error();
|
||||
}
|
||||
} else {
|
||||
LOG(ERROR) << "Failure (reboot suppressed): " << reboot_reason;
|
||||
if (IsFbeEnabled() && !android::gsi::IsGsiRunning()) {
|
||||
LOG(ERROR) << message << ": Rebooting into recovery, reason: " << reboot_reason;
|
||||
if (auto result = reboot_into_recovery(
|
||||
{"--prompt_and_wipe_data", "--reason="s + reboot_reason});
|
||||
!result.ok()) {
|
||||
LOG(FATAL) << "Could not reboot into recovery: " << result.error();
|
||||
}
|
||||
} else {
|
||||
LOG(ERROR) << message << ": rebooting, reason: " << reboot_reason;
|
||||
trigger_shutdown("reboot," + reboot_reason);
|
||||
LOG(ERROR) << "Failure (reboot suppressed): " << reboot_reason;
|
||||
}
|
||||
};
|
||||
|
||||
|
|
@ -1181,29 +1160,6 @@ static Result<void> ExecVdcRebootOnFailure(const std::string& vdc_arg) {
|
|||
return ExecWithFunctionOnFailure(args, reboot);
|
||||
}
|
||||
|
||||
static Result<void> do_remount_userdata(const BuiltinArguments& args) {
|
||||
if (initial_mount_fstab_return_code == -1) {
|
||||
return Error() << "Calling remount_userdata too early";
|
||||
}
|
||||
Fstab fstab;
|
||||
if (!ReadDefaultFstab(&fstab)) {
|
||||
// TODO(b/135984674): should we reboot here?
|
||||
return Error() << "Failed to read fstab";
|
||||
}
|
||||
// TODO(b/135984674): check that fstab contains /data.
|
||||
if (auto rc = fs_mgr_remount_userdata_into_checkpointing(&fstab); rc < 0) {
|
||||
std::string proc_mounts_output;
|
||||
android::base::ReadFileToString("/proc/mounts", &proc_mounts_output, true);
|
||||
android::base::WriteStringToFile(proc_mounts_output,
|
||||
"/metadata/userspacereboot/mount_info.txt");
|
||||
trigger_shutdown("reboot,mount_userdata_failed");
|
||||
}
|
||||
if (auto result = queue_fs_event(initial_mount_fstab_return_code); !result.ok()) {
|
||||
return Error() << "queue_fs_event() failed: " << result.error();
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
static Result<void> do_installkey(const BuiltinArguments& args) {
|
||||
if (!is_file_crypto()) return {};
|
||||
|
||||
|
|
@ -1219,8 +1175,7 @@ static Result<void> do_init_user0(const BuiltinArguments& args) {
|
|||
}
|
||||
|
||||
static Result<void> do_mark_post_data(const BuiltinArguments& args) {
|
||||
ServiceList::GetInstance().MarkPostData();
|
||||
|
||||
LOG(INFO) << "deprecated action `mark_post_data` called.";
|
||||
return {};
|
||||
}
|
||||
|
||||
|
|
@ -1319,6 +1274,13 @@ static Result<void> do_enter_default_mount_ns(const BuiltinArguments& args) {
|
|||
return {};
|
||||
}
|
||||
|
||||
static Result<void> do_swapoff(const BuiltinArguments& args) {
|
||||
if (!swapoff(args[1].c_str())) {
|
||||
return ErrnoError() << "swapoff() failed";
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
// Builtin-function-map start
|
||||
const BuiltinFunctionMap& GetBuiltinFunctionMap() {
|
||||
constexpr std::size_t kMax = std::numeric_limits<std::size_t>::max();
|
||||
|
|
@ -1364,7 +1326,6 @@ const BuiltinFunctionMap& GetBuiltinFunctionMap() {
|
|||
{"umount_all", {0, 1, {false, do_umount_all}}},
|
||||
{"update_linker_config", {0, 0, {false, do_update_linker_config}}},
|
||||
{"readahead", {1, 2, {true, do_readahead}}},
|
||||
{"remount_userdata", {0, 0, {false, do_remount_userdata}}},
|
||||
{"restart", {1, 2, {false, do_restart}}},
|
||||
{"restorecon", {1, kMax, {true, do_restorecon}}},
|
||||
{"restorecon_recursive", {1, kMax, {true, do_restorecon_recursive}}},
|
||||
|
|
@ -1375,6 +1336,7 @@ const BuiltinFunctionMap& GetBuiltinFunctionMap() {
|
|||
{"start", {1, 1, {false, do_start}}},
|
||||
{"stop", {1, 1, {false, do_stop}}},
|
||||
{"swapon_all", {0, 1, {false, do_swapon_all}}},
|
||||
{"swapoff", {1, 1, {false, do_swapoff}}},
|
||||
{"enter_default_mount_ns", {0, 0, {false, do_enter_default_mount_ns}}},
|
||||
{"symlink", {2, 2, {true, do_symlink}}},
|
||||
{"sysclktz", {1, 1, {false, do_sysclktz}}},
|
||||
|
|
|
|||
|
|
@ -283,7 +283,7 @@ std::tuple<mode_t, uid_t, gid_t> DeviceHandler::GetDevicePermissions(
|
|||
|
||||
void DeviceHandler::MakeDevice(const std::string& path, bool block, int major, int minor,
|
||||
const std::vector<std::string>& links) const {
|
||||
auto[mode, uid, gid] = GetDevicePermissions(path, links);
|
||||
auto [mode, uid, gid] = GetDevicePermissions(path, links);
|
||||
mode |= (block ? S_IFBLK : S_IFCHR);
|
||||
|
||||
std::string secontext;
|
||||
|
|
@ -330,11 +330,11 @@ void DeviceHandler::MakeDevice(const std::string& path, bool block, int major, i
|
|||
if (gid != s.st_gid) {
|
||||
new_group = gid;
|
||||
}
|
||||
if (mode != s.st_mode) {
|
||||
if (chmod(path.c_str(), mode) != 0) {
|
||||
PLOG(ERROR) << "Cannot chmod " << path << " to " << mode;
|
||||
if (mode != s.st_mode) {
|
||||
if (chmod(path.c_str(), mode) != 0) {
|
||||
PLOG(ERROR) << "Cannot chmod " << path << " to " << mode;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
PLOG(ERROR) << "Cannot stat " << path;
|
||||
}
|
||||
|
|
@ -532,7 +532,7 @@ void DeviceHandler::HandleAshmemUevent(const Uevent& uevent) {
|
|||
if (!ReadFileToString(boot_id_path, &boot_id)) {
|
||||
PLOG(ERROR) << "Cannot duplicate ashmem device node. Failed to read " << boot_id_path;
|
||||
return;
|
||||
};
|
||||
}
|
||||
boot_id = Trim(boot_id);
|
||||
|
||||
Uevent dup_ashmem_uevent = uevent;
|
||||
|
|
@ -543,10 +543,10 @@ void DeviceHandler::HandleAshmemUevent(const Uevent& uevent) {
|
|||
}
|
||||
|
||||
void DeviceHandler::HandleUevent(const Uevent& uevent) {
|
||||
if (uevent.action == "add" || uevent.action == "change" ||
|
||||
uevent.action == "bind" || uevent.action == "online") {
|
||||
FixupSysPermissions(uevent.path, uevent.subsystem);
|
||||
}
|
||||
if (uevent.action == "add" || uevent.action == "change" || uevent.action == "bind" ||
|
||||
uevent.action == "online") {
|
||||
FixupSysPermissions(uevent.path, uevent.subsystem);
|
||||
}
|
||||
|
||||
// if it's not a /dev device, nothing to do
|
||||
if (uevent.major < 0 || uevent.minor < 0) return;
|
||||
|
|
|
|||
|
|
@ -38,6 +38,7 @@
|
|||
#include <android-base/logging.h>
|
||||
#include <android-base/stringprintf.h>
|
||||
#include <android/avf_cc_flags.h>
|
||||
#include <fs_mgr.h>
|
||||
#include <modprobe/modprobe.h>
|
||||
#include <private/android_filesystem_config.h>
|
||||
|
||||
|
|
@ -303,6 +304,22 @@ static BootMode GetBootMode(const std::string& cmdline, const std::string& bootc
|
|||
return BootMode::NORMAL_MODE;
|
||||
}
|
||||
|
||||
static void MaybeResumeFromHibernation(const std::string& bootconfig) {
|
||||
std::string hibernationResumeDevice;
|
||||
android::fs_mgr::GetBootconfigFromString(bootconfig, "androidboot.hibernation_resume_device",
|
||||
&hibernationResumeDevice);
|
||||
if (!hibernationResumeDevice.empty()) {
|
||||
android::base::unique_fd fd(open("/sys/power/resume", O_RDWR | O_CLOEXEC));
|
||||
if (fd >= 0) {
|
||||
if (!android::base::WriteStringToFd(hibernationResumeDevice, fd)) {
|
||||
PLOG(ERROR) << "Failed to write to /sys/power/resume";
|
||||
}
|
||||
} else {
|
||||
PLOG(ERROR) << "Failed to open /sys/power/resume";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static std::unique_ptr<FirstStageMount> CreateFirstStageMount(const std::string& cmdline) {
|
||||
auto ret = FirstStageMount::Create(cmdline);
|
||||
if (ret.ok()) {
|
||||
|
|
@ -442,6 +459,8 @@ int FirstStageMain(int argc, char** argv) {
|
|||
<< module_elapse_time.count() << " ms";
|
||||
}
|
||||
|
||||
MaybeResumeFromHibernation(bootconfig);
|
||||
|
||||
std::unique_ptr<FirstStageMount> fsm;
|
||||
|
||||
bool created_devices = false;
|
||||
|
|
|
|||
|
|
@ -156,6 +156,13 @@ static Result<Fstab> ReadFirstStageFstabAndroid() {
|
|||
return fstab;
|
||||
}
|
||||
|
||||
static bool IsRequestingMicrodroidVendorPartition(const std::string& cmdline) {
|
||||
if (virtualization::IsEnableTpuAssignableDeviceFlagEnabled()) {
|
||||
return access("/proc/device-tree/avf/vendor_hashtree_descriptor_root_digest", F_OK) == 0;
|
||||
}
|
||||
return cmdline.find("androidboot.microdroid.mount_vendor=1") != std::string::npos;
|
||||
}
|
||||
|
||||
// Note: this is a temporary solution to avoid blocking devs that depend on /vendor partition in
|
||||
// Microdroid. For the proper solution the /vendor fstab should probably be defined in the DT.
|
||||
// TODO(b/285855430): refactor this
|
||||
|
|
@ -166,7 +173,7 @@ static Result<Fstab> ReadFirstStageFstabMicrodroid(const std::string& cmdline) {
|
|||
if (!ReadDefaultFstab(&fstab)) {
|
||||
return Error() << "failed to read fstab";
|
||||
}
|
||||
if (cmdline.find("androidboot.microdroid.mount_vendor=1") == std::string::npos) {
|
||||
if (!IsRequestingMicrodroidVendorPartition(cmdline)) {
|
||||
// We weren't asked to mount /vendor partition, filter it out from the fstab.
|
||||
auto predicate = [](const auto& entry) { return entry.mount_point == "/vendor"; };
|
||||
fstab.erase(std::remove_if(fstab.begin(), fstab.end(), predicate), fstab.end());
|
||||
|
|
@ -305,6 +312,11 @@ bool FirstStageMountVBootV2::InitDevices() {
|
|||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (IsArcvm() && !block_dev_init_.InitHvcDevice("hvc1")) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
@ -366,6 +378,14 @@ bool FirstStageMountVBootV2::CreateLogicalPartitions() {
|
|||
}
|
||||
|
||||
if (SnapshotManager::IsSnapshotManagerNeeded()) {
|
||||
auto init_devices = [this](const std::string& device) -> bool {
|
||||
if (android::base::StartsWith(device, "/dev/block/dm-")) {
|
||||
return block_dev_init_.InitDmDevice(device);
|
||||
}
|
||||
return block_dev_init_.InitDevices({device});
|
||||
};
|
||||
|
||||
SnapshotManager::MapTempOtaMetadataPartitionIfNeeded(init_devices);
|
||||
auto sm = SnapshotManager::NewForFirstStageMount();
|
||||
if (!sm) {
|
||||
return false;
|
||||
|
|
|
|||
|
|
@ -30,7 +30,6 @@ cc_defaults {
|
|||
shared_libs: [
|
||||
"libbase",
|
||||
"libfs_mgr",
|
||||
"libhidl-gen-utils",
|
||||
"libkeyutils",
|
||||
"liblog",
|
||||
"libprocessgroup",
|
||||
|
|
@ -50,7 +49,6 @@ cc_fuzz {
|
|||
srcs: [
|
||||
"init_parser_fuzzer.cpp",
|
||||
],
|
||||
shared_libs: ["libhidlmetadata",],
|
||||
defaults: [
|
||||
"libinit_fuzzer_defaults",
|
||||
],
|
||||
|
|
|
|||
|
|
@ -15,9 +15,7 @@
|
|||
*/
|
||||
|
||||
#include <fuzzer/FuzzedDataProvider.h>
|
||||
#include <hidl/metadata.h>
|
||||
#include <import_parser.h>
|
||||
#include <interface_utils.h>
|
||||
#include <rlimit_parser.h>
|
||||
|
||||
using namespace android;
|
||||
|
|
@ -34,7 +32,6 @@ const std::string kValidPaths[] = {
|
|||
};
|
||||
|
||||
const int32_t kMaxBytes = 256;
|
||||
const std::string kValidInterfaces = "android.frameworks.vr.composer@2.0::IVrComposerClient";
|
||||
|
||||
class InitParserFuzzer {
|
||||
public:
|
||||
|
|
@ -44,9 +41,6 @@ class InitParserFuzzer {
|
|||
private:
|
||||
void InvokeParser();
|
||||
void InvokeLimitParser();
|
||||
void InvokeInterfaceUtils();
|
||||
InterfaceInheritanceHierarchyMap GenerateHierarchyMap();
|
||||
std::vector<HidlInterfaceMetadata> GenerateInterfaceMetadata();
|
||||
|
||||
FuzzedDataProvider fdp_;
|
||||
};
|
||||
|
|
@ -64,60 +58,6 @@ void InitParserFuzzer::InvokeLimitParser() {
|
|||
}
|
||||
}
|
||||
|
||||
std::vector<HidlInterfaceMetadata> InitParserFuzzer::GenerateInterfaceMetadata() {
|
||||
std::vector<HidlInterfaceMetadata> random_interface;
|
||||
for (size_t idx = 0; idx < fdp_.ConsumeIntegral<size_t>(); ++idx) {
|
||||
HidlInterfaceMetadata metadata;
|
||||
metadata.name = fdp_.ConsumeRandomLengthString(kMaxBytes);
|
||||
for (size_t idx1 = 0; idx1 < fdp_.ConsumeIntegral<size_t>(); ++idx1) {
|
||||
metadata.inherited.push_back(fdp_.ConsumeRandomLengthString(kMaxBytes));
|
||||
}
|
||||
random_interface.push_back(metadata);
|
||||
}
|
||||
return random_interface;
|
||||
}
|
||||
|
||||
InterfaceInheritanceHierarchyMap InitParserFuzzer::GenerateHierarchyMap() {
|
||||
InterfaceInheritanceHierarchyMap result;
|
||||
std::vector<HidlInterfaceMetadata> random_interface;
|
||||
if (fdp_.ConsumeBool()) {
|
||||
random_interface = GenerateInterfaceMetadata();
|
||||
} else {
|
||||
random_interface = HidlInterfaceMetadata::all();
|
||||
}
|
||||
|
||||
for (const HidlInterfaceMetadata& iface : random_interface) {
|
||||
std::set<FQName> inherited_interfaces;
|
||||
for (const std::string& intf : iface.inherited) {
|
||||
FQName fqname;
|
||||
(void)fqname.setTo(intf);
|
||||
inherited_interfaces.insert(fqname);
|
||||
}
|
||||
FQName fqname;
|
||||
(void)fqname.setTo(iface.name);
|
||||
result[fqname] = inherited_interfaces;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
void InitParserFuzzer::InvokeInterfaceUtils() {
|
||||
InterfaceInheritanceHierarchyMap hierarchy_map = GenerateHierarchyMap();
|
||||
SetKnownInterfaces(hierarchy_map);
|
||||
IsKnownInterface(fdp_.ConsumeRandomLengthString(kMaxBytes));
|
||||
std::set<std::string> interface_set;
|
||||
for (size_t idx = 0; idx < fdp_.ConsumeIntegral<size_t>(); ++idx) {
|
||||
auto set_interface_values = fdp_.PickValueInArray<const std::function<void()>>({
|
||||
[&]() {
|
||||
interface_set.insert(("aidl/" + fdp_.ConsumeRandomLengthString(kMaxBytes)));
|
||||
},
|
||||
[&]() { interface_set.insert(fdp_.ConsumeRandomLengthString(kMaxBytes)); },
|
||||
[&]() { interface_set.insert(kValidInterfaces); },
|
||||
});
|
||||
set_interface_values();
|
||||
}
|
||||
CheckInterfaceInheritanceHierarchy(interface_set, hierarchy_map);
|
||||
}
|
||||
|
||||
void InitParserFuzzer::InvokeParser() {
|
||||
Parser parser;
|
||||
std::string name = fdp_.ConsumeBool() ? fdp_.ConsumeRandomLengthString(kMaxBytes) : "import";
|
||||
|
|
@ -132,7 +72,6 @@ void InitParserFuzzer::Process() {
|
|||
while (fdp_.remaining_bytes()) {
|
||||
auto invoke_parser_fuzzer = fdp_.PickValueInArray<const std::function<void()>>({
|
||||
[&]() { InvokeParser(); },
|
||||
[&]() { InvokeInterfaceUtils(); },
|
||||
[&]() { InvokeLimitParser(); },
|
||||
});
|
||||
invoke_parser_fuzzer();
|
||||
|
|
|
|||
|
|
@ -297,9 +297,7 @@ int main(int argc, char** argv) {
|
|||
ActionManager& am = ActionManager::GetInstance();
|
||||
ServiceList& sl = ServiceList::GetInstance();
|
||||
Parser parser;
|
||||
parser.AddSectionParser("service",
|
||||
std::make_unique<ServiceParser>(&sl, GetSubcontext(),
|
||||
*interface_inheritance_hierarchy_map));
|
||||
parser.AddSectionParser("service", std::make_unique<ServiceParser>(&sl, GetSubcontext()));
|
||||
parser.AddSectionParser("on", std::make_unique<ActionParser>(&am, GetSubcontext()));
|
||||
parser.AddSectionParser("import", std::make_unique<HostImportParser>());
|
||||
|
||||
|
|
@ -317,11 +315,23 @@ int main(int argc, char** argv) {
|
|||
return EXIT_FAILURE;
|
||||
}
|
||||
}
|
||||
|
||||
size_t failures = parser.parse_error_count() + am.CheckAllCommands() + sl.CheckAllCommands();
|
||||
if (failures > 0) {
|
||||
LOG(ERROR) << "Failed to parse init scripts with " << failures << " error(s).";
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
for (const auto& service : sl) {
|
||||
if (const auto& result = CheckInterfaceInheritanceHierarchy(
|
||||
service->interfaces(), *interface_inheritance_hierarchy_map);
|
||||
!result.ok()) {
|
||||
LOG(ERROR) << service->filename() << ": invalid interface in service '"
|
||||
<< service->name() << "': " << result.error();
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
}
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Reference in a new issue