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:
Michael Bestas 2024-12-10 23:23:24 +02:00
commit ffe39e16d3
212 changed files with 4742 additions and 5106 deletions

View file

@ -499,7 +499,7 @@ int32_t BootReasonStrToEnum(const std::string& boot_reason) {
} }
// Canonical list of supported primary reboot reasons. // Canonical list of supported primary reboot reasons.
const std::vector<const std::string> knownReasons = { const std::vector<std::string> knownReasons = {
// clang-format off // clang-format off
// kernel // kernel
"watchdog", "watchdog",

View file

@ -359,6 +359,7 @@ cc_test {
"libdebuggerd/test/dump_memory_test.cpp", "libdebuggerd/test/dump_memory_test.cpp",
"libdebuggerd/test/elf_fake.cpp", "libdebuggerd/test/elf_fake.cpp",
"libdebuggerd/test/log_fake.cpp", "libdebuggerd/test/log_fake.cpp",
"libdebuggerd/test/mte_stack_record_test.cpp",
"libdebuggerd/test/open_files_list_test.cpp", "libdebuggerd/test/open_files_list_test.cpp",
"libdebuggerd/test/tombstone_proto_to_text_test.cpp", "libdebuggerd/test/tombstone_proto_to_text_test.cpp",
], ],

View file

@ -138,7 +138,7 @@ bool debuggerd_trigger_dump(pid_t tid, DebuggerdDumpType dump_type, unsigned int
auto remaining = end - std::chrono::steady_clock::now(); auto remaining = end - std::chrono::steady_clock::now();
if (remaining < decltype(remaining)::zero()) { if (remaining < decltype(remaining)::zero()) {
log_error(output_fd, 0, "timeout expired"); log_error(output_fd, 0, "timeout expired (update_timeout)");
return false; return false;
} }
@ -254,7 +254,7 @@ bool debuggerd_trigger_dump(pid_t tid, DebuggerdDumpType dump_type, unsigned int
if (timeout_ms <= 0) { if (timeout_ms <= 0) {
remaining_ms = -1; remaining_ms = -1;
} else if (remaining_ms < 0) { } else if (remaining_ms < 0) {
log_error(output_fd, 0, "timeout expired"); log_error(output_fd, 0, "timeout expired before poll");
return false; return false;
} }
@ -271,7 +271,7 @@ bool debuggerd_trigger_dump(pid_t tid, DebuggerdDumpType dump_type, unsigned int
return false; return false;
} }
} else if (rc == 0) { } else if (rc == 0) {
log_error(output_fd, 0, "timeout expired"); log_error(output_fd, 0, "poll timeout expired");
return false; return false;
} }

View file

