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.
const std::vector<const std::string> knownReasons = {
const std::vector<std::string> knownReasons = {
// clang-format off
// kernel
"watchdog",

View file

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

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();
if (remaining < decltype(remaining)::zero()) {
log_error(output_fd, 0, "timeout expired");
log_error(output_fd, 0, "timeout expired (update_timeout)");
return false;
}
@ -254,7 +254,7 @@ bool debuggerd_trigger_dump(pid_t tid, DebuggerdDumpType dump_type, unsigned int
if (timeout_ms <= 0) {
remaining_ms = -1;
} else if (remaining_ms < 0) {
log_error(output_fd, 0, "timeout expired");
log_error(output_fd, 0, "timeout expired before poll");
return false;
}
@ -271,7 +271,7 @@ bool debuggerd_trigger_dump(pid_t tid, DebuggerdDumpType dump_type, unsigned int
return false;
}
} else if (rc == 0) {
log_error(output_fd, 0, "timeout expired");
log_error(output_fd, 0, "poll timeout expired");
return false;
}

View file

@ -662,6 +662,15 @@ int main(int argc, char** argv) {
info.pac_enabled_keys = -1;
}
#if defined(__aarch64__)
struct iovec tls_iov = {
&info.tls,
sizeof(info.tls),
};
if (ptrace(PTRACE_GETREGSET, thread, NT_ARM_TLS, reinterpret_cast<void*>(&tls_iov)) == -1) {
info.tls = 0;
}
#endif
if (thread == g_target_thread) {
// Read the thread's registers along with the rest of the crash info out of the pipe.
ReadCrashInfo(input_pipe, &siginfo, &info.registers, &process_info, &recoverable_crash);

View file

@ -18,6 +18,7 @@
#include <dlfcn.h>
#include <err.h>
#include <fcntl.h>
#include <inttypes.h>
#include <linux/prctl.h>
#include <malloc.h>
#include <pthread.h>
@ -69,7 +70,6 @@
#include "crash_test.h"
#include "debuggerd/handler.h"
#include "gtest/gtest.h"
#include "libdebuggerd/utility.h"
#include "protocol.h"
#include "tombstoned/tombstoned.h"
#include "util.h"
@ -741,6 +741,8 @@ TEST_F(CrasherTest, mte_multiple_causes) {
}
#if defined(__aarch64__)
constexpr size_t kTagGranuleSize = 16;
static uintptr_t CreateTagMapping() {
// Some of the MTE tag dump tests assert that there is an inaccessible page to the left and right
// of the PROT_MTE page, so map three pages and set the two guard pages to PROT_NONE.
@ -1771,6 +1773,75 @@ TEST_F(CrasherTest, seccomp_crash_logcat) {
AssertDeath(SIGABRT);
}
extern "C" void malloc_enable();
extern "C" void malloc_disable();
TEST_F(CrasherTest, seccomp_tombstone_no_allocation) {
int intercept_result;
unique_fd output_fd;
static const auto dump_type = kDebuggerdTombstone;
StartProcess(
[]() {
std::thread a(foo);
std::thread b(bar);
std::this_thread::sleep_for(100ms);
// Disable allocations to verify that nothing in the fallback
// signal handler does an allocation.
malloc_disable();
raise_debugger_signal(dump_type);
_exit(0);
},
&seccomp_fork);
StartIntercept(&output_fd, dump_type);
FinishCrasher();
AssertDeath(0);
FinishIntercept(&intercept_result);
ASSERT_EQ(1, intercept_result) << "tombstoned reported failure";
std::string result;
ConsumeFd(std::move(output_fd), &result);
ASSERT_BACKTRACE_FRAME(result, "raise_debugger_signal");
ASSERT_BACKTRACE_FRAME(result, "foo");
ASSERT_BACKTRACE_FRAME(result, "bar");
}
TEST_F(CrasherTest, seccomp_backtrace_no_allocation) {
int intercept_result;
unique_fd output_fd;
static const auto dump_type = kDebuggerdNativeBacktrace;
StartProcess(
[]() {
std::thread a(foo);
std::thread b(bar);
std::this_thread::sleep_for(100ms);
// Disable allocations to verify that nothing in the fallback
// signal handler does an allocation.
malloc_disable();
raise_debugger_signal(dump_type);
_exit(0);
},
&seccomp_fork);
StartIntercept(&output_fd, dump_type);
FinishCrasher();
AssertDeath(0);
FinishIntercept(&intercept_result);
ASSERT_EQ(1, intercept_result) << "tombstoned reported failure";
std::string result;
ConsumeFd(std::move(output_fd), &result);
ASSERT_BACKTRACE_FRAME(result, "raise_debugger_signal");
ASSERT_BACKTRACE_FRAME(result, "foo");
ASSERT_BACKTRACE_FRAME(result, "bar");
}
TEST_F(CrasherTest, competing_tracer) {
int intercept_result;
unique_fd output_fd;

View file

@ -48,50 +48,69 @@ using android::base::unique_fd;
extern "C" bool __linker_enable_fallback_allocator();
extern "C" void __linker_disable_fallback_allocator();
// This is incredibly sketchy to do inside of a signal handler, especially when libbacktrace
// uses the C++ standard library throughout, but this code runs in the linker, so we'll be using
// the linker's malloc instead of the libc one. Switch it out for a replacement, just in case.
//
// This isn't the default method of dumping because it can fail in cases such as address space
// exhaustion.
// This file implements a fallback path for processes that do not allow the
// normal fork and exec of crash_dump to handle crashes/unwinds.
// The issue is that all of this happens from within a signal handler, which
// can cause problems since this code uses the linker allocator which is not
// thread safe. In order to avoid any problems allocating, the code calls
// a function to switch to use a fallback allocator in the linker that will
// only be used for the current thread. All of the libunwindstack code does
// allocations using C++ stl, but should be fine since the code runs in the
// linker and should use the fallback handler.
// This method can still fail if the virtual space is exhausted on a 32 bit
// process or mmap failing due to hitting the maximum number of maps (65535
// total maps) on a 64 bit process.
// Class to handle automatically turning on and off the fallback allocator.
class ScopedUseFallbackAllocator {
public:
ScopedUseFallbackAllocator() { Enable(); }
~ScopedUseFallbackAllocator() { Disable(); }
bool Enable() {
if (!enabled_) {
enabled_ = __linker_enable_fallback_allocator();
if (!enabled_) {
async_safe_format_log(ANDROID_LOG_ERROR, "libc",
"Unable to enable fallback allocator, already in use.");
}
}
return enabled_;
}
void Disable() {
if (enabled_) {
__linker_disable_fallback_allocator();
enabled_ = false;
}
}
bool enabled() { return enabled_; }
private:
bool enabled_ = false;
};
static void debuggerd_fallback_trace(int output_fd, ucontext_t* ucontext) {
if (!__linker_enable_fallback_allocator()) {
async_safe_format_log(ANDROID_LOG_ERROR, "libc", "fallback allocator already in use");
return;
}
std::unique_ptr<unwindstack::Regs> regs;
{
std::unique_ptr<unwindstack::Regs> regs;
ThreadInfo thread;
thread.pid = getpid();
thread.tid = gettid();
thread.thread_name = get_thread_name(gettid());
thread.registers.reset(
unwindstack::Regs::CreateFromUcontext(unwindstack::Regs::CurrentArch(), ucontext));
ThreadInfo thread;
thread.pid = getpid();
thread.tid = gettid();
thread.thread_name = get_thread_name(gettid());
thread.registers.reset(
unwindstack::Regs::CreateFromUcontext(unwindstack::Regs::CurrentArch(), ucontext));
// Do not use the thread cache here because it will call pthread_key_create
// which doesn't work in linker code. See b/189803009.
// Use a normal cached object because the thread is stopped, and there
// is no chance of data changing between reads.
auto process_memory = unwindstack::Memory::CreateProcessMemoryCached(getpid());
// TODO: Create this once and store it in a global?
unwindstack::AndroidLocalUnwinder unwinder(process_memory);
dump_backtrace_thread(output_fd, &unwinder, thread);
}
__linker_disable_fallback_allocator();
}
static void debuggerd_fallback_tombstone(int output_fd, int proto_fd, ucontext_t* ucontext,
siginfo_t* siginfo, void* abort_message) {
if (!__linker_enable_fallback_allocator()) {
async_safe_format_log(ANDROID_LOG_ERROR, "libc", "fallback allocator already in use");
return;
}
engrave_tombstone_ucontext(output_fd, proto_fd, reinterpret_cast<uintptr_t>(abort_message),
siginfo, ucontext);
__linker_disable_fallback_allocator();
// Do not use the thread cache here because it will call pthread_key_create
// which doesn't work in linker code. See b/189803009.
// Use a normal cached object because the thread is stopped, and there
// is no chance of data changing between reads.
auto process_memory = unwindstack::Memory::CreateProcessMemoryCached(getpid());
// TODO: Create this once and store it in a global?
unwindstack::AndroidLocalUnwinder unwinder(process_memory);
dump_backtrace_thread(output_fd, &unwinder, thread);
}
static bool forward_output(int src_fd, int dst_fd, pid_t expected_tid) {
@ -154,6 +173,11 @@ static std::pair<pid_t, int> unpack_thread_fd(uint64_t value) {
}
static void trace_handler(siginfo_t* info, ucontext_t* ucontext) {
ScopedUseFallbackAllocator allocator;
if (!allocator.enabled()) {
return;
}
static std::atomic<uint64_t> trace_output(pack_thread_fd(-1, -1));
if (info->si_value.sival_ptr == kDebuggerdFallbackSivalPtrRequestDump) {
@ -181,6 +205,11 @@ static void trace_handler(siginfo_t* info, ucontext_t* ucontext) {
async_safe_format_log(ANDROID_LOG_ERROR, "libc", "failed to write to output fd");
}
// Stop using the fallback allocator before the close. This will prevent
// a race condition where the thread backtracing all of the threads tries
// to re-acquire the fallback allocator.
allocator.Disable();
close(fd);
return;
}
@ -210,10 +239,15 @@ static void trace_handler(siginfo_t* info, ucontext_t* ucontext) {
// Send a signal to all of our siblings, asking them to dump their stack.
pid_t current_tid = gettid();
if (!iterate_tids(current_tid, [&output_fd, &current_tid](pid_t tid) {
if (!iterate_tids(current_tid, [&allocator, &output_fd, &current_tid](pid_t tid) {
if (current_tid == tid) {
return;
}
if (!allocator.enabled()) {
return;
}
// Use a pipe, to be able to detect situations where the thread gracefully exits before
// receiving our signal.
unique_fd pipe_read, pipe_write;
@ -233,22 +267,29 @@ static void trace_handler(siginfo_t* info, ucontext_t* ucontext) {
return;
}
// Disable our use of the fallback allocator while the target thread
// is getting the backtrace.
allocator.Disable();
siginfo_t siginfo = {};
siginfo.si_code = SI_QUEUE;
siginfo.si_value.sival_ptr = kDebuggerdFallbackSivalPtrRequestDump;
siginfo.si_pid = getpid();
siginfo.si_uid = getuid();
if (syscall(__NR_rt_tgsigqueueinfo, getpid(), tid, BIONIC_SIGNAL_DEBUGGER, &siginfo) != 0) {
if (syscall(__NR_rt_tgsigqueueinfo, getpid(), tid, BIONIC_SIGNAL_DEBUGGER, &siginfo) == 0) {
if (!forward_output(pipe_read.get(), output_fd.get(), tid)) {
async_safe_format_log(ANDROID_LOG_ERROR, "libc",
"timeout expired while waiting for thread %d to dump", tid);
}
} else {
async_safe_format_log(ANDROID_LOG_ERROR, "libc", "failed to send trace signal to %d: %s",
tid, strerror(errno));
return;
}
bool success = forward_output(pipe_read.get(), output_fd.get(), tid);
if (!success) {
async_safe_format_log(ANDROID_LOG_ERROR, "libc",
"timeout expired while waiting for thread %d to dump", tid);
// The thread should be finished now, so try and re-enable the fallback allocator.
if (!allocator.Enable()) {
return;
}
// Regardless of whether the poll succeeds, check to see if the thread took fd ownership.
@ -260,14 +301,15 @@ static void trace_handler(siginfo_t* info, ucontext_t* ucontext) {
close(fd);
}
}
return;
})) {
async_safe_format_log(ANDROID_LOG_ERROR, "libc", "failed to open /proc/%d/task: %s",
current_tid, strerror(errno));
}
dump_backtrace_footer(output_fd.get());
if (allocator.enabled()) {
dump_backtrace_footer(output_fd.get());
}
tombstoned_notify_completion(tombstone_socket.get());
}
@ -295,7 +337,13 @@ static void crash_handler(siginfo_t* info, ucontext_t* ucontext, void* abort_mes
unique_fd tombstone_socket, output_fd, proto_fd;
bool tombstoned_connected = tombstoned_connect(getpid(), &tombstone_socket, &output_fd, &proto_fd,
kDebuggerdTombstoneProto);
debuggerd_fallback_tombstone(output_fd.get(), proto_fd.get(), ucontext, info, abort_message);
{
ScopedUseFallbackAllocator allocator;
if (allocator.enabled()) {
engrave_tombstone_ucontext(output_fd.get(), proto_fd.get(),
reinterpret_cast<uintptr_t>(abort_message), info, ucontext);
}
}
if (tombstoned_connected) {
tombstoned_notify_completion(tombstone_socket.get());
}

View file

@ -36,10 +36,12 @@
#include <sys/uio.h>
#include <sys/un.h>
#include <sys/wait.h>
#include <time.h>
#include <unistd.h>
#include <android-base/macros.h>
#include <android-base/parsebool.h>
#include <android-base/parseint.h>
#include <android-base/properties.h>
#include <android-base/unique_fd.h>
#include <async_safe/log.h>
@ -115,6 +117,59 @@ static bool is_permissive_mte() {
(permissive_env && ParseBool(permissive_env) == ParseBoolResult::kTrue);
}
static bool parse_uint_with_error_reporting(const char* s, const char* name, int* v) {
if (android::base::ParseInt(s, v) && *v >= 0) {
return true;
}
async_safe_format_log(ANDROID_LOG_ERROR, "libc", "invalid %s: %s", name, s);
return false;
}
// We cannot use base::GetIntProperty, because that internally uses
// std::string, which allocates.
static bool property_parse_int(const char* name, int* out) {
const prop_info* pi = __system_property_find(name);
if (!pi) return false;
struct cookie_t {
int* out;
bool empty;
} cookie{out, true};
__system_property_read_callback(
pi,
[](void* raw_cookie, const char* name, const char* value, uint32_t) {
// Property is set to empty value, ignoring.
if (!*value) return;
cookie_t* cookie = reinterpret_cast<cookie_t*>(raw_cookie);
if (parse_uint_with_error_reporting(value, name, cookie->out)) cookie->empty = false;
},
&cookie);
return !cookie.empty;
}
static int permissive_mte_renable_timer() {
if (char* env = getenv("MTE_PERMISSIVE_REENABLE_TIME_CPUMS")) {
int v;
if (parse_uint_with_error_reporting(env, "MTE_PERMISSIVE_REENABLE_TIME_CPUMS", &v)) return v;
}
char process_sysprop_name[512];
async_safe_format_buffer(process_sysprop_name, sizeof(process_sysprop_name),
"persist.sys.mte.permissive_reenable_timer.process.%s", getprogname());
int v;
if (property_parse_int(process_sysprop_name, &v)) return v;
if (property_parse_int("persist.sys.mte.permissive_reenable_timer.default", &v)) return v;
char process_deviceconf_sysprop_name[512];
async_safe_format_buffer(
process_deviceconf_sysprop_name, sizeof(process_deviceconf_sysprop_name),
"persist.device_config.memory_safety_native.permissive_reenable_timer.process.%s",
getprogname());
if (property_parse_int(process_deviceconf_sysprop_name, &v)) return v;
if (property_parse_int(
"persist.device_config.memory_safety_native.permissive_reenable_timer.default", &v))
return v;
return 0;
}
static inline void futex_wait(volatile void* ftx, int value) {
syscall(__NR_futex, ftx, FUTEX_WAIT, value, nullptr, nullptr, 0);
}
@ -599,12 +654,40 @@ static void debuggerd_signal_handler(int signal_number, siginfo_t* info, void* c
if (tagged_addr_ctrl < 0) {
fatal_errno("failed to PR_GET_TAGGED_ADDR_CTRL");
}
int previous = tagged_addr_ctrl & PR_MTE_TCF_MASK;
tagged_addr_ctrl = (tagged_addr_ctrl & ~PR_MTE_TCF_MASK) | PR_MTE_TCF_NONE;
if (prctl(PR_SET_TAGGED_ADDR_CTRL, tagged_addr_ctrl, 0, 0, 0) < 0) {
fatal_errno("failed to PR_SET_TAGGED_ADDR_CTRL");
}
async_safe_format_log(ANDROID_LOG_ERROR, "libc",
"MTE ERROR DETECTED BUT RUNNING IN PERMISSIVE MODE. CONTINUING.");
if (int reenable_timer = permissive_mte_renable_timer()) {
async_safe_format_log(ANDROID_LOG_ERROR, "libc",
"MTE ERROR DETECTED BUT RUNNING IN PERMISSIVE MODE. CONTINUING WITH "
"MTE DISABLED FOR %d MS OF CPU TIME.",
reenable_timer);
timer_t timerid{};
struct sigevent sev {};
sev.sigev_signo = BIONIC_ENABLE_MTE;
sev.sigev_notify = SIGEV_THREAD_ID;
sev.sigev_value.sival_int = previous;
sev.sigev_notify_thread_id = __gettid();
// This MUST be CLOCK_THREAD_CPUTIME_ID. If we used CLOCK_MONOTONIC we could get stuck
// in an endless loop of re-running the same instruction, calling this signal handler,
// and re-enabling MTE before we had a chance to re-run the instruction.
if (timer_create(CLOCK_THREAD_CPUTIME_ID, &sev, &timerid) == -1) {
fatal_errno("timer_create() failed");
}
struct itimerspec its {};
its.it_value.tv_sec = reenable_timer / 1000;
its.it_value.tv_nsec = (reenable_timer % 1000) * 1000000;
if (timer_settime(timerid, 0, &its, nullptr) == -1) {
fatal_errno("timer_settime() failed");
}
} else {
async_safe_format_log(
ANDROID_LOG_ERROR, "libc",
"MTE ERROR DETECTED BUT RUNNING IN PERMISSIVE MODE. CONTINUING WITH MTE DISABLED.");
}
pthread_mutex_unlock(&crash_mutex);
}
@ -755,7 +838,6 @@ void debuggerd_init(debuggerd_callbacks_t* callbacks) {
// Use the alternate signal stack if available so we can catch stack overflows.
action.sa_flags |= SA_ONSTACK;
#define SA_EXPOSE_TAGBITS 0x00000800
// Request that the kernel set tag bits in the fault address. This is necessary for diagnosing MTE
// faults.
action.sa_flags |= SA_EXPOSE_TAGBITS;

View file

@ -73,5 +73,8 @@ bool tombstone_proto_to_text(
void fill_in_backtrace_frame(BacktraceFrame* f, const unwindstack::FrameData& frame);
void set_human_readable_cause(Cause* cause, uint64_t fault_addr);
#if defined(__aarch64__)
void dump_stack_history(unwindstack::AndroidUnwinder* unwinder, uintptr_t target_tls,
StackHistoryBuffer& shb_ob, bool nounwind = false);
#endif
#endif // _DEBUGGERD_TOMBSTONE_H

View file

@ -41,6 +41,9 @@ struct ThreadInfo {
siginfo_t* siginfo = nullptr;
std::unique_ptr<unwindstack::Regs> guest_registers;
#if defined(__aarch64__)
uintptr_t tls; // This is currently used for MTE stack history buffer.
#endif
};
// This struct is written into a pipe from inside the crashing process.

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();
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 <signal.h>
#include <stddef.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <sys/mman.h>
@ -49,9 +50,11 @@
#include <android/log.h>
#include <android/set_abort_message.h>
#include <bionic/macros.h>
#include <bionic/reserved_signals.h>
#include <bionic/crash_detail_internal.h>
#include <bionic/macros.h>
#include <bionic/mte.h>
#include <bionic/reserved_signals.h>
#include <bionic/tls_defines.h>
#include <log/log.h>
#include <log/log_read.h>
#include <log/logprint.h>
@ -202,8 +205,117 @@ void set_human_readable_cause(Cause* cause, uint64_t fault_addr) {
error_type_str, diff, byte_suffix, location_str, heap_object.size(), heap_object.address()));
}
#if defined(__aarch64__)
void dump_stack_history(unwindstack::AndroidUnwinder* unwinder, uintptr_t target_tls,
StackHistoryBuffer& shb_obj, bool nounwind) {
auto process_memory = unwinder->GetProcessMemory();
target_tls += sizeof(void*) * TLS_SLOT_STACK_MTE;
uintptr_t stack_mte;
if (!process_memory->ReadFully(target_tls, &stack_mte, sizeof(stack_mte))) {
async_safe_format_log(ANDROID_LOG_ERROR, LOG_TAG,
"dump_stack_history: failed to read TLS_SLOT_STACK_MTE: %m");
return;
}
if (stack_mte == 0) {
async_safe_format_log(ANDROID_LOG_DEBUG, LOG_TAG,
"dump_stack_history: stack history buffer is null");
return;
}
uintptr_t untagged_stack_mte = untag_address(stack_mte);
uintptr_t buf_size = stack_mte_ringbuffer_size_from_pointer(stack_mte);
if (buf_size == 0) {
async_safe_format_log(ANDROID_LOG_ERROR, LOG_TAG, "dump_stack_history: empty size");
return;
}
uintptr_t buf_start = untagged_stack_mte & ~(buf_size - 1ULL);
std::vector<char> buf(buf_size);
if (!process_memory->ReadFully(buf_start, buf.data(), buf.size())) {
async_safe_format_log(ANDROID_LOG_ERROR, LOG_TAG,
"dump_stack_history: failed to read stack history: %m");
return;
}
uintptr_t original_off = untagged_stack_mte - buf_start;
if (original_off % 16 || original_off > buf_size) {
async_safe_format_log(ANDROID_LOG_ERROR, LOG_TAG,
"dump_stack_history: invalid offset: %" PRIuPTR, original_off);
return;
}
// The original_off is the next slot that would have been written, so the last
// slot that was written is the previous one.
for (uintptr_t idx = 16; idx <= buf_size; idx += 16) {
int64_t off = original_off - idx;
if (off < 0) off += buf_size;
uintptr_t pc, taggedfp;
memcpy(&pc, &(buf[off]), sizeof(pc));
memcpy(&taggedfp, &(buf[off + sizeof(pc)]), sizeof(taggedfp));
if (pc == 0) break;
uintptr_t fp = untag_address(taggedfp);
uintptr_t tag = taggedfp >> 56;
unwindstack::FrameData frame_data;
if (nounwind) {
frame_data.pc = pc;
} else {
// +4 is to counteract the "pc adjustment" in BuildFrameFromPcOnly.
// BuildFrameFromPcOnly assumes we are unwinding, so it needs to correct for that
// the PC is the return address. That is not the case here.
// It doesn't really matter, because either should be in the correct function, but
// this is more correct (and consistent with the nounwind case).
frame_data = unwinder->BuildFrameFromPcOnly(pc);
frame_data.pc += 4;
frame_data.rel_pc += 4;
}
StackHistoryBufferEntry* entry = shb_obj.add_entries();
fill_in_backtrace_frame(entry->mutable_addr(), frame_data);
entry->set_fp(fp);
entry->set_tag(tag);
}
}
static pid_t get_containing_thread(unwindstack::MapInfo* map_info, pid_t main_tid) {
if (map_info == nullptr) return 0;
std::string name = map_info->name();
if (name == "[stack]") {
return main_tid;
}
int tid;
if (sscanf(name.c_str(), "[anon:stack_and_tls:%d", &tid) != 1) {
return 0;
}
return tid;
}
static std::optional<std::string> maybe_stack_mte_cause(
Tombstone* tombstone, unwindstack::AndroidUnwinder* unwinder, const ThreadInfo& target_thread,
[[maybe_unused]] const std::map<pid_t, ThreadInfo>& threads, uint64_t fault_addr) {
unwindstack::Maps* maps = unwinder->GetMaps();
auto map_info = maps->Find(untag_address(fault_addr));
pid_t tid = get_containing_thread(map_info.get(), target_thread.tid);
if (!tid) {
return std::nullopt;
}
auto it = threads.find(tid);
if (it != threads.end()) {
StackHistoryBuffer* shb = tombstone->mutable_stack_history_buffer();
shb->set_tid(tid);
dump_stack_history(unwinder, it->second.tls, *shb);
} else {
async_safe_format_log(ANDROID_LOG_ERROR, LOG_TAG,
"dump_probable_cause: unknown target thread %d", tid);
}
return StringPrintf("stack tag-mismatch on thread %u", tid);
}
#endif
static void dump_probable_cause(Tombstone* tombstone, unwindstack::AndroidUnwinder* unwinder,
const ProcessInfo& process_info, const ThreadInfo& target_thread) {
const ProcessInfo& process_info, const ThreadInfo& target_thread,
[[maybe_unused]] const std::map<pid_t, ThreadInfo>& threads) {
#if defined(USE_SCUDO)
ScudoCrashData scudo_crash_data(unwinder->GetProcessMemory().get(), process_info);
if (scudo_crash_data.CrashIsMine()) {
@ -244,10 +356,21 @@ static void dump_probable_cause(Tombstone* tombstone, unwindstack::AndroidUnwind
auto map_info = maps->Find(fault_addr);
if (map_info != nullptr && map_info->flags() == PROT_EXEC) {
cause = "execute-only (no-read) memory access error; likely due to data in .text.";
} else if (fault_addr == target_thread.registers->pc() &&
map_info != nullptr && (map_info->flags() & PROT_EXEC) == 0) {
cause = "trying to execute non-executable memory.";
} else {
cause = get_stack_overflow_cause(fault_addr, target_thread.registers->sp(), maps);
}
} else if (si->si_signo == SIGSYS && si->si_code == SYS_SECCOMP) {
}
#if defined(__aarch64__) && defined(SEGV_MTESERR)
else if (si->si_signo == SIGSEGV && si->si_code == SEGV_MTESERR) {
// If this was a heap MTE crash, it would have been handled by scudo. Checking whether it
// is a stack one.
cause = maybe_stack_mte_cause(tombstone, unwinder, target_thread, threads, fault_addr);
}
#endif
else if (si->si_signo == SIGSYS && si->si_code == SYS_SECCOMP) {
cause = StringPrintf("seccomp prevented call to disallowed %s system call %d", ABI_STRING,
si->si_syscall);
}
@ -788,7 +911,7 @@ void engrave_tombstone_proto(Tombstone* tombstone, unwindstack::AndroidUnwinder*
}
}
dump_probable_cause(&result, unwinder, process_info, target_thread);
dump_probable_cause(&result, unwinder, process_info, target_thread, threads);
dump_mappings(&result, unwinder->GetMaps(), unwinder->GetProcessMemory());

View file

@ -511,6 +511,19 @@ static void print_main_thread(CallbackType callback, const Tombstone& tombstone,
"order of likelihood.");
}
if (tombstone.has_stack_history_buffer()) {
for (const StackHistoryBufferEntry& shbe : tombstone.stack_history_buffer().entries()) {
std::string stack_record_str = StringPrintf(
"stack_record fp:0x%" PRIx64 " tag:0x%" PRIx64 " pc:%s+0x%" PRIx64, shbe.fp(), shbe.tag(),
shbe.addr().file_name().c_str(), shbe.addr().rel_pc());
if (!shbe.addr().build_id().empty()) {
StringAppendF(&stack_record_str, " (BuildId: %s)", shbe.addr().build_id().c_str());
}
CBL("%s", stack_record_str.c_str());
}
}
for (const Cause& cause : tombstone.causes()) {
if (tombstone.causes_size() > 1) {
CBS("");

View file

@ -22,6 +22,21 @@ message CrashDetail {
reserved 3 to 999;
}
message StackHistoryBufferEntry {
BacktraceFrame addr = 1;
uint64 fp = 2;
uint64 tag = 3;
reserved 4 to 999;
}
message StackHistoryBuffer {
uint64 tid = 1;
repeated StackHistoryBufferEntry entries = 2;
reserved 3 to 999;
}
message Tombstone {
Architecture arch = 1;
Architecture guest_arch = 24;
@ -54,7 +69,9 @@ message Tombstone {
uint32 page_size = 22;
bool has_been_16kb_mode = 23;
reserved 26 to 999;
StackHistoryBuffer stack_history_buffer = 26;
reserved 27 to 999;
}
enum Architecture {

View file

@ -20,5 +20,14 @@
int main(int, char**) {
volatile char* f = (char*)malloc(1);
printf("%c\n", f[17]);
#ifdef __aarch64__
if (getenv("MTE_PERMISSIVE_REENABLE_TIME_CPUMS")) {
// Burn some cycles because the MTE_PERMISSIVE_REENABLE_TIME_CPUMS is based on CPU clock.
for (int i = 0; i < 1000000000; ++i) {
asm("isb");
}
printf("%c\n", f[17]);
}
#endif
return 0;
}

View file

@ -97,6 +97,34 @@ public class PermissiveMteTest extends BaseHostJUnit4Test {
}
assertThat(numberTombstones).isEqualTo(1);
}
@Test
public void testReenableCrash() throws Exception {
CommandResult result =
getDevice().executeShellV2Command("MTE_PERMISSIVE=1 MTE_PERMISSIVE_REENABLE_TIME_CPUMS=1 "
+ "/data/local/tmp/mte_crash testReenableCrash "
+ mUUID);
assertThat(result.getExitCode()).isEqualTo(0);
int numberTombstones = 0;
String[] tombstones = getDevice().getChildren("/data/tombstones");
for (String tombstone : tombstones) {
if (!tombstone.endsWith(".pb")) {
continue;
}
String tombstonePath = "/data/tombstones/" + tombstone;
Tombstone tombstoneProto = parseTombstone(tombstonePath);
if (!tombstoneProto.getCommandLineList().stream().anyMatch(x -> x.contains(mUUID))) {
continue;
}
if (!tombstoneProto.getCommandLineList().stream().anyMatch(
x -> x.contains("testReenableCrash"))) {
continue;
}
numberTombstones++;
}
assertThat(numberTombstones).isEqualTo(2);
}
@Test
public void testCrashProperty() throws Exception {
String prevValue = getDevice().getProperty("persist.sys.mte.permissive");

View file

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

View file

@ -186,6 +186,7 @@ cc_binary {
"libprotobuf-cpp-lite",
"libsparse",
"libutils",
"libselinux",
],
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
const auto msg = [&ret, no_print](const std::vector<char>& buf) {
ret += android::base::StringPrintf("(%lu bytes): ", buf.size());
std::vector<const char>::iterator iter = buf.end();
std::vector<char>::const_iterator iter = buf.end();
const unsigned max_chars = 50;
if (buf.size() > max_chars) {
iter = buf.begin() + max_chars;

View file

@ -163,7 +163,7 @@ struct TestFileHandle {
protected:
// |rel_path| is the relative path under test data directory.
TestFileHandle(const std::filesystem::path& rel_path)
: abs_path_(std::move(std::filesystem::path(GetExecutableDirectory()) / rel_path)) {}
: abs_path_(std::filesystem::path(GetExecutableDirectory()) / rel_path) {}
// Given |read_fd|, the readonly fd on the test file, return an fd that's suitable for client
// to use. Implementation is responsible for managing the lifetime of the returned fd.
virtual android::base::Result<borrowed_fd> Transform(const std::filesystem::path& abs_path,

View file

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

View file

@ -28,6 +28,7 @@
#include <sys/ioctl.h>
#include <sys/mount.h>
#include <sys/stat.h>
#include <sys/statvfs.h>
#include <sys/swap.h>
#include <sys/types.h>
#include <sys/utsname.h>
@ -37,10 +38,8 @@
#include <array>
#include <chrono>
#include <functional>
#include <map>
#include <memory>
#include <numeric>
#include <string>
#include <string_view>
#include <thread>
@ -65,6 +64,7 @@
#include <fs_mgr/file_wait.h>
#include <fs_mgr_overlayfs.h>
#include <fscrypt/fscrypt.h>
#include <fstab/fstab.h>
#include <libdm/dm.h>
#include <libdm/loop_control.h>
#include <liblp/metadata_format.h>
@ -81,7 +81,7 @@
#define F2FS_FSCK_BIN "/system/bin/fsck.f2fs"
#define MKSWAP_BIN "/system/bin/mkswap"
#define TUNE2FS_BIN "/system/bin/tune2fs"
#define RESIZE2FS_BIN "/system/bin/resize2fs"
#define RESIZE2FS_BIN "/system/bin/resize2fs"
#ifdef __ANDROID_RECOVERY__
#define FSCK_LOG_FILE "/dev/null"
@ -141,8 +141,8 @@ enum FsStatFlags {
static void log_fs_stat(const std::string& blk_device, int fs_stat) {
std::string msg =
android::base::StringPrintf("\nfs_stat,%s,0x%x\n", blk_device.c_str(), fs_stat);
android::base::unique_fd fd(TEMP_FAILURE_RETRY(open(FSCK_LOG_FILE, O_WRONLY | O_CLOEXEC |
O_APPEND | O_CREAT, 0664)));
android::base::unique_fd fd(TEMP_FAILURE_RETRY(
open(FSCK_LOG_FILE, O_WRONLY | O_CLOEXEC | O_APPEND | O_CREAT, 0664)));
if (fd == -1 || !android::base::WriteStringToFd(msg, fd)) {
LWARNING << __FUNCTION__ << "() cannot log " << msg;
}
@ -219,10 +219,6 @@ static void check_fs(const std::string& blk_device, const std::string& fs_type,
*/
if (!(*fs_stat & FS_STAT_FULL_MOUNT_FAILED)) { // already tried if full mount failed
errno = 0;
if (fs_type == "ext4") {
// This option is only valid with ext4
tmpmnt_opts += ",nomblk_io_submit";
}
ret = mount(blk_device.c_str(), target.c_str(), fs_type.c_str(), tmpmnt_flags,
tmpmnt_opts.c_str());
PINFO << __FUNCTION__ << "(): mount(" << blk_device << "," << target << "," << fs_type
@ -600,7 +596,7 @@ static void tune_metadata_csum(const std::string& blk_device, const FstabEntry&
// Must give `-T now` to prevent last_fsck_time from growing too large,
// otherwise, tune2fs won't enable metadata_csum.
const char* tune2fs_args[] = {TUNE2FS_BIN, "-O", "metadata_csum,64bit,extent",
const char* tune2fs_args[] = {TUNE2FS_BIN, "-O", "metadata_csum,64bit,extent",
"-T", "now", blk_device.c_str()};
const char* resize2fs_args[] = {RESIZE2FS_BIN, "-b", blk_device.c_str()};
@ -936,7 +932,8 @@ static bool should_use_metadata_encryption(const FstabEntry& entry) {
// attempted_idx: On return, will indicate which fstab entry
// succeeded. In case of failure, it will be the start_idx.
// Sets errno to match the 1st mount failure on failure.
static bool mount_with_alternatives(Fstab& fstab, int start_idx, int* end_idx, int* attempted_idx) {
static bool mount_with_alternatives(Fstab& fstab, int start_idx, bool interrupted, int* end_idx,
int* attempted_idx) {
unsigned long i;
int mount_errno = 0;
bool mounted = false;
@ -955,6 +952,13 @@ static bool mount_with_alternatives(Fstab& fstab, int start_idx, int* end_idx, i
continue;
}
if (interrupted) {
LINFO << __FUNCTION__ << "(): skipping fstab mountpoint=" << fstab[i].mount_point
<< " rec[" << i << "].fs_type=" << fstab[i].fs_type
<< " (previously interrupted during encryption step)";
continue;
}
// fstab[start_idx].blk_device is already updated to /dev/dm-<N> by
// AVB related functions. Copy it from start_idx to the current index i.
if ((i != start_idx) && fstab[i].fs_mgr_flags.logical &&
@ -1422,19 +1426,59 @@ static bool IsMountPointMounted(const std::string& mount_point) {
return GetEntryForMountPoint(&fstab, mount_point) != nullptr;
}
std::string fs_mgr_metadata_encryption_in_progress_file_name(const FstabEntry& entry) {
return entry.metadata_key_dir + "/in_progress";
}
bool WasMetadataEncryptionInterrupted(const FstabEntry& entry) {
if (!should_use_metadata_encryption(entry)) return false;
return access(fs_mgr_metadata_encryption_in_progress_file_name(entry).c_str(), R_OK) == 0;
}
static FstabEntry* LocateFormattableEntry(FstabEntry* const begin, FstabEntry* const end) {
if (begin == end) {
return nullptr;
}
const bool dev_option_enabled =
android::base::GetBoolProperty("ro.product.build.16k_page.enabled", false);
FstabEntry* f2fs_entry = nullptr;
for (auto iter = begin; iter != end && iter->blk_device == begin->blk_device; iter++) {
if (iter->fs_mgr_flags.formattable) {
if (getpagesize() != 4096 && is_f2fs(iter->fs_type) && dev_option_enabled) {
f2fs_entry = iter;
continue;
}
if (f2fs_entry) {
LOG(INFO) << "Skipping F2FS format for block device " << iter->blk_device << " @ "
<< iter->mount_point
<< " in non-4K mode for dev option enabled devices, "
"as these devices need to toggle between 4K/16K mode, and F2FS does "
"not support page_size != block_size configuration.";
}
return iter;
}
}
if (f2fs_entry) {
LOG(INFO) << "Using F2FS for " << f2fs_entry->blk_device << " @ " << f2fs_entry->mount_point
<< " even though we are in non-4K mode. Device might require a data wipe after "
"going back to 4K mode, as F2FS does not support page_size != block_size";
}
return f2fs_entry;
}
// When multiple fstab records share the same mount_point, it will try to mount each
// one in turn, and ignore any duplicates after a first successful mount.
// Returns -1 on error, and FS_MGR_MNTALL_* otherwise.
MountAllResult fs_mgr_mount_all(Fstab* fstab, int mount_mode) {
int fs_mgr_mount_all(Fstab* fstab, int mount_mode) {
int encryptable = FS_MGR_MNTALL_DEV_NOT_ENCRYPTABLE;
int error_count = 0;
CheckpointManager checkpoint_manager;
AvbUniquePtr avb_handle(nullptr);
bool wiped = false;
bool userdata_mounted = false;
if (fstab->empty()) {
return {FS_MGR_MNTALL_FAIL, userdata_mounted};
return FS_MGR_MNTALL_FAIL;
}
bool scratch_can_be_mounted = true;
@ -1513,7 +1557,7 @@ MountAllResult fs_mgr_mount_all(Fstab* fstab, int mount_mode) {
if (!avb_handle) {
LERROR << "Failed to open AvbHandle";
set_type_property(encryptable);
return {FS_MGR_MNTALL_FAIL, userdata_mounted};
return FS_MGR_MNTALL_FAIL;
}
}
if (avb_handle->SetUpAvbHashtree(&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 top_idx = i;
int last_idx_inspected = -1;
const int top_idx = i;
int attempted_idx = -1;
bool mret = mount_with_alternatives(*fstab, i, &last_idx_inspected, &attempted_idx);
bool encryption_interrupted = WasMetadataEncryptionInterrupted(current_entry);
bool mret = mount_with_alternatives(*fstab, i, encryption_interrupted, &last_idx_inspected,
&attempted_idx);
auto& attempted_entry = (*fstab)[attempted_idx];
i = last_idx_inspected;
int mount_errno = errno;
@ -1547,7 +1593,7 @@ MountAllResult fs_mgr_mount_all(Fstab* fstab, int mount_mode) {
if (status == FS_MGR_MNTALL_FAIL) {
// Fatal error - no point continuing.
return {status, userdata_mounted};
return status;
}
if (status != FS_MGR_MNTALL_DEV_NOT_ENCRYPTABLE) {
@ -1562,11 +1608,12 @@ MountAllResult fs_mgr_mount_all(Fstab* fstab, int mount_mode) {
attempted_entry.mount_point, wiped ? "true" : "false",
attempted_entry.fs_type,
attempted_entry.fs_mgr_flags.is_zoned ? "true" : "false",
std::to_string(attempted_entry.length),
android::base::Join(attempted_entry.user_devices, ' ')},
nullptr)) {
LERROR << "Encryption failed";
set_type_property(encryptable);
return {FS_MGR_MNTALL_FAIL, userdata_mounted};
return FS_MGR_MNTALL_FAIL;
}
}
}
@ -1580,17 +1627,23 @@ MountAllResult fs_mgr_mount_all(Fstab* fstab, int mount_mode) {
// Success! Go get the next one.
continue;
}
auto formattable_entry =
LocateFormattableEntry(fstab->data() + top_idx, fstab->data() + fstab->size());
// Mounting failed, understand why and retry.
wiped = partition_wiped(current_entry.blk_device.c_str());
if (mount_errno != EBUSY && mount_errno != EACCES &&
current_entry.fs_mgr_flags.formattable && wiped) {
current_entry.fs_mgr_flags.formattable && (wiped || encryption_interrupted)) {
// current_entry and attempted_entry point at the same partition, but sometimes
// at two different lines in the fstab. Use current_entry for formatting
// as that is the preferred one.
LERROR << __FUNCTION__ << "(): " << realpath(current_entry.blk_device)
<< " is wiped and " << current_entry.mount_point << " " << current_entry.fs_type
<< " is formattable. Format it.";
if (wiped)
LERROR << __FUNCTION__ << "(): " << realpath(current_entry.blk_device)
<< " is wiped and " << current_entry.mount_point << " "
<< current_entry.fs_type << " is formattable. Format it.";
if (encryption_interrupted)
LERROR << __FUNCTION__ << "(): " << realpath(current_entry.blk_device)
<< " was interrupted during encryption and " << current_entry.mount_point
<< " " << current_entry.fs_type << " is formattable. Format it.";
checkpoint_manager.Revert(&current_entry);
@ -1603,11 +1656,12 @@ MountAllResult fs_mgr_mount_all(Fstab* fstab, int mount_mode) {
encryptable = FS_MGR_MNTALL_DEV_IS_METADATA_ENCRYPTED;
set_type_property(encryptable);
if (!call_vdc({"cryptfs", "encryptFstab", current_entry.blk_device,
current_entry.mount_point, "true" /* shouldFormat */,
current_entry.fs_type,
current_entry.fs_mgr_flags.is_zoned ? "true" : "false",
android::base::Join(current_entry.user_devices, ' ')},
if (!call_vdc({"cryptfs", "encryptFstab", formattable_entry->blk_device,
formattable_entry->mount_point, "true" /* shouldFormat */,
formattable_entry->fs_type,
formattable_entry->fs_mgr_flags.is_zoned ? "true" : "false",
std::to_string(formattable_entry->length),
android::base::Join(formattable_entry->user_devices, ' ')},
nullptr)) {
LERROR << "Encryption failed";
} else {
@ -1616,7 +1670,7 @@ MountAllResult fs_mgr_mount_all(Fstab* fstab, int mount_mode) {
}
}
if (fs_mgr_do_format(current_entry) == 0) {
if (fs_mgr_do_format(*formattable_entry) == 0) {
// Let's replay the mount actions.
i = top_idx - 1;
continue;
@ -1629,7 +1683,7 @@ MountAllResult fs_mgr_mount_all(Fstab* fstab, int mount_mode) {
}
// mount(2) returned an error, handle the encryptable/formattable case.
if (mount_errno != EBUSY && mount_errno != EACCES &&
if (mount_errno != EBUSY && mount_errno != EACCES && !encryption_interrupted &&
should_use_metadata_encryption(attempted_entry)) {
if (!call_vdc({"cryptfs", "mountFstab", attempted_entry.blk_device,
attempted_entry.mount_point,
@ -1647,13 +1701,13 @@ MountAllResult fs_mgr_mount_all(Fstab* fstab, int mount_mode) {
// Use StringPrintf to output "(null)" instead.
if (attempted_entry.fs_mgr_flags.no_fail) {
PERROR << android::base::StringPrintf(
"Ignoring failure to mount an un-encryptable or wiped "
"Ignoring failure to mount an un-encryptable, interrupted, or wiped "
"partition on %s at %s options: %s",
attempted_entry.blk_device.c_str(), attempted_entry.mount_point.c_str(),
attempted_entry.fs_options.c_str());
} else {
PERROR << android::base::StringPrintf(
"Failed to mount an un-encryptable or wiped partition "
"Failed to mount an un-encryptable, interrupted, or wiped partition "
"on %s at %s options: %s",
attempted_entry.blk_device.c_str(), attempted_entry.mount_point.c_str(),
attempted_entry.fs_options.c_str());
@ -1679,9 +1733,9 @@ MountAllResult fs_mgr_mount_all(Fstab* fstab, int mount_mode) {
set_type_property(encryptable);
if (error_count) {
return {FS_MGR_MNTALL_FAIL, userdata_mounted};
return FS_MGR_MNTALL_FAIL;
} else {
return {encryptable, userdata_mounted};
return encryptable;
}
}
@ -1718,190 +1772,6 @@ int fs_mgr_umount_all(android::fs_mgr::Fstab* fstab) {
return ret;
}
static std::chrono::milliseconds GetMillisProperty(const std::string& name,
std::chrono::milliseconds default_value) {
auto value = GetUintProperty(name, static_cast<uint64_t>(default_value.count()));
return std::chrono::milliseconds(std::move(value));
}
static bool fs_mgr_unmount_all_data_mounts(const std::string& data_block_device) {
LINFO << __FUNCTION__ << "(): about to umount everything on top of " << data_block_device;
Timer t;
auto timeout = GetMillisProperty("init.userspace_reboot.userdata_remount.timeoutmillis", 5s);
while (true) {
bool umount_done = true;
Fstab proc_mounts;
if (!ReadFstabFromFile("/proc/mounts", &proc_mounts)) {
LERROR << __FUNCTION__ << "(): Can't read /proc/mounts";
return false;
}
// Now proceed with other bind mounts on top of /data.
for (const auto& entry : proc_mounts) {
std::string block_device;
if (StartsWith(entry.blk_device, "/dev/block") &&
!Realpath(entry.blk_device, &block_device)) {
PWARNING << __FUNCTION__ << "(): failed to realpath " << entry.blk_device;
block_device = entry.blk_device;
}
if (data_block_device == block_device) {
if (umount2(entry.mount_point.c_str(), 0) != 0) {
PERROR << __FUNCTION__ << "(): Failed to umount " << entry.mount_point;
umount_done = false;
}
}
}
if (umount_done) {
LINFO << __FUNCTION__ << "(): Unmounting /data took " << t;
return true;
}
if (t.duration() > timeout) {
LERROR << __FUNCTION__ << "(): Timed out unmounting all mounts on "
<< data_block_device;
Fstab remaining_mounts;
if (!ReadFstabFromFile("/proc/mounts", &remaining_mounts)) {
LERROR << __FUNCTION__ << "(): Can't read /proc/mounts";
} else {
LERROR << __FUNCTION__ << "(): Following mounts remaining";
for (const auto& e : remaining_mounts) {
LERROR << __FUNCTION__ << "(): mount point: " << e.mount_point
<< " block device: " << e.blk_device;
}
}
return false;
}
std::this_thread::sleep_for(50ms);
}
}
static bool UnwindDmDeviceStack(const std::string& block_device,
std::vector<std::string>* dm_stack) {
if (!StartsWith(block_device, "/dev/block/")) {
LWARNING << block_device << " is not a block device";
return false;
}
std::string current = block_device;
DeviceMapper& dm = DeviceMapper::Instance();
while (true) {
dm_stack->push_back(current);
if (!dm.IsDmBlockDevice(current)) {
break;
}
auto parent = dm.GetParentBlockDeviceByPath(current);
if (!parent) {
return false;
}
current = *parent;
}
return true;
}
FstabEntry* fs_mgr_get_mounted_entry_for_userdata(Fstab* fstab,
const std::string& data_block_device) {
std::vector<std::string> dm_stack;
if (!UnwindDmDeviceStack(data_block_device, &dm_stack)) {
LERROR << "Failed to unwind dm-device stack for " << data_block_device;
return nullptr;
}
for (auto& entry : *fstab) {
if (entry.mount_point != "/data") {
continue;
}
std::string block_device;
if (entry.fs_mgr_flags.logical) {
if (!fs_mgr_update_logical_partition(&entry)) {
LERROR << "Failed to update logic partition " << entry.blk_device;
continue;
}
block_device = entry.blk_device;
} else if (!Realpath(entry.blk_device, &block_device)) {
PWARNING << "Failed to realpath " << entry.blk_device;
block_device = entry.blk_device;
}
if (std::find(dm_stack.begin(), dm_stack.end(), block_device) != dm_stack.end()) {
return &entry;
}
}
LERROR << "Didn't find entry that was used to mount /data onto " << data_block_device;
return nullptr;
}
// TODO(b/143970043): return different error codes based on which step failed.
int fs_mgr_remount_userdata_into_checkpointing(Fstab* fstab) {
Fstab proc_mounts;
if (!ReadFstabFromFile("/proc/mounts", &proc_mounts)) {
LERROR << "Can't read /proc/mounts";
return -1;
}
auto mounted_entry = GetEntryForMountPoint(&proc_mounts, "/data");
if (mounted_entry == nullptr) {
LERROR << "/data is not mounted";
return -1;
}
std::string block_device;
if (!Realpath(mounted_entry->blk_device, &block_device)) {
PERROR << "Failed to realpath " << mounted_entry->blk_device;
return -1;
}
auto fstab_entry = fs_mgr_get_mounted_entry_for_userdata(fstab, block_device);
if (fstab_entry == nullptr) {
LERROR << "Can't find /data in fstab";
return -1;
}
bool force_umount = GetBoolProperty("sys.init.userdata_remount.force_umount", false);
if (force_umount) {
LINFO << "Will force an umount of userdata even if it's not required";
}
if (!force_umount && !SupportsCheckpoint(fstab_entry)) {
LINFO << "Userdata doesn't support checkpointing. Nothing to do";
return 0;
}
CheckpointManager checkpoint_manager;
if (!force_umount && !checkpoint_manager.NeedsCheckpoint()) {
LINFO << "Checkpointing not needed. Don't remount";
return 0;
}
if (!force_umount && fstab_entry->fs_mgr_flags.checkpoint_fs) {
// Userdata is f2fs, simply remount it.
if (!checkpoint_manager.Update(fstab_entry)) {
LERROR << "Failed to remount userdata in checkpointing mode";
return -1;
}
if (mount(block_device.c_str(), fstab_entry->mount_point.c_str(), "none",
MS_REMOUNT | fstab_entry->flags, fstab_entry->fs_options.c_str()) != 0) {
PERROR << "Failed to remount userdata in checkpointing mode";
return -1;
}
} else {
LINFO << "Unmounting /data before remounting into checkpointing mode";
if (!fs_mgr_unmount_all_data_mounts(block_device)) {
LERROR << "Failed to umount /data";
return -1;
}
DeviceMapper& dm = DeviceMapper::Instance();
while (dm.IsDmBlockDevice(block_device)) {
auto next_device = dm.GetParentBlockDeviceByPath(block_device);
auto name = dm.GetDmDeviceNameByPath(block_device);
if (!name) {
LERROR << "Failed to get dm-name for " << block_device;
return -1;
}
LINFO << "Deleting " << block_device << " named " << *name;
if (!dm.DeleteDevice(*name, 3s)) {
return -1;
}
if (!next_device) {
LERROR << "Failed to find parent device for " << block_device;
}
block_device = *next_device;
}
LINFO << "Remounting /data";
// TODO(b/143970043): remove this hack after fs_mgr_mount_all is refactored.
auto result = fs_mgr_mount_all(fstab, MOUNT_MODE_ONLY_USERDATA);
return result.code == FS_MGR_MNTALL_FAIL ? -1 : 0;
}
return 0;
}
// wrapper to __mount() and expects a fully prepared fstab_rec,
// unlike fs_mgr_do_mount which does more things with avb / verity etc.
int fs_mgr_do_mount_one(const FstabEntry& entry, const std::string& alt_mount_point) {
@ -1916,12 +1786,12 @@ int fs_mgr_do_mount_one(const FstabEntry& entry, const std::string& alt_mount_po
int ret = prepare_fs_for_mount(entry.blk_device, entry, mount_point);
// Wiped case doesn't require to try __mount below.
if (ret & FS_STAT_INVALID_MAGIC) {
return FS_MGR_DOMNT_FAILED;
return FS_MGR_DOMNT_FAILED;
}
ret = __mount(entry.blk_device, mount_point, entry);
if (ret) {
ret = (errno == EBUSY) ? FS_MGR_DOMNT_BUSY : FS_MGR_DOMNT_FAILED;
ret = (errno == EBUSY) ? FS_MGR_DOMNT_BUSY : FS_MGR_DOMNT_FAILED;
}
return ret;
@ -2073,11 +1943,45 @@ static bool InstallZramDevice(const std::string& device) {
return true;
}
/*
* Zram backing device can be created as long as /data has at least `size`
* free space, though we may want to leave some extra space for the remaining
* boot process and other system activities.
*/
static bool ZramBackingDeviceSizeAvailable(off64_t size) {
constexpr const char* data_path = "/data";
uint64_t min_free_mb =
android::base::GetUintProperty<uint64_t>("ro.zram_backing_device_min_free_mb", 0);
// No min_free property. Skip the available size check.
if (min_free_mb == 0) return true;
struct statvfs vst;
if (statvfs(data_path, &vst) < 0) {
PERROR << "Cannot check available space: " << data_path;
return false;
}
uint64_t size_free = static_cast<uint64_t>(vst.f_bfree) * vst.f_frsize;
uint64_t size_required = size + (min_free_mb * 1024 * 1024);
if (size_required > size_free) {
PERROR << "Free space is not enough for zram backing device: " << size_required << " > "
<< size_free;
return false;
}
return true;
}
static bool PrepareZramBackingDevice(off64_t size) {
constexpr const char* file_path = "/data/per_boot/zram_swap";
if (size == 0) return true;
// Check available space
if (!ZramBackingDeviceSizeAvailable(size)) {
PERROR << "No space for target path: " << file_path;
return false;
}
// Prepare target path
unique_fd target_fd(TEMP_FAILURE_RETRY(open(file_path, O_RDWR | O_CREAT | O_CLOEXEC, 0600)));
if (target_fd.get() == -1) {
@ -2086,6 +1990,7 @@ static bool PrepareZramBackingDevice(off64_t size) {
}
if (fallocate(target_fd.get(), 0, 0, size) < 0) {
PERROR << "Cannot truncate target path: " << file_path;
unlink(file_path);
return false;
}

View file

@ -74,7 +74,7 @@ bool fs_mgr_is_dsu_running() {
return android::gsi::IsGsiRunning();
}
std::vector<const std::string> OverlayMountPoints() {
std::vector<std::string> OverlayMountPoints() {
// Never fallback to legacy cache mount point if within a DSU system,
// because running a DSU system implies the device supports dynamic
// partitions, which means legacy cache mustn't be used.
@ -412,6 +412,8 @@ static bool fs_mgr_overlayfs_mount_one(const FstabEntry& fstab_entry) {
bool retval = true;
bool move_dir_shared = true;
bool parent_shared = true;
bool parent_have_parent = false;
bool parent_made_private = false;
bool root_shared = true;
bool root_made_private = false;
@ -443,6 +445,10 @@ static bool fs_mgr_overlayfs_mount_one(const FstabEntry& fstab_entry) {
if (entry.mount_point == "/") {
root_shared = entry.shared_flag;
}
// Ignore "/" as we don't overlay "/" directly.
if (entry.mount_point != "/") {
parent_have_parent |= android::base::StartsWith(mount_point, entry.mount_point + "/");
}
}
// Precondition is that kMoveMountTempDir is MS_PRIVATE, otherwise don't try to move any
@ -453,11 +459,13 @@ static bool fs_mgr_overlayfs_mount_one(const FstabEntry& fstab_entry) {
// Need to make the original mountpoint MS_PRIVATE, so that the overlayfs can be MS_MOVE.
// This could happen if its parent mount is remounted later.
if (!fs_mgr_overlayfs_set_shared_mount(mount_point, false)) {
// If failed to set "/system" mount type, it might be due to "/system" not being a valid
// mountpoint after switch root. Retry with "/" in this case.
if (errno == EINVAL && mount_point == "/system") {
root_made_private = fs_mgr_overlayfs_set_shared_mount("/", false);
if (parent_have_parent) {
parent_made_private |= fs_mgr_overlayfs_set_shared_mount(mount_point, false);
if (!parent_made_private && errno == EINVAL && mount_point == "/system") {
// If failed to set "/system" mount type, it might be due to "/system" not being a valid
// mountpoint after switch root. Retry with "/" in this case.
parent_made_private |= fs_mgr_overlayfs_set_shared_mount("/", false);
root_made_private |= parent_made_private;
}
}
@ -496,6 +504,15 @@ static bool fs_mgr_overlayfs_mount_one(const FstabEntry& fstab_entry) {
continue;
}
}
if (!parent_made_private) {
parent_made_private |= fs_mgr_overlayfs_set_shared_mount(mount_point, false);
if (!parent_made_private && errno == EINVAL && mount_point == "/system") {
// If failed to set "/system" mount type, it might be due to "/system" not being a
// valid mountpoint after switch root. Retry with "/" in this case.
parent_made_private |= fs_mgr_overlayfs_set_shared_mount("/", false);
root_made_private |= parent_made_private;
}
}
if (new_entry.shared_flag) {
new_entry.shared_flag = fs_mgr_overlayfs_set_shared_mount(new_entry.mount_point, false);
@ -524,7 +541,7 @@ static bool fs_mgr_overlayfs_mount_one(const FstabEntry& fstab_entry) {
rmdir(entry.dir.c_str());
}
// If the original (overridden) mount was MS_SHARED, then set the overlayfs mount to MS_SHARED.
if (parent_shared) {
if (parent_shared && parent_made_private) {
fs_mgr_overlayfs_set_shared_mount(mount_point, true);
}
if (root_shared && root_made_private) {

View file

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

View file

@ -80,10 +80,8 @@
using namespace std::chrono_literals;
bool fs_mgr_set_blk_ro(const std::string& blockdev, bool readonly = true);
bool fs_mgr_is_device_unlocked();
bool fs_mgr_is_ext4(const std::string& blk_device);
bool fs_mgr_is_f2fs(const std::string& blk_device);
bool fs_mgr_filesystem_available(const std::string& filesystem);

View file

@ -171,11 +171,12 @@ bool VerifyCheckpointing() {
}
if (show_help) {
show_help = false;
std::cerr << "WARNING: Userdata checkpoint is in progress. To force end checkpointing, "
"call 'vdc checkpoint commitChanges'. This can lead to data corruption if "
"rolled back."
std::cerr << "WARNING: Userdata checkpoint is in progress. "
"To forcibly end checkpointing, "
"call 'vdc checkpoint commitChanges'. "
"This can lead to data corruption if rolled back."
<< std::endl;
LOG(INFO) << "Waiting for checkpoint to complete and then continue remount.";
LOG(INFO) << "Waiting for checkpoint to complete before remounting...";
}
std::this_thread::sleep_for(4s);
}

View file

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

View file

@ -58,13 +58,8 @@ enum mount_mode {
#define FS_MGR_MNTALL_DEV_NEEDS_RECOVERY 4
#define FS_MGR_MNTALL_DEV_NOT_ENCRYPTABLE 0
#define FS_MGR_MNTALL_FAIL (-1)
struct MountAllResult {
// One of the FS_MGR_MNTALL_* returned code defined above.
int code;
// Whether userdata was mounted as a result of |fs_mgr_mount_all| call.
bool userdata_mounted;
};
// fs_mgr_mount_all() updates fstab entries that reference device-mapper.
int fs_mgr_mount_all(android::fs_mgr::Fstab* fstab, int mount_mode);
struct HashtreeInfo {
// The hash algorithm used to build the merkle tree.
@ -75,13 +70,6 @@ struct HashtreeInfo {
bool check_at_most_once;
};
// fs_mgr_mount_all() updates fstab entries that reference device-mapper.
// Returns a |MountAllResult|. The first element is one of the FS_MNG_MNTALL_* return codes
// defined above, and the second element tells whether this call to fs_mgr_mount_all was responsible
// for mounting userdata. Later is required for init to correctly enqueue fs-related events as part
// of userdata remount during userspace reboot.
MountAllResult fs_mgr_mount_all(android::fs_mgr::Fstab* fstab, int mount_mode);
#define FS_MGR_DOMNT_FAILED (-1)
#define FS_MGR_DOMNT_BUSY (-2)
#define FS_MGR_DOMNT_SUCCESS 0
@ -116,6 +104,12 @@ int fs_mgr_setup_verity(android::fs_mgr::FstabEntry* fstab, bool wait_for_verity
// returned. Otherwise, it will use the current slot.
std::string fs_mgr_get_super_partition_name(int slot = -1);
// Set readonly for the block device
bool fs_mgr_set_blk_ro(const std::string& blockdev, bool readonly = true);
// Check if the block device has ext4 filesystem
bool fs_mgr_is_ext4(const std::string& blk_device);
enum FsMgrUmountStatus : int {
SUCCESS = 0,
ERROR_UNKNOWN = 1 << 0,
@ -127,11 +121,6 @@ enum FsMgrUmountStatus : int {
// it destroys verity devices from device mapper after the device is unmounted.
int fs_mgr_umount_all(android::fs_mgr::Fstab* fstab);
// Finds a entry in |fstab| that was used to mount a /data on |data_block_device|.
android::fs_mgr::FstabEntry* fs_mgr_get_mounted_entry_for_userdata(
android::fs_mgr::Fstab* fstab, const std::string& data_block_device);
int fs_mgr_remount_userdata_into_checkpointing(android::fs_mgr::Fstab* fstab);
// Finds the dm_bow device on which this block device is stacked, or returns
// empty string
std::string fs_mgr_find_bow_device(const std::string& block_device);
@ -144,3 +133,7 @@ bool fs_mgr_create_canonical_mount_point(const std::string& mount_point);
// Unlike fs_mgr_overlayfs, mount overlayfs without upperdir and workdir, so the
// filesystem cannot be remount read-write.
bool fs_mgr_mount_overlayfs_fstab_entry(const android::fs_mgr::FstabEntry& entry);
// File name used to track if encryption was interrupted, leading to a known bad fs state
std::string fs_mgr_metadata_encryption_in_progress_file_name(
const android::fs_mgr::FstabEntry& entry);

View file

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

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) {
if (created) {
unlink(file_path.c_str());
sync();
}
}

View file

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

View file

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

View file

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

View file

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

View file

@ -16,10 +16,11 @@
#include <endian.h>
#include <random>
#include <android-base/strings.h>
#include <android-base/unique_fd.h>
#include <base/files/file_util.h>
#include <base/rand_util.h>
#include <base/strings/string_util.h>
#include <libavb/libavb.h>
#include "avb_util.h"
@ -70,7 +71,7 @@ void AvbUtilTest::SetVBMetaFlags(const base::FilePath& image_path, uint32_t flag
std::string image_file_name = image_path.RemoveExtension().BaseName().value();
bool is_vbmeta_partition =
base::StartsWith(image_file_name, "vbmeta", base::CompareCase::INSENSITIVE_ASCII);
android::base::StartsWithIgnoreCase(image_file_name, "vbmeta");
android::base::unique_fd fd(open(image_path.value().c_str(), O_RDWR | O_CLOEXEC));
EXPECT_TRUE(fd > 0);
@ -603,7 +604,7 @@ bool AvbUtilTest::CompareVBMeta(const base::FilePath& avb_image_path,
std::string image_file_name = avb_image_path.RemoveExtension().BaseName().value();
base::FilePath extracted_vbmeta_path;
if (base::StartsWith(image_file_name, "vbmeta", base::CompareCase::INSENSITIVE_ASCII)) {
if (android::base::StartsWithIgnoreCase(image_file_name, "vbmeta")) {
extracted_vbmeta_path = avb_image_path; // no need to extract if it's a vbmeta image.
} else {
extracted_vbmeta_path = ExtractVBMetaImage(avb_image_path, image_file_name + "-vbmeta.img");
@ -727,7 +728,10 @@ void AvbUtilTest::ModifyFile(const base::FilePath& file_path, size_t offset, ssi
// Introduces a new modification.
if (length > 0) {
int modify_location = base::RandInt(offset, offset + length - 1);
// mersenne_twister_engine seeded with the default seed source.
static std::mt19937 gen(std::random_device{}());
std::uniform_int_distribution<> rand_distribution(offset, offset + length - 1);
int modify_location = rand_distribution(gen);
file_content[modify_location] ^= 0x80;
last_file_path = file_path.value();
last_modified_location = modify_location;

View file

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

View file

@ -18,8 +18,8 @@
#include <stdlib.h>
#include <android-base/file.h>
#include <android-base/strings.h>
#include <base/files/file_util.h>
#include <base/strings/string_util.h>
#include <fs_avb/fs_avb.h>
#include <libavb/libavb.h>
@ -49,7 +49,7 @@ void PublicFsAvbTest::ModifyVBMetaHeaderFlags(const base::FilePath& vbmeta_image
// Only support modifying the flags in vbmeta*.img.
std::string image_file_name = vbmeta_image_path.RemoveExtension().BaseName().value();
ASSERT_TRUE(base::StartsWith(image_file_name, "vbmeta", base::CompareCase::INSENSITIVE_ASCII));
ASSERT_TRUE(android::base::StartsWithIgnoreCase(image_file_name, "vbmeta"));
android::base::unique_fd fd(open(vbmeta_image_path.value().c_str(), O_RDWR | O_CLOEXEC));
EXPECT_TRUE(fd > 0);

View file

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

View file

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

View file

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

View file

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

View file

@ -39,6 +39,10 @@
#include "fstab_priv.h"
#include "logging_macros.h"
#if !defined(MS_LAZYTIME)
#define MS_LAZYTIME (1 << 25)
#endif
using android::base::EndsWith;
using android::base::ParseByteCount;
using android::base::ParseInt;
@ -74,6 +78,7 @@ FlagList kMountFlagsList[] = {
{"private", MS_PRIVATE},
{"slave", MS_SLAVE},
{"shared", MS_SHARED},
{"lazytime", MS_LAZYTIME},
{"defaults", 0},
};
@ -521,6 +526,24 @@ std::vector<FstabPtrEntryType*> GetEntriesByPred(FstabPtr fstab, const Pred& pre
} // namespace
// Return the path to the recovery fstab file. There may be multiple fstab files;
// the one that is returned will be the first that exists of recovery.fstab.<fstab_suffix>,
// recovery.fstab.<hardware>, and recovery.fstab.<hardware.platform>.
std::string GetRecoveryFstabPath() {
for (const char* prop : {"fstab_suffix", "hardware", "hardware.platform"}) {
std::string suffix;
if (!fs_mgr_get_boot_config(prop, &suffix)) continue;
std::string fstab_path = "/etc/recovery.fstab." + suffix;
if (access(fstab_path.c_str(), F_OK) == 0) {
return fstab_path;
}
}
return "/etc/recovery.fstab";
}
// Return the path to the fstab file. There may be multiple fstab files; the
// one that is returned will be the first that exists of fstab.<fstab_suffix>,
// fstab.<hardware>, and fstab.<hardware.platform>. The fstab is searched for
@ -530,7 +553,7 @@ std::vector<FstabPtrEntryType*> GetEntriesByPred(FstabPtr fstab, const Pred& pre
// the system/etc directory is supported too and is the preferred location.
std::string GetFstabPath() {
if (InRecovery()) {
return "/etc/recovery.fstab";
return GetRecoveryFstabPath();
}
for (const char* prop : {"fstab_suffix", "hardware", "hardware.platform"}) {
std::string suffix;

View file

@ -39,9 +39,6 @@ std::string GetFstabPath();
void ImportBootconfigFromString(const std::string& bootconfig,
const std::function<void(std::string, std::string)>& fn);
bool GetBootconfigFromString(const std::string& bootconfig, const std::string& key,
std::string* out);
void ImportKernelCmdlineFromString(const std::string& cmdline,
const std::function<void(std::string, std::string)>& fn);

View file

@ -154,5 +154,8 @@ bool GetKernelCmdline(const std::string& key, std::string* out);
// Return the "other" slot for the given slot suffix.
std::string OtherSlotSuffix(const std::string& suffix);
bool GetBootconfigFromString(const std::string& bootconfig, const std::string& key,
std::string* out);
} // namespace fs_mgr
} // namespace android

View file

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

View file

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

View file

@ -13,6 +13,7 @@
// limitations under the License.
#include "device_info.h"
#include "scratch_super.h"
#include <android-base/logging.h>
#include <fs_mgr.h>
@ -37,8 +38,24 @@ constexpr bool kIsRecovery = true;
constexpr bool kIsRecovery = false;
#endif
DeviceInfo::DeviceInfo() {
std::string scratch_device = android::snapshot::GetScratchOtaMetadataPartition();
if (!scratch_device.empty()) {
std::string scratch_metadata =
android::snapshot::MapScratchOtaMetadataPartition(scratch_device);
if (!scratch_metadata.empty()) {
SetMetadataDir(scratch_metadata);
SetTempMetadata();
}
}
}
std::string DeviceInfo::GetMetadataDir() const {
return "/metadata/ota"s;
return metadata_dir_;
}
void DeviceInfo::SetMetadataDir(const std::string& value) {
metadata_dir_ = value;
}
std::string DeviceInfo::GetSlotSuffix() const {
@ -104,6 +121,24 @@ bool DeviceInfo::IsFirstStageInit() const {
return first_stage_init_;
}
bool DeviceInfo::SetActiveBootSlot([[maybe_unused]] unsigned int slot) {
#ifdef LIBSNAPSHOT_USE_HAL
if (!EnsureBootHal()) {
return false;
}
CommandResult result = boot_control_->SetActiveBootSlot(slot);
if (!result.success) {
LOG(ERROR) << "Error setting slot " << slot << " active: " << result.errMsg;
return false;
}
return true;
#else
LOG(ERROR) << "HAL support not enabled.";
return false;
#endif
}
bool DeviceInfo::SetSlotAsUnbootable([[maybe_unused]] unsigned int slot) {
#ifdef LIBSNAPSHOT_USE_HAL
if (!EnsureBootHal()) {

View file

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

View file

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

View file

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

View file

@ -30,6 +30,8 @@
#include <storage_literals/storage_literals.h>
#include <update_engine/update_metadata.pb.h>
#include "utility.h"
namespace android {
namespace snapshot {
@ -77,7 +79,7 @@ class TestDeviceInfo : public SnapshotManager::IDeviceInfo {
: TestDeviceInfo(fake_super) {
set_slot_suffix(slot_suffix);
}
std::string GetMetadataDir() const override { return "/metadata/ota/test"s; }
std::string GetMetadataDir() const override { return metadata_dir_; }
std::string GetSlotSuffix() const override { return slot_suffix_; }
std::string GetOtherSlotSuffix() const override { return slot_suffix_ == "_a" ? "_b" : "_a"; }
std::string GetSuperDevice([[maybe_unused]] uint32_t slot) const override { return "super"; }
@ -90,6 +92,7 @@ class TestDeviceInfo : public SnapshotManager::IDeviceInfo {
}
bool IsOverlayfsSetup() const override { return false; }
bool IsRecovery() const override { return recovery_; }
bool SetActiveBootSlot([[maybe_unused]] unsigned int slot) override { return true; }
bool SetSlotAsUnbootable(unsigned int slot) override {
unbootable_slots_.insert(slot);
return true;
@ -117,6 +120,7 @@ class TestDeviceInfo : public SnapshotManager::IDeviceInfo {
void set_dm(android::dm::IDeviceMapper* dm) { dm_ = dm; }
MergeStatus merge_status() const { return merge_status_; }
bool IsTempMetadata() const override { return temp_metadata_; }
private:
std::string slot_suffix_ = "_a";
@ -126,6 +130,8 @@ class TestDeviceInfo : public SnapshotManager::IDeviceInfo {
bool first_stage_init_ = false;
std::unordered_set<uint32_t> unbootable_slots_;
android::dm::IDeviceMapper* dm_ = nullptr;
std::string metadata_dir_ = "/metadata/ota/test";
bool temp_metadata_ = false;
};
class DeviceMapperWrapper : public android::dm::IDeviceMapper {
@ -234,5 +240,21 @@ bool IsVirtualAbEnabled();
#define RETURN_IF_NON_VIRTUAL_AB() RETURN_IF_NON_VIRTUAL_AB_MSG("")
#define SKIP_IF_VENDOR_ON_ANDROID_S() \
do { \
if (IsVendorFromAndroid12()) \
GTEST_SKIP() << "Skip test as Vendor partition is on Android S"; \
} while (0)
#define RETURN_IF_VENDOR_ON_ANDROID_S_MSG(msg) \
do { \
if (IsVendorFromAndroid12()) { \
std::cerr << (msg); \
return; \
} \
} while (0)
#define RETURN_IF_VENDOR_ON_ANDROID_S() RETURN_IF_VENDOR_ON_ANDROID_S_MSG("")
} // namespace snapshot
} // namespace android

View file

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

View file

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

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/xattr.h>
#include <chrono>
#include <filesystem>
#include <optional>
#include <thread>
@ -47,6 +48,7 @@
#include <libsnapshot/snapshot_stats.h>
#include "device_info.h"
#include "partition_cow_creator.h"
#include "scratch_super.h"
#include "snapshot_metadata_updater.h"
#include "utility.h"
@ -116,7 +118,11 @@ std::unique_ptr<SnapshotManager> SnapshotManager::New(IDeviceInfo* info) {
info = new DeviceInfo();
}
return std::unique_ptr<SnapshotManager>(new SnapshotManager(info));
auto sm = std::unique_ptr<SnapshotManager>(new SnapshotManager(info));
if (info->IsTempMetadata()) {
LOG(INFO) << "Using temp metadata from super";
}
return sm;
}
std::unique_ptr<SnapshotManager> SnapshotManager::NewForFirstStageMount(IDeviceInfo* info) {
@ -1109,6 +1115,13 @@ UpdateState SnapshotManager::ProcessUpdateState(const std::function<bool()>& cal
if (result.state == UpdateState::MergeFailed) {
AcknowledgeMergeFailure(result.failure_code);
}
if (result.state == UpdateState::MergeCompleted) {
if (device_->IsTempMetadata()) {
CleanupScratchOtaMetadataIfPresent();
}
}
if (result.state != UpdateState::Merging) {
// Either there is no merge, or the merge was finished, so no need
// to keep waiting.
@ -1708,6 +1721,10 @@ bool SnapshotManager::PerformInitTransition(InitTransition transition,
if (UpdateUsesODirect(lock.get())) {
snapuserd_argv->emplace_back("-o_direct");
}
uint cow_op_merge_size = GetUpdateCowOpMergeSize(lock.get());
if (cow_op_merge_size != 0) {
snapuserd_argv->emplace_back("-cow_op_merge_size=" + std::to_string(cow_op_merge_size));
}
}
size_t num_cows = 0;
@ -2130,6 +2147,11 @@ bool SnapshotManager::UpdateUsesODirect(LockedFile* lock) {
return update_status.o_direct();
}
uint32_t SnapshotManager::GetUpdateCowOpMergeSize(LockedFile* lock) {
SnapshotUpdateStatus update_status = ReadSnapshotUpdateStatus(lock);
return update_status.cow_op_merge_size();
}
bool SnapshotManager::MarkSnapuserdFromSystem() {
auto path = GetSnapuserdFromSystemPath();
@ -2300,7 +2322,27 @@ bool SnapshotManager::ListSnapshots(LockedFile* lock, std::vector<std::string>*
}
bool SnapshotManager::IsSnapshotManagerNeeded() {
return access(kBootIndicatorPath, F_OK) == 0;
if (access(kBootIndicatorPath, F_OK) == 0) {
return true;
}
if (IsScratchOtaMetadataOnSuper()) {
return true;
}
return false;
}
bool SnapshotManager::MapTempOtaMetadataPartitionIfNeeded(
const std::function<bool(const std::string&)>& init) {
auto device = android::snapshot::GetScratchOtaMetadataPartition();
if (!device.empty()) {
init(device);
if (android::snapshot::MapScratchOtaMetadataPartition(device).empty()) {
return false;
}
}
return true;
}
std::string SnapshotManager::GetGlobalRollbackIndicatorPath() {
@ -2387,6 +2429,12 @@ bool SnapshotManager::MapAllPartitions(LockedFile* lock, const std::string& supe
continue;
}
if (GetPartitionName(partition) ==
android::base::Basename(android::snapshot::kOtaMetadataMount)) {
LOG(INFO) << "Partition: " << GetPartitionName(partition) << " skipping";
continue;
}
CreateLogicalPartitionParams params = {
.block_device = super_device,
.metadata = metadata.get(),
@ -2892,10 +2940,12 @@ bool SnapshotManager::UnmapAllSnapshots() {
}
bool SnapshotManager::UnmapAllSnapshots(LockedFile* lock) {
LOG(INFO) << "Lock acquired for " << __FUNCTION__;
std::vector<std::string> snapshots;
if (!ListSnapshots(lock, &snapshots)) {
return false;
}
LOG(INFO) << "Found " << snapshots.size() << " partitions with snapshots";
for (const auto& snapshot : snapshots) {
if (!UnmapPartitionWithSnapshot(lock, snapshot)) {
@ -2903,6 +2953,7 @@ bool SnapshotManager::UnmapAllSnapshots(LockedFile* lock) {
return false;
}
}
LOG(INFO) << "Unmapped " << snapshots.size() << " partitions with snapshots";
// Terminate the daemon and release the snapuserd_client_ object.
// If we need to re-connect with the daemon, EnsureSnapuserdConnected()
@ -2918,6 +2969,7 @@ bool SnapshotManager::UnmapAllSnapshots(LockedFile* lock) {
auto SnapshotManager::OpenFile(const std::string& file,
int lock_flags) -> std::unique_ptr<LockedFile> {
const auto start = std::chrono::system_clock::now();
unique_fd fd(open(file.c_str(), O_RDONLY | O_CLOEXEC | O_NOFOLLOW));
if (fd < 0) {
PLOG(ERROR) << "Open failed: " << file;
@ -2930,6 +2982,11 @@ auto SnapshotManager::OpenFile(const std::string& file,
// For simplicity, we want to CHECK that lock_mode == LOCK_EX, in some
// calls, so strip extra flags.
int lock_mode = lock_flags & (LOCK_EX | LOCK_SH);
const auto end = std::chrono::system_clock::now();
const auto duration_ms = std::chrono::duration_cast<std::chrono::milliseconds>(end - start);
if (duration_ms >= 1000ms) {
LOG(INFO) << "Taking lock on " << file << " took " << duration_ms.count() << "ms";
}
return std::make_unique<LockedFile>(file, std::move(fd), lock_mode);
}
@ -3082,6 +3139,7 @@ bool SnapshotManager::WriteUpdateState(LockedFile* lock, UpdateState state,
status.set_io_uring_enabled(old_status.io_uring_enabled());
status.set_legacy_snapuserd(old_status.legacy_snapuserd());
status.set_o_direct(old_status.o_direct());
status.set_cow_op_merge_size(old_status.cow_op_merge_size());
}
return WriteSnapshotUpdateStatus(lock, status);
}
@ -3464,6 +3522,8 @@ Return SnapshotManager::CreateUpdateSnapshots(const DeltaArchiveManifest& manife
status.set_legacy_snapuserd(true);
LOG(INFO) << "Setting legacy_snapuserd to true";
}
status.set_cow_op_merge_size(
android::base::GetUintProperty<uint32_t>("ro.virtual_ab.cow_op_merge_size", 0));
} else if (legacy_compression) {
LOG(INFO) << "Virtual A/B using legacy snapuserd";
} else {
@ -3899,6 +3959,7 @@ bool SnapshotManager::Dump(std::ostream& os) {
ss << "Using userspace snapshots: " << update_status.userspace_snapshots() << std::endl;
ss << "Using io_uring: " << update_status.io_uring_enabled() << std::endl;
ss << "Using o_direct: " << update_status.o_direct() << std::endl;
ss << "Cow op merge size (0 for uncapped): " << update_status.cow_op_merge_size() << std::endl;
ss << "Using XOR compression: " << GetXorCompressionEnabledProperty() << std::endl;
ss << "Current slot: " << device_->GetSlotSuffix() << std::endl;
ss << "Boot indicator: booting from " << GetCurrentSlot() << " slot" << std::endl;
@ -3982,44 +4043,90 @@ bool SnapshotManager::HandleImminentDataWipe(const std::function<void()>& callba
// We allow the wipe to continue, because if we can't mount /metadata,
// it is unlikely the device would have booted anyway. If there is no
// metadata partition, then the device predates Virtual A/B.
LOG(INFO) << "/metadata not found; allowing wipe.";
return true;
}
// Check this early, so we don't accidentally start trying to populate
// the state file in recovery. Note we don't call GetUpdateState since
// we want errors in acquiring the lock to be propagated, instead of
// returning UpdateState::None.
auto state_file = GetStateFilePath();
if (access(state_file.c_str(), F_OK) != 0 && errno == ENOENT) {
return true;
}
auto slot_number = SlotNumberForSlotSuffix(device_->GetSlotSuffix());
auto super_path = device_->GetSuperDevice(slot_number);
if (!CreateLogicalAndSnapshotPartitions(super_path, 20s)) {
LOG(ERROR) << "Unable to map partitions to complete merge.";
return false;
}
auto process_callback = [&]() -> bool {
if (callback) {
callback();
// This could happen if /metadata mounted but there is no filesystem
// structure. Weird, but we have to assume there's no OTA pending, and
// thus we let the wipe proceed.
UpdateState state;
{
auto lock = LockExclusive();
if (!lock) {
LOG(ERROR) << "Unable to determine update state; allowing wipe.";
return true;
}
return true;
};
in_factory_data_reset_ = true;
UpdateState state =
ProcessUpdateStateOnDataWipe(true /* allow_forward_merge */, process_callback);
in_factory_data_reset_ = false;
if (state == UpdateState::MergeFailed) {
return false;
state = ReadUpdateState(lock.get());
LOG(INFO) << "Update state before wipe: " << state << "; slot: " << GetCurrentSlot()
<< "; suffix: " << device_->GetSlotSuffix();
}
// Nothing should be depending on partitions now, so unmap them all.
if (!UnmapAllPartitionsInRecovery()) {
LOG(ERROR) << "Unable to unmap all partitions; fastboot may fail to flash.";
bool try_merge = false;
switch (state) {
case UpdateState::None:
case UpdateState::Initiated:
LOG(INFO) << "Wipe is not impacted by update state; allowing wipe.";
break;
case UpdateState::Unverified:
if (GetCurrentSlot() != Slot::Target) {
LOG(INFO) << "Wipe is not impacted by rolled back update; allowing wipe";
break;
}
if (!HasForwardMergeIndicator()) {
auto slot_number = SlotNumberForSlotSuffix(device_->GetSlotSuffix());
auto other_slot_number = SlotNumberForSlotSuffix(device_->GetOtherSlotSuffix());
// We're not allowed to forward merge, so forcefully rollback the
// slot switch.
LOG(INFO) << "Allowing wipe due to lack of forward merge indicator; reverting to "
"old slot since update will be deleted.";
device_->SetSlotAsUnbootable(slot_number);
device_->SetActiveBootSlot(other_slot_number);
break;
}
// Forward merge indicator means we have to mount snapshots and try to merge.
LOG(INFO) << "Forward merge indicator is present.";
try_merge = true;
break;
case UpdateState::Merging:
case UpdateState::MergeFailed:
try_merge = true;
break;
case UpdateState::MergeNeedsReboot:
case UpdateState::Cancelled:
LOG(INFO) << "Unexpected update state in recovery; allowing wipe.";
break;
default:
break;
}
if (try_merge) {
auto slot_number = SlotNumberForSlotSuffix(device_->GetSlotSuffix());
auto super_path = device_->GetSuperDevice(slot_number);
if (!CreateLogicalAndSnapshotPartitions(super_path, 20s)) {
LOG(ERROR) << "Unable to map partitions to complete merge.";
return false;
}
auto process_callback = [&]() -> bool {
if (callback) {
callback();
}
return true;
};
state = ProcessUpdateStateOnDataWipe(process_callback);
if (state == UpdateState::MergeFailed) {
return false;
}
// Nothing should be depending on partitions now, so unmap them all.
if (!UnmapAllPartitionsInRecovery()) {
LOG(ERROR) << "Unable to unmap all partitions; fastboot may fail to flash.";
}
}
if (state != UpdateState::None) {
@ -4065,58 +4172,40 @@ bool SnapshotManager::FinishMergeInRecovery() {
return true;
}
UpdateState SnapshotManager::ProcessUpdateStateOnDataWipe(bool allow_forward_merge,
const std::function<bool()>& callback) {
auto slot_number = SlotNumberForSlotSuffix(device_->GetSlotSuffix());
UpdateState state = ProcessUpdateState(callback);
LOG(INFO) << "Update state in recovery: " << state;
switch (state) {
case UpdateState::MergeFailed:
LOG(ERROR) << "Unrecoverable merge failure detected.";
return state;
case UpdateState::Unverified: {
// If an OTA was just applied but has not yet started merging:
//
// - if forward merge is allowed, initiate merge and call
// ProcessUpdateState again.
//
// - if forward merge is not allowed, we
// have no choice but to revert slots, because the current slot will
// immediately become unbootable. Rather than wait for the device
// to reboot N times until a rollback, we proactively disable the
// new slot instead.
//
// Since the rollback is inevitable, we don't treat a HAL failure
// as an error here.
auto slot = GetCurrentSlot();
if (slot == Slot::Target) {
if (allow_forward_merge &&
access(GetForwardMergeIndicatorPath().c_str(), F_OK) == 0) {
LOG(INFO) << "Forward merge allowed, initiating merge now.";
if (!InitiateMerge()) {
LOG(ERROR) << "Failed to initiate merge on data wipe.";
return UpdateState::MergeFailed;
}
return ProcessUpdateStateOnDataWipe(false /* allow_forward_merge */, callback);
UpdateState SnapshotManager::ProcessUpdateStateOnDataWipe(const std::function<bool()>& callback) {
while (true) {
UpdateState state = ProcessUpdateState(callback);
LOG(INFO) << "Processed updated state in recovery: " << state;
switch (state) {
case UpdateState::MergeFailed:
LOG(ERROR) << "Unrecoverable merge failure detected.";
return state;
case UpdateState::Unverified: {
// Unverified was already handled earlier, in HandleImminentDataWipe,
// but it will fall through here if a forward merge is required.
//
// If InitiateMerge fails, we early return. If it succeeds, then we
// are guaranteed that the next call to ProcessUpdateState will not
// return Unverified.
if (!InitiateMerge()) {
LOG(ERROR) << "Failed to initiate merge on data wipe.";
return UpdateState::MergeFailed;
}
LOG(ERROR) << "Reverting to old slot since update will be deleted.";
device_->SetSlotAsUnbootable(slot_number);
} else {
LOG(INFO) << "Booting from " << slot << " slot, no action is taken.";
continue;
}
break;
case UpdateState::MergeNeedsReboot:
// We shouldn't get here, because nothing is depending on
// logical partitions.
LOG(ERROR) << "Unexpected merge-needs-reboot state in recovery.";
return state;
default:
return state;
}
case UpdateState::MergeNeedsReboot:
// We shouldn't get here, because nothing is depending on
// logical partitions.
LOG(ERROR) << "Unexpected merge-needs-reboot state in recovery.";
break;
default:
break;
}
return state;
}
bool SnapshotManager::HasForwardMergeIndicator() {
return access(GetForwardMergeIndicatorPath().c_str(), F_OK) == 0;
}
bool SnapshotManager::EnsureNoOverflowSnapshot(LockedFile* lock) {

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -27,13 +27,17 @@ namespace snapshot {
class BufferSink final {
public:
void Initialize(size_t size);
// Do not reserve any space of header by default
void Initialize(size_t size) { return Initialize(0, size); };
// This allows to set const header_size_ to be used if caller needs it
// for example, while working with dm_user
void Initialize(size_t header_size, size_t size);
void* GetBufPtr() { return buffer_.get(); }
void Clear() { memset(GetBufPtr(), 0, buffer_size_); }
void* GetPayloadBuffer(size_t size);
void* GetBuffer(size_t requested, size_t* actual);
void UpdateBufferOffset(size_t size) { buffer_offset_ += size; }
struct dm_user_header* GetHeaderPtr();
void* GetHeaderPtr();
void ResetBufferOffset() { buffer_offset_ = 0; }
void* GetPayloadBufPtr();
loff_t GetPayloadBytesWritten() { return buffer_offset_; }
@ -56,6 +60,7 @@ class BufferSink final {
std::unique_ptr<uint8_t[]> buffer_;
loff_t buffer_offset_;
size_t buffer_size_;
size_t header_size_;
};
} // namespace snapshot

View file

@ -92,15 +92,5 @@ struct dm_user_header {
__u64 len;
} __attribute__((packed));
struct dm_user_payload {
__u8 buf[];
};
// Message comprising both header and payload
struct dm_user_message {
struct dm_user_header header;
struct dm_user_payload payload;
};
} // namespace snapshot
} // namespace android

View file

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

View file

@ -35,6 +35,7 @@
#include <android-base/properties.h>
#include <android-base/strings.h>
#include <fs_mgr/file_wait.h>
#include <libdm/dm.h>
#include <snapuserd/snapuserd_client.h>
namespace android {
@ -333,7 +334,21 @@ bool SnapuserdClient::QueryUpdateVerification() {
}
std::string SnapuserdClient::GetDaemonAliveIndicatorPath() {
return "/metadata/ota/" + std::string(kDaemonAliveIndicator);
std::string metadata_dir;
std::string temp_metadata_mnt = "/mnt/scratch_ota_metadata_super";
auto& dm = ::android::dm::DeviceMapper::Instance();
auto partition_name = android::base::Basename(temp_metadata_mnt);
bool invalid_partition = (dm.GetState(partition_name) == dm::DmDeviceState::INVALID);
std::string temp_device;
if (!invalid_partition && dm.GetDmDevicePathByName(partition_name, &temp_device)) {
metadata_dir = temp_metadata_mnt + "/" + "ota/";
} else {
metadata_dir = "/metadata/ota/";
}
return metadata_dir + std::string(kDaemonAliveIndicator);
}
bool SnapuserdClient::IsTransitionedDaemonReady() {

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

View file

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

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

View file

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

View file

@ -17,6 +17,8 @@
#include <libsnapshot/cow_format.h>
#include <pthread.h>
#include <android-base/properties.h>
#include "merge_worker.h"
#include "snapuserd_core.h"
#include "utility.h"
@ -30,12 +32,18 @@ using android::base::unique_fd;
MergeWorker::MergeWorker(const std::string& cow_device, const std::string& misc_name,
const std::string& base_path_merge,
std::shared_ptr<SnapshotHandler> snapuserd)
: Worker(cow_device, misc_name, base_path_merge, snapuserd) {}
std::shared_ptr<SnapshotHandler> snapuserd, uint32_t cow_op_merge_size)
: Worker(cow_device, misc_name, base_path_merge, snapuserd),
cow_op_merge_size_(cow_op_merge_size) {}
int MergeWorker::PrepareMerge(uint64_t* source_offset, int* pending_ops,
std::vector<const CowOperation*>* replace_zero_vec) {
int num_ops = *pending_ops;
// 0 indicates ro.virtual_ab.cow_op_merge_size was not set in the build
if (cow_op_merge_size_ != 0) {
num_ops = std::min(cow_op_merge_size_, static_cast<uint32_t>(*pending_ops));
}
int nr_consecutive = 0;
bool checkOrderedOp = (replace_zero_vec == nullptr);
size_t num_blocks = 1;
@ -179,8 +187,8 @@ bool MergeWorker::MergeReplaceZeroOps() {
bufsink_.ResetBufferOffset();
if (snapuserd_->IsIOTerminated()) {
SNAP_LOG(ERROR)
<< "MergeReplaceZeroOps: MergeWorker threads terminated - shutting down merge";
SNAP_LOG(ERROR) << "MergeReplaceZeroOps: MergeWorker threads terminated - shutting "
"down merge";
return false;
}
}
@ -577,8 +585,10 @@ bool MergeWorker::Run() {
SNAP_LOG(ERROR) << "Merge terminated early...";
return true;
}
auto merge_thread_priority = android::base::GetUintProperty<uint32_t>(
"ro.virtual_ab.merge_thread_priority", ANDROID_PRIORITY_BACKGROUND);
if (!SetThreadPriority(ANDROID_PRIORITY_BACKGROUND)) {
if (!SetThreadPriority(merge_thread_priority)) {
SNAP_PLOG(ERROR) << "Failed to set thread priority";
}

View file

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

View file

@ -14,11 +14,14 @@
* limitations under the License.
*/
#include <android-base/properties.h>
#include <libsnapshot/cow_format.h>
#include <pthread.h>
#include "read_worker.h"
#include "snapuserd_core.h"
#include "user-space-merge/worker.h"
#include "utility.h"
namespace android {
@ -259,8 +262,10 @@ bool ReadWorker::Run() {
SNAP_LOG(INFO) << "Processing snapshot I/O requests....";
pthread_setname_np(pthread_self(), "ReadWorker");
auto worker_thread_priority = android::base::GetUintProperty<uint32_t>(
"ro.virtual_ab.worker_thread_priority", ANDROID_PRIORITY_NORMAL);
if (!SetThreadPriority(ANDROID_PRIORITY_NORMAL)) {
if (!SetThreadPriority(worker_thread_priority)) {
SNAP_PLOG(ERROR) << "Failed to set thread priority";
}

View file

@ -57,7 +57,7 @@ class ReadWorker : public Worker, public IBlockServer::Delegate {
bool ReadFromSourceDevice(const CowOperation* cow_op, void* buffer);
bool ReadDataFromBaseDevice(sector_t sector, void* buffer, size_t read_size);
constexpr bool IsBlockAligned(size_t size) { return ((size & (BLOCK_SZ - 1)) == 0); }
constexpr bool IsBlockAligned(uint64_t size) { return ((size & (BLOCK_SZ - 1)) == 0); }
constexpr sector_t ChunkToSector(chunk_t chunk) { return chunk << CHUNK_SHIFT; }
constexpr chunk_t SectorToChunk(sector_t sector) { return sector >> CHUNK_SHIFT; }

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -87,7 +87,8 @@ class UserSnapshotServer {
const std::string& cow_device_path,
const std::string& backing_device,
const std::string& base_path_merge,
bool o_direct = false);
bool o_direct = false,
uint32_t cow_op_merge_size = 0);
bool StartHandler(const std::string& misc_name);
void SetTerminating() { terminating_ = true; }

View file

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

View file

@ -14,11 +14,14 @@
#include "utility.h"
#include <android-base/properties.h>
#include <sys/resource.h>
#include <sys/utsname.h>
#include <unistd.h>
#include <android-base/file.h>
#include <android-base/logging.h>
#include <libdm/dm.h>
#include <processgroup/processgroup.h>
#include <private/android_filesystem_config.h>
@ -27,6 +30,7 @@ namespace android {
namespace snapshot {
using android::base::unique_fd;
using android::dm::DeviceMapper;
bool SetThreadPriority([[maybe_unused]] int priority) {
#ifdef __ANDROID__
@ -61,5 +65,38 @@ bool KernelSupportsIoUring() {
return major > 5 || (major == 5 && minor >= 6);
}
bool GetUserspaceSnapshotsEnabledProperty() {
return android::base::GetBoolProperty("ro.virtual_ab.userspace.snapshots.enabled", false);
}
bool KernelSupportsCompressedSnapshots() {
auto& dm = DeviceMapper::Instance();
return dm.GetTargetByName("user", nullptr);
}
bool IsVendorFromAndroid12() {
const std::string UNKNOWN = "unknown";
const std::string vendor_release =
android::base::GetProperty("ro.vendor.build.version.release_or_codename", UNKNOWN);
if (vendor_release.find("12") != std::string::npos) {
return true;
}
return false;
}
bool CanUseUserspaceSnapshots() {
if (!GetUserspaceSnapshotsEnabledProperty()) {
LOG(INFO) << "Virtual A/B - Userspace snapshots disabled";
return false;
}
if (!KernelSupportsCompressedSnapshots()) {
LOG(ERROR) << "Userspace snapshots requested, but no kernel support is available.";
return false;
}
return true;
}
} // namespace snapshot
} // namespace android

View file

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

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

View file

@ -37,6 +37,10 @@
using namespace android::fs_mgr;
using namespace testing;
#if !defined(MS_LAZYTIME)
#define MS_LAZYTIME (1 << 25)
#endif
namespace {
const std::string cmdline =
@ -329,6 +333,7 @@ TEST(fs_mgr, fs_mgr_read_fstab_file_proc_mounts) {
{"private", MS_PRIVATE},
{"slave", MS_SLAVE},
{"shared", MS_SHARED},
{"lazytime", MS_LAZYTIME},
{"defaults", 0},
{0, 0},
};
@ -1062,23 +1067,6 @@ TEST(fs_mgr, DefaultFstabContainsUserdata) {
<< "Default fstab doesn't contain /data entry";
}
TEST(fs_mgr, UserdataMountedFromDefaultFstab) {
if (getuid() != 0) {
GTEST_SKIP() << "Must be run as root.";
return;
}
Fstab fstab;
ASSERT_TRUE(ReadDefaultFstab(&fstab)) << "Failed to read default fstab";
Fstab proc_mounts;
ASSERT_TRUE(ReadFstabFromFile("/proc/mounts", &proc_mounts)) << "Failed to read /proc/mounts";
auto mounted_entry = GetEntryForMountPoint(&proc_mounts, "/data");
ASSERT_NE(mounted_entry, nullptr) << "/data is not mounted";
std::string block_device;
ASSERT_TRUE(android::base::Realpath(mounted_entry->blk_device, &block_device));
ASSERT_NE(nullptr, fs_mgr_get_mounted_entry_for_userdata(&fstab, block_device))
<< "/data wasn't mounted from default fstab";
}
TEST(fs_mgr, ReadFstabFromFile_FsMgrOptions_Readahead_Size_KB) {
TemporaryFile tf;
ASSERT_TRUE(tf.fd != -1);

View file

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

View file

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

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.
5. `post-fs-data` - Mount and configure `/data`; set up encryption. `/metadata` is
reformatted here if it couldn't mount in first-stage init.
6. `zygote-start` - Start the zygote.
7. `early-boot` - After zygote has started.
8. `boot` - After `early-boot` actions have completed.
6. `post-fs-data-checkpointed` - Triggered when vold has completed committing a checkpoint
after an OTA update. Not triggered if checkpointing is not needed or supported.
7. `bpf-progs-loaded` - Starts things that want to start ASAP but need eBPF (incl. netd)
8. `zygote-start` - Start the zygote.
9. `early-boot` - After zygote has started.
10. `boot` - After `early-boot` actions have completed.
Commands
--------
@ -634,7 +637,7 @@ provides the `aidl_lazy_test_1` interface.
Properties are expanded within _level_.
`mark_post_data`
> Used to mark the point right after /data is mounted.
> (This action is deprecated and no-op.)
`mkdir <path> [<mode>] [<owner>] [<group>] [encryption=<action>] [key=<key>]`
> Create a directory at _path_, optionally with the given mode, owner, and
@ -743,6 +746,9 @@ provides the `aidl_lazy_test_1` interface.
fstab.${ro.hardware} or fstab.${ro.hardware.platform} will be scanned for
under /odm/etc, /vendor/etc, or / at runtime, in that order.
`swapoff <path>`
> Stops swapping to the file or block device specified by path.
`symlink <target> <path>`
> Create a symbolic link at _path_ with the value _target_
@ -786,7 +792,6 @@ provides the `aidl_lazy_test_1` interface.
If the file does not exist, it will be created. If it does exist,
it will be truncated. Properties are expanded within _content_.
Imports
-------
`import <path>`

View file

@ -139,6 +139,10 @@ bool BlockDevInitializer::InitPlatformDevice(const std::string& dev_name) {
return InitDevice("/sys/devices/platform", dev_name);
}
bool BlockDevInitializer::InitHvcDevice(const std::string& dev_name) {
return InitDevice("/sys/devices/virtual/tty", dev_name);
}
bool BlockDevInitializer::InitDevice(const std::string& syspath, const std::string& device_name) {
bool found = false;

View file

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

View file

@ -36,6 +36,7 @@
#include <sys/resource.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <sys/swap.h>
#include <sys/syscall.h>
#include <sys/system_properties.h>
#include <sys/time.h>
@ -46,7 +47,6 @@
#include <map>
#include <memory>
#include <InitProperties.sysprop.h>
#include <android-base/chrono_utils.h>
#include <android-base/file.h>
#include <android-base/logging.h>
@ -609,8 +609,6 @@ static Result<void> queue_fs_event(int code) {
return Error() << "Invalid code: " << code;
}
static int initial_mount_fstab_return_code = -1;
/* <= Q: mount_all <fstab> [ <path> ]* [--<options>]*
* >= R: mount_all [ <fstab> ] [--<options>]*
*
@ -651,19 +649,10 @@ static Result<void> do_mount_all(const BuiltinArguments& args) {
import_late(mount_all->rc_paths);
}
if (mount_fstab_result.userdata_mounted) {
// This call to fs_mgr_mount_all mounted userdata. Keep the result in
// order for userspace reboot to correctly remount userdata.
LOG(INFO) << "Userdata mounted using "
<< (mount_all->fstab_path.empty() ? "(default fstab)" : mount_all->fstab_path)
<< " result : " << mount_fstab_result.code;
initial_mount_fstab_return_code = mount_fstab_result.code;
}
if (queue_event) {
/* queue_fs_event will queue event based on mount_fstab return code
* and return processed return code*/
auto queue_fs_result = queue_fs_event(mount_fstab_result.code);
auto queue_fs_result = queue_fs_event(mount_fstab_result);
if (!queue_fs_result.ok()) {
return Error() << "queue_fs_event() failed: " << queue_fs_result.error();
}
@ -1151,29 +1140,19 @@ static Result<void> ExecWithFunctionOnFailure(const std::vector<std::string>& ar
}
static Result<void> ExecVdcRebootOnFailure(const std::string& vdc_arg) {
bool should_reboot_into_recovery = true;
auto reboot_reason = vdc_arg + "_failed";
if (android::sysprop::InitProperties::userspace_reboot_in_progress().value_or(false)) {
should_reboot_into_recovery = false;
reboot_reason = "userspace_failed," + vdc_arg;
}
auto reboot = [reboot_reason, should_reboot_into_recovery](const std::string& message) {
auto reboot = [reboot_reason](const std::string& message) {
// TODO (b/122850122): support this in gsi
if (should_reboot_into_recovery) {
if (IsFbeEnabled() && !android::gsi::IsGsiRunning()) {
LOG(ERROR) << message << ": Rebooting into recovery, reason: " << reboot_reason;
if (auto result = reboot_into_recovery(
{"--prompt_and_wipe_data", "--reason="s + reboot_reason});
!result.ok()) {
LOG(FATAL) << "Could not reboot into recovery: " << result.error();
}
} else {
LOG(ERROR) << "Failure (reboot suppressed): " << reboot_reason;
if (IsFbeEnabled() && !android::gsi::IsGsiRunning()) {
LOG(ERROR) << message << ": Rebooting into recovery, reason: " << reboot_reason;
if (auto result = reboot_into_recovery(
{"--prompt_and_wipe_data", "--reason="s + reboot_reason});
!result.ok()) {
LOG(FATAL) << "Could not reboot into recovery: " << result.error();
}
} else {
LOG(ERROR) << message << ": rebooting, reason: " << reboot_reason;
trigger_shutdown("reboot," + reboot_reason);
LOG(ERROR) << "Failure (reboot suppressed): " << reboot_reason;
}
};
@ -1181,29 +1160,6 @@ static Result<void> ExecVdcRebootOnFailure(const std::string& vdc_arg) {
return ExecWithFunctionOnFailure(args, reboot);
}
static Result<void> do_remount_userdata(const BuiltinArguments& args) {
if (initial_mount_fstab_return_code == -1) {
return Error() << "Calling remount_userdata too early";
}
Fstab fstab;
if (!ReadDefaultFstab(&fstab)) {
// TODO(b/135984674): should we reboot here?
return Error() << "Failed to read fstab";
}
// TODO(b/135984674): check that fstab contains /data.
if (auto rc = fs_mgr_remount_userdata_into_checkpointing(&fstab); rc < 0) {
std::string proc_mounts_output;
android::base::ReadFileToString("/proc/mounts", &proc_mounts_output, true);
android::base::WriteStringToFile(proc_mounts_output,
"/metadata/userspacereboot/mount_info.txt");
trigger_shutdown("reboot,mount_userdata_failed");
}
if (auto result = queue_fs_event(initial_mount_fstab_return_code); !result.ok()) {
return Error() << "queue_fs_event() failed: " << result.error();
}
return {};
}
static Result<void> do_installkey(const BuiltinArguments& args) {
if (!is_file_crypto()) return {};
@ -1219,8 +1175,7 @@ static Result<void> do_init_user0(const BuiltinArguments& args) {
}
static Result<void> do_mark_post_data(const BuiltinArguments& args) {
ServiceList::GetInstance().MarkPostData();
LOG(INFO) << "deprecated action `mark_post_data` called.";
return {};
}
@ -1319,6 +1274,13 @@ static Result<void> do_enter_default_mount_ns(const BuiltinArguments& args) {
return {};
}
static Result<void> do_swapoff(const BuiltinArguments& args) {
if (!swapoff(args[1].c_str())) {
return ErrnoError() << "swapoff() failed";
}
return {};
}
// Builtin-function-map start
const BuiltinFunctionMap& GetBuiltinFunctionMap() {
constexpr std::size_t kMax = std::numeric_limits<std::size_t>::max();
@ -1364,7 +1326,6 @@ const BuiltinFunctionMap& GetBuiltinFunctionMap() {
{"umount_all", {0, 1, {false, do_umount_all}}},
{"update_linker_config", {0, 0, {false, do_update_linker_config}}},
{"readahead", {1, 2, {true, do_readahead}}},
{"remount_userdata", {0, 0, {false, do_remount_userdata}}},
{"restart", {1, 2, {false, do_restart}}},
{"restorecon", {1, kMax, {true, do_restorecon}}},
{"restorecon_recursive", {1, kMax, {true, do_restorecon_recursive}}},
@ -1375,6 +1336,7 @@ const BuiltinFunctionMap& GetBuiltinFunctionMap() {
{"start", {1, 1, {false, do_start}}},
{"stop", {1, 1, {false, do_stop}}},
{"swapon_all", {0, 1, {false, do_swapon_all}}},
{"swapoff", {1, 1, {false, do_swapoff}}},
{"enter_default_mount_ns", {0, 0, {false, do_enter_default_mount_ns}}},
{"symlink", {2, 2, {true, do_symlink}}},
{"sysclktz", {1, 1, {false, do_sysclktz}}},

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

View file

@ -38,6 +38,7 @@
#include <android-base/logging.h>
#include <android-base/stringprintf.h>
#include <android/avf_cc_flags.h>
#include <fs_mgr.h>
#include <modprobe/modprobe.h>
#include <private/android_filesystem_config.h>
@ -303,6 +304,22 @@ static BootMode GetBootMode(const std::string& cmdline, const std::string& bootc
return BootMode::NORMAL_MODE;
}
static void MaybeResumeFromHibernation(const std::string& bootconfig) {
std::string hibernationResumeDevice;
android::fs_mgr::GetBootconfigFromString(bootconfig, "androidboot.hibernation_resume_device",
&hibernationResumeDevice);
if (!hibernationResumeDevice.empty()) {
android::base::unique_fd fd(open("/sys/power/resume", O_RDWR | O_CLOEXEC));
if (fd >= 0) {
if (!android::base::WriteStringToFd(hibernationResumeDevice, fd)) {
PLOG(ERROR) << "Failed to write to /sys/power/resume";
}
} else {
PLOG(ERROR) << "Failed to open /sys/power/resume";
}
}
}
static std::unique_ptr<FirstStageMount> CreateFirstStageMount(const std::string& cmdline) {
auto ret = FirstStageMount::Create(cmdline);
if (ret.ok()) {
@ -442,6 +459,8 @@ int FirstStageMain(int argc, char** argv) {
<< module_elapse_time.count() << " ms";
}
MaybeResumeFromHibernation(bootconfig);
std::unique_ptr<FirstStageMount> fsm;
bool created_devices = false;

View file

@ -156,6 +156,13 @@ static Result<Fstab> ReadFirstStageFstabAndroid() {
return fstab;
}
static bool IsRequestingMicrodroidVendorPartition(const std::string& cmdline) {
if (virtualization::IsEnableTpuAssignableDeviceFlagEnabled()) {
return access("/proc/device-tree/avf/vendor_hashtree_descriptor_root_digest", F_OK) == 0;
}
return cmdline.find("androidboot.microdroid.mount_vendor=1") != std::string::npos;
}
// Note: this is a temporary solution to avoid blocking devs that depend on /vendor partition in
// Microdroid. For the proper solution the /vendor fstab should probably be defined in the DT.
// TODO(b/285855430): refactor this
@ -166,7 +173,7 @@ static Result<Fstab> ReadFirstStageFstabMicrodroid(const std::string& cmdline) {
if (!ReadDefaultFstab(&fstab)) {
return Error() << "failed to read fstab";
}
if (cmdline.find("androidboot.microdroid.mount_vendor=1") == std::string::npos) {
if (!IsRequestingMicrodroidVendorPartition(cmdline)) {
// We weren't asked to mount /vendor partition, filter it out from the fstab.
auto predicate = [](const auto& entry) { return entry.mount_point == "/vendor"; };
fstab.erase(std::remove_if(fstab.begin(), fstab.end(), predicate), fstab.end());
@ -305,6 +312,11 @@ bool FirstStageMountVBootV2::InitDevices() {
return false;
}
}
if (IsArcvm() && !block_dev_init_.InitHvcDevice("hvc1")) {
return false;
}
return true;
}
@ -366,6 +378,14 @@ bool FirstStageMountVBootV2::CreateLogicalPartitions() {
}
if (SnapshotManager::IsSnapshotManagerNeeded()) {
auto init_devices = [this](const std::string& device) -> bool {
if (android::base::StartsWith(device, "/dev/block/dm-")) {
return block_dev_init_.InitDmDevice(device);
}
return block_dev_init_.InitDevices({device});
};
SnapshotManager::MapTempOtaMetadataPartitionIfNeeded(init_devices);
auto sm = SnapshotManager::NewForFirstStageMount();
if (!sm) {
return false;

View file

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

View file

@ -15,9 +15,7 @@
*/
#include <fuzzer/FuzzedDataProvider.h>
#include <hidl/metadata.h>
#include <import_parser.h>
#include <interface_utils.h>
#include <rlimit_parser.h>
using namespace android;
@ -34,7 +32,6 @@ const std::string kValidPaths[] = {
};
const int32_t kMaxBytes = 256;
const std::string kValidInterfaces = "android.frameworks.vr.composer@2.0::IVrComposerClient";
class InitParserFuzzer {
public:
@ -44,9 +41,6 @@ class InitParserFuzzer {
private:
void InvokeParser();
void InvokeLimitParser();
void InvokeInterfaceUtils();
InterfaceInheritanceHierarchyMap GenerateHierarchyMap();
std::vector<HidlInterfaceMetadata> GenerateInterfaceMetadata();
FuzzedDataProvider fdp_;
};
@ -64,60 +58,6 @@ void InitParserFuzzer::InvokeLimitParser() {
}
}
std::vector<HidlInterfaceMetadata> InitParserFuzzer::GenerateInterfaceMetadata() {
std::vector<HidlInterfaceMetadata> random_interface;
for (size_t idx = 0; idx < fdp_.ConsumeIntegral<size_t>(); ++idx) {
HidlInterfaceMetadata metadata;
metadata.name = fdp_.ConsumeRandomLengthString(kMaxBytes);
for (size_t idx1 = 0; idx1 < fdp_.ConsumeIntegral<size_t>(); ++idx1) {
metadata.inherited.push_back(fdp_.ConsumeRandomLengthString(kMaxBytes));
}
random_interface.push_back(metadata);
}
return random_interface;
}
InterfaceInheritanceHierarchyMap InitParserFuzzer::GenerateHierarchyMap() {
InterfaceInheritanceHierarchyMap result;
std::vector<HidlInterfaceMetadata> random_interface;
if (fdp_.ConsumeBool()) {
random_interface = GenerateInterfaceMetadata();
} else {
random_interface = HidlInterfaceMetadata::all();
}
for (const HidlInterfaceMetadata& iface : random_interface) {
std::set<FQName> inherited_interfaces;
for (const std::string& intf : iface.inherited) {
FQName fqname;
(void)fqname.setTo(intf);
inherited_interfaces.insert(fqname);
}
FQName fqname;
(void)fqname.setTo(iface.name);
result[fqname] = inherited_interfaces;
}
return result;
}
void InitParserFuzzer::InvokeInterfaceUtils() {
InterfaceInheritanceHierarchyMap hierarchy_map = GenerateHierarchyMap();
SetKnownInterfaces(hierarchy_map);
IsKnownInterface(fdp_.ConsumeRandomLengthString(kMaxBytes));
std::set<std::string> interface_set;
for (size_t idx = 0; idx < fdp_.ConsumeIntegral<size_t>(); ++idx) {
auto set_interface_values = fdp_.PickValueInArray<const std::function<void()>>({
[&]() {
interface_set.insert(("aidl/" + fdp_.ConsumeRandomLengthString(kMaxBytes)));
},
[&]() { interface_set.insert(fdp_.ConsumeRandomLengthString(kMaxBytes)); },
[&]() { interface_set.insert(kValidInterfaces); },
});
set_interface_values();
}
CheckInterfaceInheritanceHierarchy(interface_set, hierarchy_map);
}
void InitParserFuzzer::InvokeParser() {
Parser parser;
std::string name = fdp_.ConsumeBool() ? fdp_.ConsumeRandomLengthString(kMaxBytes) : "import";
@ -132,7 +72,6 @@ void InitParserFuzzer::Process() {
while (fdp_.remaining_bytes()) {
auto invoke_parser_fuzzer = fdp_.PickValueInArray<const std::function<void()>>({
[&]() { InvokeParser(); },
[&]() { InvokeInterfaceUtils(); },
[&]() { InvokeLimitParser(); },
});
invoke_parser_fuzzer();

View file

@ -297,9 +297,7 @@ int main(int argc, char** argv) {
ActionManager& am = ActionManager::GetInstance();
ServiceList& sl = ServiceList::GetInstance();
Parser parser;
parser.AddSectionParser("service",
std::make_unique<ServiceParser>(&sl, GetSubcontext(),
*interface_inheritance_hierarchy_map));
parser.AddSectionParser("service", std::make_unique<ServiceParser>(&sl, GetSubcontext()));
parser.AddSectionParser("on", std::make_unique<ActionParser>(&am, GetSubcontext()));
parser.AddSectionParser("import", std::make_unique<HostImportParser>());
@ -317,11 +315,23 @@ int main(int argc, char** argv) {
return EXIT_FAILURE;
}
}
size_t failures = parser.parse_error_count() + am.CheckAllCommands() + sl.CheckAllCommands();
if (failures > 0) {
LOG(ERROR) << "Failed to parse init scripts with " << failures << " error(s).";
return EXIT_FAILURE;
}
for (const auto& service : sl) {
if (const auto& result = CheckInterfaceInheritanceHierarchy(
service->interfaces(), *interface_inheritance_hierarchy_map);
!result.ok()) {
LOG(ERROR) << service->filename() << ": invalid interface in service '"
<< service->name() << "': " << result.error();
return EXIT_FAILURE;
}
}
return EXIT_SUCCESS;
}

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