@ -662,6 +662,15 @@ int main(int argc, char** argv) {
info.pac_enabled_keys = -1; 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) { if (thread == g_target_thread) {
// Read the thread's registers along with the rest of the crash info out of the pipe. // 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); ReadCrashInfo(input_pipe, &siginfo, &info.registers, &process_info, &recoverable_crash);

View file

@ -18,6 +18,7 @@
#include <dlfcn.h> #include <dlfcn.h>
#include <err.h> #include <err.h>
#include <fcntl.h> #include <fcntl.h>
#include <inttypes.h>
#include <linux/prctl.h> #include <linux/prctl.h>
#include <malloc.h> #include <malloc.h>
#include <pthread.h> #include <pthread.h>
@ -69,7 +70,6 @@
#include "crash_test.h" #include "crash_test.h"
#include "debuggerd/handler.h" #include "debuggerd/handler.h"
#include "gtest/gtest.h" #include "gtest/gtest.h"
#include "libdebuggerd/utility.h"
#include "protocol.h" #include "protocol.h"
#include "tombstoned/tombstoned.h" #include "tombstoned/tombstoned.h"
#include "util.h" #include "util.h"
@ -741,6 +741,8 @@ TEST_F(CrasherTest, mte_multiple_causes) {
} }
#if defined(__aarch64__) #if defined(__aarch64__)
constexpr size_t kTagGranuleSize = 16;
static uintptr_t CreateTagMapping() { static uintptr_t CreateTagMapping() {
// Some of the MTE tag dump tests assert that there is an inaccessible page to the left and right // 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. // 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); 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) { TEST_F(CrasherTest, competing_tracer) {
int intercept_result; int intercept_result;
unique_fd output_fd; unique_fd output_fd;

View file

@ -48,50 +48,69 @@ using android::base::unique_fd;
extern "C" bool __linker_enable_fallback_allocator(); extern "C" bool __linker_enable_fallback_allocator();
extern "C" void __linker_disable_fallback_allocator(); extern "C" void __linker_disable_fallback_allocator();
// This is incredibly sketchy to do inside of a signal handler, especially when libbacktrace // This file implements a fallback path for processes that do not allow the
// uses the C++ standard library throughout, but this code runs in the linker, so we'll be using // normal fork and exec of crash_dump to handle crashes/unwinds.
// the linker's malloc instead of the libc one. Switch it out for a replacement, just in case. // 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
// This isn't the default method of dumping because it can fail in cases such as address space // thread safe. In order to avoid any problems allocating, the code calls
// exhaustion. // 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) { static void debuggerd_fallback_trace(int output_fd, ucontext_t* ucontext) {
if (!__linker_enable_fallback_allocator()) { std::unique_ptr<unwindstack::Regs> regs;
async_safe_format_log(ANDROID_LOG_ERROR, "libc", "fallback allocator already in use");
return;
}
{ ThreadInfo thread;
std::unique_ptr<unwindstack::Regs> regs; 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; // Do not use the thread cache here because it will call pthread_key_create
thread.pid = getpid(); // which doesn't work in linker code. See b/189803009.
thread.tid = gettid(); // Use a normal cached object because the thread is stopped, and there
thread.thread_name = get_thread_name(gettid()); // is no chance of data changing between reads.
thread.registers.reset( auto process_memory = unwindstack::Memory::CreateProcessMemoryCached(getpid());
unwindstack::Regs::CreateFromUcontext(unwindstack::Regs::CurrentArch(), ucontext)); // TODO: Create this once and store it in a global?
unwindstack::AndroidLocalUnwinder unwinder(process_memory);
// Do not use the thread cache here because it will call pthread_key_create dump_backtrace_thread(output_fd, &unwinder, thread);
// 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();
} }
static bool forward_output(int src_fd, int dst_fd, pid_t expected_tid) { 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) { 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)); static std::atomic<uint64_t> trace_output(pack_thread_fd(-1, -1));
if (info->si_value.sival_ptr == kDebuggerdFallbackSivalPtrRequestDump) { 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"); 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); close(fd);
return; 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. // Send a signal to all of our siblings, asking them to dump their stack.
pid_t current_tid = gettid(); pid_t current_tid = gettid();
if (!iterate_tids(current_tid, [&output_fd, &current_tid](pid_t tid) { if (!iterate_tids(current_tid, [&allocator, &output_fd, &current_tid](pid_t tid) {
if (current_tid == tid) { if (current_tid == tid) {
return; return;
} }
if (!allocator.enabled()) {
return;
}
// Use a pipe, to be able to detect situations where the thread gracefully exits before // Use a pipe, to be able to detect situations where the thread gracefully exits before
// receiving our signal. // receiving our signal.
unique_fd pipe_read, pipe_write; unique_fd pipe_read, pipe_write;
@ -233,22 +267,29 @@ static void trace_handler(siginfo_t* info, ucontext_t* ucontext) {
return; return;
} }
// Disable our use of the fallback allocator while the target thread
// is getting the backtrace.
allocator.Disable();
siginfo_t siginfo = {}; siginfo_t siginfo = {};
siginfo.si_code = SI_QUEUE; siginfo.si_code = SI_QUEUE;
siginfo.si_value.sival_ptr = kDebuggerdFallbackSivalPtrRequestDump; siginfo.si_value.sival_ptr = kDebuggerdFallbackSivalPtrRequestDump;
siginfo.si_pid = getpid(); siginfo.si_pid = getpid();
siginfo.si_uid = getuid(); 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", async_safe_format_log(ANDROID_LOG_ERROR, "libc", "failed to send trace signal to %d: %s",
tid, strerror(errno)); tid, strerror(errno));
return;
} }
bool success = forward_output(pipe_read.get(), output_fd.get(), tid); // The thread should be finished now, so try and re-enable the fallback allocator.
if (!success) { if (!allocator.Enable()) {
async_safe_format_log(ANDROID_LOG_ERROR, "libc", return;
"timeout expired while waiting for thread %d to dump", tid);
} }
// Regardless of whether the poll succeeds, check to see if the thread took fd ownership. // 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); close(fd);
} }
} }
return;
})) { })) {
async_safe_format_log(ANDROID_LOG_ERROR, "libc", "failed to open /proc/%d/task: %s", async_safe_format_log(ANDROID_LOG_ERROR, "libc", "failed to open /proc/%d/task: %s",
current_tid, strerror(errno)); 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()); 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; unique_fd tombstone_socket, output_fd, proto_fd;
bool tombstoned_connected = tombstoned_connect(getpid(), &tombstone_socket, &output_fd, &proto_fd, bool tombstoned_connected = tombstoned_connect(getpid(), &tombstone_socket, &output_fd, &proto_fd,
kDebuggerdTombstoneProto); 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) { if (tombstoned_connected) {
tombstoned_notify_completion(tombstone_socket.get()); tombstoned_notify_completion(tombstone_socket.get());
} }

View file

@ -36,10 +36,12 @@
#include <sys/uio.h> #include <sys/uio.h>
#include <sys/un.h> #include <sys/un.h>
#include <sys/wait.h> #include <sys/wait.h>
#include <time.h>
#include <unistd.h> #include <unistd.h>
#include <android-base/macros.h> #include <android-base/macros.h>
#include <android-base/parsebool.h> #include <android-base/parsebool.h>
#include <android-base/parseint.h>
#include <android-base/properties.h> #include <android-base/properties.h>
#include <android-base/unique_fd.h> #include <android-base/unique_fd.h>
#include <async_safe/log.h> #include <async_safe/log.h>
@ -115,6 +117,59 @@ static bool is_permissive_mte() {
(permissive_env && ParseBool(permissive_env) == ParseBoolResult::kTrue); (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) { static inline void futex_wait(volatile void* ftx, int value) {
syscall(__NR_futex, ftx, FUTEX_WAIT, value, nullptr, nullptr, 0); 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) { if (tagged_addr_ctrl < 0) {
fatal_errno("failed to PR_GET_TAGGED_ADDR_CTRL"); 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; 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) { if (prctl(PR_SET_TAGGED_ADDR_CTRL, tagged_addr_ctrl, 0, 0, 0) < 0) {
fatal_errno("failed to PR_SET_TAGGED_ADDR_CTRL"); fatal_errno("failed to PR_SET_TAGGED_ADDR_CTRL");
} }
async_safe_format_log(ANDROID_LOG_ERROR, "libc", if (int reenable_timer = permissive_mte_renable_timer()) {
"MTE ERROR DETECTED BUT RUNNING IN PERMISSIVE MODE. CONTINUING."); 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); 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. // Use the alternate signal stack if available so we can catch stack overflows.
action.sa_flags |= SA_ONSTACK; 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 // Request that the kernel set tag bits in the fault address. This is necessary for diagnosing MTE
// faults. // faults.
action.sa_flags |= SA_EXPOSE_TAGBITS; action.sa_flags |= SA_EXPOSE_TAGBITS;

View file

@ -73,5 +73,8 @@ bool tombstone_proto_to_text(
void fill_in_backtrace_frame(BacktraceFrame* f, const unwindstack::FrameData& frame); void fill_in_backtrace_frame(BacktraceFrame* f, const unwindstack::FrameData& frame);
void set_human_readable_cause(Cause* cause, uint64_t fault_addr); 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 #endif // _DEBUGGERD_TOMBSTONE_H

View file

@ -41,6 +41,9 @@ struct ThreadInfo {
siginfo_t* siginfo = nullptr; siginfo_t* siginfo = nullptr;
std::unique_ptr<unwindstack::Regs> guest_registers; 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. // This struct is written into a pipe from inside the crashing process.

View file

@ -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

View 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

View file

@ -134,3 +134,31 @@ TEST_F(TombstoneProtoToTextTest, crash_detail_bytes) {
ProtoToString(); ProtoToString();
EXPECT_MATCH(text_, R"(CRASH_DETAIL_NAME: 'helloworld\\1\\255\\3')"); 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");
}

View file

@ -27,6 +27,7 @@
#include <inttypes.h> #include <inttypes.h>
#include <signal.h> #include <signal.h>
#include <stddef.h> #include <stddef.h>
#include <stdint.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include <sys/mman.h> #include <sys/mman.h>
@ -49,9 +50,11 @@
#include <android/log.h> #include <android/log.h>
#include <android/set_abort_message.h> #include <android/set_abort_message.h>
#include <bionic/macros.h>
#include <bionic/reserved_signals.h>
#include <bionic/crash_detail_internal.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.h>
#include <log/log_read.h> #include <log/log_read.h>
#include <log/logprint.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())); 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, 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) #if defined(USE_SCUDO)
ScudoCrashData scudo_crash_data(unwinder->GetProcessMemory().get(), process_info); ScudoCrashData scudo_crash_data(unwinder->GetProcessMemory().get(), process_info);
if (scudo_crash_data.CrashIsMine()) { 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); auto map_info = maps->Find(fault_addr);
if (map_info != nullptr && map_info->flags() == PROT_EXEC) { if (map_info != nullptr && map_info->flags() == PROT_EXEC) {
cause = "execute-only (no-read) memory access error; likely due to data in .text."; 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 { } else {
cause = get_stack_overflow_cause(fault_addr, target_thread.registers->sp(), maps); 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, cause = StringPrintf("seccomp prevented call to disallowed %s system call %d", ABI_STRING,
si->si_syscall); 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()); dump_mappings(&result, unwinder->GetMaps(), unwinder->GetProcessMemory());

View file

@ -511,6 +511,19 @@ static void print_main_thread(CallbackType callback, const Tombstone& tombstone,
"order of likelihood."); "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()) { for (const Cause& cause : tombstone.causes()) {
if (tombstone.causes_size() > 1) { if (tombstone.causes_size() > 1) {
CBS(""); CBS("");

View file

@ -22,6 +22,21 @@ message CrashDetail {
reserved 3 to 999; 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 { message Tombstone {
Architecture arch = 1; Architecture arch = 1;
Architecture guest_arch = 24; Architecture guest_arch = 24;
@ -54,7 +69,9 @@ message Tombstone {
uint32 page_size = 22; uint32 page_size = 22;
bool has_been_16kb_mode = 23; bool has_been_16kb_mode = 23;
reserved 26 to 999; StackHistoryBuffer stack_history_buffer = 26;
reserved 27 to 999;
} }
enum Architecture { enum Architecture {

View file

@ -20,5 +20,14 @@
int main(int, char**) { int main(int, char**) {
volatile char* f = (char*)malloc(1); volatile char* f = (char*)malloc(1);
printf("%c\n", f[17]); 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; return 0;
} }

View file

@ -97,6 +97,34 @@ public class PermissiveMteTest extends BaseHostJUnit4Test {
} }
assertThat(numberTombstones).isEqualTo(1); 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 @Test
public void testCrashProperty() throws Exception { public void testCrashProperty() throws Exception {
String prevValue = getDevice().getProperty("persist.sys.mte.permissive"); String prevValue = getDevice().getProperty("persist.sys.mte.permissive");

View file

@ -158,7 +158,7 @@ class CrashQueue {
} }
} }
return std::move(result); return result;
} }
std::optional<CrashOutput> get_output(DebuggerdDumpType dump_type) { std::optional<CrashOutput> get_output(DebuggerdDumpType dump_type) {

View file

@ -186,6 +186,7 @@ cc_binary {
"libprotobuf-cpp-lite", "libprotobuf-cpp-lite",
"libsparse", "libsparse",
"libutils", "libutils",
"libselinux",
], ],
static_libs: [ static_libs: [

View file

@ -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 :=

View file

@ -90,7 +90,7 @@ std::string TransportSniffer::CreateTrace() {
// and be printed as a string, or just a raw byte-buffer // and be printed as a string, or just a raw byte-buffer
const auto msg = [&ret, no_print](const std::vector<char>& buf) { const auto msg = [&ret, no_print](const std::vector<char>& buf) {
ret += android::base::StringPrintf("(%lu bytes): ", buf.size()); 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; const unsigned max_chars = 50;
if (buf.size() > max_chars) { if (buf.size() > max_chars) {
iter = buf.begin() + max_chars; iter = buf.begin() + max_chars;

View file

@ -163,7 +163,7 @@ struct TestFileHandle {
protected: protected:
// |rel_path| is the relative path under test data directory. // |rel_path| is the relative path under test data directory.
TestFileHandle(const std::filesystem::path& rel_path) 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 // 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. // 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, virtual android::base::Result<borrowed_fd> Transform(const std::filesystem::path& abs_path,

View file

@ -33,6 +33,9 @@
} }
], ],
"kernel-presubmit": [ "kernel-presubmit": [
{
"name": "adb-remount-sh"
},
{ {
"name": "libdm_test" "name": "libdm_test"
}, },

View file

@ -28,6 +28,7 @@
#include <sys/ioctl.h> #include <sys/ioctl.h>
#include <sys/mount.h> #include <sys/mount.h>
#include <sys/stat.h> #include <sys/stat.h>
#include <sys/statvfs.h>
#include <sys/swap.h> #include <sys/swap.h>
#include <sys/types.h> #include <sys/types.h>
#include <sys/utsname.h> #include <sys/utsname.h>
@ -37,10 +38,8 @@
#include <array> #include <array>
#include <chrono> #include <chrono>
#include <functional>
#include <map> #include <map>
#include <memory> #include <memory>
#include <numeric>
#include <string> #include <string>
#include <string_view> #include <string_view>
#include <thread> #include <thread>
@ -65,6 +64,7 @@
#include <fs_mgr/file_wait.h> #include <fs_mgr/file_wait.h>
#include <fs_mgr_overlayfs.h> #include <fs_mgr_overlayfs.h>
#include <fscrypt/fscrypt.h> #include <fscrypt/fscrypt.h>
#include <fstab/fstab.h>
#include <libdm/dm.h> #include <libdm/dm.h>
#include <libdm/loop_control.h> #include <libdm/loop_control.h>
#include <liblp/metadata_format.h> #include <liblp/metadata_format.h>
@ -81,7 +81,7 @@
#define F2FS_FSCK_BIN "/system/bin/fsck.f2fs" #define F2FS_FSCK_BIN "/system/bin/fsck.f2fs"
#define MKSWAP_BIN "/system/bin/mkswap" #define MKSWAP_BIN "/system/bin/mkswap"
#define TUNE2FS_BIN "/system/bin/tune2fs" #define TUNE2FS_BIN "/system/bin/tune2fs"
#define RESIZE2FS_BIN "/system/bin/resize2fs" #define RESIZE2FS_BIN "/system/bin/resize2fs"
#ifdef __ANDROID_RECOVERY__ #ifdef __ANDROID_RECOVERY__
#define FSCK_LOG_FILE "/dev/null" #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) { static void log_fs_stat(const std::string& blk_device, int fs_stat) {
std::string msg = std::string msg =
android::base::StringPrintf("\nfs_stat,%s,0x%x\n", blk_device.c_str(), fs_stat); 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 | android::base::unique_fd fd(TEMP_FAILURE_RETRY(
O_APPEND | O_CREAT, 0664))); open(FSCK_LOG_FILE, O_WRONLY | O_CLOEXEC | O_APPEND | O_CREAT, 0664)));
if (fd == -1 || !android::base::WriteStringToFd(msg, fd)) { if (fd == -1 || !android::base::WriteStringToFd(msg, fd)) {
LWARNING << __FUNCTION__ << "() cannot log " << msg; 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 if (!(*fs_stat & FS_STAT_FULL_MOUNT_FAILED)) { // already tried if full mount failed
errno = 0; 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, ret = mount(blk_device.c_str(), target.c_str(), fs_type.c_str(), tmpmnt_flags,
tmpmnt_opts.c_str()); tmpmnt_opts.c_str());
PINFO << __FUNCTION__ << "(): mount(" << blk_device << "," << target << "," << fs_type 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, // Must give `-T now` to prevent last_fsck_time from growing too large,
// otherwise, tune2fs won't enable metadata_csum. // 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()}; "-T", "now", blk_device.c_str()};
const char* resize2fs_args[] = {RESIZE2FS_BIN, "-b", 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 // attempted_idx: On return, will indicate which fstab entry
// succeeded. In case of failure, it will be the start_idx. // succeeded. In case of failure, it will be the start_idx.
// Sets errno to match the 1st mount failure on failure. // 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; unsigned long i;
int mount_errno = 0; int mount_errno = 0;
bool mounted = false; bool mounted = false;
@ -955,6 +952,13 @@ static bool mount_with_alternatives(Fstab& fstab, int start_idx, int* end_idx, i
continue; 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 // 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. // AVB related functions. Copy it from start_idx to the current index i.
if ((i != start_idx) && fstab[i].fs_mgr_flags.logical && 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; 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 // 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. // one in turn, and ignore any duplicates after a first successful mount.
// Returns -1 on error, and FS_MGR_MNTALL_* otherwise. // 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 encryptable = FS_MGR_MNTALL_DEV_NOT_ENCRYPTABLE;
int error_count = 0; int error_count = 0;
CheckpointManager checkpoint_manager; CheckpointManager checkpoint_manager;
AvbUniquePtr avb_handle(nullptr); AvbUniquePtr avb_handle(nullptr);
bool wiped = false; bool wiped = false;
bool userdata_mounted = false; bool userdata_mounted = false;
if (fstab->empty()) { if (fstab->empty()) {
return {FS_MGR_MNTALL_FAIL, userdata_mounted}; return FS_MGR_MNTALL_FAIL;
} }
bool scratch_can_be_mounted = true; bool scratch_can_be_mounted = true;
@ -1513,7 +1557,7 @@ MountAllResult fs_mgr_mount_all(Fstab* fstab, int mount_mode) {
if (!avb_handle) { if (!avb_handle) {
LERROR << "Failed to open AvbHandle"; LERROR << "Failed to open AvbHandle";
set_type_property(encryptable); set_type_property(encryptable);
return {FS_MGR_MNTALL_FAIL, userdata_mounted}; return FS_MGR_MNTALL_FAIL;
} }
} }
if (avb_handle->SetUpAvbHashtree(&current_entry, true /* wait_for_verity_dev */) == if (avb_handle->SetUpAvbHashtree(&current_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 last_idx_inspected = -1;
int top_idx = i; const int top_idx = i;
int attempted_idx = -1; 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]; auto& attempted_entry = (*fstab)[attempted_idx];
i = last_idx_inspected; i = last_idx_inspected;
int mount_errno = errno; int mount_errno = errno;
@ -1547,7 +1593,7 @@ MountAllResult fs_mgr_mount_all(Fstab* fstab, int mount_mode) {
if (status == FS_MGR_MNTALL_FAIL) { if (status == FS_MGR_MNTALL_FAIL) {
// Fatal error - no point continuing. // Fatal error - no point continuing.
return {status, userdata_mounted}; return status;
} }
if (status != FS_MGR_MNTALL_DEV_NOT_ENCRYPTABLE) { 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.mount_point, wiped ? "true" : "false",
attempted_entry.fs_type, attempted_entry.fs_type,
attempted_entry.fs_mgr_flags.is_zoned ? "true" : "false", attempted_entry.fs_mgr_flags.is_zoned ? "true" : "false",
std::to_string(attempted_entry.length),
android::base::Join(attempted_entry.user_devices, ' ')}, android::base::Join(attempted_entry.user_devices, ' ')},
nullptr)) { nullptr)) {
LERROR << "Encryption failed"; LERROR << "Encryption failed";
set_type_property(encryptable); 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. // Success! Go get the next one.
continue; continue;
} }
auto formattable_entry =
LocateFormattableEntry(fstab->data() + top_idx, fstab->data() + fstab->size());
// Mounting failed, understand why and retry. // Mounting failed, understand why and retry.
wiped = partition_wiped(current_entry.blk_device.c_str()); wiped = partition_wiped(current_entry.blk_device.c_str());
if (mount_errno != EBUSY && mount_errno != EACCES && 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 // current_entry and attempted_entry point at the same partition, but sometimes
// at two different lines in the fstab. Use current_entry for formatting // at two different lines in the fstab. Use current_entry for formatting
// as that is the preferred one. // as that is the preferred one.
LERROR << __FUNCTION__ << "(): " << realpath(current_entry.blk_device) if (wiped)
<< " is wiped and " << current_entry.mount_point << " " << current_entry.fs_type LERROR << __FUNCTION__ << "(): " << realpath(current_entry.blk_device)
<< " is formattable. Format it."; << " 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(&current_entry); checkpoint_manager.Revert(&current_entry);
@ -1603,11 +1656,12 @@ MountAllResult fs_mgr_mount_all(Fstab* fstab, int mount_mode) {
encryptable = FS_MGR_MNTALL_DEV_IS_METADATA_ENCRYPTED; encryptable = FS_MGR_MNTALL_DEV_IS_METADATA_ENCRYPTED;
set_type_property(encryptable); set_type_property(encryptable);
if (!call_vdc({"cryptfs", "encryptFstab", current_entry.blk_device, if (!call_vdc({"cryptfs", "encryptFstab", formattable_entry->blk_device,
current_entry.mount_point, "true" /* shouldFormat */, formattable_entry->mount_point, "true" /* shouldFormat */,
current_entry.fs_type, formattable_entry->fs_type,
current_entry.fs_mgr_flags.is_zoned ? "true" : "false", formattable_entry->fs_mgr_flags.is_zoned ? "true" : "false",
android::base::Join(current_entry.user_devices, ' ')}, std::to_string(formattable_entry->length),
android::base::Join(formattable_entry->user_devices, ' ')},
nullptr)) { nullptr)) {
LERROR << "Encryption failed"; LERROR << "Encryption failed";
} else { } 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. // Let's replay the mount actions.
i = top_idx - 1; i = top_idx - 1;
continue; 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. // 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)) { should_use_metadata_encryption(attempted_entry)) {
if (!call_vdc({"cryptfs", "mountFstab", attempted_entry.blk_device, if (!call_vdc({"cryptfs", "mountFstab", attempted_entry.blk_device,
attempted_entry.mount_point, attempted_entry.mount_point,
@ -1647,13 +1701,13 @@ MountAllResult fs_mgr_mount_all(Fstab* fstab, int mount_mode) {
// Use StringPrintf to output "(null)" instead. // Use StringPrintf to output "(null)" instead.
if (attempted_entry.fs_mgr_flags.no_fail) { if (attempted_entry.fs_mgr_flags.no_fail) {
PERROR << android::base::StringPrintf( 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", "partition on %s at %s options: %s",
attempted_entry.blk_device.c_str(), attempted_entry.mount_point.c_str(), attempted_entry.blk_device.c_str(), attempted_entry.mount_point.c_str(),
attempted_entry.fs_options.c_str()); attempted_entry.fs_options.c_str());
} else { } else {
PERROR << android::base::StringPrintf( 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", "on %s at %s options: %s",
attempted_entry.blk_device.c_str(), attempted_entry.mount_point.c_str(), attempted_entry.blk_device.c_str(), attempted_entry.mount_point.c_str(),
attempted_entry.fs_options.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); set_type_property(encryptable);
if (error_count) { if (error_count) {
return {FS_MGR_MNTALL_FAIL, userdata_mounted}; return FS_MGR_MNTALL_FAIL;
} else { } else {
return {encryptable, userdata_mounted}; return encryptable;
} }
} }
@ -1718,190 +1772,6 @@ int fs_mgr_umount_all(android::fs_mgr::Fstab* fstab) {
return ret; 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, // wrapper to __mount() and expects a fully prepared fstab_rec,
// unlike fs_mgr_do_mount which does more things with avb / verity etc. // 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) { 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); int ret = prepare_fs_for_mount(entry.blk_device, entry, mount_point);
// Wiped case doesn't require to try __mount below. // Wiped case doesn't require to try __mount below.
if (ret & FS_STAT_INVALID_MAGIC) { if (ret & FS_STAT_INVALID_MAGIC) {
return FS_MGR_DOMNT_FAILED; return FS_MGR_DOMNT_FAILED;
} }
ret = __mount(entry.blk_device, mount_point, entry); ret = __mount(entry.blk_device, mount_point, entry);
if (ret) { 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; return ret;
@ -2073,11 +1943,45 @@ static bool InstallZramDevice(const std::string& device) {
return true; 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) { static bool PrepareZramBackingDevice(off64_t size) {
constexpr const char* file_path = "/data/per_boot/zram_swap"; constexpr const char* file_path = "/data/per_boot/zram_swap";
if (size == 0) return true; if (size == 0) return true;
// Check available space
if (!ZramBackingDeviceSizeAvailable(size)) {
PERROR << "No space for target path: " << file_path;
return false;
}
// Prepare target path // Prepare target path
unique_fd target_fd(TEMP_FAILURE_RETRY(open(file_path, O_RDWR | O_CREAT | O_CLOEXEC, 0600))); unique_fd target_fd(TEMP_FAILURE_RETRY(open(file_path, O_RDWR | O_CREAT | O_CLOEXEC, 0600)));
if (target_fd.get() == -1) { if (target_fd.get() == -1) {
@ -2086,6 +1990,7 @@ static bool PrepareZramBackingDevice(off64_t size) {
} }
if (fallocate(target_fd.get(), 0, 0, size) < 0) { if (fallocate(target_fd.get(), 0, 0, size) < 0) {
PERROR << "Cannot truncate target path: " << file_path; PERROR << "Cannot truncate target path: " << file_path;
unlink(file_path);
return false; return false;
} }

View file

@ -74,7 +74,7 @@ bool fs_mgr_is_dsu_running() {
return android::gsi::IsGsiRunning(); 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, // Never fallback to legacy cache mount point if within a DSU system,
// because running a DSU system implies the device supports dynamic // because running a DSU system implies the device supports dynamic
// partitions, which means legacy cache mustn't be used. // 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 retval = true;
bool move_dir_shared = true; bool move_dir_shared = true;
bool parent_shared = true; bool parent_shared = true;
bool parent_have_parent = false;
bool parent_made_private = false;
bool root_shared = true; bool root_shared = true;
bool root_made_private = false; bool root_made_private = false;
@ -443,6 +445,10 @@ static bool fs_mgr_overlayfs_mount_one(const FstabEntry& fstab_entry) {
if (entry.mount_point == "/") { if (entry.mount_point == "/") {
root_shared = entry.shared_flag; 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 // 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. // 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. // This could happen if its parent mount is remounted later.
if (!fs_mgr_overlayfs_set_shared_mount(mount_point, false)) { if (parent_have_parent) {
// If failed to set "/system" mount type, it might be due to "/system" not being a valid parent_made_private |= fs_mgr_overlayfs_set_shared_mount(mount_point, false);
// mountpoint after switch root. Retry with "/" in this case. if (!parent_made_private && errno == EINVAL && mount_point == "/system") {
if (errno == EINVAL && mount_point == "/system") { // If failed to set "/system" mount type, it might be due to "/system" not being a valid
root_made_private = fs_mgr_overlayfs_set_shared_mount("/", false); // 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; 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) { if (new_entry.shared_flag) {
new_entry.shared_flag = fs_mgr_overlayfs_set_shared_mount(new_entry.mount_point, false); 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()); rmdir(entry.dir.c_str());
} }
// If the original (overridden) mount was MS_SHARED, then set the overlayfs mount to MS_SHARED. // 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); fs_mgr_overlayfs_set_shared_mount(mount_point, true);
} }
if (root_shared && root_made_private) { if (root_shared && root_made_private) {

View file

@ -54,7 +54,7 @@ const std::string fs_mgr_mount_point(const std::string& mount_point);
bool OverlayfsSetupAllowed(bool verbose = false); bool OverlayfsSetupAllowed(bool verbose = false);
bool MountScratch(const std::string& device_path, bool readonly = false); bool MountScratch(const std::string& device_path, bool readonly = false);
bool fs_mgr_overlayfs_umount_scratch(); 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_overlayfs_already_mounted(const std::string& mount_point, bool overlay_only = true);
bool fs_mgr_wants_overlayfs(android::fs_mgr::FstabEntry* entry); 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); android::fs_mgr::Fstab fs_mgr_overlayfs_candidate_list(const android::fs_mgr::Fstab& fstab);

View file

@ -80,10 +80,8 @@
using namespace std::chrono_literals; 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_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_is_f2fs(const std::string& blk_device);
bool fs_mgr_filesystem_available(const std::string& filesystem); bool fs_mgr_filesystem_available(const std::string& filesystem);

View file

@ -171,11 +171,12 @@ bool VerifyCheckpointing() {
} }
if (show_help) { if (show_help) {
show_help = false; show_help = false;
std::cerr << "WARNING: Userdata checkpoint is in progress. To force end checkpointing, " std::cerr << "WARNING: Userdata checkpoint is in progress. "
"call 'vdc checkpoint commitChanges'. This can lead to data corruption if " "To forcibly end checkpointing, "
"rolled back." "call 'vdc checkpoint commitChanges'. "
"This can lead to data corruption if rolled back."
<< std::endl; << 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); std::this_thread::sleep_for(4s);
} }

View file

@ -36,7 +36,7 @@ namespace {
// The order of the list means the priority to show the files in the directory. // The order of the list means the priority to show the files in the directory.
// The last one has the highest priority. // The last one has the highest priority.
const std::vector<const std::string> kVendorOverlaySourceDirs = { const std::vector<std::string> kVendorOverlaySourceDirs = {
"/system/vendor_overlay/", "/system/vendor_overlay/",
"/product/vendor_overlay/", "/product/vendor_overlay/",
}; };

View file

@ -58,13 +58,8 @@ enum mount_mode {
#define FS_MGR_MNTALL_DEV_NEEDS_RECOVERY 4 #define FS_MGR_MNTALL_DEV_NEEDS_RECOVERY 4
#define FS_MGR_MNTALL_DEV_NOT_ENCRYPTABLE 0 #define FS_MGR_MNTALL_DEV_NOT_ENCRYPTABLE 0
#define FS_MGR_MNTALL_FAIL (-1) #define FS_MGR_MNTALL_FAIL (-1)
// fs_mgr_mount_all() updates fstab entries that reference device-mapper.
struct MountAllResult { int fs_mgr_mount_all(android::fs_mgr::Fstab* fstab, int mount_mode);
// 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;
};
struct HashtreeInfo { struct HashtreeInfo {
// The hash algorithm used to build the merkle tree. // The hash algorithm used to build the merkle tree.
@ -75,13 +70,6 @@ struct HashtreeInfo {
bool check_at_most_once; 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_FAILED (-1)
#define FS_MGR_DOMNT_BUSY (-2) #define FS_MGR_DOMNT_BUSY (-2)
#define FS_MGR_DOMNT_SUCCESS 0 #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. // returned. Otherwise, it will use the current slot.
std::string fs_mgr_get_super_partition_name(int slot = -1); 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 { enum FsMgrUmountStatus : int {
SUCCESS = 0, SUCCESS = 0,
ERROR_UNKNOWN = 1 << 0, ERROR_UNKNOWN = 1 << 0,
@ -127,11 +121,6 @@ enum FsMgrUmountStatus : int {
// it destroys verity devices from device mapper after the device is unmounted. // it destroys verity devices from device mapper after the device is unmounted.
int fs_mgr_umount_all(android::fs_mgr::Fstab* fstab); 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 // Finds the dm_bow device on which this block device is stacked, or returns
// empty string // empty string
std::string fs_mgr_find_bow_device(const std::string& block_device); 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 // Unlike fs_mgr_overlayfs, mount overlayfs without upperdir and workdir, so the
// filesystem cannot be remount read-write. // filesystem cannot be remount read-write.
bool fs_mgr_mount_overlayfs_fstab_entry(const android::fs_mgr::FstabEntry& entry); 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);

View file

@ -29,6 +29,8 @@ namespace fs_mgr {
// first match or nullptr. // first match or nullptr.
FstabEntry* GetEntryForPath(Fstab* fstab, const std::string& path); 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. // Make sure that the volume 'path' is on is mounted.
// * If 'mount_point' is nullptr, use mount point in fstab. Caller can call // * 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. // fs_mgr_ensure_path_unmounted() with the same 'path' argument to unmount.

View file

@ -60,6 +60,7 @@ static_assert(sizeof(off_t) == sizeof(uint64_t));
static inline void cleanup(const std::string& file_path, bool created) { static inline void cleanup(const std::string& file_path, bool created) {
if (created) { if (created) {
unlink(file_path.c_str()); unlink(file_path.c_str());
sync();
} }
} }

View file

@ -34,6 +34,7 @@
#include <string> #include <string>
#include <android-base/logging.h>
#include <android-base/macros.h> #include <android-base/macros.h>
#include <android-base/strings.h> #include <android-base/strings.h>
#include <android-base/unique_fd.h> #include <android-base/unique_fd.h>

View file

@ -21,6 +21,7 @@
#include <array> #include <array>
#include <sstream> #include <sstream>
#include <android-base/logging.h>
#include <android-base/file.h> #include <android-base/file.h>
#include <android-base/strings.h> #include <android-base/strings.h>
#include <android-base/unique_fd.h> #include <android-base/unique_fd.h>

View file

@ -28,6 +28,7 @@
#include <vector> #include <vector>
#include <android-base/file.h> #include <android-base/file.h>
#include <android-base/logging.h>
#include <android-base/parseint.h> #include <android-base/parseint.h>
#include <android-base/stringprintf.h> #include <android-base/stringprintf.h>
#include <android-base/strings.h> #include <android-base/strings.h>

View file

@ -20,6 +20,7 @@
#include <string> #include <string>
#include <vector> #include <vector>
#include <android-base/logging.h>
#include <android-base/strings.h> #include <android-base/strings.h>
#include <fstab/fstab.h> #include <fstab/fstab.h>
#include <libavb/libavb.h> #include <libavb/libavb.h>

View file

@ -16,10 +16,11 @@
#include <endian.h> #include <endian.h>
#include <random>
#include <android-base/strings.h>
#include <android-base/unique_fd.h> #include <android-base/unique_fd.h>
#include <base/files/file_util.h> #include <base/files/file_util.h>
#include <base/rand_util.h>
#include <base/strings/string_util.h>
#include <libavb/libavb.h> #include <libavb/libavb.h>
#include "avb_util.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(); std::string image_file_name = image_path.RemoveExtension().BaseName().value();
bool is_vbmeta_partition = 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)); android::base::unique_fd fd(open(image_path.value().c_str(), O_RDWR | O_CLOEXEC));
EXPECT_TRUE(fd > 0); 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(); std::string image_file_name = avb_image_path.RemoveExtension().BaseName().value();
base::FilePath extracted_vbmeta_path; 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. extracted_vbmeta_path = avb_image_path; // no need to extract if it's a vbmeta image.
} else { } else {
extracted_vbmeta_path = ExtractVBMetaImage(avb_image_path, image_file_name + "-vbmeta.img"); 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. // Introduces a new modification.
if (length > 0) { 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; file_content[modify_location] ^= 0x80;
last_file_path = file_path.value(); last_file_path = file_path.value();
last_modified_location = modify_location; last_modified_location = modify_location;

View file

@ -20,7 +20,6 @@
#include <android-base/file.h> #include <android-base/file.h>
#include <base/files/file_util.h> #include <base/files/file_util.h>
#include <base/strings/string_util.h>
namespace fs_avb_host_test { namespace fs_avb_host_test {

View file

@ -18,8 +18,8 @@
#include <stdlib.h> #include <stdlib.h>
#include <android-base/file.h> #include <android-base/file.h>
#include <android-base/strings.h>
#include <base/files/file_util.h> #include <base/files/file_util.h>
#include <base/strings/string_util.h>
#include <fs_avb/fs_avb.h> #include <fs_avb/fs_avb.h>
#include <libavb/libavb.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. // Only support modifying the flags in vbmeta*.img.
std::string image_file_name = vbmeta_image_path.RemoveExtension().BaseName().value(); 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)); android::base::unique_fd fd(open(vbmeta_image_path.value().c_str(), O_RDWR | O_CLOEXEC));
EXPECT_TRUE(fd > 0); EXPECT_TRUE(fd > 0);

View file

@ -19,8 +19,9 @@
#include <stdlib.h> #include <stdlib.h>
#include <android-base/file.h> #include <android-base/file.h>
#include <android-base/strings.h>
#include <android-base/stringprintf.h>
#include <base/files/file_util.h> #include <base/files/file_util.h>
#include <base/strings/string_util.h>
namespace fs_avb_host_test { namespace fs_avb_host_test {
@ -64,9 +65,7 @@ std::string BaseFsAvbTest::CalcVBMetaDigest(const std::string& file_name,
std::string vbmeta_digest_data; std::string vbmeta_digest_data;
EXPECT_TRUE(base::ReadFileToString(vbmeta_digest_path, &vbmeta_digest_data)); EXPECT_TRUE(base::ReadFileToString(vbmeta_digest_path, &vbmeta_digest_data));
// Returns the trimmed digest. // Returns the trimmed digest.
std::string trimmed_digest_data; return android::base::Trim(vbmeta_digest_data);
base::TrimString(vbmeta_digest_data, " \t\n", &trimmed_digest_data);
return trimmed_digest_data;
} }
base::FilePath BaseFsAvbTest::GenerateVBMetaImage( base::FilePath BaseFsAvbTest::GenerateVBMetaImage(
@ -91,7 +90,7 @@ base::FilePath BaseFsAvbTest::GenerateVBMetaImage(
// --chain_partitions // --chain_partitions
std::string chain_partition_options; std::string chain_partition_options;
for (const auto& partition : chain_partitions) { 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(), " --chain_partition %s:%u:%s", partition.partition_name.c_str(),
partition.rollback_index_location, partition.key_blob_path.value().c_str()); partition.rollback_index_location, partition.key_blob_path.value().c_str());
} }

View file

@ -26,9 +26,9 @@
#include <string> #include <string>
#include <vector> #include <vector>
#include <android-base/stringprintf.h>
#include <android-base/unique_fd.h> #include <android-base/unique_fd.h>
#include <base/files/file_path.h> #include <base/files/file_path.h>
#include <base/strings/stringprintf.h>
#include <fs_avb/types.h> #include <fs_avb/types.h>
#include <gtest/gtest.h> #include <gtest/gtest.h>
@ -37,7 +37,7 @@
// the command exits normally with exit status |expected_exit_status|. // the command exits normally with exit status |expected_exit_status|.
#define EXPECT_COMMAND(expected_exit_status, command_format, ...) \ #define EXPECT_COMMAND(expected_exit_status, command_format, ...) \
do { \ 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_TRUE(WIFEXITED(rc)); \
EXPECT_EQ(WEXITSTATUS(rc), expected_exit_status); \ EXPECT_EQ(WEXITSTATUS(rc), expected_exit_status); \
} while (0); } while (0);

View file

@ -22,6 +22,7 @@
#include <thread> #include <thread>
#include <android-base/logging.h>
#include <android-base/stringprintf.h> #include <android-base/stringprintf.h>
#include <android-base/unique_fd.h> #include <android-base/unique_fd.h>
#include <linux/fs.h> #include <linux/fs.h>

View file

@ -20,12 +20,6 @@
#include <string> #include <string>
#include <vector> #include <vector>
#ifdef HOST_TEST
#include <base/logging.h>
#else
#include <android-base/logging.h>
#endif
#include <android-base/result.h> #include <android-base/result.h>
using android::base::ErrnoError; using android::base::ErrnoError;

View file

@ -39,6 +39,10 @@
#include "fstab_priv.h" #include "fstab_priv.h"
#include "logging_macros.h" #include "logging_macros.h"
#if !defined(MS_LAZYTIME)
#define MS_LAZYTIME (1 << 25)
#endif
using android::base::EndsWith; using android::base::EndsWith;
using android::base::ParseByteCount; using android::base::ParseByteCount;
using android::base::ParseInt; using android::base::ParseInt;
@ -74,6 +78,7 @@ FlagList kMountFlagsList[] = {
{"private", MS_PRIVATE}, {"private", MS_PRIVATE},
{"slave", MS_SLAVE}, {"slave", MS_SLAVE},
{"shared", MS_SHARED}, {"shared", MS_SHARED},
{"lazytime", MS_LAZYTIME},
{"defaults", 0}, {"defaults", 0},
}; };
@ -521,6 +526,24 @@ std::vector<FstabPtrEntryType*> GetEntriesByPred(FstabPtr fstab, const Pred& pre
} // namespace } // 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 // 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>, // 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 // 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. // the system/etc directory is supported too and is the preferred location.
std::string GetFstabPath() { std::string GetFstabPath() {
if (InRecovery()) { if (InRecovery()) {
return "/etc/recovery.fstab"; return GetRecoveryFstabPath();
} }
for (const char* prop : {"fstab_suffix", "hardware", "hardware.platform"}) { for (const char* prop : {"fstab_suffix", "hardware", "hardware.platform"}) {
std::string suffix; std::string suffix;

View file

@ -39,9 +39,6 @@ std::string GetFstabPath();
void ImportBootconfigFromString(const std::string& bootconfig, void ImportBootconfigFromString(const std::string& bootconfig,
const std::function<void(std::string, std::string)>& fn); 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, void ImportKernelCmdlineFromString(const std::string& cmdline,
const std::function<void(std::string, std::string)>& fn); const std::function<void(std::string, std::string)>& fn);

View file

@ -154,5 +154,8 @@ bool GetKernelCmdline(const std::string& key, std::string* out);
// Return the "other" slot for the given slot suffix. // Return the "other" slot for the given slot suffix.
std::string OtherSlotSuffix(const std::string& 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 fs_mgr
} // namespace android } // namespace android

View file

@ -91,6 +91,7 @@ filegroup {
"partition_cow_creator.cpp", "partition_cow_creator.cpp",
"return.cpp", "return.cpp",
"utility.cpp", "utility.cpp",
"scratch_super.cpp",
], ],
} }
@ -110,6 +111,9 @@ cc_library_static {
static_libs: [ static_libs: [
"libfs_mgr_binder", "libfs_mgr_binder",
], ],
whole_static_libs: [
"libselinux",
],
} }
cc_library { cc_library {
@ -128,6 +132,9 @@ cc_library {
static_libs: [ static_libs: [
"libsnapshot_cow", "libsnapshot_cow",
], ],
whole_static_libs: [
"libselinux",
],
} }
cc_library_static { cc_library_static {
@ -142,6 +149,7 @@ cc_library_static {
], ],
static_libs: [ static_libs: [
"libfs_mgr", "libfs_mgr",
"libselinux",
], ],
} }
@ -159,6 +167,9 @@ cc_library_static {
static_libs: [ static_libs: [
"libfs_mgr", "libfs_mgr",
], ],
whole_static_libs: [
"libselinux",
],
} }
cc_defaults { cc_defaults {
@ -241,6 +252,7 @@ cc_library_static {
"libfs_mgr", "libfs_mgr",
"libgmock", "libgmock",
"libgtest", "libgtest",
"libselinux",
], ],
} }
@ -283,7 +295,6 @@ cc_defaults {
], ],
auto_gen_config: true, auto_gen_config: true,
require_root: true, require_root: true,
compile_multilib: "first",
} }
cc_test { cc_test {
@ -294,8 +305,17 @@ cc_test {
], ],
test_suites: [ test_suites: [
"vts", "vts",
"device-tests", "general-tests",
], ],
compile_multilib: "both",
multilib: {
lib32: {
suffix: "32",
},
lib64: {
suffix: "64",
},
},
test_options: { test_options: {
min_shipping_api_level: 30, min_shipping_api_level: 30,
}, },
@ -311,11 +331,18 @@ cc_test {
"-DLIBSNAPSHOT_TEST_VAB_LEGACY", "-DLIBSNAPSHOT_TEST_VAB_LEGACY",
], ],
test_suites: [ test_suites: [
"device-tests", "general-tests",
], ],
compile_multilib: "64",
test_options: { test_options: {
// Legacy VAB launched in Android R. // Legacy VAB launched in Android R.
min_shipping_api_level: 30, min_shipping_api_level: 30,
test_runner_options: [
{
name: "force-no-test-error",
value: "false",
},
],
}, },
} }
@ -345,6 +372,7 @@ cc_binary {
], ],
srcs: [ srcs: [
"snapshotctl.cpp", "snapshotctl.cpp",
"scratch_super.cpp",
], ],
static_libs: [ static_libs: [
"libbrotli", "libbrotli",
@ -414,7 +442,7 @@ cc_test {
"libstorage_literals_headers", "libstorage_literals_headers",
], ],
test_suites: [ test_suites: [
"device-tests", "general-tests",
], ],
test_options: { test_options: {
min_shipping_api_level: 30, min_shipping_api_level: 30,

View file

@ -229,6 +229,10 @@ message SnapshotUpdateStatus {
// Enable direct reads from source device // Enable direct reads from source device
bool o_direct = 12; bool o_direct = 12;
// Number of cow operations to be merged at once
uint32 cow_op_merge_size = 13;
} }
// Next: 10 // Next: 10

View file

@ -13,6 +13,7 @@
// limitations under the License. // limitations under the License.
#include "device_info.h" #include "device_info.h"
#include "scratch_super.h"
#include <android-base/logging.h> #include <android-base/logging.h>
#include <fs_mgr.h> #include <fs_mgr.h>
@ -37,8 +38,24 @@ constexpr bool kIsRecovery = true;
constexpr bool kIsRecovery = false; constexpr bool kIsRecovery = false;
#endif #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 { 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 { std::string DeviceInfo::GetSlotSuffix() const {
@ -104,6 +121,24 @@ bool DeviceInfo::IsFirstStageInit() const {
return first_stage_init_; 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) { bool DeviceInfo::SetSlotAsUnbootable([[maybe_unused]] unsigned int slot) {
#ifdef LIBSNAPSHOT_USE_HAL #ifdef LIBSNAPSHOT_USE_HAL
if (!EnsureBootHal()) { if (!EnsureBootHal()) {

View file

@ -29,6 +29,7 @@ class DeviceInfo final : public SnapshotManager::IDeviceInfo {
using MergeStatus = ::aidl::android::hardware::boot::MergeStatus; using MergeStatus = ::aidl::android::hardware::boot::MergeStatus;
public: public:
DeviceInfo();
std::string GetMetadataDir() const override; std::string GetMetadataDir() const override;
std::string GetSlotSuffix() const override; std::string GetSlotSuffix() const override;
std::string GetOtherSlotSuffix() 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; std::string GetSuperDevice(uint32_t slot) const override;
bool IsOverlayfsSetup() const override; bool IsOverlayfsSetup() const override;
bool SetBootControlMergeStatus(MergeStatus status) override; bool SetBootControlMergeStatus(MergeStatus status) override;
bool SetActiveBootSlot(unsigned int slot) override;
bool SetSlotAsUnbootable(unsigned int slot) override; bool SetSlotAsUnbootable(unsigned int slot) override;
bool IsRecovery() const override; bool IsRecovery() const override;
std::unique_ptr<IImageManager> OpenImageManager() const override; std::unique_ptr<IImageManager> OpenImageManager() const override;
bool IsFirstStageInit() const override; bool IsFirstStageInit() const override;
android::dm::IDeviceMapper& GetDeviceMapper() override; android::dm::IDeviceMapper& GetDeviceMapper() override;
void SetMetadataDir(const std::string& value);
void set_first_stage_init(bool value) { first_stage_init_ = 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: private:
bool EnsureBootHal(); bool EnsureBootHal();
android::fs_mgr::PartitionOpener opener_; android::fs_mgr::PartitionOpener opener_;
bool first_stage_init_ = false; bool first_stage_init_ = false;
// Default value
std::string metadata_dir_ = "/metadata/ota";
bool temp_metadata_ = false;
#ifdef LIBSNAPSHOT_USE_HAL #ifdef LIBSNAPSHOT_USE_HAL
std::unique_ptr<::android::hal::BootControlClient> boot_control_; std::unique_ptr<::android::hal::BootControlClient> boot_control_;
#endif #endif

View file

@ -29,6 +29,7 @@ class MockDeviceInfo : public SnapshotManager::IDeviceInfo {
MOCK_METHOD(const android::fs_mgr::IPartitionOpener&, GetPartitionOpener, (), (const)); MOCK_METHOD(const android::fs_mgr::IPartitionOpener&, GetPartitionOpener, (), (const));
MOCK_METHOD(bool, IsOverlayfsSetup, (), (const, override)); MOCK_METHOD(bool, IsOverlayfsSetup, (), (const, override));
MOCK_METHOD(bool, SetBootControlMergeStatus, (MergeStatus status), (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, SetSlotAsUnbootable, (unsigned int slot), (override));
MOCK_METHOD(bool, IsRecovery, (), (const, override)); MOCK_METHOD(bool, IsRecovery, (), (const, override));
MOCK_METHOD(bool, IsFirstStageInit, (), (const, override)); MOCK_METHOD(bool, IsFirstStageInit, (), (const, override));

View file

@ -104,12 +104,14 @@ class ISnapshotManager {
virtual const android::fs_mgr::IPartitionOpener& GetPartitionOpener() const = 0; virtual const android::fs_mgr::IPartitionOpener& GetPartitionOpener() const = 0;
virtual bool IsOverlayfsSetup() const = 0; virtual bool IsOverlayfsSetup() const = 0;
virtual bool SetBootControlMergeStatus(MergeStatus status) = 0; virtual bool SetBootControlMergeStatus(MergeStatus status) = 0;
virtual bool SetActiveBootSlot(unsigned int slot) = 0;
virtual bool SetSlotAsUnbootable(unsigned int slot) = 0; virtual bool SetSlotAsUnbootable(unsigned int slot) = 0;
virtual bool IsRecovery() const = 0; virtual bool IsRecovery() const = 0;
virtual bool IsTestDevice() const { return false; } virtual bool IsTestDevice() const { return false; }
virtual bool IsFirstStageInit() const = 0; virtual bool IsFirstStageInit() const = 0;
virtual std::unique_ptr<IImageManager> OpenImageManager() const = 0; virtual std::unique_ptr<IImageManager> OpenImageManager() const = 0;
virtual android::dm::IDeviceMapper& GetDeviceMapper() = 0; virtual android::dm::IDeviceMapper& GetDeviceMapper() = 0;
virtual bool IsTempMetadata() const = 0;
// Helper method for implementing OpenImageManager. // Helper method for implementing OpenImageManager.
std::unique_ptr<IImageManager> OpenImageManager(const std::string& gsid_dir) const; 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. // might be needed to perform first-stage mounts.
static bool IsSnapshotManagerNeeded(); 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. // Helper function for second stage init to restorecon on the rollback indicator.
static std::string GetGlobalRollbackIndicatorPath(); static std::string GetGlobalRollbackIndicatorPath();
@ -675,6 +681,8 @@ class SnapshotManager final : public ISnapshotManager {
std::string GetBootSnapshotsWithoutSlotSwitchPath(); std::string GetBootSnapshotsWithoutSlotSwitchPath();
std::string GetSnapuserdFromSystemPath(); std::string GetSnapuserdFromSystemPath();
bool HasForwardMergeIndicator();
const LpMetadata* ReadOldPartitionMetadata(LockedFile* lock); const LpMetadata* ReadOldPartitionMetadata(LockedFile* lock);
bool MapAllPartitions(LockedFile* lock, const std::string& super_device, uint32_t slot, 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); bool UpdateForwardMergeIndicator(bool wipe);
// Helper for HandleImminentDataWipe. // Helper for HandleImminentDataWipe.
// Call ProcessUpdateState and handle states with special rules before data wipe. Specifically, // Call ProcessUpdateState and handle states with special rules before data wipe.
// if |allow_forward_merge| and allow-forward-merge indicator exists, initiate merge if UpdateState ProcessUpdateStateOnDataWipe(const std::function<bool()>& callback);
// necessary.
UpdateState ProcessUpdateStateOnDataWipe(bool allow_forward_merge,
const std::function<bool()>& callback);
// Return device string of a mapped image, or if it is not available, the mapped image path. // Return device string of a mapped image, or if it is not available, the mapped image path.
bool GetMappedImageDeviceStringOrPath(const std::string& device_name, 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 // Check if direct reads are enabled for the source image
bool UpdateUsesODirect(LockedFile* lock); bool UpdateUsesODirect(LockedFile* lock);
// Get value of maximum cow op merge size
uint32_t GetUpdateCowOpMergeSize(LockedFile* lock);
// Wrapper around libdm, with diagnostics. // Wrapper around libdm, with diagnostics.
bool DeleteDeviceIfExists(const std::string& name, bool DeleteDeviceIfExists(const std::string& name,
const std::chrono::milliseconds& timeout_ms = {}); const std::chrono::milliseconds& timeout_ms = {});
@ -846,7 +853,6 @@ class SnapshotManager final : public ISnapshotManager {
std::string metadata_dir_; std::string metadata_dir_;
std::unique_ptr<IImageManager> images_; std::unique_ptr<IImageManager> images_;
bool use_first_stage_snapuserd_ = false; bool use_first_stage_snapuserd_ = false;
bool in_factory_data_reset_ = false;
std::function<bool(const std::string&)> uevent_regen_callback_; std::function<bool(const std::string&)> uevent_regen_callback_;
std::unique_ptr<SnapuserdClient> snapuserd_client_; std::unique_ptr<SnapuserdClient> snapuserd_client_;
std::unique_ptr<LpMetadata> old_partition_metadata_; std::unique_ptr<LpMetadata> old_partition_metadata_;

View file

@ -30,6 +30,8 @@
#include <storage_literals/storage_literals.h> #include <storage_literals/storage_literals.h>
#include <update_engine/update_metadata.pb.h> #include <update_engine/update_metadata.pb.h>
#include "utility.h"
namespace android { namespace android {
namespace snapshot { namespace snapshot {
@ -77,7 +79,7 @@ class TestDeviceInfo : public SnapshotManager::IDeviceInfo {
: TestDeviceInfo(fake_super) { : TestDeviceInfo(fake_super) {
set_slot_suffix(slot_suffix); 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 GetSlotSuffix() const override { return slot_suffix_; }
std::string GetOtherSlotSuffix() const override { return slot_suffix_ == "_a" ? "_b" : "_a"; } std::string GetOtherSlotSuffix() const override { return slot_suffix_ == "_a" ? "_b" : "_a"; }
std::string GetSuperDevice([[maybe_unused]] uint32_t slot) const override { return "super"; } 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 IsOverlayfsSetup() const override { return false; }
bool IsRecovery() const override { return recovery_; } bool IsRecovery() const override { return recovery_; }
bool SetActiveBootSlot([[maybe_unused]] unsigned int slot) override { return true; }
bool SetSlotAsUnbootable(unsigned int slot) override { bool SetSlotAsUnbootable(unsigned int slot) override {
unbootable_slots_.insert(slot); unbootable_slots_.insert(slot);
return true; return true;
@ -117,6 +120,7 @@ class TestDeviceInfo : public SnapshotManager::IDeviceInfo {
void set_dm(android::dm::IDeviceMapper* dm) { dm_ = dm; } void set_dm(android::dm::IDeviceMapper* dm) { dm_ = dm; }
MergeStatus merge_status() const { return merge_status_; } MergeStatus merge_status() const { return merge_status_; }
bool IsTempMetadata() const override { return temp_metadata_; }
private: private:
std::string slot_suffix_ = "_a"; std::string slot_suffix_ = "_a";
@ -126,6 +130,8 @@ class TestDeviceInfo : public SnapshotManager::IDeviceInfo {
bool first_stage_init_ = false; bool first_stage_init_ = false;
std::unordered_set<uint32_t> unbootable_slots_; std::unordered_set<uint32_t> unbootable_slots_;
android::dm::IDeviceMapper* dm_ = nullptr; android::dm::IDeviceMapper* dm_ = nullptr;
std::string metadata_dir_ = "/metadata/ota/test";
bool temp_metadata_ = false;
}; };
class DeviceMapperWrapper : public android::dm::IDeviceMapper { 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 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 snapshot
} // namespace android } // namespace android

View file

@ -82,7 +82,7 @@ class CowWriterV3 : public CowWriterBase {
CowOperationType type); CowOperationType type);
size_t GetCompressionFactor(const size_t blocks_to_compress, CowOperationType type) const; 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 // These are the only block size supported. Block size beyond 256k
// may impact random read performance post OTA boot. // 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}; const size_t values[] = {4_KiB, 8_KiB, 16_KiB, 32_KiB, 64_KiB, 128_KiB, 256_KiB};

View file

@ -250,8 +250,8 @@ TEST_F(PartitionCowCreatorTest, CompressionEnabled) {
.target_partition = system_b, .target_partition = system_b,
.current_metadata = builder_a.get(), .current_metadata = builder_a.get(),
.current_suffix = "_a", .current_suffix = "_a",
.using_snapuserd = true, .update = &update,
.update = &update}; .using_snapuserd = true};
auto ret = creator.Run(); auto ret = creator.Run();
ASSERT_TRUE(ret.has_value()); ASSERT_TRUE(ret.has_value());
@ -276,8 +276,8 @@ TEST_F(PartitionCowCreatorTest, CompressionWithNoManifest) {
.target_partition = system_b, .target_partition = system_b,
.current_metadata = builder_a.get(), .current_metadata = builder_a.get(),
.current_suffix = "_a", .current_suffix = "_a",
.using_snapuserd = true, .update = nullptr,
.update = nullptr}; .using_snapuserd = true};
auto ret = creator.Run(); auto ret = creator.Run();
ASSERT_FALSE(ret.has_value()); ASSERT_FALSE(ret.has_value());

View 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

View 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

View file

@ -22,6 +22,7 @@
#include <sys/unistd.h> #include <sys/unistd.h>
#include <sys/xattr.h> #include <sys/xattr.h>
#include <chrono>
#include <filesystem> #include <filesystem>
#include <optional> #include <optional>
#include <thread> #include <thread>
@ -47,6 +48,7 @@
#include <libsnapshot/snapshot_stats.h> #include <libsnapshot/snapshot_stats.h>
#include "device_info.h" #include "device_info.h"
#include "partition_cow_creator.h" #include "partition_cow_creator.h"
#include "scratch_super.h"
#include "snapshot_metadata_updater.h" #include "snapshot_metadata_updater.h"
#include "utility.h" #include "utility.h"
@ -116,7 +118,11 @@ std::unique_ptr<SnapshotManager> SnapshotManager::New(IDeviceInfo* info) {
info = new DeviceInfo(); 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) { 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) { if (result.state == UpdateState::MergeFailed) {
AcknowledgeMergeFailure(result.failure_code); AcknowledgeMergeFailure(result.failure_code);
} }
if (result.state == UpdateState::MergeCompleted) {
if (device_->IsTempMetadata()) {
CleanupScratchOtaMetadataIfPresent();
}
}
if (result.state != UpdateState::Merging) { if (result.state != UpdateState::Merging) {
// Either there is no merge, or the merge was finished, so no need // Either there is no merge, or the merge was finished, so no need
// to keep waiting. // to keep waiting.
@ -1708,6 +1721,10 @@ bool SnapshotManager::PerformInitTransition(InitTransition transition,
if (UpdateUsesODirect(lock.get())) { if (UpdateUsesODirect(lock.get())) {
snapuserd_argv->emplace_back("-o_direct"); 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; size_t num_cows = 0;
@ -2130,6 +2147,11 @@ bool SnapshotManager::UpdateUsesODirect(LockedFile* lock) {
return update_status.o_direct(); 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() { bool SnapshotManager::MarkSnapuserdFromSystem() {
auto path = GetSnapuserdFromSystemPath(); auto path = GetSnapuserdFromSystemPath();
@ -2300,7 +2322,27 @@ bool SnapshotManager::ListSnapshots(LockedFile* lock, std::vector<std::string>*
} }
bool SnapshotManager::IsSnapshotManagerNeeded() { 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() { std::string SnapshotManager::GetGlobalRollbackIndicatorPath() {
@ -2387,6 +2429,12 @@ bool SnapshotManager::MapAllPartitions(LockedFile* lock, const std::string& supe
continue; continue;
} }
if (GetPartitionName(partition) ==
android::base::Basename(android::snapshot::kOtaMetadataMount)) {
LOG(INFO) << "Partition: " << GetPartitionName(partition) << " skipping";
continue;
}
CreateLogicalPartitionParams params = { CreateLogicalPartitionParams params = {
.block_device = super_device, .block_device = super_device,
.metadata = metadata.get(), .metadata = metadata.get(),
@ -2892,10 +2940,12 @@ bool SnapshotManager::UnmapAllSnapshots() {
} }
bool SnapshotManager::UnmapAllSnapshots(LockedFile* lock) { bool SnapshotManager::UnmapAllSnapshots(LockedFile* lock) {
LOG(INFO) << "Lock acquired for " << __FUNCTION__;
std::vector<std::string> snapshots; std::vector<std::string> snapshots;
if (!ListSnapshots(lock, &snapshots)) { if (!ListSnapshots(lock, &snapshots)) {
return false; return false;
} }
LOG(INFO) << "Found " << snapshots.size() << " partitions with snapshots";
for (const auto& snapshot : snapshots) { for (const auto& snapshot : snapshots) {
if (!UnmapPartitionWithSnapshot(lock, snapshot)) { if (!UnmapPartitionWithSnapshot(lock, snapshot)) {
@ -2903,6 +2953,7 @@ bool SnapshotManager::UnmapAllSnapshots(LockedFile* lock) {
return false; return false;
} }
} }
LOG(INFO) << "Unmapped " << snapshots.size() << " partitions with snapshots";
// Terminate the daemon and release the snapuserd_client_ object. // Terminate the daemon and release the snapuserd_client_ object.
// If we need to re-connect with the daemon, EnsureSnapuserdConnected() // 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, auto SnapshotManager::OpenFile(const std::string& file,
int lock_flags) -> std::unique_ptr<LockedFile> { 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)); unique_fd fd(open(file.c_str(), O_RDONLY | O_CLOEXEC | O_NOFOLLOW));
if (fd < 0) { if (fd < 0) {
PLOG(ERROR) << "Open failed: " << file; 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 // For simplicity, we want to CHECK that lock_mode == LOCK_EX, in some
// calls, so strip extra flags. // calls, so strip extra flags.
int lock_mode = lock_flags & (LOCK_EX | LOCK_SH); 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); 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_io_uring_enabled(old_status.io_uring_enabled());
status.set_legacy_snapuserd(old_status.legacy_snapuserd()); status.set_legacy_snapuserd(old_status.legacy_snapuserd());
status.set_o_direct(old_status.o_direct()); status.set_o_direct(old_status.o_direct());
status.set_cow_op_merge_size(old_status.cow_op_merge_size());
} }
return WriteSnapshotUpdateStatus(lock, status); return WriteSnapshotUpdateStatus(lock, status);
} }
@ -3464,6 +3522,8 @@ Return SnapshotManager::CreateUpdateSnapshots(const DeltaArchiveManifest& manife
status.set_legacy_snapuserd(true); status.set_legacy_snapuserd(true);
LOG(INFO) << "Setting legacy_snapuserd to 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) { } else if (legacy_compression) {
LOG(INFO) << "Virtual A/B using legacy snapuserd"; LOG(INFO) << "Virtual A/B using legacy snapuserd";
} else { } else {
@ -3899,6 +3959,7 @@ bool SnapshotManager::Dump(std::ostream& os) {
ss << "Using userspace snapshots: " << update_status.userspace_snapshots() << std::endl; ss << "Using userspace snapshots: " << update_status.userspace_snapshots() << std::endl;
ss << "Using io_uring: " << update_status.io_uring_enabled() << std::endl; ss << "Using io_uring: " << update_status.io_uring_enabled() << std::endl;
ss << "Using o_direct: " << update_status.o_direct() << 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 << "Using XOR compression: " << GetXorCompressionEnabledProperty() << std::endl;
ss << "Current slot: " << device_->GetSlotSuffix() << std::endl; ss << "Current slot: " << device_->GetSlotSuffix() << std::endl;
ss << "Boot indicator: booting from " << GetCurrentSlot() << " slot" << 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, // 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 // it is unlikely the device would have booted anyway. If there is no
// metadata partition, then the device predates Virtual A/B. // metadata partition, then the device predates Virtual A/B.
LOG(INFO) << "/metadata not found; allowing wipe.";
return true; return true;
} }
// Check this early, so we don't accidentally start trying to populate // This could happen if /metadata mounted but there is no filesystem
// the state file in recovery. Note we don't call GetUpdateState since // structure. Weird, but we have to assume there's no OTA pending, and
// we want errors in acquiring the lock to be propagated, instead of // thus we let the wipe proceed.
// returning UpdateState::None. UpdateState state;
auto state_file = GetStateFilePath(); {
if (access(state_file.c_str(), F_OK) != 0 && errno == ENOENT) { auto lock = LockExclusive();
return true; if (!lock) {
} LOG(ERROR) << "Unable to determine update state; allowing wipe.";
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();
} }
return true;
};
in_factory_data_reset_ = true; state = ReadUpdateState(lock.get());
UpdateState state = LOG(INFO) << "Update state before wipe: " << state << "; slot: " << GetCurrentSlot()
ProcessUpdateStateOnDataWipe(true /* allow_forward_merge */, process_callback); << "; suffix: " << device_->GetSlotSuffix();
in_factory_data_reset_ = false;
if (state == UpdateState::MergeFailed) {
return false;
} }
// Nothing should be depending on partitions now, so unmap them all. bool try_merge = false;
if (!UnmapAllPartitionsInRecovery()) { switch (state) {
LOG(ERROR) << "Unable to unmap all partitions; fastboot may fail to flash."; 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) { if (state != UpdateState::None) {
@ -4065,58 +4172,40 @@ bool SnapshotManager::FinishMergeInRecovery() {
return true; return true;
} }
UpdateState SnapshotManager::ProcessUpdateStateOnDataWipe(bool allow_forward_merge, UpdateState SnapshotManager::ProcessUpdateStateOnDataWipe(const std::function<bool()>& callback) {
const std::function<bool()>& callback) { while (true) {
auto slot_number = SlotNumberForSlotSuffix(device_->GetSlotSuffix()); UpdateState state = ProcessUpdateState(callback);
UpdateState state = ProcessUpdateState(callback); LOG(INFO) << "Processed updated state in recovery: " << state;
LOG(INFO) << "Update state in recovery: " << state; switch (state) {
switch (state) { case UpdateState::MergeFailed:
case UpdateState::MergeFailed: LOG(ERROR) << "Unrecoverable merge failure detected.";
LOG(ERROR) << "Unrecoverable merge failure detected."; return state;
return state; case UpdateState::Unverified: {
case UpdateState::Unverified: { // Unverified was already handled earlier, in HandleImminentDataWipe,
// If an OTA was just applied but has not yet started merging: // but it will fall through here if a forward merge is required.
// //
// - if forward merge is allowed, initiate merge and call // If InitiateMerge fails, we early return. If it succeeds, then we
// ProcessUpdateState again. // are guaranteed that the next call to ProcessUpdateState will not
// // return Unverified.
// - if forward merge is not allowed, we if (!InitiateMerge()) {
// have no choice but to revert slots, because the current slot will LOG(ERROR) << "Failed to initiate merge on data wipe.";
// immediately become unbootable. Rather than wait for the device return UpdateState::MergeFailed;
// 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);
} }
continue;
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.";
} }
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) { bool SnapshotManager::EnsureNoOverflowSnapshot(LockedFile* lock) {

View file

@ -25,7 +25,7 @@ namespace snapshot {
SnapshotMergeStats* SnapshotMergeStats::GetInstance(SnapshotManager& parent) { SnapshotMergeStats* SnapshotMergeStats::GetInstance(SnapshotManager& parent) {
static SnapshotMergeStats g_instance(parent.GetMergeStateFilePath()); static SnapshotMergeStats g_instance(parent.GetMergeStateFilePath());
CHECK(g_instance.path_ == parent.GetMergeStateFilePath()); CHECK_EQ(g_instance.path_, parent.GetMergeStateFilePath());
return &g_instance; return &g_instance;
} }

View file

@ -47,6 +47,7 @@
#include <android/snapshot/snapshot.pb.h> #include <android/snapshot/snapshot.pb.h>
#include <libsnapshot/test_helpers.h> #include <libsnapshot/test_helpers.h>
#include "partition_cow_creator.h" #include "partition_cow_creator.h"
#include "scratch_super.h"
#include "utility.h" #include "utility.h"
// Mock classes are not used. Header included to ensure mocked class definition aligns with the // 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_; LOG(INFO) << "Starting test: " << test_name_;
SKIP_IF_NON_VIRTUAL_AB(); SKIP_IF_NON_VIRTUAL_AB();
SKIP_IF_VENDOR_ON_ANDROID_S();
SetupProperties(); SetupProperties();
if (!DeviceSupportsMode()) { if (!DeviceSupportsMode()) {
@ -168,6 +170,7 @@ class SnapshotTest : public ::testing::Test {
void TearDown() override { void TearDown() override {
RETURN_IF_NON_VIRTUAL_AB(); RETURN_IF_NON_VIRTUAL_AB();
RETURN_IF_VENDOR_ON_ANDROID_S();
LOG(INFO) << "Tearing down SnapshotTest test: " << test_name_; LOG(INFO) << "Tearing down SnapshotTest test: " << test_name_;
@ -1015,6 +1018,7 @@ class SnapshotUpdateTest : public SnapshotTest {
public: public:
void SetUp() override { void SetUp() override {
SKIP_IF_NON_VIRTUAL_AB(); SKIP_IF_NON_VIRTUAL_AB();
SKIP_IF_VENDOR_ON_ANDROID_S();
SnapshotTest::SetUp(); SnapshotTest::SetUp();
if (!image_manager_) { if (!image_manager_) {
@ -1097,6 +1101,7 @@ class SnapshotUpdateTest : public SnapshotTest {
} }
void TearDown() override { void TearDown() override {
RETURN_IF_NON_VIRTUAL_AB(); RETURN_IF_NON_VIRTUAL_AB();
RETURN_IF_VENDOR_ON_ANDROID_S();
LOG(INFO) << "Tearing down SnapshotUpdateTest test: " << test_name_; LOG(INFO) << "Tearing down SnapshotUpdateTest test: " << test_name_;
@ -1338,6 +1343,15 @@ class SnapshotUpdateTest : public SnapshotTest {
DynamicPartitionGroup* group_ = nullptr; 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, // Test full update flow executed by update_engine. Some partitions uses super empty space,
// some uses images, and some uses both. // some uses images, and some uses both.
// Also test UnmapUpdateSnapshot unmaps everything. // Also test UnmapUpdateSnapshot unmaps everything.
@ -1978,6 +1992,8 @@ TEST_F(MetadataMountedTest, Android) {
} }
TEST_F(MetadataMountedTest, Recovery) { TEST_F(MetadataMountedTest, Recovery) {
GTEST_SKIP() << "b/350715463";
test_device->set_recovery(true); test_device->set_recovery(true);
metadata_dir_ = test_device->GetMetadataDir(); metadata_dir_ = test_device->GetMetadataDir();
@ -2096,10 +2112,10 @@ TEST_F(SnapshotUpdateTest, DataWipeRollbackInRecovery) {
test_device->set_recovery(true); test_device->set_recovery(true);
auto new_sm = NewManagerForFirstStageMount(test_device); auto new_sm = NewManagerForFirstStageMount(test_device);
EXPECT_EQ(new_sm->GetUpdateState(), UpdateState::Unverified);
ASSERT_TRUE(new_sm->HandleImminentDataWipe()); ASSERT_TRUE(new_sm->HandleImminentDataWipe());
// Manually mount metadata so that we can call GetUpdateState() below. // Manually mount metadata so that we can call GetUpdateState() below.
MountMetadata(); MountMetadata();
EXPECT_EQ(new_sm->GetUpdateState(), UpdateState::None);
EXPECT_TRUE(test_device->IsSlotUnbootable(1)); EXPECT_TRUE(test_device->IsSlotUnbootable(1));
EXPECT_FALSE(test_device->IsSlotUnbootable(0)); EXPECT_FALSE(test_device->IsSlotUnbootable(0));
} }
@ -2121,6 +2137,7 @@ TEST_F(SnapshotUpdateTest, DataWipeAfterRollback) {
test_device->set_recovery(true); test_device->set_recovery(true);
auto new_sm = NewManagerForFirstStageMount(test_device); auto new_sm = NewManagerForFirstStageMount(test_device);
EXPECT_EQ(new_sm->GetUpdateState(), UpdateState::Unverified);
ASSERT_TRUE(new_sm->HandleImminentDataWipe()); ASSERT_TRUE(new_sm->HandleImminentDataWipe());
EXPECT_EQ(new_sm->GetUpdateState(), UpdateState::None); EXPECT_EQ(new_sm->GetUpdateState(), UpdateState::None);
EXPECT_FALSE(test_device->IsSlotUnbootable(0)); EXPECT_FALSE(test_device->IsSlotUnbootable(0));
@ -2129,10 +2146,6 @@ TEST_F(SnapshotUpdateTest, DataWipeAfterRollback) {
// Test update package that requests data wipe. // Test update package that requests data wipe.
TEST_F(SnapshotUpdateTest, DataWipeRequiredInPackage) { TEST_F(SnapshotUpdateTest, DataWipeRequiredInPackage) {
if (ShouldSkipLegacyMerging()) {
GTEST_SKIP() << "Skipping legacy merge in test";
}
AddOperationForPartitions(); AddOperationForPartitions();
// Execute the update. // Execute the update.
ASSERT_TRUE(sm->BeginUpdate()); ASSERT_TRUE(sm->BeginUpdate());
@ -2151,6 +2164,7 @@ TEST_F(SnapshotUpdateTest, DataWipeRequiredInPackage) {
test_device->set_recovery(true); test_device->set_recovery(true);
auto new_sm = NewManagerForFirstStageMount(test_device); auto new_sm = NewManagerForFirstStageMount(test_device);
EXPECT_EQ(new_sm->GetUpdateState(), UpdateState::Unverified);
ASSERT_TRUE(new_sm->HandleImminentDataWipe()); ASSERT_TRUE(new_sm->HandleImminentDataWipe());
// Manually mount metadata so that we can call GetUpdateState() below. // Manually mount metadata so that we can call GetUpdateState() below.
MountMetadata(); MountMetadata();
@ -2172,10 +2186,6 @@ TEST_F(SnapshotUpdateTest, DataWipeRequiredInPackage) {
// Test update package that requests data wipe. // Test update package that requests data wipe.
TEST_F(SnapshotUpdateTest, DataWipeWithStaleSnapshots) { TEST_F(SnapshotUpdateTest, DataWipeWithStaleSnapshots) {
if (ShouldSkipLegacyMerging()) {
GTEST_SKIP() << "Skipping legacy merge in test";
}
AddOperationForPartitions(); AddOperationForPartitions();
// Execute the update. // Execute the update.
@ -2216,6 +2226,7 @@ TEST_F(SnapshotUpdateTest, DataWipeWithStaleSnapshots) {
test_device->set_recovery(true); test_device->set_recovery(true);
auto new_sm = NewManagerForFirstStageMount(test_device); auto new_sm = NewManagerForFirstStageMount(test_device);
EXPECT_EQ(new_sm->GetUpdateState(), UpdateState::Unverified);
ASSERT_TRUE(new_sm->HandleImminentDataWipe()); ASSERT_TRUE(new_sm->HandleImminentDataWipe());
// Manually mount metadata so that we can call GetUpdateState() below. // Manually mount metadata so that we can call GetUpdateState() below.
MountMetadata(); MountMetadata();
@ -2659,6 +2670,7 @@ TEST_F(SnapshotTest, FlagCheck) {
status.set_o_direct(true); status.set_o_direct(true);
status.set_io_uring_enabled(true); status.set_io_uring_enabled(true);
status.set_userspace_snapshots(true); status.set_userspace_snapshots(true);
status.set_cow_op_merge_size(16);
sm->WriteSnapshotUpdateStatus(lock_.get(), status); sm->WriteSnapshotUpdateStatus(lock_.get(), status);
// Ensure a connection to the second-stage daemon, but use the first-stage // Ensure a connection to the second-stage daemon, but use the first-stage
@ -2680,6 +2692,8 @@ TEST_F(SnapshotTest, FlagCheck) {
snapuserd_argv.end()); snapuserd_argv.end());
ASSERT_TRUE(std::find(snapuserd_argv.begin(), snapuserd_argv.end(), "-user_snapshot") != ASSERT_TRUE(std::find(snapuserd_argv.begin(), snapuserd_argv.end(), "-user_snapshot") !=
snapuserd_argv.end()); 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, class FlashAfterUpdateTest : public SnapshotUpdateTest,
@ -2885,6 +2899,8 @@ void SnapshotTestEnvironment::SetUp() {
void SnapshotTestEnvironment::TearDown() { void SnapshotTestEnvironment::TearDown() {
RETURN_IF_NON_VIRTUAL_AB(); RETURN_IF_NON_VIRTUAL_AB();
RETURN_IF_VENDOR_ON_ANDROID_S();
if (super_images_ != nullptr) { if (super_images_ != nullptr) {
DeleteBackingImage(super_images_.get(), "fake-super"); DeleteBackingImage(super_images_.get(), "fake-super");
} }

View file

@ -16,7 +16,6 @@
#include <sysexits.h> #include <sysexits.h>
#include <unistd.h> #include <unistd.h>
#include <chrono> #include <chrono>
#include <filesystem> #include <filesystem>
#include <fstream> #include <fstream>
@ -46,6 +45,7 @@
#include <storage_literals/storage_literals.h> #include <storage_literals/storage_literals.h>
#include "partition_cow_creator.h" #include "partition_cow_creator.h"
#include "scratch_super.h"
#ifdef SNAPSHOTCTL_USERDEBUG_OR_ENG #ifdef SNAPSHOTCTL_USERDEBUG_OR_ENG
#include <BootControlClient.h> #include <BootControlClient.h>
@ -57,6 +57,8 @@ using namespace android::storage_literals;
using android::base::LogdLogger; using android::base::LogdLogger;
using android::base::StderrLogger; using android::base::StderrLogger;
using android::base::TeeLogger; using android::base::TeeLogger;
using namespace android::dm;
using namespace android::fs_mgr;
using android::fs_mgr::CreateLogicalPartitionParams; using android::fs_mgr::CreateLogicalPartitionParams;
using android::fs_mgr::FindPartition; using android::fs_mgr::FindPartition;
using android::fs_mgr::GetPartitionSize; using android::fs_mgr::GetPartitionSize;
@ -97,7 +99,7 @@ namespace snapshot {
#ifdef SNAPSHOTCTL_USERDEBUG_OR_ENG #ifdef SNAPSHOTCTL_USERDEBUG_OR_ENG
class MapSnapshots { class MapSnapshots {
public: public:
MapSnapshots(std::string path = ""); MapSnapshots(std::string path = "", bool metadata_super = false);
bool CreateSnapshotDevice(std::string& partition_name, std::string& patch); bool CreateSnapshotDevice(std::string& partition_name, std::string& patch);
bool InitiateThreadedSnapshotWrite(std::string& pname, std::string& snapshot_patch); bool InitiateThreadedSnapshotWrite(std::string& pname, std::string& snapshot_patch);
bool FinishSnapshotWrites(); bool FinishSnapshotWrites();
@ -122,15 +124,12 @@ class MapSnapshots {
std::vector<std::string> patchfiles_; std::vector<std::string> patchfiles_;
chromeos_update_engine::DeltaArchiveManifest manifest_; chromeos_update_engine::DeltaArchiveManifest manifest_;
bool metadata_super_ = false;
}; };
MapSnapshots::MapSnapshots(std::string path) { MapSnapshots::MapSnapshots(std::string path, bool metadata_super) {
sm_ = SnapshotManager::New();
if (!sm_) {
std::cout << "Failed to create snapshotmanager";
exit(1);
}
snapshot_dir_path_ = path + "/"; snapshot_dir_path_ = path + "/";
metadata_super_ = metadata_super;
} }
std::string MapSnapshots::GetGroupName(const android::fs_mgr::LpMetadata& pt, 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() { 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 = fs_mgr_get_slot_suffix();
auto source_slot_number = SlotNumberForSlotSuffix(source_slot); auto source_slot_number = SlotNumberForSlotSuffix(source_slot);
auto super_source = fs_mgr_get_super_partition_name(source_slot_number); 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) { bool MapSnapshots::GetCowDevicePath(std::string partition_name, std::string* cow_path) {
auto& dm = android::dm::DeviceMapper::Instance(); 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)) { if (dm.GetDmDevicePathByName(cow_device, cow_path)) {
return true; return true;
} }
@ -321,6 +336,12 @@ bool MapSnapshots::ApplyUpdate() {
} }
bool MapSnapshots::BeginUpdate() { bool MapSnapshots::BeginUpdate() {
if (metadata_super_ && !CreateScratchOtaMetadataOnSuper()) {
LOG(ERROR) << "Failed to create OTA metadata on super";
return false;
}
sm_ = SnapshotManager::New();
lock_ = sm_->LockExclusive(); lock_ = sm_->LockExclusive();
std::vector<std::string> snapshots; std::vector<std::string> snapshots;
sm_->ListSnapshots(lock_.get(), &snapshots); sm_->ListSnapshots(lock_.get(), &snapshots);
@ -470,10 +491,12 @@ bool MapSnapshots::FinishSnapshotWrites() {
} }
bool MapSnapshots::UnmapCowImagePath(std::string& name) { bool MapSnapshots::UnmapCowImagePath(std::string& name) {
sm_ = SnapshotManager::New();
return sm_->UnmapCowImage(name); return sm_->UnmapCowImage(name);
} }
bool MapSnapshots::DeleteSnapshots() { bool MapSnapshots::DeleteSnapshots() {
sm_ = SnapshotManager::New();
lock_ = sm_->LockExclusive(); lock_ = sm_->LockExclusive();
if (!sm_->RemoveAllUpdateState(lock_.get())) { if (!sm_->RemoveAllUpdateState(lock_.get())) {
LOG(ERROR) << "Remove All Update State failed"; LOG(ERROR) << "Remove All Update State failed";
@ -583,13 +606,19 @@ bool ApplyUpdate(int argc, char** argv) {
} }
if (argc < 3) { 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"; " Apply the snapshots to the COW block device\n";
return false; return false;
} }
std::string path = std::string(argv[2]); 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()) { if (!cow.ApplyUpdate()) {
return false; return false;
} }
@ -607,7 +636,7 @@ bool MapPrecreatedSnapshots(int argc, char** argv) {
} }
if (argc < 3) { 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"; " Map all snapshots based on patches present in the directory\n";
return false; 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()) { if (!cow.BeginUpdate()) {
LOG(ERROR) << "BeginUpdate failed"; LOG(ERROR) << "BeginUpdate failed";
return false; return false;

View file

@ -42,6 +42,7 @@ cc_library_static {
static_libs: [ static_libs: [
"libcutils_sockets", "libcutils_sockets",
"libfs_mgr_file_wait", "libfs_mgr_file_wait",
"libdm",
], ],
shared_libs: [ shared_libs: [
"libbase", "libbase",
@ -169,7 +170,7 @@ cc_binary {
recovery_available: true, 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 // It will also create a symblink on /system/bin/snapuserd that point to
// /system/bin/snapuserd_ramdisk . // /system/bin/snapuserd_ramdisk .
// This way, init can check if generic ramdisk copy exists. // This way, init can check if generic ramdisk copy exists.
@ -238,9 +239,19 @@ cc_defaults {
test_options: { test_options: {
min_shipping_api_level: 30, min_shipping_api_level: 30,
}, },
compile_multilib: "both",
multilib: {
lib32: {
suffix: "32",
},
lib64: {
suffix: "64",
},
},
auto_gen_config: true, auto_gen_config: true,
require_root: true, require_root: true,
compile_multilib: "first",
} }
cc_test { cc_test {
@ -248,8 +259,16 @@ cc_test {
defaults: ["snapuserd_test_defaults"], defaults: ["snapuserd_test_defaults"],
host_supported: true, host_supported: true,
test_suites: [ test_suites: [
"device-tests", "general-tests",
], ],
test_options: {
test_runner_options: [
{
name: "force-no-test-error",
value: "false",
},
],
},
} }
// vts tests cannot be host_supported. // vts tests cannot be host_supported.
@ -259,6 +278,10 @@ cc_test {
test_suites: [ test_suites: [
"vts", "vts",
], ],
test_options: {
// VABC mandatory in Android T per VSR.
min_shipping_api_level: 32,
},
} }
cc_binary_host { cc_binary_host {

View file

@ -27,11 +27,12 @@ using android::base::unique_fd;
DmUserBlockServer::DmUserBlockServer(const std::string& misc_name, unique_fd&& ctrl_fd, DmUserBlockServer::DmUserBlockServer(const std::string& misc_name, unique_fd&& ctrl_fd,
Delegate* delegate, size_t buffer_size) Delegate* delegate, size_t buffer_size)
: misc_name_(misc_name), ctrl_fd_(std::move(ctrl_fd)), delegate_(delegate) { : 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() { 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 (!android::base::ReadFully(ctrl_fd_, header, sizeof(*header))) {
if (errno != ENOTBLK) { if (errno != ENOTBLK) {
SNAP_PLOG(ERROR) << "Control-read failed"; SNAP_PLOG(ERROR) << "Control-read failed";
@ -90,7 +91,8 @@ bool DmUserBlockServer::SendBufferedIo() {
} }
void DmUserBlockServer::SendError() { 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; header->type = DM_USER_RESP_ERROR;
// This is an issue with the dm-user interface. There // This is an issue with the dm-user interface. There
// is no way to propagate the I/O error back to dm-user // is no way to propagate the I/O error back to dm-user

View file

@ -20,6 +20,7 @@
#include <snapuserd/block_server.h> #include <snapuserd/block_server.h>
#include <snapuserd/snapuserd_buffer.h> #include <snapuserd/snapuserd_buffer.h>
#include <snapuserd/snapuserd_kernel.h>
namespace android { namespace android {
namespace snapshot { namespace snapshot {

View file

@ -27,13 +27,17 @@ namespace snapshot {
class BufferSink final { class BufferSink final {
public: 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* GetBufPtr() { return buffer_.get(); }
void Clear() { memset(GetBufPtr(), 0, buffer_size_); } void Clear() { memset(GetBufPtr(), 0, buffer_size_); }
void* GetPayloadBuffer(size_t size); void* GetPayloadBuffer(size_t size);
void* GetBuffer(size_t requested, size_t* actual); void* GetBuffer(size_t requested, size_t* actual);
void UpdateBufferOffset(size_t size) { buffer_offset_ += size; } void UpdateBufferOffset(size_t size) { buffer_offset_ += size; }
struct dm_user_header* GetHeaderPtr(); void* GetHeaderPtr();
void ResetBufferOffset() { buffer_offset_ = 0; } void ResetBufferOffset() { buffer_offset_ = 0; }
void* GetPayloadBufPtr(); void* GetPayloadBufPtr();
loff_t GetPayloadBytesWritten() { return buffer_offset_; } loff_t GetPayloadBytesWritten() { return buffer_offset_; }
@ -56,6 +60,7 @@ class BufferSink final {
std::unique_ptr<uint8_t[]> buffer_; std::unique_ptr<uint8_t[]> buffer_;
loff_t buffer_offset_; loff_t buffer_offset_;
size_t buffer_size_; size_t buffer_size_;
size_t header_size_;
}; };
} // namespace snapshot } // namespace snapshot

View file

@ -92,15 +92,5 @@ struct dm_user_header {
__u64 len; __u64 len;
} __attribute__((packed)); } __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 snapshot
} // namespace android } // namespace android

View file

@ -22,8 +22,9 @@
namespace android { namespace android {
namespace snapshot { namespace snapshot {
void BufferSink::Initialize(size_t size) { void BufferSink::Initialize(size_t header_size, size_t size) {
buffer_size_ = size + sizeof(struct dm_user_header); header_size_ = header_size;
buffer_size_ = size + header_size;
buffer_offset_ = 0; buffer_offset_ = 0;
buffer_ = std::make_unique<uint8_t[]>(buffer_size_); 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) { void* BufferSink::GetPayloadBuffer(size_t size) {
char* buffer = reinterpret_cast<char*>(GetBufPtr()); 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 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) { void* BufferSink::GetBuffer(size_t requested, size_t* actual) {
@ -58,19 +59,18 @@ void* BufferSink::GetBuffer(size_t requested, size_t* actual) {
return buf; return buf;
} }
struct dm_user_header* BufferSink::GetHeaderPtr() { void* BufferSink::GetHeaderPtr() {
if (!(sizeof(struct dm_user_header) <= buffer_size_)) { // If no sufficient space or header not reserved
if (!(header_size_ <= buffer_size_) || !header_size_) {
return nullptr; return nullptr;
} }
char* buf = reinterpret_cast<char*>(GetBufPtr()); char* buf = reinterpret_cast<char*>(GetBufPtr());
struct dm_user_header* header = (struct dm_user_header*)(&(buf[0])); return (void*)(&(buf[0]));
return header;
} }
void* BufferSink::GetPayloadBufPtr() { void* BufferSink::GetPayloadBufPtr() {
char* buffer = reinterpret_cast<char*>(GetBufPtr()); char* buffer = reinterpret_cast<char*>(GetBufPtr());
struct dm_user_message* msg = reinterpret_cast<struct dm_user_message*>(&(buffer[0])); return &buffer[header_size_];
return msg->payload.buf;
} }
} // namespace snapshot } // namespace snapshot

View file

@ -35,6 +35,7 @@
#include <android-base/properties.h> #include <android-base/properties.h>
#include <android-base/strings.h> #include <android-base/strings.h>
#include <fs_mgr/file_wait.h> #include <fs_mgr/file_wait.h>
#include <libdm/dm.h>
#include <snapuserd/snapuserd_client.h> #include <snapuserd/snapuserd_client.h>
namespace android { namespace android {
@ -333,7 +334,21 @@ bool SnapuserdClient::QueryUpdateVerification() {
} }
std::string SnapuserdClient::GetDaemonAliveIndicatorPath() { 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() { bool SnapuserdClient::IsTransitionedDaemonReady() {

View file

@ -30,6 +30,7 @@ DEFINE_bool(socket_handoff, false,
DEFINE_bool(user_snapshot, false, "If true, user-space snapshots are used"); 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(io_uring, false, "If true, io_uring feature is enabled");
DEFINE_bool(o_direct, false, "If true, enable direct reads on source device"); 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 android {
namespace snapshot { namespace snapshot {
@ -106,7 +107,6 @@ bool Daemon::StartServerForUserspaceSnapshots(int arg_start, int argc, char** ar
} }
return user_server_.Run(); return user_server_.Run();
} }
for (int i = arg_start; i < argc; i++) { for (int i = arg_start; i < argc; i++) {
auto parts = android::base::Split(argv[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."; LOG(ERROR) << "Malformed message, expected at least four sub-arguments.";
return false; return false;
} }
auto handler = auto handler = user_server_.AddHandler(parts[0], parts[1], parts[2], parts[3],
user_server_.AddHandler(parts[0], parts[1], parts[2], parts[3], FLAGS_o_direct); FLAGS_o_direct, FLAGS_cow_op_merge_size);
if (!handler || !user_server_.StartHandler(parts[0])) { if (!handler || !user_server_.StartHandler(parts[0])) {
return false; return false;
} }

View file

@ -41,7 +41,7 @@ Extractor::Extractor(const std::string& base_path, const std::string& cow_path)
bool Extractor::Init() { bool Extractor::Init() {
auto opener = factory_.CreateTestOpener(control_name_); auto opener = factory_.CreateTestOpener(control_name_);
handler_ = std::make_shared<SnapshotHandler>(control_name_, cow_path_, base_path_, base_path_, 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()) { if (!handler_->InitCowDevice()) {
return false; return false;
} }

View file

@ -53,10 +53,10 @@ std::shared_ptr<HandlerThread> SnapshotHandlerManager::AddHandler(
const std::string& misc_name, const std::string& cow_device_path, const std::string& misc_name, const std::string& cow_device_path,
const std::string& backing_device, const std::string& base_path_merge, const std::string& backing_device, const std::string& base_path_merge,
std::shared_ptr<IBlockServerOpener> opener, int num_worker_threads, bool use_iouring, 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>( auto snapuserd = std::make_shared<SnapshotHandler>(
misc_name, cow_device_path, backing_device, base_path_merge, opener, num_worker_threads, 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()) { if (!snapuserd->InitCowDevice()) {
LOG(ERROR) << "Failed to initialize Snapuserd"; LOG(ERROR) << "Failed to initialize Snapuserd";
return nullptr; return nullptr;

View file

@ -52,13 +52,11 @@ class ISnapshotHandlerManager {
virtual ~ISnapshotHandlerManager() {} virtual ~ISnapshotHandlerManager() {}
// Add a new snapshot handler but do not start serving requests yet. // Add a new snapshot handler but do not start serving requests yet.
virtual std::shared_ptr<HandlerThread> AddHandler(const std::string& misc_name, virtual std::shared_ptr<HandlerThread> AddHandler(
const std::string& cow_device_path, const std::string& misc_name, const std::string& cow_device_path,
const std::string& backing_device, const std::string& backing_device, const std::string& base_path_merge,
const std::string& base_path_merge, std::shared_ptr<IBlockServerOpener> opener, int num_worker_threads, bool use_iouring,
std::shared_ptr<IBlockServerOpener> opener, bool o_direct, uint32_t cow_op_merge_size) = 0;
int num_worker_threads, bool use_iouring,
bool o_direct) = 0;
// Start serving requests on a snapshot handler. // Start serving requests on a snapshot handler.
virtual bool StartHandler(const std::string& misc_name) = 0; virtual bool StartHandler(const std::string& misc_name) = 0;
@ -98,7 +96,7 @@ class SnapshotHandlerManager final : public ISnapshotHandlerManager {
const std::string& base_path_merge, const std::string& base_path_merge,
std::shared_ptr<IBlockServerOpener> opener, std::shared_ptr<IBlockServerOpener> opener,
int num_worker_threads, bool use_iouring, 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 StartHandler(const std::string& misc_name) override;
bool DeleteHandler(const std::string& misc_name) override; bool DeleteHandler(const std::string& misc_name) override;
bool InitiateMerge(const std::string& misc_name) override; bool InitiateMerge(const std::string& misc_name) override;

View file

@ -17,6 +17,8 @@
#include <libsnapshot/cow_format.h> #include <libsnapshot/cow_format.h>
#include <pthread.h> #include <pthread.h>
#include <android-base/properties.h>
#include "merge_worker.h" #include "merge_worker.h"
#include "snapuserd_core.h" #include "snapuserd_core.h"
#include "utility.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, MergeWorker::MergeWorker(const std::string& cow_device, const std::string& misc_name,
const std::string& base_path_merge, const std::string& base_path_merge,
std::shared_ptr<SnapshotHandler> snapuserd) std::shared_ptr<SnapshotHandler> snapuserd, uint32_t cow_op_merge_size)
: Worker(cow_device, misc_name, base_path_merge, snapuserd) {} : 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, int MergeWorker::PrepareMerge(uint64_t* source_offset, int* pending_ops,
std::vector<const CowOperation*>* replace_zero_vec) { std::vector<const CowOperation*>* replace_zero_vec) {
int num_ops = *pending_ops; 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; int nr_consecutive = 0;
bool checkOrderedOp = (replace_zero_vec == nullptr); bool checkOrderedOp = (replace_zero_vec == nullptr);
size_t num_blocks = 1; size_t num_blocks = 1;
@ -179,8 +187,8 @@ bool MergeWorker::MergeReplaceZeroOps() {
bufsink_.ResetBufferOffset(); bufsink_.ResetBufferOffset();
if (snapuserd_->IsIOTerminated()) { if (snapuserd_->IsIOTerminated()) {
SNAP_LOG(ERROR) SNAP_LOG(ERROR) << "MergeReplaceZeroOps: MergeWorker threads terminated - shutting "
<< "MergeReplaceZeroOps: MergeWorker threads terminated - shutting down merge"; "down merge";
return false; return false;
} }
} }
@ -577,8 +585,10 @@ bool MergeWorker::Run() {
SNAP_LOG(ERROR) << "Merge terminated early..."; SNAP_LOG(ERROR) << "Merge terminated early...";
return true; 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"; SNAP_PLOG(ERROR) << "Failed to set thread priority";
} }

View file

@ -23,7 +23,8 @@ namespace snapshot {
class MergeWorker : public Worker { class MergeWorker : public Worker {
public: public:
MergeWorker(const std::string& cow_device, const std::string& misc_name, 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(); bool Run();
private: private:
@ -53,6 +54,7 @@ class MergeWorker : public Worker {
// syscalls and fallback to synchronous I/O, we // syscalls and fallback to synchronous I/O, we
// don't want huge queue depth // don't want huge queue depth
int queue_depth_ = 8; int queue_depth_ = 8;
uint32_t cow_op_merge_size_ = 0;
}; };
} // namespace snapshot } // namespace snapshot

View file

@ -14,11 +14,14 @@
* limitations under the License. * limitations under the License.
*/ */
#include <android-base/properties.h>
#include <libsnapshot/cow_format.h> #include <libsnapshot/cow_format.h>
#include <pthread.h> #include <pthread.h>
#include "read_worker.h" #include "read_worker.h"
#include "snapuserd_core.h" #include "snapuserd_core.h"
#include "user-space-merge/worker.h"
#include "utility.h" #include "utility.h"
namespace android { namespace android {
@ -259,8 +262,10 @@ bool ReadWorker::Run() {
SNAP_LOG(INFO) << "Processing snapshot I/O requests...."; SNAP_LOG(INFO) << "Processing snapshot I/O requests....";
pthread_setname_np(pthread_self(), "ReadWorker"); 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"; SNAP_PLOG(ERROR) << "Failed to set thread priority";
} }

View file

@ -57,7 +57,7 @@ class ReadWorker : public Worker, public IBlockServer::Delegate {
bool ReadFromSourceDevice(const CowOperation* cow_op, void* buffer); bool ReadFromSourceDevice(const CowOperation* cow_op, void* buffer);
bool ReadDataFromBaseDevice(sector_t sector, void* buffer, size_t read_size); 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 sector_t ChunkToSector(chunk_t chunk) { return chunk << CHUNK_SHIFT; }
constexpr chunk_t SectorToChunk(sector_t sector) { return sector >> CHUNK_SHIFT; } constexpr chunk_t SectorToChunk(sector_t sector) { return sector >> CHUNK_SHIFT; }

View file

@ -36,7 +36,8 @@ using android::base::unique_fd;
SnapshotHandler::SnapshotHandler(std::string misc_name, std::string cow_device, SnapshotHandler::SnapshotHandler(std::string misc_name, std::string cow_device,
std::string backing_device, std::string base_path_merge, std::string backing_device, std::string base_path_merge,
std::shared_ptr<IBlockServerOpener> opener, int num_worker_threads, 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); misc_name_ = std::move(misc_name);
cow_device_ = std::move(cow_device); cow_device_ = std::move(cow_device);
backing_store_device_ = std::move(backing_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; is_io_uring_enabled_ = use_iouring;
perform_verification_ = perform_verification; perform_verification_ = perform_verification;
o_direct_ = o_direct; o_direct_ = o_direct;
cow_op_merge_size_ = cow_op_merge_size;
} }
bool SnapshotHandler::InitializeWorkers() { bool SnapshotHandler::InitializeWorkers() {
@ -60,12 +62,11 @@ bool SnapshotHandler::InitializeWorkers() {
worker_threads_.push_back(std::move(wt)); worker_threads_.push_back(std::move(wt));
} }
merge_thread_ = std::make_unique<MergeWorker>(cow_device_, misc_name_, base_path_merge_, 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_, 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_); update_verify_ = std::make_unique<UpdateVerify>(misc_name_);

View file

@ -104,7 +104,8 @@ class SnapshotHandler : public std::enable_shared_from_this<SnapshotHandler> {
public: public:
SnapshotHandler(std::string misc_name, std::string cow_device, std::string backing_device, SnapshotHandler(std::string misc_name, std::string cow_device, std::string backing_device,
std::string base_path_merge, std::shared_ptr<IBlockServerOpener> opener, 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 InitCowDevice();
bool Start(); bool Start();
@ -247,7 +248,7 @@ class SnapshotHandler : public std::enable_shared_from_this<SnapshotHandler> {
bool resume_merge_ = false; bool resume_merge_ = false;
bool merge_complete_ = false; bool merge_complete_ = false;
bool o_direct_ = false; bool o_direct_ = false;
uint32_t cow_op_merge_size_ = 0;
std::unique_ptr<UpdateVerify> update_verify_; std::unique_ptr<UpdateVerify> update_verify_;
std::shared_ptr<IBlockServerOpener> block_server_opener_; std::shared_ptr<IBlockServerOpener> block_server_opener_;
}; };

View file

@ -18,6 +18,7 @@
#include <pthread.h> #include <pthread.h>
#include "android-base/properties.h"
#include "snapuserd_core.h" #include "snapuserd_core.h"
#include "utility.h" #include "utility.h"
@ -29,11 +30,13 @@ using namespace android::dm;
using android::base::unique_fd; using android::base::unique_fd;
ReadAhead::ReadAhead(const std::string& cow_device, const std::string& backing_device, 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; cow_device_ = cow_device;
backing_store_device_ = backing_device; backing_store_device_ = backing_device;
misc_name_ = misc_name; misc_name_ = misc_name;
snapuserd_ = snapuserd; snapuserd_ = snapuserd;
cow_op_merge_size_ = cow_op_merge_size;
} }
void ReadAhead::CheckOverlap(const CowOperation* cow_op) { 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<uint64_t>& blocks,
std::vector<const CowOperation*>& xor_op_vec) { std::vector<const CowOperation*>& xor_op_vec) {
int num_ops = *pending_ops; 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); bool is_ops_present = (!RAIterDone() && num_ops);
if (!is_ops_present) { if (!is_ops_present) {

View file

@ -35,7 +35,8 @@ class SnapshotHandler;
class ReadAhead { class ReadAhead {
public: public:
ReadAhead(const std::string& cow_device, const std::string& backing_device, 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(); bool RunThread();
private: private:
@ -106,6 +107,7 @@ class ReadAhead {
// syscalls and fallback to synchronous I/O, we // syscalls and fallback to synchronous I/O, we
// don't want huge queue depth // don't want huge queue depth
int queue_depth_ = 8; int queue_depth_ = 8;
uint32_t cow_op_merge_size_;
std::unique_ptr<struct io_uring> ring_; std::unique_ptr<struct io_uring> ring_;
}; };

View file

@ -22,6 +22,7 @@
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <sys/socket.h> #include <sys/socket.h>
#include <sys/system_properties.h>
#include <sys/types.h> #include <sys/types.h>
#include <unistd.h> #include <unistd.h>
@ -35,9 +36,6 @@
#include <snapuserd/snapuserd_client.h> #include <snapuserd/snapuserd_client.h>
#include "snapuserd_server.h" #include "snapuserd_server.h"
#define _REALLY_INCLUDE_SYS__SYSTEM_PROPERTIES_H_
#include <sys/_system_properties.h>
namespace android { namespace android {
namespace snapshot { namespace snapshot {
@ -347,7 +345,8 @@ std::shared_ptr<HandlerThread> UserSnapshotServer::AddHandler(const std::string&
const std::string& cow_device_path, const std::string& cow_device_path,
const std::string& backing_device, const std::string& backing_device,
const std::string& base_path_merge, 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 // We will need multiple worker threads only during
// device boot after OTA. For all other purposes, // device boot after OTA. For all other purposes,
// one thread is sufficient. We don't want to consume // 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); auto opener = block_server_factory_->CreateOpener(misc_name);
return handlers_->AddHandler(misc_name, cow_device_path, backing_device, base_path_merge, 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() { bool UserSnapshotServer::WaitForSocket() {

View file

@ -87,7 +87,8 @@ class UserSnapshotServer {
const std::string& cow_device_path, const std::string& cow_device_path,
const std::string& backing_device, const std::string& backing_device,
const std::string& base_path_merge, 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); bool StartHandler(const std::string& misc_name);
void SetTerminating() { terminating_ = true; } void SetTerminating() { terminating_ = true; }

View file

@ -67,6 +67,7 @@ struct TestParam {
std::string compression; std::string compression;
int block_size; int block_size;
int num_threads; int num_threads;
uint32_t cow_op_merge_size;
}; };
class SnapuserdTestBase : public ::testing::TestWithParam<TestParam> { 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> CreateCowDeviceInternal();
std::unique_ptr<ICowWriter> CreateV3Cow(); std::unique_ptr<ICowWriter> CreateV3Cow();
unique_fd GetCowFd() { return unique_fd{dup(cow_system_->fd)}; }
std::unique_ptr<ITestHarness> harness_; std::unique_ptr<ITestHarness> harness_;
size_t size_ = 10_MiB; size_t size_ = 10_MiB;
int total_base_size_ = 0; int total_base_size_ = 0;
@ -101,7 +104,9 @@ void SnapuserdTestBase::SetUp() {
#endif #endif
} }
void SnapuserdTestBase::TearDown() {} void SnapuserdTestBase::TearDown() {
cow_system_ = nullptr;
}
void SnapuserdTestBase::CreateBaseDevice() { void SnapuserdTestBase::CreateBaseDevice() {
total_base_size_ = (size_ * 5); total_base_size_ = (size_ * 5);
@ -132,10 +137,7 @@ std::unique_ptr<ICowWriter> SnapuserdTestBase::CreateCowDeviceInternal() {
CowOptions options; CowOptions options;
options.compression = "gz"; options.compression = "gz";
unique_fd fd(cow_system_->fd); return CreateCowWriter(2, options, GetCowFd());
cow_system_->fd = -1;
return CreateCowWriter(2, options, std::move(fd));
} }
std::unique_ptr<ICowWriter> SnapuserdTestBase::CreateV3Cow() { std::unique_ptr<ICowWriter> SnapuserdTestBase::CreateV3Cow() {
@ -151,10 +153,7 @@ std::unique_ptr<ICowWriter> SnapuserdTestBase::CreateV3Cow() {
std::string path = android::base::GetExecutableDirectory(); std::string path = android::base::GetExecutableDirectory();
cow_system_ = std::make_unique<TemporaryFile>(path); cow_system_ = std::make_unique<TemporaryFile>(path);
unique_fd fd(cow_system_->fd); return CreateCowWriter(3, options, GetCowFd());
cow_system_->fd = -1;
return CreateCowWriter(3, options, std::move(fd));
} }
void SnapuserdTestBase::CreateCowDevice() { void SnapuserdTestBase::CreateCowDevice() {
@ -710,9 +709,9 @@ void SnapuserdTest::InitCowDevice() {
auto opener = factory->CreateOpener(system_device_ctrl_name_); auto opener = factory->CreateOpener(system_device_ctrl_name_);
handlers_->DisableVerification(); handlers_->DisableVerification();
const TestParam params = GetParam(); const TestParam params = GetParam();
auto handler = handlers_->AddHandler(system_device_ctrl_name_, cow_system_->path, auto handler = handlers_->AddHandler(
base_dev_->GetPath(), base_dev_->GetPath(), opener, 1, system_device_ctrl_name_, cow_system_->path, base_dev_->GetPath(), base_dev_->GetPath(),
params.io_uring, params.o_direct); opener, 1, params.io_uring, params.o_direct, params.cow_op_merge_size);
ASSERT_NE(handler, nullptr); ASSERT_NE(handler, nullptr);
ASSERT_NE(handler->snapuserd(), nullptr); ASSERT_NE(handler->snapuserd(), nullptr);
#ifdef __ANDROID__ #ifdef __ANDROID__
@ -1229,9 +1228,9 @@ void HandlerTest::InitializeDevice() {
ASSERT_NE(opener_, nullptr); ASSERT_NE(opener_, nullptr);
const TestParam params = GetParam(); const TestParam params = GetParam();
handler_ = std::make_shared<SnapshotHandler>(system_device_ctrl_name_, cow_system_->path, handler_ = std::make_shared<SnapshotHandler>(
base_dev_->GetPath(), base_dev_->GetPath(), system_device_ctrl_name_, cow_system_->path, base_dev_->GetPath(), base_dev_->GetPath(),
opener_, 1, false, false, params.o_direct); opener_, 1, false, false, params.o_direct, params.cow_op_merge_size);
ASSERT_TRUE(handler_->InitCowDevice()); ASSERT_TRUE(handler_->InitCowDevice());
ASSERT_TRUE(handler_->InitializeWorkers()); ASSERT_TRUE(handler_->InitializeWorkers());
@ -1509,6 +1508,7 @@ std::vector<TestParam> GetVariableBlockTestConfigs() {
param.num_threads = thread; param.num_threads = thread;
param.io_uring = io_uring; param.io_uring = io_uring;
param.o_direct = false; param.o_direct = false;
param.cow_op_merge_size = 0;
testParams.push_back(std::move(param)); 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) { int main(int argc, char** argv) {
::testing::InitGoogleTest(&argc, 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); gflags::ParseCommandLineFlags(&argc, &argv, false);
return RUN_ALL_TESTS(); return RUN_ALL_TESTS();

View file

@ -14,11 +14,14 @@
#include "utility.h" #include "utility.h"
#include <android-base/properties.h>
#include <sys/resource.h> #include <sys/resource.h>
#include <sys/utsname.h> #include <sys/utsname.h>
#include <unistd.h> #include <unistd.h>
#include <android-base/file.h> #include <android-base/file.h>
#include <android-base/logging.h>
#include <libdm/dm.h>
#include <processgroup/processgroup.h> #include <processgroup/processgroup.h>
#include <private/android_filesystem_config.h> #include <private/android_filesystem_config.h>
@ -27,6 +30,7 @@ namespace android {
namespace snapshot { namespace snapshot {
using android::base::unique_fd; using android::base::unique_fd;
using android::dm::DeviceMapper;
bool SetThreadPriority([[maybe_unused]] int priority) { bool SetThreadPriority([[maybe_unused]] int priority) {
#ifdef __ANDROID__ #ifdef __ANDROID__
@ -61,5 +65,38 @@ bool KernelSupportsIoUring() {
return major > 5 || (major == 5 && minor >= 6); 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 snapshot
} // namespace android } // namespace android

View file

@ -24,5 +24,10 @@ bool SetThreadPriority(int priority);
bool SetProfiles(std::initializer_list<std::string_view> profiles); bool SetProfiles(std::initializer_list<std::string_view> profiles);
bool KernelSupportsIoUring(); bool KernelSupportsIoUring();
bool GetUserspaceSnapshotsEnabledProperty();
bool KernelSupportsCompressedSnapshots();
bool CanUseUserspaceSnapshots();
bool IsVendorFromAndroid12();
} // namespace snapshot } // namespace snapshot
} // namespace android } // namespace android

View file

@ -16,6 +16,7 @@
<option name="config-descriptor:metadata" key="parameter" value="not_instant_app" /> <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="multi_abi" />
<option name="config-descriptor:metadata" key="parameter" value="secondary_user" /> <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"> <target_preparer class="com.android.compatibility.common.tradefed.targetprep.FilePusher">
<option name="cleanup" value="true" /> <option name="cleanup" value="true" />
<option name="push" value="CtsFsMgrTestCases->/data/local/tmp/CtsFsMgrTestCases" /> <option name="push" value="CtsFsMgrTestCases->/data/local/tmp/CtsFsMgrTestCases" />

View file

@ -37,6 +37,10 @@
using namespace android::fs_mgr; using namespace android::fs_mgr;
using namespace testing; using namespace testing;
#if !defined(MS_LAZYTIME)
#define MS_LAZYTIME (1 << 25)
#endif
namespace { namespace {
const std::string cmdline = const std::string cmdline =
@ -329,6 +333,7 @@ TEST(fs_mgr, fs_mgr_read_fstab_file_proc_mounts) {
{"private", MS_PRIVATE}, {"private", MS_PRIVATE},
{"slave", MS_SLAVE}, {"slave", MS_SLAVE},
{"shared", MS_SHARED}, {"shared", MS_SHARED},
{"lazytime", MS_LAZYTIME},
{"defaults", 0}, {"defaults", 0},
{0, 0}, {0, 0},
}; };
@ -1062,23 +1067,6 @@ TEST(fs_mgr, DefaultFstabContainsUserdata) {
<< "Default fstab doesn't contain /data entry"; << "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) { TEST(fs_mgr, ReadFstabFromFile_FsMgrOptions_Readahead_Size_KB) {
TemporaryFile tf; TemporaryFile tf;
ASSERT_TRUE(tf.fd != -1); ASSERT_TRUE(tf.fd != -1);

View file

@ -306,8 +306,8 @@ void Charger::UpdateScreenState(int64_t now) {
if (!batt_anim_.run || now < next_screen_transition_) return; if (!batt_anim_.run || now < next_screen_transition_) return;
// If battery level is not ready, keep checking in the defined time // If battery status is not ready, keep checking in the defined time
if (health_info_.battery_level == 0 && health_info_.battery_status == BatteryStatus::UNKNOWN) { if (health_info_.battery_status == BatteryStatus::UNKNOWN) {
if (wait_batt_level_timestamp_ == 0) { if (wait_batt_level_timestamp_ == 0) {
// Set max delay time and skip drawing screen // Set max delay time and skip drawing screen
wait_batt_level_timestamp_ = now + MAX_BATT_LEVEL_WAIT_TIME; wait_batt_level_timestamp_ = now + MAX_BATT_LEVEL_WAIT_TIME;
@ -317,7 +317,7 @@ void Charger::UpdateScreenState(int64_t now) {
// Do nothing, keep waiting // Do nothing, keep waiting
return; 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; if (healthd_draw_ == nullptr) return;

View file

@ -38,7 +38,6 @@ init_common_sources = [
"capabilities.cpp", "capabilities.cpp",
"epoll.cpp", "epoll.cpp",
"import_parser.cpp", "import_parser.cpp",
"interface_utils.cpp",
"interprocess_fifo.cpp", "interprocess_fifo.cpp",
"keychords.cpp", "keychords.cpp",
"parser.cpp", "parser.cpp",
@ -85,10 +84,6 @@ init_device_sources = [
"ueventd.cpp", "ueventd.cpp",
"ueventd_parser.cpp", "ueventd_parser.cpp",
] ]
init_host_sources = [
"check_builtins.cpp",
"host_import_parser.cpp",
]
cc_library_static { cc_library_static {
name: "vendor_init", name: "vendor_init",
@ -170,7 +165,7 @@ libinit_cc_defaults {
}, },
release_write_appcompat_override_system_properties: { release_write_appcompat_override_system_properties: {
cflags: ["-DWRITE_APPCOMPAT_OVERRIDE_SYSTEM_PROPERTIES"], cflags: ["-DWRITE_APPCOMPAT_OVERRIDE_SYSTEM_PROPERTIES"],
} },
}, },
static_libs: [ static_libs: [
"libavb", "libavb",
@ -198,7 +193,6 @@ libinit_cc_defaults {
"libext4_utils", "libext4_utils",
"libfs_mgr", "libfs_mgr",
"libgsi", "libgsi",
"libhidl-gen-utils",
"libkeyutils", "libkeyutils",
"liblog", "liblog",
"liblogwrap", "liblogwrap",
@ -236,7 +230,6 @@ cc_defaults {
], ],
whole_static_libs: [ whole_static_libs: [
"libcap", "libcap",
"libcom.android.sysprop.init",
], ],
header_libs: ["bootimg_headers"], header_libs: ["bootimg_headers"],
proto: { proto: {
@ -340,7 +333,7 @@ cc_binary {
static_libs: ["libinit.microdroid"], static_libs: ["libinit.microdroid"],
cflags: ["-DMICRODROID=1"], cflags: ["-DMICRODROID=1"],
no_full_install: true, no_full_install: true,
visibility: ["//packages/modules/Virtualization/microdroid"], visibility: ["//packages/modules/Virtualization/build/microdroid"],
} }
soong_config_module_type { soong_config_module_type {
@ -348,7 +341,7 @@ soong_config_module_type {
module_type: "cc_defaults", module_type: "cc_defaults",
config_namespace: "ANDROID", config_namespace: "ANDROID",
bool_variables: ["BOARD_USES_RECOVERY_AS_BOOT"], 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. // 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", name: "init_first_stage_defaults",
soong_config_variables: { soong_config_variables: {
BOARD_USES_RECOVERY_AS_BOOT: { BOARD_USES_RECOVERY_AS_BOOT: {
installable: false, no_full_install: true,
}, },
}, },
@ -618,8 +611,6 @@ cc_defaults {
whole_static_libs: ["libcap"], whole_static_libs: ["libcap"],
shared_libs: [ shared_libs: [
"libcutils", "libcutils",
"libhidl-gen-utils",
"libhidlmetadata",
"liblog", "liblog",
"libprocessgroup", "libprocessgroup",
"libprotobuf-cpp-lite", "libprotobuf-cpp-lite",
@ -627,9 +618,6 @@ cc_defaults {
proto: { proto: {
type: "lite", type: "lite",
}, },
generated_headers: [
"generated_stub_builtin_function_map",
],
target: { target: {
android: { android: {
enabled: false, enabled: false,
@ -648,17 +636,43 @@ cc_defaults {
cc_binary { cc_binary {
name: "host_init_verifier", name: "host_init_verifier",
defaults: ["init_host_defaults"], 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_headers: [
"generated_android_ids", "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 { cc_library_host_static {
name: "libinit_host", name: "libinit_host",
defaults: ["init_host_defaults"], defaults: ["init_host_defaults"],
srcs: init_common_sources + init_host_sources, srcs: [
"noop_builtins.cpp",
] + init_common_sources,
export_include_dirs: ["."], export_include_dirs: ["."],
generated_headers: [
"noop_builtin_function_map",
],
proto: { proto: {
export_proto_headers: true, export_proto_headers: true,
}, },
@ -673,3 +687,11 @@ sh_binary {
src: "extra_free_kbytes.sh", src: "extra_free_kbytes.sh",
filename_from_src: true, filename_from_src: true,
} }
phony {
name: "init_vendor",
required: select(soong_config_variable("ANDROID", "BOARD_USES_RECOVERY_AS_BOOT"), {
true: [],
default: ["init_first_stage"],
}),
}

View file

@ -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)

View file

@ -499,9 +499,12 @@ have been omitted.
4. `late-fs` - Mount partitions marked as latemounted. 4. `late-fs` - Mount partitions marked as latemounted.
5. `post-fs-data` - Mount and configure `/data`; set up encryption. `/metadata` is 5. `post-fs-data` - Mount and configure `/data`; set up encryption. `/metadata` is
reformatted here if it couldn't mount in first-stage init. reformatted here if it couldn't mount in first-stage init.
6. `zygote-start` - Start the zygote. 6. `post-fs-data-checkpointed` - Triggered when vold has completed committing a checkpoint
7. `early-boot` - After zygote has started. after an OTA update. Not triggered if checkpointing is not needed or supported.
8. `boot` - After `early-boot` actions have completed. 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 Commands
-------- --------
@ -634,7 +637,7 @@ provides the `aidl_lazy_test_1` interface.
Properties are expanded within _level_. Properties are expanded within _level_.
`mark_post_data` `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>]` `mkdir <path> [<mode>] [<owner>] [<group>] [encryption=<action>] [key=<key>]`
> Create a directory at _path_, optionally with the given mode, owner, and > 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 fstab.${ro.hardware} or fstab.${ro.hardware.platform} will be scanned for
under /odm/etc, /vendor/etc, or / at runtime, in that order. 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>` `symlink <target> <path>`
> Create a symbolic link at _path_ with the value _target_ > 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, If the file does not exist, it will be created. If it does exist,
it will be truncated. Properties are expanded within _content_. it will be truncated. Properties are expanded within _content_.
Imports Imports
------- -------
`import <path>` `import <path>`

View file

@ -139,6 +139,10 @@ bool BlockDevInitializer::InitPlatformDevice(const std::string& dev_name) {
return InitDevice("/sys/devices/platform", 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 BlockDevInitializer::InitDevice(const std::string& syspath, const std::string& device_name) {
bool found = false; bool found = false;

View file

@ -34,6 +34,7 @@ class BlockDevInitializer final {
bool InitDevices(std::set<std::string> devices); bool InitDevices(std::set<std::string> devices);
bool InitDmDevice(const std::string& device); bool InitDmDevice(const std::string& device);
bool InitPlatformDevice(const std::string& device); bool InitPlatformDevice(const std::string& device);
bool InitHvcDevice(const std::string& device);
private: private:
ListenerAction HandleUevent(const Uevent& uevent, std::set<std::string>* devices); ListenerAction HandleUevent(const Uevent& uevent, std::set<std::string>* devices);

View file

@ -36,6 +36,7 @@
#include <sys/resource.h> #include <sys/resource.h>
#include <sys/socket.h> #include <sys/socket.h>
#include <sys/stat.h> #include <sys/stat.h>
#include <sys/swap.h>
#include <sys/syscall.h> #include <sys/syscall.h>
#include <sys/system_properties.h> #include <sys/system_properties.h>
#include <sys/time.h> #include <sys/time.h>
@ -46,7 +47,6 @@
#include <map> #include <map>
#include <memory> #include <memory>
#include <InitProperties.sysprop.h>
#include <android-base/chrono_utils.h> #include <android-base/chrono_utils.h>
#include <android-base/file.h> #include <android-base/file.h>
#include <android-base/logging.h> #include <android-base/logging.h>
@ -609,8 +609,6 @@ static Result<void> queue_fs_event(int code) {
return Error() << "Invalid code: " << code; return Error() << "Invalid code: " << code;
} }
static int initial_mount_fstab_return_code = -1;
/* <= Q: mount_all <fstab> [ <path> ]* [--<options>]* /* <= Q: mount_all <fstab> [ <path> ]* [--<options>]*
* >= R: mount_all [ <fstab> ] [--<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); 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) { if (queue_event) {
/* queue_fs_event will queue event based on mount_fstab return code /* queue_fs_event will queue event based on mount_fstab return code
* and return processed 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()) { if (!queue_fs_result.ok()) {
return Error() << "queue_fs_event() failed: " << queue_fs_result.error(); 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) { static Result<void> ExecVdcRebootOnFailure(const std::string& vdc_arg) {
bool should_reboot_into_recovery = true;
auto reboot_reason = vdc_arg + "_failed"; 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 // TODO (b/122850122): support this in gsi
if (should_reboot_into_recovery) { if (IsFbeEnabled() && !android::gsi::IsGsiRunning()) {
if (IsFbeEnabled() && !android::gsi::IsGsiRunning()) { LOG(ERROR) << message << ": Rebooting into recovery, reason: " << reboot_reason;
LOG(ERROR) << message << ": Rebooting into recovery, reason: " << reboot_reason; if (auto result = reboot_into_recovery(
if (auto result = reboot_into_recovery( {"--prompt_and_wipe_data", "--reason="s + reboot_reason});
{"--prompt_and_wipe_data", "--reason="s + reboot_reason}); !result.ok()) {
!result.ok()) { LOG(FATAL) << "Could not reboot into recovery: " << result.error();
LOG(FATAL) << "Could not reboot into recovery: " << result.error();
}
} else {
LOG(ERROR) << "Failure (reboot suppressed): " << reboot_reason;
} }
} else { } else {
LOG(ERROR) << message << ": rebooting, reason: " << reboot_reason; LOG(ERROR) << "Failure (reboot suppressed): " << reboot_reason;
trigger_shutdown("reboot," + reboot_reason);
} }
}; };
@ -1181,29 +1160,6 @@ static Result<void> ExecVdcRebootOnFailure(const std::string& vdc_arg) {
return ExecWithFunctionOnFailure(args, reboot); 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) { static Result<void> do_installkey(const BuiltinArguments& args) {
if (!is_file_crypto()) return {}; 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) { static Result<void> do_mark_post_data(const BuiltinArguments& args) {
ServiceList::GetInstance().MarkPostData(); LOG(INFO) << "deprecated action `mark_post_data` called.";
return {}; return {};
} }
@ -1319,6 +1274,13 @@ static Result<void> do_enter_default_mount_ns(const BuiltinArguments& args) {
return {}; return {};
} }
static Result<void> do_swapoff(const BuiltinArguments& args) {
if (!swapoff(args[1].c_str())) {
return ErrnoError() << "swapoff() failed";
}
return {};
}
// Builtin-function-map start // Builtin-function-map start
const BuiltinFunctionMap& GetBuiltinFunctionMap() { const BuiltinFunctionMap& GetBuiltinFunctionMap() {
constexpr std::size_t kMax = std::numeric_limits<std::size_t>::max(); 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}}}, {"umount_all", {0, 1, {false, do_umount_all}}},
{"update_linker_config", {0, 0, {false, do_update_linker_config}}}, {"update_linker_config", {0, 0, {false, do_update_linker_config}}},
{"readahead", {1, 2, {true, do_readahead}}}, {"readahead", {1, 2, {true, do_readahead}}},
{"remount_userdata", {0, 0, {false, do_remount_userdata}}},
{"restart", {1, 2, {false, do_restart}}}, {"restart", {1, 2, {false, do_restart}}},
{"restorecon", {1, kMax, {true, do_restorecon}}}, {"restorecon", {1, kMax, {true, do_restorecon}}},
{"restorecon_recursive", {1, kMax, {true, do_restorecon_recursive}}}, {"restorecon_recursive", {1, kMax, {true, do_restorecon_recursive}}},
@ -1375,6 +1336,7 @@ const BuiltinFunctionMap& GetBuiltinFunctionMap() {
{"start", {1, 1, {false, do_start}}}, {"start", {1, 1, {false, do_start}}},
{"stop", {1, 1, {false, do_stop}}}, {"stop", {1, 1, {false, do_stop}}},
{"swapon_all", {0, 1, {false, do_swapon_all}}}, {"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}}}, {"enter_default_mount_ns", {0, 0, {false, do_enter_default_mount_ns}}},
{"symlink", {2, 2, {true, do_symlink}}}, {"symlink", {2, 2, {true, do_symlink}}},
{"sysclktz", {1, 1, {false, do_sysclktz}}}, {"sysclktz", {1, 1, {false, do_sysclktz}}},

View file

@ -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, void DeviceHandler::MakeDevice(const std::string& path, bool block, int major, int minor,
const std::vector<std::string>& links) const { 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); mode |= (block ? S_IFBLK : S_IFCHR);
std::string secontext; std::string secontext;
@ -330,11 +330,11 @@ void DeviceHandler::MakeDevice(const std::string& path, bool block, int major, i
if (gid != s.st_gid) { if (gid != s.st_gid) {
new_group = gid; new_group = gid;
} }
if (mode != s.st_mode) { if (mode != s.st_mode) {
if (chmod(path.c_str(), mode) != 0) { if (chmod(path.c_str(), mode) != 0) {
PLOG(ERROR) << "Cannot chmod " << path << " to " << mode; PLOG(ERROR) << "Cannot chmod " << path << " to " << mode;
}
} }
}
} else { } else {
PLOG(ERROR) << "Cannot stat " << path; PLOG(ERROR) << "Cannot stat " << path;
} }
@ -532,7 +532,7 @@ void DeviceHandler::HandleAshmemUevent(const Uevent& uevent) {
if (!ReadFileToString(boot_id_path, &boot_id)) { if (!ReadFileToString(boot_id_path, &boot_id)) {
PLOG(ERROR) << "Cannot duplicate ashmem device node. Failed to read " << boot_id_path; PLOG(ERROR) << "Cannot duplicate ashmem device node. Failed to read " << boot_id_path;
return; return;
}; }
boot_id = Trim(boot_id); boot_id = Trim(boot_id);
Uevent dup_ashmem_uevent = uevent; Uevent dup_ashmem_uevent = uevent;
@ -543,10 +543,10 @@ void DeviceHandler::HandleAshmemUevent(const Uevent& uevent) {
} }
void DeviceHandler::HandleUevent(const Uevent& uevent) { void DeviceHandler::HandleUevent(const Uevent& uevent) {
if (uevent.action == "add" || uevent.action == "change" || if (uevent.action == "add" || uevent.action == "change" || uevent.action == "bind" ||
uevent.action == "bind" || uevent.action == "online") { uevent.action == "online") {
FixupSysPermissions(uevent.path, uevent.subsystem); FixupSysPermissions(uevent.path, uevent.subsystem);
} }
// if it's not a /dev device, nothing to do // if it's not a /dev device, nothing to do
if (uevent.major < 0 || uevent.minor < 0) return; if (uevent.major < 0 || uevent.minor < 0) return;

View file

@ -38,6 +38,7 @@
#include <android-base/logging.h> #include <android-base/logging.h>
#include <android-base/stringprintf.h> #include <android-base/stringprintf.h>
#include <android/avf_cc_flags.h> #include <android/avf_cc_flags.h>
#include <fs_mgr.h>
#include <modprobe/modprobe.h> #include <modprobe/modprobe.h>
#include <private/android_filesystem_config.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; 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) { static std::unique_ptr<FirstStageMount> CreateFirstStageMount(const std::string& cmdline) {
auto ret = FirstStageMount::Create(cmdline); auto ret = FirstStageMount::Create(cmdline);
if (ret.ok()) { if (ret.ok()) {
@ -442,6 +459,8 @@ int FirstStageMain(int argc, char** argv) {
<< module_elapse_time.count() << " ms"; << module_elapse_time.count() << " ms";
} }
MaybeResumeFromHibernation(bootconfig);
std::unique_ptr<FirstStageMount> fsm; std::unique_ptr<FirstStageMount> fsm;
bool created_devices = false; bool created_devices = false;

View file

@ -156,6 +156,13 @@ static Result<Fstab> ReadFirstStageFstabAndroid() {
return fstab; 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 // 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. // Microdroid. For the proper solution the /vendor fstab should probably be defined in the DT.
// TODO(b/285855430): refactor this // TODO(b/285855430): refactor this
@ -166,7 +173,7 @@ static Result<Fstab> ReadFirstStageFstabMicrodroid(const std::string& cmdline) {
if (!ReadDefaultFstab(&fstab)) { if (!ReadDefaultFstab(&fstab)) {
return Error() << "failed to read 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. // We weren't asked to mount /vendor partition, filter it out from the fstab.
auto predicate = [](const auto& entry) { return entry.mount_point == "/vendor"; }; auto predicate = [](const auto& entry) { return entry.mount_point == "/vendor"; };
fstab.erase(std::remove_if(fstab.begin(), fstab.end(), predicate), fstab.end()); fstab.erase(std::remove_if(fstab.begin(), fstab.end(), predicate), fstab.end());
@ -305,6 +312,11 @@ bool FirstStageMountVBootV2::InitDevices() {
return false; return false;
} }
} }
if (IsArcvm() && !block_dev_init_.InitHvcDevice("hvc1")) {
return false;
}
return true; return true;
} }
@ -366,6 +378,14 @@ bool FirstStageMountVBootV2::CreateLogicalPartitions() {
} }
if (SnapshotManager::IsSnapshotManagerNeeded()) { 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(); auto sm = SnapshotManager::NewForFirstStageMount();
if (!sm) { if (!sm) {
return false; return false;

View file

@ -30,7 +30,6 @@ cc_defaults {
shared_libs: [ shared_libs: [
"libbase", "libbase",
"libfs_mgr", "libfs_mgr",
"libhidl-gen-utils",
"libkeyutils", "libkeyutils",
"liblog", "liblog",
"libprocessgroup", "libprocessgroup",
@ -50,7 +49,6 @@ cc_fuzz {
srcs: [ srcs: [
"init_parser_fuzzer.cpp", "init_parser_fuzzer.cpp",
], ],
shared_libs: ["libhidlmetadata",],
defaults: [ defaults: [
"libinit_fuzzer_defaults", "libinit_fuzzer_defaults",
], ],

View file

@ -15,9 +15,7 @@
*/ */
#include <fuzzer/FuzzedDataProvider.h> #include <fuzzer/FuzzedDataProvider.h>
#include <hidl/metadata.h>
#include <import_parser.h> #include <import_parser.h>
#include <interface_utils.h>
#include <rlimit_parser.h> #include <rlimit_parser.h>
using namespace android; using namespace android;
@ -34,7 +32,6 @@ const std::string kValidPaths[] = {
}; };
const int32_t kMaxBytes = 256; const int32_t kMaxBytes = 256;
const std::string kValidInterfaces = "android.frameworks.vr.composer@2.0::IVrComposerClient";
class InitParserFuzzer { class InitParserFuzzer {
public: public:
@ -44,9 +41,6 @@ class InitParserFuzzer {
private: private:
void InvokeParser(); void InvokeParser();
void InvokeLimitParser(); void InvokeLimitParser();
void InvokeInterfaceUtils();
InterfaceInheritanceHierarchyMap GenerateHierarchyMap();
std::vector<HidlInterfaceMetadata> GenerateInterfaceMetadata();
FuzzedDataProvider fdp_; 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() { void InitParserFuzzer::InvokeParser() {
Parser parser; Parser parser;
std::string name = fdp_.ConsumeBool() ? fdp_.ConsumeRandomLengthString(kMaxBytes) : "import"; std::string name = fdp_.ConsumeBool() ? fdp_.ConsumeRandomLengthString(kMaxBytes) : "import";
@ -132,7 +72,6 @@ void InitParserFuzzer::Process() {
while (fdp_.remaining_bytes()) { while (fdp_.remaining_bytes()) {
auto invoke_parser_fuzzer = fdp_.PickValueInArray<const std::function<void()>>({ auto invoke_parser_fuzzer = fdp_.PickValueInArray<const std::function<void()>>({
[&]() { InvokeParser(); }, [&]() { InvokeParser(); },
[&]() { InvokeInterfaceUtils(); },
[&]() { InvokeLimitParser(); }, [&]() { InvokeLimitParser(); },
}); });
invoke_parser_fuzzer(); invoke_parser_fuzzer();

View file

@ -297,9 +297,7 @@ int main(int argc, char** argv) {
ActionManager& am = ActionManager::GetInstance(); ActionManager& am = ActionManager::GetInstance();
ServiceList& sl = ServiceList::GetInstance(); ServiceList& sl = ServiceList::GetInstance();
Parser parser; Parser parser;
parser.AddSectionParser("service", parser.AddSectionParser("service", std::make_unique<ServiceParser>(&sl, GetSubcontext()));
std::make_unique<ServiceParser>(&sl, GetSubcontext(),
*interface_inheritance_hierarchy_map));
parser.AddSectionParser("on", std::make_unique<ActionParser>(&am, GetSubcontext())); parser.AddSectionParser("on", std::make_unique<ActionParser>(&am, GetSubcontext()));
parser.AddSectionParser("import", std::make_unique<HostImportParser>()); parser.AddSectionParser("import", std::make_unique<HostImportParser>());
@ -317,11 +315,23 @@ int main(int argc, char** argv) {
return EXIT_FAILURE; return EXIT_FAILURE;
} }
} }
size_t failures = parser.parse_error_count() + am.CheckAllCommands() + sl.CheckAllCommands(); size_t failures = parser.parse_error_count() + am.CheckAllCommands() + sl.CheckAllCommands();
if (failures > 0) { if (failures > 0) {
LOG(ERROR) << "Failed to parse init scripts with " << failures << " error(s)."; LOG(ERROR) << "Failed to parse init scripts with " << failures << " error(s).";
return EXIT_FAILURE; 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; return EXIT_SUCCESS;
} }

Some files were not shown because too many files have changed in this diff Show more