Merge "libsnapshot: Remove flaky image creation test." into android13-tests-dev am: 220d4aba05
Original change: https://android-review.googlesource.com/c/platform/system/core/+/2190056
Change-Id: I91ca9d97ba751e292dd8f68188470ab6735e68eb
Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
diff --git a/bootstat/boot_reason_test.sh b/bootstat/boot_reason_test.sh
index 7cff7dc..299b5d4 100755
--- a/bootstat/boot_reason_test.sh
+++ b/bootstat/boot_reason_test.sh
@@ -518,20 +518,20 @@
reboot,bootloader | reboot,bootloader,* ) var=${var#reboot,} ; var=${var%,} ;;
reboot | reboot,?* ) ;;
# Aliases and Heuristics
- *wdog* | *watchdog* ) var="watchdog" ;;
- *powerkey* | *power_key* | *PowerKey* ) var="cold,powerkey" ;;
- *panic* | *kernel_panic* ) var="kernel_panic" ;;
- *thermal* ) var="shutdown,thermal" ;;
- *s3_wakeup* ) var="warm,s3_wakeup" ;;
- *hw_reset* ) var="hard,hw_reset" ;;
- *usb* ) var="cold,charger" ;;
- *rtc* ) var="cold,rtc" ;;
- *2sec_reboot* ) var="cold,rtc,2sec" ;;
- *wdt_by_pass_pwk* ) var="warm" ;;
- wdt ) var="reboot" ;;
- *tool_by_pass_pwk* ) var="reboot,tool" ;;
- *bootloader* ) var="bootloader" ;;
- * ) var="reboot" ;;
+ *wdog* | *watchdog* ) var="watchdog" ;;
+ *powerkey* | *power_on_key* | *power_key* | *PowerKey* ) var="cold,powerkey" ;;
+ *panic* | *kernel_panic* ) var="kernel_panic" ;;
+ *thermal* ) var="shutdown,thermal" ;;
+ *s3_wakeup* ) var="warm,s3_wakeup" ;;
+ *hw_reset* ) var="hard,hw_reset" ;;
+ *usb* | *power_on_cable* ) var="cold,charger" ;;
+ *rtc* ) var="cold,rtc" ;;
+ *2sec_reboot* ) var="cold,rtc,2sec" ;;
+ *wdt_by_pass_pwk* ) var="warm" ;;
+ wdt ) var="reboot" ;;
+ *tool_by_pass_pwk* ) var="reboot,tool" ;;
+ *bootloader* ) var="bootloader" ;;
+ * ) var="reboot" ;;
esac
echo ${var}
}
diff --git a/bootstat/bootstat.cpp b/bootstat/bootstat.cpp
index 2c878f0..589b99a 100644
--- a/bootstat/bootstat.cpp
+++ b/bootstat/bootstat.cpp
@@ -58,7 +58,7 @@
};
// Maps BootEvent used inside bootstat into statsd atom defined in
-// frameworks/base/cmds/statsd/src/atoms.proto.
+// frameworks/proto_logging/stats/atoms.proto.
const std::unordered_map<std::string_view, AtomInfo> kBootEventToAtomInfo = {
// ELAPSED_TIME
{"ro.boottime.init",
@@ -463,6 +463,18 @@
{"watchdog,gsa,hard", 215},
{"watchdog,gsa,soft", 216},
{"watchdog,pmucal", 217},
+ {"reboot,early,bl", 218},
+ {"watchdog,apc,gsa,crashed", 219},
+ {"watchdog,apc,bl31,crashed", 220},
+ {"watchdog,apc,pbl,crashed", 221},
+ {"reboot,memory_protect,hyp", 222},
+ {"reboot,tsd,pmic,main", 223},
+ {"reboot,tsd,pmic,sub", 224},
+ {"reboot,ocp,pmic,main", 225},
+ {"reboot,ocp,pmic,sub", 226},
+ {"reboot,sys_ldo_ok,pmic,main", 227},
+ {"reboot,sys_ldo_ok,pmic,sub", 228},
+ {"reboot,smpl_timeout,pmic,main", 229},
};
// Converts a string value representing the reason the system booted to an
diff --git a/debuggerd/Android.bp b/debuggerd/Android.bp
index ad0231d..1c89472 100644
--- a/debuggerd/Android.bp
+++ b/debuggerd/Android.bp
@@ -14,9 +14,15 @@
"-Wno-nullability-completeness",
"-Os",
"-fno-finite-loops",
+ "-DANDROID_DEBUGGABLE=0",
],
local_include_dirs: ["include"],
+ product_variables: {
+ debuggable: {
+ cflags: ["-UANDROID_DEBUGGABLE", "-DANDROID_DEBUGGABLE=1"],
+ }
+ },
}
cc_library_headers {
@@ -204,6 +210,7 @@
header_libs: [
"bionic_libc_platform_headers",
"gwp_asan_headers",
+ "liblog_headers",
],
static_libs: [
@@ -212,7 +219,6 @@
"liblzma",
"libbase",
"libcutils",
- "liblog",
],
runtime_libs: [
"libdexfile", // libdexfile_support dependency
diff --git a/debuggerd/crash_dump.cpp b/debuggerd/crash_dump.cpp
index 967b942..e3ea455 100644
--- a/debuggerd/crash_dump.cpp
+++ b/debuggerd/crash_dump.cpp
@@ -51,12 +51,9 @@
#define ATRACE_TAG ATRACE_TAG_BIONIC
#include <utils/Trace.h>
-#include <unwindstack/DexFiles.h>
-#include <unwindstack/JitDebug.h>
-#include <unwindstack/Maps.h>
-#include <unwindstack/Memory.h>
+#include <unwindstack/AndroidUnwinder.h>
+#include <unwindstack/Error.h>
#include <unwindstack/Regs.h>
-#include <unwindstack/Unwinder.h>
#include "libdebuggerd/backtrace.h"
#include "libdebuggerd/tombstone.h"
@@ -623,9 +620,12 @@
}
// TODO: Use seccomp to lock ourselves down.
- unwindstack::UnwinderFromPid unwinder(256, vm_pid, unwindstack::Regs::CurrentArch());
- if (!unwinder.Init()) {
- LOG(FATAL) << "Failed to init unwinder object.";
+
+ unwindstack::AndroidRemoteUnwinder unwinder(vm_pid, unwindstack::Regs::CurrentArch());
+ unwindstack::ErrorData error_data;
+ if (!unwinder.Initialize(error_data)) {
+ LOG(FATAL) << "Failed to initialize unwinder object: "
+ << unwindstack::GetErrorCodeString(error_data.code);
}
std::string amfd_data;
diff --git a/debuggerd/debuggerd_test.cpp b/debuggerd/debuggerd_test.cpp
index e113308..ed90ab4 100644
--- a/debuggerd/debuggerd_test.cpp
+++ b/debuggerd/debuggerd_test.cpp
@@ -1486,6 +1486,37 @@
ASSERT_BACKTRACE_FRAME(result, "abort");
}
+TEST_F(CrasherTest, seccomp_tombstone_multiple_threads_abort) {
+ 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);
+
+ std::thread abort_thread([] { abort(); });
+ abort_thread.join();
+ },
+ &seccomp_fork);
+
+ StartIntercept(&output_fd, dump_type);
+ FinishCrasher();
+ AssertDeath(SIGABRT);
+ FinishIntercept(&intercept_result);
+ ASSERT_EQ(1, intercept_result) << "tombstoned reported failure";
+
+ std::string result;
+ ConsumeFd(std::move(output_fd), &result);
+ ASSERT_BACKTRACE_FRAME(result, "abort");
+ ASSERT_BACKTRACE_FRAME(result, "foo");
+ ASSERT_BACKTRACE_FRAME(result, "bar");
+ ASSERT_BACKTRACE_FRAME(result, "main");
+}
+
TEST_F(CrasherTest, seccomp_backtrace) {
int intercept_result;
unique_fd output_fd;
@@ -1516,6 +1547,40 @@
ASSERT_BACKTRACE_FRAME(result, "bar");
}
+TEST_F(CrasherTest, seccomp_backtrace_from_thread) {
+ 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);
+
+ std::thread raise_thread([] {
+ raise_debugger_signal(dump_type);
+ _exit(0);
+ });
+ raise_thread.join();
+ },
+ &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");
+ ASSERT_BACKTRACE_FRAME(result, "main");
+}
+
TEST_F(CrasherTest, seccomp_crash_logcat) {
StartProcess([]() { abort(); }, &seccomp_fork);
FinishCrasher();
diff --git a/debuggerd/handler/debuggerd_fallback.cpp b/debuggerd/handler/debuggerd_fallback.cpp
index c8b25ae..4721391 100644
--- a/debuggerd/handler/debuggerd_fallback.cpp
+++ b/debuggerd/handler/debuggerd_fallback.cpp
@@ -31,12 +31,9 @@
#include <android-base/unique_fd.h>
#include <async_safe/log.h>
#include <bionic/reserved_signals.h>
-#include <unwindstack/DexFiles.h>
-#include <unwindstack/JitDebug.h>
-#include <unwindstack/Maps.h>
+#include <unwindstack/AndroidUnwinder.h>
#include <unwindstack/Memory.h>
#include <unwindstack/Regs.h>
-#include <unwindstack/Unwinder.h>
#include "debuggerd/handler.h"
#include "handler/fallback.h"
@@ -75,11 +72,11 @@
// 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 process is stopped, and there
+ // 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::UnwinderFromPid unwinder(kMaxFrames, getpid(), process_memory);
+ unwindstack::AndroidLocalUnwinder unwinder(process_memory);
dump_backtrace_thread(output_fd, &unwinder, thread);
}
__linker_disable_fallback_allocator();
@@ -213,7 +210,10 @@
// 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](pid_t tid) {
+ if (!iterate_tids(current_tid, [&output_fd, ¤t_tid](pid_t tid) {
+ if (current_tid == tid) {
+ 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;
diff --git a/debuggerd/handler/debuggerd_handler.cpp b/debuggerd/handler/debuggerd_handler.cpp
index 35be2bf..92e7675 100644
--- a/debuggerd/handler/debuggerd_handler.cpp
+++ b/debuggerd/handler/debuggerd_handler.cpp
@@ -38,6 +38,8 @@
#include <unistd.h>
#include <android-base/macros.h>
+#include <android-base/parsebool.h>
+#include <android-base/properties.h>
#include <android-base/unique_fd.h>
#include <async_safe/log.h>
#include <bionic/reserved_signals.h>
@@ -49,7 +51,10 @@
#include "handler/fallback.h"
-using android::base::Pipe;
+using ::android::base::GetBoolProperty;
+using ::android::base::ParseBool;
+using ::android::base::ParseBoolResult;
+using ::android::base::Pipe;
// We muck with our fds in a 'thread' that doesn't share the same fd table.
// Close fds in that thread with a raw close syscall instead of going through libc.
@@ -82,6 +87,13 @@
return syscall(__NR_gettid);
}
+static bool is_permissive_mte() {
+ // Environment variable for testing or local use from shell.
+ char* permissive_env = getenv("MTE_PERMISSIVE");
+ return GetBoolProperty("persist.sys.mte.permissive", false) ||
+ (permissive_env && ParseBool(permissive_env) == ParseBoolResult::kTrue);
+}
+
static inline void futex_wait(volatile void* ftx, int value) {
syscall(__NR_futex, ftx, FUTEX_WAIT, value, nullptr, nullptr, 0);
}
@@ -592,7 +604,28 @@
// If the signal is fatal, don't unlock the mutex to prevent other crashing threads from
// starting to dump right before our death.
pthread_mutex_unlock(&crash_mutex);
- } else {
+ }
+#ifdef __aarch64__
+ else if (info->si_signo == SIGSEGV &&
+ (info->si_code == SEGV_MTESERR || info->si_code == SEGV_MTEAERR) &&
+ is_permissive_mte()) {
+ // If we are in permissive MTE mode, we do not crash, but instead disable MTE on this thread,
+ // and then let the failing instruction be retried. The second time should work (except
+ // if there is another non-MTE fault).
+ int tagged_addr_ctrl = prctl(PR_GET_TAGGED_ADDR_CTRL, 0, 0, 0, 0);
+ if (tagged_addr_ctrl < 0) {
+ fatal_errno("failed to PR_GET_TAGGED_ADDR_CTRL");
+ }
+ tagged_addr_ctrl = (tagged_addr_ctrl & ~PR_MTE_TCF_MASK) | PR_MTE_TCF_NONE;
+ if (prctl(PR_SET_TAGGED_ADDR_CTRL, tagged_addr_ctrl, 0, 0, 0) < 0) {
+ fatal_errno("failed to PR_SET_TAGGED_ADDR_CTRL");
+ }
+ async_safe_format_log(ANDROID_LOG_ERROR, "libc",
+ "MTE ERROR DETECTED BUT RUNNING IN PERMISSIVE MODE. CONTINUING.");
+ pthread_mutex_unlock(&crash_mutex);
+ }
+#endif
+ else {
// Resend the signal, so that either the debugger or the parent's waitpid sees it.
resend_signal(info);
}
diff --git a/debuggerd/include/debuggerd/handler.h b/debuggerd/include/debuggerd/handler.h
index bc08327..68b2e67 100644
--- a/debuggerd/include/debuggerd/handler.h
+++ b/debuggerd/include/debuggerd/handler.h
@@ -62,10 +62,11 @@
#define DEBUGGER_SIGNAL BIONIC_SIGNAL_DEBUGGER
static void __attribute__((__unused__)) debuggerd_register_handlers(struct sigaction* action) {
+ bool enabled = true;
+#if ANDROID_DEBUGGABLE
char value[PROP_VALUE_MAX] = "";
- bool enabled =
- !(__system_property_get("ro.debuggable", value) > 0 && !strcmp(value, "1") &&
- __system_property_get("debug.debuggerd.disable", value) > 0 && !strcmp(value, "1"));
+ enabled = !(__system_property_get("debug.debuggerd.disable", value) > 0 && !strcmp(value, "1"));
+#endif
if (enabled) {
sigaction(SIGABRT, action, nullptr);
sigaction(SIGBUS, action, nullptr);
diff --git a/debuggerd/libdebuggerd/backtrace.cpp b/debuggerd/libdebuggerd/backtrace.cpp
index fd91038..3ff9710 100644
--- a/debuggerd/libdebuggerd/backtrace.cpp
+++ b/debuggerd/libdebuggerd/backtrace.cpp
@@ -37,6 +37,7 @@
#include <android-base/strings.h>
#include <android-base/unique_fd.h>
#include <log/log.h>
+#include <unwindstack/AndroidUnwinder.h>
#include <unwindstack/Unwinder.h>
#include "libdebuggerd/types.h"
@@ -57,7 +58,7 @@
_LOG(log, logtype::BACKTRACE, "\n----- end %d -----\n", pid);
}
-void dump_backtrace_thread(int output_fd, unwindstack::Unwinder* unwinder,
+void dump_backtrace_thread(int output_fd, unwindstack::AndroidUnwinder* unwinder,
const ThreadInfo& thread) {
log_t log;
log.tfd = output_fd;
@@ -65,21 +66,17 @@
_LOG(&log, logtype::BACKTRACE, "\n\"%s\" sysTid=%d\n", thread.thread_name.c_str(), thread.tid);
- unwinder->SetRegs(thread.registers.get());
- unwinder->Unwind();
- if (unwinder->NumFrames() == 0) {
- _LOG(&log, logtype::THREAD, "Unwind failed: tid = %d\n", thread.tid);
- if (unwinder->LastErrorCode() != unwindstack::ERROR_NONE) {
- _LOG(&log, logtype::THREAD, " Error code: %s\n", unwinder->LastErrorCodeString());
- _LOG(&log, logtype::THREAD, " Error address: 0x%" PRIx64 "\n", unwinder->LastErrorAddress());
- }
+ unwindstack::AndroidUnwinderData data;
+ if (!unwinder->Unwind(thread.registers.get(), data)) {
+ _LOG(&log, logtype::THREAD, "Unwind failed: tid = %d: Error %s\n", thread.tid,
+ data.GetErrorString().c_str());
return;
}
- log_backtrace(&log, unwinder, " ");
+ log_backtrace(&log, unwinder, data, " ");
}
-void dump_backtrace(android::base::unique_fd output_fd, unwindstack::Unwinder* unwinder,
+void dump_backtrace(android::base::unique_fd output_fd, unwindstack::AndroidUnwinder* unwinder,
const std::map<pid_t, ThreadInfo>& thread_info, pid_t target_thread) {
log_t log;
log.tfd = output_fd.get();
diff --git a/debuggerd/libdebuggerd/gwp_asan.cpp b/debuggerd/libdebuggerd/gwp_asan.cpp
index 3d96627..d8f74e0 100644
--- a/debuggerd/libdebuggerd/gwp_asan.cpp
+++ b/debuggerd/libdebuggerd/gwp_asan.cpp
@@ -21,9 +21,8 @@
#include "gwp_asan/common.h"
#include "gwp_asan/crash_handler.h"
-#include <unwindstack/Maps.h>
+#include <unwindstack/AndroidUnwinder.h>
#include <unwindstack/Memory.h>
-#include <unwindstack/Regs.h>
#include <unwindstack/Unwinder.h>
#include "tombstone.pb.h"
@@ -106,7 +105,8 @@
constexpr size_t kMaxTraceLength = gwp_asan::AllocationMetadata::kMaxTraceLengthToCollect;
-void GwpAsanCrashData::AddCauseProtos(Tombstone* tombstone, unwindstack::Unwinder* unwinder) const {
+void GwpAsanCrashData::AddCauseProtos(Tombstone* tombstone,
+ unwindstack::AndroidUnwinder* unwinder) const {
if (!CrashIsMine()) {
ALOGE("Internal Error: AddCauseProtos() on a non-GWP-ASan crash.");
return;
@@ -140,7 +140,6 @@
heap_object->set_address(__gwp_asan_get_allocation_address(responsible_allocation_));
heap_object->set_size(__gwp_asan_get_allocation_size(responsible_allocation_));
- unwinder->SetDisplayBuildID(true);
std::unique_ptr<uintptr_t[]> frames(new uintptr_t[kMaxTraceLength]);
diff --git a/debuggerd/libdebuggerd/include/libdebuggerd/backtrace.h b/debuggerd/libdebuggerd/include/libdebuggerd/backtrace.h
index c20d090..531afea 100644
--- a/debuggerd/libdebuggerd/include/libdebuggerd/backtrace.h
+++ b/debuggerd/libdebuggerd/include/libdebuggerd/backtrace.h
@@ -30,16 +30,16 @@
// Forward delcaration
namespace unwindstack {
-class Unwinder;
+class AndroidUnwinder;
}
// Dumps a backtrace using a format similar to what Dalvik uses so that the result
// can be intermixed in a bug report.
-void dump_backtrace(android::base::unique_fd output_fd, unwindstack::Unwinder* unwinder,
+void dump_backtrace(android::base::unique_fd output_fd, unwindstack::AndroidUnwinder* unwinder,
const std::map<pid_t, ThreadInfo>& thread_info, pid_t target_thread);
void dump_backtrace_header(int output_fd);
-void dump_backtrace_thread(int output_fd, unwindstack::Unwinder* unwinder,
+void dump_backtrace_thread(int output_fd, unwindstack::AndroidUnwinder* unwinder,
const ThreadInfo& thread);
void dump_backtrace_footer(int output_fd);
diff --git a/debuggerd/libdebuggerd/include/libdebuggerd/gwp_asan.h b/debuggerd/libdebuggerd/include/libdebuggerd/gwp_asan.h
index a979370..0429643 100644
--- a/debuggerd/libdebuggerd/include/libdebuggerd/gwp_asan.h
+++ b/debuggerd/libdebuggerd/include/libdebuggerd/gwp_asan.h
@@ -26,9 +26,15 @@
#include "types.h"
#include "utility.h"
+// Forward delcarations
class Cause;
class Tombstone;
+namespace unwindstack {
+class AndroidUnwinder;
+class Memory;
+} // namespace unwindstack
+
class GwpAsanCrashData {
public:
GwpAsanCrashData() = delete;
@@ -52,7 +58,7 @@
// allocator crash state.
uintptr_t GetFaultAddress() const;
- void AddCauseProtos(Tombstone* tombstone, unwindstack::Unwinder* unwinder) const;
+ void AddCauseProtos(Tombstone* tombstone, unwindstack::AndroidUnwinder* unwinder) const;
protected:
// Is GWP-ASan responsible for this crash.
diff --git a/debuggerd/libdebuggerd/include/libdebuggerd/scudo.h b/debuggerd/libdebuggerd/include/libdebuggerd/scudo.h
index 172ffe9..a506859 100644
--- a/debuggerd/libdebuggerd/include/libdebuggerd/scudo.h
+++ b/debuggerd/libdebuggerd/include/libdebuggerd/scudo.h
@@ -23,9 +23,15 @@
#include "scudo/interface.h"
+// Forward delcarations
class Cause;
class Tombstone;
+namespace unwindstack {
+class AndroidUnwinder;
+class Memory;
+} // namespace unwindstack
+
class ScudoCrashData {
public:
ScudoCrashData() = delete;
@@ -34,12 +40,12 @@
bool CrashIsMine() const;
- void AddCauseProtos(Tombstone* tombstone, unwindstack::Unwinder* unwinder) const;
+ void AddCauseProtos(Tombstone* tombstone, unwindstack::AndroidUnwinder* unwinder) const;
private:
scudo_error_info error_info_ = {};
uintptr_t untagged_fault_addr_;
void FillInCause(Cause* cause, const scudo_error_report* report,
- unwindstack::Unwinder* unwinder) const;
+ unwindstack::AndroidUnwinder* unwinder) const;
};
diff --git a/debuggerd/libdebuggerd/include/libdebuggerd/tombstone.h b/debuggerd/libdebuggerd/include/libdebuggerd/tombstone.h
index 7bf1688..be999e0 100644
--- a/debuggerd/libdebuggerd/include/libdebuggerd/tombstone.h
+++ b/debuggerd/libdebuggerd/include/libdebuggerd/tombstone.h
@@ -37,7 +37,7 @@
namespace unwindstack {
struct FrameData;
-class Unwinder;
+class AndroidUnwinder;
}
// The maximum number of frames to save when unwinding.
@@ -51,7 +51,7 @@
/* Creates a tombstone file and writes the crash dump to it. */
void engrave_tombstone(android::base::unique_fd output_fd, android::base::unique_fd proto_fd,
- unwindstack::Unwinder* unwinder,
+ unwindstack::AndroidUnwinder* unwinder,
const std::map<pid_t, ThreadInfo>& thread_info, pid_t target_thread,
const ProcessInfo& process_info, OpenFilesList* open_files,
std::string* amfd_data);
@@ -59,7 +59,7 @@
void engrave_tombstone_ucontext(int tombstone_fd, int proto_fd, uint64_t abort_msg_address,
siginfo_t* siginfo, ucontext_t* ucontext);
-void engrave_tombstone_proto(Tombstone* tombstone, unwindstack::Unwinder* unwinder,
+void engrave_tombstone_proto(Tombstone* tombstone, unwindstack::AndroidUnwinder* unwinder,
const std::map<pid_t, ThreadInfo>& threads, pid_t target_thread,
const ProcessInfo& process_info, const OpenFilesList* open_files);
diff --git a/debuggerd/libdebuggerd/include/libdebuggerd/utility.h b/debuggerd/libdebuggerd/include/libdebuggerd/utility.h
index 63e142f..25b03af 100644
--- a/debuggerd/libdebuggerd/include/libdebuggerd/utility.h
+++ b/debuggerd/libdebuggerd/include/libdebuggerd/utility.h
@@ -73,11 +73,13 @@
void _VLOG(log_t* log, logtype ltype, const char* fmt, va_list ap);
namespace unwindstack {
-class Unwinder;
+class AndroidUnwinder;
class Memory;
+struct AndroidUnwinderData;
}
-void log_backtrace(log_t* log, unwindstack::Unwinder* unwinder, const char* prefix);
+void log_backtrace(log_t* log, unwindstack::AndroidUnwinder* unwinder,
+ unwindstack::AndroidUnwinderData& data, const char* prefix);
ssize_t dump_memory(void* out, size_t len, uint8_t* tags, size_t tags_len, uint64_t* addr,
unwindstack::Memory* memory);
diff --git a/debuggerd/libdebuggerd/scudo.cpp b/debuggerd/libdebuggerd/scudo.cpp
index a4836d7..5d861f8 100644
--- a/debuggerd/libdebuggerd/scudo.cpp
+++ b/debuggerd/libdebuggerd/scudo.cpp
@@ -17,8 +17,8 @@
#include "libdebuggerd/scudo.h"
#include "libdebuggerd/tombstone.h"
+#include "unwindstack/AndroidUnwinder.h"
#include "unwindstack/Memory.h"
-#include "unwindstack/Unwinder.h"
#include <android-base/macros.h>
#include <bionic/macros.h>
@@ -46,6 +46,9 @@
__scudo_get_region_info_size());
auto ring_buffer = AllocAndReadFully(process_memory, process_info.scudo_ring_buffer,
__scudo_get_ring_buffer_size());
+ if (!stack_depot || !region_info || !ring_buffer) {
+ return;
+ }
untagged_fault_addr_ = process_info.untagged_fault_address;
uintptr_t fault_page = untagged_fault_addr_ & ~(PAGE_SIZE - 1);
@@ -80,7 +83,7 @@
}
void ScudoCrashData::FillInCause(Cause* cause, const scudo_error_report* report,
- unwindstack::Unwinder* unwinder) const {
+ unwindstack::AndroidUnwinder* unwinder) const {
MemoryError* memory_error = cause->mutable_memory_error();
HeapObject* heap_object = memory_error->mutable_heap();
@@ -102,7 +105,6 @@
heap_object->set_address(report->allocation_address);
heap_object->set_size(report->allocation_size);
- unwinder->SetDisplayBuildID(true);
heap_object->set_allocation_tid(report->allocation_tid);
for (size_t i = 0; i < arraysize(report->allocation_trace) && report->allocation_trace[i]; ++i) {
@@ -123,7 +125,8 @@
set_human_readable_cause(cause, untagged_fault_addr_);
}
-void ScudoCrashData::AddCauseProtos(Tombstone* tombstone, unwindstack::Unwinder* unwinder) const {
+void ScudoCrashData::AddCauseProtos(Tombstone* tombstone,
+ unwindstack::AndroidUnwinder* unwinder) const {
size_t report_num = 0;
while (report_num < sizeof(error_info_.reports) / sizeof(error_info_.reports[0]) &&
error_info_.reports[report_num].error_type != UNKNOWN) {
diff --git a/debuggerd/libdebuggerd/tombstone.cpp b/debuggerd/libdebuggerd/tombstone.cpp
index eda7182..e5b4d74 100644
--- a/debuggerd/libdebuggerd/tombstone.cpp
+++ b/debuggerd/libdebuggerd/tombstone.cpp
@@ -36,9 +36,9 @@
#include <async_safe/log.h>
#include <log/log.h>
#include <private/android_filesystem_config.h>
-#include <unwindstack/Memory.h>
+#include <unwindstack/AndroidUnwinder.h>
+#include <unwindstack/Error.h>
#include <unwindstack/Regs.h>
-#include <unwindstack/Unwinder.h>
#include "libdebuggerd/backtrace.h"
#include "libdebuggerd/open_files_list.h"
@@ -55,15 +55,15 @@
siginfo_t* siginfo, ucontext_t* ucontext) {
pid_t uid = getuid();
pid_t pid = getpid();
- pid_t tid = gettid();
+ pid_t target_tid = gettid();
log_t log;
- log.current_tid = tid;
- log.crashed_tid = tid;
+ log.current_tid = target_tid;
+ log.crashed_tid = target_tid;
log.tfd = tombstone_fd;
log.amfd_data = nullptr;
- std::string thread_name = get_thread_name(tid);
+ std::string thread_name = get_thread_name(target_tid);
std::vector<std::string> command_line = get_command_line(pid);
std::unique_ptr<unwindstack::Regs> regs(
@@ -73,50 +73,57 @@
android::base::ReadFileToString("/proc/self/attr/current", &selinux_label);
std::map<pid_t, ThreadInfo> threads;
- threads[tid] = ThreadInfo{
- .registers = std::move(regs), .uid = uid, .tid = tid, .thread_name = std::move(thread_name),
- .pid = pid, .command_line = std::move(command_line), .selinux_label = std::move(selinux_label),
- .siginfo = siginfo,
+ threads[target_tid] = ThreadInfo {
+ .registers = std::move(regs), .uid = uid, .tid = target_tid,
+ .thread_name = std::move(thread_name), .pid = pid, .command_line = std::move(command_line),
+ .selinux_label = std::move(selinux_label), .siginfo = siginfo,
#if defined(__aarch64__)
// Only supported on aarch64 for now.
.tagged_addr_ctrl = prctl(PR_GET_TAGGED_ADDR_CTRL, 0, 0, 0, 0),
.pac_enabled_keys = prctl(PR_PAC_GET_ENABLED_KEYS, 0, 0, 0, 0),
#endif
};
- if (pid == tid) {
- const ThreadInfo& thread = threads[pid];
- if (!iterate_tids(pid, [&threads, &thread](pid_t tid) {
- threads[tid] = ThreadInfo{
- .uid = thread.uid,
- .tid = tid,
- .pid = thread.pid,
- .command_line = thread.command_line,
- .thread_name = get_thread_name(tid),
- .tagged_addr_ctrl = thread.tagged_addr_ctrl,
- .pac_enabled_keys = thread.pac_enabled_keys,
- };
- })) {
- async_safe_format_log(ANDROID_LOG_ERROR, LOG_TAG, "failed to open /proc/%d/task: %s", pid,
- strerror(errno));
- }
+ const ThreadInfo& thread = threads[pid];
+ if (!iterate_tids(pid, [&threads, &thread, &target_tid](pid_t tid) {
+ if (target_tid == tid) {
+ return;
+ }
+ async_safe_format_log(ANDROID_LOG_ERROR, LOG_TAG, "Adding thread %d", tid);
+ threads[tid] = ThreadInfo{
+ .uid = thread.uid,
+ .tid = tid,
+ .pid = thread.pid,
+ .command_line = thread.command_line,
+ .thread_name = get_thread_name(tid),
+ .tagged_addr_ctrl = thread.tagged_addr_ctrl,
+ .pac_enabled_keys = thread.pac_enabled_keys,
+ };
+ })) {
+ async_safe_format_log(ANDROID_LOG_ERROR, LOG_TAG, "failed to open /proc/%d/task: %s", pid,
+ strerror(errno));
}
- auto process_memory =
- unwindstack::Memory::CreateProcessMemoryCached(getpid());
- unwindstack::UnwinderFromPid unwinder(kMaxFrames, pid, unwindstack::Regs::CurrentArch(), nullptr,
- process_memory);
- if (!unwinder.Init()) {
- async_safe_format_log(ANDROID_LOG_ERROR, LOG_TAG, "failed to init unwinder object");
+ // 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());
+ unwindstack::AndroidLocalUnwinder unwinder(process_memory);
+ unwindstack::ErrorData error;
+ if (!unwinder.Initialize(error)) {
+ async_safe_format_log(ANDROID_LOG_ERROR, LOG_TAG, "failed to init unwinder object: %s",
+ unwindstack::GetErrorCodeString(error.code));
return;
}
ProcessInfo process_info;
process_info.abort_msg_address = abort_msg_address;
- engrave_tombstone(unique_fd(dup(tombstone_fd)), unique_fd(dup(proto_fd)), &unwinder, threads, tid,
- process_info, nullptr, nullptr);
+ engrave_tombstone(unique_fd(dup(tombstone_fd)), unique_fd(dup(proto_fd)), &unwinder, threads,
+ target_tid, process_info, nullptr, nullptr);
}
-void engrave_tombstone(unique_fd output_fd, unique_fd proto_fd, unwindstack::Unwinder* unwinder,
+void engrave_tombstone(unique_fd output_fd, unique_fd proto_fd,
+ unwindstack::AndroidUnwinder* unwinder,
const std::map<pid_t, ThreadInfo>& threads, pid_t target_thread,
const ProcessInfo& process_info, OpenFilesList* open_files,
std::string* amfd_data) {
diff --git a/debuggerd/libdebuggerd/tombstone_proto.cpp b/debuggerd/libdebuggerd/tombstone_proto.cpp
index bd05837..159ebc8 100644
--- a/debuggerd/libdebuggerd/tombstone_proto.cpp
+++ b/debuggerd/libdebuggerd/tombstone_proto.cpp
@@ -56,10 +56,11 @@
#include <private/android_filesystem_config.h>
#include <procinfo/process.h>
+#include <unwindstack/AndroidUnwinder.h>
+#include <unwindstack/Error.h>
+#include <unwindstack/MapInfo.h>
#include <unwindstack/Maps.h>
-#include <unwindstack/Memory.h>
#include <unwindstack/Regs.h>
-#include <unwindstack/Unwinder.h>
#include "libdebuggerd/open_files_list.h"
#include "libdebuggerd/utility.h"
@@ -189,7 +190,7 @@
error_type_str, diff, byte_suffix, location_str, heap_object.size(), heap_object.address()));
}
-static void dump_probable_cause(Tombstone* tombstone, unwindstack::Unwinder* unwinder,
+static void dump_probable_cause(Tombstone* tombstone, unwindstack::AndroidUnwinder* unwinder,
const ProcessInfo& process_info, const ThreadInfo& main_thread) {
#if defined(USE_SCUDO)
ScudoCrashData scudo_crash_data(unwinder->GetProcessMemory().get(), process_info);
@@ -245,9 +246,9 @@
}
}
-static void dump_abort_message(Tombstone* tombstone, unwindstack::Unwinder* unwinder,
+static void dump_abort_message(Tombstone* tombstone,
+ std::shared_ptr<unwindstack::Memory>& process_memory,
const ProcessInfo& process_info) {
- std::shared_ptr<unwindstack::Memory> process_memory = unwinder->GetProcessMemory();
uintptr_t address = process_info.abort_msg_address;
if (address == 0) {
return;
@@ -348,7 +349,7 @@
f->set_build_id(frame.map_info->GetPrintableBuildID());
}
-static void dump_registers(unwindstack::Unwinder* unwinder,
+static void dump_registers(unwindstack::AndroidUnwinder* unwinder,
const std::unique_ptr<unwindstack::Regs>& regs, Thread& thread,
bool memory_dump) {
if (regs == nullptr) {
@@ -402,27 +403,9 @@
});
}
-static void log_unwinder_error(unwindstack::Unwinder* unwinder) {
- if (unwinder->LastErrorCode() == unwindstack::ERROR_NONE) {
- return;
- }
-
- async_safe_format_log(ANDROID_LOG_ERROR, LOG_TAG, " error code: %s",
- unwinder->LastErrorCodeString());
- async_safe_format_log(ANDROID_LOG_ERROR, LOG_TAG, " error address: 0x%" PRIx64,
- unwinder->LastErrorAddress());
-}
-
-static void dump_thread_backtrace(unwindstack::Unwinder* unwinder, Thread& thread) {
- if (unwinder->NumFrames() == 0) {
- async_safe_format_log(ANDROID_LOG_ERROR, LOG_TAG, "failed to unwind");
- log_unwinder_error(unwinder);
- return;
- }
-
- unwinder->SetDisplayBuildID(true);
+static void dump_thread_backtrace(std::vector<unwindstack::FrameData>& frames, Thread& thread) {
std::set<std::string> unreadable_elf_files;
- for (const auto& frame : unwinder->frames()) {
+ for (const auto& frame : frames) {
BacktraceFrame* f = thread.add_current_backtrace();
fill_in_backtrace_frame(f, frame);
if (frame.map_info != nullptr && frame.map_info->ElfFileNotReadable()) {
@@ -446,7 +429,7 @@
}
}
-static void dump_thread(Tombstone* tombstone, unwindstack::Unwinder* unwinder,
+static void dump_thread(Tombstone* tombstone, unwindstack::AndroidUnwinder* unwinder,
const ThreadInfo& thread_info, bool memory_dump = false) {
Thread thread;
@@ -455,36 +438,29 @@
thread.set_tagged_addr_ctrl(thread_info.tagged_addr_ctrl);
thread.set_pac_enabled_keys(thread_info.pac_enabled_keys);
- if (thread_info.registers == nullptr) {
- // Fallback path for non-main thread, doing unwind from running process.
- unwindstack::ThreadUnwinder thread_unwinder(kMaxFrames, unwinder->GetMaps());
- if (!thread_unwinder.Init()) {
- async_safe_format_log(ANDROID_LOG_ERROR, LOG_TAG,
- "Unable to initialize ThreadUnwinder object.");
- log_unwinder_error(&thread_unwinder);
- return;
- }
-
- std::unique_ptr<unwindstack::Regs> initial_regs;
- thread_unwinder.UnwindWithSignal(BIONIC_SIGNAL_BACKTRACE, thread_info.tid, &initial_regs);
- dump_registers(&thread_unwinder, initial_regs, thread, memory_dump);
- dump_thread_backtrace(&thread_unwinder, thread);
+ unwindstack::AndroidUnwinderData data;
+ // Indicate we want a copy of the initial registers.
+ data.saved_initial_regs = std::make_optional<std::unique_ptr<unwindstack::Regs>>();
+ bool unwind_ret;
+ if (thread_info.registers != nullptr) {
+ unwind_ret = unwinder->Unwind(thread_info.registers.get(), data);
} else {
- dump_registers(unwinder, thread_info.registers, thread, memory_dump);
- std::unique_ptr<unwindstack::Regs> regs_copy(thread_info.registers->Clone());
- unwinder->SetRegs(regs_copy.get());
- unwinder->Unwind();
- dump_thread_backtrace(unwinder, thread);
+ unwind_ret = unwinder->Unwind(thread_info.tid, data);
}
+ if (!unwind_ret) {
+ async_safe_format_log(ANDROID_LOG_ERROR, LOG_TAG, "Unwind failed for tid %d: Error %s",
+ thread_info.tid, data.GetErrorString().c_str());
+ } else {
+ dump_thread_backtrace(data.frames, thread);
+ }
+ dump_registers(unwinder, *data.saved_initial_regs, thread, memory_dump);
auto& threads = *tombstone->mutable_threads();
threads[thread_info.tid] = thread;
}
-static void dump_mappings(Tombstone* tombstone, unwindstack::Unwinder* unwinder) {
- unwindstack::Maps* maps = unwinder->GetMaps();
- std::shared_ptr<unwindstack::Memory> process_memory = unwinder->GetProcessMemory();
-
+static void dump_mappings(Tombstone* tombstone, unwindstack::Maps* maps,
+ std::shared_ptr<unwindstack::Memory>& process_memory) {
for (const auto& map_info : *maps) {
auto* map = tombstone->add_memory_mappings();
map->set_begin_address(map_info->start());
@@ -593,7 +569,8 @@
}
static void dump_tags_around_fault_addr(Signal* signal, const Tombstone& tombstone,
- unwindstack::Unwinder* unwinder, uintptr_t fault_addr) {
+ std::shared_ptr<unwindstack::Memory>& process_memory,
+ uintptr_t fault_addr) {
if (tombstone.arch() != Architecture::ARM64) return;
fault_addr = untag_address(fault_addr);
@@ -604,8 +581,6 @@
// a valid address for us to dump tags from.
if (fault_addr < kBytesToRead / 2) return;
- unwindstack::Memory* memory = unwinder->GetProcessMemory().get();
-
constexpr uintptr_t kRowStartMask = ~(kNumTagColumns * kTagGranuleSize - 1);
size_t start_address = (fault_addr & kRowStartMask) - kBytesToRead / 2;
MemoryDump tag_dump;
@@ -614,7 +589,7 @@
// Attempt to read the first tag. If reading fails, this likely indicates the
// lowest touched page is inaccessible or not marked with PROT_MTE.
// Fast-forward over pages until one has tags, or we exhaust the search range.
- while (memory->ReadTag(start_address) < 0) {
+ while (process_memory->ReadTag(start_address) < 0) {
size_t page_size = sysconf(_SC_PAGE_SIZE);
size_t bytes_to_next_page = page_size - (start_address % page_size);
if (bytes_to_next_page >= granules_to_read * kTagGranuleSize) return;
@@ -626,7 +601,7 @@
std::string* mte_tags = tag_dump.mutable_arm_mte_metadata()->mutable_memory_tags();
for (size_t i = 0; i < granules_to_read; ++i) {
- long tag = memory->ReadTag(start_address + i * kTagGranuleSize);
+ long tag = process_memory->ReadTag(start_address + i * kTagGranuleSize);
if (tag < 0) break;
mte_tags->push_back(static_cast<uint8_t>(tag));
}
@@ -636,7 +611,7 @@
}
}
-void engrave_tombstone_proto(Tombstone* tombstone, unwindstack::Unwinder* unwinder,
+void engrave_tombstone_proto(Tombstone* tombstone, unwindstack::AndroidUnwinder* unwinder,
const std::map<pid_t, ThreadInfo>& threads, pid_t target_thread,
const ProcessInfo& process_info, const OpenFilesList* open_files) {
Tombstone result;
@@ -691,12 +666,12 @@
sig.set_has_fault_address(true);
uintptr_t fault_addr = process_info.maybe_tagged_fault_address;
sig.set_fault_address(fault_addr);
- dump_tags_around_fault_addr(&sig, result, unwinder, fault_addr);
+ dump_tags_around_fault_addr(&sig, result, unwinder->GetProcessMemory(), fault_addr);
}
*result.mutable_signal_info() = sig;
- dump_abort_message(&result, unwinder, process_info);
+ dump_abort_message(&result, unwinder->GetProcessMemory(), process_info);
// Dump the main thread, but save the memory around the registers.
dump_thread(&result, unwinder, main_thread, /* memory_dump */ true);
@@ -709,7 +684,7 @@
dump_probable_cause(&result, unwinder, process_info, main_thread);
- dump_mappings(&result, unwinder);
+ dump_mappings(&result, unwinder->GetMaps(), unwinder->GetProcessMemory());
// Only dump logs on debuggable devices.
if (android::base::GetBoolProperty("ro.debuggable", false)) {
diff --git a/debuggerd/libdebuggerd/utility.cpp b/debuggerd/libdebuggerd/utility.cpp
index ecd98a4..74a1423 100644
--- a/debuggerd/libdebuggerd/utility.cpp
+++ b/debuggerd/libdebuggerd/utility.cpp
@@ -39,6 +39,7 @@
#include <bionic/reserved_signals.h>
#include <debuggerd/handler.h>
#include <log/log.h>
+#include <unwindstack/AndroidUnwinder.h>
#include <unwindstack/Memory.h>
#include <unwindstack/Unwinder.h>
@@ -483,10 +484,10 @@
return describe_end(value, desc);
}
-void log_backtrace(log_t* log, unwindstack::Unwinder* unwinder, const char* prefix) {
+void log_backtrace(log_t* log, unwindstack::AndroidUnwinder* unwinder,
+ unwindstack::AndroidUnwinderData& data, const char* prefix) {
std::set<std::string> unreadable_elf_files;
- unwinder->SetDisplayBuildID(true);
- for (const auto& frame : unwinder->frames()) {
+ for (const auto& frame : data.frames) {
if (frame.map_info != nullptr && frame.map_info->ElfFileNotReadable()) {
unreadable_elf_files.emplace(frame.map_info->name());
}
@@ -509,7 +510,7 @@
}
}
- for (const auto& frame : unwinder->frames()) {
+ for (const auto& frame : data.frames) {
_LOG(log, logtype::BACKTRACE, "%s%s\n", prefix, unwinder->FormatFrame(frame).c_str());
}
}
diff --git a/debuggerd/test_permissive_mte/Android.bp b/debuggerd/test_permissive_mte/Android.bp
new file mode 100644
index 0000000..1c09240
--- /dev/null
+++ b/debuggerd/test_permissive_mte/Android.bp
@@ -0,0 +1,38 @@
+// Copyright (C) 2022 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package {
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+cc_binary {
+ name: "mte_crash",
+ srcs: ["mte_crash.cpp"],
+ sanitize: {
+ memtag_heap: true,
+ diag: {
+ memtag_heap: true,
+ },
+ },
+}
+
+java_test_host {
+ name: "permissive_mte_test",
+ libs: ["tradefed"],
+ static_libs: ["frameworks-base-hostutils", "cts-install-lib-host"],
+ srcs: ["src/**/PermissiveMteTest.java", ":libtombstone_proto-src"],
+ data: [":mte_crash"],
+ test_config: "AndroidTest.xml",
+ test_suites: ["general-tests"],
+}
diff --git a/debuggerd/test_permissive_mte/AndroidTest.xml b/debuggerd/test_permissive_mte/AndroidTest.xml
new file mode 100644
index 0000000..bd3d018
--- /dev/null
+++ b/debuggerd/test_permissive_mte/AndroidTest.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2022 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.
+-->
+<configuration description="Runs the permissive MTE tests">
+ <option name="test-suite-tag" value="init_test_upgrade_mte" />
+ <option name="test-suite-tag" value="apct" />
+
+ <!-- For tombstone inspection. -->
+ <target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer"/>
+ <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer">
+ <option name="cleanup" value="true" />
+ <option name="push" value="mte_crash->/data/local/tmp/mte_crash" />
+ </target_preparer>
+ <test class="com.android.tradefed.testtype.HostTest" >
+ <option name="jar" value="permissive_mte_test.jar" />
+ </test>
+</configuration>
\ No newline at end of file
diff --git a/debuggerd/test_permissive_mte/mte_crash.cpp b/debuggerd/test_permissive_mte/mte_crash.cpp
new file mode 100644
index 0000000..97ad73f
--- /dev/null
+++ b/debuggerd/test_permissive_mte/mte_crash.cpp
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) 2022 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 <stdio.h>
+#include <stdlib.h>
+
+int main(int, char**) {
+ volatile char* f = (char*)malloc(1);
+ printf("%c\n", f[17]);
+ return 0;
+}
diff --git a/debuggerd/test_permissive_mte/src/com/android/tests/debuggerd/PermissiveMteTest.java b/debuggerd/test_permissive_mte/src/com/android/tests/debuggerd/PermissiveMteTest.java
new file mode 100644
index 0000000..5ff2b5b
--- /dev/null
+++ b/debuggerd/test_permissive_mte/src/com/android/tests/debuggerd/PermissiveMteTest.java
@@ -0,0 +1,100 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.tests.init;
+
+import static com.google.common.truth.Truth.assertThat;
+import static org.junit.Assume.assumeTrue;
+
+import com.android.server.os.TombstoneProtos.Tombstone;
+import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
+import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test;
+import com.android.tradefed.util.CommandResult;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.InputStream;
+import java.util.Arrays;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(DeviceJUnit4ClassRunner.class)
+public class PermissiveMteTest extends BaseHostJUnit4Test {
+ String mUUID;
+
+ @Before
+ public void setUp() throws Exception {
+ mUUID = java.util.UUID.randomUUID().toString();
+ CommandResult result =
+ getDevice().executeShellV2Command("/data/local/tmp/mte_crash setUp " + mUUID);
+ assumeTrue("mte_crash needs to segfault", result.getExitCode() == 139);
+ }
+
+ Tombstone parseTombstone(String tombstonePath) throws Exception {
+ File tombstoneFile = getDevice().pullFile(tombstonePath);
+ InputStream istr = new FileInputStream(tombstoneFile);
+ Tombstone tombstoneProto;
+ try {
+ tombstoneProto = Tombstone.parseFrom(istr);
+ } finally {
+ istr.close();
+ }
+ return tombstoneProto;
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ 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;
+ }
+ getDevice().deleteFile(tombstonePath);
+ // remove the non .pb file as well.
+ getDevice().deleteFile(tombstonePath.substring(0, tombstonePath.length() - 3));
+ }
+ }
+
+ @Test
+ public void testCrash() throws Exception {
+ CommandResult result = getDevice().executeShellV2Command(
+ "MTE_PERMISSIVE=1 /data/local/tmp/mte_crash testCrash " + 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("testCrash"))) {
+ continue;
+ }
+ numberTombstones++;
+ }
+ assertThat(numberTombstones).isEqualTo(1);
+ }
+}
diff --git a/debuggerd/util.cpp b/debuggerd/util.cpp
index 5c6abc9..df033df 100644
--- a/debuggerd/util.cpp
+++ b/debuggerd/util.cpp
@@ -90,9 +90,7 @@
if (tid == 0) {
continue;
}
- if (pid != tid) {
- callback(tid);
- }
+ callback(tid);
}
return true;
}
diff --git a/fastboot/Android.bp b/fastboot/Android.bp
index 9ae2c37..b478e6e 100644
--- a/fastboot/Android.bp
+++ b/fastboot/Android.bp
@@ -130,12 +130,10 @@
"-Werror",
"-Wvla",
"-DANDROID_BASE_UNIQUE_FD_DISABLE_IMPLICIT_CONVERSION",
+ "-Wthread-safety",
],
rtti: true,
- clang_cflags: [
- "-Wthread-safety",
- ],
}
cc_binary {
@@ -156,6 +154,7 @@
"device/flashing.cpp",
"device/main.cpp",
"device/usb.cpp",
+ "device/usb_iouring.cpp",
"device/usb_client.cpp",
"device/tcp_client.cpp",
"device/utility.cpp",
@@ -166,6 +165,8 @@
shared_libs: [
"android.hardware.boot@1.0",
"android.hardware.boot@1.1",
+ "android.hardware.boot-V1-ndk",
+ "libboot_control_client",
"android.hardware.fastboot@1.1",
"android.hardware.health@2.0",
"android.hardware.health-V1-ndk",
@@ -192,9 +193,12 @@
"libhealthhalutils",
"libhealthshim",
"libsnapshot_cow",
+ "liblz4",
"libsnapshot_nobinder",
"update_metadata-protos",
+ "liburing",
],
+ include_dirs: ["bionic/libc/kernel"],
header_libs: [
"avb_headers",
@@ -215,7 +219,7 @@
"-Werror",
"-Wunreachable-code",
"-DANDROID_BASE_UNIQUE_FD_DISABLE_IMPLICIT_CONVERSION",
- "-D_FILE_OFFSET_BITS=64"
+ "-D_FILE_OFFSET_BITS=64",
],
target: {
@@ -256,6 +260,7 @@
"libsparse",
"libutils",
"liblog",
+ "liblz4",
"libz",
"libdiagnose_usb",
"libbase",
@@ -344,9 +349,7 @@
target: {
not_windows: {
required: [
- "e2fsdroid",
"mke2fs.conf",
- "sload_f2fs",
],
},
windows: {
@@ -411,7 +414,7 @@
":fastboot_test_vendor_ramdisk_replace",
":fastboot_test_vendor_boot_v3",
":fastboot_test_vendor_boot_v4_without_frag",
- ":fastboot_test_vendor_boot_v4_with_frag"
+ ":fastboot_test_vendor_boot_v4_with_frag",
],
}
diff --git a/fastboot/Android.mk b/fastboot/Android.mk
index 10bed6d..cde0cb2 100644
--- a/fastboot/Android.mk
+++ b/fastboot/Android.mk
@@ -19,9 +19,7 @@
#
my_dist_files := $(HOST_OUT_EXECUTABLES)/mke2fs
-my_dist_files += $(HOST_OUT_EXECUTABLES)/e2fsdroid
my_dist_files += $(HOST_OUT_EXECUTABLES)/make_f2fs
my_dist_files += $(HOST_OUT_EXECUTABLES)/make_f2fs_casefold
-my_dist_files += $(HOST_OUT_EXECUTABLES)/sload_f2fs
$(call dist-for-goals,dist_files sdk,$(my_dist_files))
my_dist_files :=
diff --git a/fastboot/OWNERS b/fastboot/OWNERS
index 17b3466..3dec07e 100644
--- a/fastboot/OWNERS
+++ b/fastboot/OWNERS
@@ -1,3 +1,5 @@
dvander@google.com
elsk@google.com
enh@google.com
+zhangkelvin@google.com
+
diff --git a/fastboot/constants.h b/fastboot/constants.h
index 4ea68da..b732c76 100644
--- a/fastboot/constants.h
+++ b/fastboot/constants.h
@@ -79,3 +79,4 @@
#define FB_VAR_SECURITY_PATCH_LEVEL "security-patch-level"
#define FB_VAR_TREBLE_ENABLED "treble-enabled"
#define FB_VAR_MAX_FETCH_SIZE "max-fetch-size"
+#define FB_VAR_DMESG "dmesg"
diff --git a/fastboot/device/commands.cpp b/fastboot/device/commands.cpp
index b9f6c97..823783e 100644
--- a/fastboot/device/commands.cpp
+++ b/fastboot/device/commands.cpp
@@ -40,6 +40,7 @@
#include <storage_literals/storage_literals.h>
#include <uuid/uuid.h>
+#include "BootControlClient.h"
#include "constants.h"
#include "fastboot_device.h"
#include "flashing.h"
@@ -52,15 +53,12 @@
#endif
using android::fs_mgr::MetadataBuilder;
+using android::hal::CommandResult;
using ::android::hardware::hidl_string;
-using ::android::hardware::boot::V1_0::BoolResult;
-using ::android::hardware::boot::V1_0::CommandResult;
-using ::android::hardware::boot::V1_0::Slot;
-using ::android::hardware::boot::V1_1::MergeStatus;
using ::android::hardware::fastboot::V1_0::Result;
using ::android::hardware::fastboot::V1_0::Status;
using android::snapshot::SnapshotManager;
-using IBootControl1_1 = ::android::hardware::boot::V1_1::IBootControl;
+using MergeStatus = android::hal::BootControlClient::MergeStatus;
using namespace android::storage_literals;
@@ -112,52 +110,65 @@
}
}
-bool GetVarHandler(FastbootDevice* device, const std::vector<std::string>& args) {
- const std::unordered_map<std::string, VariableHandlers> kVariableMap = {
- {FB_VAR_VERSION, {GetVersion, nullptr}},
- {FB_VAR_VERSION_BOOTLOADER, {GetBootloaderVersion, nullptr}},
- {FB_VAR_VERSION_BASEBAND, {GetBasebandVersion, nullptr}},
- {FB_VAR_VERSION_OS, {GetOsVersion, nullptr}},
- {FB_VAR_VERSION_VNDK, {GetVndkVersion, nullptr}},
- {FB_VAR_PRODUCT, {GetProduct, nullptr}},
- {FB_VAR_SERIALNO, {GetSerial, nullptr}},
- {FB_VAR_VARIANT, {GetVariant, nullptr}},
- {FB_VAR_SECURE, {GetSecure, nullptr}},
- {FB_VAR_UNLOCKED, {GetUnlocked, nullptr}},
- {FB_VAR_MAX_DOWNLOAD_SIZE, {GetMaxDownloadSize, nullptr}},
- {FB_VAR_CURRENT_SLOT, {::GetCurrentSlot, nullptr}},
- {FB_VAR_SLOT_COUNT, {GetSlotCount, nullptr}},
- {FB_VAR_HAS_SLOT, {GetHasSlot, GetAllPartitionArgsNoSlot}},
- {FB_VAR_SLOT_SUCCESSFUL, {GetSlotSuccessful, nullptr}},
- {FB_VAR_SLOT_UNBOOTABLE, {GetSlotUnbootable, nullptr}},
- {FB_VAR_PARTITION_SIZE, {GetPartitionSize, GetAllPartitionArgsWithSlot}},
- {FB_VAR_PARTITION_TYPE, {GetPartitionType, GetAllPartitionArgsWithSlot}},
- {FB_VAR_IS_LOGICAL, {GetPartitionIsLogical, GetAllPartitionArgsWithSlot}},
- {FB_VAR_IS_USERSPACE, {GetIsUserspace, nullptr}},
- {FB_VAR_OFF_MODE_CHARGE_STATE, {GetOffModeChargeState, nullptr}},
- {FB_VAR_BATTERY_VOLTAGE, {GetBatteryVoltage, nullptr}},
- {FB_VAR_BATTERY_SOC_OK, {GetBatterySoCOk, nullptr}},
- {FB_VAR_HW_REVISION, {GetHardwareRevision, nullptr}},
- {FB_VAR_SUPER_PARTITION_NAME, {GetSuperPartitionName, nullptr}},
- {FB_VAR_SNAPSHOT_UPDATE_STATUS, {GetSnapshotUpdateStatus, nullptr}},
- {FB_VAR_CPU_ABI, {GetCpuAbi, nullptr}},
- {FB_VAR_SYSTEM_FINGERPRINT, {GetSystemFingerprint, nullptr}},
- {FB_VAR_VENDOR_FINGERPRINT, {GetVendorFingerprint, nullptr}},
- {FB_VAR_DYNAMIC_PARTITION, {GetDynamicPartition, nullptr}},
- {FB_VAR_FIRST_API_LEVEL, {GetFirstApiLevel, nullptr}},
- {FB_VAR_SECURITY_PATCH_LEVEL, {GetSecurityPatchLevel, nullptr}},
- {FB_VAR_TREBLE_ENABLED, {GetTrebleEnabled, nullptr}},
- {FB_VAR_MAX_FETCH_SIZE, {GetMaxFetchSize, nullptr}},
- };
+const std::unordered_map<std::string, VariableHandlers> kVariableMap = {
+ {FB_VAR_VERSION, {GetVersion, nullptr}},
+ {FB_VAR_VERSION_BOOTLOADER, {GetBootloaderVersion, nullptr}},
+ {FB_VAR_VERSION_BASEBAND, {GetBasebandVersion, nullptr}},
+ {FB_VAR_VERSION_OS, {GetOsVersion, nullptr}},
+ {FB_VAR_VERSION_VNDK, {GetVndkVersion, nullptr}},
+ {FB_VAR_PRODUCT, {GetProduct, nullptr}},
+ {FB_VAR_SERIALNO, {GetSerial, nullptr}},
+ {FB_VAR_VARIANT, {GetVariant, nullptr}},
+ {FB_VAR_SECURE, {GetSecure, nullptr}},
+ {FB_VAR_UNLOCKED, {GetUnlocked, nullptr}},
+ {FB_VAR_MAX_DOWNLOAD_SIZE, {GetMaxDownloadSize, nullptr}},
+ {FB_VAR_CURRENT_SLOT, {::GetCurrentSlot, nullptr}},
+ {FB_VAR_SLOT_COUNT, {GetSlotCount, nullptr}},
+ {FB_VAR_HAS_SLOT, {GetHasSlot, GetAllPartitionArgsNoSlot}},
+ {FB_VAR_SLOT_SUCCESSFUL, {GetSlotSuccessful, nullptr}},
+ {FB_VAR_SLOT_UNBOOTABLE, {GetSlotUnbootable, nullptr}},
+ {FB_VAR_PARTITION_SIZE, {GetPartitionSize, GetAllPartitionArgsWithSlot}},
+ {FB_VAR_PARTITION_TYPE, {GetPartitionType, GetAllPartitionArgsWithSlot}},
+ {FB_VAR_IS_LOGICAL, {GetPartitionIsLogical, GetAllPartitionArgsWithSlot}},
+ {FB_VAR_IS_USERSPACE, {GetIsUserspace, nullptr}},
+ {FB_VAR_OFF_MODE_CHARGE_STATE, {GetOffModeChargeState, nullptr}},
+ {FB_VAR_BATTERY_VOLTAGE, {GetBatteryVoltage, nullptr}},
+ {FB_VAR_BATTERY_SOC_OK, {GetBatterySoCOk, nullptr}},
+ {FB_VAR_HW_REVISION, {GetHardwareRevision, nullptr}},
+ {FB_VAR_SUPER_PARTITION_NAME, {GetSuperPartitionName, nullptr}},
+ {FB_VAR_SNAPSHOT_UPDATE_STATUS, {GetSnapshotUpdateStatus, nullptr}},
+ {FB_VAR_CPU_ABI, {GetCpuAbi, nullptr}},
+ {FB_VAR_SYSTEM_FINGERPRINT, {GetSystemFingerprint, nullptr}},
+ {FB_VAR_VENDOR_FINGERPRINT, {GetVendorFingerprint, nullptr}},
+ {FB_VAR_DYNAMIC_PARTITION, {GetDynamicPartition, nullptr}},
+ {FB_VAR_FIRST_API_LEVEL, {GetFirstApiLevel, nullptr}},
+ {FB_VAR_SECURITY_PATCH_LEVEL, {GetSecurityPatchLevel, nullptr}},
+ {FB_VAR_TREBLE_ENABLED, {GetTrebleEnabled, nullptr}},
+ {FB_VAR_MAX_FETCH_SIZE, {GetMaxFetchSize, nullptr}},
+};
+static bool GetVarAll(FastbootDevice* device) {
+ for (const auto& [name, handlers] : kVariableMap) {
+ GetAllVars(device, name, handlers);
+ }
+ return true;
+}
+
+const std::unordered_map<std::string, std::function<bool(FastbootDevice*)>> kSpecialVars = {
+ {"all", GetVarAll},
+ {"dmesg", GetDmesg},
+};
+
+bool GetVarHandler(FastbootDevice* device, const std::vector<std::string>& args) {
if (args.size() < 2) {
return device->WriteFail("Missing argument");
}
- // Special case: return all variables that we can.
- if (args[1] == "all") {
- for (const auto& [name, handlers] : kVariableMap) {
- GetAllVars(device, name, handlers);
+ // "all" and "dmesg" are multiline and handled specially.
+ auto found_special = kSpecialVars.find(args[1]);
+ if (found_special != kSpecialVars.end()) {
+ if (!found_special->second(device)) {
+ return false;
}
return device->WriteOkay("");
}
@@ -254,6 +265,7 @@
return device->WriteStatus(FastbootResult::FAIL, ret.message);
}
+ device->WriteInfo(ret.message);
return device->WriteStatus(FastbootResult::OKAY, ret.message);
}
@@ -303,7 +315,7 @@
"set_active command is not allowed on locked devices");
}
- Slot slot;
+ int32_t slot = 0;
if (!GetSlotNumber(args[1], &slot)) {
// Slot suffix needs to be between 'a' and 'z'.
return device->WriteStatus(FastbootResult::FAIL, "Bad slot suffix");
@@ -315,7 +327,7 @@
return device->WriteStatus(FastbootResult::FAIL,
"Cannot set slot: boot control HAL absent");
}
- if (slot >= boot_control_hal->getNumberSlots()) {
+ if (slot >= boot_control_hal->GetNumSlots()) {
return device->WriteStatus(FastbootResult::FAIL, "Slot out of range");
}
@@ -344,10 +356,8 @@
}
}
- CommandResult ret;
- auto cb = [&ret](CommandResult result) { ret = result; };
- auto result = boot_control_hal->setActiveBootSlot(slot, cb);
- if (result.isOk() && ret.success) {
+ CommandResult ret = boot_control_hal->SetActiveBootSlot(slot);
+ if (ret.success) {
// Save as slot suffix to match the suffix format as returned from
// the boot control HAL.
auto current_slot = "_" + args[1];
@@ -668,9 +678,14 @@
if (args[1] == "cancel") {
switch (status) {
case MergeStatus::SNAPSHOTTED:
- case MergeStatus::MERGING:
- hal->setSnapshotMergeStatus(MergeStatus::CANCELLED);
+ case MergeStatus::MERGING: {
+ const auto ret = hal->SetSnapshotMergeStatus(MergeStatus::CANCELLED);
+ if (!ret.success) {
+ device->WriteFail("Failed to SetSnapshotMergeStatus(MergeStatus::CANCELLED) " +
+ ret.errMsg);
+ }
break;
+ }
default:
break;
}
diff --git a/fastboot/device/fastboot_device.cpp b/fastboot/device/fastboot_device.cpp
index ae225de..4932e5c 100644
--- a/fastboot/device/fastboot_device.cpp
+++ b/fastboot/device/fastboot_device.cpp
@@ -18,6 +18,7 @@
#include <algorithm>
+#include <BootControlClient.h>
#include <android-base/logging.h>
#include <android-base/properties.h>
#include <android-base/strings.h>
@@ -38,9 +39,8 @@
using android::fs_mgr::EnsurePathUnmounted;
using android::fs_mgr::Fstab;
using ::android::hardware::hidl_string;
-using ::android::hardware::boot::V1_0::IBootControl;
-using ::android::hardware::boot::V1_0::Slot;
using ::android::hardware::fastboot::V1_1::IFastboot;
+using BootControlClient = FastbootDevice::BootControlClient;
namespace sph = std::placeholders;
@@ -85,7 +85,7 @@
{FB_CMD_SNAPSHOT_UPDATE, SnapshotUpdateHandler},
{FB_CMD_FETCH, FetchHandler},
}),
- boot_control_hal_(IBootControl::getService()),
+ boot_control_hal_(BootControlClient::WaitForService()),
health_hal_(get_health_service()),
fastboot_hal_(IFastboot::getService()),
active_slot_("") {
@@ -95,10 +95,6 @@
transport_ = std::make_unique<ClientUsbTransport>();
}
- if (boot_control_hal_) {
- boot1_1_ = android::hardware::boot::V1_1::IBootControl::castFrom(boot_control_hal_);
- }
-
// Make sure cache is unmounted, since recovery will have mounted it for
// logging.
Fstab fstab;
@@ -125,12 +121,17 @@
if (!boot_control_hal_) {
return "";
}
- std::string suffix;
- auto cb = [&suffix](hidl_string s) { suffix = s; };
- boot_control_hal_->getSuffix(boot_control_hal_->getCurrentSlot(), cb);
+ std::string suffix = boot_control_hal_->GetSuffix(boot_control_hal_->GetCurrentSlot());
return suffix;
}
+BootControlClient* FastbootDevice::boot1_1() const {
+ if (boot_control_hal_->GetVersion() >= android::hal::BootControlVersion::BOOTCTL_V1_1) {
+ return boot_control_hal_.get();
+ }
+ return nullptr;
+}
+
bool FastbootDevice::WriteStatus(FastbootResult result, const std::string& message) {
constexpr size_t kResponseReasonSize = 4;
constexpr size_t kNumResponseTypes = 4; // "FAIL", "OKAY", "INFO", "DATA"
diff --git a/fastboot/device/fastboot_device.h b/fastboot/device/fastboot_device.h
index 91ffce3..9df8fa5 100644
--- a/fastboot/device/fastboot_device.h
+++ b/fastboot/device/fastboot_device.h
@@ -22,9 +22,8 @@
#include <utility>
#include <vector>
+#include <BootControlClient.h>
#include <aidl/android/hardware/health/IHealth.h>
-#include <android/hardware/boot/1.0/IBootControl.h>
-#include <android/hardware/boot/1.1/IBootControl.h>
#include <android/hardware/fastboot/1.1/IFastboot.h>
#include "commands.h"
@@ -33,6 +32,7 @@
class FastbootDevice {
public:
+ using BootControlClient = android::hal::BootControlClient;
FastbootDevice();
~FastbootDevice();
@@ -50,10 +50,8 @@
std::vector<char>& download_data() { return download_data_; }
Transport* get_transport() { return transport_.get(); }
- android::sp<android::hardware::boot::V1_0::IBootControl> boot_control_hal() {
- return boot_control_hal_;
- }
- android::sp<android::hardware::boot::V1_1::IBootControl> boot1_1() { return boot1_1_; }
+ BootControlClient* boot_control_hal() const { return boot_control_hal_.get(); }
+ BootControlClient* boot1_1() const;
android::sp<android::hardware::fastboot::V1_1::IFastboot> fastboot_hal() {
return fastboot_hal_;
}
@@ -65,8 +63,7 @@
const std::unordered_map<std::string, CommandHandler> kCommandMap;
std::unique_ptr<Transport> transport_;
- android::sp<android::hardware::boot::V1_0::IBootControl> boot_control_hal_;
- android::sp<android::hardware::boot::V1_1::IBootControl> boot1_1_;
+ std::unique_ptr<BootControlClient> boot_control_hal_;
std::shared_ptr<aidl::android::hardware::health::IHealth> health_hal_;
android::sp<android::hardware::fastboot::V1_1::IFastboot> fastboot_hal_;
std::vector<char> download_data_;
diff --git a/fastboot/device/flashing.cpp b/fastboot/device/flashing.cpp
index 06ffe0f..05186a2 100644
--- a/fastboot/device/flashing.cpp
+++ b/fastboot/device/flashing.cpp
@@ -121,7 +121,12 @@
int WriteCallback(void* priv, const void* data, size_t len) {
PartitionHandle* handle = reinterpret_cast<PartitionHandle*>(priv);
if (!data) {
- return lseek64(handle->fd(), len, SEEK_CUR) >= 0 ? 0 : -errno;
+ if (lseek64(handle->fd(), len, SEEK_CUR) < 0) {
+ int rv = -errno;
+ PLOG(ERROR) << "lseek failed";
+ return rv;
+ }
+ return 0;
}
return FlashRawDataChunk(handle, reinterpret_cast<const char*>(data), len);
}
@@ -131,6 +136,7 @@
downloaded_data.size(), true, false);
if (!file) {
// Invalid sparse format
+ LOG(ERROR) << "Unable to open sparse data for flashing";
return -EINVAL;
}
return sparse_file_callback(file, false, false, WriteCallback, reinterpret_cast<void*>(handle));
@@ -175,10 +181,13 @@
std::vector<char> data = std::move(device->download_data());
if (data.size() == 0) {
+ LOG(ERROR) << "Cannot flash empty data vector";
return -EINVAL;
}
uint64_t block_device_size = get_block_device_size(handle.fd());
if (data.size() > block_device_size) {
+ LOG(ERROR) << "Cannot flash " << data.size() << " bytes to block device of size "
+ << block_device_size;
return -EOVERFLOW;
} else if (data.size() < block_device_size &&
(partition_name == "boot" || partition_name == "boot_a" ||
diff --git a/fastboot/device/main.cpp b/fastboot/device/main.cpp
index df9c900..08c9358 100644
--- a/fastboot/device/main.cpp
+++ b/fastboot/device/main.cpp
@@ -14,13 +14,30 @@
* limitations under the License.
*/
+#include <stdarg.h>
+
#include <android-base/logging.h>
+#include <android-base/stringprintf.h>
+#include <sparse/sparse.h>
#include "fastboot_device.h"
+static void LogSparseVerboseMessage(const char* fmt, ...) {
+ std::string message;
+
+ va_list ap;
+ va_start(ap, fmt);
+ android::base::StringAppendV(&message, fmt, ap);
+ va_end(ap);
+
+ LOG(ERROR) << "libsparse message: " << message;
+}
+
int main(int /*argc*/, char* argv[]) {
android::base::InitLogging(argv, &android::base::KernelLogger);
+ sparse_print_verbose = LogSparseVerboseMessage;
+
while (true) {
FastbootDevice device;
device.ExecuteCommands();
diff --git a/fastboot/device/usb.cpp b/fastboot/device/usb.cpp
index 4115a6d..b77d772 100644
--- a/fastboot/device/usb.cpp
+++ b/fastboot/device/usb.cpp
@@ -15,6 +15,7 @@
*/
#include "usb.h"
+#include "usb_iouring.h"
#include <dirent.h>
#include <errno.h>
@@ -28,6 +29,7 @@
#include <linux/usb/ch9.h>
#include <linux/usb/functionfs.h>
+#include <sys/utsname.h>
#include <algorithm>
#include <atomic>
@@ -38,6 +40,7 @@
#include <android-base/logging.h>
#include <android-base/properties.h>
+#include <liburing.h>
using namespace std::chrono_literals;
@@ -65,8 +68,8 @@
}
}
-static int getMaxPacketSize(int ffs_fd) {
- usb_endpoint_descriptor desc;
+int getMaxPacketSize(int ffs_fd) {
+ usb_endpoint_descriptor desc{};
if (ioctl(ffs_fd, FUNCTIONFS_ENDPOINT_DESC, reinterpret_cast<unsigned long>(&desc))) {
D("[ could not get endpoint descriptor! (%d) ]", errno);
return MAX_PACKET_SIZE_HS;
@@ -128,11 +131,9 @@
static int usb_ffs_do_aio(usb_handle* h, const void* data, int len, bool read) {
aio_block* aiob = read ? &h->read_aiob : &h->write_aiob;
- bool zero_packet = false;
int num_bufs = len / h->io_size + (len % h->io_size == 0 ? 0 : 1);
const char* cur_data = reinterpret_cast<const char*>(data);
- int packet_size = getMaxPacketSize(aiob->fd);
if (posix_madvise(const_cast<void*>(data), len, POSIX_MADV_SEQUENTIAL | POSIX_MADV_WILLNEED) <
0) {
@@ -145,17 +146,6 @@
len -= buf_len;
cur_data += buf_len;
-
- if (len == 0 && buf_len % packet_size == 0 && read) {
- // adb does not expect the device to send a zero packet after data transfer,
- // but the host *does* send a zero packet for the device to read.
- zero_packet = h->reads_zero_packets;
- }
- }
- if (zero_packet) {
- io_prep(&aiob->iocb[num_bufs], aiob->fd, reinterpret_cast<const void*>(cur_data),
- packet_size, 0, read);
- num_bufs += 1;
}
while (true) {
@@ -204,21 +194,46 @@
h->open_new_connection = true;
h->lock.unlock();
h->notify.notify_one();
+ if (h->aio_type == AIOType::IO_URING) {
+ exit_io_uring_ffs(h);
+ }
}
-usb_handle* create_usb_handle(unsigned num_bufs, unsigned io_size) {
- usb_handle* h = new usb_handle();
+bool DoesKernelSupportIouring() {
+ struct utsname uts {};
+ unsigned int major = 0, minor = 0;
+ if ((uname(&uts) != 0) || (sscanf(uts.release, "%u.%u", &major, &minor) != 2)) {
+ return false;
+ }
+ if (major > 5) {
+ return true;
+ }
+ // We will only support kernels from 5.6 onwards as IOSQE_ASYNC flag and
+ // IO_URING_OP_READ/WRITE opcodes were introduced only on 5.6 kernel
+ return minor >= 6;
+}
- if (android::base::GetBoolProperty("sys.usb.ffs.aio_compat", false)) {
+std::unique_ptr<usb_handle> create_usb_handle(unsigned num_bufs, unsigned io_size) {
+ auto h = std::make_unique<usb_handle>();
+ if (DoesKernelSupportIouring() &&
+ android::base::GetBoolProperty("sys.usb.ffs.io_uring_enabled", false)) {
+ init_io_uring_ffs(h.get(), num_bufs);
+ h->aio_type = AIOType::IO_URING;
+ LOG(INFO) << "Using io_uring for usb ffs";
+ } else if (android::base::GetBoolProperty("sys.usb.ffs.aio_compat", false)) {
// Devices on older kernels (< 3.18) will not have aio support for ffs
// unless backported. Fall back on the non-aio functions instead.
h->write = usb_ffs_write;
h->read = usb_ffs_read;
+ h->aio_type = AIOType::SYNC_IO;
+ LOG(INFO) << "Using sync io for usb ffs";
} else {
h->write = usb_ffs_aio_write;
h->read = usb_ffs_aio_read;
aio_block_init(&h->read_aiob, num_bufs);
aio_block_init(&h->write_aiob, num_bufs);
+ h->aio_type = AIOType::AIO;
+ LOG(INFO) << "Using aio for usb ffs";
}
h->io_size = io_size;
h->close = usb_ffs_close;
diff --git a/fastboot/device/usb.h b/fastboot/device/usb.h
index 6c3f542..8996c31 100644
--- a/fastboot/device/usb.h
+++ b/fastboot/device/usb.h
@@ -18,8 +18,10 @@
#include <linux/usb/functionfs.h>
+#include <liburing.h>
#include <atomic>
#include <condition_variable>
+#include <memory>
#include <mutex>
#include <vector>
@@ -35,9 +37,11 @@
int fd;
};
-struct usb_handle {
- usb_handle() {}
+int getMaxPacketSize(int ffs_fd);
+enum class AIOType { SYNC_IO, AIO, IO_URING };
+
+struct usb_handle {
std::condition_variable notify;
std::mutex lock;
bool open_new_connection = true;
@@ -56,8 +60,9 @@
struct aio_block read_aiob;
struct aio_block write_aiob;
- bool reads_zero_packets;
+ io_uring ring;
size_t io_size;
+ AIOType aio_type;
};
-usb_handle* create_usb_handle(unsigned num_bufs, unsigned io_size);
+std::unique_ptr<usb_handle> create_usb_handle(unsigned num_bufs, unsigned io_size);
diff --git a/fastboot/device/usb_client.cpp b/fastboot/device/usb_client.cpp
index 3f9b0f0..d1b38d4 100644
--- a/fastboot/device/usb_client.cpp
+++ b/fastboot/device/usb_client.cpp
@@ -232,7 +232,6 @@
h->read_aiob.fd = h->bulk_out.get();
h->write_aiob.fd = h->bulk_in.get();
- h->reads_zero_packets = false;
return true;
err:
diff --git a/fastboot/device/usb_iouring.cpp b/fastboot/device/usb_iouring.cpp
new file mode 100644
index 0000000..d987712
--- /dev/null
+++ b/fastboot/device/usb_iouring.cpp
@@ -0,0 +1,140 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <android-base/logging.h>
+#include <liburing.h>
+#include "liburing/io_uring.h"
+#include "usb.h"
+
+static int prep_async_read(struct io_uring* ring, int fd, void* data, size_t len, int64_t offset) {
+ if (io_uring_sq_space_left(ring) <= 0) {
+ LOG(ERROR) << "Submission queue run out of space.";
+ return -1;
+ }
+ auto sqe = io_uring_get_sqe(ring);
+ if (sqe == nullptr) {
+ return -1;
+ }
+ io_uring_sqe_set_flags(sqe, IOSQE_IO_LINK | IOSQE_ASYNC);
+ io_uring_prep_read(sqe, fd, data, len, offset);
+ return 0;
+}
+
+static int prep_async_write(struct io_uring* ring, int fd, const void* data, size_t len,
+ int64_t offset) {
+ if (io_uring_sq_space_left(ring) <= 0) {
+ LOG(ERROR) << "Submission queue run out of space.";
+ return -1;
+ }
+ auto sqe = io_uring_get_sqe(ring);
+ if (sqe == nullptr) {
+ return -1;
+ }
+ io_uring_sqe_set_flags(sqe, IOSQE_IO_LINK | IOSQE_ASYNC);
+ io_uring_prep_write(sqe, fd, data, len, offset);
+ return 0;
+}
+
+template <bool read, typename T>
+int prep_async_io(struct io_uring* ring, int fd, T* data, size_t len, int64_t offset) {
+ if constexpr (read) {
+ return prep_async_read(ring, fd, data, len, offset);
+ } else {
+ return prep_async_write(ring, fd, data, len, offset);
+ }
+}
+
+template <typename T>
+static constexpr T DivRoundup(T x, T y) {
+ return (x + y - 1) / y;
+}
+
+extern int getMaxPacketSize(int ffs_fd);
+
+template <bool read, typename T>
+static int usb_ffs_do_aio(usb_handle* h, T* const data, const int len) {
+ const aio_block* aiob = read ? &h->read_aiob : &h->write_aiob;
+ const int num_requests = DivRoundup<int>(len, h->io_size);
+ auto cur_data = data;
+ const auto packet_size = getMaxPacketSize(aiob->fd);
+
+ for (int bytes_remain = len; bytes_remain > 0;) {
+ const int buf_len = std::min(bytes_remain, static_cast<int>(h->io_size));
+ const auto ret = prep_async_io<read>(&h->ring, aiob->fd, cur_data, buf_len, 0);
+ if (ret < 0) {
+ PLOG(ERROR) << "Failed to queue io_uring request";
+ return -1;
+ }
+
+ bytes_remain -= buf_len;
+ cur_data = reinterpret_cast<T*>(reinterpret_cast<size_t>(cur_data) + buf_len);
+ }
+ const int ret = io_uring_submit(&h->ring);
+ if (ret <= 0 || ret != num_requests) {
+ PLOG(ERROR) << "io_uring: failed to submit SQE entries to kernel";
+ return -1;
+ }
+ int res = 0;
+ bool success = true;
+ for (int i = 0; i < num_requests; ++i) {
+ struct io_uring_cqe* cqe{};
+ const auto ret = TEMP_FAILURE_RETRY(io_uring_wait_cqe(&h->ring, &cqe));
+ if (ret < 0 || cqe == nullptr) {
+ PLOG(ERROR) << "Failed to get CQE from kernel";
+ success = false;
+ continue;
+ }
+ res += cqe->res;
+ if (cqe->res < 0) {
+ LOG(ERROR) << "io_uring request failed:, i = " << i
+ << ", num_requests = " << num_requests << ", res = " << cqe->res << ": "
+ << strerror(cqe->res) << (read ? " read" : " write")
+ << " request size: " << len << ", io_size: " << h->io_size
+ << " max packet size: " << packet_size << ", fd: " << aiob->fd;
+ success = false;
+ errno = -cqe->res;
+ }
+ io_uring_cqe_seen(&h->ring, cqe);
+ }
+ if (!success) {
+ return -1;
+ }
+ return res;
+}
+
+static int usb_ffs_io_uring_read(usb_handle* h, void* data, int len, bool /* allow_partial */) {
+ return usb_ffs_do_aio<true>(h, data, len);
+}
+
+static int usb_ffs_io_uring_write(usb_handle* h, const void* data, int len) {
+ return usb_ffs_do_aio<false>(h, data, len);
+}
+
+void exit_io_uring_ffs(usb_handle* h) {
+ io_uring_queue_exit(&h->ring);
+}
+
+bool init_io_uring_ffs(usb_handle* h, size_t queue_depth) {
+ const auto err = io_uring_queue_init(queue_depth, &h->ring, 0);
+ if (err) {
+ LOG(ERROR) << "Failed to initialize io_uring of depth " << queue_depth << ": "
+ << strerror(err);
+ return false;
+ }
+ h->write = usb_ffs_io_uring_write;
+ h->read = usb_ffs_io_uring_read;
+ return true;
+}
diff --git a/fastboot/device/usb_iouring.h b/fastboot/device/usb_iouring.h
new file mode 100644
index 0000000..7c14b81
--- /dev/null
+++ b/fastboot/device/usb_iouring.h
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2022 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 "usb.h"
+
+bool init_io_uring_ffs(usb_handle* h, size_t queue_depth);
+
+void exit_io_uring_ffs(usb_handle* h);
diff --git a/fastboot/device/utility.cpp b/fastboot/device/utility.cpp
index 3302c43..e12ee64 100644
--- a/fastboot/device/utility.cpp
+++ b/fastboot/device/utility.cpp
@@ -36,7 +36,6 @@
using namespace android::fs_mgr;
using namespace std::chrono_literals;
using android::base::unique_fd;
-using android::hardware::boot::V1_0::Slot;
namespace {
@@ -137,7 +136,7 @@
return true;
}
-bool GetSlotNumber(const std::string& slot, Slot* number) {
+bool GetSlotNumber(const std::string& slot, int32_t* number) {
if (slot.size() != 1) {
return false;
}
@@ -204,7 +203,7 @@
size_t num_slots = 1;
auto boot_control_hal = device->boot_control_hal();
if (boot_control_hal) {
- num_slots = boot_control_hal->getNumberSlots();
+ num_slots = boot_control_hal->GetNumSlots();
}
bool ok = true;
diff --git a/fastboot/device/utility.h b/fastboot/device/utility.h
index 6e1453f..a62125e 100644
--- a/fastboot/device/utility.h
+++ b/fastboot/device/utility.h
@@ -21,7 +21,6 @@
#include <android-base/file.h>
#include <android-base/logging.h>
#include <android-base/unique_fd.h>
-#include <android/hardware/boot/1.0/IBootControl.h>
#include <fstab/fstab.h>
#include <liblp/liblp.h>
@@ -124,7 +123,7 @@
bool OpenPartition(FastbootDevice* device, const std::string& name, PartitionHandle* handle,
int flags = O_WRONLY);
-bool GetSlotNumber(const std::string& slot, android::hardware::boot::V1_0::Slot* number);
+bool GetSlotNumber(const std::string& slot, int32_t* number);
std::vector<std::string> ListPartitions(FastbootDevice* device);
bool GetDeviceLockStatus();
diff --git a/fastboot/device/variables.cpp b/fastboot/device/variables.cpp
index 0cf4699..b6eb2cd 100644
--- a/fastboot/device/variables.cpp
+++ b/fastboot/device/variables.cpp
@@ -17,6 +17,7 @@
#include "variables.h"
#include <inttypes.h>
+#include <stdio.h>
#include <android-base/file.h>
#include <android-base/logging.h>
@@ -28,6 +29,7 @@
#include <fs_mgr.h>
#include <liblp/liblp.h>
+#include "BootControlClient.h"
#include "fastboot_device.h"
#include "flashing.h"
#include "utility.h"
@@ -38,14 +40,12 @@
static constexpr bool kEnableFetch = false;
#endif
-using ::android::hardware::boot::V1_0::BoolResult;
-using ::android::hardware::boot::V1_0::Slot;
-using ::android::hardware::boot::V1_1::MergeStatus;
+using MergeStatus = android::hal::BootControlClient::MergeStatus;
using ::android::hardware::fastboot::V1_0::FileSystemType;
using ::android::hardware::fastboot::V1_0::Result;
using ::android::hardware::fastboot::V1_0::Status;
-using IBootControl1_1 = ::android::hardware::boot::V1_1::IBootControl;
using namespace android::fs_mgr;
+using namespace std::string_literals;
constexpr char kFastbootProtocolVersion[] = "0.4";
@@ -208,7 +208,7 @@
if (!boot_control_hal) {
*message = "0";
} else {
- *message = std::to_string(boot_control_hal->getNumberSlots());
+ *message = std::to_string(boot_control_hal->GetNumSlots());
}
return true;
}
@@ -219,7 +219,7 @@
*message = "Missing argument";
return false;
}
- Slot slot;
+ int32_t slot = -1;
if (!GetSlotNumber(args[0], &slot)) {
*message = "Invalid slot";
return false;
@@ -229,7 +229,7 @@
*message = "Device has no slots";
return false;
}
- if (boot_control_hal->isSlotMarkedSuccessful(slot) != BoolResult::TRUE) {
+ if (boot_control_hal->IsSlotMarkedSuccessful(slot).value_or(false)) {
*message = "no";
} else {
*message = "yes";
@@ -243,7 +243,7 @@
*message = "Missing argument";
return false;
}
- Slot slot;
+ int32_t slot = -1;
if (!GetSlotNumber(args[0], &slot)) {
*message = "Invalid slot";
return false;
@@ -253,7 +253,7 @@
*message = "Device has no slots";
return false;
}
- if (boot_control_hal->isSlotBootable(slot) != BoolResult::TRUE) {
+ if (!boot_control_hal->IsSlotBootable(slot).value_or(false)) {
*message = "yes";
} else {
*message = "no";
@@ -518,3 +518,36 @@
*message = android::base::StringPrintf("0x%X", kMaxFetchSizeDefault);
return true;
}
+
+bool GetDmesg(FastbootDevice* device) {
+ if (GetDeviceLockStatus()) {
+ return device->WriteFail("Cannot use when device flashing is locked");
+ }
+
+ std::unique_ptr<FILE, decltype(&::fclose)> fp(popen("/system/bin/dmesg", "re"), ::fclose);
+ if (!fp) {
+ PLOG(ERROR) << "popen /system/bin/dmesg";
+ return device->WriteFail("Unable to run dmesg: "s + strerror(errno));
+ }
+
+ ssize_t rv;
+ size_t n = 0;
+ char* str = nullptr;
+ while ((rv = ::getline(&str, &n, fp.get())) > 0) {
+ if (str[rv - 1] == '\n') {
+ rv--;
+ }
+ device->WriteInfo(std::string(str, rv));
+ }
+
+ int saved_errno = errno;
+ ::free(str);
+
+ if (rv < 0 && saved_errno) {
+ LOG(ERROR) << "dmesg getline: " << strerror(saved_errno);
+ device->WriteFail("Unable to read dmesg: "s + strerror(saved_errno));
+ return false;
+ }
+
+ return true;
+}
diff --git a/fastboot/device/variables.h b/fastboot/device/variables.h
index f40a025..aa4d9fc 100644
--- a/fastboot/device/variables.h
+++ b/fastboot/device/variables.h
@@ -83,6 +83,9 @@
bool GetMaxFetchSize(FastbootDevice* /* device */, const std::vector<std::string>& /* args */,
std::string* message);
+// Complex cases.
+bool GetDmesg(FastbootDevice* device);
+
// Helpers for getvar all.
std::vector<std::vector<std::string>> GetAllPartitionArgsWithSlot(FastbootDevice* device);
std::vector<std::vector<std::string>> GetAllPartitionArgsNoSlot(FastbootDevice* device);
diff --git a/fastboot/fastboot.bash b/fastboot/fastboot.bash
index e9bf9e9..911071a 100644
--- a/fastboot/fastboot.bash
+++ b/fastboot/fastboot.bash
@@ -109,7 +109,7 @@
cur="${COMP_WORDS[COMP_CWORD]}"
if [[ $i -eq $COMP_CWORD ]]; then
- partitions="boot bootloader dtbo init_boot modem odm odm_dlkm oem product pvmfw radio recovery system system_dlkm vbmeta vendor vendor_dlkm"
+ partitions="boot bootloader dtbo init_boot modem odm odm_dlkm oem product pvmfw radio recovery system system_dlkm vbmeta vendor vendor_dlkm vendor_kernel_boot"
COMPREPLY=( $(compgen -W "$partitions" -- $cur) )
else
_fastboot_util_complete_local_file "${cur}" '!*.img'
diff --git a/fastboot/fastboot.cpp b/fastboot/fastboot.cpp
index 39d86f9..46190f2 100644
--- a/fastboot/fastboot.cpp
+++ b/fastboot/fastboot.cpp
@@ -186,6 +186,11 @@
"vendor_dlkm.img", "vendor_dlkm.sig",
"vendor_dlkm",
true, ImageType::Normal },
+ { "vendor_kernel_boot",
+ "vendor_kernel_boot.img",
+ "vendor_kernel_boot.sig",
+ "vendor_kernel_boot",
+ true, ImageType::BootCritical },
{ nullptr, "vendor_other.img", "vendor.sig", "vendor", true, ImageType::Normal },
// clang-format on
};
@@ -1678,10 +1683,9 @@
return size;
}
-static void fb_perform_format(
- const std::string& partition, int skip_if_not_supported,
+static void fb_perform_format(const std::string& partition, int skip_if_not_supported,
const std::string& type_override, const std::string& size_override,
- const std::string& initial_dir, const unsigned fs_options) {
+ const unsigned fs_options) {
std::string partition_type, partition_size;
struct fastboot_buffer buf;
@@ -1743,8 +1747,7 @@
eraseBlkSize = fb_get_flash_block_size("erase-block-size");
logicalBlkSize = fb_get_flash_block_size("logical-block-size");
- if (fs_generator_generate(gen, output.path, size, initial_dir,
- eraseBlkSize, logicalBlkSize, fs_options)) {
+ if (fs_generator_generate(gen, output.path, size, eraseBlkSize, logicalBlkSize, fs_options)) {
die("Cannot generate image for %s", partition.c_str());
}
@@ -2086,7 +2089,7 @@
std::string partition = next_arg(&args);
auto format = [&](const std::string& partition) {
- fb_perform_format(partition, 0, type_override, size_override, "", fs_options);
+ fb_perform_format(partition, 0, type_override, size_override, fs_options);
};
do_for_partitions(partition, slot_override, format, true);
} else if (command == "signature") {
@@ -2277,7 +2280,7 @@
}
if (partition_type.empty()) continue;
fb->Erase(partition);
- fb_perform_format(partition, 1, partition_type, "", "", fs_options);
+ fb_perform_format(partition, 1, partition_type, "", fs_options);
}
}
if (wants_set_active) {
diff --git a/fastboot/fs.cpp b/fastboot/fs.cpp
index d268a50..c8d1b59 100644
--- a/fastboot/fs.cpp
+++ b/fastboot/fs.cpp
@@ -111,8 +111,7 @@
}
#endif
-static int generate_ext4_image(const char* fileName, long long partSize,
- const std::string& initial_dir, unsigned eraseBlkSize,
+static int generate_ext4_image(const char* fileName, long long partSize, unsigned eraseBlkSize,
unsigned logicalBlkSize, const unsigned fsOptions) {
static constexpr int block_size = 4096;
const std::string exec_dir = android::base::GetExecutableDirectory();
@@ -163,16 +162,7 @@
if (ret != 0) {
return -1;
}
-
- if (initial_dir.empty()) {
- return 0;
- }
-
- const std::string e2fsdroid_path = exec_dir + "/e2fsdroid";
- std::vector<const char*> e2fsdroid_args = {e2fsdroid_path.c_str(), "-f", initial_dir.c_str(),
- fileName, nullptr};
-
- return exec_cmd(e2fsdroid_args[0], e2fsdroid_args.data(), nullptr);
+ return 0;
}
enum {
@@ -188,8 +178,7 @@
// clang-format on
};
-static int generate_f2fs_image(const char* fileName, long long partSize,
- const std::string& initial_dir, unsigned /* unused */,
+static int generate_f2fs_image(const char* fileName, long long partSize, unsigned /* unused */,
unsigned /* unused */, const unsigned fsOptions) {
const std::string exec_dir = android::base::GetExecutableDirectory();
const std::string mkf2fs_path = exec_dir + "/make_f2fs";
@@ -227,19 +216,6 @@
if (ret != 0) {
return -1;
}
-
- if (initial_dir.empty()) {
- return 0;
- }
-
- const std::string sload_path = exec_dir + "/sload_f2fs";
- std::vector<const char*> sload_args = {sload_path.c_str(), "-S",
- "-f", initial_dir.c_str(), fileName, nullptr};
-
- ret = exec_cmd(sload_args[0], sload_args.data(), nullptr);
- if (ret != 0 && ret != FSCK_ERROR_CORRECTED) {
- return -1;
- }
return 0;
}
@@ -247,8 +223,8 @@
const char* fs_type; //must match what fastboot reports for partition type
//returns 0 or error value
- int (*generate)(const char* fileName, long long partSize, const std::string& initial_dir,
- unsigned eraseBlkSize, unsigned logicalBlkSize, const unsigned fsOptions);
+ int (*generate)(const char* fileName, long long partSize, unsigned eraseBlkSize,
+ unsigned logicalBlkSize, const unsigned fsOptions);
} generators[] = {
{ "ext4", generate_ext4_image},
@@ -265,7 +241,7 @@
}
int fs_generator_generate(const struct fs_generator* gen, const char* fileName, long long partSize,
- const std::string& initial_dir, unsigned eraseBlkSize,
- unsigned logicalBlkSize, const unsigned fsOptions) {
- return gen->generate(fileName, partSize, initial_dir, eraseBlkSize, logicalBlkSize, fsOptions);
+ unsigned eraseBlkSize, unsigned logicalBlkSize,
+ const unsigned fsOptions) {
+ return gen->generate(fileName, partSize, eraseBlkSize, logicalBlkSize, fsOptions);
}
diff --git a/fastboot/fs.h b/fastboot/fs.h
index f832938..5ae473b 100644
--- a/fastboot/fs.h
+++ b/fastboot/fs.h
@@ -13,5 +13,5 @@
const struct fs_generator* fs_get_generator(const std::string& fs_type);
int fs_generator_generate(const struct fs_generator* gen, const char* fileName, long long partSize,
- const std::string& initial_dir, unsigned eraseBlkSize = 0,
- unsigned logicalBlkSize = 0, unsigned fsOptions = 0);
+ unsigned eraseBlkSize = 0, unsigned logicalBlkSize = 0,
+ unsigned fsOptions = 0);
diff --git a/fastboot/fuzzy_fastboot/Android.bp b/fastboot/fuzzy_fastboot/Android.bp
index 159c314..2031170 100644
--- a/fastboot/fuzzy_fastboot/Android.bp
+++ b/fastboot/fuzzy_fastboot/Android.bp
@@ -9,6 +9,7 @@
cc_test_host {
name: "fuzzy_fastboot",
+ isolated: false,
compile_multilib: "first",
srcs: [
diff --git a/fastboot/vendor_boot_img_utils.cpp b/fastboot/vendor_boot_img_utils.cpp
index 9e09abb..9f05253 100644
--- a/fastboot/vendor_boot_img_utils.cpp
+++ b/fastboot/vendor_boot_img_utils.cpp
@@ -152,6 +152,9 @@
if (memcmp(hdr->magic, VENDOR_BOOT_MAGIC, VENDOR_BOOT_MAGIC_SIZE) != 0) {
return Errorf("Vendor boot image magic mismatch");
}
+ if (hdr->page_size == 0) {
+ return Errorf("Page size cannot be zero");
+ }
if (hdr->header_version < version) {
return Errorf("Require vendor boot header V{} but is V{}", version, hdr->header_version);
}
@@ -199,6 +202,7 @@
}
// round |value| up to a multiple of |page_size|.
+// aware that this can be integer overflow if value is too large
inline uint32_t round_up(uint32_t value, uint32_t page_size) {
return (value + page_size - 1) / page_size * page_size;
}
@@ -311,7 +315,13 @@
const uint32_t r = round_up(hdr->vendor_ramdisk_table_size, hdr->page_size);
const uint32_t s = round_up(hdr->bootconfig_size, hdr->page_size);
- if (hdr->vendor_ramdisk_table_entry_num == std::numeric_limits<uint32_t>::max()) {
+ uint64_t total_size = (uint64_t)o + p + q + r + s;
+ if (total_size > vendor_boot.size()) {
+ return Errorf("Vendor boot image size is too small, overflow");
+ }
+
+ if ((uint64_t)hdr->vendor_ramdisk_table_entry_num * sizeof(vendor_ramdisk_table_entry_v4) >
+ (uint64_t)o + p + q + r) {
return Errorf("Too many vendor ramdisk entries in table, overflow");
}
diff --git a/fs_mgr/fs_mgr.cpp b/fs_mgr/fs_mgr.cpp
index 6863894..9fe8e18 100644
--- a/fs_mgr/fs_mgr.cpp
+++ b/fs_mgr/fs_mgr.cpp
@@ -90,9 +90,6 @@
#define SYSFS_EXT4_VERITY "/sys/fs/ext4/features/verity"
#define SYSFS_EXT4_CASEFOLD "/sys/fs/ext4/features/casefold"
-// FIXME: this should be in system/extras
-#define EXT4_FEATURE_COMPAT_STABLE_INODES 0x0800
-
#define ARRAY_SIZE(a) (sizeof(a) / sizeof(*(a)))
using android::base::Basename;
@@ -124,8 +121,8 @@
FS_STAT_RO_MOUNT_FAILED = 0x0040,
FS_STAT_RO_UNMOUNT_FAILED = 0x0080,
FS_STAT_FULL_MOUNT_FAILED = 0x0100,
- FS_STAT_E2FSCK_FAILED = 0x0200,
- FS_STAT_E2FSCK_FS_FIXED = 0x0400,
+ FS_STAT_FSCK_FAILED = 0x0200,
+ FS_STAT_FSCK_FS_FIXED = 0x0400,
FS_STAT_INVALID_MAGIC = 0x0800,
FS_STAT_TOGGLE_QUOTAS_FAILED = 0x10000,
FS_STAT_SET_RESERVED_BLOCKS_FAILED = 0x20000,
@@ -136,7 +133,6 @@
};
static void log_fs_stat(const std::string& blk_device, int fs_stat) {
- if ((fs_stat & FS_STAT_IS_EXT4) == 0) return; // only log ext4
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 |
@@ -166,7 +162,7 @@
return fs_stat &
(FS_STAT_E2FSCK_F_ALWAYS | FS_STAT_UNCLEAN_SHUTDOWN | FS_STAT_QUOTA_ENABLED |
FS_STAT_RO_MOUNT_FAILED | FS_STAT_RO_UNMOUNT_FAILED | FS_STAT_FULL_MOUNT_FAILED |
- FS_STAT_E2FSCK_FAILED | FS_STAT_TOGGLE_QUOTAS_FAILED |
+ FS_STAT_FSCK_FAILED | FS_STAT_TOGGLE_QUOTAS_FAILED |
FS_STAT_SET_RESERVED_BLOCKS_FAILED | FS_STAT_ENABLE_ENCRYPTION_FAILED);
}
@@ -255,10 +251,10 @@
if (ret < 0) {
/* No need to check for error in fork, we can't really handle it now */
LERROR << "Failed trying to run " << E2FSCK_BIN;
- *fs_stat |= FS_STAT_E2FSCK_FAILED;
+ *fs_stat |= FS_STAT_FSCK_FAILED;
} else if (status != 0) {
LINFO << "e2fsck returned status 0x" << std::hex << status;
- *fs_stat |= FS_STAT_E2FSCK_FS_FIXED;
+ *fs_stat |= FS_STAT_FSCK_FS_FIXED;
}
}
} else if (is_f2fs(fs_type)) {
@@ -267,20 +263,30 @@
const char* f2fs_fsck_forced_argv[] = {
F2FS_FSCK_BIN, "-f", "-c", "10000", "--debug-cache", blk_device.c_str()};
- if (should_force_check(*fs_stat)) {
- LINFO << "Running " << F2FS_FSCK_BIN << " -f -c 10000 --debug-cache "
- << realpath(blk_device);
- ret = logwrap_fork_execvp(ARRAY_SIZE(f2fs_fsck_forced_argv), f2fs_fsck_forced_argv,
- &status, false, LOG_KLOG | LOG_FILE, false, nullptr);
+ if (access(F2FS_FSCK_BIN, X_OK)) {
+ LINFO << "Not running " << F2FS_FSCK_BIN << " on " << realpath(blk_device)
+ << " (executable not in system image)";
} else {
- LINFO << "Running " << F2FS_FSCK_BIN << " -a -c 10000 --debug-cache "
- << realpath(blk_device);
- ret = logwrap_fork_execvp(ARRAY_SIZE(f2fs_fsck_argv), f2fs_fsck_argv, &status, false,
- LOG_KLOG | LOG_FILE, false, nullptr);
- }
- if (ret < 0) {
- /* No need to check for error in fork, we can't really handle it now */
- LERROR << "Failed trying to run " << F2FS_FSCK_BIN;
+ if (should_force_check(*fs_stat)) {
+ LINFO << "Running " << F2FS_FSCK_BIN << " -f -c 10000 --debug-cache "
+ << realpath(blk_device);
+ ret = logwrap_fork_execvp(ARRAY_SIZE(f2fs_fsck_forced_argv), f2fs_fsck_forced_argv,
+ &status, false, LOG_KLOG | LOG_FILE, false,
+ FSCK_LOG_FILE);
+ } else {
+ LINFO << "Running " << F2FS_FSCK_BIN << " -a -c 10000 --debug-cache "
+ << realpath(blk_device);
+ ret = logwrap_fork_execvp(ARRAY_SIZE(f2fs_fsck_argv), f2fs_fsck_argv, &status,
+ false, LOG_KLOG | LOG_FILE, false, FSCK_LOG_FILE);
+ }
+ if (ret < 0) {
+ /* No need to check for error in fork, we can't really handle it now */
+ LERROR << "Failed trying to run " << F2FS_FSCK_BIN;
+ *fs_stat |= FS_STAT_FSCK_FAILED;
+ } else if (status != 0) {
+ LINFO << F2FS_FSCK_BIN << " returned status 0x" << std::hex << status;
+ *fs_stat |= FS_STAT_FSCK_FS_FIXED;
+ }
}
}
android::base::SetProperty("ro.boottime.init.fsck." + Basename(target),
@@ -840,8 +846,10 @@
if ((ret == 0) && (mountflags & MS_RDONLY) != 0) {
fs_mgr_set_blk_ro(source);
}
- android::base::SetProperty("ro.boottime.init.mount." + Basename(target),
- std::to_string(t.duration().count()));
+ if (ret == 0) {
+ android::base::SetProperty("ro.boottime.init.mount." + Basename(target),
+ std::to_string(t.duration().count()));
+ }
errno = save_errno;
return ret;
}
@@ -881,7 +889,7 @@
// 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(const Fstab& fstab, int start_idx, int* end_idx,
+static bool mount_with_alternatives(Fstab& fstab, int start_idx, int* end_idx,
int* attempted_idx) {
unsigned long i;
int mount_errno = 0;
@@ -895,12 +903,20 @@
// Deal with alternate entries for the same point which are required to be all following
// each other.
if (mounted) {
- LERROR << __FUNCTION__ << "(): skipping fstab dup mountpoint=" << fstab[i].mount_point
- << " rec[" << i << "].fs_type=" << fstab[i].fs_type << " already mounted as "
- << fstab[*attempted_idx].fs_type;
+ LINFO << __FUNCTION__ << "(): skipping fstab dup mountpoint=" << fstab[i].mount_point
+ << " rec[" << i << "].fs_type=" << fstab[i].fs_type << " already mounted as "
+ << fstab[*attempted_idx].fs_type;
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 &&
+ fstab[start_idx].fs_mgr_flags.logical &&
+ (fstab[i].logical_partition_name == fstab[start_idx].logical_partition_name)) {
+ fstab[i].blk_device = fstab[start_idx].blk_device;
+ }
+
int fs_stat = prepare_fs_for_mount(fstab[i].blk_device, fstab[i]);
if (fs_stat & FS_STAT_INVALID_MAGIC) {
LERROR << __FUNCTION__
@@ -917,9 +933,9 @@
*attempted_idx = i;
mounted = true;
if (i != start_idx) {
- LERROR << __FUNCTION__ << "(): Mounted " << fstab[i].blk_device << " on "
- << fstab[i].mount_point << " with fs_type=" << fstab[i].fs_type
- << " instead of " << fstab[start_idx].fs_type;
+ LINFO << __FUNCTION__ << "(): Mounted " << fstab[i].blk_device << " on "
+ << fstab[i].mount_point << " with fs_type=" << fstab[i].fs_type
+ << " instead of " << fstab[start_idx].fs_type;
}
fs_stat &= ~FS_STAT_FULL_MOUNT_FAILED;
mount_errno = 0;
@@ -1474,7 +1490,7 @@
if (status == FS_MGR_MNTALL_DEV_NEEDS_METADATA_ENCRYPTION) {
if (!call_vdc({"cryptfs", "encryptFstab", attempted_entry.blk_device,
attempted_entry.mount_point, wiped ? "true" : "false",
- attempted_entry.fs_type},
+ attempted_entry.fs_type, attempted_entry.zoned_device},
nullptr)) {
LERROR << "Encryption failed";
set_type_property(encryptable);
@@ -1514,7 +1530,7 @@
if (!call_vdc({"cryptfs", "encryptFstab", current_entry.blk_device,
current_entry.mount_point, "true" /* shouldFormat */,
- current_entry.fs_type},
+ current_entry.fs_type, current_entry.zoned_device},
nullptr)) {
LERROR << "Encryption failed";
} else {
@@ -1539,7 +1555,7 @@
if (mount_errno != EBUSY && mount_errno != EACCES &&
should_use_metadata_encryption(attempted_entry)) {
if (!call_vdc({"cryptfs", "mountFstab", attempted_entry.blk_device,
- attempted_entry.mount_point},
+ attempted_entry.mount_point, attempted_entry.zoned_device},
nullptr)) {
++error_count;
} else if (current_entry.mount_point == "/data") {
@@ -1911,6 +1927,7 @@
while (retry_count-- > 0) {
if (!__mount(n_blk_device, mount_point, fstab_entry)) {
fs_stat &= ~FS_STAT_FULL_MOUNT_FAILED;
+ log_fs_stat(fstab_entry.blk_device, fs_stat);
return FS_MGR_DOMNT_SUCCESS;
} else {
if (retry_count <= 0) break; // run check_fs only once
diff --git a/fs_mgr/fs_mgr_format.cpp b/fs_mgr/fs_mgr_format.cpp
index 6f59ed3..7385f79 100644
--- a/fs_mgr/fs_mgr_format.cpp
+++ b/fs_mgr/fs_mgr_format.cpp
@@ -117,7 +117,7 @@
}
static int format_f2fs(const std::string& fs_blkdev, uint64_t dev_sz, bool needs_projid,
- bool needs_casefold, bool fs_compress) {
+ bool needs_casefold, bool fs_compress, const std::string& zoned_device) {
if (!dev_sz) {
int rc = get_dev_sz(fs_blkdev, &dev_sz);
if (rc) {
@@ -146,8 +146,15 @@
args.push_back("-O");
args.push_back("extra_attr");
}
- args.push_back(fs_blkdev.c_str());
- args.push_back(size_str.c_str());
+ if (!zoned_device.empty()) {
+ args.push_back("-c");
+ args.push_back(zoned_device.c_str());
+ args.push_back("-m");
+ args.push_back(fs_blkdev.c_str());
+ } else {
+ args.push_back(fs_blkdev.c_str());
+ args.push_back(size_str.c_str());
+ }
return logwrap_fork_execvp(args.size(), args.data(), nullptr, false, LOG_KLOG, false, nullptr);
}
@@ -164,7 +171,7 @@
if (entry.fs_type == "f2fs") {
return format_f2fs(entry.blk_device, entry.length, needs_projid, needs_casefold,
- entry.fs_mgr_flags.fs_compress);
+ entry.fs_mgr_flags.fs_compress, entry.zoned_device);
} else if (entry.fs_type == "ext4") {
return format_ext4(entry.blk_device, entry.mount_point, needs_projid,
entry.fs_mgr_flags.ext_meta_csum);
diff --git a/fs_mgr/fs_mgr_fstab.cpp b/fs_mgr/fs_mgr_fstab.cpp
index 8c719c8..43961da 100644
--- a/fs_mgr/fs_mgr_fstab.cpp
+++ b/fs_mgr/fs_mgr_fstab.cpp
@@ -304,6 +304,14 @@
if (!ParseByteCount(arg, &entry->zram_backingdev_size)) {
LWARNING << "Warning: zram_backingdev_size= flag malformed: " << arg;
}
+ } else if (StartsWith(flag, "zoned_device=")) {
+ std::string zoned;
+ if (ReadFileToString("/sys/class/block/" + arg + "/queue/zoned", &zoned) &&
+ android::base::StartsWith(zoned, "host-managed")) {
+ entry->zoned_device = "/dev/block/" + arg;
+ } else {
+ LWARNING << "Warning: cannot find the zoned device: " << arg;
+ }
} else {
LWARNING << "Warning: unknown flag: " << flag;
}
@@ -431,34 +439,6 @@
return fstab_result;
}
-// Return the path to the fstab file. There may be multiple fstab files; the
-// one that is returned will be the first that exists of fstab.<fstab_suffix>,
-// fstab.<hardware>, and fstab.<hardware.platform>. The fstab is searched for
-// in /odm/etc/ and /vendor/etc/, as well as in the locations where it may be in
-// the first stage ramdisk during early boot. Previously, the first stage
-// ramdisk's copy of the fstab had to be located in the root directory, but now
-// the system/etc directory is supported too and is the preferred location.
-std::string GetFstabPath() {
- for (const char* prop : {"fstab_suffix", "hardware", "hardware.platform"}) {
- std::string suffix;
-
- if (!fs_mgr_get_boot_config(prop, &suffix)) continue;
-
- for (const char* prefix : {// late-boot/post-boot locations
- "/odm/etc/fstab.", "/vendor/etc/fstab.",
- // early boot locations
- "/system/etc/fstab.", "/first_stage_ramdisk/system/etc/fstab.",
- "/fstab.", "/first_stage_ramdisk/fstab."}) {
- std::string fstab_path = prefix + suffix;
- if (access(fstab_path.c_str(), F_OK) == 0) {
- return fstab_path;
- }
- }
- }
-
- return "";
-}
-
/* Extracts <device>s from the by-name symlinks specified in a fstab:
* /dev/block/<type>/<device>/by-name/<partition>
*
@@ -502,37 +482,50 @@
return boot_devices;
}
-FstabEntry BuildDsuUserdataFstabEntry() {
- constexpr uint32_t kFlags = MS_NOATIME | MS_NOSUID | MS_NODEV;
-
- FstabEntry userdata = {
- .blk_device = "userdata_gsi",
- .mount_point = "/data",
- .fs_type = "ext4",
- .flags = kFlags,
- .reserved_size = 128 * 1024 * 1024,
- };
- userdata.fs_mgr_flags.wait = true;
- userdata.fs_mgr_flags.check = true;
- userdata.fs_mgr_flags.logical = true;
- userdata.fs_mgr_flags.quota = true;
- userdata.fs_mgr_flags.late_mount = true;
- userdata.fs_mgr_flags.formattable = true;
- return userdata;
-}
-
-bool EraseFstabEntry(Fstab* fstab, const std::string& mount_point) {
- auto iter = std::remove_if(fstab->begin(), fstab->end(),
- [&](const auto& entry) { return entry.mount_point == mount_point; });
- if (iter != fstab->end()) {
- fstab->erase(iter, fstab->end());
- return true;
+template <typename Pred>
+std::vector<FstabEntry*> GetEntriesByPred(Fstab* fstab, const Pred& pred) {
+ if (fstab == nullptr) {
+ return {};
}
- return false;
+ std::vector<FstabEntry*> entries;
+ for (auto&& entry : *fstab) {
+ if (pred(entry)) {
+ entries.push_back(&entry);
+ }
+ }
+ return entries;
}
} // namespace
+// Return the path to the fstab file. There may be multiple fstab files; the
+// one that is returned will be the first that exists of fstab.<fstab_suffix>,
+// fstab.<hardware>, and fstab.<hardware.platform>. The fstab is searched for
+// in /odm/etc/ and /vendor/etc/, as well as in the locations where it may be in
+// the first stage ramdisk during early boot. Previously, the first stage
+// ramdisk's copy of the fstab had to be located in the root directory, but now
+// the system/etc directory is supported too and is the preferred location.
+std::string GetFstabPath() {
+ for (const char* prop : {"fstab_suffix", "hardware", "hardware.platform"}) {
+ std::string suffix;
+
+ if (!fs_mgr_get_boot_config(prop, &suffix)) continue;
+
+ for (const char* prefix : {// late-boot/post-boot locations
+ "/odm/etc/fstab.", "/vendor/etc/fstab.",
+ // early boot locations
+ "/system/etc/fstab.", "/first_stage_ramdisk/system/etc/fstab.",
+ "/fstab.", "/first_stage_ramdisk/fstab."}) {
+ std::string fstab_path = prefix + suffix;
+ if (access(fstab_path.c_str(), F_OK) == 0) {
+ return fstab_path;
+ }
+ }
+ }
+
+ return "";
+}
+
bool ParseFstabFromString(const std::string& fstab_str, bool proc_mounts, Fstab* fstab_out) {
const int expected_fields = proc_mounts ? 4 : 5;
@@ -591,38 +584,28 @@
void TransformFstabForDsu(Fstab* fstab, const std::string& dsu_slot,
const std::vector<std::string>& dsu_partitions) {
static constexpr char kDsuKeysDir[] = "/avb";
- // Convert userdata
- // Inherit fstab properties for userdata.
- FstabEntry userdata;
- if (FstabEntry* entry = GetEntryForMountPoint(fstab, "/data")) {
- userdata = *entry;
- userdata.blk_device = android::gsi::kDsuUserdata;
- userdata.fs_mgr_flags.logical = true;
- userdata.fs_mgr_flags.formattable = true;
- if (!userdata.metadata_key_dir.empty()) {
- userdata.metadata_key_dir = android::gsi::GetDsuMetadataKeyDir(dsu_slot);
- }
- } else {
- userdata = BuildDsuUserdataFstabEntry();
- }
-
- if (EraseFstabEntry(fstab, "/data")) {
- fstab->emplace_back(userdata);
- }
-
- // Convert others
for (auto&& partition : dsu_partitions) {
if (!EndsWith(partition, gsi::kDsuPostfix)) {
continue;
}
- // userdata has been handled
- if (partition == android::gsi::kDsuUserdata) {
- continue;
- }
// scratch is handled by fs_mgr_overlayfs
if (partition == android::gsi::kDsuScratch) {
continue;
}
+ // Convert userdata partition.
+ if (partition == android::gsi::kDsuUserdata) {
+ for (auto&& entry : GetEntriesForMountPoint(fstab, "/data")) {
+ entry->blk_device = android::gsi::kDsuUserdata;
+ entry->fs_mgr_flags.logical = true;
+ entry->fs_mgr_flags.formattable = true;
+ if (!entry->metadata_key_dir.empty()) {
+ entry->metadata_key_dir = android::gsi::GetDsuMetadataKeyDir(dsu_slot);
+ }
+ }
+ continue;
+ }
+ // Convert RO partitions.
+ //
// dsu_partition_name = corresponding_partition_name + kDsuPostfix
// e.g.
// system_gsi for system
@@ -630,45 +613,51 @@
// vendor_gsi for vendor
std::string lp_name = partition.substr(0, partition.length() - strlen(gsi::kDsuPostfix));
std::string mount_point = "/" + lp_name;
- std::vector<FstabEntry*> entries = GetEntriesForMountPoint(fstab, mount_point);
- if (entries.empty()) {
- FstabEntry entry = {
- .blk_device = partition,
- // .logical_partition_name is required to look up AVB Hashtree descriptors.
- .logical_partition_name = "system",
- .mount_point = mount_point,
- .fs_type = "ext4",
- .flags = MS_RDONLY,
- .fs_options = "barrier=1",
- .avb_keys = kDsuKeysDir,
- };
- entry.fs_mgr_flags.wait = true;
- entry.fs_mgr_flags.logical = true;
- entry.fs_mgr_flags.first_stage_mount = true;
- fstab->emplace_back(entry);
- } else {
- // If the corresponding partition exists, transform all its Fstab
- // by pointing .blk_device to the DSU partition.
- for (auto&& entry : entries) {
- entry->blk_device = partition;
- // AVB keys for DSU should always be under kDsuKeysDir.
- entry->avb_keys = kDsuKeysDir;
- entry->fs_mgr_flags.logical = true;
+
+ // List of fs_type entries we're lacking, need to synthesis these later.
+ std::vector<std::string> lack_fs_list = {"ext4", "erofs"};
+
+ // Only support early mount (first_stage_mount) partitions.
+ auto pred = [&mount_point](const FstabEntry& entry) {
+ return entry.fs_mgr_flags.first_stage_mount && entry.mount_point == mount_point;
+ };
+
+ // Transform all matching entries and assume they are all adjacent for simplicity.
+ for (auto&& entry : GetEntriesByPred(fstab, pred)) {
+ // .blk_device is replaced with the DSU partition.
+ entry->blk_device = partition;
+ // .avb_keys hints first_stage_mount to load the chained-vbmeta image from partition
+ // footer. See aosp/932779 for more details.
+ entry->avb_keys = kDsuKeysDir;
+ // .logical_partition_name is required to look up AVB Hashtree descriptors.
+ entry->logical_partition_name = lp_name;
+ entry->fs_mgr_flags.logical = true;
+ entry->fs_mgr_flags.slot_select = false;
+ entry->fs_mgr_flags.slot_select_other = false;
+
+ if (auto it = std::find(lack_fs_list.begin(), lack_fs_list.end(), entry->fs_type);
+ it != lack_fs_list.end()) {
+ lack_fs_list.erase(it);
}
- // Make sure the ext4 is included to support GSI.
- auto partition_ext4 =
- std::find_if(fstab->begin(), fstab->end(), [&](const auto& entry) {
- return entry.mount_point == mount_point && entry.fs_type == "ext4";
- });
- if (partition_ext4 == fstab->end()) {
- auto new_entry = *GetEntryForMountPoint(fstab, mount_point);
- new_entry.fs_type = "ext4";
- auto it = std::find_if(fstab->rbegin(), fstab->rend(),
- [&mount_point](const auto& entry) {
- return entry.mount_point == mount_point;
- });
- auto end_of_mount_point_group = fstab->begin() + std::distance(it, fstab->rend());
- fstab->insert(end_of_mount_point_group, new_entry);
+ }
+
+ if (!lack_fs_list.empty()) {
+ // Insert at the end of the existing mountpoint group, or at the end of fstab.
+ // We assume there is at most one matching mountpoint group, which is the common case.
+ auto it = std::find_if_not(std::find_if(fstab->begin(), fstab->end(), pred),
+ fstab->end(), pred);
+ for (const auto& fs_type : lack_fs_list) {
+ it = std::next(fstab->insert(it, {.blk_device = partition,
+ .logical_partition_name = lp_name,
+ .mount_point = mount_point,
+ .fs_type = fs_type,
+ .flags = MS_RDONLY,
+ .avb_keys = kDsuKeysDir,
+ .fs_mgr_flags{
+ .wait = true,
+ .logical = true,
+ .first_stage_mount = true,
+ }}));
}
}
}
@@ -713,22 +702,13 @@
}
if (!is_proc_mounts) {
if (!access(android::gsi::kGsiBootedIndicatorFile, F_OK)) {
- // This is expected to fail if host is android Q, since Q doesn't
- // support DSU slotting. The DSU "active" indicator file would be
- // non-existent or empty if DSU is enabled within the guest system.
- // In that case, just use the default slot name "dsu".
std::string dsu_slot;
- if (!android::gsi::GetActiveDsu(&dsu_slot) && errno != ENOENT) {
+ if (!android::gsi::GetActiveDsu(&dsu_slot)) {
PERROR << __FUNCTION__ << "(): failed to get active DSU slot";
return false;
}
- if (dsu_slot.empty()) {
- dsu_slot = "dsu";
- LWARNING << __FUNCTION__ << "(): assuming default DSU slot: " << dsu_slot;
- }
- // This file is non-existent on Q vendor.
std::string lp_names;
- if (!ReadFileToString(gsi::kGsiLpNamesFile, &lp_names) && errno != ENOENT) {
+ if (!ReadFileToString(gsi::kGsiLpNamesFile, &lp_names)) {
PERROR << __FUNCTION__ << "(): failed to read DSU LP names";
return false;
}
@@ -824,7 +804,7 @@
std::string default_fstab_path;
// Use different fstab paths for normal boot and recovery boot, respectively
- if (access("/system/bin/recovery", F_OK) == 0) {
+ if ((access("/sbin/recovery", F_OK) == 0) || (access("/system/bin/recovery", F_OK) == 0)) {
default_fstab_path = "/etc/recovery.fstab";
} else { // normal boot
default_fstab_path = GetFstabPath();
@@ -857,18 +837,8 @@
}
std::vector<FstabEntry*> GetEntriesForMountPoint(Fstab* fstab, const std::string& path) {
- std::vector<FstabEntry*> entries;
- if (fstab == nullptr) {
- return entries;
- }
-
- for (auto& entry : *fstab) {
- if (entry.mount_point == path) {
- entries.emplace_back(&entry);
- }
- }
-
- return entries;
+ return GetEntriesByPred(fstab,
+ [&path](const FstabEntry& entry) { return entry.mount_point == path; });
}
std::set<std::string> GetBootDevices() {
diff --git a/fs_mgr/fs_mgr_overlayfs.cpp b/fs_mgr/fs_mgr_overlayfs.cpp
index 82b5275..57762e6 100644
--- a/fs_mgr/fs_mgr_overlayfs.cpp
+++ b/fs_mgr/fs_mgr_overlayfs.cpp
@@ -81,10 +81,17 @@
return filesystems.find("\t" + filesystem + "\n") != std::string::npos;
}
+const auto kLowerdirOption = "lowerdir="s;
+const auto kUpperdirOption = "upperdir="s;
+
} // namespace
#if ALLOW_ADBD_DISABLE_VERITY == 0 // If we are a user build, provide stubs
+bool fs_mgr_wants_overlayfs(FstabEntry*) {
+ return false;
+}
+
Fstab fs_mgr_overlayfs_candidate_list(const Fstab&) {
return {};
}
@@ -93,13 +100,12 @@
return false;
}
-bool fs_mgr_overlayfs_setup(const char*, const char*, bool* change, bool) {
- if (change) *change = false;
+bool fs_mgr_overlayfs_setup(const char*, bool*, bool) {
+ LOG(ERROR) << "Overlayfs remounts can only be used in debuggable builds";
return false;
}
-bool fs_mgr_overlayfs_teardown(const char*, bool* change) {
- if (change) *change = false;
+bool fs_mgr_overlayfs_teardown(const char*, bool*) {
return false;
}
@@ -324,9 +330,6 @@
return "";
}
-const auto kLowerdirOption = "lowerdir="s;
-const auto kUpperdirOption = "upperdir="s;
-
static inline bool KernelSupportsUserXattrs() {
struct utsname uts;
uname(&uts);
@@ -366,121 +369,99 @@
return ret;
}
-bool fs_mgr_overlayfs_already_mounted(const std::string& mount_point, bool overlay_only = true) {
- Fstab fstab;
- auto save_errno = errno;
- if (!ReadFstabFromFile("/proc/mounts", &fstab)) {
- return false;
- }
- errno = save_errno;
- const auto lowerdir = kLowerdirOption + mount_point;
- for (const auto& entry : fstab) {
- if (overlay_only && "overlay" != entry.fs_type && "overlayfs" != entry.fs_type) continue;
- if (mount_point != entry.mount_point) continue;
- if (!overlay_only) return true;
- const auto options = android::base::Split(entry.fs_options, ",");
- for (const auto& opt : options) {
- if (opt == lowerdir) {
- return true;
- }
- }
- }
- return false;
-}
-
-bool fs_mgr_wants_overlayfs(FstabEntry* entry) {
- // Don't check entries that are managed by vold.
- if (entry->fs_mgr_flags.vold_managed || entry->fs_mgr_flags.recovery_only) return false;
-
- // *_other doesn't want overlayfs.
- if (entry->fs_mgr_flags.slot_select_other) return false;
-
- // Only concerned with readonly partitions.
- if (!(entry->flags & MS_RDONLY)) return false;
-
- // If unbindable, do not allow overlayfs as this could expose us to
- // security issues. On Android, this could also be used to turn off
- // the ability to overlay an otherwise acceptable filesystem since
- // /system and /vendor are never bound(sic) to.
- if (entry->flags & MS_UNBINDABLE) return false;
-
- if (!fs_mgr_overlayfs_enabled(entry)) return false;
-
- return true;
-}
constexpr char kOverlayfsFileContext[] = "u:object_r:overlayfs_file:s0";
-bool fs_mgr_overlayfs_setup_dir(const std::string& dir, std::string* overlay, bool* change) {
- auto ret = true;
- auto top = dir + kOverlayTopDir;
- if (setfscreatecon(kOverlayfsFileContext)) {
- ret = false;
- PERROR << "setfscreatecon " << kOverlayfsFileContext;
- }
- auto save_errno = errno;
- if (!mkdir(top.c_str(), 0755)) {
- if (change) *change = true;
- } else if (errno != EEXIST) {
- ret = false;
- PERROR << "mkdir " << top;
- } else {
- errno = save_errno;
- }
- setfscreatecon(nullptr);
+class AutoSetFsCreateCon final {
+ public:
+ AutoSetFsCreateCon() {}
+ AutoSetFsCreateCon(const std::string& context) { Set(context); }
+ ~AutoSetFsCreateCon() { Restore(); }
- if (overlay) *overlay = std::move(top);
- return ret;
+ bool Ok() const { return ok_; }
+ bool Set(const std::string& context) {
+ if (setfscreatecon(context.c_str())) {
+ PLOG(ERROR) << "setfscreatecon " << context;
+ return false;
+ }
+ ok_ = true;
+ return true;
+ }
+ bool Restore() {
+ if (restored_ || !ok_) {
+ return true;
+ }
+ if (setfscreatecon(nullptr)) {
+ PLOG(ERROR) << "setfscreatecon null";
+ return false;
+ }
+ restored_ = true;
+ return true;
+ }
+
+ private:
+ bool ok_ = false;
+ bool restored_ = false;
+};
+
+std::string fs_mgr_overlayfs_setup_dir(const std::string& dir) {
+ auto top = dir + kOverlayTopDir;
+
+ AutoSetFsCreateCon createcon(kOverlayfsFileContext);
+ if (!createcon.Ok()) {
+ return {};
+ }
+ if (mkdir(top.c_str(), 0755) != 0 && errno != EEXIST) {
+ PERROR << "mkdir " << top;
+ return {};
+ }
+ if (!createcon.Restore()) {
+ return {};
+ }
+ return top;
}
bool fs_mgr_overlayfs_setup_one(const std::string& overlay, const std::string& mount_point,
- bool* change) {
- auto ret = true;
- if (fs_mgr_overlayfs_already_mounted(mount_point)) return ret;
+ bool* want_reboot) {
+ if (fs_mgr_overlayfs_already_mounted(mount_point)) {
+ return true;
+ }
auto fsrec_mount_point = overlay + "/" + android::base::Basename(mount_point) + "/";
- if (setfscreatecon(kOverlayfsFileContext)) {
- ret = false;
- PERROR << "setfscreatecon " << kOverlayfsFileContext;
+ AutoSetFsCreateCon createcon(kOverlayfsFileContext);
+ if (!createcon.Ok()) {
+ return false;
}
- auto save_errno = errno;
- if (!mkdir(fsrec_mount_point.c_str(), 0755)) {
- if (change) *change = true;
- } else if (errno != EEXIST) {
- ret = false;
+ if (mkdir(fsrec_mount_point.c_str(), 0755) != 0 && errno != EEXIST) {
PERROR << "mkdir " << fsrec_mount_point;
- } else {
- errno = save_errno;
+ return false;
+ }
+ if (mkdir((fsrec_mount_point + kWorkName).c_str(), 0755) != 0 && errno != EEXIST) {
+ PERROR << "mkdir " << fsrec_mount_point << kWorkName;
+ return false;
+ }
+ if (!createcon.Restore()) {
+ return false;
}
- save_errno = errno;
- if (!mkdir((fsrec_mount_point + kWorkName).c_str(), 0755)) {
- if (change) *change = true;
- } else if (errno != EEXIST) {
- ret = false;
- PERROR << "mkdir " << fsrec_mount_point << kWorkName;
- } else {
- errno = save_errno;
- }
- setfscreatecon(nullptr);
+ createcon = {};
auto new_context = fs_mgr_get_context(mount_point);
- if (!new_context.empty() && setfscreatecon(new_context.c_str())) {
- ret = false;
- PERROR << "setfscreatecon " << new_context;
+ if (new_context.empty() || !createcon.Set(new_context)) {
+ return false;
}
- auto upper = fsrec_mount_point + kUpperName;
- save_errno = errno;
- if (!mkdir(upper.c_str(), 0755)) {
- if (change) *change = true;
- } else if (errno != EEXIST) {
- ret = false;
- PERROR << "mkdir " << upper;
- } else {
- errno = save_errno;
- }
- if (!new_context.empty()) setfscreatecon(nullptr);
- return ret;
+ auto upper = fsrec_mount_point + kUpperName;
+ if (mkdir(upper.c_str(), 0755) != 0 && errno != EEXIST) {
+ PERROR << "mkdir " << upper;
+ return false;
+ }
+ if (!createcon.Restore()) {
+ return false;
+ }
+
+ if (want_reboot) *want_reboot = true;
+
+ return true;
}
uint32_t fs_mgr_overlayfs_slot_number() {
@@ -642,6 +623,10 @@
if (ret) {
PERROR << "__mount(target=" << mount_point
<< ",flag=" << (shared_flag ? "MS_SHARED" : "MS_PRIVATE") << ")=" << ret;
+ // If "/system" doesn't look like a mountpoint, retry with "/".
+ if (errno == EINVAL && mount_point == "/system") {
+ return fs_mgr_overlayfs_set_shared_mount("/", shared_flag);
+ }
return false;
}
return true;
@@ -763,21 +748,23 @@
}
// use as the bound directory in /dev.
+ AutoSetFsCreateCon createcon;
auto new_context = fs_mgr_get_context(entry.mount_point);
- if (!new_context.empty() && setfscreatecon(new_context.c_str())) {
- PERROR << "setfscreatecon " << new_context;
+ if (new_context.empty() || !createcon.Set(new_context)) {
+ continue;
}
move_entry new_entry = {std::move(entry.mount_point), "/dev/TemporaryDir-XXXXXX",
entry.shared_flag};
const auto target = mkdtemp(new_entry.dir.data());
+ if (!createcon.Restore()) {
+ return false;
+ }
if (!target) {
retval = false;
save_errno = errno;
PERROR << "temporary directory for MS_BIND";
- setfscreatecon(nullptr);
continue;
}
- setfscreatecon(nullptr);
if (!parent_private && !parent_made_private) {
parent_made_private = fs_mgr_overlayfs_set_shared_mount(mount_point, false);
@@ -848,20 +835,29 @@
bool fs_mgr_overlayfs_mount_scratch(const std::string& device_path, const std::string mnt_type,
bool readonly = false) {
if (readonly) {
- if (!fs_mgr_access(device_path)) return false;
- } else {
- if (!fs_mgr_rw_access(device_path)) return false;
+ if (!fs_mgr_access(device_path)) {
+ LOG(ERROR) << "Path does not exist: " << device_path;
+ return false;
+ }
+ } else if (!fs_mgr_rw_access(device_path)) {
+ LOG(ERROR) << "Path does not exist or is not readwrite: " << device_path;
+ return false;
}
auto f2fs = fs_mgr_is_f2fs(device_path);
auto ext4 = fs_mgr_is_ext4(device_path);
- if (!f2fs && !ext4) return false;
+ if (!f2fs && !ext4) {
+ LOG(ERROR) << "Scratch partition is not f2fs or ext4";
+ return false;
+ }
- if (setfscreatecon(kOverlayfsFileContext)) {
- PERROR << "setfscreatecon " << kOverlayfsFileContext;
+ AutoSetFsCreateCon createcon(kOverlayfsFileContext);
+ if (!createcon.Ok()) {
+ return false;
}
if (mkdir(kScratchMountPoint.c_str(), 0755) && (errno != EEXIST)) {
PERROR << "create " << kScratchMountPoint;
+ return false;
}
FstabEntry entry;
@@ -893,7 +889,6 @@
if (fs_mgr_overlayfs_already_mounted("/data", false)) {
entry.fs_mgr_flags.check = true;
}
- auto save_errno = errno;
if (mounted) mounted = fs_mgr_do_mount_one(entry) == 0;
if (!mounted) {
if ((entry.fs_type == "f2fs") && ext4) {
@@ -903,12 +898,15 @@
entry.fs_type = "f2fs";
mounted = fs_mgr_do_mount_one(entry) == 0;
}
- if (!mounted) save_errno = errno;
}
- setfscreatecon(nullptr);
- if (!mounted) rmdir(kScratchMountPoint.c_str());
- errno = save_errno;
- return mounted;
+ if (!createcon.Restore()) {
+ return false;
+ }
+ if (!mounted) {
+ rmdir(kScratchMountPoint.c_str());
+ return false;
+ }
+ return true;
}
const std::string kMkF2fs("/system/bin/make_f2fs");
@@ -996,7 +994,6 @@
} else if (mnt_type == "ext4") {
command = kMkExt4 + " -F -b 4096 -t ext4 -m 0 -O has_journal -M " + kScratchMountPoint;
} else {
- errno = ESRCH;
LERROR << mnt_type << " has no mkfs cookbook";
return false;
}
@@ -1029,8 +1026,7 @@
}
// Create or update a scratch partition within super.
-static bool CreateDynamicScratch(std::string* scratch_device, bool* partition_exists,
- bool* change) {
+static bool CreateDynamicScratch(std::string* scratch_device, bool* partition_exists) {
const auto partition_name = android::base::Basename(kScratchMountPoint);
auto& dm = DeviceMapper::Instance();
@@ -1103,8 +1099,6 @@
LERROR << "add partition " << partition_name;
return false;
}
-
- if (change) *change = true;
}
if (changed || partition_create) {
@@ -1118,8 +1112,6 @@
if (!CreateLogicalPartition(params, scratch_device)) {
return false;
}
-
- if (change) *change = true;
} else if (scratch_device->empty()) {
*scratch_device = GetBootScratchDevice();
}
@@ -1140,12 +1132,17 @@
return 0;
}
- return std::min(super_info.size, (uint64_t(s.f_frsize) * s.f_bfree) / 2);
+ auto ideal_size = std::min(super_info.size, (uint64_t(s.f_frsize) * s.f_bfree) / 2);
+
+ // Align up to the filesystem block size.
+ if (auto remainder = ideal_size % s.f_bsize; remainder > 0) {
+ ideal_size += s.f_bsize - remainder;
+ }
+ return ideal_size;
}
-static bool CreateScratchOnData(std::string* scratch_device, bool* partition_exists, bool* change) {
+static bool CreateScratchOnData(std::string* scratch_device, bool* partition_exists) {
*partition_exists = false;
- if (change) *change = false;
auto images = IImageManager::Open("remount", 10s);
if (!images) {
@@ -1158,8 +1155,6 @@
return true;
}
- if (change) *change = true;
-
// Note: calling RemoveDisabledImages here ensures that we do not race with
// clean_scratch_files and accidentally try to map an image that will be
// deleted.
@@ -1201,12 +1196,11 @@
}
bool fs_mgr_overlayfs_create_scratch(const Fstab& fstab, std::string* scratch_device,
- bool* partition_exists, bool* change) {
+ bool* partition_exists) {
// Use the DSU scratch device managed by gsid if within a DSU system.
if (fs_mgr_is_dsu_running()) {
*scratch_device = GetDsuScratchDevice();
*partition_exists = !scratch_device->empty();
- *change = false;
return *partition_exists;
}
@@ -1222,22 +1216,24 @@
if (CanUseSuperPartition(fstab, &is_virtual_ab)) {
bool can_use_data = false;
if (is_virtual_ab && FilesystemHasReliablePinning("/data", &can_use_data) && can_use_data) {
- return CreateScratchOnData(scratch_device, partition_exists, change);
+ return CreateScratchOnData(scratch_device, partition_exists);
}
- return CreateDynamicScratch(scratch_device, partition_exists, change);
+ return CreateDynamicScratch(scratch_device, partition_exists);
}
- errno = ENXIO;
return false;
}
// Create and mount kScratchMountPoint storage if we have logical partitions
-bool fs_mgr_overlayfs_setup_scratch(const Fstab& fstab, bool* change) {
- if (fs_mgr_overlayfs_already_mounted(kScratchMountPoint, false)) return true;
+bool fs_mgr_overlayfs_setup_scratch(const Fstab& fstab) {
+ if (fs_mgr_overlayfs_already_mounted(kScratchMountPoint, false)) {
+ return true;
+ }
std::string scratch_device;
bool partition_exists;
- if (!fs_mgr_overlayfs_create_scratch(fstab, &scratch_device, &partition_exists, change)) {
+ if (!fs_mgr_overlayfs_create_scratch(fstab, &scratch_device, &partition_exists)) {
+ LOG(ERROR) << "Failed to create scratch partition";
return false;
}
@@ -1245,22 +1241,19 @@
auto mnt_type = fs_mgr_overlayfs_scratch_mount_type();
if (partition_exists) {
if (fs_mgr_overlayfs_mount_scratch(scratch_device, mnt_type)) {
- if (!fs_mgr_access(kScratchMountPoint + kOverlayTopDir) &&
- !fs_mgr_filesystem_has_space(kScratchMountPoint)) {
- // declare it useless, no overrides and no free space
- fs_mgr_overlayfs_umount_scratch();
- } else {
- if (change) *change = true;
+ if (fs_mgr_access(kScratchMountPoint + kOverlayTopDir) ||
+ fs_mgr_filesystem_has_space(kScratchMountPoint)) {
return true;
}
+ // declare it useless, no overrides and no free space
+ fs_mgr_overlayfs_umount_scratch();
}
- // partition existed, but was not initialized; fall through to make it.
- errno = 0;
}
- if (!fs_mgr_overlayfs_make_scratch(scratch_device, mnt_type)) return false;
-
- if (change) *change = true;
+ if (!fs_mgr_overlayfs_make_scratch(scratch_device, mnt_type)) {
+ LOG(ERROR) << "Failed to format scratch partition";
+ return false;
+ }
return fs_mgr_overlayfs_mount_scratch(scratch_device, mnt_type);
}
@@ -1274,9 +1267,45 @@
} // namespace
+bool fs_mgr_wants_overlayfs(FstabEntry* entry) {
+ // Don't check entries that are managed by vold.
+ if (entry->fs_mgr_flags.vold_managed || entry->fs_mgr_flags.recovery_only) return false;
+
+ // *_other doesn't want overlayfs.
+ if (entry->fs_mgr_flags.slot_select_other) return false;
+
+ // Only concerned with readonly partitions.
+ if (!(entry->flags & MS_RDONLY)) return false;
+
+ // If unbindable, do not allow overlayfs as this could expose us to
+ // security issues. On Android, this could also be used to turn off
+ // the ability to overlay an otherwise acceptable filesystem since
+ // /system and /vendor are never bound(sic) to.
+ if (entry->flags & MS_UNBINDABLE) return false;
+
+ if (!fs_mgr_overlayfs_enabled(entry)) return false;
+
+ return true;
+}
+
Fstab fs_mgr_overlayfs_candidate_list(const Fstab& fstab) {
+ android::fs_mgr::Fstab mounts;
+ if (!android::fs_mgr::ReadFstabFromFile("/proc/mounts", &mounts)) {
+ PLOG(ERROR) << "Failed to read /proc/mounts";
+ return {};
+ }
+
Fstab candidates;
for (const auto& entry : fstab) {
+ // Filter out partitions whose type doesn't match what's mounted.
+ // This avoids spammy behavior on devices which can mount different
+ // filesystems for each partition.
+ auto proc_mount_point = (entry.mount_point == "/system") ? "/" : entry.mount_point;
+ auto mounted = GetEntryForMountPoint(&mounts, proc_mount_point);
+ if (!mounted || mounted->fs_type != entry.fs_type) {
+ continue;
+ }
+
FstabEntry new_entry = entry;
if (!fs_mgr_overlayfs_already_mounted(entry.mount_point) &&
!fs_mgr_wants_overlayfs(&new_entry)) {
@@ -1347,25 +1376,23 @@
return ret;
}
-// Returns false if setup not permitted, errno set to last error.
-// If something is altered, set *change.
-bool fs_mgr_overlayfs_setup(const char* backing, const char* mount_point, bool* change,
- bool force) {
- if (change) *change = false;
- auto ret = false;
- if (fs_mgr_overlayfs_valid() == OverlayfsValidResult::kNotSupported) return ret;
- if (!fs_mgr_boot_completed()) {
- errno = EBUSY;
- PERROR << "setup";
- return ret;
- }
-
- auto save_errno = errno;
- Fstab fstab;
- if (!ReadDefaultFstab(&fstab)) {
+bool fs_mgr_overlayfs_setup(const char* mount_point, bool* want_reboot, bool just_disabled_verity) {
+ if (fs_mgr_overlayfs_valid() == OverlayfsValidResult::kNotSupported) {
+ LOG(ERROR) << "Overlayfs is not supported";
return false;
}
- errno = save_errno;
+
+ if (!fs_mgr_boot_completed()) {
+ LOG(ERROR) << "Cannot setup overlayfs before persistent properties are ready";
+ return false;
+ }
+
+ Fstab fstab;
+ if (!ReadDefaultFstab(&fstab)) {
+ LOG(ERROR) << "Could not read fstab";
+ return false;
+ }
+
auto candidates = fs_mgr_overlayfs_candidate_list(fstab);
for (auto it = candidates.begin(); it != candidates.end();) {
if (mount_point &&
@@ -1373,9 +1400,8 @@
it = candidates.erase(it);
continue;
}
- save_errno = errno;
- auto verity_enabled = !force && fs_mgr_is_verity_enabled(*it);
- if (errno == ENOENT || errno == ENXIO) errno = save_errno;
+
+ auto verity_enabled = !just_disabled_verity && fs_mgr_is_verity_enabled(*it);
if (verity_enabled) {
it = candidates.erase(it);
continue;
@@ -1383,13 +1409,20 @@
++it;
}
- if (candidates.empty()) return ret;
+ if (candidates.empty()) {
+ if (mount_point) {
+ LOG(ERROR) << "No overlayfs candidate was found for " << mount_point;
+ return false;
+ }
+ return true;
+ }
std::string dir;
for (const auto& overlay_mount_point : OverlayMountPoints()) {
- if (backing && backing[0] && (overlay_mount_point != backing)) continue;
if (overlay_mount_point == kScratchMountPoint) {
- if (!fs_mgr_overlayfs_setup_scratch(fstab, change)) continue;
+ if (!fs_mgr_overlayfs_setup_scratch(fstab)) {
+ continue;
+ }
} else {
if (GetEntryForMountPoint(&fstab, overlay_mount_point) == nullptr) {
continue;
@@ -1399,17 +1432,21 @@
break;
}
if (dir.empty()) {
- if (change && *change) errno = ESRCH;
- if (errno == EPERM) errno = save_errno;
- return ret;
+ LOG(ERROR) << "Could not allocate backing storage for overlays";
+ return false;
}
- std::string overlay;
- ret |= fs_mgr_overlayfs_setup_dir(dir, &overlay, change);
- for (const auto& entry : candidates) {
- ret |= fs_mgr_overlayfs_setup_one(overlay, fs_mgr_mount_point(entry.mount_point), change);
+ const auto overlay = fs_mgr_overlayfs_setup_dir(dir);
+ if (overlay.empty()) {
+ return false;
}
- return ret;
+
+ bool ok = true;
+ for (const auto& entry : candidates) {
+ auto fstab_mount_point = fs_mgr_mount_point(entry.mount_point);
+ ok &= fs_mgr_overlayfs_setup_one(overlay, fstab_mount_point, want_reboot);
+ }
+ return ok;
}
struct MapInfo {
@@ -1683,6 +1720,28 @@
#endif // ALLOW_ADBD_DISABLE_VERITY != 0
+bool fs_mgr_overlayfs_already_mounted(const std::string& mount_point, bool overlay_only) {
+ Fstab fstab;
+ auto save_errno = errno;
+ if (!ReadFstabFromFile("/proc/mounts", &fstab)) {
+ return false;
+ }
+ errno = save_errno;
+ const auto lowerdir = kLowerdirOption + mount_point;
+ for (const auto& entry : fstab) {
+ if (overlay_only && "overlay" != entry.fs_type && "overlayfs" != entry.fs_type) continue;
+ if (mount_point != entry.mount_point) continue;
+ if (!overlay_only) return true;
+ const auto options = android::base::Split(entry.fs_options, ",");
+ for (const auto& opt : options) {
+ if (opt == lowerdir) {
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
bool fs_mgr_has_shared_blocks(const std::string& mount_point, const std::string& dev) {
struct statfs fs;
if ((statfs((mount_point + "/lost+found").c_str(), &fs) == -1) ||
@@ -1708,6 +1767,7 @@
std::string fs_mgr_get_context(const std::string& mount_point) {
char* ctx = nullptr;
if (getfilecon(mount_point.c_str(), &ctx) == -1) {
+ PLOG(ERROR) << "getfilecon " << mount_point;
return "";
}
diff --git a/fs_mgr/fs_mgr_remount.cpp b/fs_mgr/fs_mgr_remount.cpp
index deaf5f7..2202fda 100644
--- a/fs_mgr/fs_mgr_remount.cpp
+++ b/fs_mgr/fs_mgr_remount.cpp
@@ -43,6 +43,8 @@
#include <libgsi/libgsid.h>
using namespace std::literals;
+using android::fs_mgr::Fstab;
+using android::fs_mgr::FstabEntry;
namespace {
@@ -62,22 +64,12 @@
::exit(exit_status);
}
-bool remountable_partition(const android::fs_mgr::FstabEntry& entry) {
- if (entry.fs_mgr_flags.vold_managed) return false;
- if (entry.fs_mgr_flags.recovery_only) return false;
- if (entry.fs_mgr_flags.slot_select_other) return false;
- if (!(entry.flags & MS_RDONLY)) return false;
- if (entry.fs_type == "vfat") return false;
- return true;
-}
-
const std::string system_mount_point(const android::fs_mgr::FstabEntry& entry) {
if (entry.mount_point == "/") return "/system";
return entry.mount_point;
}
-const android::fs_mgr::FstabEntry* is_wrapped(const android::fs_mgr::Fstab& overlayfs_candidates,
- const android::fs_mgr::FstabEntry& entry) {
+const FstabEntry* GetWrappedEntry(const Fstab& overlayfs_candidates, const FstabEntry& entry) {
auto mount_point = system_mount_point(entry);
auto it = std::find_if(overlayfs_candidates.begin(), overlayfs_candidates.end(),
[&mount_point](const auto& entry) {
@@ -99,12 +91,8 @@
logd(id, severity, tag, file, line, message);
}
-[[noreturn]] void reboot(bool overlayfs = false) {
- if (overlayfs) {
- LOG(INFO) << "Successfully setup overlayfs\nrebooting device";
- } else {
- LOG(INFO) << "Successfully disabled verity\nrebooting device";
- }
+[[noreturn]] void reboot() {
+ LOG(INFO) << "Rebooting device for new settings to take effect";
::sync();
android::base::SetProperty(ANDROID_RB_PROPERTY, "reboot,remount");
::sleep(60);
@@ -140,15 +128,368 @@
BAD_OVERLAY,
NO_MOUNTS,
REMOUNT_FAILED,
- MUST_REBOOT,
BINDER_ERROR,
CHECKPOINTING,
GSID_ERROR,
- CLEAN_SCRATCH_FILES,
};
-static int do_remount(int argc, char* argv[]) {
- RemountStatus retval = REMOUNT_SUCCESS;
+static bool ReadFstab(const char* fstab_file, android::fs_mgr::Fstab* fstab) {
+ if (fstab_file) {
+ return android::fs_mgr::ReadFstabFromFile(fstab_file, fstab);
+ }
+ if (!android::fs_mgr::ReadDefaultFstab(fstab)) {
+ return false;
+ }
+
+ // Manufacture a / entry from /proc/mounts if missing.
+ if (!GetEntryForMountPoint(fstab, "/system") && !GetEntryForMountPoint(fstab, "/")) {
+ android::fs_mgr::Fstab mounts;
+ if (android::fs_mgr::ReadFstabFromFile("/proc/mounts", &mounts)) {
+ if (auto entry = GetEntryForMountPoint(&mounts, "/")) {
+ if (entry->fs_type != "rootfs") fstab->emplace_back(*entry);
+ }
+ }
+ }
+ return true;
+}
+
+static RemountStatus VerifyCheckpointing() {
+ if (!android::base::GetBoolProperty("ro.virtual_ab.enabled", false) &&
+ !android::base::GetBoolProperty("ro.virtual_ab.retrofit", false)) {
+ return REMOUNT_SUCCESS;
+ }
+
+ // Virtual A/B devices can use /data as backing storage; make sure we're
+ // not checkpointing.
+ auto vold = GetVold();
+ bool checkpointing = false;
+ if (!vold->isCheckpointing(&checkpointing).isOk()) {
+ LOG(ERROR) << "Could not determine checkpointing status.";
+ return BINDER_ERROR;
+ }
+ if (checkpointing) {
+ LOG(ERROR) << "Cannot use remount when a checkpoint is in progress.";
+ return CHECKPOINTING;
+ }
+ return REMOUNT_SUCCESS;
+}
+
+static bool IsRemountable(Fstab& candidates, const FstabEntry& entry) {
+ if (entry.fs_mgr_flags.vold_managed || entry.fs_mgr_flags.recovery_only ||
+ entry.fs_mgr_flags.slot_select_other) {
+ return false;
+ }
+ if (!(entry.flags & MS_RDONLY)) {
+ return false;
+ }
+ if (entry.fs_type == "vfat") {
+ return false;
+ }
+ if (auto candidate_entry = GetEntryForMountPoint(&candidates, entry.mount_point)) {
+ return candidate_entry->fs_type == entry.fs_type;
+ }
+ if (GetWrappedEntry(candidates, entry)) {
+ return false;
+ }
+ return true;
+}
+
+static Fstab::const_iterator FindPartition(const Fstab& fstab, const std::string& partition) {
+ Fstab mounts;
+ if (!android::fs_mgr::ReadFstabFromFile("/proc/mounts", &mounts)) {
+ LOG(ERROR) << "Failed to read /proc/mounts";
+ return fstab.end();
+ }
+
+ for (auto iter = fstab.begin(); iter != fstab.end(); iter++) {
+ const auto mount_point = system_mount_point(*iter);
+ if (partition == mount_point || partition == android::base::Basename(mount_point)) {
+ // In case fstab has multiple entries, pick the one that matches the
+ // actual mounted filesystem type.
+ auto proc_mount_point = (iter->mount_point == "/system") ? "/" : iter->mount_point;
+ auto mounted = GetEntryForMountPoint(&mounts, proc_mount_point);
+ if (mounted && mounted->fs_type == iter->fs_type) {
+ return iter;
+ }
+ }
+ }
+ return fstab.end();
+}
+
+static Fstab GetAllRemountablePartitions(Fstab& fstab) {
+ auto candidates = fs_mgr_overlayfs_candidate_list(fstab);
+
+ Fstab partitions;
+ for (const auto& entry : fstab) {
+ if (IsRemountable(candidates, entry)) {
+ partitions.emplace_back(entry);
+ }
+ }
+ return partitions;
+}
+
+static RemountStatus GetRemountList(const Fstab& fstab, const std::vector<std::string>& argv,
+ Fstab* partitions) {
+ auto candidates = fs_mgr_overlayfs_candidate_list(fstab);
+
+ for (const auto& arg : argv) {
+ std::string partition = arg;
+ if (partition == "/") {
+ partition = "/system";
+ }
+
+ auto it = FindPartition(fstab, partition);
+ if (it == fstab.end()) {
+ LOG(ERROR) << "Unknown partition " << arg;
+ return UNKNOWN_PARTITION;
+ }
+
+ const FstabEntry* entry = &*it;
+ if (auto wrap = GetWrappedEntry(candidates, *entry); wrap != nullptr) {
+ LOG(INFO) << "partition " << arg << " covered by overlayfs for " << wrap->mount_point
+ << ", switching";
+ entry = wrap;
+ }
+
+ // If it's already remounted, include it so it gets gracefully skipped
+ // later on.
+ if (!fs_mgr_overlayfs_already_mounted(entry->mount_point) &&
+ !IsRemountable(candidates, *entry)) {
+ LOG(ERROR) << "Invalid partition " << arg;
+ return INVALID_PARTITION;
+ }
+ if (GetEntryForMountPoint(partitions, entry->mount_point) != nullptr) {
+ continue;
+ }
+ partitions->emplace_back(*entry);
+ }
+
+ return REMOUNT_SUCCESS;
+}
+
+struct RemountCheckResult {
+ bool reboot_later = false;
+ bool setup_overlayfs = false;
+ bool disabled_verity = false;
+ bool verity_error = false;
+ bool remounted_anything = false;
+};
+
+static RemountStatus CheckVerity(const FstabEntry& entry, RemountCheckResult* result) {
+ if (!fs_mgr_is_verity_enabled(entry)) {
+ return REMOUNT_SUCCESS;
+ }
+ if (android::base::GetProperty("ro.boot.vbmeta.device_state", "") == "locked") {
+ return VERITY_PARTITION;
+ }
+
+ bool ok = false;
+
+ std::unique_ptr<AvbOps, decltype(&::avb_ops_user_free)> ops(avb_ops_user_new(),
+ &::avb_ops_user_free);
+ if (ops) {
+ auto suffix = android::base::GetProperty("ro.boot.slot_suffix", "");
+ ok = avb_user_verity_set(ops.get(), suffix.c_str(), false);
+ }
+ if (!ok && fs_mgr_set_blk_ro(entry.blk_device, false)) {
+ fec::io fh(entry.blk_device.c_str(), O_RDWR);
+ ok = fh && fh.set_verity_status(false);
+ }
+ if (!ok) {
+ return VERITY_PARTITION;
+ }
+ result->disabled_verity = true;
+ result->reboot_later = true;
+ return REMOUNT_SUCCESS;
+}
+
+static RemountStatus CheckVerityAndOverlayfs(Fstab* partitions, RemountCheckResult* result) {
+ RemountStatus status = REMOUNT_SUCCESS;
+ for (auto it = partitions->begin(); it != partitions->end();) {
+ auto& entry = *it;
+ const auto& mount_point = entry.mount_point;
+
+ if (auto rv = CheckVerity(entry, result); rv != REMOUNT_SUCCESS) {
+ LOG(ERROR) << "Skipping verified partition " << mount_point << " for remount";
+ status = rv;
+ it = partitions->erase(it);
+ continue;
+ }
+
+ if (fs_mgr_wants_overlayfs(&entry)) {
+ bool want_reboot = false;
+ bool force = result->disabled_verity;
+ if (!fs_mgr_overlayfs_setup(mount_point.c_str(), &want_reboot, force)) {
+ LOG(ERROR) << "Overlayfs setup for " << mount_point << " failed, skipping";
+ status = BAD_OVERLAY;
+ it = partitions->erase(it);
+ continue;
+ }
+ if (want_reboot) {
+ LOG(INFO) << "Using overlayfs for " << mount_point;
+ result->reboot_later = true;
+ result->setup_overlayfs = true;
+ }
+ }
+ it++;
+ }
+ return status;
+}
+
+static RemountStatus EnableDsuIfNeeded() {
+ auto gsid = android::gsi::GetGsiService();
+ if (!gsid) {
+ return REMOUNT_SUCCESS;
+ }
+
+ auto dsu_running = false;
+ if (auto status = gsid->isGsiRunning(&dsu_running); !status.isOk()) {
+ LOG(ERROR) << "Failed to get DSU running state: " << status;
+ return BINDER_ERROR;
+ }
+ auto dsu_enabled = false;
+ if (auto status = gsid->isGsiEnabled(&dsu_enabled); !status.isOk()) {
+ LOG(ERROR) << "Failed to get DSU enabled state: " << status;
+ return BINDER_ERROR;
+ }
+ if (dsu_running && !dsu_enabled) {
+ std::string dsu_slot;
+ if (auto status = gsid->getActiveDsuSlot(&dsu_slot); !status.isOk()) {
+ LOG(ERROR) << "Failed to get active DSU slot: " << status;
+ return BINDER_ERROR;
+ }
+ LOG(INFO) << "DSU is running but disabled, enable DSU so that we stay within the "
+ "DSU guest system after reboot";
+ int error = 0;
+ if (auto status = gsid->enableGsi(/* oneShot = */ true, dsu_slot, &error);
+ !status.isOk() || error != android::gsi::IGsiService::INSTALL_OK) {
+ LOG(ERROR) << "Failed to enable DSU: " << status << ", error code: " << error;
+ return !status.isOk() ? BINDER_ERROR : GSID_ERROR;
+ }
+ LOG(INFO) << "Successfully enabled DSU (one-shot mode)";
+ }
+ return REMOUNT_SUCCESS;
+}
+
+static RemountStatus RemountPartition(Fstab& fstab, Fstab& mounts, FstabEntry& entry) {
+ // unlock the r/o key for the mount point device
+ if (entry.fs_mgr_flags.logical) {
+ fs_mgr_update_logical_partition(&entry);
+ }
+ auto blk_device = entry.blk_device;
+ auto mount_point = entry.mount_point;
+
+ auto found = false;
+ for (auto it = mounts.rbegin(); it != mounts.rend(); ++it) {
+ auto& rentry = *it;
+ if (mount_point == rentry.mount_point) {
+ blk_device = rentry.blk_device;
+ found = true;
+ break;
+ }
+ // Find overlayfs mount point?
+ if ((mount_point == "/" && rentry.mount_point == "/system") ||
+ (mount_point == "/system" && rentry.mount_point == "/")) {
+ blk_device = rentry.blk_device;
+ mount_point = "/system";
+ found = true;
+ break;
+ }
+ }
+ if (!found) {
+ PLOG(INFO) << "skip unmounted partition dev:" << blk_device << " mnt:" << mount_point;
+ return REMOUNT_SUCCESS;
+ }
+ if (blk_device == "/dev/root") {
+ auto from_fstab = GetEntryForMountPoint(&fstab, mount_point);
+ if (from_fstab) blk_device = from_fstab->blk_device;
+ }
+ fs_mgr_set_blk_ro(blk_device, false);
+
+ // Find system-as-root mount point?
+ if ((mount_point == "/system") && !GetEntryForMountPoint(&mounts, mount_point) &&
+ GetEntryForMountPoint(&mounts, "/")) {
+ mount_point = "/";
+ }
+
+ // Now remount!
+ if (::mount(blk_device.c_str(), mount_point.c_str(), entry.fs_type.c_str(), MS_REMOUNT,
+ nullptr) == 0) {
+ return REMOUNT_SUCCESS;
+ }
+ if ((errno == EINVAL) && (mount_point != entry.mount_point)) {
+ mount_point = entry.mount_point;
+ if (::mount(blk_device.c_str(), mount_point.c_str(), entry.fs_type.c_str(), MS_REMOUNT,
+ nullptr) == 0) {
+ return REMOUNT_SUCCESS;
+ }
+ }
+
+ PLOG(ERROR) << "failed to remount partition dev:" << blk_device << " mnt:" << mount_point;
+ return REMOUNT_FAILED;
+}
+
+static int do_remount(Fstab& fstab, const std::vector<std::string>& partition_args,
+ RemountCheckResult* check_result) {
+ Fstab partitions;
+ if (partition_args.empty()) {
+ partitions = GetAllRemountablePartitions(fstab);
+ } else {
+ if (auto rv = GetRemountList(fstab, partition_args, &partitions); rv != REMOUNT_SUCCESS) {
+ return rv;
+ }
+ }
+
+ // Check verity and optionally setup overlayfs backing.
+ auto retval = CheckVerityAndOverlayfs(&partitions, check_result);
+
+ if (partitions.empty() || check_result->disabled_verity) {
+ if (partitions.empty()) {
+ LOG(WARNING) << "No remountable partitions were found.";
+ }
+ return retval;
+ }
+
+ // Mount overlayfs.
+ errno = 0;
+ if (!fs_mgr_overlayfs_mount_all(&partitions) && errno) {
+ PLOG(ERROR) << "Can not mount overlayfs for partitions";
+ return BAD_OVERLAY;
+ }
+
+ // Get actual mounts _after_ overlayfs has been added.
+ android::fs_mgr::Fstab mounts;
+ if (!android::fs_mgr::ReadFstabFromFile("/proc/mounts", &mounts) || mounts.empty()) {
+ PLOG(ERROR) << "Failed to read /proc/mounts";
+ return NO_MOUNTS;
+ }
+
+ // Remount selected partitions.
+ for (auto& entry : partitions) {
+ if (auto rv = RemountPartition(fstab, mounts, entry); rv != REMOUNT_SUCCESS) {
+ retval = rv;
+ } else {
+ check_result->remounted_anything = true;
+ }
+ }
+ return retval;
+}
+
+int main(int argc, char* argv[]) {
+ // Do not use MyLogger() when running as clean_scratch_files, as stdout/stderr of daemon process
+ // are discarded.
+ if (argc > 0 && android::base::Basename(argv[0]) == "clean_scratch_files"s) {
+ android::fs_mgr::CleanupOldScratchFiles();
+ return 0;
+ }
+
+ android::base::InitLogging(argv, MyLogger);
+
+ // Make sure we are root.
+ if (::getuid() != 0) {
+ LOG(ERROR) << "Not running as root. Try \"adb root\" first.";
+ return NOT_ROOT;
+ }
// If somehow this executable is delivered on a "user" build, it can
// not function, so providing a clear message to the caller rather than
@@ -159,14 +500,14 @@
}
const char* fstab_file = nullptr;
- auto can_reboot = false;
+ auto auto_reboot = false;
+ std::vector<std::string> partition_args;
struct option longopts[] = {
{"fstab", required_argument, nullptr, 'T'},
{"help", no_argument, nullptr, 'h'},
{"reboot", no_argument, nullptr, 'R'},
{"verbose", no_argument, nullptr, 'v'},
- {"clean_scratch_files", no_argument, nullptr, 'C'},
{0, 0, nullptr, 0},
};
for (int opt; (opt = ::getopt_long(argc, argv, "hRT:v", longopts, nullptr)) != -1;) {
@@ -175,7 +516,7 @@
usage(SUCCESS);
break;
case 'R':
- can_reboot = true;
+ auto_reboot = true;
break;
case 'T':
if (fstab_file) {
@@ -187,8 +528,6 @@
case 'v':
verbose = true;
break;
- case 'C':
- return CLEAN_SCRATCH_FILES;
default:
LOG(ERROR) << "Bad Argument -" << char(opt);
usage(BADARG);
@@ -196,310 +535,51 @@
}
}
- // Make sure we are root.
- if (::getuid() != 0) {
- LOG(ERROR) << "Not running as root. Try \"adb root\" first.";
- return NOT_ROOT;
+ for (; argc > optind; ++optind) {
+ partition_args.emplace_back(argv[optind]);
+ }
+
+ // Make sure checkpointing is disabled if necessary.
+ if (auto rv = VerifyCheckpointing(); rv != REMOUNT_SUCCESS) {
+ return rv;
}
// Read the selected fstab.
- android::fs_mgr::Fstab fstab;
- auto fstab_read = false;
- if (fstab_file) {
- fstab_read = android::fs_mgr::ReadFstabFromFile(fstab_file, &fstab);
- } else {
- fstab_read = android::fs_mgr::ReadDefaultFstab(&fstab);
- // Manufacture a / entry from /proc/mounts if missing.
- if (!GetEntryForMountPoint(&fstab, "/system") && !GetEntryForMountPoint(&fstab, "/")) {
- android::fs_mgr::Fstab mounts;
- if (android::fs_mgr::ReadFstabFromFile("/proc/mounts", &mounts)) {
- if (auto entry = GetEntryForMountPoint(&mounts, "/")) {
- if (entry->fs_type != "rootfs") fstab.emplace_back(*entry);
- }
- }
- }
- }
- if (!fstab_read || fstab.empty()) {
+ Fstab fstab;
+ if (!ReadFstab(fstab_file, &fstab) || fstab.empty()) {
PLOG(ERROR) << "Failed to read fstab";
return NO_FSTAB;
}
- if (android::base::GetBoolProperty("ro.virtual_ab.enabled", false) &&
- !android::base::GetBoolProperty("ro.virtual_ab.retrofit", false)) {
- // Virtual A/B devices can use /data as backing storage; make sure we're
- // not checkpointing.
- auto vold = GetVold();
- bool checkpointing = false;
- if (!vold->isCheckpointing(&checkpointing).isOk()) {
- LOG(ERROR) << "Could not determine checkpointing status.";
- return BINDER_ERROR;
- }
- if (checkpointing) {
- LOG(ERROR) << "Cannot use remount when a checkpoint is in progress.";
- return CHECKPOINTING;
- }
+ RemountCheckResult check_result;
+ int result = do_remount(fstab, partition_args, &check_result);
+
+ if (check_result.disabled_verity && check_result.setup_overlayfs) {
+ LOG(INFO) << "Verity disabled; overlayfs enabled.";
+ } else if (check_result.disabled_verity) {
+ LOG(INFO) << "Verity disabled.";
+ } else if (check_result.setup_overlayfs) {
+ LOG(INFO) << "Overlayfs enabled.";
}
- // Generate the list of supported overlayfs mount points.
- auto overlayfs_candidates = fs_mgr_overlayfs_candidate_list(fstab);
-
- // Generate the all remountable partitions sub-list
- android::fs_mgr::Fstab all;
- for (auto const& entry : fstab) {
- if (!remountable_partition(entry)) continue;
- if (overlayfs_candidates.empty() ||
- GetEntryForMountPoint(&overlayfs_candidates, entry.mount_point) ||
- (is_wrapped(overlayfs_candidates, entry) == nullptr)) {
- all.emplace_back(entry);
- }
- }
-
- // Parse the unique list of valid partition arguments.
- android::fs_mgr::Fstab partitions;
- for (; argc > optind; ++optind) {
- auto partition = std::string(argv[optind]);
- if (partition.empty()) continue;
- if (partition == "/") partition = "/system";
- auto find_part = [&partition](const auto& entry) {
- const auto mount_point = system_mount_point(entry);
- if (partition == mount_point) return true;
- if (partition == android::base::Basename(mount_point)) return true;
- return false;
- };
- // Do we know about the partition?
- auto it = std::find_if(fstab.begin(), fstab.end(), find_part);
- if (it == fstab.end()) {
- LOG(ERROR) << "Unknown partition " << argv[optind] << ", skipping";
- retval = UNKNOWN_PARTITION;
- continue;
- }
- // Is that one covered by an existing overlayfs?
- auto wrap = is_wrapped(overlayfs_candidates, *it);
- if (wrap) {
- LOG(INFO) << "partition " << argv[optind] << " covered by overlayfs for "
- << wrap->mount_point << ", switching";
- partition = system_mount_point(*wrap);
- }
- // Is it a remountable partition?
- it = std::find_if(all.begin(), all.end(), find_part);
- if (it == all.end()) {
- LOG(ERROR) << "Invalid partition " << argv[optind] << ", skipping";
- retval = INVALID_PARTITION;
- continue;
- }
- if (GetEntryForMountPoint(&partitions, it->mount_point) == nullptr) {
- partitions.emplace_back(*it);
- }
- }
-
- if (partitions.empty() && !retval) {
- partitions = all;
- }
-
- // Check verity and optionally setup overlayfs backing.
- auto reboot_later = false;
- auto user_please_reboot_later = false;
- auto setup_overlayfs = false;
- auto just_disabled_verity = false;
- for (auto it = partitions.begin(); it != partitions.end();) {
- auto& entry = *it;
- auto& mount_point = entry.mount_point;
- if (fs_mgr_is_verity_enabled(entry)) {
- retval = VERITY_PARTITION;
- auto ret = false;
- if (android::base::GetProperty("ro.boot.vbmeta.device_state", "") != "locked") {
- if (AvbOps* ops = avb_ops_user_new()) {
- ret = avb_user_verity_set(
- ops, android::base::GetProperty("ro.boot.slot_suffix", "").c_str(),
- false);
- avb_ops_user_free(ops);
- }
- if (!ret && fs_mgr_set_blk_ro(entry.blk_device, false)) {
- fec::io fh(entry.blk_device.c_str(), O_RDWR);
- ret = fh && fh.set_verity_status(false);
- }
- if (ret) {
- LOG(WARNING) << "Disabling verity for " << mount_point;
- just_disabled_verity = true;
- reboot_later = can_reboot;
- user_please_reboot_later = true;
- }
+ if (check_result.reboot_later) {
+ if (auto_reboot) {
+ // If (1) remount requires a reboot to take effect, (2) system is currently
+ // running a DSU guest and (3) DSU is disabled, then enable DSU so that the
+ // next reboot would not take us back to the host system but stay within
+ // the guest system.
+ if (auto rv = EnableDsuIfNeeded(); rv != REMOUNT_SUCCESS) {
+ LOG(ERROR) << "Unable to automatically enable DSU";
+ return rv;
}
- if (!ret) {
- LOG(ERROR) << "Skipping " << mount_point << " for remount";
- it = partitions.erase(it);
- continue;
- }
+ reboot();
+ } else {
+ LOG(INFO) << "Now reboot your device for settings to take effect";
}
-
- auto change = false;
- errno = 0;
- if (fs_mgr_overlayfs_setup(nullptr, mount_point.c_str(), &change, just_disabled_verity)) {
- if (change) {
- LOG(INFO) << "Using overlayfs for " << mount_point;
- reboot_later = can_reboot;
- user_please_reboot_later = true;
- setup_overlayfs = true;
- }
- } else if (errno) {
- PLOG(ERROR) << "Overlayfs setup for " << mount_point << " failed, skipping";
- retval = BAD_OVERLAY;
- it = partitions.erase(it);
- continue;
- }
- ++it;
+ return REMOUNT_SUCCESS;
}
-
- // If (1) remount requires a reboot to take effect, (2) system is currently
- // running a DSU guest and (3) DSU is disabled, then enable DSU so that the
- // next reboot would not take us back to the host system but stay within
- // the guest system.
- if (reboot_later) {
- if (auto gsid = android::gsi::GetGsiService()) {
- auto dsu_running = false;
- if (auto status = gsid->isGsiRunning(&dsu_running); !status.isOk()) {
- LOG(ERROR) << "Failed to get DSU running state: " << status;
- return BINDER_ERROR;
- }
- auto dsu_enabled = false;
- if (auto status = gsid->isGsiEnabled(&dsu_enabled); !status.isOk()) {
- LOG(ERROR) << "Failed to get DSU enabled state: " << status;
- return BINDER_ERROR;
- }
- if (dsu_running && !dsu_enabled) {
- std::string dsu_slot;
- if (auto status = gsid->getActiveDsuSlot(&dsu_slot); !status.isOk()) {
- LOG(ERROR) << "Failed to get active DSU slot: " << status;
- return BINDER_ERROR;
- }
- LOG(INFO) << "DSU is running but disabled, enable DSU so that we stay within the "
- "DSU guest system after reboot";
- int error = 0;
- if (auto status = gsid->enableGsi(/* oneShot = */ true, dsu_slot, &error);
- !status.isOk() || error != android::gsi::IGsiService::INSTALL_OK) {
- LOG(ERROR) << "Failed to enable DSU: " << status << ", error code: " << error;
- return !status.isOk() ? BINDER_ERROR : GSID_ERROR;
- }
- LOG(INFO) << "Successfully enabled DSU (one-shot mode)";
- }
- }
- }
-
- if (partitions.empty() || just_disabled_verity) {
- if (reboot_later) reboot(setup_overlayfs);
- if (user_please_reboot_later) {
- return MUST_REBOOT;
- }
- LOG(WARNING) << "No partitions to remount";
- return retval;
- }
-
- // Mount overlayfs.
- errno = 0;
- if (!fs_mgr_overlayfs_mount_all(&partitions) && errno) {
- retval = BAD_OVERLAY;
- PLOG(ERROR) << "Can not mount overlayfs for partitions";
- }
-
- // Get actual mounts _after_ overlayfs has been added.
- android::fs_mgr::Fstab mounts;
- if (!android::fs_mgr::ReadFstabFromFile("/proc/mounts", &mounts) || mounts.empty()) {
- PLOG(ERROR) << "Failed to read /proc/mounts";
- retval = NO_MOUNTS;
- }
-
- // Remount selected partitions.
- for (auto& entry : partitions) {
- // unlock the r/o key for the mount point device
- if (entry.fs_mgr_flags.logical) {
- fs_mgr_update_logical_partition(&entry);
- }
- auto blk_device = entry.blk_device;
- auto mount_point = entry.mount_point;
-
- auto found = false;
- for (auto it = mounts.rbegin(); it != mounts.rend(); ++it) {
- auto& rentry = *it;
- if (mount_point == rentry.mount_point) {
- blk_device = rentry.blk_device;
- found = true;
- break;
- }
- // Find overlayfs mount point?
- if ((mount_point == "/" && rentry.mount_point == "/system") ||
- (mount_point == "/system" && rentry.mount_point == "/")) {
- blk_device = rentry.blk_device;
- mount_point = "/system";
- found = true;
- break;
- }
- }
- if (!found) {
- PLOG(INFO) << "skip unmounted partition dev:" << blk_device << " mnt:" << mount_point;
- continue;
- }
- if (blk_device == "/dev/root") {
- auto from_fstab = GetEntryForMountPoint(&fstab, mount_point);
- if (from_fstab) blk_device = from_fstab->blk_device;
- }
- fs_mgr_set_blk_ro(blk_device, false);
-
- // Find system-as-root mount point?
- if ((mount_point == "/system") && !GetEntryForMountPoint(&mounts, mount_point) &&
- GetEntryForMountPoint(&mounts, "/")) {
- mount_point = "/";
- }
-
- // Now remount!
- if (::mount(blk_device.c_str(), mount_point.c_str(), entry.fs_type.c_str(), MS_REMOUNT,
- nullptr) == 0) {
- continue;
- }
- if ((errno == EINVAL) && (mount_point != entry.mount_point)) {
- mount_point = entry.mount_point;
- if (::mount(blk_device.c_str(), mount_point.c_str(), entry.fs_type.c_str(), MS_REMOUNT,
- nullptr) == 0) {
- continue;
- }
- }
- PLOG(ERROR) << "failed to remount partition dev:" << blk_device << " mnt:" << mount_point;
- // If errno is EROFS at this point, we are dealing with r/o
- // filesystem types like squashfs, erofs or ext4 dedupe. We will
- // consider such a device that does not have CONFIG_OVERLAY_FS
- // in the kernel as a misconfigured.
- if (errno == EROFS) {
- LOG(ERROR) << "Consider providing all the dependencies to enable overlayfs";
- }
- retval = REMOUNT_FAILED;
- }
-
- if (reboot_later) reboot(setup_overlayfs);
- if (user_please_reboot_later) {
- LOG(INFO) << "Now reboot your device for settings to take effect";
- return 0;
- }
-
- return retval;
-}
-
-static int do_clean_scratch_files() {
- android::fs_mgr::CleanupOldScratchFiles();
- return 0;
-}
-
-int main(int argc, char* argv[]) {
- android::base::InitLogging(argv, MyLogger);
- if (argc > 0 && android::base::Basename(argv[0]) == "clean_scratch_files"s) {
- return do_clean_scratch_files();
- }
- int result = do_remount(argc, argv);
- if (result == MUST_REBOOT) {
- LOG(INFO) << "Now reboot your device for settings to take effect";
- result = 0;
- } else if (result == REMOUNT_SUCCESS) {
+ if (result == REMOUNT_SUCCESS) {
printf("remount succeeded\n");
- } else if (result == CLEAN_SCRATCH_FILES) {
- return do_clean_scratch_files();
} else {
printf("remount failed\n");
}
diff --git a/fs_mgr/include/fs_mgr_overlayfs.h b/fs_mgr/include/fs_mgr_overlayfs.h
index 6caab1f..590f66b 100644
--- a/fs_mgr/include/fs_mgr_overlayfs.h
+++ b/fs_mgr/include/fs_mgr_overlayfs.h
@@ -26,14 +26,22 @@
android::fs_mgr::Fstab fs_mgr_overlayfs_candidate_list(const android::fs_mgr::Fstab& fstab);
+bool fs_mgr_wants_overlayfs(android::fs_mgr::FstabEntry* entry);
bool fs_mgr_overlayfs_mount_all(android::fs_mgr::Fstab* fstab);
-bool fs_mgr_overlayfs_setup(const char* backing = nullptr, const char* mount_point = nullptr,
- bool* change = nullptr, bool force = true);
bool fs_mgr_overlayfs_teardown(const char* mount_point = nullptr, bool* change = nullptr);
bool fs_mgr_overlayfs_is_setup();
bool fs_mgr_has_shared_blocks(const std::string& mount_point, const std::string& dev);
+bool fs_mgr_overlayfs_already_mounted(const std::string& mount_point, bool overlay_only = true);
std::string fs_mgr_get_context(const std::string& mount_point);
+// If "mount_point" is non-null, set up exactly one overlay.
+// If "mount_point" is null, setup any overlays.
+//
+// If |want_reboot| is non-null, and a reboot is needed to apply overlays, then
+// it will be true on return. The caller is responsible for initializing it.
+bool fs_mgr_overlayfs_setup(const char* mount_point = nullptr, bool* want_reboot = nullptr,
+ bool just_disabled_verity = true);
+
enum class OverlayfsValidResult {
kNotSupported = 0,
kOk,
diff --git a/fs_mgr/include_fstab/fstab/fstab.h b/fs_mgr/include_fstab/fstab/fstab.h
index f26fb24..689d18b 100644
--- a/fs_mgr/include_fstab/fstab/fstab.h
+++ b/fs_mgr/include_fstab/fstab/fstab.h
@@ -31,6 +31,7 @@
struct FstabEntry {
std::string blk_device;
+ std::string zoned_device;
std::string logical_partition_name;
std::string mount_point;
std::string fs_type;
@@ -94,6 +95,8 @@
// Exported for testability. Regular users should use ReadFstabFromFile().
bool ParseFstabFromString(const std::string& fstab_str, bool proc_mounts, Fstab* fstab_out);
+// Exported for testability. Regular users should use ReadDefaultFstab().
+std::string GetFstabPath();
bool ReadFstabFromFile(const std::string& path, Fstab* fstab);
bool ReadFstabFromDt(Fstab* fstab, bool verbose = true);
diff --git a/fs_mgr/libfiemap/image_test.cpp b/fs_mgr/libfiemap/image_test.cpp
index 7472949..fb104b7 100644
--- a/fs_mgr/libfiemap/image_test.cpp
+++ b/fs_mgr/libfiemap/image_test.cpp
@@ -14,11 +14,13 @@
// limitations under the License.
//
+#include <inttypes.h>
#include <stdlib.h>
#include <string.h>
#include <sys/mount.h>
#include <sys/stat.h>
#include <sys/types.h>
+#include <sys/vfs.h>
#include <chrono>
#include <iostream>
@@ -26,12 +28,14 @@
#include <android-base/file.h>
#include <android-base/properties.h>
+#include <android-base/stringprintf.h>
#include <android-base/strings.h>
#include <android-base/unique_fd.h>
#include <ext4_utils/ext4_utils.h>
#include <fs_mgr/file_wait.h>
#include <gtest/gtest.h>
#include <libdm/dm.h>
+#include <libdm/loop_control.h>
#include <libfiemap/image_manager.h>
#include "utility.h"
@@ -46,7 +50,7 @@
using android::fs_mgr::WaitForFile;
static std::string gDataPath;
-static std::string gDataMountPath;
+static std::string gTestDir;
static constexpr char kMetadataPath[] = "/metadata/gsi/test";
static constexpr uint64_t kTestImageSize = 1024 * 1024;
@@ -178,6 +182,119 @@
INSTANTIATE_TEST_SUITE_P(IsSubdirTest, IsSubdirTest, ::testing::ValuesIn(IsSubdirTestValues()));
+// This allows test cases for filesystems with larger than 4KiB alignment.
+// It creates a loop device, formats it with a FAT filesystem, and then
+// creates an ImageManager so backing images can be created on that filesystem.
+class VfatTest : public ::testing::Test {
+ protected:
+ // 64MB Filesystem and 32k block size by default
+ static constexpr uint64_t kBlockSize = 32768;
+ static constexpr uint64_t kFilesystemSize = 64 * 1024 * 1024;
+
+ void SetUp() override {
+ const ::testing::TestInfo* tinfo = ::testing::UnitTest::GetInstance()->current_test_info();
+ base_name_ = tinfo->name();
+
+ fs_path_ = gTestDir + "/vfat.img";
+ uint64_t count = kFilesystemSize / kBlockSize;
+ std::string dd_cmd =
+ ::android::base::StringPrintf("/system/bin/dd if=/dev/zero of=%s bs=%" PRIu64
+ " count=%" PRIu64 " > /dev/null 2>&1",
+ fs_path_.c_str(), kBlockSize, count);
+ // create mount point
+ mntpoint_ = std::string(getenv("TMPDIR")) + "/fiemap_mnt";
+ if (mkdir(mntpoint_.c_str(), S_IRWXU) < 0) {
+ ASSERT_EQ(errno, EEXIST) << strerror(errno);
+ }
+
+ // create file for the file system
+ int ret = system(dd_cmd.c_str());
+ ASSERT_EQ(ret, 0);
+
+ // Get and attach a loop device to the filesystem we created
+ loop_device_.emplace(fs_path_, 10s);
+ ASSERT_TRUE(loop_device_->valid());
+
+ // create file system
+ uint64_t sectors = kFilesystemSize / 512;
+ std::string mkfs_cmd =
+ ::android::base::StringPrintf("/system/bin/newfs_msdos -A -O Android -s %" PRIu64
+ " -b %" PRIu64 " %s > /dev/null 2>&1",
+ sectors, kBlockSize, loop_device_->device().c_str());
+ ret = system(mkfs_cmd.c_str());
+ ASSERT_EQ(ret, 0);
+
+ // Create a wrapping DM device to prevent gsid taking the loopback path.
+ auto& dm = DeviceMapper::Instance();
+ DmTable table;
+ table.Emplace<DmTargetLinear>(0, kFilesystemSize / 512, loop_device_->device(), 0);
+
+ dm_name_ = android::base::Basename(loop_device_->device()) + "-wrapper";
+ ASSERT_TRUE(dm.CreateDevice(dm_name_, table, &dm_path_, 10s));
+
+ // mount the file system
+ ASSERT_EQ(mount(dm_path_.c_str(), mntpoint_.c_str(), "vfat", 0, nullptr), 0)
+ << strerror(errno);
+ }
+
+ void TearDown() override {
+ // Clear up anything backed on the temporary FS.
+ if (manager_) {
+ manager_->UnmapImageIfExists(base_name_);
+ manager_->DeleteBackingImage(base_name_);
+ }
+
+ // Unmount temporary FS.
+ if (umount(mntpoint_.c_str()) < 0) {
+ ASSERT_EQ(errno, EINVAL) << strerror(errno);
+ }
+
+ // Destroy the dm wrapper.
+ auto& dm = DeviceMapper::Instance();
+ ASSERT_TRUE(dm.DeleteDeviceIfExists(dm_name_));
+
+ // Destroy the loop device.
+ loop_device_ = {};
+
+ // Destroy the temporary FS.
+ if (rmdir(mntpoint_.c_str()) < 0) {
+ ASSERT_EQ(errno, ENOENT) << strerror(errno);
+ }
+ if (unlink(fs_path_.c_str()) < 0) {
+ ASSERT_EQ(errno, ENOENT) << strerror(errno);
+ }
+ }
+
+ std::string base_name_;
+ std::string mntpoint_;
+ std::string fs_path_;
+ std::optional<LoopDevice> loop_device_;
+ std::string dm_name_;
+ std::string dm_path_;
+ std::unique_ptr<ImageManager> manager_;
+};
+
+// The actual size of the block device should be the requested size. For
+// example, a 16KB image should be mapped as a 16KB device, even if the
+// underlying filesystem requires 32KB to be fallocated.
+TEST_F(VfatTest, DeviceIsRequestedSize) {
+ manager_ = ImageManager::Open(kMetadataPath, mntpoint_);
+ ASSERT_NE(manager_, nullptr);
+
+ manager_->set_partition_opener(std::make_unique<TestPartitionOpener>());
+
+ // Create something not aligned to the backing fs block size.
+ constexpr uint64_t kTestSize = (kBlockSize * 64) - (kBlockSize / 2);
+ ASSERT_TRUE(manager_->CreateBackingImage(base_name_, kTestSize, false, nullptr));
+
+ std::string path;
+ ASSERT_TRUE(manager_->MapImageDevice(base_name_, 10s, &path));
+
+ unique_fd fd(open(path.c_str(), O_RDONLY | O_CLOEXEC));
+ ASSERT_GE(fd, 0);
+ ASSERT_EQ(get_block_device_size(fd.get()), kTestSize);
+}
+
} // namespace
bool Mkdir(const std::string& path) {
@@ -194,13 +311,27 @@
if (argc >= 2) {
gDataPath = argv[1];
} else {
- gDataPath = "/data/gsi/test";
+ gDataPath = "/data/local/tmp";
}
- gDataMountPath = gDataPath + "/mnt"s;
- if (!Mkdir(gDataPath) || !Mkdir(kMetadataPath) || !Mkdir(gDataMountPath) ||
- !Mkdir(kMetadataPath + "/mnt"s)) {
+ if (!Mkdir(gDataPath) || !Mkdir(kMetadataPath) || !Mkdir(kMetadataPath + "/mnt"s)) {
return 1;
}
- return RUN_ALL_TESTS();
+
+ std::string tempdir = gDataPath + "/XXXXXX";
+ if (!mkdtemp(tempdir.data())) {
+ std::cerr << "unable to create tempdir on " << tempdir << "\n";
+ exit(EXIT_FAILURE);
+ }
+ if (!android::base::Realpath(tempdir, &gTestDir)) {
+ std::cerr << "unable to find realpath for " << tempdir;
+ exit(EXIT_FAILURE);
+ }
+
+ auto rv = RUN_ALL_TESTS();
+
+ std::string cmd = "rm -rf " + gTestDir;
+ system(cmd.c_str());
+
+ return rv;
}
diff --git a/fs_mgr/liblp/TEST_MAPPING b/fs_mgr/liblp/TEST_MAPPING
index 875ccb0..cf0d22b 100644
--- a/fs_mgr/liblp/TEST_MAPPING
+++ b/fs_mgr/liblp/TEST_MAPPING
@@ -4,7 +4,7 @@
"name": "liblp_test"
}
],
- "hwasan-postsubmit": [
+ "hwasan-presubmit": [
{
"name": "liblp_test"
}
diff --git a/fs_mgr/libsnapshot/Android.bp b/fs_mgr/libsnapshot/Android.bp
index 6db8f13..d5e85e6 100644
--- a/fs_mgr/libsnapshot/Android.bp
+++ b/fs_mgr/libsnapshot/Android.bp
@@ -44,7 +44,6 @@
"libext2_uuid",
"libext4_utils",
"libfstab",
- "libsnapshot_cow",
"libsnapshot_snapuserd",
"libz",
],
@@ -75,6 +74,8 @@
shared_libs: [
"android.hardware.boot@1.0",
"android.hardware.boot@1.1",
+ "android.hardware.boot-V1-ndk",
+ "libboot_control_client",
],
}
@@ -154,22 +155,6 @@
"-Wall",
"-Werror",
],
- export_include_dirs: ["include"],
- srcs: [
- "cow_decompress.cpp",
- "cow_reader.cpp",
- "cow_writer.cpp",
- "cow_format.cpp",
- ],
-}
-
-cc_library_static {
- name: "libsnapshot_cow",
- defaults: [
- "libsnapshot_cow_defaults",
- ],
- host_supported: true,
- recovery_available: true,
shared_libs: [
"libbase",
"liblog",
@@ -177,7 +162,24 @@
static_libs: [
"libbrotli",
"libz",
+ "liblz4",
],
+ export_include_dirs: ["include"],
+}
+
+cc_library_static {
+ name: "libsnapshot_cow",
+ defaults: [
+ "libsnapshot_cow_defaults",
+ ],
+ srcs: [
+ "cow_decompress.cpp",
+ "cow_reader.cpp",
+ "cow_writer.cpp",
+ "cow_format.cpp",
+ ],
+ host_supported: true,
+ recovery_available: true,
ramdisk_available: true,
vendor_ramdisk_available: true,
}
@@ -214,7 +216,7 @@
cc_defaults {
name: "libsnapshot_test_defaults",
- defaults: ["libsnapshot_defaults"],
+ defaults: ["libsnapshot_defaults", "libsnapshot_cow_defaults"],
srcs: [
"partition_cow_creator_test.cpp",
"snapshot_metadata_updater_test.cpp",
@@ -233,6 +235,7 @@
static_libs: [
"android.hardware.boot@1.0",
"android.hardware.boot@1.1",
+ "android.hardware.boot-V1-ndk",
"libbrotli",
"libc++fs",
"libfs_mgr_binder",
@@ -261,7 +264,7 @@
cc_test {
name: "vts_libsnapshot_test",
- defaults: ["libsnapshot_test_defaults"],
+ defaults: ["libsnapshot_test_defaults", "libsnapshot_hal_deps"],
}
sh_test {
@@ -295,6 +298,7 @@
cc_binary {
name: "snapshotctl",
+ defaults: ["libsnapshot_cow_defaults", "libsnapshot_hal_deps"],
srcs: [
"snapshotctl.cpp",
],
@@ -308,8 +312,6 @@
"update_metadata-protos",
],
shared_libs: [
- "android.hardware.boot@1.0",
- "android.hardware.boot@1.1",
"libbase",
"libext2_uuid",
"libext4_utils",
@@ -343,6 +345,9 @@
cc_defaults {
name: "libsnapshot_fuzzer_defaults",
+ defaults: [
+ "libsnapshot_cow_defaults",
+ ],
native_coverage : true,
srcs: [
// Compile the protobuf definition again with type full.
@@ -416,6 +421,7 @@
name: "cow_api_test",
defaults: [
"fs_mgr_defaults",
+ "libsnapshot_cow_defaults",
],
srcs: [
"cow_api_test.cpp",
@@ -471,6 +477,7 @@
"libsparse",
"libxz",
"libz",
+ "liblz4",
"libziparchive",
"update_metadata-protos",
],
@@ -486,6 +493,9 @@
cc_binary {
name: "estimate_cow_from_nonab_ota",
+ defaults: [
+ "libsnapshot_cow_defaults",
+ ],
host_supported: true,
device_supported: false,
cflags: [
@@ -519,6 +529,7 @@
name: "inspect_cow",
host_supported: true,
device_supported: true,
+ defaults: ["libsnapshot_cow_defaults"],
cflags: [
"-D_FILE_OFFSET_BITS=64",
"-Wall",
diff --git a/fs_mgr/libsnapshot/android/snapshot/snapshot.proto b/fs_mgr/libsnapshot/android/snapshot/snapshot.proto
index 5daa84d..b3763ae 100644
--- a/fs_mgr/libsnapshot/android/snapshot/snapshot.proto
+++ b/fs_mgr/libsnapshot/android/snapshot/snapshot.proto
@@ -97,8 +97,8 @@
// This is non-zero when |state| == MERGING or MERGE_COMPLETED.
uint64 metadata_sectors = 8;
- // True if compression is enabled, false otherwise.
- bool compression_enabled = 9;
+ // True if using snapuserd, false otherwise.
+ bool using_snapuserd = 9;
// The old partition size (if none existed, this will be zero).
uint64 old_partition_size = 10;
@@ -184,7 +184,7 @@
uint64 metadata_sectors = 4;
// Whether compression/dm-user was used for any snapshots.
- bool compression_enabled = 5;
+ bool using_snapuserd = 5;
// Merge phase (if state == MERGING).
MergePhase merge_phase = 6;
@@ -235,4 +235,13 @@
// The source fingerprint at the time the OTA was downloaded.
string source_build_fingerprint = 10;
+
+ // Whether this update attempt uses userspace snapshots.
+ bool userspace_snapshots_used = 11;
+
+ // Whether this update attempt uses XOR compression.
+ bool xor_compression_used = 12;
+
+ // Whether this update attempt used io_uring.
+ bool iouring_used = 13;
}
diff --git a/fs_mgr/libsnapshot/cow_decompress.cpp b/fs_mgr/libsnapshot/cow_decompress.cpp
index faceafe..a4d2277 100644
--- a/fs_mgr/libsnapshot/cow_decompress.cpp
+++ b/fs_mgr/libsnapshot/cow_decompress.cpp
@@ -20,6 +20,7 @@
#include <android-base/logging.h>
#include <brotli/decode.h>
+#include <lz4.h>
#include <zlib.h>
namespace android {
@@ -260,5 +261,43 @@
return std::unique_ptr<IDecompressor>(new BrotliDecompressor());
}
+class Lz4Decompressor final : public IDecompressor {
+ public:
+ ~Lz4Decompressor() override = default;
+
+ bool Decompress(const size_t output_size) override {
+ size_t actual_buffer_size = 0;
+ auto&& output_buffer = sink_->GetBuffer(output_size, &actual_buffer_size);
+ if (actual_buffer_size != output_size) {
+ LOG(ERROR) << "Failed to allocate buffer of size " << output_size << " only got "
+ << actual_buffer_size << " bytes";
+ return false;
+ }
+ std::string input_buffer;
+ input_buffer.resize(stream_->Size());
+ size_t bytes_read = 0;
+ stream_->Read(input_buffer.data(), input_buffer.size(), &bytes_read);
+ if (bytes_read != input_buffer.size()) {
+ LOG(ERROR) << "Failed to read all input at once. Expected: " << input_buffer.size()
+ << " actual: " << bytes_read;
+ return false;
+ }
+ const int bytes_decompressed =
+ LZ4_decompress_safe(input_buffer.data(), static_cast<char*>(output_buffer),
+ input_buffer.size(), output_size);
+ if (bytes_decompressed != output_size) {
+ LOG(ERROR) << "Failed to decompress LZ4 block, expected output size: " << output_size
+ << ", actual: " << bytes_decompressed;
+ return false;
+ }
+ sink_->ReturnData(output_buffer, output_size);
+ return true;
+ }
+};
+
+std::unique_ptr<IDecompressor> IDecompressor::Lz4() {
+ return std::make_unique<Lz4Decompressor>();
+}
+
} // namespace snapshot
} // namespace android
diff --git a/fs_mgr/libsnapshot/cow_decompress.h b/fs_mgr/libsnapshot/cow_decompress.h
index f485256..7f74eda 100644
--- a/fs_mgr/libsnapshot/cow_decompress.h
+++ b/fs_mgr/libsnapshot/cow_decompress.h
@@ -41,6 +41,7 @@
static std::unique_ptr<IDecompressor> Uncompressed();
static std::unique_ptr<IDecompressor> Gz();
static std::unique_ptr<IDecompressor> Brotli();
+ static std::unique_ptr<IDecompressor> Lz4();
// |output_bytes| is the expected total number of bytes to sink.
virtual bool Decompress(size_t output_bytes) = 0;
diff --git a/fs_mgr/libsnapshot/cow_reader.cpp b/fs_mgr/libsnapshot/cow_reader.cpp
index 746feeb..45be191 100644
--- a/fs_mgr/libsnapshot/cow_reader.cpp
+++ b/fs_mgr/libsnapshot/cow_reader.cpp
@@ -34,12 +34,13 @@
namespace android {
namespace snapshot {
-CowReader::CowReader(ReaderFlags reader_flag)
+CowReader::CowReader(ReaderFlags reader_flag, bool is_merge)
: fd_(-1),
header_(),
fd_size_(0),
- merge_op_blocks_(std::make_shared<std::vector<uint32_t>>()),
- reader_flag_(reader_flag) {}
+ block_pos_index_(std::make_shared<std::vector<int>>()),
+ reader_flag_(reader_flag),
+ is_merge_(is_merge) {}
static void SHA256(const void*, size_t, uint8_t[]) {
#if 0
@@ -58,13 +59,13 @@
cow->fd_size_ = fd_size_;
cow->last_label_ = last_label_;
cow->ops_ = ops_;
- cow->merge_op_blocks_ = merge_op_blocks_;
cow->merge_op_start_ = merge_op_start_;
- cow->block_map_ = block_map_;
cow->num_total_data_ops_ = num_total_data_ops_;
cow->num_ordered_ops_to_merge_ = num_ordered_ops_to_merge_;
cow->has_seq_ops_ = has_seq_ops_;
cow->data_loc_ = data_loc_;
+ cow->block_pos_index_ = block_pos_index_;
+ cow->is_merge_ = is_merge_;
return cow;
}
@@ -415,10 +416,10 @@
// Replace-op-4, Zero-op-9, Replace-op-5 }
//==============================================================
bool CowReader::PrepMergeOps() {
- auto merge_op_blocks = std::make_shared<std::vector<uint32_t>>();
+ auto merge_op_blocks = std::make_unique<std::vector<uint32_t>>();
std::vector<int> other_ops;
auto seq_ops_set = std::unordered_set<uint32_t>();
- auto block_map = std::make_shared<std::unordered_map<uint32_t, int>>();
+ auto block_map = std::make_unique<std::unordered_map<uint32_t, int>>();
size_t num_seqs = 0;
size_t read;
@@ -482,8 +483,26 @@
merge_op_start_ = header_.num_merge_ops;
}
- block_map_ = block_map;
- merge_op_blocks_ = merge_op_blocks;
+ if (is_merge_) {
+ // Metadata ops are not required for merge. Thus, just re-arrange
+ // the ops vector as required for merge operations.
+ auto merge_ops_buffer = std::make_shared<std::vector<CowOperation>>();
+ merge_ops_buffer->reserve(num_total_data_ops_);
+ for (auto block : *merge_op_blocks) {
+ merge_ops_buffer->emplace_back(ops_->data()[block_map->at(block)]);
+ }
+ ops_->clear();
+ ops_ = merge_ops_buffer;
+ ops_->shrink_to_fit();
+ } else {
+ for (auto block : *merge_op_blocks) {
+ block_pos_index_->push_back(block_map->at(block));
+ }
+ }
+
+ block_map->clear();
+ merge_op_blocks->clear();
+
return true;
}
@@ -544,7 +563,7 @@
class CowOpIter final : public ICowOpIter {
public:
- CowOpIter(std::shared_ptr<std::vector<CowOperation>>& ops);
+ CowOpIter(std::shared_ptr<std::vector<CowOperation>>& ops, uint64_t start);
bool Done() override;
const CowOperation& Get() override;
@@ -558,9 +577,9 @@
std::vector<CowOperation>::iterator op_iter_;
};
-CowOpIter::CowOpIter(std::shared_ptr<std::vector<CowOperation>>& ops) {
+CowOpIter::CowOpIter(std::shared_ptr<std::vector<CowOperation>>& ops, uint64_t start) {
ops_ = ops;
- op_iter_ = ops_->begin();
+ op_iter_ = ops_->begin() + start;
}
bool CowOpIter::RDone() {
@@ -589,9 +608,7 @@
class CowRevMergeOpIter final : public ICowOpIter {
public:
explicit CowRevMergeOpIter(std::shared_ptr<std::vector<CowOperation>> ops,
- std::shared_ptr<std::vector<uint32_t>> merge_op_blocks,
- std::shared_ptr<std::unordered_map<uint32_t, int>> map,
- uint64_t start);
+ std::shared_ptr<std::vector<int>> block_pos_index, uint64_t start);
bool Done() override;
const CowOperation& Get() override;
@@ -602,17 +619,15 @@
private:
std::shared_ptr<std::vector<CowOperation>> ops_;
- std::shared_ptr<std::vector<uint32_t>> merge_op_blocks_;
- std::shared_ptr<std::unordered_map<uint32_t, int>> map_;
- std::vector<uint32_t>::reverse_iterator block_riter_;
+ std::vector<int>::reverse_iterator block_riter_;
+ std::shared_ptr<std::vector<int>> cow_op_index_vec_;
uint64_t start_;
};
class CowMergeOpIter final : public ICowOpIter {
public:
explicit CowMergeOpIter(std::shared_ptr<std::vector<CowOperation>> ops,
- std::shared_ptr<std::vector<uint32_t>> merge_op_blocks,
- std::shared_ptr<std::unordered_map<uint32_t, int>> map, uint64_t start);
+ std::shared_ptr<std::vector<int>> block_pos_index, uint64_t start);
bool Done() override;
const CowOperation& Get() override;
@@ -623,26 +638,21 @@
private:
std::shared_ptr<std::vector<CowOperation>> ops_;
- std::shared_ptr<std::vector<uint32_t>> merge_op_blocks_;
- std::shared_ptr<std::unordered_map<uint32_t, int>> map_;
- std::vector<uint32_t>::iterator block_iter_;
+ std::vector<int>::iterator block_iter_;
+ std::shared_ptr<std::vector<int>> cow_op_index_vec_;
uint64_t start_;
};
CowMergeOpIter::CowMergeOpIter(std::shared_ptr<std::vector<CowOperation>> ops,
- std::shared_ptr<std::vector<uint32_t>> merge_op_blocks,
- std::shared_ptr<std::unordered_map<uint32_t, int>> map,
- uint64_t start) {
+ std::shared_ptr<std::vector<int>> block_pos_index, uint64_t start) {
ops_ = ops;
- merge_op_blocks_ = merge_op_blocks;
- map_ = map;
start_ = start;
-
- block_iter_ = merge_op_blocks->begin() + start;
+ cow_op_index_vec_ = block_pos_index;
+ block_iter_ = cow_op_index_vec_->begin() + start;
}
bool CowMergeOpIter::RDone() {
- return block_iter_ == merge_op_blocks_->begin();
+ return block_iter_ == cow_op_index_vec_->begin();
}
void CowMergeOpIter::Prev() {
@@ -651,7 +661,7 @@
}
bool CowMergeOpIter::Done() {
- return block_iter_ == merge_op_blocks_->end();
+ return block_iter_ == cow_op_index_vec_->end();
}
void CowMergeOpIter::Next() {
@@ -661,23 +671,20 @@
const CowOperation& CowMergeOpIter::Get() {
CHECK(!Done());
- return ops_->data()[map_->at(*block_iter_)];
+ return ops_->data()[*block_iter_];
}
CowRevMergeOpIter::CowRevMergeOpIter(std::shared_ptr<std::vector<CowOperation>> ops,
- std::shared_ptr<std::vector<uint32_t>> merge_op_blocks,
- std::shared_ptr<std::unordered_map<uint32_t, int>> map,
+ std::shared_ptr<std::vector<int>> block_pos_index,
uint64_t start) {
ops_ = ops;
- merge_op_blocks_ = merge_op_blocks;
- map_ = map;
start_ = start;
-
- block_riter_ = merge_op_blocks->rbegin();
+ cow_op_index_vec_ = block_pos_index;
+ block_riter_ = cow_op_index_vec_->rbegin();
}
bool CowRevMergeOpIter::RDone() {
- return block_riter_ == merge_op_blocks_->rbegin();
+ return block_riter_ == cow_op_index_vec_->rbegin();
}
void CowRevMergeOpIter::Prev() {
@@ -686,7 +693,7 @@
}
bool CowRevMergeOpIter::Done() {
- return block_riter_ == merge_op_blocks_->rend() - start_;
+ return block_riter_ == cow_op_index_vec_->rend() - start_;
}
void CowRevMergeOpIter::Next() {
@@ -696,20 +703,20 @@
const CowOperation& CowRevMergeOpIter::Get() {
CHECK(!Done());
- return ops_->data()[map_->at(*block_riter_)];
+ return ops_->data()[*block_riter_];
}
-std::unique_ptr<ICowOpIter> CowReader::GetOpIter() {
- return std::make_unique<CowOpIter>(ops_);
+std::unique_ptr<ICowOpIter> CowReader::GetOpIter(bool merge_progress) {
+ return std::make_unique<CowOpIter>(ops_, merge_progress ? merge_op_start_ : 0);
}
std::unique_ptr<ICowOpIter> CowReader::GetRevMergeOpIter(bool ignore_progress) {
- return std::make_unique<CowRevMergeOpIter>(ops_, merge_op_blocks_, block_map_,
+ return std::make_unique<CowRevMergeOpIter>(ops_, block_pos_index_,
ignore_progress ? 0 : merge_op_start_);
}
std::unique_ptr<ICowOpIter> CowReader::GetMergeOpIter(bool ignore_progress) {
- return std::make_unique<CowMergeOpIter>(ops_, merge_op_blocks_, block_map_,
+ return std::make_unique<CowMergeOpIter>(ops_, block_pos_index_,
ignore_progress ? 0 : merge_op_start_);
}
@@ -775,6 +782,9 @@
case kCowCompressBrotli:
decompressor = IDecompressor::Brotli();
break;
+ case kCowCompressLz4:
+ decompressor = IDecompressor::Lz4();
+ break;
default:
LOG(ERROR) << "Unknown compression type: " << op.compression;
return false;
diff --git a/fs_mgr/libsnapshot/cow_writer.cpp b/fs_mgr/libsnapshot/cow_writer.cpp
index 5ce1d3b..7281fc2 100644
--- a/fs_mgr/libsnapshot/cow_writer.cpp
+++ b/fs_mgr/libsnapshot/cow_writer.cpp
@@ -24,8 +24,10 @@
#include <android-base/logging.h>
#include <android-base/unique_fd.h>
#include <brotli/encode.h>
+#include <libsnapshot/cow_format.h>
#include <libsnapshot/cow_reader.h>
#include <libsnapshot/cow_writer.h>
+#include <lz4.h>
#include <zlib.h>
namespace android {
@@ -129,6 +131,8 @@
compression_ = kCowCompressGz;
} else if (options_.compression == "brotli") {
compression_ = kCowCompressBrotli;
+ } else if (options_.compression == "lz4") {
+ compression_ = kCowCompressLz4;
} else if (options_.compression == "none") {
compression_ = kCowCompressNone;
} else if (!options_.compression.empty()) {
@@ -403,35 +407,56 @@
std::basic_string<uint8_t> CowWriter::Compress(const void* data, size_t length) {
switch (compression_) {
case kCowCompressGz: {
- auto bound = compressBound(length);
- auto buffer = std::make_unique<uint8_t[]>(bound);
+ const auto bound = compressBound(length);
+ std::basic_string<uint8_t> buffer(bound, '\0');
uLongf dest_len = bound;
- auto rv = compress2(buffer.get(), &dest_len, reinterpret_cast<const Bytef*>(data),
+ auto rv = compress2(buffer.data(), &dest_len, reinterpret_cast<const Bytef*>(data),
length, Z_BEST_COMPRESSION);
if (rv != Z_OK) {
LOG(ERROR) << "compress2 returned: " << rv;
return {};
}
- return std::basic_string<uint8_t>(buffer.get(), dest_len);
+ buffer.resize(dest_len);
+ return buffer;
}
case kCowCompressBrotli: {
- auto bound = BrotliEncoderMaxCompressedSize(length);
+ const auto bound = BrotliEncoderMaxCompressedSize(length);
if (!bound) {
LOG(ERROR) << "BrotliEncoderMaxCompressedSize returned 0";
return {};
}
- auto buffer = std::make_unique<uint8_t[]>(bound);
+ std::basic_string<uint8_t> buffer(bound, '\0');
size_t encoded_size = bound;
auto rv = BrotliEncoderCompress(
BROTLI_DEFAULT_QUALITY, BROTLI_DEFAULT_WINDOW, BROTLI_DEFAULT_MODE, length,
- reinterpret_cast<const uint8_t*>(data), &encoded_size, buffer.get());
+ reinterpret_cast<const uint8_t*>(data), &encoded_size, buffer.data());
if (!rv) {
LOG(ERROR) << "BrotliEncoderCompress failed";
return {};
}
- return std::basic_string<uint8_t>(buffer.get(), encoded_size);
+ buffer.resize(encoded_size);
+ return buffer;
+ }
+ case kCowCompressLz4: {
+ const auto bound = LZ4_compressBound(length);
+ if (!bound) {
+ LOG(ERROR) << "LZ4_compressBound returned 0";
+ return {};
+ }
+ std::basic_string<uint8_t> buffer(bound, '\0');
+
+ const auto compressed_size = LZ4_compress_default(
+ static_cast<const char*>(data), reinterpret_cast<char*>(buffer.data()), length,
+ buffer.size());
+ if (compressed_size <= 0) {
+ LOG(ERROR) << "LZ4_compress_default failed, input size: " << length
+ << ", compression bound: " << bound << ", ret: " << compressed_size;
+ return {};
+ }
+ buffer.resize(compressed_size);
+ return buffer;
}
default:
LOG(ERROR) << "unhandled compression type: " << compression_;
diff --git a/fs_mgr/libsnapshot/device_info.cpp b/fs_mgr/libsnapshot/device_info.cpp
index a6d96ed..0ab6103 100644
--- a/fs_mgr/libsnapshot/device_info.cpp
+++ b/fs_mgr/libsnapshot/device_info.cpp
@@ -23,8 +23,9 @@
namespace snapshot {
#ifdef LIBSNAPSHOT_USE_HAL
-using android::hardware::boot::V1_0::BoolResult;
-using android::hardware::boot::V1_0::CommandResult;
+using android::hal::BootControlClient;
+using android::hal::BootControlVersion;
+using android::hal::CommandResult;
#endif
using namespace std::chrono_literals;
@@ -63,16 +64,16 @@
#ifdef LIBSNAPSHOT_USE_HAL
bool DeviceInfo::EnsureBootHal() {
if (!boot_control_) {
- auto hal = android::hardware::boot::V1_0::IBootControl::getService();
+ auto hal = BootControlClient::WaitForService();
if (!hal) {
LOG(ERROR) << "Could not find IBootControl HAL";
return false;
}
- boot_control_ = android::hardware::boot::V1_1::IBootControl::castFrom(hal);
- if (!boot_control_) {
+ if (hal->GetVersion() < BootControlVersion::BOOTCTL_V1_1) {
LOG(ERROR) << "Could not find IBootControl 1.1 HAL";
return false;
}
+ boot_control_ = std::move(hal);
}
return true;
}
@@ -83,8 +84,9 @@
if (!EnsureBootHal()) {
return false;
}
- if (!boot_control_->setSnapshotMergeStatus(status)) {
- LOG(ERROR) << "Unable to set the snapshot merge status";
+ const auto ret = boot_control_->SetSnapshotMergeStatus(status);
+ if (!ret.IsOk()) {
+ LOG(ERROR) << "Unable to set the snapshot merge status " << ret.errMsg;
return false;
}
return true;
@@ -108,9 +110,7 @@
return false;
}
- CommandResult result = {};
- auto cb = [&](CommandResult r) -> void { result = r; };
- boot_control_->setSlotAsUnbootable(slot, cb);
+ CommandResult result = boot_control_->MarkSlotUnbootable(slot);
if (!result.success) {
LOG(ERROR) << "Error setting slot " << slot << " unbootable: " << result.errMsg;
return false;
diff --git a/fs_mgr/libsnapshot/device_info.h b/fs_mgr/libsnapshot/device_info.h
index 8aefb85..d06f1be 100644
--- a/fs_mgr/libsnapshot/device_info.h
+++ b/fs_mgr/libsnapshot/device_info.h
@@ -17,7 +17,7 @@
#include <string>
#ifdef LIBSNAPSHOT_USE_HAL
-#include <android/hardware/boot/1.1/IBootControl.h>
+#include <BootControlClient.h>
#endif
#include <liblp/partition_opener.h>
#include <libsnapshot/snapshot.h>
@@ -26,7 +26,7 @@
namespace snapshot {
class DeviceInfo final : public SnapshotManager::IDeviceInfo {
- using MergeStatus = android::hardware::boot::V1_1::MergeStatus;
+ using MergeStatus = ::aidl::android::hardware::boot::MergeStatus;
public:
std::string GetMetadataDir() const override;
@@ -50,7 +50,7 @@
android::fs_mgr::PartitionOpener opener_;
bool first_stage_init_ = false;
#ifdef LIBSNAPSHOT_USE_HAL
- android::sp<android::hardware::boot::V1_1::IBootControl> boot_control_;
+ std::unique_ptr<::android::hal::BootControlClient> boot_control_;
#endif
};
diff --git a/fs_mgr/libsnapshot/include/libsnapshot/cow_format.h b/fs_mgr/libsnapshot/include/libsnapshot/cow_format.h
index 9f4ddbb..ba75a8d 100644
--- a/fs_mgr/libsnapshot/include/libsnapshot/cow_format.h
+++ b/fs_mgr/libsnapshot/include/libsnapshot/cow_format.h
@@ -154,9 +154,12 @@
static constexpr uint8_t kCowSequenceOp = 7;
static constexpr uint8_t kCowFooterOp = -1;
-static constexpr uint8_t kCowCompressNone = 0;
-static constexpr uint8_t kCowCompressGz = 1;
-static constexpr uint8_t kCowCompressBrotli = 2;
+enum CowCompressionAlgorithm : uint8_t {
+ kCowCompressNone = 0,
+ kCowCompressGz = 1,
+ kCowCompressBrotli = 2,
+ kCowCompressLz4 = 3
+};
static constexpr uint8_t kCowReadAheadNotStarted = 0;
static constexpr uint8_t kCowReadAheadInProgress = 1;
diff --git a/fs_mgr/libsnapshot/include/libsnapshot/cow_reader.h b/fs_mgr/libsnapshot/include/libsnapshot/cow_reader.h
index f4d5c72..e8e4d72 100644
--- a/fs_mgr/libsnapshot/include/libsnapshot/cow_reader.h
+++ b/fs_mgr/libsnapshot/include/libsnapshot/cow_reader.h
@@ -74,7 +74,7 @@
virtual bool GetLastLabel(uint64_t* label) = 0;
// Return an iterator for retrieving CowOperation entries.
- virtual std::unique_ptr<ICowOpIter> GetOpIter() = 0;
+ virtual std::unique_ptr<ICowOpIter> GetOpIter(bool merge_progress) = 0;
// Return an iterator for retrieving CowOperation entries in reverse merge order
virtual std::unique_ptr<ICowOpIter> GetRevMergeOpIter(bool ignore_progress) = 0;
@@ -115,7 +115,7 @@
USERSPACE_MERGE = 1,
};
- CowReader(ReaderFlags reader_flag = ReaderFlags::DEFAULT);
+ CowReader(ReaderFlags reader_flag = ReaderFlags::DEFAULT, bool is_merge = false);
~CowReader() { owned_fd_ = {}; }
// Parse the COW, optionally, up to the given label. If no label is
@@ -135,7 +135,7 @@
// CowOperation objects. Get() returns a unique CowOperation object
// whose lifetime depends on the CowOpIter object; the return
// value of these will never be null.
- std::unique_ptr<ICowOpIter> GetOpIter() override;
+ std::unique_ptr<ICowOpIter> GetOpIter(bool merge_progress = false) override;
std::unique_ptr<ICowOpIter> GetRevMergeOpIter(bool ignore_progress = false) override;
std::unique_ptr<ICowOpIter> GetMergeOpIter(bool ignore_progress = false) override;
@@ -170,14 +170,14 @@
uint64_t fd_size_;
std::optional<uint64_t> last_label_;
std::shared_ptr<std::vector<CowOperation>> ops_;
- std::shared_ptr<std::vector<uint32_t>> merge_op_blocks_;
uint64_t merge_op_start_{};
- std::shared_ptr<std::unordered_map<uint32_t, int>> block_map_;
+ std::shared_ptr<std::vector<int>> block_pos_index_;
uint64_t num_total_data_ops_{};
uint64_t num_ordered_ops_to_merge_{};
bool has_seq_ops_{};
std::shared_ptr<std::unordered_map<uint64_t, uint64_t>> data_loc_;
ReaderFlags reader_flag_;
+ bool is_merge_{};
};
} // namespace snapshot
diff --git a/fs_mgr/libsnapshot/include/libsnapshot/cow_writer.h b/fs_mgr/libsnapshot/include/libsnapshot/cow_writer.h
index e17b5c6..e7a2f02 100644
--- a/fs_mgr/libsnapshot/include/libsnapshot/cow_writer.h
+++ b/fs_mgr/libsnapshot/include/libsnapshot/cow_writer.h
@@ -155,7 +155,7 @@
android::base::borrowed_fd fd_;
CowHeader header_{};
CowFooter footer_{};
- int compression_ = 0;
+ CowCompressionAlgorithm compression_ = kCowCompressNone;
uint64_t next_op_pos_ = 0;
uint64_t next_data_pos_ = 0;
uint32_t cluster_size_ = 0;
diff --git a/fs_mgr/libsnapshot/include/libsnapshot/mock_snapshot.h b/fs_mgr/libsnapshot/include/libsnapshot/mock_snapshot.h
index ba62330..d458b87 100644
--- a/fs_mgr/libsnapshot/include/libsnapshot/mock_snapshot.h
+++ b/fs_mgr/libsnapshot/include/libsnapshot/mock_snapshot.h
@@ -62,6 +62,7 @@
MOCK_METHOD(std::unique_ptr<AutoDevice>, EnsureMetadataMounted, (), (override));
MOCK_METHOD(ISnapshotMergeStats*, GetSnapshotMergeStatsInstance, (), (override));
MOCK_METHOD(std::string, ReadSourceBuildFingerprint, (), (override));
+ MOCK_METHOD(void, SetMergeStatsFeatures, (ISnapshotMergeStats*), (override));
};
} // namespace android::snapshot
diff --git a/fs_mgr/libsnapshot/include/libsnapshot/mock_snapshot_merge_stats.h b/fs_mgr/libsnapshot/include/libsnapshot/mock_snapshot_merge_stats.h
index 3d384cc..8280f2e 100644
--- a/fs_mgr/libsnapshot/include/libsnapshot/mock_snapshot_merge_stats.h
+++ b/fs_mgr/libsnapshot/include/libsnapshot/mock_snapshot_merge_stats.h
@@ -28,10 +28,7 @@
virtual ~MockSnapshotMergeStats() = default;
// Called when merge starts or resumes.
MOCK_METHOD(bool, Start, (), (override));
- MOCK_METHOD(void, set_state, (android::snapshot::UpdateState, bool), (override));
- MOCK_METHOD(void, set_cow_file_size, (uint64_t), ());
- MOCK_METHOD(void, set_total_cow_size_bytes, (uint64_t), (override));
- MOCK_METHOD(void, set_estimated_cow_size_bytes, (uint64_t), (override));
+ MOCK_METHOD(void, set_state, (android::snapshot::UpdateState), (override));
MOCK_METHOD(void, set_boot_complete_time_ms, (uint32_t), (override));
MOCK_METHOD(void, set_boot_complete_to_merge_start_time_ms, (uint32_t), (override));
MOCK_METHOD(void, set_merge_failure_code, (MergeFailureCode), (override));
@@ -45,6 +42,7 @@
MOCK_METHOD(MergeFailureCode, merge_failure_code, (), (override));
MOCK_METHOD(std::unique_ptr<Result>, Finish, (), (override));
MOCK_METHOD(bool, WriteState, (), (override));
+ MOCK_METHOD(SnapshotMergeReport*, report, (), (override));
using ISnapshotMergeStats::Result;
// Return nullptr if any failure.
diff --git a/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h b/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h
index 11da568..34c7baf 100644
--- a/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h
+++ b/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h
@@ -46,6 +46,10 @@
#define DEFINED_FRIEND_TEST
#endif
+namespace aidl::android::hardware::boot {
+enum class MergeStatus;
+}
+
namespace android {
namespace fiemap {
@@ -59,13 +63,6 @@
// Forward declare IBootControl types since we cannot include only the headers
// with Soong. Note: keep the enum width in sync.
-namespace hardware {
-namespace boot {
-namespace V1_1 {
-enum class MergeStatus : int32_t;
-} // namespace V1_1
-} // namespace boot
-} // namespace hardware
namespace snapshot {
@@ -95,6 +92,7 @@
class IDeviceInfo {
public:
using IImageManager = android::fiemap::IImageManager;
+ using MergeStatus = aidl::android::hardware::boot::MergeStatus;
virtual ~IDeviceInfo() {}
virtual std::string GetMetadataDir() const = 0;
@@ -103,8 +101,7 @@
virtual std::string GetSuperDevice(uint32_t slot) const = 0;
virtual const android::fs_mgr::IPartitionOpener& GetPartitionOpener() const = 0;
virtual bool IsOverlayfsSetup() const = 0;
- virtual bool SetBootControlMergeStatus(
- android::hardware::boot::V1_1::MergeStatus status) = 0;
+ virtual bool SetBootControlMergeStatus(MergeStatus status) = 0;
virtual bool SetSlotAsUnbootable(unsigned int slot) = 0;
virtual bool IsRecovery() const = 0;
virtual bool IsTestDevice() const { return false; }
@@ -134,6 +131,9 @@
// may need to be merged before wiping.
virtual bool FinishedSnapshotWrites(bool wipe) = 0;
+ // Set feature flags on an ISnapshotMergeStats object.
+ virtual void SetMergeStatsFeatures(ISnapshotMergeStats* stats) = 0;
+
// Update an ISnapshotMergeStats object with statistics about COW usage.
// This should be called before the merge begins as otherwise snapshots
// may be deleted.
@@ -308,7 +308,7 @@
using LpMetadata = android::fs_mgr::LpMetadata;
using MetadataBuilder = android::fs_mgr::MetadataBuilder;
using DeltaArchiveManifest = chromeos_update_engine::DeltaArchiveManifest;
- using MergeStatus = android::hardware::boot::V1_1::MergeStatus;
+ using MergeStatus = aidl::android::hardware::boot::MergeStatus;
using FiemapStatus = android::fiemap::FiemapStatus;
friend class SnapshotMergeStats;
@@ -378,6 +378,7 @@
bool MapAllSnapshots(const std::chrono::milliseconds& timeout_ms = {}) override;
bool UnmapAllSnapshots() override;
std::string ReadSourceBuildFingerprint() override;
+ void SetMergeStatsFeatures(ISnapshotMergeStats* stats) override;
// We can't use WaitForFile during first-stage init, because ueventd is not
// running and therefore will not automatically create symlinks. Instead,
@@ -640,6 +641,7 @@
MergeFailureCode CheckMergeConsistency(LockedFile* lock, const std::string& name,
const SnapshotStatus& update_status);
+ auto UpdateStateToStr(enum UpdateState state);
// Get status or table information about a device-mapper node with a single target.
enum class TableQuery {
Table,
diff --git a/fs_mgr/libsnapshot/include/libsnapshot/snapshot_stats.h b/fs_mgr/libsnapshot/include/libsnapshot/snapshot_stats.h
index 8c2fec7..8a70400 100644
--- a/fs_mgr/libsnapshot/include/libsnapshot/snapshot_stats.h
+++ b/fs_mgr/libsnapshot/include/libsnapshot/snapshot_stats.h
@@ -28,10 +28,7 @@
virtual ~ISnapshotMergeStats() = default;
// Called when merge starts or resumes.
virtual bool Start() = 0;
- virtual void set_state(android::snapshot::UpdateState state, bool using_compression) = 0;
- virtual void set_cow_file_size(uint64_t cow_file_size) = 0;
- virtual void set_total_cow_size_bytes(uint64_t bytes) = 0;
- virtual void set_estimated_cow_size_bytes(uint64_t bytes) = 0;
+ virtual void set_state(android::snapshot::UpdateState state) = 0;
virtual void set_boot_complete_time_ms(uint32_t ms) = 0;
virtual void set_boot_complete_to_merge_start_time_ms(uint32_t ms) = 0;
virtual void set_merge_failure_code(MergeFailureCode code) = 0;
@@ -55,6 +52,9 @@
// Return nullptr if any failure.
virtual std::unique_ptr<Result> Finish() = 0;
+ // Return the underlying implementation.
+ virtual SnapshotMergeReport* report() = 0;
+
// Write out the current state. This should be called when data might be lost that
// cannot be recovered (eg the COW sizes).
virtual bool WriteState() = 0;
@@ -67,11 +67,8 @@
// ISnapshotMergeStats overrides
bool Start() override;
- void set_state(android::snapshot::UpdateState state, bool using_compression) override;
- void set_cow_file_size(uint64_t cow_file_size) override;
+ void set_state(android::snapshot::UpdateState state) override;
uint64_t cow_file_size() override;
- void set_total_cow_size_bytes(uint64_t bytes) override;
- void set_estimated_cow_size_bytes(uint64_t bytes) override;
uint64_t total_cow_size_bytes() override;
uint64_t estimated_cow_size_bytes() override;
void set_boot_complete_time_ms(uint32_t ms) override;
@@ -85,6 +82,9 @@
std::unique_ptr<Result> Finish() override;
bool WriteState() override;
+ // Access the underlying report before it is finished.
+ SnapshotMergeReport* report() override { return &report_; }
+
private:
bool ReadState();
bool DeleteState();
diff --git a/fs_mgr/libsnapshot/include/libsnapshot/snapshot_stub.h b/fs_mgr/libsnapshot/include/libsnapshot/snapshot_stub.h
index 318e525..171c7c6 100644
--- a/fs_mgr/libsnapshot/include/libsnapshot/snapshot_stub.h
+++ b/fs_mgr/libsnapshot/include/libsnapshot/snapshot_stub.h
@@ -59,6 +59,7 @@
bool MapAllSnapshots(const std::chrono::milliseconds& timeout_ms) override;
bool UnmapAllSnapshots() override;
std::string ReadSourceBuildFingerprint() override;
+ void SetMergeStatsFeatures(ISnapshotMergeStats* stats) override;
};
} // namespace android::snapshot
diff --git a/fs_mgr/libsnapshot/include_test/libsnapshot/test_helpers.h b/fs_mgr/libsnapshot/include_test/libsnapshot/test_helpers.h
index c3b40dc..f850b94 100644
--- a/fs_mgr/libsnapshot/include_test/libsnapshot/test_helpers.h
+++ b/fs_mgr/libsnapshot/include_test/libsnapshot/test_helpers.h
@@ -17,10 +17,10 @@
#include <memory>
#include <optional>
#include <string>
+#include <unordered_map>
#include <unordered_set>
#include <android-base/file.h>
-#include <android/hardware/boot/1.1/IBootControl.h>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include <libfiemap/image_manager.h>
@@ -33,10 +33,10 @@
namespace android {
namespace snapshot {
+using aidl::android::hardware::boot::MergeStatus;
using android::fs_mgr::IPropertyFetcher;
using android::fs_mgr::MetadataBuilder;
using android::fs_mgr::testing::MockPropertyFetcher;
-using android::hardware::boot::V1_1::MergeStatus;
using chromeos_update_engine::DeltaArchiveManifest;
using chromeos_update_engine::PartitionUpdate;
using testing::_;
@@ -166,27 +166,25 @@
android::dm::IDeviceMapper& impl_;
};
-class SnapshotTestPropertyFetcher : public android::fs_mgr::testing::MockPropertyFetcher {
+class SnapshotTestPropertyFetcher : public android::fs_mgr::IPropertyFetcher {
public:
- SnapshotTestPropertyFetcher(const std::string& slot_suffix) {
- using testing::Return;
- ON_CALL(*this, GetProperty("ro.boot.slot_suffix", _)).WillByDefault(Return(slot_suffix));
- ON_CALL(*this, GetBoolProperty("ro.boot.dynamic_partitions", _))
- .WillByDefault(Return(true));
- ON_CALL(*this, GetBoolProperty("ro.boot.dynamic_partitions_retrofit", _))
- .WillByDefault(Return(false));
- ON_CALL(*this, GetBoolProperty("ro.virtual_ab.enabled", _)).WillByDefault(Return(true));
- }
+ explicit SnapshotTestPropertyFetcher(const std::string& slot_suffix,
+ std::unordered_map<std::string, std::string>&& props = {});
+
+ std::string GetProperty(const std::string& key, const std::string& defaultValue) override;
+ bool GetBoolProperty(const std::string& key, bool defaultValue) override;
static void SetUp(const std::string& slot_suffix = "_a") { Reset(slot_suffix); }
-
static void TearDown() { Reset("_a"); }
private:
static void Reset(const std::string& slot_suffix) {
IPropertyFetcher::OverrideForTesting(
- std::make_unique<NiceMock<SnapshotTestPropertyFetcher>>(slot_suffix));
+ std::make_unique<SnapshotTestPropertyFetcher>(slot_suffix));
}
+
+ private:
+ std::unordered_map<std::string, std::string> properties_;
};
// Helper for error-spam-free cleanup.
@@ -197,9 +195,10 @@
// Expect space of |path| is multiple of 4K.
bool WriteRandomData(const std::string& path, std::optional<size_t> expect_size = std::nullopt,
std::string* hash = nullptr);
-bool WriteRandomData(ICowWriter* writer, std::string* hash = nullptr);
std::string HashSnapshot(ISnapshotWriter* writer);
+std::string ToHexString(const uint8_t* buf, size_t len);
+
std::optional<std::string> GetHash(const std::string& path);
// Add partitions and groups described by |manifest|.
diff --git a/fs_mgr/libsnapshot/partition_cow_creator.cpp b/fs_mgr/libsnapshot/partition_cow_creator.cpp
index 5569da0..7057223 100644
--- a/fs_mgr/libsnapshot/partition_cow_creator.cpp
+++ b/fs_mgr/libsnapshot/partition_cow_creator.cpp
@@ -143,7 +143,7 @@
}
std::optional<uint64_t> PartitionCowCreator::GetCowSize() {
- if (compression_enabled) {
+ if (using_snapuserd) {
if (update == nullptr || !update->has_estimate_cow_size()) {
LOG(ERROR) << "Update manifest does not include a COW size";
return std::nullopt;
diff --git a/fs_mgr/libsnapshot/partition_cow_creator.h b/fs_mgr/libsnapshot/partition_cow_creator.h
index 34b39ca..949e6c5 100644
--- a/fs_mgr/libsnapshot/partition_cow_creator.h
+++ b/fs_mgr/libsnapshot/partition_cow_creator.h
@@ -56,8 +56,8 @@
// Extra extents that are going to be invalidated during the update
// process.
std::vector<ChromeOSExtent> extra_extents = {};
- // True if compression is enabled.
- bool compression_enabled = false;
+ // True if snapuserd COWs are enabled.
+ bool using_snapuserd = false;
std::string compression_algorithm;
struct Return {
diff --git a/fs_mgr/libsnapshot/partition_cow_creator_test.cpp b/fs_mgr/libsnapshot/partition_cow_creator_test.cpp
index de35c13..cf26a16 100644
--- a/fs_mgr/libsnapshot/partition_cow_creator_test.cpp
+++ b/fs_mgr/libsnapshot/partition_cow_creator_test.cpp
@@ -249,7 +249,7 @@
.target_partition = system_b,
.current_metadata = builder_a.get(),
.current_suffix = "_a",
- .compression_enabled = true,
+ .using_snapuserd = true,
.update = &update};
auto ret = creator.Run();
@@ -275,7 +275,7 @@
.target_partition = system_b,
.current_metadata = builder_a.get(),
.current_suffix = "_a",
- .compression_enabled = true,
+ .using_snapuserd = true,
.update = nullptr};
auto ret = creator.Run();
diff --git a/fs_mgr/libsnapshot/snapshot.cpp b/fs_mgr/libsnapshot/snapshot.cpp
index 019b64a..59abd6f 100644
--- a/fs_mgr/libsnapshot/snapshot.cpp
+++ b/fs_mgr/libsnapshot/snapshot.cpp
@@ -53,6 +53,7 @@
namespace android {
namespace snapshot {
+using aidl::android::hardware::boot::MergeStatus;
using android::base::unique_fd;
using android::dm::DeviceMapper;
using android::dm::DmDeviceState;
@@ -72,7 +73,6 @@
using android::fs_mgr::LpMetadata;
using android::fs_mgr::MetadataBuilder;
using android::fs_mgr::SlotNumberForSlotSuffix;
-using android::hardware::boot::V1_1::MergeStatus;
using chromeos_update_engine::DeltaArchiveManifest;
using chromeos_update_engine::Extent;
using chromeos_update_engine::FileDescriptor;
@@ -398,7 +398,7 @@
status->set_state(SnapshotState::CREATED);
status->set_sectors_allocated(0);
status->set_metadata_sectors(0);
- status->set_compression_enabled(cow_creator->compression_enabled);
+ status->set_using_snapuserd(cow_creator->using_snapuserd);
status->set_compression_algorithm(cow_creator->compression_algorithm);
if (!WriteSnapshotStatus(lock, *status)) {
@@ -788,7 +788,7 @@
}
}
- bool compression_enabled = false;
+ bool using_snapuserd = false;
std::vector<std::string> first_merge_group;
@@ -809,7 +809,7 @@
return false;
}
- compression_enabled |= snapshot_status.compression_enabled();
+ using_snapuserd |= snapshot_status.using_snapuserd();
if (DecideMergePhase(snapshot_status) == MergePhase::FIRST_PHASE) {
first_merge_group.emplace_back(snapshot);
}
@@ -817,7 +817,7 @@
SnapshotUpdateStatus initial_status = ReadSnapshotUpdateStatus(lock.get());
initial_status.set_state(UpdateState::Merging);
- initial_status.set_compression_enabled(compression_enabled);
+ initial_status.set_using_snapuserd(using_snapuserd);
if (!UpdateUsesUserSnapshots(lock.get())) {
initial_status.set_sectors_allocated(initial_target_values.sectors_allocated);
@@ -988,6 +988,29 @@
return true;
}
+auto SnapshotManager::UpdateStateToStr(const enum UpdateState state) {
+ switch (state) {
+ case None:
+ return "None";
+ case Initiated:
+ return "Initiated";
+ case Unverified:
+ return "Unverified";
+ case Merging:
+ return "Merging";
+ case MergeNeedsReboot:
+ return "MergeNeedsReboot";
+ case MergeCompleted:
+ return "MergeCompleted";
+ case MergeFailed:
+ return "MergeFailed";
+ case Cancelled:
+ return "Cancelled";
+ default:
+ return "Unknown";
+ }
+}
+
bool SnapshotManager::QuerySnapshotStatus(const std::string& dm_name, std::string* target_type,
DmTargetSnapshot::Status* status) {
DeviceMapper::TargetInfo target;
@@ -1016,7 +1039,7 @@
const std::function<bool()>& before_cancel) {
while (true) {
auto result = CheckMergeState(before_cancel);
- LOG(INFO) << "ProcessUpdateState handling state: " << result.state;
+ LOG(INFO) << "ProcessUpdateState handling state: " << UpdateStateToStr(result.state);
if (result.state == UpdateState::MergeFailed) {
AcknowledgeMergeFailure(result.failure_code);
@@ -1044,7 +1067,7 @@
}
auto result = CheckMergeState(lock.get(), before_cancel);
- LOG(INFO) << "CheckMergeState for snapshots returned: " << result.state;
+ LOG(INFO) << "CheckMergeState for snapshots returned: " << UpdateStateToStr(result.state);
if (result.state == UpdateState::MergeCompleted) {
// Do this inside the same lock. Failures get acknowledged without the
@@ -1109,7 +1132,8 @@
}
auto result = CheckTargetMergeState(lock, snapshot, update_status);
- LOG(INFO) << "CheckTargetMergeState for " << snapshot << " returned: " << result.state;
+ LOG(INFO) << "CheckTargetMergeState for " << snapshot
+ << " returned: " << UpdateStateToStr(result.state);
switch (result.state) {
case UpdateState::MergeFailed:
@@ -1340,7 +1364,7 @@
}
MergeFailureCode CheckMergeConsistency(const std::string& name, const SnapshotStatus& status) {
- if (!status.compression_enabled()) {
+ if (!status.using_snapuserd()) {
// Do not try to verify old-style COWs yet.
return MergeFailureCode::Ok;
}
@@ -1474,7 +1498,6 @@
if (UpdateUsesUserSnapshots(lock) && !device()->IsTestDevice()) {
if (snapuserd_client_) {
snapuserd_client_->DetachSnapuserd();
- snapuserd_client_->CloseConnection();
snapuserd_client_ = nullptr;
}
}
@@ -1601,7 +1624,7 @@
// as unmap will fail since dm-user itself was a snapshot device prior
// to switching of tables. Unmap will fail as the device will be mounted
// by system partitions
- if (status.compression_enabled()) {
+ if (status.using_snapuserd()) {
auto dm_user_name = GetDmUserCowName(name, GetSnapshotDriver(lock));
UnmapDmUserDevice(dm_user_name);
}
@@ -2091,8 +2114,10 @@
}
bool SnapshotManager::UpdateUsesCompression(LockedFile* lock) {
+ // This returns true even if compression is "none", since update_engine is
+ // really just trying to see if snapuserd is in use.
SnapshotUpdateStatus update_status = ReadSnapshotUpdateStatus(lock);
- return update_status.compression_enabled();
+ return update_status.using_snapuserd();
}
bool SnapshotManager::UpdateUsesIouring(LockedFile* lock) {
@@ -2151,8 +2176,17 @@
if (!suffix.empty() && !android::base::EndsWith(name, suffix)) {
continue;
}
- snapshots->emplace_back(std::move(name));
+
+ // Insert system and product partition at the beginning so that
+ // during snapshot-merge, these partitions are merged first.
+ if (name == "system_a" || name == "system_b" || name == "product_a" ||
+ name == "product_b") {
+ snapshots->insert(snapshots->begin(), std::move(name));
+ } else {
+ snapshots->emplace_back(std::move(name));
+ }
}
+
return true;
}
@@ -2241,8 +2275,8 @@
.block_device = super_device,
.metadata = metadata.get(),
.partition = &partition,
- .partition_opener = &opener,
.timeout_ms = timeout_ms,
+ .partition_opener = &opener,
};
if (!MapPartitionWithSnapshot(lock, std::move(params), SnapshotContext::Mount, nullptr)) {
return false;
@@ -2403,13 +2437,13 @@
remaining_time = GetRemainingTime(params.timeout_ms, begin);
if (remaining_time.count() < 0) return false;
- if (context == SnapshotContext::Update && live_snapshot_status->compression_enabled()) {
+ if (context == SnapshotContext::Update && live_snapshot_status->using_snapuserd()) {
// Stop here, we can't run dm-user yet, the COW isn't built.
created_devices.Release();
return true;
}
- if (live_snapshot_status->compression_enabled()) {
+ if (live_snapshot_status->using_snapuserd()) {
// Get the source device (eg the view of the partition from before it was resized).
std::string source_device_path;
if (live_snapshot_status->old_partition_size() > 0) {
@@ -2719,8 +2753,8 @@
.block_device = super_device,
.metadata = metadata.get(),
.partition_name = snapshot,
- .partition_opener = &opener,
.timeout_ms = timeout_ms,
+ .partition_opener = &opener,
};
if (!MapPartitionWithSnapshot(lock.get(), std::move(params), SnapshotContext::Mount,
nullptr)) {
@@ -2759,7 +2793,6 @@
if (snapuserd_client_) {
LOG(INFO) << "Shutdown snapuserd daemon";
snapuserd_client_->DetachSnapuserd();
- snapuserd_client_->CloseConnection();
snapuserd_client_ = nullptr;
}
@@ -2911,7 +2944,7 @@
// build fingerprint.
if (!(state == UpdateState::Initiated || state == UpdateState::None)) {
SnapshotUpdateStatus old_status = ReadSnapshotUpdateStatus(lock);
- status.set_compression_enabled(old_status.compression_enabled());
+ status.set_using_snapuserd(old_status.using_snapuserd());
status.set_source_build_fingerprint(old_status.source_build_fingerprint());
status.set_merge_phase(old_status.merge_phase());
status.set_userspace_snapshots(old_status.userspace_snapshots());
@@ -3165,18 +3198,42 @@
LOG(INFO) << " dap_metadata.cow_version(): " << dap_metadata.cow_version()
<< " writer.GetCowVersion(): " << writer.GetCowVersion();
- bool use_compression = IsCompressionEnabled() && dap_metadata.vabc_enabled() &&
- !device_->IsRecovery() && cow_format_support;
+ // Deduce supported features.
+ bool userspace_snapshots = CanUseUserspaceSnapshots();
+ bool legacy_compression = GetLegacyCompressionEnabledProperty();
+
+ std::string vabc_disable_reason;
+ if (!dap_metadata.vabc_enabled()) {
+ vabc_disable_reason = "not enabled metadata";
+ } else if (device_->IsRecovery()) {
+ vabc_disable_reason = "recovery";
+ } else if (!cow_format_support) {
+ vabc_disable_reason = "cow format not supported";
+ }
+
+ if (!vabc_disable_reason.empty()) {
+ if (userspace_snapshots) {
+ LOG(INFO) << "Userspace snapshots disabled: " << vabc_disable_reason;
+ }
+ if (legacy_compression) {
+ LOG(INFO) << "Compression disabled: " << vabc_disable_reason;
+ }
+ userspace_snapshots = false;
+ legacy_compression = false;
+ }
+
+ const bool using_snapuserd = userspace_snapshots || legacy_compression;
+ if (!using_snapuserd) {
+ LOG(INFO) << "Using legacy Virtual A/B (dm-snapshot)";
+ }
std::string compression_algorithm;
- if (use_compression) {
+ if (using_snapuserd) {
compression_algorithm = dap_metadata.vabc_compression_param();
if (compression_algorithm.empty()) {
// Older OTAs don't set an explicit compression type, so default to gz.
compression_algorithm = "gz";
}
- } else {
- compression_algorithm = "none";
}
PartitionCowCreator cow_creator{
@@ -3187,7 +3244,7 @@
.current_suffix = current_suffix,
.update = nullptr,
.extra_extents = {},
- .compression_enabled = use_compression,
+ .using_snapuserd = using_snapuserd,
.compression_algorithm = compression_algorithm,
};
@@ -3212,11 +3269,11 @@
return Return::Error();
}
- // If compression is enabled, we need to retain a copy of the old metadata
+ // If snapuserd is enabled, we need to retain a copy of the old metadata
// so we can access original blocks in case they are moved around. We do
// not want to rely on the old super metadata slot because we don't
// guarantee its validity after the slot switch is successful.
- if (cow_creator.compression_enabled) {
+ if (using_snapuserd) {
auto metadata = current_metadata->Export();
if (!metadata) {
LOG(ERROR) << "Could not export current metadata";
@@ -3232,70 +3289,36 @@
SnapshotUpdateStatus status = ReadSnapshotUpdateStatus(lock.get());
status.set_state(update_state);
- status.set_compression_enabled(cow_creator.compression_enabled);
- if (cow_creator.compression_enabled) {
- if (!device()->IsTestDevice()) {
- bool userSnapshotsEnabled = IsUserspaceSnapshotsEnabled();
- const std::string UNKNOWN = "unknown";
- const std::string vendor_release = android::base::GetProperty(
- "ro.vendor.build.version.release_or_codename", UNKNOWN);
+ status.set_using_snapuserd(using_snapuserd);
- // No user-space snapshots if vendor partition is on Android 12
- if (vendor_release.find("12") != std::string::npos) {
- LOG(INFO) << "Userspace snapshots disabled as vendor partition is on Android: "
- << vendor_release;
- userSnapshotsEnabled = false;
- }
+ if (userspace_snapshots) {
+ status.set_userspace_snapshots(true);
+ LOG(INFO) << "Virtual A/B using userspace snapshots";
- // Userspace snapshots is enabled only if compression is enabled
- status.set_userspace_snapshots(userSnapshotsEnabled);
- if (userSnapshotsEnabled) {
- is_snapshot_userspace_ = true;
- status.set_io_uring_enabled(IsIouringEnabled());
- LOG(INFO) << "Userspace snapshots enabled";
- } else {
- is_snapshot_userspace_ = false;
- LOG(INFO) << "Userspace snapshots disabled";
- }
+ if (GetIouringEnabledProperty()) {
+ status.set_io_uring_enabled(true);
+ LOG(INFO) << "io_uring for snapshots enabled";
+ }
+ } else if (legacy_compression) {
+ LOG(INFO) << "Virtual A/B using legacy snapuserd";
+ } else {
+ LOG(INFO) << "Virtual A/B using dm-snapshot";
+ }
- // Terminate stale daemon if any
- std::unique_ptr<SnapuserdClient> snapuserd_client =
- SnapuserdClient::Connect(kSnapuserdSocket, 5s);
- if (snapuserd_client) {
- snapuserd_client->DetachSnapuserd();
- snapuserd_client->CloseConnection();
- snapuserd_client = nullptr;
- }
+ is_snapshot_userspace_.emplace(userspace_snapshots);
- // Clear the cached client if any
- if (snapuserd_client_) {
- snapuserd_client_->CloseConnection();
- snapuserd_client_ = nullptr;
- }
- } else {
- bool userSnapshotsEnabled = true;
- const std::string UNKNOWN = "unknown";
- const std::string vendor_release = android::base::GetProperty(
- "ro.vendor.build.version.release_or_codename", UNKNOWN);
-
- // No user-space snapshots if vendor partition is on Android 12
- if (vendor_release.find("12") != std::string::npos) {
- LOG(INFO) << "Userspace snapshots disabled as vendor partition is on Android: "
- << vendor_release;
- userSnapshotsEnabled = false;
- }
-
- userSnapshotsEnabled = (userSnapshotsEnabled && !IsDmSnapshotTestingEnabled());
- status.set_userspace_snapshots(userSnapshotsEnabled);
- if (!userSnapshotsEnabled) {
- is_snapshot_userspace_ = false;
- LOG(INFO) << "User-space snapshots disabled for testing";
- } else {
- is_snapshot_userspace_ = true;
- LOG(INFO) << "User-space snapshots enabled for testing";
- }
+ if (!device()->IsTestDevice() && using_snapuserd) {
+ // Terminate stale daemon if any
+ std::unique_ptr<SnapuserdClient> snapuserd_client = std::move(snapuserd_client_);
+ if (!snapuserd_client) {
+ snapuserd_client = SnapuserdClient::Connect(kSnapuserdSocket, 5s);
+ }
+ if (snapuserd_client) {
+ snapuserd_client->DetachSnapuserd();
+ snapuserd_client = nullptr;
}
}
+
if (!WriteSnapshotUpdateStatus(lock.get(), status)) {
LOG(ERROR) << "Unable to write new update state";
return Return::Error();
@@ -3488,7 +3511,7 @@
return Return::Error();
}
- if (it->second.compression_enabled()) {
+ if (it->second.using_snapuserd()) {
unique_fd fd(open(cow_path.c_str(), O_RDWR | O_CLOEXEC));
if (fd < 0) {
PLOG(ERROR) << "open " << cow_path << " failed for snapshot "
@@ -3534,8 +3557,8 @@
if (!ReadSnapshotStatus(lock.get(), params.GetPartitionName(), &status)) {
return false;
}
- if (status.compression_enabled()) {
- LOG(ERROR) << "Cannot use MapUpdateSnapshot with compressed snapshots";
+ if (status.using_snapuserd()) {
+ LOG(ERROR) << "Cannot use MapUpdateSnapshot with snapuserd";
return false;
}
@@ -3592,7 +3615,7 @@
return nullptr;
}
- if (status.compression_enabled()) {
+ if (status.using_snapuserd()) {
return OpenCompressedSnapshotWriter(lock.get(), source_device, params.GetPartitionName(),
status, paths);
}
@@ -3722,7 +3745,10 @@
auto update_status = ReadSnapshotUpdateStatus(file.get());
ss << "Update state: " << ReadUpdateState(file.get()) << std::endl;
- ss << "Compression: " << update_status.compression_enabled() << std::endl;
+ ss << "Using snapuserd: " << update_status.using_snapuserd() << std::endl;
+ ss << "Using userspace snapshots: " << update_status.userspace_snapshots() << std::endl;
+ ss << "Using io_uring: " << update_status.io_uring_enabled() << std::endl;
+ ss << "Using XOR compression: " << GetXorCompressionEnabledProperty() << std::endl;
ss << "Current slot: " << device_->GetSlotSuffix() << std::endl;
ss << "Boot indicator: booting from " << GetCurrentSlot() << " slot" << std::endl;
ss << "Rollback indicator: "
@@ -3943,7 +3969,7 @@
if (!ReadSnapshotStatus(lock, snapshot, &status)) {
return false;
}
- if (status.compression_enabled()) {
+ if (status.using_snapuserd()) {
continue;
}
@@ -4107,7 +4133,7 @@
if (!lock) return false;
auto status = ReadSnapshotUpdateStatus(lock.get());
- return status.state() != UpdateState::None && status.compression_enabled();
+ return status.state() != UpdateState::None && status.using_snapuserd();
}
bool SnapshotManager::DetachSnapuserdForSelinux(std::vector<std::string>* snapuserd_argv) {
@@ -4133,7 +4159,7 @@
}
MergePhase SnapshotManager::DecideMergePhase(const SnapshotStatus& status) {
- if (status.compression_enabled() && status.device_size() < status.old_partition_size()) {
+ if (status.using_snapuserd() && status.device_size() < status.old_partition_size()) {
return MergePhase::FIRST_PHASE;
}
return MergePhase::SECOND_PHASE;
@@ -4163,9 +4189,19 @@
estimated_cow_size += status.estimated_cow_size();
}
- stats->set_cow_file_size(cow_file_size);
- stats->set_total_cow_size_bytes(total_cow_size);
- stats->set_estimated_cow_size_bytes(estimated_cow_size);
+ stats->report()->set_cow_file_size(cow_file_size);
+ stats->report()->set_total_cow_size_bytes(total_cow_size);
+ stats->report()->set_estimated_cow_size_bytes(estimated_cow_size);
+}
+
+void SnapshotManager::SetMergeStatsFeatures(ISnapshotMergeStats* stats) {
+ auto lock = LockExclusive();
+ if (!lock) return;
+
+ SnapshotUpdateStatus update_status = ReadSnapshotUpdateStatus(lock.get());
+ stats->report()->set_iouring_used(update_status.io_uring_enabled());
+ stats->report()->set_userspace_snapshots_used(update_status.userspace_snapshots());
+ stats->report()->set_xor_compression_used(GetXorCompressionEnabledProperty());
}
bool SnapshotManager::DeleteDeviceIfExists(const std::string& name,
diff --git a/fs_mgr/libsnapshot/snapshot_fuzz_utils.h b/fs_mgr/libsnapshot/snapshot_fuzz_utils.h
index a648384..eb8246a 100644
--- a/fs_mgr/libsnapshot/snapshot_fuzz_utils.h
+++ b/fs_mgr/libsnapshot/snapshot_fuzz_utils.h
@@ -29,6 +29,7 @@
// by SnapshotManager.
#include "android/snapshot/snapshot_fuzz.pb.h"
+#include "libsnapshot/snapshot.h"
namespace android::snapshot {
@@ -94,6 +95,7 @@
class SnapshotFuzzDeviceInfo : public ISnapshotManager::IDeviceInfo {
public:
+ using MergeStatus = ISnapshotManager::IDeviceInfo::MergeStatus;
// Client is responsible for maintaining the lifetime of |data|.
SnapshotFuzzDeviceInfo(SnapshotFuzzEnv* env, const FuzzDeviceInfoData& data,
std::unique_ptr<TestPartitionOpener>&& partition_opener,
@@ -118,7 +120,7 @@
std::string GetSlotSuffix() const override { return CurrentSlotIsA() ? "_a" : "_b"; }
std::string GetOtherSlotSuffix() const override { return CurrentSlotIsA() ? "_b" : "_a"; }
bool IsOverlayfsSetup() const override { return data_->is_overlayfs_setup(); }
- bool SetBootControlMergeStatus(android::hardware::boot::V1_1::MergeStatus) override {
+ bool SetBootControlMergeStatus(MergeStatus) override {
return data_->allow_set_boot_control_merge_status();
}
bool SetSlotAsUnbootable(unsigned int) override {
diff --git a/fs_mgr/libsnapshot/snapshot_stats.cpp b/fs_mgr/libsnapshot/snapshot_stats.cpp
index 712eafb..9b6eb2c 100644
--- a/fs_mgr/libsnapshot/snapshot_stats.cpp
+++ b/fs_mgr/libsnapshot/snapshot_stats.cpp
@@ -84,27 +84,14 @@
return WriteState();
}
-void SnapshotMergeStats::set_state(android::snapshot::UpdateState state, bool using_compression) {
+void SnapshotMergeStats::set_state(android::snapshot::UpdateState state) {
report_.set_state(state);
- report_.set_compression_enabled(using_compression);
-}
-
-void SnapshotMergeStats::set_cow_file_size(uint64_t cow_file_size) {
- report_.set_cow_file_size(cow_file_size);
}
uint64_t SnapshotMergeStats::cow_file_size() {
return report_.cow_file_size();
}
-void SnapshotMergeStats::set_total_cow_size_bytes(uint64_t bytes) {
- report_.set_total_cow_size_bytes(bytes);
-}
-
-void SnapshotMergeStats::set_estimated_cow_size_bytes(uint64_t bytes) {
- report_.set_estimated_cow_size_bytes(bytes);
-}
-
uint64_t SnapshotMergeStats::total_cow_size_bytes() {
return report_.total_cow_size_bytes();
}
diff --git a/fs_mgr/libsnapshot/snapshot_stub.cpp b/fs_mgr/libsnapshot/snapshot_stub.cpp
index 4af5367..84e2226 100644
--- a/fs_mgr/libsnapshot/snapshot_stub.cpp
+++ b/fs_mgr/libsnapshot/snapshot_stub.cpp
@@ -128,12 +128,9 @@
class SnapshotMergeStatsStub : public ISnapshotMergeStats {
bool Start() override { return false; }
- void set_state(android::snapshot::UpdateState, bool) override {}
- void set_cow_file_size(uint64_t) override {}
+ void set_state(android::snapshot::UpdateState) override {}
uint64_t cow_file_size() override { return 0; }
std::unique_ptr<Result> Finish() override { return nullptr; }
- void set_total_cow_size_bytes(uint64_t) override {}
- void set_estimated_cow_size_bytes(uint64_t) override {}
uint64_t total_cow_size_bytes() override { return 0; }
uint64_t estimated_cow_size_bytes() override { return 0; }
void set_boot_complete_time_ms(uint32_t) override {}
@@ -145,6 +142,10 @@
void set_source_build_fingerprint(const std::string&) override {}
std::string source_build_fingerprint() override { return {}; }
bool WriteState() override { return false; }
+ SnapshotMergeReport* report() override { return &report_; }
+
+ private:
+ SnapshotMergeReport report_;
};
ISnapshotMergeStats* SnapshotManagerStub::GetSnapshotMergeStatsInstance() {
@@ -183,4 +184,8 @@
return {};
}
+void SnapshotManagerStub::SetMergeStatsFeatures(ISnapshotMergeStats*) {
+ LOG(ERROR) << __FUNCTION__ << " should never be called.";
+}
+
} // namespace android::snapshot
diff --git a/fs_mgr/libsnapshot/snapshot_test.cpp b/fs_mgr/libsnapshot/snapshot_test.cpp
index e7ffb16..2c01cf6 100644
--- a/fs_mgr/libsnapshot/snapshot_test.cpp
+++ b/fs_mgr/libsnapshot/snapshot_test.cpp
@@ -26,6 +26,7 @@
#include <future>
#include <iostream>
+#include <aidl/android/hardware/boot/MergeStatus.h>
#include <android-base/file.h>
#include <android-base/logging.h>
#include <android-base/properties.h>
@@ -39,6 +40,7 @@
#include <libdm/dm.h>
#include <libfiemap/image_manager.h>
#include <liblp/builder.h>
+#include <openssl/sha.h>
#include <storage_literals/storage_literals.h>
#include <android/snapshot/snapshot.pb.h>
@@ -51,9 +53,11 @@
#include <libsnapshot/mock_device_info.h>
#include <libsnapshot/mock_snapshot.h>
-DEFINE_string(force_config, "", "Force testing mode (dmsnap, vab, vabc) ignoring device config.");
+DEFINE_string(force_mode, "",
+ "Force testing older modes (vab-legacy, vabc-legacy) ignoring device config.");
DEFINE_string(force_iouring_disable, "",
"Force testing mode (iouring_disabled) - disable io_uring");
+DEFINE_string(compression_method, "gz", "Default compression algorithm.");
namespace android {
namespace snapshot {
@@ -90,8 +94,6 @@
std::string fake_super;
void MountMetadata();
-bool ShouldUseCompression();
-bool IsDaemonRequired();
class SnapshotTest : public ::testing::Test {
public:
@@ -105,9 +107,15 @@
protected:
void SetUp() override {
+ const testing::TestInfo* const test_info =
+ testing::UnitTest::GetInstance()->current_test_info();
+ test_name_ = test_info->test_suite_name() + "/"s + test_info->name();
+
+ LOG(INFO) << "Starting test: " << test_name_;
+
SKIP_IF_NON_VIRTUAL_AB();
- SnapshotTestPropertyFetcher::SetUp();
+ SetupProperties();
InitializeState();
CleanupTestArtifacts();
FormatFakeSuper();
@@ -115,13 +123,49 @@
ASSERT_TRUE(sm->BeginUpdate());
}
+ void SetupProperties() {
+ std::unordered_map<std::string, std::string> properties;
+
+ ASSERT_TRUE(android::base::SetProperty("snapuserd.test.dm.snapshots", "0"))
+ << "Failed to disable property: virtual_ab.userspace.snapshots.enabled";
+ ASSERT_TRUE(android::base::SetProperty("snapuserd.test.io_uring.force_disable", "0"))
+ << "Failed to set property: snapuserd.test.io_uring.disabled";
+
+ if (FLAGS_force_mode == "vabc-legacy") {
+ ASSERT_TRUE(android::base::SetProperty("snapuserd.test.dm.snapshots", "1"))
+ << "Failed to disable property: virtual_ab.userspace.snapshots.enabled";
+ properties["ro.virtual_ab.compression.enabled"] = "true";
+ properties["ro.virtual_ab.userspace.snapshots.enabled"] = "false";
+ } else if (FLAGS_force_mode == "vab-legacy") {
+ properties["ro.virtual_ab.compression.enabled"] = "false";
+ properties["ro.virtual_ab.userspace.snapshots.enabled"] = "false";
+ }
+
+ if (FLAGS_force_iouring_disable == "iouring_disabled") {
+ ASSERT_TRUE(android::base::SetProperty("snapuserd.test.io_uring.force_disable", "1"))
+ << "Failed to set property: snapuserd.test.io_uring.disabled";
+ properties["ro.virtual_ab.io_uring.enabled"] = "false";
+ }
+
+ auto fetcher = std::make_unique<SnapshotTestPropertyFetcher>("_a", std::move(properties));
+ IPropertyFetcher::OverrideForTesting(std::move(fetcher));
+
+ if (GetLegacyCompressionEnabledProperty() || CanUseUserspaceSnapshots()) {
+ snapuserd_required_ = true;
+ }
+ }
+
void TearDown() override {
RETURN_IF_NON_VIRTUAL_AB();
+ LOG(INFO) << "Tearing down SnapshotTest test: " << test_name_;
+
lock_ = nullptr;
CleanupTestArtifacts();
SnapshotTestPropertyFetcher::TearDown();
+
+ LOG(INFO) << "Teardown complete for test: " << test_name_;
}
void InitializeState() {
@@ -315,7 +359,7 @@
}
AssertionResult DeleteDevice(const std::string& device) {
- if (!dm_.DeleteDeviceIfExists(device)) {
+ if (!sm->DeleteDeviceIfExists(device, 1s)) {
return AssertionFailure() << "Can't delete " << device;
}
return AssertionSuccess();
@@ -357,8 +401,11 @@
DeltaArchiveManifest manifest;
auto dynamic_partition_metadata = manifest.mutable_dynamic_partition_metadata();
- dynamic_partition_metadata->set_vabc_enabled(IsCompressionEnabled());
+ dynamic_partition_metadata->set_vabc_enabled(snapuserd_required_);
dynamic_partition_metadata->set_cow_version(android::snapshot::kCowVersionMajor);
+ if (snapuserd_required_) {
+ dynamic_partition_metadata->set_vabc_compression_param(FLAGS_compression_method);
+ }
auto group = dynamic_partition_metadata->add_groups();
group->set_name("group");
@@ -396,7 +443,7 @@
if (!res) {
return res;
}
- } else if (!IsCompressionEnabled()) {
+ } else if (!snapuserd_required_) {
std::string ignore;
if (!MapUpdateSnapshot("test_partition_b", &ignore)) {
return AssertionFailure() << "Failed to map test_partition_b";
@@ -449,15 +496,17 @@
std::unique_ptr<SnapshotManager::LockedFile> lock_;
android::fiemap::IImageManager* image_manager_ = nullptr;
std::string fake_super_;
+ bool snapuserd_required_ = false;
+ std::string test_name_;
};
TEST_F(SnapshotTest, CreateSnapshot) {
ASSERT_TRUE(AcquireLock());
PartitionCowCreator cow_creator;
- cow_creator.compression_enabled = ShouldUseCompression();
- if (cow_creator.compression_enabled) {
- cow_creator.compression_algorithm = "gz";
+ cow_creator.using_snapuserd = snapuserd_required_;
+ if (cow_creator.using_snapuserd) {
+ cow_creator.compression_algorithm = FLAGS_compression_method;
} else {
cow_creator.compression_algorithm = "none";
}
@@ -483,7 +532,7 @@
ASSERT_EQ(status.state(), SnapshotState::CREATED);
ASSERT_EQ(status.device_size(), kDeviceSize);
ASSERT_EQ(status.snapshot_size(), kDeviceSize);
- ASSERT_EQ(status.compression_enabled(), cow_creator.compression_enabled);
+ ASSERT_EQ(status.using_snapuserd(), cow_creator.using_snapuserd);
ASSERT_EQ(status.compression_algorithm(), cow_creator.compression_algorithm);
}
@@ -496,7 +545,7 @@
ASSERT_TRUE(AcquireLock());
PartitionCowCreator cow_creator;
- cow_creator.compression_enabled = ShouldUseCompression();
+ cow_creator.using_snapuserd = snapuserd_required_;
static const uint64_t kDeviceSize = 1024 * 1024;
SnapshotStatus status;
@@ -623,10 +672,10 @@
SnapshotStatus status;
ASSERT_TRUE(init->ReadSnapshotStatus(lock_.get(), "test_partition_b", &status));
ASSERT_EQ(status.state(), SnapshotState::CREATED);
- if (ShouldUseCompression()) {
- ASSERT_EQ(status.compression_algorithm(), "gz");
+ if (snapuserd_required_) {
+ ASSERT_EQ(status.compression_algorithm(), FLAGS_compression_method);
} else {
- ASSERT_EQ(status.compression_algorithm(), "none");
+ ASSERT_EQ(status.compression_algorithm(), "");
}
DeviceMapper::TargetInfo target;
@@ -897,8 +946,11 @@
opener_ = std::make_unique<TestPartitionOpener>(fake_super);
auto dynamic_partition_metadata = manifest_.mutable_dynamic_partition_metadata();
- dynamic_partition_metadata->set_vabc_enabled(ShouldUseCompression());
+ dynamic_partition_metadata->set_vabc_enabled(snapuserd_required_);
dynamic_partition_metadata->set_cow_version(android::snapshot::kCowVersionMajor);
+ if (snapuserd_required_) {
+ dynamic_partition_metadata->set_vabc_compression_param(FLAGS_compression_method);
+ }
// Create a fake update package metadata.
// Not using full name "system", "vendor", "product" because these names collide with the
@@ -962,6 +1014,8 @@
void TearDown() override {
RETURN_IF_NON_VIRTUAL_AB();
+ LOG(INFO) << "Tearing down SnapshotUpdateTest test: " << test_name_;
+
Cleanup();
SnapshotTest::TearDown();
}
@@ -1030,7 +1084,7 @@
}
AssertionResult MapOneUpdateSnapshot(const std::string& name) {
- if (ShouldUseCompression()) {
+ if (snapuserd_required_) {
std::unique_ptr<ISnapshotWriter> writer;
return MapUpdateSnapshot(name, &writer);
} else {
@@ -1039,14 +1093,25 @@
}
}
- AssertionResult WriteSnapshotAndHash(const std::string& name) {
- if (ShouldUseCompression()) {
+ AssertionResult WriteSnapshots() {
+ for (const auto& partition : {sys_, vnd_, prd_}) {
+ auto res = WriteSnapshotAndHash(partition);
+ if (!res) {
+ return res;
+ }
+ }
+ return AssertionSuccess();
+ }
+
+ AssertionResult WriteSnapshotAndHash(PartitionUpdate* partition) {
+ std::string name = partition->partition_name() + "_b";
+ if (snapuserd_required_) {
std::unique_ptr<ISnapshotWriter> writer;
auto res = MapUpdateSnapshot(name, &writer);
if (!res) {
return res;
}
- if (!WriteRandomData(writer.get(), &hashes_[name])) {
+ if (!WriteRandomSnapshotData(writer.get(), &hashes_[name])) {
return AssertionFailure() << "Unable to write random data to snapshot " << name;
}
if (!writer->Finalize()) {
@@ -1070,6 +1135,42 @@
<< ", hash: " << hashes_[name];
}
+ bool WriteRandomSnapshotData(ICowWriter* writer, std::string* hash) {
+ unique_fd rand(open("/dev/urandom", O_RDONLY));
+ if (rand < 0) {
+ PLOG(ERROR) << "open /dev/urandom";
+ return false;
+ }
+
+ SHA256_CTX ctx;
+ SHA256_Init(&ctx);
+
+ if (!writer->options().max_blocks) {
+ LOG(ERROR) << "CowWriter must specify maximum number of blocks";
+ return false;
+ }
+ const auto num_blocks = writer->options().max_blocks.value();
+
+ const auto block_size = writer->options().block_size;
+ std::string block(block_size, '\0');
+ for (uint64_t i = 0; i < num_blocks; i++) {
+ if (!ReadFully(rand, block.data(), block.size())) {
+ PLOG(ERROR) << "read /dev/urandom";
+ return false;
+ }
+ if (!writer->AddRawBlocks(i, block.data(), block.size())) {
+ LOG(ERROR) << "Failed to add raw block " << i;
+ return false;
+ }
+ SHA256_Update(&ctx, block.data(), block.size());
+ }
+
+ uint8_t out[32];
+ SHA256_Final(out, &ctx);
+ *hash = ToHexString(out, sizeof(out));
+ return true;
+ }
+
// Generate a snapshot that moves all the upper blocks down to the start.
// It doesn't really matter the order, we just want copies that reference
// blocks that won't exist if the partition shrinks.
@@ -1178,9 +1279,7 @@
ASSERT_EQ(nullptr, tgt->FindPartition("prd_b-cow"));
// Write some data to target partitions.
- for (const auto& name : {"sys_b", "vnd_b", "prd_b"}) {
- ASSERT_TRUE(WriteSnapshotAndHash(name));
- }
+ ASSERT_TRUE(WriteSnapshots());
// Assert that source partitions aren't affected.
for (const auto& name : {"sys_a", "vnd_a", "prd_a"}) {
@@ -1208,7 +1307,7 @@
// Initiate the merge and wait for it to be completed.
ASSERT_TRUE(init->InitiateMerge());
- ASSERT_EQ(init->IsSnapuserdRequired(), IsDaemonRequired());
+ ASSERT_EQ(init->IsSnapuserdRequired(), snapuserd_required_);
{
// We should have started in SECOND_PHASE since nothing shrinks.
ASSERT_TRUE(AcquireLock());
@@ -1235,8 +1334,8 @@
}
TEST_F(SnapshotUpdateTest, DuplicateOps) {
- if (!ShouldUseCompression()) {
- GTEST_SKIP() << "Compression-only test";
+ if (!snapuserd_required_) {
+ GTEST_SKIP() << "snapuserd-only test";
}
// Execute the update.
@@ -1244,9 +1343,7 @@
ASSERT_TRUE(sm->CreateUpdateSnapshots(manifest_));
// Write some data to target partitions.
- for (const auto& name : {"sys_b", "vnd_b", "prd_b"}) {
- ASSERT_TRUE(WriteSnapshotAndHash(name));
- }
+ ASSERT_TRUE(WriteSnapshots());
std::vector<PartitionUpdate*> partitions = {sys_, vnd_, prd_};
for (auto* partition : partitions) {
@@ -1279,9 +1376,9 @@
// Test that shrinking and growing partitions at the same time is handled
// correctly in VABC.
TEST_F(SnapshotUpdateTest, SpaceSwapUpdate) {
- if (!ShouldUseCompression()) {
+ if (!snapuserd_required_) {
// b/179111359
- GTEST_SKIP() << "Skipping Virtual A/B Compression test";
+ GTEST_SKIP() << "Skipping snapuserd test";
}
auto old_sys_size = GetSize(sys_);
@@ -1310,8 +1407,8 @@
ASSERT_EQ(status.old_partition_size(), 3145728);
}
- ASSERT_TRUE(WriteSnapshotAndHash("sys_b"));
- ASSERT_TRUE(WriteSnapshotAndHash("vnd_b"));
+ ASSERT_TRUE(WriteSnapshotAndHash(sys_));
+ ASSERT_TRUE(WriteSnapshotAndHash(vnd_));
ASSERT_TRUE(ShiftAllSnapshotBlocks("prd_b", old_prd_size));
sync();
@@ -1342,7 +1439,7 @@
// Initiate the merge and wait for it to be completed.
ASSERT_TRUE(init->InitiateMerge());
- ASSERT_EQ(init->IsSnapuserdRequired(), IsDaemonRequired());
+ ASSERT_EQ(init->IsSnapuserdRequired(), snapuserd_required_);
{
// Check that the merge phase is FIRST_PHASE until at least one call
// to ProcessUpdateState() occurs.
@@ -1396,9 +1493,9 @@
// Test that a transient merge consistency check failure can resume properly.
TEST_F(SnapshotUpdateTest, ConsistencyCheckResume) {
- if (!ShouldUseCompression()) {
+ if (!snapuserd_required_) {
// b/179111359
- GTEST_SKIP() << "Skipping Virtual A/B Compression test";
+ GTEST_SKIP() << "Skipping snapuserd test";
}
auto old_sys_size = GetSize(sys_);
@@ -1414,8 +1511,8 @@
ASSERT_TRUE(sm->BeginUpdate());
ASSERT_TRUE(sm->CreateUpdateSnapshots(manifest_));
- ASSERT_TRUE(WriteSnapshotAndHash("sys_b"));
- ASSERT_TRUE(WriteSnapshotAndHash("vnd_b"));
+ ASSERT_TRUE(WriteSnapshotAndHash(sys_));
+ ASSERT_TRUE(WriteSnapshotAndHash(vnd_));
ASSERT_TRUE(ShiftAllSnapshotBlocks("prd_b", old_prd_size));
sync();
@@ -1450,7 +1547,7 @@
// Initiate the merge and wait for it to be completed.
ASSERT_TRUE(init->InitiateMerge());
- ASSERT_EQ(init->IsSnapuserdRequired(), IsDaemonRequired());
+ ASSERT_EQ(init->IsSnapuserdRequired(), snapuserd_required_);
{
// Check that the merge phase is FIRST_PHASE until at least one call
// to ProcessUpdateState() occurs.
@@ -1576,9 +1673,7 @@
ASSERT_TRUE(sm->CreateUpdateSnapshots(manifest_));
// Write some data to target partitions.
- for (const auto& name : {"sys_b", "vnd_b", "prd_b"}) {
- ASSERT_TRUE(WriteSnapshotAndHash(name));
- }
+ ASSERT_TRUE(WriteSnapshots());
// Assert that source partitions aren't affected.
for (const auto& name : {"sys_a", "vnd_a", "prd_a"}) {
@@ -1737,9 +1832,7 @@
ASSERT_FALSE(image_manager_->BackingImageExists("prd_b-cow-img"));
// Write some data to target partitions.
- for (const auto& name : {"sys_b", "vnd_b", "prd_b"}) {
- ASSERT_TRUE(WriteSnapshotAndHash(name));
- }
+ ASSERT_TRUE(WriteSnapshots());
// Assert that source partitions aren't affected.
for (const auto& name : {"sys_a", "vnd_a", "prd_a"}) {
@@ -2011,9 +2104,7 @@
ASSERT_TRUE(sm->CreateUpdateSnapshots(manifest_));
// Write some data to target partitions.
- for (const auto& name : {"sys_b", "vnd_b", "prd_b"}) {
- ASSERT_TRUE(WriteSnapshotAndHash(name)) << name;
- }
+ ASSERT_TRUE(WriteSnapshots());
ASSERT_TRUE(sm->FinishedSnapshotWrites(true /* wipe */));
@@ -2053,17 +2144,15 @@
ASSERT_TRUE(sm->CreateUpdateSnapshots(manifest_));
// Write some data to target partitions.
- for (const auto& name : {"sys_b", "vnd_b", "prd_b"}) {
- ASSERT_TRUE(WriteSnapshotAndHash(name)) << name;
- }
+ ASSERT_TRUE(WriteSnapshots());
// Create a stale snapshot that should not exist.
{
ASSERT_TRUE(AcquireLock());
PartitionCowCreator cow_creator = {
- .compression_enabled = ShouldUseCompression(),
- .compression_algorithm = ShouldUseCompression() ? "gz" : "none",
+ .using_snapuserd = snapuserd_required_,
+ .compression_algorithm = snapuserd_required_ ? FLAGS_compression_method : "",
};
SnapshotStatus status;
status.set_name("sys_a");
@@ -2138,7 +2227,7 @@
// Map and write some data to target partition.
ASSERT_TRUE(MapUpdateSnapshots({"vnd_b", "prd_b"}));
- ASSERT_TRUE(WriteSnapshotAndHash("sys_b"));
+ ASSERT_TRUE(WriteSnapshotAndHash(sys_));
// Finish update.
ASSERT_TRUE(sm->FinishedSnapshotWrites(false));
@@ -2159,8 +2248,8 @@
// Test for overflow bit after update
TEST_F(SnapshotUpdateTest, Overflow) {
- if (ShouldUseCompression()) {
- GTEST_SKIP() << "No overflow bit set for userspace COWs";
+ if (snapuserd_required_) {
+ GTEST_SKIP() << "No overflow bit set for snapuserd COWs";
}
const auto actual_write_size = GetSize(sys_);
@@ -2174,7 +2263,7 @@
// Map and write some data to target partitions.
ASSERT_TRUE(MapUpdateSnapshots({"vnd_b", "prd_b"}));
- ASSERT_TRUE(WriteSnapshotAndHash("sys_b"));
+ ASSERT_TRUE(WriteSnapshotAndHash(sys_));
std::vector<android::dm::DeviceMapper::TargetInfo> table;
ASSERT_TRUE(DeviceMapper::Instance().GetTableStatus("sys_b", &table));
@@ -2234,8 +2323,8 @@
ASSERT_TRUE(sm->CreateUpdateSnapshots(manifest_));
// Write some data to target partitions.
- for (const auto& name : {"sys_b", "vnd_b", "prd_b", "dlkm_b"}) {
- ASSERT_TRUE(WriteSnapshotAndHash(name));
+ for (const auto& partition : {sys_, vnd_, prd_, dlkm}) {
+ ASSERT_TRUE(WriteSnapshotAndHash(partition));
}
// Assert that source partitions aren't affected.
@@ -2294,8 +2383,8 @@
};
TEST_F(SnapshotUpdateTest, DaemonTransition) {
- if (!ShouldUseCompression()) {
- GTEST_SKIP() << "Skipping Virtual A/B Compression test";
+ if (!snapuserd_required_) {
+ GTEST_SKIP() << "Skipping snapuserd test";
}
// Ensure a connection to the second-stage daemon, but use the first-stage
@@ -2359,9 +2448,7 @@
// Execute the update.
ASSERT_TRUE(sm->BeginUpdate());
ASSERT_TRUE(sm->CreateUpdateSnapshots(manifest_));
- for (const auto& name : {"sys_b", "vnd_b", "prd_b"}) {
- ASSERT_TRUE(WriteSnapshotAndHash(name));
- }
+ ASSERT_TRUE(WriteSnapshots());
ASSERT_TRUE(sm->FinishedSnapshotWrites(false));
ASSERT_TRUE(sm->MapAllSnapshots(10s));
@@ -2411,13 +2498,6 @@
// fit in super, but not |prd|.
constexpr uint64_t partition_size = 3788_KiB;
SetSize(sys_, partition_size);
- SetSize(vnd_, partition_size);
- SetSize(prd_, 18_MiB);
-
- // Make sure |prd| does not fit in super at all. On VABC, this means we
- // fake an extra large COW for |vnd| to fill up super.
- vnd_->set_estimate_cow_size(30_MiB);
- prd_->set_estimate_cow_size(30_MiB);
AddOperationForPartitions();
@@ -2429,23 +2509,7 @@
GTEST_SKIP() << "Test does not apply to userspace snapshots";
}
- // Test that partitions prioritize using space in super.
- auto tgt = MetadataBuilder::New(*opener_, "super", 1);
- ASSERT_NE(tgt, nullptr);
- ASSERT_NE(nullptr, tgt->FindPartition("sys_b-cow"));
- ASSERT_NE(nullptr, tgt->FindPartition("vnd_b-cow"));
- ASSERT_EQ(nullptr, tgt->FindPartition("prd_b-cow"));
-
- // Write some data to target partitions.
- for (const auto& name : {"sys_b", "vnd_b", "prd_b"}) {
- ASSERT_TRUE(WriteSnapshotAndHash(name));
- }
-
- // Assert that source partitions aren't affected.
- for (const auto& name : {"sys_a", "vnd_a", "prd_a"}) {
- ASSERT_TRUE(IsPartitionUnchanged(name));
- }
-
+ ASSERT_TRUE(WriteSnapshots());
ASSERT_TRUE(sm->FinishedSnapshotWrites(false));
ASSERT_TRUE(UnmapAll());
@@ -2736,41 +2800,16 @@
}
}
-bool IsDaemonRequired() {
- if (FLAGS_force_config == "dmsnap") {
- return false;
+void KillSnapuserd() {
+ auto status = android::base::GetProperty("init.svc.snapuserd", "stopped");
+ if (status == "stopped") {
+ return;
}
-
- if (!IsCompressionEnabled()) {
- return false;
+ auto snapuserd_client = SnapuserdClient::Connect(kSnapuserdSocket, 5s);
+ if (!snapuserd_client) {
+ return;
}
-
- const std::string UNKNOWN = "unknown";
- const std::string vendor_release =
- android::base::GetProperty("ro.vendor.build.version.release_or_codename", UNKNOWN);
-
- // No userspace snapshots if vendor partition is on Android 12
- // However, for GRF devices, snapuserd daemon will be on
- // vendor ramdisk in Android 12.
- if (vendor_release.find("12") != std::string::npos) {
- return true;
- }
-
- if (!FLAGS_force_config.empty()) {
- return true;
- }
-
- return IsUserspaceSnapshotsEnabled();
-}
-
-bool ShouldUseCompression() {
- if (FLAGS_force_config == "vab" || FLAGS_force_config == "dmsnap") {
- return false;
- }
- if (FLAGS_force_config == "vabc") {
- return true;
- }
- return IsCompressionEnabled();
+ snapuserd_client->DetachSnapuserd();
}
} // namespace snapshot
@@ -2783,35 +2822,20 @@
android::base::SetProperty("ctl.stop", "snapuserd");
- std::unordered_set<std::string> configs = {"", "dmsnap", "vab", "vabc"};
- if (configs.count(FLAGS_force_config) == 0) {
+ std::unordered_set<std::string> modes = {"", "vab-legacy", "vabc-legacy"};
+ if (modes.count(FLAGS_force_mode) == 0) {
std::cerr << "Unexpected force_config argument\n";
return 1;
}
- if (FLAGS_force_config == "dmsnap") {
- if (!android::base::SetProperty("snapuserd.test.dm.snapshots", "1")) {
- return testing::AssertionFailure()
- << "Failed to disable property: virtual_ab.userspace.snapshots.enabled";
- }
- }
-
- if (FLAGS_force_iouring_disable == "iouring_disabled") {
- if (!android::base::SetProperty("snapuserd.test.io_uring.force_disable", "1")) {
- return testing::AssertionFailure()
- << "Failed to disable property: snapuserd.test.io_uring.disabled";
- }
- }
+ // This is necessary if the configuration we're testing doesn't match the device.
+ android::snapshot::KillSnapuserd();
int ret = RUN_ALL_TESTS();
- if (FLAGS_force_config == "dmsnap") {
- android::base::SetProperty("snapuserd.test.dm.snapshots", "0");
- }
+ android::base::SetProperty("snapuserd.test.dm.snapshots", "0");
+ android::base::SetProperty("snapuserd.test.io_uring.force_disable", "0");
- if (FLAGS_force_iouring_disable == "iouring_disabled") {
- android::base::SetProperty("snapuserd.test.io_uring.force_disable", "0");
- }
-
+ android::snapshot::KillSnapuserd();
return ret;
}
diff --git a/fs_mgr/libsnapshot/snapuserd/Android.bp b/fs_mgr/libsnapshot/snapuserd/Android.bp
index bc2bceb..64e0b8a 100644
--- a/fs_mgr/libsnapshot/snapuserd/Android.bp
+++ b/fs_mgr/libsnapshot/snapuserd/Android.bp
@@ -47,6 +47,7 @@
"libbase",
"liblog",
],
+ export_include_dirs: ["include"],
ramdisk_available: true,
}
@@ -68,11 +69,12 @@
"user-space-merge/snapuserd_readahead.cpp",
"user-space-merge/snapuserd_transitions.cpp",
"user-space-merge/snapuserd_server.cpp",
+ "user-space-merge/snapuserd_verify.cpp",
],
cflags: [
"-Wall",
- "-Werror"
+ "-Werror",
],
static_libs: [
@@ -85,19 +87,18 @@
"liblog",
"libsnapshot_cow",
"libz",
+ "liblz4",
"libext4_utils",
"liburing",
],
- include_dirs: ["bionic/libc/kernel"],
-}
-cc_binary {
- name: "snapuserd",
- defaults: ["snapuserd_defaults"],
- init_rc: [
- "snapuserd.rc",
+ header_libs: [
+ "libstorage_literals_headers",
],
+ include_dirs: ["bionic/libc/kernel"],
+ system_shared_libs: [],
+
// snapuserd is started during early boot by first-stage init. At that
// point, /system is mounted using the "dm-user" device-mapper kernel
// module. dm-user routes all I/O to userspace to be handled by
@@ -105,22 +106,47 @@
// faults for its code pages.
static_executable: true,
- system_shared_libs: [],
- ramdisk_available: true,
- vendor_ramdisk_available: true,
- recovery_available: true,
-
// Snapuserd segfaults with ThinLTO
// http://b/208565717
lto: {
never: true,
- }
+ },
+}
+
+cc_binary {
+ name: "snapuserd",
+ defaults: ["snapuserd_defaults"],
+ init_rc: [
+ "snapuserd.rc",
+ ],
+ ramdisk_available: false,
+ vendor_ramdisk_available: true,
+ recovery_available: true,
+}
+
+// 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.
+cc_binary {
+ name: "snapuserd_ramdisk",
+ defaults: ["snapuserd_defaults"],
+ init_rc: [
+ "snapuserd.rc",
+ ],
+ // This target is specifically for generic ramdisk, therefore we set
+ // vendor_ramdisk_available to false.
+ ramdisk_available: true,
+ vendor_ramdisk_available: false,
+ ramdisk: true,
+ symlinks: ["snapuserd"],
}
cc_test {
name: "cow_snapuserd_test",
defaults: [
"fs_mgr_defaults",
+ "libsnapshot_cow_defaults",
],
srcs: [
"dm-snapshot-merge/cow_snapuserd_test.cpp",
@@ -162,6 +188,7 @@
name: "snapuserd_test",
defaults: [
"fs_mgr_defaults",
+ "libsnapshot_cow_defaults",
],
srcs: [
"user-space-merge/snapuserd_test.cpp",
diff --git a/fs_mgr/libsnapshot/snapuserd/dm-snapshot-merge/snapuserd_server.cpp b/fs_mgr/libsnapshot/snapuserd/dm-snapshot-merge/snapuserd_server.cpp
index 9ddc963..577b09d 100644
--- a/fs_mgr/libsnapshot/snapuserd/dm-snapshot-merge/snapuserd_server.cpp
+++ b/fs_mgr/libsnapshot/snapuserd/dm-snapshot-merge/snapuserd_server.cpp
@@ -505,7 +505,7 @@
// We don't care if the ACK is received.
code[0] = 'a';
- if (TEMP_FAILURE_RETRY(send(fd, code, sizeof(code), MSG_NOSIGNAL) < 0)) {
+ if (TEMP_FAILURE_RETRY(send(fd, code, sizeof(code), MSG_NOSIGNAL)) < 0) {
PLOG(ERROR) << "Failed to send ACK to proxy";
return false;
}
diff --git a/fs_mgr/libsnapshot/snapuserd/include/snapuserd/snapuserd_client.h b/fs_mgr/libsnapshot/snapuserd/include/snapuserd/snapuserd_client.h
index cebda1c..4b62b20 100644
--- a/fs_mgr/libsnapshot/snapuserd/include/snapuserd/snapuserd_client.h
+++ b/fs_mgr/libsnapshot/snapuserd/include/snapuserd/snapuserd_client.h
@@ -71,8 +71,6 @@
// must ONLY be called if the control device has already been deleted.
bool WaitForDeviceDelete(const std::string& control_device);
- void CloseConnection() { sockfd_ = {}; }
-
// Detach snapuserd. This shuts down the listener socket, and will cause
// snapuserd to gracefully exit once all handler threads have terminated.
// This should only be used on first-stage instances of snapuserd.
@@ -89,6 +87,10 @@
// Return the status of the snapshot
std::string QuerySnapshotStatus(const std::string& misc_name);
+
+ // Check the update verification status - invoked by update_verifier during
+ // boot
+ bool QueryUpdateVerification();
};
} // namespace snapshot
diff --git a/fs_mgr/libsnapshot/snapuserd/snapuserd_client.cpp b/fs_mgr/libsnapshot/snapuserd/snapuserd_client.cpp
index 7b1c7a3..e08cf9b 100644
--- a/fs_mgr/libsnapshot/snapuserd/snapuserd_client.cpp
+++ b/fs_mgr/libsnapshot/snapuserd/snapuserd_client.cpp
@@ -269,5 +269,15 @@
return Receivemsg();
}
+bool SnapuserdClient::QueryUpdateVerification() {
+ std::string msg = "update-verify";
+ if (!Sendmsg(msg)) {
+ LOG(ERROR) << "Failed to send message " << msg << " to snapuserd";
+ return false;
+ }
+ std::string response = Receivemsg();
+ return response == "success";
+}
+
} // namespace snapshot
} // namespace android
diff --git a/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_core.cpp b/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_core.cpp
index 692cb74..492c43f 100644
--- a/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_core.cpp
+++ b/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_core.cpp
@@ -18,6 +18,7 @@
#include <sys/utsname.h>
+#include <android-base/chrono_utils.h>
#include <android-base/properties.h>
#include <android-base/scopeguard.h>
#include <android-base/strings.h>
@@ -70,6 +71,9 @@
read_ahead_thread_ = std::make_unique<ReadAhead>(cow_device_, backing_store_device_, misc_name_,
GetSharedPtr());
+
+ update_verify_ = std::make_unique<UpdateVerify>(misc_name_);
+
return true;
}
@@ -143,21 +147,22 @@
NotifyRAForMergeReady();
}
-void SnapshotHandler::CheckMergeCompletionStatus() {
+bool SnapshotHandler::CheckMergeCompletionStatus() {
if (!merge_initiated_) {
SNAP_LOG(INFO) << "Merge was not initiated. Total-data-ops: "
<< reader_->get_num_total_data_ops();
- return;
+ return false;
}
struct CowHeader* ch = reinterpret_cast<struct CowHeader*>(mapped_addr_);
SNAP_LOG(INFO) << "Merge-status: Total-Merged-ops: " << ch->num_merge_ops
<< " Total-data-ops: " << reader_->get_num_total_data_ops();
+ return true;
}
bool SnapshotHandler::ReadMetadata() {
- reader_ = std::make_unique<CowReader>(CowReader::ReaderFlags::USERSPACE_MERGE);
+ reader_ = std::make_unique<CowReader>(CowReader::ReaderFlags::USERSPACE_MERGE, true);
CowHeader header;
CowOptions options;
@@ -188,7 +193,7 @@
UpdateMergeCompletionPercentage();
// Initialize the iterator for reading metadata
- std::unique_ptr<ICowOpIter> cowop_iter = reader_->GetMergeOpIter();
+ std::unique_ptr<ICowOpIter> cowop_iter = reader_->GetOpIter(true);
int num_ra_ops_per_iter = ((GetBufferDataSize()) / BLOCK_SZ);
int ra_index = 0;
@@ -306,206 +311,6 @@
return ReadMetadata();
}
-void SnapshotHandler::FinalizeIouring() {
- io_uring_queue_exit(ring_.get());
-}
-
-bool SnapshotHandler::InitializeIouring(int io_depth) {
- ring_ = std::make_unique<struct io_uring>();
-
- int ret = io_uring_queue_init(io_depth, ring_.get(), 0);
- if (ret) {
- LOG(ERROR) << "io_uring_queue_init failed with ret: " << ret;
- return false;
- }
-
- LOG(INFO) << "io_uring_queue_init success with io_depth: " << io_depth;
- return true;
-}
-
-bool SnapshotHandler::ReadBlocksAsync(const std::string& dm_block_device,
- const std::string& partition_name, size_t size) {
- // 64k block size with io_depth of 64 is optimal
- // for a single thread. We just need a single thread
- // to read all the blocks from all dynamic partitions.
- size_t io_depth = 64;
- size_t bs = (64 * 1024);
-
- if (!InitializeIouring(io_depth)) {
- return false;
- }
-
- LOG(INFO) << "ReadBlockAsync start "
- << " Block-device: " << dm_block_device << " Partition-name: " << partition_name
- << " Size: " << size;
-
- auto scope_guard = android::base::make_scope_guard([this]() -> void { FinalizeIouring(); });
-
- std::vector<std::unique_ptr<struct iovec>> vecs;
- using AlignedBuf = std::unique_ptr<void, decltype(free)*>;
- std::vector<AlignedBuf> alignedBufVector;
-
- /*
- * TODO: We need aligned memory for DIRECT-IO. However, if we do
- * a DIRECT-IO and verify the blocks then we need to inform
- * update-verifier that block verification has been done and
- * there is no need to repeat the same. We are not there yet
- * as we need to see if there are any boot time improvements doing
- * a DIRECT-IO.
- *
- * Also, we could you the same function post merge for block verification;
- * again, we can do a DIRECT-IO instead of thrashing page-cache and
- * hurting other applications.
- *
- * For now, we will just create aligned buffers but rely on buffered
- * I/O until we have perf numbers to justify DIRECT-IO.
- */
- for (int i = 0; i < io_depth; i++) {
- auto iovec = std::make_unique<struct iovec>();
- vecs.push_back(std::move(iovec));
-
- struct iovec* iovec_ptr = vecs[i].get();
-
- if (posix_memalign(&iovec_ptr->iov_base, BLOCK_SZ, bs)) {
- LOG(ERROR) << "posix_memalign failed";
- return false;
- }
-
- iovec_ptr->iov_len = bs;
- alignedBufVector.push_back(
- std::unique_ptr<void, decltype(free)*>(iovec_ptr->iov_base, free));
- }
-
- android::base::unique_fd fd(TEMP_FAILURE_RETRY(open(dm_block_device.c_str(), O_RDONLY)));
- if (fd.get() == -1) {
- SNAP_PLOG(ERROR) << "File open failed - block-device " << dm_block_device
- << " partition-name: " << partition_name;
- return false;
- }
-
- loff_t offset = 0;
- size_t remain = size;
- size_t read_sz = io_depth * bs;
-
- while (remain > 0) {
- size_t to_read = std::min(remain, read_sz);
- size_t queue_size = to_read / bs;
-
- for (int i = 0; i < queue_size; i++) {
- struct io_uring_sqe* sqe = io_uring_get_sqe(ring_.get());
- if (!sqe) {
- SNAP_LOG(ERROR) << "io_uring_get_sqe() failed";
- return false;
- }
-
- struct iovec* iovec_ptr = vecs[i].get();
-
- io_uring_prep_read(sqe, fd.get(), iovec_ptr->iov_base, iovec_ptr->iov_len, offset);
- sqe->flags |= IOSQE_ASYNC;
- offset += bs;
- }
-
- int ret = io_uring_submit(ring_.get());
- if (ret != queue_size) {
- SNAP_LOG(ERROR) << "submit got: " << ret << " wanted: " << queue_size;
- return false;
- }
-
- for (int i = 0; i < queue_size; i++) {
- struct io_uring_cqe* cqe;
-
- int ret = io_uring_wait_cqe(ring_.get(), &cqe);
- if (ret) {
- SNAP_PLOG(ERROR) << "wait_cqe failed" << ret;
- return false;
- }
-
- if (cqe->res < 0) {
- SNAP_LOG(ERROR) << "io failed with res: " << cqe->res;
- return false;
- }
- io_uring_cqe_seen(ring_.get(), cqe);
- }
-
- remain -= to_read;
- }
-
- LOG(INFO) << "ReadBlockAsync complete: "
- << " Block-device: " << dm_block_device << " Partition-name: " << partition_name
- << " Size: " << size;
- return true;
-}
-
-void SnapshotHandler::ReadBlocksToCache(const std::string& dm_block_device,
- const std::string& partition_name, off_t offset,
- size_t size) {
- android::base::unique_fd fd(TEMP_FAILURE_RETRY(open(dm_block_device.c_str(), O_RDONLY)));
- if (fd.get() == -1) {
- SNAP_PLOG(ERROR) << "Error reading " << dm_block_device
- << " partition-name: " << partition_name;
- return;
- }
-
- size_t remain = size;
- off_t file_offset = offset;
- // We pick 4M I/O size based on the fact that the current
- // update_verifier has a similar I/O size.
- size_t read_sz = 1024 * BLOCK_SZ;
- std::vector<uint8_t> buf(read_sz);
-
- while (remain > 0) {
- size_t to_read = std::min(remain, read_sz);
-
- if (!android::base::ReadFullyAtOffset(fd.get(), buf.data(), to_read, file_offset)) {
- SNAP_PLOG(ERROR) << "Failed to read block from block device: " << dm_block_device
- << " at offset: " << file_offset
- << " partition-name: " << partition_name << " total-size: " << size
- << " remain_size: " << remain;
- return;
- }
-
- file_offset += to_read;
- remain -= to_read;
- }
-
- SNAP_LOG(INFO) << "Finished reading block-device: " << dm_block_device
- << " partition: " << partition_name << " size: " << size
- << " offset: " << offset;
-}
-
-void SnapshotHandler::ReadBlocks(const std::string partition_name,
- const std::string& dm_block_device) {
- SNAP_LOG(DEBUG) << "Reading partition: " << partition_name
- << " Block-Device: " << dm_block_device;
-
- uint64_t dev_sz = 0;
-
- unique_fd fd(TEMP_FAILURE_RETRY(open(dm_block_device.c_str(), O_RDONLY | O_CLOEXEC)));
- if (fd < 0) {
- SNAP_LOG(ERROR) << "Cannot open block device";
- return;
- }
-
- dev_sz = get_block_device_size(fd.get());
- if (!dev_sz) {
- SNAP_PLOG(ERROR) << "Could not determine block device size: " << dm_block_device;
- return;
- }
-
- int num_threads = 2;
- size_t num_blocks = dev_sz >> BLOCK_SHIFT;
- size_t num_blocks_per_thread = num_blocks / num_threads;
- size_t read_sz_per_thread = num_blocks_per_thread << BLOCK_SHIFT;
- off_t offset = 0;
-
- for (int i = 0; i < num_threads; i++) {
- std::async(std::launch::async, &SnapshotHandler::ReadBlocksToCache, this, dm_block_device,
- partition_name, offset, read_sz_per_thread);
-
- offset += read_sz_per_thread;
- }
-}
-
/*
* Entry point to launch threads
*/
@@ -526,42 +331,22 @@
std::async(std::launch::async, &Worker::RunThread, worker_threads_[i].get()));
}
- bool second_stage_init = true;
+ bool partition_verification = true;
- // We don't want to read the blocks during first stage init.
+ // We don't want to read the blocks during first stage init or
+ // during post-install phase.
if (android::base::EndsWith(misc_name_, "-init") || is_socket_present_) {
- second_stage_init = false;
- }
-
- if (second_stage_init) {
- SNAP_LOG(INFO) << "Reading blocks to cache....";
- auto& dm = DeviceMapper::Instance();
- auto dm_block_devices = dm.FindDmPartitions();
- if (dm_block_devices.empty()) {
- SNAP_LOG(ERROR) << "No dm-enabled block device is found.";
- } else {
- auto parts = android::base::Split(misc_name_, "-");
- std::string partition_name = parts[0];
-
- const char* suffix_b = "_b";
- const char* suffix_a = "_a";
-
- partition_name.erase(partition_name.find_last_not_of(suffix_b) + 1);
- partition_name.erase(partition_name.find_last_not_of(suffix_a) + 1);
-
- if (dm_block_devices.find(partition_name) == dm_block_devices.end()) {
- SNAP_LOG(ERROR) << "Failed to find dm block device for " << partition_name;
- } else {
- ReadBlocks(partition_name, dm_block_devices.at(partition_name));
- }
- }
- } else {
- SNAP_LOG(INFO) << "Not reading block device into cache";
+ partition_verification = false;
}
std::future<bool> merge_thread =
std::async(std::launch::async, &Worker::RunMergeThread, merge_thread_.get());
+ // Now that the worker threads are up, scan the partitions.
+ if (partition_verification) {
+ update_verify_->VerifyUpdatePartition();
+ }
+
bool ret = true;
for (auto& t : threads) {
ret = t.get() && ret;
diff --git a/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_core.h b/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_core.h
index 83d40f6..42237ef 100644
--- a/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_core.h
+++ b/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_core.h
@@ -18,6 +18,9 @@
#include <stdint.h>
#include <stdlib.h>
#include <sys/mman.h>
+#include <sys/resource.h>
+#include <sys/time.h>
+#include <unistd.h>
#include <condition_variable>
#include <cstring>
@@ -42,18 +45,22 @@
#include <liburing.h>
#include <snapuserd/snapuserd_buffer.h>
#include <snapuserd/snapuserd_kernel.h>
+#include <storage_literals/storage_literals.h>
namespace android {
namespace snapshot {
using android::base::unique_fd;
using namespace std::chrono_literals;
+using namespace android::storage_literals;
static constexpr size_t PAYLOAD_BUFFER_SZ = (1UL << 20);
static_assert(PAYLOAD_BUFFER_SZ >= BLOCK_SZ);
static constexpr int kNumWorkerThreads = 4;
+static constexpr int kNiceValueForMergeThreads = -5;
+
#define SNAP_LOG(level) LOG(level) << misc_name_ << ": "
#define SNAP_PLOG(level) PLOG(level) << misc_name_ << ": "
@@ -165,6 +172,36 @@
std::unique_ptr<struct io_uring> ring_;
};
+class UpdateVerify {
+ public:
+ UpdateVerify(const std::string& misc_name);
+ void VerifyUpdatePartition();
+ bool CheckPartitionVerification();
+
+ private:
+ enum class UpdateVerifyState {
+ VERIFY_UNKNOWN,
+ VERIFY_FAILED,
+ VERIFY_SUCCESS,
+ };
+
+ std::string misc_name_;
+ UpdateVerifyState state_;
+ std::mutex m_lock_;
+ std::condition_variable m_cv_;
+
+ int kMinThreadsToVerify = 1;
+ int kMaxThreadsToVerify = 4;
+ uint64_t kThresholdSize = 512_MiB;
+ uint64_t kBlockSizeVerify = 1_MiB;
+
+ bool IsBlockAligned(uint64_t read_size) { return ((read_size & (BLOCK_SZ - 1)) == 0); }
+ void UpdatePartitionVerificationState(UpdateVerifyState state);
+ bool VerifyPartition(const std::string& partition_name, const std::string& dm_block_device);
+ bool VerifyBlocks(const std::string& partition_name, const std::string& dm_block_device,
+ off_t offset, int skip_blocks, uint64_t dev_sz);
+};
+
class Worker {
public:
Worker(const std::string& cow_device, const std::string& backing_device,
@@ -274,7 +311,7 @@
const bool& IsAttached() const { return attached_; }
void AttachControlDevice() { attached_ = true; }
- void CheckMergeCompletionStatus();
+ bool CheckMergeCompletionStatus();
bool CommitMerge(int num_merge_ops);
void CloseFds() { cow_fd_ = {}; }
@@ -305,6 +342,8 @@
// State transitions for merge
void InitiateMerge();
+ void MonitorMerge();
+ void WakeupMonitorMergeThread();
void WaitForMergeComplete();
bool WaitForMergeBegin();
void NotifyRAForMergeReady();
@@ -333,6 +372,7 @@
void SetSocketPresent(bool socket) { is_socket_present_ = socket; }
void SetIouringEnabled(bool io_uring_enabled) { is_io_uring_enabled_ = io_uring_enabled; }
bool MergeInitiated() { return merge_initiated_; }
+ bool MergeMonitored() { return merge_monitored_; }
double GetMergePercentage() { return merge_completion_percentage_; }
// Merge Block State Transitions
@@ -344,24 +384,16 @@
MERGE_GROUP_STATE ProcessMergingBlock(uint64_t new_block, void* buffer);
bool IsIouringSupported();
+ bool CheckPartitionVerification() { return update_verify_->CheckPartitionVerification(); }
private:
bool ReadMetadata();
sector_t ChunkToSector(chunk_t chunk) { return chunk << CHUNK_SHIFT; }
chunk_t SectorToChunk(sector_t sector) { return sector >> CHUNK_SHIFT; }
- bool IsBlockAligned(int read_size) { return ((read_size & (BLOCK_SZ - 1)) == 0); }
+ bool IsBlockAligned(uint64_t read_size) { return ((read_size & (BLOCK_SZ - 1)) == 0); }
struct BufferState* GetBufferState();
void UpdateMergeCompletionPercentage();
- void ReadBlocks(const std::string partition_name, const std::string& dm_block_device);
- void ReadBlocksToCache(const std::string& dm_block_device, const std::string& partition_name,
- off_t offset, size_t size);
-
- bool InitializeIouring(int io_depth);
- void FinalizeIouring();
- bool ReadBlocksAsync(const std::string& dm_block_device, const std::string& partition_name,
- size_t size);
-
// COW device
std::string cow_device_;
// Source device
@@ -407,12 +439,14 @@
double merge_completion_percentage_;
bool merge_initiated_ = false;
+ bool merge_monitored_ = false;
bool attached_ = false;
bool is_socket_present_;
bool is_io_uring_enabled_ = false;
bool scratch_space_ = false;
std::unique_ptr<struct io_uring> ring_;
+ std::unique_ptr<UpdateVerify> update_verify_;
};
} // namespace snapshot
diff --git a/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_merge.cpp b/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_merge.cpp
index c26a2cd..d57f434 100644
--- a/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_merge.cpp
+++ b/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_merge.cpp
@@ -71,16 +71,16 @@
}
bool Worker::MergeReplaceZeroOps() {
- // Flush every 8192 ops. Since all ops are independent and there is no
+ // Flush after merging 2MB. Since all ops are independent and there is no
// dependency between COW ops, we will flush the data and the number
- // of ops merged in COW file for every 8192 ops. If there is a crash,
- // we will end up replaying some of the COW ops which were already merged.
- // That is ok.
+ // of ops merged in COW block device. If there is a crash, we will
+ // end up replaying some of the COW ops which were already merged. That is
+ // ok.
//
- // Why 8192 ops ? Increasing this may improve merge time 3-4 seconds but
- // we need to make sure that we checkpoint; 8k ops seems optimal. In-case
- // if there is a crash merge should always make forward progress.
- int total_ops_merged_per_commit = (PAYLOAD_BUFFER_SZ / BLOCK_SZ) * 32;
+ // Although increasing this greater than 2MB may help in improving merge
+ // times; however, on devices with low memory, this can be problematic
+ // when there are multiple merge threads in parallel.
+ int total_ops_merged_per_commit = (PAYLOAD_BUFFER_SZ / BLOCK_SZ) * 2;
int num_ops_merged = 0;
SNAP_LOG(INFO) << "MergeReplaceZeroOps started....";
@@ -466,7 +466,7 @@
}
bool Worker::Merge() {
- cowop_iter_ = reader_->GetMergeOpIter();
+ cowop_iter_ = reader_->GetOpIter(true);
bool retry = false;
bool ordered_ops_merge_status;
@@ -543,6 +543,10 @@
return true;
}
+ if (setpriority(PRIO_PROCESS, gettid(), kNiceValueForMergeThreads)) {
+ SNAP_PLOG(ERROR) << "Failed to set priority for TID: " << gettid();
+ }
+
SNAP_LOG(INFO) << "Merge starting..";
if (!Init()) {
diff --git a/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_readahead.cpp b/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_readahead.cpp
index fa2866f..fbe57d2 100644
--- a/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_readahead.cpp
+++ b/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_readahead.cpp
@@ -727,6 +727,10 @@
InitializeIouring();
+ if (setpriority(PRIO_PROCESS, gettid(), kNiceValueForMergeThreads)) {
+ SNAP_PLOG(ERROR) << "Failed to set priority for TID: " << gettid();
+ }
+
while (!RAIterDone()) {
if (!ReadAheadIOStart()) {
break;
@@ -768,7 +772,7 @@
}
void ReadAhead::InitializeRAIter() {
- cowop_iter_ = reader_->GetMergeOpIter();
+ cowop_iter_ = reader_->GetOpIter(true);
}
bool ReadAhead::RAIterDone() {
diff --git a/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_server.cpp b/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_server.cpp
index 82b2b25..d437d32 100644
--- a/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_server.cpp
+++ b/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_server.cpp
@@ -55,10 +55,19 @@
if (input == "initiate_merge") return DaemonOps::INITIATE;
if (input == "merge_percent") return DaemonOps::PERCENTAGE;
if (input == "getstatus") return DaemonOps::GETSTATUS;
+ if (input == "update-verify") return DaemonOps::UPDATE_VERIFY;
return DaemonOps::INVALID;
}
+UserSnapshotServer::UserSnapshotServer() {
+ monitor_merge_event_fd_.reset(eventfd(0, EFD_CLOEXEC));
+ if (monitor_merge_event_fd_ == -1) {
+ PLOG(FATAL) << "monitor_merge_event_fd_: failed to create eventfd";
+ }
+ terminating_ = false;
+}
+
UserSnapshotServer::~UserSnapshotServer() {
// Close any client sockets that were added via AcceptClient().
for (size_t i = 1; i < watched_fds_.size(); i++) {
@@ -249,7 +258,7 @@
return Sendmsg(fd, "fail");
}
- if (!StartMerge(*iter)) {
+ if (!StartMerge(&lock, *iter)) {
return Sendmsg(fd, "fail");
}
@@ -282,6 +291,14 @@
return Sendmsg(fd, merge_status);
}
}
+ case DaemonOps::UPDATE_VERIFY: {
+ std::lock_guard<std::mutex> lock(lock_);
+ if (!UpdateVerification(&lock)) {
+ return Sendmsg(fd, "fail");
+ }
+
+ return Sendmsg(fd, "success");
+ }
default: {
LOG(ERROR) << "Received unknown message type from client";
Sendmsg(fd, "fail");
@@ -298,7 +315,7 @@
}
handler->snapuserd()->CloseFds();
- handler->snapuserd()->CheckMergeCompletionStatus();
+ bool merge_completed = handler->snapuserd()->CheckMergeCompletionStatus();
handler->snapuserd()->UnmapBufferRegion();
auto misc_name = handler->misc_name();
@@ -306,7 +323,11 @@
{
std::lock_guard<std::mutex> lock(lock_);
- num_partitions_merge_complete_ += 1;
+ if (merge_completed) {
+ num_partitions_merge_complete_ += 1;
+ active_merge_threads_ -= 1;
+ WakeupMonitorMergeThread();
+ }
handler->SetThreadTerminated();
auto iter = FindHandler(&lock, handler->misc_name());
if (iter == dm_users_.end()) {
@@ -418,6 +439,9 @@
if (th.joinable()) th.join();
}
+
+ stop_monitor_merge_thread_ = true;
+ WakeupMonitorMergeThread();
}
void UserSnapshotServer::AddWatchedFd(android::base::borrowed_fd fd, int events) {
@@ -438,15 +462,14 @@
}
bool UserSnapshotServer::HandleClient(android::base::borrowed_fd fd, int revents) {
- if (revents & POLLHUP) {
- LOG(DEBUG) << "Snapuserd client disconnected";
- return false;
- }
-
std::string str;
if (!Recv(fd, &str)) {
return false;
}
+ if (str.empty() && (revents & POLLHUP)) {
+ LOG(DEBUG) << "Snapuserd client disconnected";
+ return false;
+ }
if (!Receivemsg(fd, str)) {
LOG(ERROR) << "Encountered error handling client message, revents: " << revents;
return false;
@@ -502,13 +525,24 @@
return true;
}
-bool UserSnapshotServer::StartMerge(const std::shared_ptr<UserSnapshotDmUserHandler>& handler) {
+bool UserSnapshotServer::StartMerge(std::lock_guard<std::mutex>* proof_of_lock,
+ const std::shared_ptr<UserSnapshotDmUserHandler>& handler) {
+ CHECK(proof_of_lock);
+
if (!handler->snapuserd()->IsAttached()) {
LOG(ERROR) << "Handler not attached to dm-user - Merge thread cannot be started";
return false;
}
- handler->snapuserd()->InitiateMerge();
+ handler->snapuserd()->MonitorMerge();
+
+ if (!is_merge_monitor_started_.has_value()) {
+ std::thread(&UserSnapshotServer::MonitorMerge, this).detach();
+ is_merge_monitor_started_ = true;
+ }
+
+ merge_handlers_.push(handler);
+ WakeupMonitorMergeThread();
return true;
}
@@ -590,6 +624,48 @@
return true;
}
+void UserSnapshotServer::WakeupMonitorMergeThread() {
+ uint64_t notify = 1;
+ ssize_t rc = TEMP_FAILURE_RETRY(write(monitor_merge_event_fd_.get(), ¬ify, sizeof(notify)));
+ if (rc < 0) {
+ PLOG(FATAL) << "failed to notify monitor merge thread";
+ }
+}
+
+void UserSnapshotServer::MonitorMerge() {
+ while (!stop_monitor_merge_thread_) {
+ uint64_t testVal;
+ ssize_t ret =
+ TEMP_FAILURE_RETRY(read(monitor_merge_event_fd_.get(), &testVal, sizeof(testVal)));
+ if (ret == -1) {
+ PLOG(FATAL) << "Failed to read from eventfd";
+ } else if (ret == 0) {
+ LOG(FATAL) << "Hit EOF on eventfd";
+ }
+
+ LOG(INFO) << "MonitorMerge: active-merge-threads: " << active_merge_threads_;
+ {
+ std::lock_guard<std::mutex> lock(lock_);
+ while (active_merge_threads_ < kMaxMergeThreads && merge_handlers_.size() > 0) {
+ auto handler = merge_handlers_.front();
+ merge_handlers_.pop();
+
+ if (!handler->snapuserd()) {
+ LOG(INFO) << "MonitorMerge: skipping deleted handler: " << handler->misc_name();
+ continue;
+ }
+
+ LOG(INFO) << "Starting merge for partition: "
+ << handler->snapuserd()->GetMiscName();
+ handler->snapuserd()->InitiateMerge();
+ active_merge_threads_ += 1;
+ }
+ }
+ }
+
+ LOG(INFO) << "Exiting MonitorMerge: size: " << merge_handlers_.size();
+}
+
bool UserSnapshotServer::WaitForSocket() {
auto scope_guard = android::base::make_scope_guard([this]() -> void { JoinAllThreads(); });
@@ -637,7 +713,7 @@
// We don't care if the ACK is received.
code[0] = 'a';
- if (TEMP_FAILURE_RETRY(send(fd, code, sizeof(code), MSG_NOSIGNAL) < 0)) {
+ if (TEMP_FAILURE_RETRY(send(fd, code, sizeof(code), MSG_NOSIGNAL)) < 0) {
PLOG(ERROR) << "Failed to send ACK to proxy";
return false;
}
@@ -646,6 +722,7 @@
if (!StartWithSocket(true)) {
return false;
}
+
return Run();
}
@@ -687,5 +764,22 @@
return true;
}
+bool UserSnapshotServer::UpdateVerification(std::lock_guard<std::mutex>* proof_of_lock) {
+ CHECK(proof_of_lock);
+
+ bool status = true;
+ for (auto iter = dm_users_.begin(); iter != dm_users_.end(); iter++) {
+ auto& th = (*iter)->thread();
+ if (th.joinable() && status) {
+ status = (*iter)->snapuserd()->CheckPartitionVerification() && status;
+ } else {
+ // return immediately if there is a failure
+ return false;
+ }
+ }
+
+ return status;
+}
+
} // namespace snapshot
} // namespace android
diff --git a/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_server.h b/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_server.h
index 34e7941..c2af61f 100644
--- a/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_server.h
+++ b/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_server.h
@@ -15,6 +15,7 @@
#pragma once
#include <poll.h>
+#include <sys/eventfd.h>
#include <cstdio>
#include <cstring>
@@ -22,6 +23,8 @@
#include <future>
#include <iostream>
#include <mutex>
+#include <optional>
+#include <queue>
#include <sstream>
#include <string>
#include <thread>
@@ -34,6 +37,7 @@
namespace snapshot {
static constexpr uint32_t kMaxPacketSize = 512;
+static constexpr uint8_t kMaxMergeThreads = 2;
enum class DaemonOps {
INIT,
@@ -46,6 +50,7 @@
INITIATE,
PERCENTAGE,
GETSTATUS,
+ UPDATE_VERIFY,
INVALID,
};
@@ -84,13 +89,19 @@
std::vector<struct pollfd> watched_fds_;
bool is_socket_present_ = false;
int num_partitions_merge_complete_ = 0;
+ int active_merge_threads_ = 0;
+ bool stop_monitor_merge_thread_ = false;
bool is_server_running_ = false;
bool io_uring_enabled_ = false;
+ std::optional<bool> is_merge_monitor_started_;
+
+ android::base::unique_fd monitor_merge_event_fd_;
std::mutex lock_;
using HandlerList = std::vector<std::shared_ptr<UserSnapshotDmUserHandler>>;
HandlerList dm_users_;
+ std::queue<std::shared_ptr<UserSnapshotDmUserHandler>> merge_handlers_;
void AddWatchedFd(android::base::borrowed_fd fd, int events);
void AcceptClient();
@@ -108,6 +119,8 @@
bool IsTerminating() { return terminating_; }
void RunThread(std::shared_ptr<UserSnapshotDmUserHandler> handler);
+ void MonitorMerge();
+
void JoinAllThreads();
bool StartWithSocket(bool start_listening);
@@ -118,8 +131,10 @@
double GetMergePercentage(std::lock_guard<std::mutex>* proof_of_lock);
void TerminateMergeThreads(std::lock_guard<std::mutex>* proof_of_lock);
+ bool UpdateVerification(std::lock_guard<std::mutex>* proof_of_lock);
+
public:
- UserSnapshotServer() { terminating_ = false; }
+ UserSnapshotServer();
~UserSnapshotServer();
bool Start(const std::string& socketname);
@@ -133,9 +148,11 @@
const std::string& backing_device,
const std::string& base_path_merge);
bool StartHandler(const std::shared_ptr<UserSnapshotDmUserHandler>& handler);
- bool StartMerge(const std::shared_ptr<UserSnapshotDmUserHandler>& handler);
+ bool StartMerge(std::lock_guard<std::mutex>* proof_of_lock,
+ const std::shared_ptr<UserSnapshotDmUserHandler>& handler);
std::string GetMergeStatus(const std::shared_ptr<UserSnapshotDmUserHandler>& handler);
+ void WakeupMonitorMergeThread();
void SetTerminating() { terminating_ = true; }
void ReceivedSocketSignal() { received_socket_signal_ = true; }
void SetServerRunning() { is_server_running_ = true; }
diff --git a/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_transitions.cpp b/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_transitions.cpp
index d4e1d7c..28c9f68 100644
--- a/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_transitions.cpp
+++ b/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_transitions.cpp
@@ -165,6 +165,13 @@
using namespace android::dm;
using android::base::unique_fd;
+void SnapshotHandler::MonitorMerge() {
+ {
+ std::lock_guard<std::mutex> lock(lock_);
+ merge_monitored_ = true;
+ }
+}
+
// This is invoked once primarily by update-engine to initiate
// the merge
void SnapshotHandler::InitiateMerge() {
@@ -361,10 +368,16 @@
std::string SnapshotHandler::GetMergeStatus() {
bool merge_not_initiated = false;
+ bool merge_monitored = false;
bool merge_failed = false;
{
std::lock_guard<std::mutex> lock(lock_);
+
+ if (MergeMonitored()) {
+ merge_monitored = true;
+ }
+
if (!MergeInitiated()) {
merge_not_initiated = true;
}
@@ -387,6 +400,12 @@
return "snapshot-merge-complete";
}
+ // Merge monitor thread is tracking the merge but the merge thread
+ // is not started yet.
+ if (merge_monitored) {
+ return "snapshot-merge";
+ }
+
// Return the state as "snapshot". If the device was rebooted during
// merge, we will return the status as "snapshot". This is ok, as
// libsnapshot will explicitly resume the merge. This is slightly
diff --git a/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_verify.cpp b/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_verify.cpp
new file mode 100644
index 0000000..18c1dfc
--- /dev/null
+++ b/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_verify.cpp
@@ -0,0 +1,222 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "snapuserd_core.h"
+
+#include <android-base/chrono_utils.h>
+#include <android-base/scopeguard.h>
+#include <android-base/strings.h>
+
+namespace android {
+namespace snapshot {
+
+using namespace android;
+using namespace android::dm;
+using android::base::unique_fd;
+
+UpdateVerify::UpdateVerify(const std::string& misc_name)
+ : misc_name_(misc_name), state_(UpdateVerifyState::VERIFY_UNKNOWN) {}
+
+bool UpdateVerify::CheckPartitionVerification() {
+ auto now = std::chrono::system_clock::now();
+ auto deadline = now + 10s;
+ {
+ std::unique_lock<std::mutex> cv_lock(m_lock_);
+ while (state_ == UpdateVerifyState::VERIFY_UNKNOWN) {
+ auto status = m_cv_.wait_until(cv_lock, deadline);
+ if (status == std::cv_status::timeout) {
+ return false;
+ }
+ }
+ }
+
+ return (state_ == UpdateVerifyState::VERIFY_SUCCESS);
+}
+
+void UpdateVerify::UpdatePartitionVerificationState(UpdateVerifyState state) {
+ {
+ std::lock_guard<std::mutex> lock(m_lock_);
+ state_ = state;
+ }
+ m_cv_.notify_all();
+}
+
+void UpdateVerify::VerifyUpdatePartition() {
+ bool succeeded = false;
+
+ auto scope_guard = android::base::make_scope_guard([this, &succeeded]() -> void {
+ if (!succeeded) {
+ UpdatePartitionVerificationState(UpdateVerifyState::VERIFY_FAILED);
+ }
+ });
+
+ auto& dm = DeviceMapper::Instance();
+ auto dm_block_devices = dm.FindDmPartitions();
+ if (dm_block_devices.empty()) {
+ SNAP_LOG(ERROR) << "No dm-enabled block device is found.";
+ return;
+ }
+
+ const auto parts = android::base::Split(misc_name_, "-");
+ std::string partition_name = parts[0];
+
+ constexpr auto&& suffix_b = "_b";
+ constexpr auto&& suffix_a = "_a";
+
+ partition_name.erase(partition_name.find_last_not_of(suffix_b) + 1);
+ partition_name.erase(partition_name.find_last_not_of(suffix_a) + 1);
+
+ if (dm_block_devices.find(partition_name) == dm_block_devices.end()) {
+ SNAP_LOG(ERROR) << "Failed to find dm block device for " << partition_name;
+ return;
+ }
+
+ if (!VerifyPartition(partition_name, dm_block_devices.at(partition_name))) {
+ SNAP_LOG(ERROR) << "Partition: " << partition_name
+ << " Block-device: " << dm_block_devices.at(partition_name)
+ << " verification failed";
+ }
+ succeeded = true;
+}
+
+bool UpdateVerify::VerifyBlocks(const std::string& partition_name,
+ const std::string& dm_block_device, off_t offset, int skip_blocks,
+ uint64_t dev_sz) {
+ unique_fd fd(TEMP_FAILURE_RETRY(open(dm_block_device.c_str(), O_RDONLY | O_DIRECT)));
+ if (fd < 0) {
+ SNAP_LOG(ERROR) << "open failed: " << dm_block_device;
+ return false;
+ }
+
+ loff_t file_offset = offset;
+ const uint64_t read_sz = kBlockSizeVerify;
+
+ void* addr;
+ ssize_t page_size = getpagesize();
+ if (posix_memalign(&addr, page_size, read_sz) < 0) {
+ SNAP_PLOG(ERROR) << "posix_memalign failed "
+ << " page_size: " << page_size << " read_sz: " << read_sz;
+ return false;
+ }
+
+ std::unique_ptr<void, decltype(&::free)> buffer(addr, ::free);
+
+ uint64_t bytes_read = 0;
+
+ while (true) {
+ size_t to_read = std::min((dev_sz - file_offset), read_sz);
+
+ if (!android::base::ReadFullyAtOffset(fd.get(), buffer.get(), to_read, file_offset)) {
+ SNAP_PLOG(ERROR) << "Failed to read block from block device: " << dm_block_device
+ << " partition-name: " << partition_name
+ << " at offset: " << file_offset << " read-size: " << to_read
+ << " block-size: " << dev_sz;
+ return false;
+ }
+
+ bytes_read += to_read;
+ file_offset += (skip_blocks * kBlockSizeVerify);
+ if (file_offset >= dev_sz) {
+ break;
+ }
+ }
+
+ SNAP_LOG(DEBUG) << "Verification success with bytes-read: " << bytes_read
+ << " dev_sz: " << dev_sz << " partition_name: " << partition_name;
+
+ return true;
+}
+
+bool UpdateVerify::VerifyPartition(const std::string& partition_name,
+ const std::string& dm_block_device) {
+ android::base::Timer timer;
+
+ SNAP_LOG(INFO) << "VerifyPartition: " << partition_name << " Block-device: " << dm_block_device;
+
+ bool succeeded = false;
+ auto scope_guard = android::base::make_scope_guard([this, &succeeded]() -> void {
+ if (!succeeded) {
+ UpdatePartitionVerificationState(UpdateVerifyState::VERIFY_FAILED);
+ }
+ });
+
+ unique_fd fd(TEMP_FAILURE_RETRY(open(dm_block_device.c_str(), O_RDONLY | O_DIRECT)));
+ if (fd < 0) {
+ SNAP_LOG(ERROR) << "open failed: " << dm_block_device;
+ return false;
+ }
+
+ uint64_t dev_sz = get_block_device_size(fd.get());
+ if (!dev_sz) {
+ SNAP_PLOG(ERROR) << "Could not determine block device size: " << dm_block_device;
+ return false;
+ }
+
+ if (!IsBlockAligned(dev_sz)) {
+ SNAP_LOG(ERROR) << "dev_sz: " << dev_sz << " is not block aligned";
+ return false;
+ }
+
+ /*
+ * Not all partitions are of same size. Some partitions are as small as
+ * 100Mb. We can just finish them in a single thread. For bigger partitions
+ * such as product, 4 threads are sufficient enough.
+ *
+ * TODO: With io_uring SQ_POLL support, we can completely cut this
+ * down to just single thread for all partitions and potentially verify all
+ * the partitions with zero syscalls. Additionally, since block layer
+ * supports polling, IO_POLL could be used which will further cut down
+ * latency.
+ */
+ int num_threads = kMinThreadsToVerify;
+ if (dev_sz > kThresholdSize) {
+ num_threads = kMaxThreadsToVerify;
+ }
+
+ std::vector<std::future<bool>> threads;
+ off_t start_offset = 0;
+ const int skip_blocks = num_threads;
+
+ while (num_threads) {
+ threads.emplace_back(std::async(std::launch::async, &UpdateVerify::VerifyBlocks, this,
+ partition_name, dm_block_device, start_offset, skip_blocks,
+ dev_sz));
+ start_offset += kBlockSizeVerify;
+ num_threads -= 1;
+ if (start_offset >= dev_sz) {
+ break;
+ }
+ }
+
+ bool ret = true;
+ for (auto& t : threads) {
+ ret = t.get() && ret;
+ }
+
+ if (ret) {
+ succeeded = true;
+ UpdatePartitionVerificationState(UpdateVerifyState::VERIFY_SUCCESS);
+ SNAP_LOG(INFO) << "Partition: " << partition_name << " Block-device: " << dm_block_device
+ << " Size: " << dev_sz
+ << " verification success. Duration : " << timer.duration().count() << " ms";
+ return true;
+ }
+
+ return false;
+}
+
+} // namespace snapshot
+} // namespace android
diff --git a/fs_mgr/libsnapshot/test_helpers.cpp b/fs_mgr/libsnapshot/test_helpers.cpp
index 71fe124..b05123a 100644
--- a/fs_mgr/libsnapshot/test_helpers.cpp
+++ b/fs_mgr/libsnapshot/test_helpers.cpp
@@ -18,10 +18,12 @@
#include <android-base/file.h>
#include <android-base/logging.h>
+#include <android-base/parsebool.h>
#include <android-base/properties.h>
#include <android-base/strings.h>
#include <android-base/unique_fd.h>
#include <gtest/gtest.h>
+#include <liblp/property_fetcher.h>
#include <openssl/sha.h>
#include <payload_consumer/file_descriptor.h>
@@ -128,48 +130,6 @@
return true;
}
-bool WriteRandomData(ICowWriter* writer, std::string* hash) {
- unique_fd rand(open("/dev/urandom", O_RDONLY));
- if (rand < 0) {
- PLOG(ERROR) << "open /dev/urandom";
- return false;
- }
-
- SHA256_CTX ctx;
- if (hash) {
- SHA256_Init(&ctx);
- }
-
- if (!writer->options().max_blocks) {
- LOG(ERROR) << "CowWriter must specify maximum number of blocks";
- return false;
- }
- uint64_t num_blocks = writer->options().max_blocks.value();
-
- size_t block_size = writer->options().block_size;
- std::string block(block_size, '\0');
- for (uint64_t i = 0; i < num_blocks; i++) {
- if (!ReadFully(rand, block.data(), block.size())) {
- PLOG(ERROR) << "read /dev/urandom";
- return false;
- }
- if (!writer->AddRawBlocks(i, block.data(), block.size())) {
- LOG(ERROR) << "Failed to add raw block " << i;
- return false;
- }
- if (hash) {
- SHA256_Update(&ctx, block.data(), block.size());
- }
- }
-
- if (hash) {
- uint8_t out[32];
- SHA256_Final(out, &ctx);
- *hash = ToHexString(out, sizeof(out));
- }
- return true;
-}
-
std::string HashSnapshot(ISnapshotWriter* writer) {
auto reader = writer->OpenReader();
if (!reader) {
@@ -320,5 +280,38 @@
return android::base::GetBoolProperty("ro.virtual_ab.enabled", false);
}
+SnapshotTestPropertyFetcher::SnapshotTestPropertyFetcher(
+ const std::string& slot_suffix, std::unordered_map<std::string, std::string>&& props)
+ : properties_(std::move(props)) {
+ properties_["ro.boot.slot_suffix"] = slot_suffix;
+ properties_["ro.boot.dynamic_partitions"] = "true";
+ properties_["ro.boot.dynamic_partitions_retrofit"] = "false";
+ properties_["ro.virtual_ab.enabled"] = "true";
+}
+
+std::string SnapshotTestPropertyFetcher::GetProperty(const std::string& key,
+ const std::string& defaultValue) {
+ auto iter = properties_.find(key);
+ if (iter == properties_.end()) {
+ return android::base::GetProperty(key, defaultValue);
+ }
+ return iter->second;
+}
+
+bool SnapshotTestPropertyFetcher::GetBoolProperty(const std::string& key, bool defaultValue) {
+ auto iter = properties_.find(key);
+ if (iter == properties_.end()) {
+ return android::base::GetBoolProperty(key, defaultValue);
+ }
+ switch (android::base::ParseBool(iter->second)) {
+ case android::base::ParseBoolResult::kTrue:
+ return true;
+ case android::base::ParseBoolResult::kFalse:
+ return false;
+ default:
+ return defaultValue;
+ }
+}
+
} // namespace snapshot
} // namespace android
diff --git a/fs_mgr/libsnapshot/utility.cpp b/fs_mgr/libsnapshot/utility.cpp
index f01bec9..0a1be0d 100644
--- a/fs_mgr/libsnapshot/utility.cpp
+++ b/fs_mgr/libsnapshot/utility.cpp
@@ -26,6 +26,7 @@
#include <android-base/properties.h>
#include <android-base/strings.h>
#include <fs_mgr/roots.h>
+#include <liblp/property_fetcher.h>
using android::dm::kSectorSize;
using android::fiemap::FiemapStatus;
@@ -33,6 +34,7 @@
using android::fs_mgr::EnsurePathUnmounted;
using android::fs_mgr::Fstab;
using android::fs_mgr::GetEntryForPath;
+using android::fs_mgr::IPropertyFetcher;
using android::fs_mgr::MetadataBuilder;
using android::fs_mgr::Partition;
using android::fs_mgr::ReadDefaultFstab;
@@ -184,16 +186,50 @@
new_extent->set_num_blocks(num_blocks);
}
-bool IsCompressionEnabled() {
- return android::base::GetBoolProperty("ro.virtual_ab.compression.enabled", false);
+bool GetLegacyCompressionEnabledProperty() {
+ auto fetcher = IPropertyFetcher::GetInstance();
+ return fetcher->GetBoolProperty("ro.virtual_ab.compression.enabled", false);
}
-bool IsUserspaceSnapshotsEnabled() {
- return android::base::GetBoolProperty("ro.virtual_ab.userspace.snapshots.enabled", false);
+bool GetUserspaceSnapshotsEnabledProperty() {
+ auto fetcher = IPropertyFetcher::GetInstance();
+ return fetcher->GetBoolProperty("ro.virtual_ab.userspace.snapshots.enabled", false);
}
-bool IsIouringEnabled() {
- return android::base::GetBoolProperty("ro.virtual_ab.io_uring.enabled", false);
+bool CanUseUserspaceSnapshots() {
+ if (!GetUserspaceSnapshotsEnabledProperty()) {
+ return false;
+ }
+
+ auto fetcher = IPropertyFetcher::GetInstance();
+
+ const std::string UNKNOWN = "unknown";
+ const std::string vendor_release =
+ fetcher->GetProperty("ro.vendor.build.version.release_or_codename", UNKNOWN);
+
+ // No user-space snapshots if vendor partition is on Android 12
+ if (vendor_release.find("12") != std::string::npos) {
+ LOG(INFO) << "Userspace snapshots disabled as vendor partition is on Android: "
+ << vendor_release;
+ return false;
+ }
+
+ if (IsDmSnapshotTestingEnabled()) {
+ LOG(INFO) << "Userspace snapshots disabled for testing";
+ return false;
+ }
+
+ return true;
+}
+
+bool GetIouringEnabledProperty() {
+ auto fetcher = IPropertyFetcher::GetInstance();
+ return fetcher->GetBoolProperty("ro.virtual_ab.io_uring.enabled", false);
+}
+
+bool GetXorCompressionEnabledProperty() {
+ auto fetcher = IPropertyFetcher::GetInstance();
+ return fetcher->GetBoolProperty("ro.virtual_ab.compression.xor.enabled", false);
}
std::string GetOtherPartitionName(const std::string& name) {
@@ -205,7 +241,8 @@
}
bool IsDmSnapshotTestingEnabled() {
- return android::base::GetBoolProperty("snapuserd.test.dm.snapshots", false);
+ auto fetcher = IPropertyFetcher::GetInstance();
+ return fetcher->GetBoolProperty("snapuserd.test.dm.snapshots", false);
}
} // namespace snapshot
diff --git a/fs_mgr/libsnapshot/utility.h b/fs_mgr/libsnapshot/utility.h
index 0ef3234..eff6f10 100644
--- a/fs_mgr/libsnapshot/utility.h
+++ b/fs_mgr/libsnapshot/utility.h
@@ -59,7 +59,6 @@
// On destruct, delete |name| from device mapper.
AutoUnmapDevice(android::dm::IDeviceMapper* dm, const std::string& name)
: AutoDevice(name), dm_(dm) {}
- AutoUnmapDevice(AutoUnmapDevice&& other) = default;
~AutoUnmapDevice();
private:
@@ -72,7 +71,6 @@
// On destruct, delete |name| from image manager.
AutoUnmapImage(android::fiemap::IImageManager* images, const std::string& name)
: AutoDevice(name), images_(images) {}
- AutoUnmapImage(AutoUnmapImage&& other) = default;
~AutoUnmapImage();
private:
@@ -86,7 +84,6 @@
AutoDeleteSnapshot(SnapshotManager* manager, SnapshotManager::LockedFile* lock,
const std::string& name)
: AutoDevice(name), manager_(manager), lock_(lock) {}
- AutoDeleteSnapshot(AutoDeleteSnapshot&& other);
~AutoDeleteSnapshot();
private:
@@ -129,15 +126,16 @@
void AppendExtent(google::protobuf::RepeatedPtrField<chromeos_update_engine::Extent>* extents,
uint64_t start_block, uint64_t num_blocks);
-bool IsCompressionEnabled();
+bool GetLegacyCompressionEnabledProperty();
+bool GetUserspaceSnapshotsEnabledProperty();
+bool GetIouringEnabledProperty();
+bool GetXorCompressionEnabledProperty();
-bool IsUserspaceSnapshotsEnabled();
-
+bool CanUseUserspaceSnapshots();
bool IsDmSnapshotTestingEnabled();
-bool IsIouringEnabled();
-
// Swap the suffix of a partition name.
std::string GetOtherPartitionName(const std::string& name);
+
} // namespace snapshot
} // namespace android
diff --git a/fs_mgr/tests/Android.bp b/fs_mgr/tests/Android.bp
index d82d566..fdc0d8e 100644
--- a/fs_mgr/tests/Android.bp
+++ b/fs_mgr/tests/Android.bp
@@ -52,9 +52,9 @@
],
}
-cc_prebuilt_binary {
+sh_binary_host {
name: "adb-remount-test.sh",
- srcs: ["adb-remount-test.sh"],
+ src: "adb-remount-test.sh",
target: {
darwin: {
enabled: false,
@@ -62,11 +62,7 @@
windows: {
enabled: false,
},
- android: {
- enabled: false,
- },
},
- host_supported: true,
}
sh_test {
diff --git a/fs_mgr/tests/adb-remount-test.sh b/fs_mgr/tests/adb-remount-test.sh
index 9542bc1..a6bdd6c 100755
--- a/fs_mgr/tests/adb-remount-test.sh
+++ b/fs_mgr/tests/adb-remount-test.sh
@@ -50,15 +50,18 @@
ESCAPE="`echo | tr '\n' '\033'`"
# A _real_ embedded carriage return character
CR="`echo | tr '\n' '\r'`"
-GREEN="${ESCAPE}[32m"
-RED="${ESCAPE}[31m"
-YELLOW="${ESCAPE}[33m"
-BLUE="${ESCAPE}[34m"
-NORMAL="${ESCAPE}[0m"
-TMPDIR=${TMPDIR:-/tmp}
-print_time=false
+RED=
+GREEN=
+YELLOW=
+BLUE=
+NORMAL=
+color=false
+# Assume support color if stdout is terminal.
+[ -t 1 ] && color=true
+print_time=true
start_time=`date +%s`
ACTIVE_SLOT=
+OVERLAYFS_BACKING="cache mnt/scratch"
ADB_WAIT=4m
FASTBOOT_WAIT=2m
@@ -68,6 +71,46 @@
## Helper Functions
##
+[ "USAGE: LOG [RUN|OK|PASSED|WARNING|ERROR|FAILED|INFO] [message]..." ]
+LOG() {
+ if ${print_time}; then
+ echo -n "$(date '+%m-%d %T') "
+ fi >&2
+ case "${1}" in
+ R*)
+ shift
+ echo "${GREEN}[ RUN ]${NORMAL}" "${@}"
+ ;;
+ OK)
+ shift
+ echo "${GREEN}[ OK ]${NORMAL}" "${@}"
+ ;;
+ P*)
+ shift
+ echo "${GREEN}[ PASSED ]${NORMAL}" "${@}"
+ ;;
+ W*)
+ shift
+ echo "${YELLOW}[ WARNING ]${NORMAL}" "${@}"
+ ;;
+ E*)
+ shift
+ echo "${RED}[ ERROR ]${NORMAL}" "${@}"
+ ;;
+ F*)
+ shift
+ echo "${RED}[ FAILED ]${NORMAL}" "${@}"
+ ;;
+ I*)
+ shift
+ echo "${BLUE}[ INFO ]${NORMAL}" "${@}"
+ ;;
+ *)
+ echo "${BLUE}[ INFO ]${NORMAL}" "${@}"
+ ;;
+ esac >&2
+}
+
[ "USAGE: inFastboot
Returns: true if device is in fastboot mode" ]
@@ -143,7 +186,7 @@
Returns: the logcat output" ]
adb_logcat() {
- echo "${RED}[ INFO ]${NORMAL} logcat ${@}" >&2 &&
+ LOG INFO "logcat ${*}"
adb logcat "${@}" </dev/null |
tr -d '\r' |
grep -v 'logd : logdr: UID=' |
@@ -154,7 +197,7 @@
Returns: worrisome avc violations" ]
avc_check() {
- if ! ${overlayfs_supported:-false}; then
+ if ! ${overlayfs_needed:-false}; then
return
fi
local L=`adb_logcat -b all -v brief -d \
@@ -164,7 +207,7 @@
if [ -z "${L}" ]; then
return
fi
- echo "${YELLOW}[ WARNING ]${NORMAL} unlabeled sepolicy violations:" >&2
+ LOG WARNING "unlabeled sepolicy violations:"
echo "${L}" | sed "s/^/${INDENT}/" >&2
}
@@ -175,15 +218,6 @@
adb_sh getprop ${1} </dev/null
}
-[ "USAGE: isDebuggable
-
-Returns: true if device is (likely) a debug build" ]
-isDebuggable() {
- if inAdb && [ 1 != "`get_property ro.debuggable`" ]; then
- false
- fi
-}
-
[ "USAGE: adb_su <commands> </dev/stdin >/dev/stdout 2>/dev/stderr
Returns: true if the command running as root succeeded" ]
@@ -202,17 +236,6 @@
return ${ret}
}
-[ "USAGE: adb_ls <dirfile> >stdout
-
-Returns: filename or directoru content to stdout with carriage returns skipped,
- true if the ls had no errors" ]
-adb_ls() {
- local OUTPUT="`adb_sh ls ${1} </dev/null 2>/dev/null`"
- local ret=${?}
- echo "${OUTPUT}" | tr -d '\r'
- return ${ret}
-}
-
[ "USAGE: adb_test <expression>
Returns: exit status of the test expression" ]
@@ -227,6 +250,7 @@
avc_check
adb reboot remount-test </dev/null || true
sleep 2
+ adb_wait "${ADB_WAIT}"
}
[ "USAGE: format_duration [<seconds>|<seconds>s|<minutes>m|<hours>h|<days>d]
@@ -241,15 +265,15 @@
if [ X"${duration}" != X"${duration%s}" ]; then
duration=${duration%s}
elif [ X"${duration}" != X"${duration%m}" ]; then
- duration=`expr ${duration%m} \* 60`
+ duration=$(( ${duration%m} * 60 ))
elif [ X"${duration}" != X"${duration%h}" ]; then
- duration=`expr ${duration%h} \* 3600`
+ duration=$(( ${duration%h} * 3600 ))
elif [ X"${duration}" != X"${duration%d}" ]; then
- duration=`expr ${duration%d} \* 86400`
+ duration=$(( ${duration%d} * 86400 ))
fi
- local seconds=`expr ${duration} % 60`
- local minutes=`expr \( ${duration} / 60 \) % 60`
- local hours=`expr ${duration} / 3600`
+ local seconds=$(( ${duration} % 60 ))
+ local minutes=$(( ( ${duration} / 60 ) % 60 ))
+ local hours=$(( ${duration} / 3600 ))
if [ 0 -eq ${minutes} -a 0 -eq ${hours} ]; then
if [ 1 -eq ${duration} ]; then
echo 1 second
@@ -265,10 +289,10 @@
return
fi
if [ 0 -eq ${hours} ]; then
- echo ${minutes}:`expr ${seconds} / 10``expr ${seconds} % 10`
+ echo ${minutes}:$(( ${seconds} / 10 ))$(( ${seconds} % 10 ))
return
fi
- echo ${hours}:`expr ${minutes} / 10``expr ${minutes} % 10`:`expr ${seconds} / 10``expr ${seconds} % 10`
+ echo ${hours}:$(( ${minutes} / 10 ))$(( ${minutes} % 10 )):$(( ${seconds} / 10 ))$(( ${seconds} % 10))
}
[ "USAGE: USB_DEVICE=\`usb_devnum [--next]\`
@@ -282,7 +306,7 @@
if [ -n "${usb_device}" ]; then
USB_DEVICE=dev${usb_device}
elif [ -n "${USB_DEVICE}" -a "${1}" ]; then
- USB_DEVICE=dev`expr ${USB_DEVICE#dev} + 1`
+ USB_DEVICE=dev$(( ${USB_DEVICE#dev} + 1 ))
fi
echo "${USB_DEVICE}"
fi
@@ -298,10 +322,10 @@
if [ -n "${1}" -a -n "`which timeout`" ]; then
USB_DEVICE=`usb_devnum --next`
duration=`format_duration ${1}`
- echo -n ". . . waiting ${duration}" ${ANDROID_SERIAL} ${USB_ADDRESS} ${USB_DEVICE} "${CR}"
+ echo -n ". . . waiting ${duration}" ${ANDROID_SERIAL} ${USB_ADDRESS} ${USB_DEVICE} "${CR}" >&2
timeout --preserve-status --signal=KILL ${1} adb wait-for-device 2>/dev/null
ret=${?}
- echo -n " ${CR}"
+ echo -n " ${CR}" >&2
else
adb wait-for-device
ret=${?}
@@ -310,11 +334,11 @@
if [ 0 = ${ret} -a -n "${ACTIVE_SLOT}" ]; then
local active_slot=`get_active_slot`
if [ X"${ACTIVE_SLOT}" != X"${active_slot}" ]; then
- echo "${YELLOW}[ WARNING ]${NORMAL} Active slot changed from ${ACTIVE_SLOT} to ${active_slot}" >&2
+ LOG WARNING "Active slot changed from ${ACTIVE_SLOT} to ${active_slot}"
fi
fi
local end=`date +%s`
- local diff_time=`expr ${end} - ${start}`
+ local diff_time=$(( ${end} - ${start} ))
local _print_time=${print_time}
if [ ${diff_time} -lt 15 ]; then
_print_time=false
@@ -339,8 +363,8 @@
;;
esac
if ${_print_time} || [ -n "${reason}" ]; then
- echo "${BLUE}[ INFO ]${NORMAL} adb wait duration ${diff_time}${reason}"
- fi >&2
+ LOG INFO "adb wait duration ${diff_time}${reason}"
+ fi
return ${ret}
}
@@ -413,8 +437,8 @@
if [ 0 = ${ret} -a -n "${ACTIVE_SLOT}" ]; then
local active_slot=`get_active_slot`
if [ X"${ACTIVE_SLOT}" != X"${active_slot}" ]; then
- echo "${YELLOW}[ WARNING ]${NORMAL} Active slot changed from ${ACTIVE_SLOT} to ${active_slot}"
- fi >&2
+ LOG WARNING "Active slot changed from ${ACTIVE_SLOT} to ${active_slot}"
+ fi
fi
return ${ret}
}
@@ -438,8 +462,8 @@
if [ 0 = ${ret} -a -n "${ACTIVE_SLOT}" ]; then
local active_slot=`get_active_slot`
if [ X"${ACTIVE_SLOT}" != X"${active_slot}" ]; then
- echo "${YELLOW}[ WARNING ]${NORMAL} Active slot changed from ${ACTIVE_SLOT} to ${active_slot}"
- fi >&2
+ LOG WARNING "Active slot changed from ${ACTIVE_SLOT} to ${active_slot}"
+ fi
fi
return ${ret}
}
@@ -494,10 +518,10 @@
break
fi
fi
- counter=`expr ${counter} + 1`
+ counter=$(( ${counter} + 1 ))
if [ ${counter} -gt ${timeout} ]; then
${exit_function}
- echo "ERROR: wait_for_screen() timed out (`format_duration ${timeout}`)" >&2
+ LOG ERROR "wait_for_screen() timed out ($(format_duration ${timeout}))"
return 1
fi
sleep 1
@@ -570,22 +594,13 @@
[ "USAGE: restore
-Do nothing: should be redefined when necessary. Called after cleanup.
+Do nothing: should be redefined when necessary.
Returns: reverses configurations" ]
restore() {
true
}
-[ "USAGE: cleanup
-
-Do nothing: should be redefined when necessary
-
-Returns: cleans up any latent resources" ]
-cleanup() {
- true
-}
-
[ "USAGE: test_duration >/dev/stderr
Prints the duration of the test
@@ -593,12 +608,12 @@
Returns: reports duration" ]
test_duration() {
if ${print_time}; then
- echo "${BLUE}[ INFO ]${NORMAL} end `date`"
+ LOG INFO "end $(date)"
[ -n "${start_time}" ] || return
end_time=`date +%s`
- local diff_time=`expr ${end_time} - ${start_time}`
- echo "${BLUE}[ INFO ]${NORMAL} duration `format_duration ${diff_time}`"
- fi >&2
+ local diff_time=$(( ${end_time} - ${start_time} ))
+ LOG INFO "duration $(format_duration ${diff_time})"
+ fi
}
[ "USAGE: die [-d|-t <epoch>] [message] >/dev/stderr
@@ -618,121 +633,65 @@
fi
shift 2
fi >&2
- echo "${RED}[ FAILED ]${NORMAL} ${@}" >&2
- cleanup
- restore
- test_duration
+ LOG FAILED "${@}"
exit 1
}
-[ "USAGE: EXPECT_EQ <lval> <rval> [--warning [message]]
-
-Returns true if (regex) lval matches rval" ]
-EXPECT_EQ() {
- local lval="${1}"
- local rval="${2}"
- shift 2
- local error=1
- local prefix="${RED}[ ERROR ]${NORMAL}"
- if [ X"${1}" = X"--warning" ]; then
- prefix="${RED}[ WARNING ]${NORMAL}"
- error=0
- shift 1
- fi
- if ! ( echo X"${rval}" | grep '^X'"${lval}"'$' >/dev/null 2>/dev/null ); then
- if [ `echo ${lval}${rval}${*} | wc -c` -gt 50 -o "${rval}" != "${rval%
-*}" ]; then
- echo "${prefix} expected \"${lval}\""
- echo "${prefix} got \"${rval}\"" |
- sed ": again
- N
- s/\(\n\)\([^ ]\)/\1${INDENT}\2/
- t again"
- if [ -n "${*}" ] ; then
- echo "${prefix} ${*}"
- fi
- else
- echo "${prefix} expected \"${lval}\" got \"${rval}\" ${*}"
- fi >&2
- return ${error}
- fi
- if [ -n "${*}" ] ; then
- prefix="${GREEN}[ INFO ]${NORMAL}"
- if [ X"${lval}" != X"${rval}" ]; then # we were supplied a regex?
- if [ `echo ${lval}${rval}${*} | wc -c` -gt 60 -o "${rval}" != "${rval% *}" ]; then
- echo "${prefix} ok \"${lval}\""
- echo " = \"${rval}\"" |
- sed ": again
- N
- s/\(\n\)\([^ ]\)/\1${INDENT}\2/
- t again"
- if [ -n "${*}" ] ; then
- echo "${prefix} ${*}"
- fi
- else
- echo "${prefix} ok \"${lval}\" = \"${rval}\" ${*}"
- fi
- else
- echo "${prefix} ok \"${lval}\" ${*}"
- fi >&2
- fi
- return 0
-}
-
-[ "USAGE: EXPECT_NE <lval> <rval> [--warning [message]]
-
-Returns true if lval matches rval" ]
-EXPECT_NE() {
- local lval="${1}"
- local rval="${2}"
- shift 2
- local error=1
- local prefix="${RED}[ ERROR ]${NORMAL}"
- if [ X"${1}" = X"--warning" ]; then
- prefix="${RED}[ WARNING ]${NORMAL}"
- error=0
- shift 1
- fi
- if [ X"${rval}" = X"${lval}" ]; then
- echo "${prefix} did not expect \"${lval}\" ${*}" >&2
- return ${error}
- fi
- if [ -n "${*}" ] ; then
- echo "${prefix} ok \"${lval}\" not \"${rval}\" ${*}" >&2
- fi
- return 0
-}
-
[ "USAGE: check_eq <lval> <rval> [--warning [message]]
-Exits if (regex) lval mismatches rval" ]
+Exits if (regex) lval mismatches rval.
+
+Returns: true if lval matches rval" ]
check_eq() {
local lval="${1}"
local rval="${2}"
shift 2
+ if [[ "${rval}" =~ ^${lval}$ ]]; then
+ return 0
+ fi
+
+ local error=true
+ local logt=ERROR
if [ X"${1}" = X"--warning" ]; then
- EXPECT_EQ "${lval}" "${rval}" ${*}
- return
+ shift 1
+ error=false
+ logt=WARNING
fi
- if ! EXPECT_EQ "${lval}" "${rval}"; then
- die "${@}"
+ if [ $(( ${#lval} + ${#rval} )) -gt 40 ]; then
+ LOG "${logt}" "expected \"${lval}\"
+${INDENT}got \"${rval}\""
+ else
+ LOG "${logt}" "expected \"${lval}\" got \"${rval}\""
fi
+ ${error} && die "${*}"
+ [ -n "${*}" ] && LOG "${logt}" "${*}"
+ return 1
}
[ "USAGE: check_ne <lval> <rval> [--warning [message]]
-Exits if lval matches rval" ]
+Exits if (regex) lval matches rval.
+
+Returns: true if lval mismatches rval" ]
check_ne() {
local lval="${1}"
local rval="${2}"
shift 2
+ if ! [[ "${rval}" =~ ^${lval}$ ]]; then
+ return 0
+ fi
+
+ local error=true
+ local logt=ERROR
if [ X"${1}" = X"--warning" ]; then
- EXPECT_NE "${lval}" "${rval}" ${*}
- return
+ shift 1
+ error=false
+ logt=WARNING
fi
- if ! EXPECT_NE "${lval}" "${rval}"; then
- die "${@}"
- fi
+ LOG "${logt}" "unexpected \"${rval}\""
+ ${error} && die "${*}"
+ [ -n "${*}" ] && LOG "${logt}" "${*}"
+ return 1
}
[ "USAGE: join_with <delimiter> <strings>
@@ -752,40 +711,68 @@
echo "${result}"
}
-[ "USAGE: skip_administrative_mounts [data] < /proc/mounts
+[ "USAGE: skip_administrative_mounts < /proc/mounts
Filters out all administrative (eg: sysfs) mounts uninteresting to the test" ]
skip_administrative_mounts() {
local exclude_filesystems=(
"overlay" "tmpfs" "none" "sysfs" "proc" "selinuxfs" "debugfs" "bpf"
"binfmt_misc" "cg2_bpf" "pstore" "tracefs" "adb" "mtp" "ptp" "devpts"
- "ramdumpfs" "binder" "securityfs" "functionfs" "rootfs"
+ "ramdumpfs" "binder" "securityfs" "functionfs" "rootfs" "fuse"
)
local exclude_devices=(
"\/sys\/kernel\/debug" "\/data\/media" "\/dev\/block\/loop[0-9]*"
+ "\/dev\/block\/vold\/[^ ]+"
"${exclude_filesystems[@]}"
)
local exclude_mount_points=(
"\/cache" "\/mnt\/scratch" "\/mnt\/vendor\/persist" "\/persist"
- "\/metadata"
+ "\/metadata" "\/apex\/[^ ]+"
)
- if [ "data" = "${1}" ]; then
- exclude_mount_points+=("\/data")
- fi
awk '$1 !~ /^('"$(join_with "|" "${exclude_devices[@]}")"')$/ &&
$2 !~ /^('"$(join_with "|" "${exclude_mount_points[@]}")"')$/ &&
$3 !~ /^('"$(join_with "|" "${exclude_filesystems[@]}")"')$/'
}
-[ "USAGE: skip_unrelated_mounts < /proc/mounts
+[ "USAGE: surgically_wipe_overlayfs
-or output from df
+Surgically wipe any mounted overlayfs scratch files.
-Filters out all apex and vendor override administrative overlay mounts
-uninteresting to the test" ]
-skip_unrelated_mounts() {
- grep -v "^overlay.* /\(apex\|bionic\|system\|vendor\)/[^ ]" |
- grep -v "[%] /\(data_mirror\|apex\|bionic\|system\|vendor\)/[^ ][^ ]*$"
+Returns: true if wiped anything" ]
+surgically_wipe_overlayfs() {
+ local wiped_anything=false
+ for d in ${OVERLAYFS_BACKING}; do
+ if adb_su test -d "/${d}/overlay" </dev/null; then
+ LOG INFO "/${d}/overlay is setup, surgically wiping"
+ adb_su rm -rf "/${d}/overlay" </dev/null
+ wiped_anything=true
+ fi
+ done
+ ${wiped_anything}
+}
+
+[ "USAGE: is_overlayfs_mounted [mountpoint]
+
+Diagnostic output of overlayfs df lines to stderr.
+
+Returns: true if overlayfs is mounted [on mountpoint]" ]
+is_overlayfs_mounted() {
+ local df_output=$(adb_su df -k </dev/null)
+ local df_header_line=$(echo "${df_output}" | head -1)
+ # KISS (we do not support sub-mounts for system partitions currently)
+ local overlay_mounts=$(echo "${df_output}" | tail +2 |
+ grep -vE "[%] /(apex|system|vendor)/[^ ]+$" |
+ awk '$1 == "overlay" || $6 == "/mnt/scratch"')
+ if ! echo "${overlay_mounts}" | grep -q '^overlay '; then
+ return 1
+ fi >/dev/null 2>/dev/null
+ ( echo "${df_header_line}"
+ echo "${overlay_mounts}"
+ ) >&2
+ if [ "${#}" -gt 0 ] && ! ( echo "${overlay_mounts}" | grep -qE " ${1}\$" ); then
+ return 1
+ fi >/dev/null 2>/dev/null
+ return 0
}
##
@@ -798,7 +785,7 @@
--longoptions wait-adb:,wait-fastboot:
--longoptions wait-screen,wait-display
--longoptions no-wait-screen,no-wait-display
- --longoptions gtest_print_time,print-time
+ --longoptions gtest_print_time,print-time,no-print-time
--"
if [ "Darwin" = "${HOSTOS}" ]; then
GETOPTS=
@@ -813,12 +800,11 @@
s/--wait-adb/ /g
s/--wait-fastboot/ /g'`"
fi
-OPTIONS=`getopt ${GETOPTS} "?a:cCdDf:hs:t" ${*}` ||
+OPTIONS=`getopt ${GETOPTS} "?a:cCdDf:hs:tT" ${*}` ||
( echo "${USAGE}" >&2 ; false ) ||
die "getopt failure"
set -- ${OPTIONS}
-color=false
while [ ${#} -gt 0 ]; do
case ${1} in
-h | --help | -\?)
@@ -844,6 +830,9 @@
-t | --print-time | --gtest_print_time)
print_time=true
;;
+ -T | --no-print-time)
+ print_time=false
+ ;;
-a | --wait-adb)
ADB_WAIT=${2}
shift
@@ -866,42 +855,68 @@
esac
shift
done
-if ! ${color}; then
- GREEN=""
- RED=""
- YELLOW=""
- BLUE=""
- NORMAL=""
+
+if ${color}; then
+ RED="${ESCAPE}[31m"
+ GREEN="${ESCAPE}[32m"
+ YELLOW="${ESCAPE}[33m"
+ BLUE="${ESCAPE}[34m"
+ NORMAL="${ESCAPE}[0m"
fi
-# Set an ERR trap handler to report any unhandled error
-trap 'die "line ${LINENO}: unhandled error"' ERR
+TMPDIR=
+
+exit_handler() {
+ [ -n "${TMPDIR}" ] && rm -rf "${TMPDIR}"
+ local err=0
+ if ! restore; then
+ LOG ERROR "restore failed"
+ err=1
+ fi >&2
+ test_duration || true
+ if [ "${err}" != 0 ]; then
+ exit "${err}"
+ fi
+}
+trap 'exit_handler' EXIT
+
+TMPDIR=$(mktemp -d)
if ${print_time}; then
- echo "${BLUE}[ INFO ]${NORMAL}" start `date` >&2
+ LOG INFO "start $(date)"
fi
+if [ -z "${ANDROID_SERIAL}" ]; then
+ inAdb || die "no device or more than one device in adb mode"
+ D=$(adb devices | awk '$2 == "device" { print $1; exit }')
+ [ -n "${D}" ] || die "cannot get device serial"
+ ANDROID_SERIAL="${D}"
+fi
+export ANDROID_SERIAL
+
inFastboot && die "device in fastboot mode"
inRecovery && die "device in recovery mode"
if ! inAdb; then
- echo "${YELLOW}[ WARNING ]${NORMAL} device not in adb mode" >&2
+ LOG WARNING "device not in adb mode"
adb_wait ${ADB_WAIT}
fi
inAdb || die "specified device not in adb mode"
-isDebuggable || die "device not a debug build"
+[ "1" = "$(get_property ro.debuggable)" ] || die "device not a debug build"
+[ "orange" = "$(get_property ro.boot.verifiedbootstate)" ] || die "device not bootloader unlocked"
+
+################################################################################
+# Collect characteristics of the device and report.
+can_restore_verity=true
+if [ "2" != "$(get_property partition.system.verified)" ]; then
+ LOG WARNING "device might not support verity"
+ can_restore_verity=false
+fi
enforcing=true
if ! adb_su getenforce </dev/null | grep 'Enforcing' >/dev/null; then
- echo "${YELLOW}[ WARNING ]${NORMAL} device does not have sepolicy in enforcing mode" >&2
+ LOG WARNING "device does not have sepolicy in enforcing mode"
enforcing=false
fi
-# Do something.
-
-# Collect characteristics of the device and report.
-
-D=`get_property ro.serialno`
-[ -n "${D}" ] || D=`get_property ro.boot.serialno`
-[ -z "${D}" -o -n "${ANDROID_SERIAL}" ] || ANDROID_SERIAL=${D}
USB_SERIAL=
if [ -n "${ANDROID_SERIAL}" -a "Darwin" != "${HOSTOS}" ]; then
USB_SERIAL="`find /sys/devices -name serial | grep usb || true`"
@@ -915,35 +930,47 @@
USB_ADDRESS=${USB_SERIAL%/serial}
USB_ADDRESS=usb${USB_ADDRESS##*/}
fi
-[ -z "${ANDROID_SERIAL}${USB_ADDRESS}" ] ||
- USB_DEVICE=`usb_devnum`
- echo "${BLUE}[ INFO ]${NORMAL}" ${ANDROID_SERIAL} ${USB_ADDRESS} ${USB_DEVICE} >&2
+USB_DEVICE=$(usb_devnum)
+[ -z "${ANDROID_SERIAL}${USB_ADDRESS}${USB_DEVICE}" ] ||
+ LOG INFO "${ANDROID_SERIAL} ${USB_ADDRESS} ${USB_DEVICE}"
BUILD_DESCRIPTION=`get_property ro.build.description`
[ -z "${BUILD_DESCRIPTION}" ] ||
- echo "${BLUE}[ INFO ]${NORMAL} ${BUILD_DESCRIPTION}" >&2
+ LOG INFO "${BUILD_DESCRIPTION}"
KERNEL_VERSION="`adb_su cat /proc/version </dev/null 2>/dev/null`"
[ -z "${KERNEL_VERSION}" ] ||
- echo "${BLUE}[ INFO ]${NORMAL} ${KERNEL_VERSION}" >&2
+ LOG INFO "${KERNEL_VERSION}"
ACTIVE_SLOT=`get_active_slot`
[ -z "${ACTIVE_SLOT}" ] ||
- echo "${BLUE}[ INFO ]${NORMAL} active slot is ${ACTIVE_SLOT}" >&2
+ LOG INFO "active slot is ${ACTIVE_SLOT}"
# Acquire list of system partitions
+FSTAB_SUFFIXES=(
+ "$(get_property ro.boot.fstab_suffix)"
+ "$(get_property ro.boot.hardware)"
+ "$(get_property ro.boot.hardware.platform)"
+)
+FSTAB_PATTERN='\.('"$(join_with "|" "${FSTAB_SUFFIXES[@]}")"')$'
+FSTAB_FILE=$(adb_su ls -1 '/vendor/etc/fstab*' </dev/null |
+ grep -E "${FSTAB_PATTERN}" |
+ head -1)
# KISS (assume system partition mount point is "/<partition name>")
-PARTITIONS=`adb_su cat /vendor/etc/fstab* </dev/null |
- grep -v "^[#${SPACE}${TAB}]" |
- skip_administrative_mounts |
- awk '$1 ~ /^[^\/]+$/ && "/"$1 == $2 && $4 ~ /(^|,)ro(,|$)/ { print $1 }' |
- sort -u |
- tr '\n' ' '`
-PARTITIONS="${PARTITIONS:-system vendor}"
+if [ -n "${FSTAB_FILE}" ]; then
+ PARTITIONS=$(adb_su grep -v "^[#${SPACE}${TAB}]" "${FSTAB_FILE}" |
+ skip_administrative_mounts |
+ awk '$1 ~ /^[^\/]+$/ && "/"$1 == $2 && $4 ~ /(^|,)ro(,|$)/ { print $1 }' |
+ sort -u |
+ tr '\n' ' ')
+else
+ PARTITIONS="system vendor"
+fi
+
# KISS (we do not support sub-mounts for system partitions currently)
-MOUNTS="`for i in ${PARTITIONS}; do
- echo /${i}
- done |
- tr '\n' ' '`"
-echo "${BLUE}[ INFO ]${NORMAL} System Partitions list: ${PARTITIONS}" >&2
+# Ensure /system and /vendor mountpoints are in mounts list
+MOUNTS=$(for i in system vendor ${PARTITIONS}; do
+ echo "/${i}"
+ done | sort -u | tr '\n' ' ')
+LOG INFO "System Partitions list: ${PARTITIONS}"
# Report existing partition sizes
adb_sh ls -l /dev/block/by-name/ /dev/block/mapper/ </dev/null 2>/dev/null |
@@ -964,425 +991,388 @@
;;
esac
size=`adb_su cat /sys/block/${device}/size 2>/dev/null </dev/null` &&
- size=`expr ${size} / 2` &&
- echo "${BLUE}[ INFO ]${NORMAL} partition ${name} device ${device} size ${size}K" >&2
+ size=$(( ${size} / 2 )) &&
+ LOG INFO "partition ${name} device ${device} size ${size}K"
done
+restore() {
+ LOG INFO "restoring device"
+ inFastboot &&
+ fastboot reboot &&
+ adb_wait "${ADB_WAIT}" ||
+ true
+ if ! inAdb; then
+ LOG ERROR "expect adb device"
+ return 1
+ fi
+ adb_root || true
+ local reboot=false
+ if surgically_wipe_overlayfs; then
+ reboot=true
+ fi
+ if ${can_restore_verity}; then
+ if ! adb enable-verity; then
+ LOG ERROR "adb enable-verity"
+ return 1
+ fi
+ LOG INFO "restored verity"
+ reboot=true
+ fi >&2
+ if ${reboot}; then
+ adb_reboot
+ fi
+}
+
# If reboot too soon after fresh flash, could trip device update failure logic
if ${screen_wait}; then
- echo "${YELLOW}[ WARNING ]${NORMAL} waiting for screen to come up. Consider --no-wait-screen option" >&2
+ LOG INFO "waiting for screen to come up. Consider --no-wait-screen option"
fi
if ! wait_for_screen && ${screen_wait}; then
screen_wait=false
- echo "${YELLOW}[ WARNING ]${NORMAL} not healthy, no launcher, skipping wait for screen" >&2
+ LOG WARNING "not healthy, no launcher, skipping wait for screen"
fi
-# Can we test remount -R command?
-OVERLAYFS_BACKING="cache mnt/scratch"
-overlayfs_supported=true
-if [ "orange" != "`get_property ro.boot.verifiedbootstate`" -o \
- "2" != "`get_property partition.system.verified`" ]; then
- restore() {
- ${overlayfs_supported} || return 0
- inFastboot &&
- fastboot reboot &&
- adb_wait ${ADB_WAIT} ||
- true
- if inAdb; then
- reboot=false
- for d in ${OVERLAYFS_BACKING}; do
- if adb_test -d /${d}/overlay; then
- adb_su rm -rf /${d}/overlay </dev/null
- reboot=true
- fi
- done
- if ${reboot}; then
- adb_reboot &&
- adb_wait ${ADB_WAIT}
- fi
- fi
- }
-else
- restore() {
- ${overlayfs_supported} || return 0
- inFastboot &&
- fastboot reboot &&
- adb_wait ${ADB_WAIT} ||
- true
- inAdb &&
- adb_root &&
- adb enable-verity >/dev/null 2>/dev/null &&
- adb_reboot &&
- adb_wait ${ADB_WAIT}
- }
-
- echo "${GREEN}[ RUN ]${NORMAL} Testing adb shell su root remount -R command" >&2
-
- avc_check
- T=`adb_date`
- adb_su remount -R system </dev/null
- err=${?}
- if [ "${err}" != 0 ]; then
- echo "${YELLOW}[ WARNING ]${NORMAL} adb shell su root remount -R system = ${err}, likely did not reboot!" >&2
- T="-t ${T}"
- else
- # Rebooted, logcat will be meaningless, and last logcat will likely be clear
- T=""
- fi
- sleep 2
- adb_wait ${ADB_WAIT} ||
- die "waiting for device after adb shell su root remount -R system `usb_status`"
- if [ "orange" != "`get_property ro.boot.verifiedbootstate`" -o \
- "2" = "`get_property partition.system.verified`" ]; then
- die ${T} "remount -R command failed
-${INDENT}ro.boot.verifiedbootstate=\"`get_property ro.boot.verifiedbootstate`\"
-${INDENT}partition.system.verified=\"`get_property partition.system.verified`\""
- fi
-
- echo "${GREEN}[ OK ]${NORMAL} adb shell su root remount -R command" >&2
-fi
-
-echo "${GREEN}[ RUN ]${NORMAL} Testing kernel support for overlayfs" >&2
+################################################################################
+LOG RUN "Checking current overlayfs status"
adb_wait || die "wait for device failed"
-adb_root ||
- die "initial setup"
-
-adb_test -d /sys/module/overlay ||
- adb_sh grep "nodev${TAB}overlay" /proc/filesystems </dev/null >/dev/null 2>/dev/null &&
- echo "${GREEN}[ OK ]${NORMAL} overlay module present" >&2 ||
- (
- echo "${YELLOW}[ WARNING ]${NORMAL} overlay module not present" >&2 &&
- false
- ) ||
- overlayfs_supported=false
-if ${overlayfs_supported}; then
- adb_test -f /sys/module/overlay/parameters/override_creds &&
- echo "${GREEN}[ OK ]${NORMAL} overlay module supports override_creds" >&2 ||
- case `adb_sh uname -r </dev/null` in
- 4.[456789].* | 4.[1-9][0-9]* | [56789].*)
- echo "${YELLOW}[ WARNING ]${NORMAL} overlay module does not support override_creds" >&2 &&
- overlayfs_supported=false
- ;;
- *)
- echo "${GREEN}[ OK ]${NORMAL} overlay module uses caller's creds" >&2
- ;;
- esac
-fi
-
-echo "${GREEN}[ RUN ]${NORMAL} Checking current overlayfs status" >&2
+adb_root || die "adb root failed"
# We can not universally use adb enable-verity to ensure device is
# in a overlayfs disabled state since it can prevent reboot on
# devices that remount the physical content rather than overlayfs.
# So lets do our best to surgically wipe the overlayfs state without
# having to go through enable-verity transition.
-reboot=false
-for d in ${OVERLAYFS_BACKING}; do
- if adb_test -d /${d}/overlay; then
- echo "${YELLOW}[ WARNING ]${NORMAL} /${d}/overlay is setup, surgically wiping" >&2
- adb_sh rm -rf /${d}/overlay </dev/null ||
- die "/${d}/overlay wipe"
- reboot=true
- fi
-done
-if ${reboot}; then
- echo "${YELLOW}[ WARNING ]${NORMAL} rebooting before test" >&2
- adb_reboot &&
- adb_wait ${ADB_WAIT} ||
- die "lost device after reboot after wipe `usb_status`"
+if surgically_wipe_overlayfs; then
+ LOG WARNING "rebooting before test"
+ adb_reboot ||
+ die "lost device after reboot after overlay wipe $(usb_status)"
adb_root ||
die "lost device after elevation to root after wipe `usb_status`"
fi
-D=`adb_sh df -k </dev/null` &&
- H=`echo "${D}" | head -1` &&
- D=`echo "${D}" | grep -v " /vendor/..*$" | grep "^overlay "` &&
- echo "${H}" &&
- echo "${D}" &&
- echo "${YELLOW}[ WARNING ]${NORMAL} overlays present before setup" >&2 ||
- echo "${GREEN}[ OK ]${NORMAL} no overlay present before setup" >&2
+is_overlayfs_mounted &&
+ die "overlay takeover unexpected at this phase"
+
overlayfs_needed=true
-D=`adb_sh cat /proc/mounts </dev/null |
- skip_administrative_mounts data`
-if echo "${D}" | grep /dev/root >/dev/null; then
- D=`echo / /
- echo "${D}" | grep -v /dev/root`
-fi
-D=`echo "${D}" | cut -s -d' ' -f1 | sort -u`
+data_device=$(adb_sh awk '$2 == "/data" { print $1; exit }' /proc/mounts)
+D=$(adb_sh grep " ro," /proc/mounts </dev/null |
+ grep -v "^${data_device}" |
+ skip_administrative_mounts |
+ awk '{ print $1 }' |
+ sed 's|/dev/root|/|' |
+ sort -u)
no_dedupe=true
for d in ${D}; do
adb_sh tune2fs -l $d </dev/null 2>&1 |
grep "Filesystem features:.*shared_blocks" >/dev/null &&
no_dedupe=false
done
-D=`adb_sh df -k ${D} </dev/null |
- sed 's@\([%] /\)\(apex\|bionic\|system\|vendor\)/[^ ][^ ]*$@\1@'`
-echo "${D}"
+D=$(adb_sh df -k ${D} </dev/null)
+echo "${D}" >&2
if [ X"${D}" = X"${D##* 100[%] }" ] && ${no_dedupe} ; then
overlayfs_needed=false
# if device does not need overlays, then adb enable-verity will brick device
- restore() {
- ${overlayfs_supported} || return 0
- inFastboot &&
- fastboot reboot &&
- adb_wait ${ADB_WAIT}
- inAdb &&
- adb_wait ${ADB_WAIT}
- }
-elif ! ${overlayfs_supported}; then
- die "need overlayfs, but do not have it"
+ can_restore_verity=false
fi
+LOG OK "no overlay present before setup"
-echo "${GREEN}[ RUN ]${NORMAL} disable verity" >&2
+################################################################################
+# Precondition is overlayfs *not* setup.
+LOG RUN "Testing adb disable-verity -R"
-T=`adb_date`
-H=`adb disable-verity 2>&1`
-err=${?}
-L=
-D="${H%?Now reboot your device for settings to take effect*}"
-if [ X"${D}" != X"${D##*[Uu]sing overlayfs}" ]; then
- echo "${GREEN}[ OK ]${NORMAL} using overlayfs" >&2
+T=$(adb_date)
+adb_su disable-verity -R >&2 ||
+ die -t "${T}" "disable-verity -R failed"
+sleep 2
+adb_wait "${ADB_WAIT}" ||
+ die "lost device after adb disable-verity -R $(usb_status)"
+
+if [ "2" = "$(get_property partition.system.verified)" ]; then
+ LOG ERROR "partition.system.verified=$(get_property partition.system.verified)"
+ die "verity not disabled after adb disable-verity -R"
fi
-if [ ${err} != 0 ]; then
- echo "${H}"
- ( [ -n "${L}" ] && echo "${L}" && false ) ||
- die -t "${T}" "disable-verity"
+if ${overlayfs_needed}; then
+ is_overlayfs_mounted ||
+ die -d "no overlay takeover after adb disable-verity -R"
+ LOG OK "overlay takeover after adb disable-verity -R"
fi
-rebooted=false
-if [ X"${D}" != X"${H}" ]; then
- echo "${H}"
- if [ X"${D}" != X"${D##*setup failed}" ]; then
- echo "${YELLOW}[ WARNING ]${NORMAL} overlayfs setup whined" >&2
- fi
- D=`adb_sh df -k </dev/null` &&
- H=`echo "${D}" | head -1` &&
- D=`echo "${D}" | grep -v " /vendor/..*$" | grep "^overlay " || true` &&
- [ -z "${D}" ] ||
- ( echo "${H}" && echo "${D}" && false ) ||
- die -t ${T} "overlay takeover unexpected at this phase"
- echo "${GREEN}[ INFO ]${NORMAL} rebooting as requested" >&2
- L=`adb_logcat -b all -v nsec -t ${T} 2>&1`
- adb_reboot &&
- adb_wait ${ADB_WAIT} ||
- die "lost device after reboot requested `usb_status`"
- adb_root ||
- die "lost device after elevation to root `usb_status`"
- rebooted=true
- # re-disable verity to see the setup remarks expected
- T=`adb_date`
- H=`adb disable-verity 2>&1`
- err=${?}
- D="${H%?Now reboot your device for settings to take effect*}"
- if [ X"${D}" != X"${D##*[Uu]sing overlayfs}" ]; then
- echo "${GREEN}[ OK ]${NORMAL} using overlayfs" >&2
- fi
- if [ ${err} != 0 ]; then
- T=
- fi
-fi
-if ${overlayfs_supported} && ${overlayfs_needed} && [ X"${D}" != X"${D##*setup failed}" ]; then
- echo "${D}"
- ( [ -n "${L}" ] && echo "${L}" && false ) ||
- die -t "${T}" "setup for overlay"
-fi
-if [ X"${D}" != X"${D##*Successfully disabled verity}" ]; then
- echo "${H}"
- D=`adb_sh df -k </dev/null` &&
- H=`echo "${D}" | head -1` &&
- D=`echo "${D}" | grep -v " /vendor/..*$" | grep "^overlay " || true` &&
- [ -z "${D}" ] ||
- ( echo "${H}" && echo "${D}" && false ) ||
- ( [ -n "${L}" ] && echo "${L}" && false ) ||
- die -t "${T}" "overlay takeover unexpected"
- [ -n "${L}" ] && echo "${L}"
- die -t "${T}" "unexpected report of verity being disabled a second time"
-elif ${rebooted}; then
- echo "${GREEN}[ OK ]${NORMAL} verity already disabled" >&2
+LOG OK "adb disable-verity -R"
+
+################################################################################
+LOG RUN "Checking kernel has overlayfs required patches"
+
+adb_root || die "adb root"
+if adb_test -d /sys/module/overlay ||
+ adb_sh grep -q "nodev${TAB}overlay" /proc/filesystems; then
+ LOG OK "overlay module present"
else
- echo "${YELLOW}[ WARNING ]${NORMAL} verity already disabled" >&2
+ LOG INFO "overlay module not present"
+fi
+if is_overlayfs_mounted 2>/dev/null; then
+ if adb_test -f /sys/module/overlay/parameters/override_creds; then
+ LOG OK "overlay module supports override_creds"
+ else
+ case "$(adb_sh uname -r </dev/null)" in
+ 4.[456789].* | 4.[1-9][0-9]* | [56789].*)
+ die "overlay module does not support override_creds"
+ ;;
+ *)
+ LOG OK "overlay module uses caller's creds"
+ ;;
+ esac
+ fi
fi
-echo "${GREEN}[ RUN ]${NORMAL} remount" >&2
+################################################################################
+# Precondition is a verity-disabled device with overlayfs already setup.
+LOG RUN "Testing raw remount commands"
+
+adb_sh grep -qE " (/system|/) [^ ]* rw," /proc/mounts </dev/null &&
+ die "/system is not RO"
+adb_sh grep -q " /vendor [^ ]* rw," /proc/mounts </dev/null &&
+ die "/vendor is not RO"
+
+T=$(adb_date)
+adb_su mount -o remount,rw /vendor ||
+ die -t "${T}" "mount -o remount,rw /vendor"
+adb_sh grep -q " /vendor [^ ]* rw," /proc/mounts </dev/null ||
+ die "/vendor is not RW after mount -o remount,rw"
+LOG OK "mount -o remount,rw"
+
+T=$(adb_date)
+adb_su mount -o remount,ro /vendor ||
+ die -t "${T}" "mount -o remount,ro /vendor"
+adb_sh grep -q " /vendor [^ ]* rw," /proc/mounts </dev/null &&
+ die "/vendor is not RO after mount -o remount,ro"
+LOG OK "mount -o remount,ro"
+
+T=$(adb_date)
+adb_su remount vendor >&2 ||
+ die -t "${T}" "adb remount vendor"
+adb_sh grep -q " /vendor [^ ]* rw," /proc/mounts </dev/null ||
+ die -t "${T}" "/vendor is not RW after adb remount vendor"
+adb_sh grep -qE " (/system|/) [^ ]* rw," /proc/mounts </dev/null &&
+ die -t "${T}" "/system is not RO after adb remount vendor"
+LOG OK "adb remount vendor"
+
+LOG INFO "Restoring device RO state and destroying overlayfs"
+T=$(adb_date)
+adb_su mount -o remount,ro /vendor ||
+ die -t "${T}" "mount -o remount,ro /vendor"
+if surgically_wipe_overlayfs; then
+ adb_reboot ||
+ die "lost device after reboot after overlay wipe $(usb_status)"
+fi
+is_overlayfs_mounted &&
+ die "overlay takeover unexpected at this phase"
+
+################################################################################
+# Precondition is a verity-disabled device with overlayfs *not* setup.
+LOG RUN "Testing adb remount performs overlayfs setup from scratch"
+
+adb_sh grep -q " /vendor [^ ]* rw," /proc/mounts </dev/null &&
+ die "/vendor is not RO"
+T=$(adb_date)
+adb_su remount vendor >&2 ||
+ die -t "${T}" "adb remount vendor from scratch"
+if ${overlayfs_needed}; then
+ is_overlayfs_mounted /vendor ||
+ die -t "${T}" "expected overlay takeover /vendor"
+ is_overlayfs_mounted /system 2>/dev/null &&
+ die -t "${T}" "unexpected overlay takeover /system"
+fi
+adb_sh grep -q " /vendor [^ ]* rw," /proc/mounts </dev/null ||
+ die -t "${T}" "/vendor is not RW after adb remount vendor"
+adb_sh grep -qE " (/system|/) [^ ]* rw," /proc/mounts </dev/null &&
+ die -t "${T}" "/system is not RO after adb remount vendor"
+LOG OK "adb remount from scratch"
+
+################################################################################
+# Precondition is overlayfs partially setup by previous test.
+LOG RUN "Testing adb remount -R"
+
+T=$(adb_date)
+adb_su remount -R </dev/null >&2 ||
+ die -t "${T}" "adb remount -R failed"
+sleep 2
+adb_wait "${ADB_WAIT}" ||
+ die "lost device after adb remount -R $(usb_status)"
+
+if [ "2" = "$(get_property partition.system.verified)" ]; then
+ LOG ERROR "partition.system.verified=$(get_property partition.system.verified)"
+ die "verity not disabled after adb remount -R"
+fi
+if ${overlayfs_needed}; then
+ is_overlayfs_mounted /system ||
+ die -d "expected overlay takeover /system"
+ is_overlayfs_mounted /vendor 2>/dev/null ||
+ die -d "expected overlay takeover /vendor"
+ LOG OK "overlay takeover after adb remount -R"
+fi
+LOG OK "adb remount -R"
+
+# For devices using overlayfs, remount -R should reboot after overlayfs setup.
+# For legacy device, manual reboot to ensure device clean state.
+if ! ${overlayfs_needed}; then
+ LOG WARNING "Reboot to RO (device doesn't use overlayfs)"
+ adb_reboot ||
+ die "lost device after reboot to RO $(usb_status)"
+fi
+
+################################################################################
+# Precondition is a verity-disabled device with overlayfs already setup.
+LOG RUN "Testing adb remount RW"
# Feed log with selinux denials as baseline before overlays
adb_unroot
adb_sh find ${MOUNTS} </dev/null >/dev/null 2>/dev/null || true
adb_root
-D=`adb remount 2>&1`
-ret=${?}
-echo "${D}"
-[ ${ret} != 0 ] ||
- [ X"${D}" = X"${D##*remount failed}" ] ||
- ( [ -n "${L}" ] && echo "${L}" && false ) ||
- die -t "${T}" "adb remount failed"
-D=`adb_sh df -k </dev/null` &&
- H=`echo "${D}" | head -1` &&
- D=`echo "${D}" | skip_unrelated_mounts | grep "^overlay "` ||
- ( [ -n "${L}" ] && echo "${L}" && false )
-ret=${?}
-uses_dynamic_scratch=false
-scratch_partition=
-virtual_ab=`get_property ro.virtual_ab.enabled`
-if ${overlayfs_needed}; then
- if [ ${ret} != 0 ]; then
- die -t ${T} "overlay takeover failed"
- fi
- echo "${D}" | grep "^overlay .* /system\$" >/dev/null ||
- echo "${YELLOW}[ WARNING ]${NORMAL} overlay takeover not complete" >&2
- if [ -z "${virtual_ab}" ]; then
- scratch_partition=scratch
- fi
- if echo "${D}" | grep " /mnt/scratch" >/dev/null; then
- echo "${BLUE}[ INFO ]${NORMAL} using ${scratch_partition} dynamic partition for overrides" >&2
- fi
- M=`adb_sh cat /proc/mounts </dev/null |
- sed -n 's@\([^ ]*\) /mnt/scratch \([^ ]*\) .*@\2 on \1@p'`
- [ -n "${M}" ] &&
- echo "${BLUE}[ INFO ]${NORMAL} scratch filesystem ${M}"
- uses_dynamic_scratch=true
- if [ "${M}" != "${M##*/dev/block/by-name/}" ]; then
- uses_dynamic_scratch=false
- scratch_partition="${M##*/dev/block/by-name/}"
- fi
- scratch_size=`adb_sh df -k /mnt/scratch </dev/null 2>/dev/null |
- while read device kblocks used available use mounted on; do
- if [ "/mnt/scratch" = "\${mounted}" ]; then
- echo \${kblocks}
- fi
- done` &&
- [ -n "${scratch_size}" ] ||
- die "scratch size"
- echo "${BLUE}[ INFO ]${NORMAL} scratch size ${scratch_size}KB" >&2
- for d in ${OVERLAYFS_BACKING}; do
- if adb_test -d /${d}/overlay/system/upper; then
- echo "${BLUE}[ INFO ]${NORMAL} /${d}/overlay is setup" >&2
- fi
- done
+adb_sh grep -qE " (/system|/) [^ ]* rw," /proc/mounts </dev/null &&
+ die "/system is not RO"
+adb_sh grep -q " /vendor [^ ]* rw," /proc/mounts </dev/null &&
+ die "/vendor is not RO"
- echo "${H}" &&
- echo "${D}" &&
- echo "${D}" | grep "^overlay .* /system\$" >/dev/null ||
- die "overlay takeover after remount"
- !(adb_sh grep "^overlay " /proc/mounts </dev/null |
- skip_unrelated_mounts |
- grep " overlay ro,") ||
- die "remount overlayfs missed a spot (ro)"
- !(adb_sh grep -v noatime /proc/mounts </dev/null |
- skip_administrative_mounts data |
- skip_unrelated_mounts |
- grep -v ' ro,') ||
- die "mounts are not noatime"
- D=`adb_sh grep " rw," /proc/mounts </dev/null |
- skip_administrative_mounts data`
- if echo "${D}" | grep /dev/root >/dev/null; then
- D=`echo / /
- echo "${D}" | grep -v /dev/root`
- fi
- D=`echo "${D}" | cut -s -d' ' -f1 | sort -u`
- bad_rw=false
- for d in ${D}; do
- if adb_sh tune2fs -l $d </dev/null 2>&1 |
- grep "Filesystem features:.*shared_blocks" >/dev/null; then
- bad_rw=true
- else
- d=`adb_sh df -k ${D} </dev/null |
- sed 's@\([%] /\)\(apex\|bionic\|system\|vendor\)/[^ ][^ ]*$@\1@'`
- [ X"${d}" = X"${d##* 100[%] }" ] ||
- bad_rw=true
- fi
- done
- [ -z "${D}" ] ||
- D=`adb_sh df -k ${D} </dev/null |
- sed -e 's@\([%] /\)\(apex\|bionic\|system\|vendor\)/[^ ][^ ]*$@\1@' \
- -e 's/^Filesystem /Filesystem (rw) /'`
- [ -z "${D}" ] || echo "${D}"
- ${bad_rw} && die "remount overlayfs missed a spot (rw)"
+T=$(adb_date)
+adb remount >&2 ||
+ die -t "${T}" "adb remount"
+adb_sh grep -qE " (/system|/) [^ ]* rw," /proc/mounts </dev/null ||
+ die -t "${T}" "/system is not RW"
+adb_sh grep -q " /vendor [^ ]* rw," /proc/mounts </dev/null ||
+ die -t "${T}" "/vendor is not RW"
+
+if ${overlayfs_needed}; then
+ is_overlayfs_mounted || die -t "${T}" "expected overlay takeover"
else
- if [ ${ret} = 0 ]; then
- die -t ${T} "unexpected overlay takeover"
- fi
+ is_overlayfs_mounted && die -t "${T}" "unexpected overlay takeover"
fi
-# Check something.
+# If scratch_partition && uses_dynamic_scratch, then scratch is on super.
+# If scratch_partition && !uses_dynamic_scratch, then scratch is super_other, system_other.
+# If !scratch_partition, then scratch is on /data via image_manager.
+uses_dynamic_scratch=false
+scratch_partition=
+virtual_ab=$(get_property ro.virtual_ab.enabled)
+if ${overlayfs_needed}; then
+ M=$(adb_sh cat /proc/mounts </dev/null |
+ awk '$2 == "/mnt/scratch" { print $1, $3; exit }')
+ [ -z "${M}" ] && die "cannot find any scratch device mounted on /mnt/scratch"
-echo "${GREEN}[ RUN ]${NORMAL} push content to ${MOUNTS}" >&2
+ scratch_device=$(echo "${M}" | awk '{ print $1 }')
+ scratch_filesystem=$(echo "${M}" | awk '{ print $2 }')
+ scratch_size=$(adb_sh df -k "${scratch_device}" </dev/null |
+ tail +2 | head -1 | awk '{ print $2 }')
+ [ -z "${scratch_size}" ] && die "cannot get size of scratch device (${scratch_device})"
+ if [ -n "${virtual_ab}" ]; then
+ LOG INFO "using dynamic scratch partition on /data (VAB device)"
+ elif [[ "${scratch_device}" == /dev/block/by-name/* ]]; then
+ scratch_partition="${scratch_device##/dev/block/by-name/}"
+ LOG INFO "using physical scratch partition ${scratch_partition}"
+ else
+ uses_dynamic_scratch=true
+ scratch_partition=scratch
+ LOG INFO "using dynamic scratch partition on super"
+ fi
+ LOG INFO "scratch device ${scratch_device} filesystem ${scratch_filesystem} size ${scratch_size}KiB"
+
+ for d in ${OVERLAYFS_BACKING}; do
+ if adb_test -d /${d}/overlay/system/upper; then
+ LOG INFO "/${d}/overlay is setup"
+ fi
+ done
+
+ data_device=$(adb_sh awk '$2 == "/data" { print $1; exit }' /proc/mounts)
+ is_overlayfs_mounted /system 2>/dev/null ||
+ die -t "${T}" "expected overlay to takeover /system after remount"
+ # KISS (we do not support sub-mounts for system partitions currently)
+ adb_sh grep "^overlay " /proc/mounts </dev/null |
+ grep -vE "^overlay.* /(apex|system|vendor)/[^ ]" |
+ grep " overlay ro," &&
+ die "expected overlay to be RW after remount"
+ adb_sh grep -v noatime /proc/mounts </dev/null |
+ grep -v "^${data_device}" |
+ skip_administrative_mounts |
+ grep -v ' ro,' &&
+ die "mounts are not noatime"
+
+ D=$(adb_sh grep " rw," /proc/mounts </dev/null |
+ grep -v "^${data_device}" |
+ skip_administrative_mounts |
+ awk '{ print $1 }' |
+ sed 's|/dev/root|/|' |
+ sort -u)
+ if [ -n "${D}" ]; then
+ adb_sh df -k ${D} </dev/null |
+ sed -e 's/^Filesystem /Filesystem (rw) /'
+ fi >&2
+ for d in ${D}; do
+ if adb_sh tune2fs -l "${d}" </dev/null 2>&1 | grep -q "Filesystem features:.*shared_blocks" ||
+ adb_sh df -k "${d}" | grep -q " 100% "; then
+ die "remount overlayfs missed a spot (rw)"
+ fi
+ done
+fi
+
+LOG OK "adb remount RW"
+
+################################################################################
+LOG RUN "push content to ${MOUNTS}"
+
+adb_root || die "adb root"
A="Hello World! $(date)"
-for i in ${MOUNTS}; do
+for i in ${MOUNTS} /system/priv-app; do
echo "${A}" | adb_sh cat - ">${i}/hello"
B="`adb_cat ${i}/hello`" ||
die "${i#/} hello"
check_eq "${A}" "${B}" ${i} before reboot
done
-echo "${A}" | adb_sh cat - ">/system/priv-app/hello"
-B="`adb_cat /system/priv-app/hello`" ||
- die "system priv-app hello"
-check_eq "${A}" "${B}" /system/priv-app before reboot
-SYSTEM_DEVT=`adb_sh stat --format=%D /system/hello </dev/null`
-VENDOR_DEVT=`adb_sh stat --format=%D /vendor/hello </dev/null`
SYSTEM_INO=`adb_sh stat --format=%i /system/hello </dev/null`
VENDOR_INO=`adb_sh stat --format=%i /vendor/hello </dev/null`
-BASE_SYSTEM_DEVT=`adb_sh stat --format=%D /system/bin/stat </dev/null`
-BASE_VENDOR_DEVT=`adb_sh stat --format=%D /vendor/bin/stat </dev/null`
-check_eq "${SYSTEM_DEVT%[0-9a-fA-F][0-9a-fA-F]}" "${VENDOR_DEVT%[0-9a-fA-F][0-9a-fA-F]}" vendor and system devt
check_ne "${SYSTEM_INO}" "${VENDOR_INO}" vendor and system inode
-if ${overlayfs_needed}; then
- check_ne "${SYSTEM_DEVT}" "${BASE_SYSTEM_DEVT}" system devt
- check_ne "${VENDOR_DEVT}" "${BASE_VENDOR_DEVT}" vendor devt
-else
- check_eq "${SYSTEM_DEVT}" "${BASE_SYSTEM_DEVT}" system devt
- check_eq "${VENDOR_DEVT}" "${BASE_VENDOR_DEVT}" vendor devt
-fi
-check_ne "${BASE_SYSTEM_DEVT}" "${BASE_VENDOR_DEVT}" --warning system/vendor devt
-[ -n "${SYSTEM_DEVT%[0-9a-fA-F][0-9a-fA-F]}" ] ||
- echo "${YELLOW}[ WARNING ]${NORMAL} system devt ${SYSTEM_DEVT} major 0" >&2
-[ -n "${VENDOR_DEVT%[0-9a-fA-F][0-9a-fA-F]}" ] ||
- echo "${YELLOW}[ WARNING ]${NORMAL} vendor devt ${VENDOR_DEVT} major 0" >&2
-# Download libc.so, append some garbage, push back, and check if the file
-# is updated.
-tempdir="`mktemp -d`"
-cleanup() {
- rm -rf ${tempdir}
-}
-adb pull /system/lib/bootstrap/libc.so ${tempdir} >/dev/null ||
- die "pull libc.so from device"
-garbage="D105225BBFCB1EB8AB8EBDB7094646F0"
-echo "${garbage}" >> ${tempdir}/libc.so
-adb push ${tempdir}/libc.so /system/lib/bootstrap/libc.so >/dev/null ||
- die "push libc.so to device"
-adb pull /system/lib/bootstrap/libc.so ${tempdir}/libc.so.fromdevice >/dev/null ||
- die "pull libc.so from device"
-diff ${tempdir}/libc.so ${tempdir}/libc.so.fromdevice > /dev/null ||
- die "libc.so differ"
+# Edit build.prop and check if properties are updated.
+system_build_prop_original="${TMPDIR}/system_build.prop.original"
+system_build_prop_modified="${TMPDIR}/system_build.prop.modified"
+system_build_prop_fromdevice="${TMPDIR}/system_build.prop.fromdevice"
+adb pull /system/build.prop "${system_build_prop_original}" >/dev/null ||
+ die "adb pull /system/build.prop"
+# Prepend with extra newline in case the original file doesn't end with a newline.
+cat "${system_build_prop_original}" - <<EOF >"${system_build_prop_modified}"
-echo "${GREEN}[ RUN ]${NORMAL} reboot to confirm content persistent" >&2
+# Properties added by adb remount test
+test.adb.remount.system.build.prop=true
+EOF
+adb push "${system_build_prop_modified}" /system/build.prop >/dev/null ||
+ die "adb push /system/build.prop"
+adb pull /system/build.prop "${system_build_prop_fromdevice}" >/dev/null ||
+ die "adb pull /system/build.prop"
+diff "${system_build_prop_modified}" "${system_build_prop_fromdevice}" >/dev/null ||
+ die "/system/build.prop differs from pushed content"
+
+################################################################################
+LOG RUN "reboot to confirm content persistent"
fixup_from_recovery() {
inRecovery || return 1
- echo "${YELLOW}[ ERROR ]${NORMAL} Device in recovery" >&2
+ LOG ERROR "Device in recovery"
adb reboot </dev/null
adb_wait ${ADB_WAIT}
}
-adb_reboot &&
- adb_wait ${ADB_WAIT} ||
+adb_reboot ||
fixup_from_recovery ||
die "reboot after override content added failed `usb_status`"
if ${overlayfs_needed}; then
- D=`adb_su df -k </dev/null` &&
- H=`echo "${D}" | head -1` &&
- D=`echo "${D}" | grep -v " /vendor/..*$" | grep "^overlay "` ||
- ( echo "${L}" && false ) ||
+ is_overlayfs_mounted ||
die -d "overlay takeover failed after reboot"
adb_su sed -n '1,/overlay \/system/p' /proc/mounts </dev/null |
skip_administrative_mounts |
grep -v ' \(erofs\|squashfs\|ext4\|f2fs\|vfat\) ' &&
- echo "${YELLOW}[ WARNING ]${NORMAL} overlay takeover after first stage init" >&2 ||
- echo "${GREEN}[ OK ]${NORMAL} overlay takeover in first stage init" >&2
+ LOG WARNING "overlay takeover after first stage init" ||
+ LOG OK "overlay takeover in first stage init"
fi
if ${enforcing}; then
@@ -1390,220 +1380,178 @@
die "device not in unroot'd state"
B="`adb_cat /vendor/hello 2>&1`"
check_eq "cat: /vendor/hello: Permission denied" "${B}" vendor after reboot w/o root
- echo "${GREEN}[ OK ]${NORMAL} /vendor content correct MAC after reboot" >&2
+ LOG OK "/vendor content correct MAC after reboot"
# Feed unprivileged log with selinux denials as a result of overlays
wait_for_screen
adb_sh find ${MOUNTS} </dev/null >/dev/null 2>/dev/null || true
fi
# If overlayfs has a nested security problem, this will fail.
-B="`adb_ls /system/`" ||
- die "adb ls /system"
-[ X"${B}" != X"${B#*priv-app}" ] ||
- die "adb ls /system/priv-app"
+adb_sh ls /system >/dev/null || die "ls /system"
+adb_test -d /system/priv-app || die "[ -d /system/priv-app ]"
B="`adb_cat /system/priv-app/hello`"
check_eq "${A}" "${B}" /system/priv-app after reboot
+
# Only root can read vendor if sepolicy permissions are as expected.
-adb_root ||
- die "adb root"
+adb_root || die "adb root"
for i in ${MOUNTS}; do
B="`adb_cat ${i}/hello`"
check_eq "${A}" "${B}" ${i#/} after reboot
- echo "${GREEN}[ OK ]${NORMAL} ${i} content remains after reboot" >&2
+ LOG OK "${i} content remains after reboot"
done
-check_eq "${SYSTEM_DEVT}" "`adb_sh stat --format=%D /system/hello </dev/null`" system devt after reboot
-check_eq "${VENDOR_DEVT}" "`adb_sh stat --format=%D /vendor/hello </dev/null`" vendor devt after reboot
check_eq "${SYSTEM_INO}" "`adb_sh stat --format=%i /system/hello </dev/null`" system inode after reboot
check_eq "${VENDOR_INO}" "`adb_sh stat --format=%i /vendor/hello </dev/null`" vendor inode after reboot
-check_eq "${BASE_SYSTEM_DEVT}" "`adb_sh stat --format=%D /system/bin/stat </dev/null`" --warning base system devt after reboot
-check_eq "${BASE_VENDOR_DEVT}" "`adb_sh stat --format=%D /vendor/bin/stat </dev/null`" --warning base vendor devt after reboot
-check_eq "${BASE_SYSTEM_DEVT}" "`adb_sh stat --format=%D /system/xbin/su </dev/null`" --warning devt for su after reboot
# Feed log with selinux denials as a result of overlays
adb_sh find ${MOUNTS} </dev/null >/dev/null 2>/dev/null || true
-# Check if the updated libc.so is persistent after reboot.
-adb_root &&
- adb pull /system/lib/bootstrap/libc.so ${tempdir}/libc.so.fromdevice >/dev/null ||
- die "pull libc.so from device"
-diff ${tempdir}/libc.so ${tempdir}/libc.so.fromdevice > /dev/null || die "libc.so differ"
-rm -rf ${tempdir}
-cleanup() {
- true
-}
-echo "${GREEN}[ OK ]${NORMAL} /system/lib/bootstrap/libc.so content remains after reboot" >&2
+# Check if the updated build.prop is persistent after reboot.
+check_eq "true" "$(get_property 'test.adb.remount.system.build.prop')" "load modified build.prop"
+adb pull /system/build.prop "${system_build_prop_fromdevice}" >/dev/null ||
+ die "adb pull /system/build.prop"
+diff "${system_build_prop_modified}" "${system_build_prop_fromdevice}" >/dev/null ||
+ die "/system/build.prop differs from pushed content"
+LOG OK "/system/build.prop content remains after reboot"
-echo "${GREEN}[ RUN ]${NORMAL} flash vendor, confirm its content disappears" >&2
+################################################################################
+LOG RUN "flash vendor, and confirm vendor override disappears"
-H=`adb_sh echo '${HOSTNAME}' </dev/null 2>/dev/null`
-is_bootloader_fastboot=false
+is_bootloader_fastboot=true
# cuttlefish?
-[ X"${H}" != X"${H#vsoc}" ] || is_bootloader_fastboot=true
+[[ "$(get_property ro.product.device)" == vsoc* ]] &&
+ is_bootloader_fastboot=false
is_userspace_fastboot=false
if ! ${is_bootloader_fastboot}; then
- echo "${YELLOW}[ WARNING ]${NORMAL} does not support fastboot, skipping"
-elif [ -z "${ANDROID_PRODUCT_OUT}" ]; then
- echo "${YELLOW}[ WARNING ]${NORMAL} build tree not setup, skipping"
-elif [ ! -s "${ANDROID_PRODUCT_OUT}/vendor.img" ]; then
- echo "${YELLOW}[ WARNING ]${NORMAL} vendor image missing, skipping"
-elif [ "${ANDROID_PRODUCT_OUT}" = "${ANDROID_PRODUCT_OUT%*/${H}}" ]; then
- echo "${YELLOW}[ WARNING ]${NORMAL} wrong vendor image, skipping"
-elif [ -z "${ANDROID_HOST_OUT}" ]; then
- echo "${YELLOW}[ WARNING ]${NORMAL} please run lunch, skipping"
-elif ! (
- adb_cat /vendor/build.prop |
- cmp -s ${ANDROID_PRODUCT_OUT}/vendor/build.prop
- ) >/dev/null 2>/dev/null; then
- echo "${YELLOW}[ WARNING ]${NORMAL} vendor image signature mismatch, skipping"
+ LOG WARNING "does not support fastboot flash, skipping"
else
wait_for_screen
+ adb_root || die "adb root"
+
+ VENDOR_DEVICE_CANDIDATES=(
+ "/dev/block/mapper/vendor"{_${ACTIVE_SLOT},}
+ "/dev/block/by-name/vendor"{_${ACTIVE_SLOT},}
+ )
+ for b in "${VENDOR_DEVICE_CANDIDATES[@]}"; do
+ if adb_test -e "${b}"; then
+ adb pull "${b}" "${TMPDIR}/vendor.img" || die "adb pull ${b}"
+ LOG INFO "pulled ${b} from device as vendor.img"
+ break
+ fi
+ done
+ [ -f "${TMPDIR}/vendor.img" ] ||
+ die "cannot find block device of vendor partition"
+
avc_check
adb reboot fastboot </dev/null ||
die "fastbootd not supported (wrong adb in path?)"
any_wait ${ADB_WAIT} &&
inFastboot ||
die "reboot into fastboot to flash vendor `usb_status` (bad bootloader?)"
- fastboot flash vendor ||
+ fastboot flash vendor "${TMPDIR}/vendor.img" ||
( fastboot reboot && false) ||
die "fastboot flash vendor"
+ LOG OK "flashed vendor"
+
fastboot_getvar is-userspace yes &&
is_userspace_fastboot=true
- if [ -n "${scratch_paritition}" ]; then
+ # check ${scratch_partition} via fastboot
+ if [ -n "${scratch_partition}" ]; then
fastboot_getvar partition-type:${scratch_partition} raw ||
( fastboot reboot && false) ||
die "fastboot can not see ${scratch_partition} parameters"
if ${uses_dynamic_scratch}; then
- # check ${scratch_partition} via fastboot
fastboot_getvar has-slot:${scratch_partition} no &&
fastboot_getvar is-logical:${scratch_partition} yes ||
( fastboot reboot && false) ||
die "fastboot can not see ${scratch_partition} parameters"
+ LOG INFO "expect fastboot erase ${scratch_partition} to fail"
+ fastboot erase ${scratch_partition} &&
+ ( fastboot reboot || true) &&
+ die "fastboot can erase ${scratch_partition}"
else
fastboot_getvar is-logical:${scratch_partition} no ||
( fastboot reboot && false) ||
die "fastboot can not see ${scratch_partition} parameters"
- fi
- if ! ${uses_dynamic_scratch}; then
fastboot reboot-bootloader ||
- die "Reboot into fastboot"
+ die "fastboot reboot bootloader"
fi
- if ${uses_dynamic_scratch}; then
- echo "${BLUE}[ INFO ]${NORMAL} expect fastboot erase ${scratch_partition} to fail" >&2
- fastboot erase ${scratch_partition} &&
- ( fastboot reboot || true) &&
- die "fastboot can erase ${scratch_partition}"
- fi
- echo "${BLUE}[ INFO ]${NORMAL} expect fastboot format ${scratch_partition} to fail" >&2
+ LOG INFO "expect fastboot format ${scratch_partition} to fail"
fastboot format ${scratch_partition} &&
( fastboot reboot || true) &&
die "fastboot can format ${scratch_partition}"
fi
- fastboot reboot ||
- die "can not reboot out of fastboot"
- echo "${YELLOW}[ WARNING ]${NORMAL} adb after fastboot"
+
+ fastboot reboot || die "cannot reboot out of fastboot"
+ LOG INFO "reboot from fastboot"
adb_wait ${ADB_WAIT} ||
fixup_from_recovery ||
- die "did not reboot after formatting ${scratch_partition} `usb_status`"
+ die "cannot reboot after flash vendor $(usb_status)"
if ${overlayfs_needed}; then
- adb_root &&
- D=`adb_sh df -k </dev/null` &&
- H=`echo "${D}" | head -1` &&
- D=`echo "${D}" | skip_unrelated_mounts | grep "^overlay "` &&
- echo "${H}" &&
- echo "${D}" &&
- echo "${D}" | grep "^overlay .* /system\$" >/dev/null ||
+ is_overlayfs_mounted /system ||
die "overlay /system takeover after flash vendor"
- echo "${D}" | grep "^overlay .* /vendor\$" >/dev/null &&
+ if is_overlayfs_mounted /vendor 2>/dev/null; then
if ${is_userspace_fastboot}; then
die "overlay supposed to be minus /vendor takeover after flash vendor"
else
- echo "${YELLOW}[ WARNING ]${NORMAL} user fastboot missing required to invalidate, ignoring a failure" >&2
- echo "${YELLOW}[ WARNING ]${NORMAL} overlay supposed to be minus /vendor takeover after flash vendor" >&2
+ LOG WARNING "fastbootd missing required to invalidate, ignoring a failure"
+ LOG WARNING "overlay supposed to be minus /vendor takeover after flash vendor"
fi
+ fi
fi
- B="`adb_cat /system/hello`"
- check_eq "${A}" "${B}" system after flash vendor
- B="`adb_ls /system/`" ||
- die "adb ls /system"
- [ X"${B}" != X"${B#*priv-app}" ] ||
- die "adb ls /system/priv-app"
- B="`adb_cat /system/priv-app/hello`"
- check_eq "${A}" "${B}" system/priv-app after flash vendor
- adb_root ||
- die "adb root"
- B="`adb_cat /vendor/hello`"
- if ${is_userspace_fastboot} || ! ${overlayfs_needed}; then
- check_eq "cat: /vendor/hello: No such file or directory" "${B}" \
- vendor content after flash vendor
- else
- echo "${YELLOW}[ WARNING ]${NORMAL} user fastboot missing required to invalidate, ignoring a failure" >&2
- check_eq "cat: /vendor/hello: No such file or directory" "${B}" \
- --warning vendor content after flash vendor
+ check_eq "${A}" "$(adb_cat /system/hello)" "/system content after flash vendor"
+ check_eq "${SYSTEM_INO}" "$(adb_sh stat --format=%i /system/hello </dev/null)" "system inode after flash vendor"
+ adb_sh ls /system >/dev/null || die "ls /system"
+ adb_test -d /system/priv-app || die "[ -d /system/priv-app ]"
+ check_eq "${A}" "$(adb_cat /system/priv-app/hello)" "/system/priv-app content after flash vendor"
+ adb_root || die "adb root"
+ if adb_test -e /vendor/hello; then
+ if ${is_userspace_fastboot} || ! ${overlayfs_needed}; then
+ die "vendor content after flash vendor"
+ else
+ LOG WARNING "fastbootd missing required to invalidate, ignoring a failure"
+ LOG WARNING "vendor content after flash vendor"
+ fi
fi
-
- check_eq "${SYSTEM_DEVT}" "`adb_sh stat --format=%D /system/hello </dev/null`" system devt after reboot
- check_eq "${SYSTEM_INO}" "`adb_sh stat --format=%i /system/hello </dev/null`" system inode after reboot
- check_eq "${BASE_SYSTEM_DEVT}" "`adb_sh stat --format=%D /system/bin/stat </dev/null`" --warning base system devt after reboot
- check_eq "${BASE_SYSTEM_DEVT}" "`adb_sh stat --format=%D /system/xbin/su </dev/null`" --warning devt for su after reboot
-
-fi
+ LOG OK "vendor override destroyed after flash verdor"
+fi >&2
wait_for_screen
-echo "${GREEN}[ RUN ]${NORMAL} remove test content (cleanup)" >&2
-T=`adb_date`
-H=`adb remount 2>&1`
-err=${?}
-L=
-D="${H%?Now reboot your device for settings to take effect*}"
-if [ X"${H}" != X"${D}" ]; then
- echo "${YELLOW}[ WARNING ]${NORMAL} adb remount requires a reboot after partial flash (legacy avb)"
- L=`adb_logcat -b all -v nsec -t ${T} 2>&1`
- adb_reboot &&
- adb_wait ${ADB_WAIT} &&
- adb_root ||
- die "failed to reboot"
- T=`adb_date`
- H=`adb remount 2>&1`
- err=${?}
+################################################################################
+LOG RUN "Clean up test content"
+
+adb_root || die "adb root"
+T=$(adb_date)
+D=$(adb remount 2>&1) ||
+ die -t "${T}" "adb remount"
+echo "${D}" >&2
+if [[ "${D}" =~ [Rr]eboot ]]; then
+ LOG OK "adb remount calls for a reboot after partial flash"
+ # but we don't really want to, since rebooting just recreates the already tore
+ # down vendor overlay.
fi
-echo "${H}"
-[ ${err} = 0 ] &&
- ( adb_sh rm /vendor/hello </dev/null 2>/dev/null || true ) &&
- adb_sh rm /system/hello /system/priv-app/hello </dev/null ||
- ( [ -n "${L}" ] && echo "${L}" && false ) ||
- die -t ${T} "cleanup hello"
-B="`adb_cat /system/hello`"
-check_eq "cat: /system/hello: No such file or directory" "${B}" after rm
-B="`adb_cat /system/priv-app/hello`"
-check_eq "cat: /system/priv-app/hello: No such file or directory" "${B}" after rm
-B="`adb_cat /vendor/hello`"
-check_eq "cat: /vendor/hello: No such file or directory" "${B}" after rm
-for i in ${MOUNTS}; do
- adb_sh rm ${i}/hello </dev/null 2>/dev/null || true
+
+for i in ${MOUNTS} /system/priv-app; do
+ adb_sh rm "${i}/hello" 2>/dev/null || true
+ adb_test -e "${i}/hello" &&
+ die -t "${T}" "/${i}/hello lingers after rm"
done
+################################################################################
if ${is_bootloader_fastboot} && [ -n "${scratch_partition}" ]; then
- echo "${GREEN}[ RUN ]${NORMAL} test fastboot flash to ${scratch_partition} recovery" >&2
+ LOG RUN "test fastboot flash to ${scratch_partition} recovery"
avc_check
adb reboot fastboot </dev/null ||
die "Reboot into fastbootd"
- img=${TMPDIR}/adb-remount-test-${$}.img
- cleanup() {
- rm ${img}
- }
+ img="${TMPDIR}/adb-remount-test-${$}.img"
dd if=/dev/zero of=${img} bs=4096 count=16 2>/dev/null &&
fastboot_wait ${FASTBOOT_WAIT} ||
die "reboot into fastboot to flash scratch `usb_status`"
fastboot flash --force ${scratch_partition} ${img}
err=${?}
- cleanup
- cleanup() {
- true
- }
fastboot reboot ||
die "can not reboot out of fastboot"
[ 0 -eq ${err} ] ||
@@ -1616,9 +1564,8 @@
err=${?}
if [ X"${D}" != "${D%?Now reboot your device for settings to take effect*}" ]
then
- echo "${YELLOW}[ WARNING ]${NORMAL} adb disable-verity requires a reboot after partial flash"
+ LOG WARNING "adb disable-verity requires a reboot after partial flash"
adb_reboot &&
- adb_wait ${ADB_WAIT} &&
adb_root ||
die "failed to reboot"
T=`adb_date`
@@ -1627,135 +1574,15 @@
err=${?}
fi
- echo "${D}"
+ echo "${D}" >&2
[ ${err} = 0 ] &&
[ X"${D}" = X"${D##*setup failed}" ] &&
[ X"${D}" != X"${D##*[Uu]sing overlayfs}" ] &&
- echo "${GREEN}[ OK ]${NORMAL} ${scratch_partition} recreated" >&2 ||
+ LOG OK "${scratch_partition} recreated" ||
die -t ${T} "setup for overlayfs"
- D=`adb remount 2>&1`
- err=${?}
- echo "${D}"
- [ ${err} != 0 ] ||
- [ X"${D}" = X"${D##*remount failed}" ] ||
- ( echo "${D}" && false ) ||
+ adb remount >&2 ||
die -t ${T} "remount failed"
fi
-echo "${GREEN}[ RUN ]${NORMAL} test raw remount commands" >&2
-fixup_from_fastboot() {
- inFastboot || return 1
- if [ -n "${ACTIVE_SLOT}" ]; then
- local active_slot=`get_active_slot`
- if [ X"${ACTIVE_SLOT}" != X"${active_slot}" ]; then
- echo "${YELLOW}[ ERROR ]${NORMAL} Active slot changed from ${ACTIVE_SLOT} to ${active_slot}"
- else
- echo "${YELLOW}[ ERROR ]${NORMAL} Active slot to be set to ${ACTIVE_SLOT}"
- fi >&2
- fastboot --set-active=${ACTIVE_SLOT}
- fi
- fastboot reboot
- adb_wait ${ADB_WAIT}
-}
-
-# Prerequisite is a prepped device from above.
-adb_reboot &&
- adb_wait ${ADB_WAIT} ||
- fixup_from_fastboot ||
- die "lost device after reboot to ro state `usb_status`"
-adb_sh grep " /vendor .* rw," /proc/mounts >/dev/null </dev/null &&
- die "/vendor is not read-only"
-adb_su mount -o rw,remount /vendor </dev/null ||
- die "remount command"
-adb_sh grep " /vendor .* rw," /proc/mounts >/dev/null </dev/null ||
- die "/vendor is not read-write"
-echo "${GREEN}[ OK ]${NORMAL} mount -o rw,remount command works" >&2
-
-# Prerequisite is a prepped device from above.
-adb_reboot &&
- adb_wait ${ADB_WAIT} ||
- fixup_from_fastboot ||
- die "lost device after reboot to ro state `usb_status`"
-adb_sh grep " /vendor .* rw," /proc/mounts >/dev/null </dev/null &&
- die "/vendor is not read-only"
-adb_su remount vendor </dev/null ||
- die "remount command"
-adb_sh grep " /vendor .* rw," /proc/mounts >/dev/null </dev/null ||
- die "/vendor is not read-write"
-adb_sh grep " /system .* rw," /proc/mounts >/dev/null </dev/null &&
- die "/vendor is not read-only"
-echo "${GREEN}[ OK ]${NORMAL} remount command works from setup" >&2
-
-# Prerequisite is an overlayfs deconstructed device but with verity disabled.
-# This also saves a lot of 'noise' from the command doing a mkfs on backing
-# storage and all the related tuning and adjustment.
-for d in ${OVERLAYFS_BACKING}; do
- if adb_test -d /${d}/overlay; then
- adb_su rm -rf /${d}/overlay </dev/null ||
- die "/${d}/overlay wipe"
- fi
-done
-adb_reboot &&
- adb_wait ${ADB_WAIT} ||
- fixup_from_fastboot ||
- die "lost device after reboot after wipe `usb_status`"
-adb_sh grep " /vendor .* rw," /proc/mounts >/dev/null </dev/null &&
- die "/vendor is not read-only"
-adb_su remount vendor </dev/null ||
- die "remount command"
-adb_su df -k </dev/null | skip_unrelated_mounts
-adb_sh grep " /vendor .* rw," /proc/mounts >/dev/null </dev/null ||
- die "/vendor is not read-write"
-adb_sh grep " \(/system\|/\) .* rw," /proc/mounts >/dev/null </dev/null &&
- die "/system is not read-only"
-echo "${GREEN}[ OK ]${NORMAL} remount command works from scratch" >&2
-
-if ! restore; then
- restore() {
- true
- }
- die "failed to restore verity after remount from scratch test"
-fi
-
-err=0
-
-if ${overlayfs_supported}; then
- echo "${GREEN}[ RUN ]${NORMAL} test 'adb remount -R'" >&2
- avc_check
- adb_root ||
- die "adb root in preparation for adb remount -R"
- T=`adb_date`
- adb remount -R
- err=${?}
- if [ "${err}" != 0 ]; then
- die -t ${T} "adb remount -R = ${err}"
- fi
- sleep 2
- adb_wait ${ADB_WAIT} ||
- die "waiting for device after adb remount -R `usb_status`"
- if [ "orange" != "`get_property ro.boot.verifiedbootstate`" -o \
- "2" = "`get_property partition.system.verified`" ] &&
- [ -n "`get_property ro.boot.verifiedbootstate`" -o \
- -n "`get_property partition.system.verified`" ]; then
- die "remount -R command failed to disable verity
-${INDENT}ro.boot.verifiedbootstate=\"`get_property ro.boot.verifiedbootstate`\"
-${INDENT}partition.system.verified=\"`get_property partition.system.verified`\""
- fi
-
- echo "${GREEN}[ OK ]${NORMAL} 'adb remount -R' command" >&2
-
- restore
- err=${?}
-fi
-
-restore() {
- true
-}
-
-[ ${err} = 0 ] ||
- die "failed to restore verity"
-
-echo "${GREEN}[ PASSED ]${NORMAL} adb remount" >&2
-
-test_duration
+LOG PASSED "adb remount test"
diff --git a/fs_mgr/tests/fs_mgr_test.cpp b/fs_mgr/tests/fs_mgr_test.cpp
index 6c881c0..e33681c 100644
--- a/fs_mgr/tests/fs_mgr_test.cpp
+++ b/fs_mgr/tests/fs_mgr_test.cpp
@@ -1109,14 +1109,17 @@
TemporaryFile tf;
ASSERT_TRUE(tf.fd != -1);
std::string fstab_contents = R"fs(
+data /data f2fs noatime wait,latemount
system /system erofs ro wait,logical,first_stage_mount
system /system ext4 ro wait,logical,first_stage_mount
vendor /vendor ext4 ro wait,logical,first_stage_mount
-data /data f2fs noatime wait
)fs";
ASSERT_TRUE(android::base::WriteStringToFile(fstab_contents, tf.path));
+ // If GSI is installed, ReadFstabFromFile() would have called TransformFstabForDsu() implicitly.
+ // In other words, TransformFstabForDsu() would be called two times if running CTS-on-GSI,
+ // which implies TransformFstabForDsu() should be idempotent.
Fstab fstab;
EXPECT_TRUE(ReadFstabFromFile(tf.path, &fstab));
TransformFstabForDsu(&fstab, "dsu", {"system_gsi", "userdata_gsi"});
@@ -1124,21 +1127,23 @@
auto entry = fstab.begin();
- EXPECT_EQ("/system", entry->mount_point);
- EXPECT_EQ("system_gsi", entry->blk_device);
+ EXPECT_EQ("/data", entry->mount_point);
+ EXPECT_EQ("userdata_gsi", entry->blk_device);
entry++;
EXPECT_EQ("/system", entry->mount_point);
EXPECT_EQ("system_gsi", entry->blk_device);
+ EXPECT_EQ("erofs", entry->fs_type);
+ entry++;
+
+ EXPECT_EQ("/system", entry->mount_point);
+ EXPECT_EQ("system_gsi", entry->blk_device);
+ EXPECT_EQ("ext4", entry->fs_type);
entry++;
EXPECT_EQ("/vendor", entry->mount_point);
EXPECT_EQ("vendor", entry->blk_device);
entry++;
-
- EXPECT_EQ("/data", entry->mount_point);
- EXPECT_EQ("userdata_gsi", entry->blk_device);
- entry++;
}
TEST(fs_mgr, TransformFstabForDsu_synthesisExt4Entry) {
@@ -1147,7 +1152,7 @@
std::string fstab_contents = R"fs(
system /system erofs ro wait,logical,first_stage_mount
vendor /vendor ext4 ro wait,logical,first_stage_mount
-data /data f2fs noatime wait
+data /data f2fs noatime wait,latemount
)fs";
ASSERT_TRUE(android::base::WriteStringToFile(fstab_contents, tf.path));
@@ -1177,3 +1182,39 @@
EXPECT_EQ("userdata_gsi", entry->blk_device);
entry++;
}
+
+TEST(fs_mgr, TransformFstabForDsu_synthesisAllMissingEntries) {
+ TemporaryFile tf;
+ ASSERT_TRUE(tf.fd != -1);
+ std::string fstab_contents = R"fs(
+data /data f2fs noatime wait,latemount
+vendor /vendor ext4 ro wait,logical,first_stage_mount
+)fs";
+
+ ASSERT_TRUE(android::base::WriteStringToFile(fstab_contents, tf.path));
+
+ Fstab fstab;
+ EXPECT_TRUE(ReadFstabFromFile(tf.path, &fstab));
+ TransformFstabForDsu(&fstab, "dsu", {"system_gsi", "userdata_gsi"});
+ ASSERT_EQ(4U, fstab.size());
+
+ auto entry = fstab.begin();
+
+ EXPECT_EQ("/data", entry->mount_point);
+ EXPECT_EQ("userdata_gsi", entry->blk_device);
+ entry++;
+
+ EXPECT_EQ("/vendor", entry->mount_point);
+ EXPECT_EQ("vendor", entry->blk_device);
+ entry++;
+
+ EXPECT_EQ("/system", entry->mount_point);
+ EXPECT_EQ("system_gsi", entry->blk_device);
+ EXPECT_EQ("ext4", entry->fs_type);
+ entry++;
+
+ EXPECT_EQ("/system", entry->mount_point);
+ EXPECT_EQ("system_gsi", entry->blk_device);
+ EXPECT_EQ("erofs", entry->fs_type);
+ entry++;
+}
diff --git a/fs_mgr/tests/vts_fs_test.cpp b/fs_mgr/tests/vts_fs_test.cpp
index ae8e459..b8b34e2 100644
--- a/fs_mgr/tests/vts_fs_test.cpp
+++ b/fs_mgr/tests/vts_fs_test.cpp
@@ -23,6 +23,9 @@
#include <gtest/gtest.h>
#include <libdm/dm.h>
+using testing::Contains;
+using testing::Not;
+
static int GetVsrLevel() {
return android::base::GetIntProperty("ro.vendor.api_level", -1);
}
@@ -117,3 +120,30 @@
android::fs_mgr::Fstab fstab;
EXPECT_FALSE(android::fs_mgr::ReadFstabFromDt(&fstab, false));
}
+
+TEST(fs, NoLegacyVerifiedBoot) {
+ if (GetVsrLevel() < __ANDROID_API_T__) {
+ GTEST_SKIP();
+ }
+
+ const auto& default_fstab_path = android::fs_mgr::GetFstabPath();
+ EXPECT_FALSE(default_fstab_path.empty());
+
+ std::string fstab_str;
+ EXPECT_TRUE(android::base::ReadFileToString(default_fstab_path, &fstab_str,
+ /* follow_symlinks = */ true));
+
+ for (const auto& line : android::base::Split(fstab_str, "\n")) {
+ auto fields = android::base::Tokenize(line, " \t");
+ // Ignores empty lines and comments.
+ if (fields.empty() || android::base::StartsWith(fields.front(), '#')) {
+ continue;
+ }
+ // Each line in a fstab should have at least five entries.
+ // <src> <mnt_point> <type> <mnt_flags and options> <fs_mgr_flags>
+ ASSERT_GE(fields.size(), 5);
+ EXPECT_THAT(android::base::Split(fields[4], ","), Not(Contains("verify")))
+ << "AVB 1.0 isn't supported now, but the 'verify' flag is found:\n"
+ << " " << line;
+ }
+}
diff --git a/healthd/Android.bp b/healthd/Android.bp
index f180006..a090b74 100644
--- a/healthd/Android.bp
+++ b/healthd/Android.bp
@@ -342,20 +342,20 @@
],
}
-// /vendor/etc/res/images/charger/battery_fail.png
+// /vendor/etc/res/images/default/charger/battery_fail.png
prebuilt_etc {
name: "system_core_charger_res_images_battery_fail.png_default_vendor",
src: "images/battery_fail.png",
- relative_install_path: "res/images/charger/default",
+ relative_install_path: "res/images/default/charger",
vendor: true,
filename: "battery_fail.png",
}
-// /vendor/etc/res/images/charger/battery_scale.png
+// /vendor/etc/res/images/default/charger/battery_scale.png
prebuilt_etc {
name: "system_core_charger_res_images_battery_scale.png_default_vendor",
src: "images/battery_scale.png",
- relative_install_path: "res/images/charger/default",
+ relative_install_path: "res/images/default/charger",
vendor: true,
filename: "battery_scale.png",
}
diff --git a/healthd/healthd_mode_charger.cpp b/healthd/healthd_mode_charger.cpp
index 9fe85d4..e305a86 100644
--- a/healthd/healthd_mode_charger.cpp
+++ b/healthd/healthd_mode_charger.cpp
@@ -308,30 +308,7 @@
// If timeout and battery level is still not ready, draw unknown battery
}
- if (healthd_draw_ == nullptr) {
- std::optional<bool> out_screen_on = configuration_->ChargerShouldKeepScreenOn();
- if (out_screen_on.has_value()) {
- if (!*out_screen_on) {
- LOGV("[%" PRId64 "] leave screen off\n", now);
- batt_anim_.run = false;
- next_screen_transition_ = -1;
- if (configuration_->ChargerIsOnline()) {
- RequestEnableSuspend();
- }
- return;
- }
- }
-
- healthd_draw_ = HealthdDraw::Create(&batt_anim_);
- if (healthd_draw_ == nullptr) return;
-
-#if !defined(__ANDROID_VNDK__)
- if (android::sysprop::ChargerProperties::disable_init_blank().value_or(false)) {
- healthd_draw_->blank_screen(true, static_cast<int>(drm_));
- screen_blanked_ = true;
- }
-#endif
- }
+ if (healthd_draw_ == nullptr) return;
/* animation is over, blank screen and leave */
if (batt_anim_.num_cycles > 0 && batt_anim_.cur_cycle == batt_anim_.num_cycles) {
@@ -736,6 +713,33 @@
}
}
+void Charger::InitHealthdDraw() {
+ if (healthd_draw_ == nullptr) {
+ std::optional<bool> out_screen_on = configuration_->ChargerShouldKeepScreenOn();
+ if (out_screen_on.has_value()) {
+ if (!*out_screen_on) {
+ LOGV("[%" PRId64 "] leave screen off\n", curr_time_ms());
+ batt_anim_.run = false;
+ next_screen_transition_ = -1;
+ if (configuration_->ChargerIsOnline()) {
+ RequestEnableSuspend();
+ }
+ return;
+ }
+ }
+
+ healthd_draw_ = HealthdDraw::Create(&batt_anim_);
+ if (healthd_draw_ == nullptr) return;
+
+#if !defined(__ANDROID_VNDK__)
+ if (android::sysprop::ChargerProperties::disable_init_blank().value_or(false)) {
+ healthd_draw_->blank_screen(true, static_cast<int>(drm_));
+ screen_blanked_ = true;
+ }
+#endif
+ }
+}
+
void Charger::OnInit(struct healthd_config* config) {
int ret;
int i;
@@ -753,6 +757,7 @@
}
InitAnimation();
+ InitHealthdDraw();
ret = CreateDisplaySurface(batt_anim_.fail_file, &surf_unknown_);
if (ret < 0) {
diff --git a/healthd/include_charger/charger/healthd_mode_charger.h b/healthd/include_charger/charger/healthd_mode_charger.h
index 28e1fb5..82e4ddf 100644
--- a/healthd/include_charger/charger/healthd_mode_charger.h
+++ b/healthd/include_charger/charger/healthd_mode_charger.h
@@ -104,6 +104,7 @@
void HandleInputState(int64_t now);
void HandlePowerSupplyState(int64_t now);
int InputCallback(int fd, unsigned int epevents);
+ void InitHealthdDraw();
void InitAnimation();
int RequestEnableSuspend();
int RequestDisableSuspend();
diff --git a/init/Android.bp b/init/Android.bp
index dd67d04..dfc90da 100644
--- a/init/Android.bp
+++ b/init/Android.bp
@@ -53,6 +53,7 @@
"util.cpp",
]
init_device_sources = [
+ "apex_init_util.cpp",
"block_dev_initializer.cpp",
"bootchart.cpp",
"builtins.cpp",
@@ -164,6 +165,7 @@
"libcgrouprc_format",
"libfsverity_init",
"liblmkd_utils",
+ "liblz4",
"libmini_keyctl_static",
"libmodprobe",
"libprocinfo",
@@ -178,7 +180,6 @@
"update_metadata-protos",
],
shared_libs: [
- "libbacktrace",
"libbase",
"libbootloader_message",
"libcrypto",
@@ -195,6 +196,7 @@
"libprocessgroup",
"libprocessgroup_setup",
"libselinux",
+ "libunwindstack",
"libutils",
"libziparchive",
],
@@ -202,6 +204,12 @@
visibility: [":__subpackages__"],
}
+cc_library_headers {
+ name: "libinit_headers",
+ export_include_dirs: ["."],
+ visibility: [":__subpackages__"],
+}
+
cc_library_static {
name: "libinit",
recovery_available: true,
@@ -210,13 +218,14 @@
"selinux_policy_version",
],
srcs: init_common_sources + init_device_sources,
+ export_include_dirs: ["."],
generated_sources: [
"apex-info-list",
],
whole_static_libs: [
"libcap",
- "com.android.sysprop.apex",
- "com.android.sysprop.init",
+ "libcom.android.sysprop.apex",
+ "libcom.android.sysprop.init",
],
header_libs: ["bootimg_headers"],
proto: {
@@ -239,6 +248,10 @@
],
},
},
+ visibility: [
+ "//system/apex/apexd",
+ "//frameworks/native/cmds/installd",
+ ],
}
phony {
@@ -352,11 +365,11 @@
"libgsi",
"liblzma",
"libunwindstack_no_dex",
- "libbacktrace_no_dex",
"libmodprobe",
"libext2_uuid",
"libprotobuf-cpp-lite",
"libsnapshot_cow",
+ "liblz4",
"libsnapshot_init",
"update_metadata-protos",
"libprocinfo",
diff --git a/init/README.md b/init/README.md
index 13c6ebd..fed81db 100644
--- a/init/README.md
+++ b/init/README.md
@@ -606,8 +606,12 @@
group. If not provided, the directory is created with permissions 755 and
owned by the root user and root group. If provided, the mode, owner and group
will be updated if the directory exists already.
+ If the directory does not exist, it will receive the security context from
+ the current SELinux policy or its parent if not specified in the policy. If
+ the directory exists, its security context will not be changed (even if
+ different from the policy).
- > _action_ can be one of:
+ > _action_ can be one of:
* `None`: take no encryption action; directory will be encrypted if parent is.
* `Require`: encrypt directory, abort boot process if encryption fails
* `Attempt`: try to set an encryption policy, but continue if it fails
diff --git a/init/TEST_MAPPING b/init/TEST_MAPPING
index fa1627c..402b501 100644
--- a/init/TEST_MAPPING
+++ b/init/TEST_MAPPING
@@ -8,9 +8,17 @@
},
{
"name": "MicrodroidHostTestCases"
+ },
+ {
+ "name": "CtsSecurityHostTestCases",
+ "options": [
+ {
+ "include-filter": "android.security.cts.SeamendcHostTest"
+ }
+ ]
}
],
- "hwasan-postsubmit": [
+ "hwasan-presubmit": [
{
"name": "CtsInitTestCases"
},
@@ -19,6 +27,14 @@
},
{
"name": "MicrodroidHostTestCases"
+ },
+ {
+ "name": "CtsSecurityHostTestCases",
+ "options": [
+ {
+ "include-filter": "android.security.cts.SeamendcHostTest"
+ }
+ ]
}
]
}
diff --git a/init/action.h b/init/action.h
index 1534bf9..eddc384 100644
--- a/init/action.h
+++ b/init/action.h
@@ -22,6 +22,8 @@
#include <variant>
#include <vector>
+#include <android-base/strings.h>
+
#include "builtins.h"
#include "keyword_map.h"
#include "result.h"
@@ -79,6 +81,7 @@
static void set_function_map(const BuiltinFunctionMap* function_map) {
function_map_ = function_map;
}
+ bool IsFromApex() const { return base::StartsWith(filename_, "/apex/"); }
private:
void ExecuteCommand(const Command& command) const;
diff --git a/init/action_manager.h b/init/action_manager.h
index b6f93d9..68912a8 100644
--- a/init/action_manager.h
+++ b/init/action_manager.h
@@ -37,6 +37,10 @@
size_t CheckAllCommands();
void AddAction(std::unique_ptr<Action> action);
+ template <class UnaryPredicate>
+ void RemoveActionIf(UnaryPredicate predicate) {
+ actions_.erase(std::remove_if(actions_.begin(), actions_.end(), predicate), actions_.end());
+ }
void QueueEventTrigger(const std::string& trigger);
void QueuePropertyChange(const std::string& name, const std::string& value);
void QueueAllPropertyActions();
@@ -45,6 +49,7 @@
bool HasMoreCommands() const;
void DumpState() const;
void ClearQueue();
+ auto size() const { return actions_.size(); }
private:
ActionManager(ActionManager const&) = delete;
diff --git a/init/action_parser.cpp b/init/action_parser.cpp
index 52f6a1f..49fe24a 100644
--- a/init/action_parser.cpp
+++ b/init/action_parser.cpp
@@ -142,6 +142,14 @@
action_subcontext = subcontext_;
}
+ // We support 'on' for only Vendor APEXes from /{vendor, odm}.
+ // It is to prevent mainline modules from using 'on' triggers because events/properties are
+ // not stable for mainline modules.
+ // Note that this relies on Subcontext::PathMatchesSubcontext() to identify Vendor APEXes.
+ if (StartsWith(filename, "/apex/") && !action_subcontext) {
+ return Error() << "ParseSection() failed: 'on' is supported for only Vendor APEXes.";
+ }
+
std::string event_trigger;
std::map<std::string, std::string> property_triggers;
diff --git a/init/apex_init_util.cpp b/init/apex_init_util.cpp
new file mode 100644
index 0000000..d618a6e
--- /dev/null
+++ b/init/apex_init_util.cpp
@@ -0,0 +1,96 @@
+/*
+ * Copyright (C) 2022 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 "apex_init_util.h"
+
+#include <glob.h>
+
+#include <map>
+#include <vector>
+
+#include <android-base/logging.h>
+#include <android-base/result.h>
+#include <android-base/properties.h>
+#include <android-base/strings.h>
+
+#include "action_manager.h"
+#include "init.h"
+#include "parser.h"
+#include "service_list.h"
+#include "util.h"
+
+namespace android {
+namespace init {
+
+static Result<std::vector<std::string>> CollectApexConfigs(const std::string& apex_name) {
+ glob_t glob_result;
+ std::string glob_pattern = apex_name.empty() ?
+ "/apex/*/etc/*rc" : "/apex/" + apex_name + "/etc/*rc";
+
+ const int ret = glob(glob_pattern.c_str(), GLOB_MARK, nullptr, &glob_result);
+ if (ret != 0 && ret != GLOB_NOMATCH) {
+ globfree(&glob_result);
+ return Error() << "Glob pattern '" << glob_pattern << "' failed";
+ }
+ std::vector<std::string> configs;
+ for (size_t i = 0; i < glob_result.gl_pathc; i++) {
+ std::string path = glob_result.gl_pathv[i];
+ // Filter-out /apex/<name>@<ver> paths. The paths are bind-mounted to
+ // /apex/<name> paths, so unless we filter them out, we will parse the
+ // same file twice.
+ std::vector<std::string> paths = android::base::Split(path, "/");
+ if (paths.size() >= 3 && paths[2].find('@') != std::string::npos) {
+ continue;
+ }
+ // Filter directories
+ if (path.back() == '/') {
+ continue;
+ }
+ configs.push_back(path);
+ }
+ globfree(&glob_result);
+ return configs;
+}
+
+static Result<void> ParseConfigs(const std::vector<std::string>& configs) {
+ Parser parser = CreateApexConfigParser(ActionManager::GetInstance(),
+ ServiceList::GetInstance());
+ bool success = true;
+ for (const auto& c : configs) {
+ success &= parser.ParseConfigFile(c);
+ }
+
+ if (success) {
+ return {};
+ } else {
+ return Error() << "Unable to parse apex configs";
+ }
+}
+
+Result<void> ParseApexConfigs(const std::string& apex_name) {
+ auto configs = OR_RETURN(CollectApexConfigs(apex_name));
+
+ if (configs.empty()) {
+ return {};
+ }
+
+ auto filtered_configs = FilterVersionedConfigs(configs,
+ android::base::GetIntProperty("ro.build.version.sdk", INT_MAX));
+ return ParseConfigs(filtered_configs);
+}
+
+} // namespace init
+} // namespace android
diff --git a/init/apex_init_util.h b/init/apex_init_util.h
new file mode 100644
index 0000000..43f8ad5
--- /dev/null
+++ b/init/apex_init_util.h
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <string>
+#include <vector>
+
+#include "result.h"
+
+namespace android {
+namespace init {
+
+// Parse all config files for a given apex.
+// If apex name is empty(""), config files for all apexes will be parsed.
+Result<void> ParseApexConfigs(const std::string& apex_name);
+
+} // namespace init
+} // namespace android
diff --git a/init/builtins.cpp b/init/builtins.cpp
index 01db4f5..c8cb253 100644
--- a/init/builtins.cpp
+++ b/init/builtins.cpp
@@ -69,6 +69,7 @@
#include <system/thread_defs.h>
#include "action_manager.h"
+#include "apex_init_util.h"
#include "bootchart.h"
#include "builtin_arguments.h"
#include "fscrypt_init_extensions.h"
@@ -426,7 +427,7 @@
return ErrnoError() << "fchmodat() failed on " << options.target;
}
}
- if (fscrypt_is_native()) {
+ if (IsFbeEnabled()) {
if (!FscryptSetDirectoryPolicy(ref_basename, options.fscrypt_action, options.target)) {
return reboot_into_recovery(
{"--prompt_and_wipe_data", "--reason=set_policy_failed:"s + options.target});
@@ -1175,7 +1176,7 @@
auto reboot = [reboot_reason, should_reboot_into_recovery](const std::string& message) {
// TODO (b/122850122): support this in gsi
if (should_reboot_into_recovery) {
- if (fscrypt_is_native() && !android::gsi::IsGsiRunning()) {
+ 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});
@@ -1279,47 +1280,6 @@
return GenerateLinkerConfiguration();
}
-static Result<void> parse_apex_configs() {
- glob_t glob_result;
- static constexpr char glob_pattern[] = "/apex/*/etc/*rc";
- const int ret = glob(glob_pattern, GLOB_MARK, nullptr, &glob_result);
- if (ret != 0 && ret != GLOB_NOMATCH) {
- globfree(&glob_result);
- return Error() << "glob pattern '" << glob_pattern << "' failed";
- }
- std::vector<std::string> configs;
- Parser parser = CreateServiceOnlyParser(ServiceList::GetInstance(), true);
- for (size_t i = 0; i < glob_result.gl_pathc; i++) {
- std::string path = glob_result.gl_pathv[i];
- // Filter-out /apex/<name>@<ver> paths. The paths are bind-mounted to
- // /apex/<name> paths, so unless we filter them out, we will parse the
- // same file twice.
- std::vector<std::string> paths = android::base::Split(path, "/");
- if (paths.size() >= 3 && paths[2].find('@') != std::string::npos) {
- continue;
- }
- // Filter directories
- if (path.back() == '/') {
- continue;
- }
- configs.push_back(path);
- }
- globfree(&glob_result);
-
- int active_sdk = android::base::GetIntProperty("ro.build.version.sdk", INT_MAX);
-
- bool success = true;
- for (const auto& c : parser.FilterVersionedConfigs(configs, active_sdk)) {
- success &= parser.ParseConfigFile(c);
- }
- ServiceList::GetInstance().MarkServicesUpdate();
- if (success) {
- return {};
- } else {
- return Error() << "Could not parse apex configs";
- }
-}
-
/*
* Creates a directory under /data/misc/apexdata/ for each APEX.
*/
@@ -1350,7 +1310,8 @@
if (!create_dirs.ok()) {
return create_dirs.error();
}
- auto parse_configs = parse_apex_configs();
+ auto parse_configs = ParseApexConfigs(/*apex_name=*/"");
+ ServiceList::GetInstance().MarkServicesUpdate();
if (!parse_configs.ok()) {
return parse_configs.error();
}
diff --git a/init/compare-bootcharts.py b/init/compare-bootcharts.py
index 2057b55..009b639 100755
--- a/init/compare-bootcharts.py
+++ b/init/compare-bootcharts.py
@@ -56,24 +56,24 @@
]
jw = jiffy_record['jiffy_to_wallclock']
- print "process: baseline experiment (delta)"
- print " - Unit is ms (a jiffy is %d ms on the system)" % jw
- print "------------------------------------"
+ print("process: baseline experiment (delta)")
+ print(" - Unit is ms (a jiffy is %d ms on the system)" % jw)
+ print("------------------------------------")
for p in processes_of_interest:
# e.g., 32-bit system doesn't have zygote64
if p in process_map1 and p in process_map2:
- print "%s: %d %d (%+d)" % (
+ print("%s: %d %d (%+d)" % (
p, process_map1[p]['start_time'] * jw,
process_map2[p]['start_time'] * jw,
(process_map2[p]['start_time'] -
- process_map1[p]['start_time']) * jw)
+ process_map1[p]['start_time']) * jw))
# Print the last tick for the bootanimation process
- print "bootanimation ends at: %d %d (%+d)" % (
+ print("bootanimation ends at: %d %d (%+d)" % (
process_map1['/system/bin/bootanimation']['last_tick'] * jw,
process_map2['/system/bin/bootanimation']['last_tick'] * jw,
(process_map2['/system/bin/bootanimation']['last_tick'] -
- process_map1['/system/bin/bootanimation']['last_tick']) * jw)
+ process_map1['/system/bin/bootanimation']['last_tick']) * jw))
def parse_proc_file(pathname, process_map, jiffy_record=None):
# Uncompress bootchart.tgz
@@ -83,7 +83,7 @@
f = tf.extractfile('proc_ps.log')
# Break proc_ps into chunks based on timestamps
- blocks = f.read().split('\n\n')
+ blocks = f.read().decode('utf-8').split('\n\n')
for b in blocks:
lines = b.split('\n')
if not lines[0]:
@@ -133,7 +133,7 @@
def main():
if len(sys.argv) != 3:
- print "Usage: %s base_bootchart_dir exp_bootchart_dir" % sys.argv[0]
+ print("Usage: %s base_bootchart_dir exp_bootchart_dir" % sys.argv[0])
sys.exit(1)
process_map1 = {}
diff --git a/init/devices.cpp b/init/devices.cpp
index d4a3cb9..28406f6 100644
--- a/init/devices.cpp
+++ b/init/devices.cpp
@@ -307,8 +307,8 @@
PLOG(ERROR) << "setegid(" << gid << ") for " << path << " device failed";
goto out;
}
- /* If the node already exists update its SELinux label to handle cases when
- * it was created with the wrong context during coldboot procedure. */
+ /* If the node already exists update its SELinux label and the file mode to handle cases when
+ * it was created with the wrong context and file mode during coldboot procedure. */
if (mknod(path.c_str(), mode, dev) && (errno == EEXIST) && !secontext.empty()) {
char* fcon = nullptr;
int rc = lgetfilecon(path.c_str(), &fcon);
@@ -330,6 +330,11 @@
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;
+ }
+ }
} else {
PLOG(ERROR) << "Cannot stat " << path;
}
diff --git a/init/extra_free_kbytes.sh b/init/extra_free_kbytes.sh
index aeaa912..a0141be 100755
--- a/init/extra_free_kbytes.sh
+++ b/init/extra_free_kbytes.sh
@@ -77,7 +77,19 @@
exit
fi
-watermark_scale=`cat /proc/sys/vm/watermark_scale_factor`
+# record the original watermark_scale_factor value
+watermark_scale=$(getprop "ro.kernel.watermark_scale_factor")
+if [ -z "$watermark_scale" ]
+then
+ watermark_scale=$(cat /proc/sys/vm/watermark_scale_factor)
+ setprop "ro.kernel.watermark_scale_factor" "$watermark_scale"
+ # On older distributions with no policies configured setprop may fail.
+ # If that happens, use the kernel default of 10.
+ if [ -z $(getprop "ro.kernel.watermark_scale_factor") ]
+ then
+ watermark_scale=10
+ fi
+fi
# convert extra_free_kbytes to pages
page_size=$(getconf PAGESIZE)
diff --git a/init/first_stage_init.cpp b/init/first_stage_init.cpp
index d050ed7..107e99a 100644
--- a/init/first_stage_init.cpp
+++ b/init/first_stage_init.cpp
@@ -119,19 +119,14 @@
// Move snapuserd before switching root, so that it is available at the same path
// after switching root.
void PrepareSwitchRoot() {
- constexpr const char* src = "/system/bin/snapuserd";
- constexpr const char* dst = "/first_stage_ramdisk/system/bin/snapuserd";
+ static constexpr const auto& snapuserd = "/system/bin/snapuserd";
+ static constexpr const auto& snapuserd_ramdisk = "/system/bin/snapuserd_ramdisk";
+ static constexpr const auto& dst = "/first_stage_ramdisk/system/bin/snapuserd";
if (access(dst, X_OK) == 0) {
LOG(INFO) << dst << " already exists and it can be executed";
return;
}
-
- if (access(src, F_OK) != 0) {
- PLOG(INFO) << "Not moving " << src << " because it cannot be accessed";
- return;
- }
-
auto dst_dir = android::base::Dirname(dst);
std::error_code ec;
if (access(dst_dir.c_str(), F_OK) != 0) {
@@ -139,7 +134,18 @@
LOG(FATAL) << "Cannot create " << dst_dir << ": " << ec.message();
}
}
- Copy(src, dst);
+
+ // prefer the generic ramdisk copy of snapuserd, because that's on system side of treble
+ // boundary, and therefore is more likely to be updated along with the Android platform.
+ // The vendor ramdisk copy might be under vendor freeze, or vendor might choose not to update
+ // it.
+ if (access(snapuserd_ramdisk, F_OK) == 0) {
+ LOG(INFO) << "Using generic ramdisk copy of snapuserd " << snapuserd_ramdisk;
+ Copy(snapuserd_ramdisk, dst);
+ } else if (access(snapuserd, F_OK) == 0) {
+ LOG(INFO) << "Using vendor ramdisk copy of snapuserd " << snapuserd;
+ Copy(snapuserd, dst);
+ }
}
} // namespace
@@ -289,9 +295,6 @@
// stage init
CHECKCALL(mount("tmpfs", kSecondStageRes, "tmpfs", MS_NOEXEC | MS_NOSUID | MS_NODEV,
"mode=0755,uid=0,gid=0"))
-
- // First stage init stores Mainline sepolicy here.
- CHECKCALL(mkdir("/dev/selinux", 0744));
#undef CHECKCALL
SetStdioToDevNull(argv);
diff --git a/init/first_stage_mount.cpp b/init/first_stage_mount.cpp
index 042988e..07ce458 100644
--- a/init/first_stage_mount.cpp
+++ b/init/first_stage_mount.cpp
@@ -507,16 +507,16 @@
SaveRamdiskPathToSnapuserd();
}
- if (MountPartition(system_partition, false /* erase_same_mounts */)) {
- if (dsu_not_on_userdata_ && fs_mgr_verity_is_check_at_most_once(*system_partition)) {
- LOG(ERROR) << "check_most_at_once forbidden on external media";
- return false;
- }
- SwitchRoot("/system");
- } else {
+ if (!MountPartition(system_partition, false /* erase_same_mounts */)) {
PLOG(ERROR) << "Failed to mount /system";
return false;
}
+ if (dsu_not_on_userdata_ && fs_mgr_verity_is_check_at_most_once(*system_partition)) {
+ LOG(ERROR) << "check_at_most_once forbidden on external media";
+ return false;
+ }
+
+ SwitchRoot("/system");
return true;
}
@@ -651,14 +651,9 @@
return;
}
- std::string lp_names = "";
- std::vector<std::string> dsu_partitions;
- for (auto&& name : images->GetAllBackingImages()) {
- dsu_partitions.push_back(name);
- lp_names += name + ",";
- }
- // Publish the logical partition names for TransformFstabForDsu
- WriteFile(gsi::kGsiLpNamesFile, lp_names);
+ // Publish the logical partition names for TransformFstabForDsu() and ReadFstabFromFile().
+ const auto dsu_partitions = images->GetAllBackingImages();
+ WriteFile(gsi::kGsiLpNamesFile, android::base::Join(dsu_partitions, ","));
TransformFstabForDsu(&fstab_, active_dsu, dsu_partitions);
}
diff --git a/init/init.cpp b/init/init.cpp
index 29f643e..ce668d7 100644
--- a/init/init.cpp
+++ b/init/init.cpp
@@ -51,19 +51,22 @@
#include <android-base/properties.h>
#include <android-base/stringprintf.h>
#include <android-base/strings.h>
-#include <backtrace/Backtrace.h>
#include <fs_avb/fs_avb.h>
#include <fs_mgr_vendor_overlay.h>
#include <keyutils.h>
#include <libavb/libavb.h>
#include <libgsi/libgsi.h>
#include <libsnapshot/snapshot.h>
+#include <logwrap/logwrap.h>
#include <processgroup/processgroup.h>
#include <processgroup/setup.h>
#include <selinux/android.h>
+#include <unwindstack/AndroidUnwinder.h>
+#include "action.h"
+#include "action_manager.h"
#include "action_parser.h"
-#include "builtins.h"
+#include "apex_init_util.h"
#include "epoll.h"
#include "first_stage_init.h"
#include "first_stage_mount.h"
@@ -81,6 +84,7 @@
#include "selabel.h"
#include "selinux.h"
#include "service.h"
+#include "service_list.h"
#include "service_parser.h"
#include "sigchld_handler.h"
#include "snapuserd_transition.h"
@@ -88,6 +92,10 @@
#include "system/core/init/property_service.pb.h"
#include "util.h"
+#ifndef RECOVERY
+#include "com_android_apex.h"
+#endif // RECOVERY
+
using namespace std::chrono_literals;
using namespace std::string_literals;
@@ -256,12 +264,14 @@
} shutdown_state;
static void UnwindMainThreadStack() {
- std::unique_ptr<Backtrace> backtrace(Backtrace::Create(BACKTRACE_CURRENT_PROCESS, 1));
- if (!backtrace->Unwind(0)) {
- LOG(ERROR) << __FUNCTION__ << "sys.powerctl: Failed to unwind callstack.";
+ unwindstack::AndroidLocalUnwinder unwinder;
+ unwindstack::AndroidUnwinderData data;
+ if (!unwinder.Unwind(data)) {
+ LOG(ERROR) << __FUNCTION__
+ << "sys.powerctl: Failed to unwind callstack: " << data.GetErrorString();
}
- for (size_t i = 0; i < backtrace->NumFrames(); i++) {
- LOG(ERROR) << "sys.powerctl: " << backtrace->FormatFrameData(i);
+ for (const auto& frame : data.frames) {
+ LOG(ERROR) << "sys.powerctl: " << unwinder.FormatFrame(frame);
}
}
@@ -294,13 +304,59 @@
return parser;
}
-// parser that only accepts new services
-Parser CreateServiceOnlyParser(ServiceList& service_list, bool from_apex) {
- Parser parser;
+#ifndef RECOVERY
+template <typename T>
+struct LibXmlErrorHandler {
+ T handler_;
+ template <typename Handler>
+ LibXmlErrorHandler(Handler&& handler) : handler_(std::move(handler)) {
+ xmlSetGenericErrorFunc(nullptr, &ErrorHandler);
+ }
+ ~LibXmlErrorHandler() { xmlSetGenericErrorFunc(nullptr, nullptr); }
+ static void ErrorHandler(void*, const char* msg, ...) {
+ va_list args;
+ va_start(args, msg);
+ char* formatted;
+ if (vasprintf(&formatted, msg, args) >= 0) {
+ LOG(ERROR) << formatted;
+ }
+ free(formatted);
+ va_end(args);
+ }
+};
- parser.AddSectionParser(
- "service", std::make_unique<ServiceParser>(&service_list, GetSubcontext(), std::nullopt,
- from_apex));
+template <typename Handler>
+LibXmlErrorHandler(Handler&&) -> LibXmlErrorHandler<Handler>;
+#endif // RECOVERY
+
+// Returns a Parser that accepts scripts from APEX modules. It supports `service` and `on`.
+Parser CreateApexConfigParser(ActionManager& action_manager, ServiceList& service_list) {
+ Parser parser;
+ auto subcontext = GetSubcontext();
+#ifndef RECOVERY
+ if (subcontext) {
+ const auto apex_info_list_file = "/apex/apex-info-list.xml";
+ auto error_handler = LibXmlErrorHandler([&](const auto& error_message) {
+ LOG(ERROR) << "Failed to read " << apex_info_list_file << ":" << error_message;
+ });
+ const auto apex_info_list = com::android::apex::readApexInfoList(apex_info_list_file);
+ if (apex_info_list.has_value()) {
+ std::vector<std::string> subcontext_apexes;
+ for (const auto& info : apex_info_list->getApexInfo()) {
+ if (info.hasPreinstalledModulePath() &&
+ subcontext->PathMatchesSubcontext(info.getPreinstalledModulePath())) {
+ subcontext_apexes.push_back(info.getModuleName());
+ }
+ }
+ subcontext->SetApexList(std::move(subcontext_apexes));
+ }
+ }
+#endif // RECOVERY
+ parser.AddSectionParser("service",
+ std::make_unique<ServiceParser>(&service_list, subcontext,
+ std::nullopt));
+ parser.AddSectionParser("on", std::make_unique<ActionParser>(&action_manager, subcontext));
+
return parser;
}
@@ -393,6 +449,81 @@
return {};
}
+int StopServicesFromApex(const std::string& apex_name) {
+ auto services = ServiceList::GetInstance().FindServicesByApexName(apex_name);
+ if (services.empty()) {
+ LOG(INFO) << "No service found for APEX: " << apex_name;
+ return 0;
+ }
+ std::set<std::string> service_names;
+ for (const auto& service : services) {
+ service_names.emplace(service->name());
+ }
+ constexpr std::chrono::milliseconds kServiceStopTimeout = 10s;
+ int still_running = StopServicesAndLogViolations(service_names, kServiceStopTimeout,
+ true /*SIGTERM*/);
+ // Send SIGKILL to ones that didn't terminate cleanly.
+ if (still_running > 0) {
+ still_running = StopServicesAndLogViolations(service_names, 0ms, false /*SIGKILL*/);
+ }
+ return still_running;
+}
+
+void RemoveServiceAndActionFromApex(const std::string& apex_name) {
+ // Remove services and actions that match apex name
+ ActionManager::GetInstance().RemoveActionIf([&](const std::unique_ptr<Action>& action) -> bool {
+ if (GetApexNameFromFileName(action->filename()) == apex_name) {
+ return true;
+ }
+ return false;
+ });
+ ServiceList::GetInstance().RemoveServiceIf([&](const std::unique_ptr<Service>& s) -> bool {
+ if (GetApexNameFromFileName(s->filename()) == apex_name) {
+ return true;
+ }
+ return false;
+ });
+}
+
+static Result<void> DoUnloadApex(const std::string& apex_name) {
+ if (StopServicesFromApex(apex_name) > 0) {
+ return Error() << "Unable to stop all service from " << apex_name;
+ }
+ RemoveServiceAndActionFromApex(apex_name);
+ return {};
+}
+
+static Result<void> UpdateApexLinkerConfig(const std::string& apex_name) {
+ // Do not invoke linkerconfig when there's no bin/ in the apex.
+ const std::string bin_path = "/apex/" + apex_name + "/bin";
+ if (access(bin_path.c_str(), R_OK) != 0) {
+ return {};
+ }
+ const char* linkerconfig_binary = "/apex/com.android.runtime/bin/linkerconfig";
+ const char* linkerconfig_target = "/linkerconfig";
+ const char* arguments[] = {linkerconfig_binary, "--target", linkerconfig_target, "--apex",
+ apex_name.c_str(), "--strict"};
+
+ if (logwrap_fork_execvp(arraysize(arguments), arguments, nullptr, false, LOG_KLOG, false,
+ nullptr) != 0) {
+ return ErrnoError() << "failed to execute linkerconfig";
+ }
+ LOG(INFO) << "Generated linker configuration for " << apex_name;
+ return {};
+}
+
+static Result<void> DoLoadApex(const std::string& apex_name) {
+ if(auto result = ParseApexConfigs(apex_name); !result.ok()) {
+ return result.error();
+ }
+
+ if (auto result = UpdateApexLinkerConfig(apex_name); !result.ok()) {
+ return result.error();
+ }
+
+ return {};
+}
+
enum class ControlTarget {
SERVICE, // function gets called for the named service
INTERFACE, // action gets called for every service that holds this interface
@@ -416,6 +547,17 @@
return control_message_functions;
}
+static Result<void> HandleApexControlMessage(std::string_view action, const std::string& name,
+ std::string_view message) {
+ if (action == "load") {
+ return DoLoadApex(name);
+ } else if (action == "unload") {
+ return DoUnloadApex(name);
+ } else {
+ return Error() << "Unknown control msg '" << message << "'";
+ }
+}
+
static bool HandleControlMessage(std::string_view message, const std::string& name,
pid_t from_pid) {
std::string cmdline_path = StringPrintf("proc/%d/cmdline", from_pid);
@@ -427,8 +569,20 @@
process_cmdline = "unknown process";
}
- Service* service = nullptr;
auto action = message;
+ if (ConsumePrefix(&action, "apex_")) {
+ if (auto result = HandleApexControlMessage(action, name, message); !result.ok()) {
+ LOG(ERROR) << "Control message: Could not ctl." << message << " for '" << name
+ << "' from pid: " << from_pid << " (" << process_cmdline
+ << "): " << result.error();
+ return false;
+ }
+ LOG(INFO) << "Control message: Processed ctl." << message << " for '" << name
+ << "' from pid: " << from_pid << " (" << process_cmdline << ")";
+ return true;
+ }
+
+ Service* service = nullptr;
if (ConsumePrefix(&action, "interface_")) {
service = ServiceList::GetInstance().FindInterface(name);
} else {
diff --git a/init/init.h b/init/init.h
index 4f686cb..063632a 100644
--- a/init/init.h
+++ b/init/init.h
@@ -29,7 +29,7 @@
namespace init {
Parser CreateParser(ActionManager& action_manager, ServiceList& service_list);
-Parser CreateServiceOnlyParser(ServiceList& service_list, bool from_apex);
+Parser CreateApexConfigParser(ActionManager& action_manager, ServiceList& service_list);
bool start_waiting_for_property(const char *name, const char *value);
@@ -46,5 +46,9 @@
int SecondStageMain(int argc, char** argv);
+int StopServicesFromApex(const std::string& apex_name);
+
+void RemoveServiceAndActionFromApex(const std::string& apex_name);
+
} // namespace init
} // namespace android
diff --git a/init/init_test.cpp b/init/init_test.cpp
index 8550ec8..5c1e9ef 100644
--- a/init/init_test.cpp
+++ b/init/init_test.cpp
@@ -15,11 +15,14 @@
*/
#include <functional>
+#include <string_view>
+#include <type_traits>
#include <android-base/file.h>
#include <android-base/logging.h>
#include <android-base/properties.h>
#include <gtest/gtest.h>
+#include <selinux/selinux.h>
#include "action.h"
#include "action_manager.h"
@@ -27,6 +30,7 @@
#include "builtin_arguments.h"
#include "builtins.h"
#include "import_parser.h"
+#include "init.h"
#include "keyword_map.h"
#include "parser.h"
#include "service.h"
@@ -35,6 +39,11 @@
#include "util.h"
using android::base::GetIntProperty;
+using android::base::GetProperty;
+using android::base::SetProperty;
+using android::base::StringReplace;
+using android::base::WaitForProperty;
+using namespace std::literals;
namespace android {
namespace init {
@@ -42,34 +51,34 @@
using ActionManagerCommand = std::function<void(ActionManager&)>;
void TestInit(const std::string& init_script_file, const BuiltinFunctionMap& test_function_map,
- const std::vector<ActionManagerCommand>& commands, ServiceList* service_list) {
- ActionManager am;
-
+ const std::vector<ActionManagerCommand>& commands, ActionManager* action_manager,
+ ServiceList* service_list) {
Action::set_function_map(&test_function_map);
Parser parser;
parser.AddSectionParser("service",
std::make_unique<ServiceParser>(service_list, nullptr, std::nullopt));
- parser.AddSectionParser("on", std::make_unique<ActionParser>(&am, nullptr));
+ parser.AddSectionParser("on", std::make_unique<ActionParser>(action_manager, nullptr));
parser.AddSectionParser("import", std::make_unique<ImportParser>(&parser));
ASSERT_TRUE(parser.ParseConfig(init_script_file));
for (const auto& command : commands) {
- command(am);
+ command(*action_manager);
}
- while (am.HasMoreCommands()) {
- am.ExecuteOneCommand();
+ while (action_manager->HasMoreCommands()) {
+ action_manager->ExecuteOneCommand();
}
}
void TestInitText(const std::string& init_script, const BuiltinFunctionMap& test_function_map,
- const std::vector<ActionManagerCommand>& commands, ServiceList* service_list) {
+ const std::vector<ActionManagerCommand>& commands, ActionManager* action_manager,
+ ServiceList* service_list) {
TemporaryFile tf;
ASSERT_TRUE(tf.fd != -1);
ASSERT_TRUE(android::base::WriteStringToFd(init_script, tf.fd));
- TestInit(tf.path, test_function_map, commands, service_list);
+ TestInit(tf.path, test_function_map, commands, action_manager, service_list);
}
TEST(init, SimpleEventTrigger) {
@@ -91,8 +100,9 @@
ActionManagerCommand trigger_boot = [](ActionManager& am) { am.QueueEventTrigger("boot"); };
std::vector<ActionManagerCommand> commands{trigger_boot};
+ ActionManager action_manager;
ServiceList service_list;
- TestInitText(init_script, test_function_map, commands, &service_list);
+ TestInitText(init_script, test_function_map, commands, &action_manager, &service_list);
EXPECT_TRUE(expect_true);
}
@@ -154,8 +164,10 @@
ActionManagerCommand trigger_boot = [](ActionManager& am) { am.QueueEventTrigger("boot"); };
std::vector<ActionManagerCommand> commands{trigger_boot};
+ ActionManager action_manager;
ServiceList service_list;
- TestInitText(init_script, test_function_map, commands, &service_list);
+ TestInitText(init_script, test_function_map, commands, &action_manager, &service_list);
+ EXPECT_EQ(3, num_executed);
}
TEST(init, OverrideService) {
@@ -169,8 +181,9 @@
)init";
+ ActionManager action_manager;
ServiceList service_list;
- TestInitText(init_script, BuiltinFunctionMap(), {}, &service_list);
+ TestInitText(init_script, BuiltinFunctionMap(), {}, &action_manager, &service_list);
ASSERT_EQ(1, std::distance(service_list.begin(), service_list.end()));
auto service = service_list.begin()->get();
@@ -180,6 +193,233 @@
EXPECT_TRUE(service->is_override());
}
+static std::string GetSecurityContext() {
+ char* ctx;
+ if (getcon(&ctx) == -1) {
+ ADD_FAILURE() << "Failed to call getcon : " << strerror(errno);
+ }
+ std::string result = std::string(ctx);
+ freecon(ctx);
+ return result;
+}
+
+void TestStartApexServices(const std::vector<std::string>& service_names,
+ const std::string& apex_name) {
+ for (auto const& svc : service_names) {
+ auto service = ServiceList::GetInstance().FindService(svc);
+ ASSERT_NE(nullptr, service);
+ ASSERT_RESULT_OK(service->Start());
+ ASSERT_TRUE(service->IsRunning());
+ LOG(INFO) << "Service " << svc << " is running";
+ if (!apex_name.empty()) {
+ service->set_filename("/apex/" + apex_name + "/init_test.rc");
+ } else {
+ service->set_filename("");
+ }
+ }
+ if (!apex_name.empty()) {
+ auto apex_services = ServiceList::GetInstance().FindServicesByApexName(apex_name);
+ EXPECT_EQ(service_names.size(), apex_services.size());
+ }
+}
+
+void TestStopApexServices(const std::vector<std::string>& service_names, bool expect_to_run) {
+ for (auto const& svc : service_names) {
+ auto service = ServiceList::GetInstance().FindService(svc);
+ ASSERT_NE(nullptr, service);
+ EXPECT_EQ(expect_to_run, service->IsRunning());
+ }
+}
+
+void TestRemoveApexService(const std::vector<std::string>& service_names, bool exist) {
+ for (auto const& svc : service_names) {
+ auto service = ServiceList::GetInstance().FindService(svc);
+ ASSERT_EQ(exist, service != nullptr);
+ }
+}
+
+void InitApexService(const std::string_view& init_template) {
+ std::string init_script = StringReplace(init_template, "$selabel",
+ GetSecurityContext(), true);
+
+ TestInitText(init_script, BuiltinFunctionMap(), {}, &ActionManager::GetInstance(),
+ &ServiceList::GetInstance());
+}
+
+void CleanupApexServices() {
+ std::vector<std::string> names;
+ for (const auto& s : ServiceList::GetInstance()) {
+ names.push_back(s->name());
+ }
+
+ for (const auto& name : names) {
+ auto s = ServiceList::GetInstance().FindService(name);
+ auto pid = s->pid();
+ ServiceList::GetInstance().RemoveService(*s);
+ if (pid > 0) {
+ kill(pid, SIGTERM);
+ kill(pid, SIGKILL);
+ }
+ }
+
+ ActionManager::GetInstance().RemoveActionIf([&](const std::unique_ptr<Action>& s) -> bool {
+ return true;
+ });
+}
+
+void TestApexServicesInit(const std::vector<std::string>& apex_services,
+ const std::vector<std::string>& other_apex_services,
+ const std::vector<std::string> non_apex_services) {
+ auto num_svc = apex_services.size() + other_apex_services.size() + non_apex_services.size();
+ ASSERT_EQ(num_svc, ServiceList::GetInstance().size());
+
+ TestStartApexServices(apex_services, "com.android.apex.test_service");
+ TestStartApexServices(other_apex_services, "com.android.other_apex.test_service");
+ TestStartApexServices(non_apex_services, /*apex_anme=*/ "");
+
+ StopServicesFromApex("com.android.apex.test_service");
+ TestStopApexServices(apex_services, /*expect_to_run=*/ false);
+ TestStopApexServices(other_apex_services, /*expect_to_run=*/ true);
+ TestStopApexServices(non_apex_services, /*expect_to_run=*/ true);
+
+ RemoveServiceAndActionFromApex("com.android.apex.test_service");
+ ASSERT_EQ(other_apex_services.size() + non_apex_services.size(),
+ ServiceList::GetInstance().size());
+
+ // TODO(b/244232142): Add test to check if actions are removed
+ TestRemoveApexService(apex_services, /*exist*/ false);
+ TestRemoveApexService(other_apex_services, /*exist*/ true);
+ TestRemoveApexService(non_apex_services, /*exist*/ true);
+
+ CleanupApexServices();
+}
+
+TEST(init, StopServiceByApexName) {
+ if (getuid() != 0) {
+ GTEST_SKIP() << "Must be run as root.";
+ return;
+ }
+ std::string_view script_template = R"init(
+service apex_test_service /system/bin/yes
+ user shell
+ group shell
+ seclabel $selabel
+)init";
+ InitApexService(script_template);
+ TestApexServicesInit({"apex_test_service"}, {}, {});
+}
+
+TEST(init, StopMultipleServicesByApexName) {
+ if (getuid() != 0) {
+ GTEST_SKIP() << "Must be run as root.";
+ return;
+ }
+ std::string_view script_template = R"init(
+service apex_test_service_multiple_a /system/bin/yes
+ user shell
+ group shell
+ seclabel $selabel
+service apex_test_service_multiple_b /system/bin/id
+ user shell
+ group shell
+ seclabel $selabel
+)init";
+ InitApexService(script_template);
+ TestApexServicesInit({"apex_test_service_multiple_a",
+ "apex_test_service_multiple_b"}, {}, {});
+}
+
+TEST(init, StopServicesFromMultipleApexes) {
+ if (getuid() != 0) {
+ GTEST_SKIP() << "Must be run as root.";
+ return;
+ }
+ std::string_view apex_script_template = R"init(
+service apex_test_service_multi_apex_a /system/bin/yes
+ user shell
+ group shell
+ seclabel $selabel
+service apex_test_service_multi_apex_b /system/bin/id
+ user shell
+ group shell
+ seclabel $selabel
+)init";
+ InitApexService(apex_script_template);
+
+ std::string_view other_apex_script_template = R"init(
+service apex_test_service_multi_apex_c /system/bin/yes
+ user shell
+ group shell
+ seclabel $selabel
+)init";
+ InitApexService(other_apex_script_template);
+
+ TestApexServicesInit({"apex_test_service_multi_apex_a",
+ "apex_test_service_multi_apex_b"}, {"apex_test_service_multi_apex_c"}, {});
+}
+
+TEST(init, StopServicesFromApexAndNonApex) {
+ if (getuid() != 0) {
+ GTEST_SKIP() << "Must be run as root.";
+ return;
+ }
+ std::string_view apex_script_template = R"init(
+service apex_test_service_apex_a /system/bin/yes
+ user shell
+ group shell
+ seclabel $selabel
+service apex_test_service_apex_b /system/bin/id
+ user shell
+ group shell
+ seclabel $selabel
+)init";
+ InitApexService(apex_script_template);
+
+ std::string_view non_apex_script_template = R"init(
+service apex_test_service_non_apex /system/bin/yes
+ user shell
+ group shell
+ seclabel $selabel
+)init";
+ InitApexService(non_apex_script_template);
+
+ TestApexServicesInit({"apex_test_service_apex_a",
+ "apex_test_service_apex_b"}, {}, {"apex_test_service_non_apex"});
+}
+
+TEST(init, StopServicesFromApexMixed) {
+ if (getuid() != 0) {
+ GTEST_SKIP() << "Must be run as root.";
+ return;
+ }
+ std::string_view script_template = R"init(
+service apex_test_service_mixed_a /system/bin/yes
+ user shell
+ group shell
+ seclabel $selabel
+)init";
+ InitApexService(script_template);
+
+ std::string_view other_apex_script_template = R"init(
+service apex_test_service_mixed_b /system/bin/yes
+ user shell
+ group shell
+ seclabel $selabel
+)init";
+ InitApexService(other_apex_script_template);
+
+ std::string_view non_apex_script_template = R"init(
+service apex_test_service_mixed_c /system/bin/yes
+ user shell
+ group shell
+ seclabel $selabel
+)init";
+ InitApexService(non_apex_script_template);
+
+ TestApexServicesInit({"apex_test_service_mixed_a"},
+ {"apex_test_service_mixed_b"}, {"apex_test_service_mixed_c"});
+}
+
TEST(init, EventTriggerOrderMultipleFiles) {
// 6 total files, which should have their triggers executed in the following order:
// 1: start - original script parsed
@@ -236,13 +476,100 @@
ActionManagerCommand trigger_boot = [](ActionManager& am) { am.QueueEventTrigger("boot"); };
std::vector<ActionManagerCommand> commands{trigger_boot};
+ ActionManager action_manager;
ServiceList service_list;
-
- TestInit(start.path, test_function_map, commands, &service_list);
+ TestInit(start.path, test_function_map, commands, &action_manager, &service_list);
EXPECT_EQ(6, num_executed);
}
+BuiltinFunctionMap GetTestFunctionMapForLazyLoad(int& num_executed, ActionManager& action_manager) {
+ auto execute_command = [&num_executed](const BuiltinArguments& args) {
+ EXPECT_EQ(2U, args.size());
+ EXPECT_EQ(++num_executed, std::stoi(args[1]));
+ return Result<void>{};
+ };
+ auto load_command = [&action_manager](const BuiltinArguments& args) -> Result<void> {
+ EXPECT_EQ(2U, args.size());
+ Parser parser;
+ parser.AddSectionParser("on", std::make_unique<ActionParser>(&action_manager, nullptr));
+ if (!parser.ParseConfig(args[1])) {
+ return Error() << "Failed to load";
+ }
+ return Result<void>{};
+ };
+ auto trigger_command = [&action_manager](const BuiltinArguments& args) {
+ EXPECT_EQ(2U, args.size());
+ LOG(INFO) << "Queue event trigger: " << args[1];
+ action_manager.QueueEventTrigger(args[1]);
+ return Result<void>{};
+ };
+ BuiltinFunctionMap test_function_map = {
+ {"execute", {1, 1, {false, execute_command}}},
+ {"load", {1, 1, {false, load_command}}},
+ {"trigger", {1, 1, {false, trigger_command}}},
+ };
+ return test_function_map;
+}
+
+TEST(init, LazilyLoadedActionsCantBeTriggeredByTheSameTrigger) {
+ // "start" script loads "lazy" script. Even though "lazy" scripts
+ // defines "on boot" action, it's not executed by the current "boot"
+ // event because it's already processed.
+ TemporaryFile lazy;
+ ASSERT_TRUE(lazy.fd != -1);
+ ASSERT_TRUE(android::base::WriteStringToFd("on boot\nexecute 2", lazy.fd));
+
+ TemporaryFile start;
+ // clang-format off
+ std::string start_script = "on boot\n"
+ "load " + std::string(lazy.path) + "\n"
+ "execute 1";
+ // clang-format on
+ ASSERT_TRUE(android::base::WriteStringToFd(start_script, start.fd));
+
+ int num_executed = 0;
+ ActionManager action_manager;
+ ServiceList service_list;
+ BuiltinFunctionMap test_function_map =
+ GetTestFunctionMapForLazyLoad(num_executed, action_manager);
+
+ ActionManagerCommand trigger_boot = [](ActionManager& am) { am.QueueEventTrigger("boot"); };
+ std::vector<ActionManagerCommand> commands{trigger_boot};
+ TestInit(start.path, test_function_map, commands, &action_manager, &service_list);
+
+ EXPECT_EQ(1, num_executed);
+}
+
+TEST(init, LazilyLoadedActionsCanBeTriggeredByTheNextTrigger) {
+ // "start" script loads "lazy" script and then triggers "next" event
+ // which executes "on next" action loaded by the previous command.
+ TemporaryFile lazy;
+ ASSERT_TRUE(lazy.fd != -1);
+ ASSERT_TRUE(android::base::WriteStringToFd("on next\nexecute 2", lazy.fd));
+
+ TemporaryFile start;
+ // clang-format off
+ std::string start_script = "on boot\n"
+ "load " + std::string(lazy.path) + "\n"
+ "execute 1\n"
+ "trigger next";
+ // clang-format on
+ ASSERT_TRUE(android::base::WriteStringToFd(start_script, start.fd));
+
+ int num_executed = 0;
+ ActionManager action_manager;
+ ServiceList service_list;
+ BuiltinFunctionMap test_function_map =
+ GetTestFunctionMapForLazyLoad(num_executed, action_manager);
+
+ ActionManagerCommand trigger_boot = [](ActionManager& am) { am.QueueEventTrigger("boot"); };
+ std::vector<ActionManagerCommand> commands{trigger_boot};
+ TestInit(start.path, test_function_map, commands, &action_manager, &service_list);
+
+ EXPECT_EQ(2, num_executed);
+}
+
TEST(init, RejectsCriticalAndOneshotService) {
if (GetIntProperty("ro.product.first_api_level", 10000) < 30) {
GTEST_SKIP() << "Test only valid for devices launching with R or later";
diff --git a/init/mount_namespace.cpp b/init/mount_namespace.cpp
index bce1cc3..fead371 100644
--- a/init/mount_namespace.cpp
+++ b/init/mount_namespace.cpp
@@ -190,15 +190,33 @@
return success;
}
+// Switch the mount namespace of the current process from bootstrap to default OR from default to
+// bootstrap. If the current mount namespace is neither bootstrap nor default, keep it that way.
Result<void> SwitchToMountNamespaceIfNeeded(MountNamespace target_mount_namespace) {
if (IsRecoveryMode() || !IsApexUpdatable()) {
// we don't have multiple namespaces in recovery mode or if apex is not updatable
return {};
}
- const auto& ns_id = target_mount_namespace == NS_BOOTSTRAP ? bootstrap_ns_id : default_ns_id;
+
+ const std::string current_namespace_id = GetMountNamespaceId();
+ MountNamespace current_mount_namespace;
+ if (current_namespace_id == bootstrap_ns_id) {
+ current_mount_namespace = NS_BOOTSTRAP;
+ } else if (current_namespace_id == default_ns_id) {
+ current_mount_namespace = NS_DEFAULT;
+ } else {
+ // services with `namespace mnt` start in its own mount namespace. So we need to keep it.
+ return {};
+ }
+
+ // We're already in the target mount namespace.
+ if (current_mount_namespace == target_mount_namespace) {
+ return {};
+ }
+
const auto& ns_fd = target_mount_namespace == NS_BOOTSTRAP ? bootstrap_ns_fd : default_ns_fd;
const auto& ns_name = target_mount_namespace == NS_BOOTSTRAP ? "bootstrap" : "default";
- if (ns_id != GetMountNamespaceId() && ns_fd.get() != -1) {
+ if (ns_fd.get() != -1) {
if (setns(ns_fd.get(), CLONE_NEWNS) == -1) {
return ErrnoError() << "Failed to switch to " << ns_name << " mount namespace.";
}
diff --git a/init/parser.cpp b/init/parser.cpp
index abc2017..0a388db 100644
--- a/init/parser.cpp
+++ b/init/parser.cpp
@@ -156,58 +156,6 @@
return true;
}
-std::vector<std::string> Parser::FilterVersionedConfigs(const std::vector<std::string>& configs,
- int active_sdk) {
- std::vector<std::string> filtered_configs;
-
- std::map<std::string, std::pair<std::string, int>> script_map;
- for (const auto& c : configs) {
- int sdk = 0;
- const std::vector<std::string> parts = android::base::Split(c, ".");
- std::string base;
- if (parts.size() < 2) {
- continue;
- }
-
- // parts[size()-1], aka the suffix, should be "rc" or "#rc"
- // any other pattern gets discarded
-
- const auto& suffix = parts[parts.size() - 1];
- if (suffix == "rc") {
- sdk = 0;
- } else {
- char trailer[9] = {0};
- int r = sscanf(suffix.c_str(), "%d%8s", &sdk, trailer);
- if (r != 2) {
- continue;
- }
- if (strlen(trailer) > 2 || strcmp(trailer, "rc") != 0) {
- continue;
- }
- }
-
- if (sdk < 0 || sdk > active_sdk) {
- continue;
- }
-
- base = parts[0];
- for (unsigned int i = 1; i < parts.size() - 1; i++) {
- base = base + "." + parts[i];
- }
-
- // is this preferred over what we already have
- auto it = script_map.find(base);
- if (it == script_map.end() || it->second.second < sdk) {
- script_map[base] = std::make_pair(c, sdk);
- }
- }
-
- for (const auto& m : script_map) {
- filtered_configs.push_back(m.second.first);
- }
- return filtered_configs;
-}
-
bool Parser::ParseConfigDir(const std::string& path) {
LOG(INFO) << "Parsing directory " << path << "...";
std::unique_ptr<DIR, decltype(&closedir)> config_dir(opendir(path.c_str()), closedir);
diff --git a/init/parser.h b/init/parser.h
index 2f4108f..95b0cd7 100644
--- a/init/parser.h
+++ b/init/parser.h
@@ -76,12 +76,6 @@
void AddSectionParser(const std::string& name, std::unique_ptr<SectionParser> parser);
void AddSingleLineParser(const std::string& prefix, LineCallback callback);
- // Compare all files */path.#rc and */path.rc with the same path prefix.
- // Keep the one with the highest # that doesn't exceed the system's SDK.
- // (.rc == .0rc for ranking purposes)
- std::vector<std::string> FilterVersionedConfigs(const std::vector<std::string>& configs,
- int active_sdk);
-
// Host init verifier check file permissions.
bool ParseConfigFileInsecure(const std::string& path);
diff --git a/init/persistent_properties.cpp b/init/persistent_properties.cpp
index 716f62e..d33a6b8 100644
--- a/init/persistent_properties.cpp
+++ b/init/persistent_properties.cpp
@@ -155,19 +155,33 @@
return *file_contents;
}
+Result<PersistentProperties> ParsePersistentPropertyFile(const std::string& file_contents) {
+ PersistentProperties persistent_properties;
+ if (!persistent_properties.ParseFromString(file_contents)) {
+ return Error() << "Unable to parse persistent property file: Could not parse protobuf";
+ }
+ for (auto& prop : persistent_properties.properties()) {
+ if (!StartsWith(prop.name(), "persist.")) {
+ return Error() << "Unable to load persistent property file: property '" << prop.name()
+ << "' doesn't start with 'persist.'";
+ }
+ }
+ return persistent_properties;
+}
+
} // namespace
Result<PersistentProperties> LoadPersistentPropertyFile() {
auto file_contents = ReadPersistentPropertyFile();
if (!file_contents.ok()) return file_contents.error();
- PersistentProperties persistent_properties;
- if (persistent_properties.ParseFromString(*file_contents)) return persistent_properties;
-
- // If the file cannot be parsed in either format, then we don't have any recovery
- // mechanisms, so we delete it to allow for future writes to take place successfully.
- unlink(persistent_property_filename.c_str());
- return Error() << "Unable to parse persistent property file: Could not parse protobuf";
+ auto persistent_properties = ParsePersistentPropertyFile(*file_contents);
+ if (!persistent_properties.ok()) {
+ // If the file cannot be parsed in either format, then we don't have any recovery
+ // mechanisms, so we delete it to allow for future writes to take place successfully.
+ unlink(persistent_property_filename.c_str());
+ }
+ return persistent_properties;
}
Result<void> WritePersistentPropertyFile(const PersistentProperties& persistent_properties) {
diff --git a/init/persistent_properties_test.cpp b/init/persistent_properties_test.cpp
index 60cecde..e5d26db 100644
--- a/init/persistent_properties_test.cpp
+++ b/init/persistent_properties_test.cpp
@@ -155,5 +155,28 @@
EXPECT_FALSE(it == read_back_properties.properties().end());
}
+TEST(persistent_properties, RejectNonPersistProperty) {
+ TemporaryFile tf;
+ ASSERT_TRUE(tf.fd != -1);
+ persistent_property_filename = tf.path;
+
+ WritePersistentProperty("notpersist.sys.locale", "pt-BR");
+
+ auto read_back_properties = LoadPersistentProperties();
+ EXPECT_EQ(read_back_properties.properties().size(), 0);
+
+ WritePersistentProperty("persist.sys.locale", "pt-BR");
+
+ read_back_properties = LoadPersistentProperties();
+ EXPECT_GT(read_back_properties.properties().size(), 0);
+
+ auto it = std::find_if(read_back_properties.properties().begin(),
+ read_back_properties.properties().end(), [](const auto& entry) {
+ return entry.name() == "persist.sys.locale" &&
+ entry.value() == "pt-BR";
+ });
+ EXPECT_FALSE(it == read_back_properties.properties().end());
+}
+
} // namespace init
} // namespace android
diff --git a/init/property_service.cpp b/init/property_service.cpp
index 9f7c215..7e92538 100644
--- a/init/property_service.cpp
+++ b/init/property_service.cpp
@@ -54,6 +54,7 @@
#include <android-base/logging.h>
#include <android-base/parseint.h>
#include <android-base/properties.h>
+#include <android-base/result.h>
#include <android-base/stringprintf.h>
#include <android-base/strings.h>
#include <property_info_parser/property_info_parser.h>
@@ -76,9 +77,12 @@
using namespace std::literals;
+using android::base::ErrnoError;
+using android::base::Error;
using android::base::GetProperty;
using android::base::ParseInt;
using android::base::ReadFileToString;
+using android::base::Result;
using android::base::Split;
using android::base::StartsWith;
using android::base::StringPrintf;
@@ -629,8 +633,8 @@
return result;
}
-static bool load_properties_from_file(const char*, const char*,
- std::map<std::string, std::string>*);
+static Result<void> load_properties_from_file(const char*, const char*,
+ std::map<std::string, std::string>*);
/*
* Filter is used to decide which properties to load: NULL loads all keys,
@@ -691,7 +695,10 @@
continue;
}
- load_properties_from_file(expanded_filename->c_str(), key, properties);
+ if (auto res = load_properties_from_file(expanded_filename->c_str(), key, properties);
+ !res.ok()) {
+ LOG(WARNING) << res.error();
+ }
} else {
value = strchr(key, '=');
if (!value) continue;
@@ -738,20 +745,19 @@
// Filter is used to decide which properties to load: NULL loads all keys,
// "ro.foo.*" is a prefix match, and "ro.foo.bar" is an exact match.
-static bool load_properties_from_file(const char* filename, const char* filter,
- std::map<std::string, std::string>* properties) {
+static Result<void> load_properties_from_file(const char* filename, const char* filter,
+ std::map<std::string, std::string>* properties) {
Timer t;
auto file_contents = ReadFile(filename);
if (!file_contents.ok()) {
- PLOG(WARNING) << "Couldn't load property file '" << filename
- << "': " << file_contents.error();
- return false;
+ return Error() << "Couldn't load property file '" << filename
+ << "': " << file_contents.error();
}
file_contents->push_back('\n');
LoadProperties(file_contents->data(), filter, filename, properties);
LOG(VERBOSE) << "(Loading properties from " << filename << " took " << t << ".)";
- return true;
+ return {};
}
static void LoadPropertiesFromSecondStageRes(std::map<std::string, std::string>* properties) {
@@ -760,7 +766,9 @@
CHECK(errno == ENOENT) << "Cannot access " << prop << ": " << strerror(errno);
return;
}
- load_properties_from_file(prop.c_str(), nullptr, properties);
+ if (auto res = load_properties_from_file(prop.c_str(), nullptr, properties); !res.ok()) {
+ LOG(WARNING) << res.error();
+ }
}
// persist.sys.usb.config values can't be combined on build-time when property
@@ -1058,7 +1066,10 @@
std::map<std::string, std::string> properties;
if (IsRecoveryMode()) {
- load_properties_from_file("/prop.default", nullptr, &properties);
+ if (auto res = load_properties_from_file("/prop.default", nullptr, &properties);
+ !res.ok()) {
+ LOG(ERROR) << res.error();
+ }
}
// /<part>/etc/build.prop is the canonical location of the build-time properties since S.
@@ -1067,7 +1078,7 @@
const auto load_properties_from_partition = [&properties](const std::string& partition,
int support_legacy_path_until) {
auto path = "/" + partition + "/etc/build.prop";
- if (load_properties_from_file(path.c_str(), nullptr, &properties)) {
+ if (load_properties_from_file(path.c_str(), nullptr, &properties).ok()) {
return;
}
// To read ro.<partition>.build.version.sdk, temporarily load the legacy paths into a
@@ -1105,7 +1116,13 @@
// Order matters here. The more the partition is specific to a product, the higher its
// precedence is.
LoadPropertiesFromSecondStageRes(&properties);
- load_properties_from_file("/system/build.prop", nullptr, &properties);
+
+ // system should have build.prop, unlike the other partitions
+ if (auto res = load_properties_from_file("/system/build.prop", nullptr, &properties);
+ !res.ok()) {
+ LOG(WARNING) << res.error();
+ }
+
load_properties_from_partition("system_ext", /* support_legacy_path_until */ 30);
load_properties_from_file("/system_dlkm/etc/build.prop", nullptr, &properties);
// TODO(b/117892318): uncomment the following condition when vendor.imgs for aosp_* targets are
@@ -1121,7 +1138,10 @@
if (access(kDebugRamdiskProp, R_OK) == 0) {
LOG(INFO) << "Loading " << kDebugRamdiskProp;
- load_properties_from_file(kDebugRamdiskProp, nullptr, &properties);
+ if (auto res = load_properties_from_file(kDebugRamdiskProp, nullptr, &properties);
+ !res.ok()) {
+ LOG(WARNING) << res.error();
+ }
}
for (const auto& [name, value] : properties) {
@@ -1331,6 +1351,11 @@
InitPropertySet(persistent_property_record.name(),
persistent_property_record.value());
}
+ // Apply debug ramdisk special settings after persistent properties are loaded.
+ if (android::base::GetBoolProperty("ro.force.debuggable", false)) {
+ // Always enable usb adb if device is booted with debug ramdisk.
+ update_sys_usb_config();
+ }
InitPropertySet("ro.persistent_properties.ready", "true");
persistent_properties_loaded = true;
break;
diff --git a/init/reboot.cpp b/init/reboot.cpp
index 41cf748..880674c 100644
--- a/init/reboot.cpp
+++ b/init/reboot.cpp
@@ -491,7 +491,7 @@
return ErrnoError() << "zram_backing_dev: swapoff (" << backing_dev << ")"
<< " failed";
}
- LOG(INFO) << "swapoff() took " << swap_timer;;
+ LOG(INFO) << "swapoff() took " << swap_timer;
if (!WriteStringToFile("1", ZRAM_RESET)) {
return Error() << "zram_backing_dev: reset (" << backing_dev << ")"
@@ -892,7 +892,16 @@
sub_reason = "ns_switch";
return Error() << "Failed to switch to bootstrap namespace";
}
- // Remove services that were defined in an APEX.
+ ActionManager::GetInstance().RemoveActionIf([](const auto& action) -> bool {
+ if (action->IsFromApex()) {
+ std::string trigger_name = action->BuildTriggersString();
+ LOG(INFO) << "Removing action (" << trigger_name << ") from (" << action->filename()
+ << ":" << action->line() << ")";
+ return true;
+ }
+ return false;
+ });
+ // Remove services that were defined in an APEX
ServiceList::GetInstance().RemoveServiceIf([](const std::unique_ptr<Service>& s) -> bool {
if (s->is_from_apex()) {
LOG(INFO) << "Removing service '" << s->name() << "' because it's defined in an APEX";
diff --git a/init/reboot_utils.cpp b/init/reboot_utils.cpp
index b3fa9fd..f8e1de0 100644
--- a/init/reboot_utils.cpp
+++ b/init/reboot_utils.cpp
@@ -26,8 +26,8 @@
#include <android-base/logging.h>
#include <android-base/properties.h>
#include <android-base/strings.h>
-#include <backtrace/Backtrace.h>
#include <cutils/android_reboot.h>
+#include <unwindstack/AndroidUnwinder.h>
#include "capabilities.h"
#include "reboot_utils.h"
@@ -157,13 +157,13 @@
// In the parent, let's try to get a backtrace then shutdown.
LOG(ERROR) << __FUNCTION__ << ": signal " << signal_number;
- std::unique_ptr<Backtrace> backtrace(
- Backtrace::Create(BACKTRACE_CURRENT_PROCESS, BACKTRACE_CURRENT_THREAD));
- if (!backtrace->Unwind(0)) {
- LOG(ERROR) << __FUNCTION__ << ": Failed to unwind callstack.";
+ unwindstack::AndroidLocalUnwinder unwinder;
+ unwindstack::AndroidUnwinderData data;
+ if (!unwinder.Unwind(data)) {
+ LOG(ERROR) << __FUNCTION__ << ": Failed to unwind callstack: " << data.GetErrorString();
}
- for (size_t i = 0; i < backtrace->NumFrames(); i++) {
- LOG(ERROR) << backtrace->FormatFrameData(i);
+ for (const auto& frame : data.frames) {
+ LOG(ERROR) << unwinder.FormatFrame(frame);
}
if (init_fatal_panic) {
LOG(ERROR) << __FUNCTION__ << ": Trigger crash";
diff --git a/init/security.cpp b/init/security.cpp
index 970696e..0e9f6c2 100644
--- a/init/security.cpp
+++ b/init/security.cpp
@@ -15,6 +15,7 @@
*/
#include "security.h"
+#include "util.h"
#include <errno.h>
#include <fcntl.h>
@@ -89,7 +90,7 @@
// Set /proc/sys/vm/mmap_rnd_bits and potentially
// /proc/sys/vm/mmap_rnd_compat_bits to the maximum supported values.
-// Returns -1 if unable to set these to an acceptable value.
+// Returns an error if unable to set these to an acceptable value.
//
// To support this sysctl, the following upstream commits are needed:
//
@@ -105,13 +106,20 @@
// uml does not support mmap_rnd_bits
return {};
#elif defined(__aarch64__)
- // arm64 supports 18 - 33 bits depending on pagesize and VA_SIZE
- if (SetMmapRndBitsMin(33, 24, false) && SetMmapRndBitsMin(16, 16, true)) {
+ // arm64 architecture supports 18 - 33 rnd bits depending on pagesize and
+ // VA_SIZE. However the kernel might have been compiled with a narrower
+ // range using CONFIG_ARCH_MMAP_RND_BITS_MIN/MAX. To use the maximum
+ // supported number of bits, we start from the theoretical maximum of 33
+ // bits and try smaller values until we reach 24 bits which is the
+ // Android-specific minimum. Don't go lower even if the configured maximum
+ // is smaller than 24.
+ if (SetMmapRndBitsMin(33, 24, false) && (!Has32BitAbi() || SetMmapRndBitsMin(16, 16, true))) {
return {};
}
#elif defined(__x86_64__)
- // x86_64 supports 28 - 32 bits
- if (SetMmapRndBitsMin(32, 32, false) && SetMmapRndBitsMin(16, 16, true)) {
+ // x86_64 supports 28 - 32 rnd bits, but Android wants to ensure that the
+ // theoretical maximum of 32 bits is always supported and used.
+ if (SetMmapRndBitsMin(32, 32, false) && (!Has32BitAbi() || SetMmapRndBitsMin(16, 16, true))) {
return {};
}
#elif defined(__arm__) || defined(__i386__)
diff --git a/init/selinux.cpp b/init/selinux.cpp
index be8c554..ab5b0a0 100644
--- a/init/selinux.cpp
+++ b/init/selinux.cpp
@@ -237,9 +237,9 @@
// If there is an odm partition, precompiled_sepolicy will be in
// odm/etc/selinux. Otherwise it will be in vendor/etc/selinux.
static constexpr const char vendor_precompiled_sepolicy[] =
- "/vendor/etc/selinux/precompiled_sepolicy";
+ "/vendor/etc/selinux/precompiled_sepolicy";
static constexpr const char odm_precompiled_sepolicy[] =
- "/odm/etc/selinux/precompiled_sepolicy";
+ "/odm/etc/selinux/precompiled_sepolicy";
if (access(odm_precompiled_sepolicy, R_OK) == 0) {
precompiled_sepolicy = odm_precompiled_sepolicy;
} else if (access(vendor_precompiled_sepolicy, R_OK) == 0) {
@@ -525,6 +525,32 @@
"apex_service_contexts", "apex_seapp_contexts",
"apex_test"};
+Result<void> CreateTmpfsDir() {
+ mode_t mode = 0744;
+ struct stat stat_data;
+ if (stat(kTmpfsDir.c_str(), &stat_data) != 0) {
+ if (errno != ENOENT) {
+ return ErrnoError() << "Could not stat " << kTmpfsDir;
+ }
+ if (mkdir(kTmpfsDir.c_str(), mode) != 0) {
+ return ErrnoError() << "Could not mkdir " << kTmpfsDir;
+ }
+ } else {
+ if (!S_ISDIR(stat_data.st_mode)) {
+ return Error() << kTmpfsDir << " exists and is not a directory.";
+ }
+ LOG(WARNING) << "Directory " << kTmpfsDir << " already exists";
+ }
+
+ // Need to manually call chmod because mkdir will create a folder with
+ // permissions mode & ~umask.
+ if (chmod(kTmpfsDir.c_str(), mode) != 0) {
+ return ErrnoError() << "Could not chmod " << kTmpfsDir;
+ }
+
+ return {};
+}
+
Result<void> PutFileInTmpfs(ZipArchiveHandle archive, const std::string& fileName) {
ZipEntry entry;
std::string dstPath = kTmpfsDir + fileName;
@@ -538,7 +564,7 @@
unique_fd fd(TEMP_FAILURE_RETRY(
open(dstPath.c_str(), O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC, S_IRUSR | S_IWUSR)));
if (fd == -1) {
- return Error() << "Failed to open " << dstPath;
+ return ErrnoError() << "Failed to open " << dstPath;
}
ret = ExtractEntryToFile(archive, &entry, fd);
@@ -568,6 +594,11 @@
auto handle_guard = android::base::make_scope_guard([&handle] { CloseArchive(handle); });
+ auto create = CreateTmpfsDir();
+ if (!create.ok()) {
+ return create.error();
+ }
+
for (const auto& file : kApexSepolicy) {
auto extract = PutFileInTmpfs(handle, file);
if (!extract.ok()) {
diff --git a/init/service.cpp b/init/service.cpp
index bd704cf..4cf409c 100644
--- a/init/service.cpp
+++ b/init/service.cpp
@@ -36,6 +36,8 @@
#include <processgroup/processgroup.h>
#include <selinux/selinux.h>
+#include <string>
+
#include "lmkd_service.h"
#include "service_list.h"
#include "util.h"
@@ -53,6 +55,7 @@
using android::base::boot_clock;
using android::base::GetBoolProperty;
+using android::base::GetIntProperty;
using android::base::GetProperty;
using android::base::Join;
using android::base::make_scope_guard;
@@ -131,13 +134,13 @@
std::chrono::time_point<std::chrono::steady_clock> Service::exec_service_started_;
Service::Service(const std::string& name, Subcontext* subcontext_for_restart_commands,
- const std::vector<std::string>& args, bool from_apex)
- : Service(name, 0, 0, 0, {}, 0, "", subcontext_for_restart_commands, args, from_apex) {}
+ const std::string& filename, const std::vector<std::string>& args)
+ : Service(name, 0, 0, 0, {}, 0, "", subcontext_for_restart_commands, filename, args) {}
Service::Service(const std::string& name, unsigned flags, uid_t uid, gid_t gid,
const std::vector<gid_t>& supp_gids, int namespace_flags,
const std::string& seclabel, Subcontext* subcontext_for_restart_commands,
- const std::vector<std::string>& args, bool from_apex)
+ const std::string& filename, const std::vector<std::string>& args)
: name_(name),
classnames_({"default"}),
flags_(flags),
@@ -157,7 +160,7 @@
oom_score_adjust_(DEFAULT_OOM_SCORE_ADJUST),
start_order_(0),
args_(args),
- from_apex_(from_apex) {}
+ filename_(filename) {}
void Service::NotifyStateChange(const std::string& new_state) const {
if ((flags_ & SVC_TEMPORARY) != 0) {
@@ -316,7 +319,23 @@
#else
static bool is_apex_updatable = false;
#endif
- const bool is_process_updatable = !use_bootstrap_ns_ && is_apex_updatable;
+ const bool use_default_mount_ns =
+ mount_namespace_.has_value() && *mount_namespace_ == NS_DEFAULT;
+ const bool is_process_updatable = use_default_mount_ns && is_apex_updatable;
+
+#ifdef SEGV_MTEAERR
+ // As a precaution, we only upgrade a service once per reboot, to limit
+ // the potential impact.
+ // TODO(b/244471804): Once we have a kernel API to get sicode, compare it to MTEAERR here.
+ bool should_upgrade_mte = siginfo.si_code != CLD_EXITED && siginfo.si_status == SIGSEGV &&
+ !upgraded_mte_;
+
+ if (should_upgrade_mte) {
+ LOG(INFO) << "Upgrading service " << name_ << " to sync MTE";
+ once_environment_vars_.emplace_back("BIONIC_MEMTAG_UPGRADE_SECS", "60");
+ upgraded_mte_ = true;
+ }
+#endif
// If we crash > 4 times in 'fatal_crash_window_' minutes or before boot_completed,
// reboot into bootloader or set crashing property
@@ -476,13 +495,15 @@
}
// Enters namespaces, sets environment variables, writes PID files and runs the service executable.
-void Service::RunService(const std::optional<MountNamespace>& override_mount_namespace,
- const std::vector<Descriptor>& descriptors,
+void Service::RunService(const std::vector<Descriptor>& descriptors,
std::unique_ptr<std::array<int, 2>, decltype(&ClosePipe)> pipefd) {
- if (auto result = EnterNamespaces(namespaces_, name_, override_mount_namespace); !result.ok()) {
+ if (auto result = EnterNamespaces(namespaces_, name_, mount_namespace_); !result.ok()) {
LOG(FATAL) << "Service '" << name_ << "' failed to set up namespaces: " << result.error();
}
+ for (const auto& [key, value] : once_environment_vars_) {
+ setenv(key.c_str(), value.c_str(), 1);
+ }
for (const auto& [key, value] : environment_vars_) {
setenv(key.c_str(), value.c_str(), 1);
}
@@ -583,26 +604,9 @@
scon = *result;
}
- // APEXd is always started in the "current" namespace because it is the process to set up
- // the current namespace.
- const bool is_apexd = args_[0] == "/system/bin/apexd";
-
- if (!IsDefaultMountNamespaceReady() && !is_apexd) {
- // If this service is started before APEXes and corresponding linker configuration
- // get available, mark it as pre-apexd one. Note that this marking is
- // permanent. So for example, if the service is re-launched (e.g., due
- // to crash), it is still recognized as pre-apexd... for consistency.
- use_bootstrap_ns_ = true;
- }
-
- // For pre-apexd services, override mount namespace as "bootstrap" one before starting.
- // Note: "ueventd" is supposed to be run in "default" mount namespace even if it's pre-apexd
- // to support loading firmwares from APEXes.
- std::optional<MountNamespace> override_mount_namespace;
- if (name_ == "ueventd") {
- override_mount_namespace = NS_DEFAULT;
- } else if (use_bootstrap_ns_) {
- override_mount_namespace = NS_BOOTSTRAP;
+ if (!mount_namespace_.has_value()) {
+ // remember from which mount namespace the service should start
+ SetMountNamespace();
}
post_data_ = ServiceList::GetInstance().IsPostData();
@@ -635,7 +639,7 @@
if (pid == 0) {
umask(077);
- RunService(override_mount_namespace, descriptors, std::move(pipefd));
+ RunService(descriptors, std::move(pipefd));
_exit(127);
}
@@ -644,6 +648,8 @@
return ErrnoError() << "Failed to fork";
}
+ once_environment_vars_.clear();
+
if (oom_score_adjust_ != DEFAULT_OOM_SCORE_ADJUST) {
std::string oom_str = std::to_string(oom_score_adjust_);
std::string oom_file = StringPrintf("/proc/%d/oom_score_adj", pid);
@@ -669,6 +675,11 @@
<< ") failed for service '" << name_ << "'";
}
+ // When the blkio controller is mounted in the v1 hierarchy, NormalIoPriority is
+ // the default (/dev/blkio). When the blkio controller is mounted in the v2 hierarchy, the
+ // NormalIoPriority profile has to be applied explicitly.
+ SetProcessProfiles(proc_attr_.uid, pid_, {"NormalIoPriority"});
+
if (use_memcg) {
ConfigureMemcg();
}
@@ -686,6 +697,33 @@
return {};
}
+// Set mount namespace for the service.
+// The reason why remember the mount namespace:
+// If this service is started before APEXes and corresponding linker configuration
+// get available, mark it as pre-apexd one. Note that this marking is
+// permanent. So for example, if the service is re-launched (e.g., due
+// to crash), it is still recognized as pre-apexd... for consistency.
+void Service::SetMountNamespace() {
+ // APEXd is always started in the "current" namespace because it is the process to set up
+ // the current namespace. So, leave mount_namespace_ as empty.
+ if (args_[0] == "/system/bin/apexd") {
+ return;
+ }
+ // Services in the following list start in the "default" mount namespace.
+ // Note that they should use bootstrap bionic if they start before APEXes are ready.
+ static const std::set<std::string> kUseDefaultMountNamespace = {
+ "ueventd", // load firmwares from APEXes
+ "hwservicemanager", // load VINTF fragments from APEXes
+ "servicemanager", // load VINTF fragments from APEXes
+ };
+ if (kUseDefaultMountNamespace.find(name_) != kUseDefaultMountNamespace.end()) {
+ mount_namespace_ = NS_DEFAULT;
+ return;
+ }
+ // Use the "default" mount namespace only if it's ready
+ mount_namespace_ = IsDefaultMountNamespaceReady() ? NS_DEFAULT : NS_BOOTSTRAP;
+}
+
void Service::SetStartedInFirstStage(pid_t pid) {
LOG(INFO) << "adding first-stage service '" << name_ << "'...";
@@ -851,7 +889,7 @@
}
return std::make_unique<Service>(name, flags, *uid, *gid, supp_gids, namespace_flags, seclabel,
- nullptr, str_args, false);
+ nullptr, /*filename=*/"", str_args);
}
// This is used for snapuserd_proxy, which hands off a socket to snapuserd. It's
diff --git a/init/service.h b/init/service.h
index c314aa1..ab19865 100644
--- a/init/service.h
+++ b/init/service.h
@@ -32,6 +32,7 @@
#include "action.h"
#include "capabilities.h"
#include "keyword_map.h"
+#include "mount_namespace.h"
#include "parser.h"
#include "service_utils.h"
#include "subcontext.h"
@@ -65,12 +66,12 @@
public:
Service(const std::string& name, Subcontext* subcontext_for_restart_commands,
- const std::vector<std::string>& args, bool from_apex = false);
+ const std::string& filename, const std::vector<std::string>& args);
Service(const std::string& name, unsigned flags, uid_t uid, gid_t gid,
const std::vector<gid_t>& supp_gids, int namespace_flags, const std::string& seclabel,
- Subcontext* subcontext_for_restart_commands, const std::vector<std::string>& args,
- bool from_apex = false);
+ Subcontext* subcontext_for_restart_commands, const std::string& filename,
+ const std::vector<std::string>& args);
static Result<std::unique_ptr<Service>> MakeTemporaryOneshotService(
const std::vector<std::string>& args);
@@ -133,7 +134,7 @@
const std::vector<std::string>& args() const { return args_; }
bool is_updatable() const { return updatable_; }
bool is_post_data() const { return post_data_; }
- bool is_from_apex() const { return from_apex_; }
+ bool is_from_apex() const { return base::StartsWith(filename_, "/apex/"); }
void set_oneshot(bool value) {
if (value) {
flags_ |= SVC_ONESHOT;
@@ -142,6 +143,8 @@
}
}
Subcontext* subcontext() const { return subcontext_; }
+ const std::string& filename() const { return filename_; }
+ void set_filename(const std::string& name) { filename_ = name; }
private:
void NotifyStateChange(const std::string& new_state) const;
@@ -152,10 +155,9 @@
Result<void> CheckConsole();
void ConfigureMemcg();
void RunService(
- const std::optional<MountNamespace>& override_mount_namespace,
const std::vector<Descriptor>& descriptors,
std::unique_ptr<std::array<int, 2>, void (*)(const std::array<int, 2>* pipe)> pipefd);
-
+ void SetMountNamespace();
static unsigned long next_start_order_;
static bool is_exec_service_running_;
static std::chrono::time_point<std::chrono::steady_clock> exec_service_started_;
@@ -169,6 +171,7 @@
android::base::boot_clock::time_point time_started_; // time of last start
android::base::boot_clock::time_point time_crashed_; // first crash within inspection window
int crash_count_; // number of times crashed within window
+ bool upgraded_mte_ = false; // whether we upgraded async MTE -> sync MTE before
std::chrono::minutes fatal_crash_window_ = 4min; // fatal() when more than 4 crashes in it
std::optional<std::string> fatal_reboot_target_; // reboot target of fatal handler
@@ -181,6 +184,8 @@
std::vector<SocketDescriptor> sockets_;
std::vector<FileDescriptor> files_;
std::vector<std::pair<std::string, std::string>> environment_vars_;
+ // Environment variables that only get applied to the next run.
+ std::vector<std::pair<std::string, std::string>> once_environment_vars_;
Subcontext* subcontext_;
Action onrestart_; // Commands to execute on restart.
@@ -220,13 +225,13 @@
std::vector<std::function<void(const siginfo_t& siginfo)>> reap_callbacks_;
- bool use_bootstrap_ns_ = false;
+ std::optional<MountNamespace> mount_namespace_;
bool post_data_ = false;
std::optional<std::string> on_failure_reboot_target_;
- bool from_apex_ = false;
+ std::string filename_;
};
} // namespace init
diff --git a/init/service_list.h b/init/service_list.h
index 555da25..f858bc3 100644
--- a/init/service_list.h
+++ b/init/service_list.h
@@ -16,10 +16,14 @@
#pragma once
+#include <iterator>
#include <memory>
#include <vector>
+#include <android-base/logging.h>
+
#include "service.h"
+#include "util.h"
namespace android {
namespace init {
@@ -52,6 +56,17 @@
return nullptr;
}
+ std::vector<Service*> FindServicesByApexName(const std::string& apex_name) const {
+ CHECK(!apex_name.empty()) << "APEX name cannot be empty";
+ std::vector<Service*> matches;
+ for (const auto& svc : services_) {
+ if (GetApexNameFromFileName(svc->filename()) == apex_name) {
+ matches.emplace_back(svc.get());
+ }
+ }
+ return matches;
+ }
+
Service* FindInterface(const std::string& interface_name) {
for (const auto& svc : services_) {
if (svc->interfaces().count(interface_name) > 0) {
@@ -79,6 +94,8 @@
services_update_finished_ = false;
}
+ auto size() const { return services_.size(); }
+
private:
std::vector<std::unique_ptr<Service>> services_;
diff --git a/init/service_parser.cpp b/init/service_parser.cpp
index 9e914ee..32c57c4 100644
--- a/init/service_parser.cpp
+++ b/init/service_parser.cpp
@@ -647,7 +647,7 @@
}
}
- service_ = std::make_unique<Service>(name, restart_action_subcontext, str_args, from_apex_);
+ service_ = std::make_unique<Service>(name, restart_action_subcontext, filename, str_args);
return {};
}
diff --git a/init/service_parser.h b/init/service_parser.h
index 0fd2da5..54503dd 100644
--- a/init/service_parser.h
+++ b/init/service_parser.h
@@ -31,13 +31,11 @@
public:
ServiceParser(
ServiceList* service_list, Subcontext* subcontext,
- const std::optional<InterfaceInheritanceHierarchyMap>& interface_inheritance_hierarchy,
- bool from_apex = false)
+ const std::optional<InterfaceInheritanceHierarchyMap>& interface_inheritance_hierarchy)
: service_list_(service_list),
subcontext_(subcontext),
interface_inheritance_hierarchy_(interface_inheritance_hierarchy),
- service_(nullptr),
- from_apex_(from_apex) {}
+ service_(nullptr) {}
Result<void> ParseSection(std::vector<std::string>&& args, const std::string& filename,
int line) override;
Result<void> ParseLineSection(std::vector<std::string>&& args, int line) override;
@@ -92,7 +90,6 @@
std::optional<InterfaceInheritanceHierarchyMap> interface_inheritance_hierarchy_;
std::unique_ptr<Service> service_;
std::string filename_;
- bool from_apex_ = false;
};
} // namespace init
diff --git a/init/service_test.cpp b/init/service_test.cpp
index 22ee844..87a2ce5 100644
--- a/init/service_test.cpp
+++ b/init/service_test.cpp
@@ -39,7 +39,7 @@
std::vector<std::string> dummy_args{"/bin/test"};
Service* service_in_old_memory =
- new (old_memory) Service("test_old_memory", nullptr, dummy_args);
+ new (old_memory) Service("test_old_memory", nullptr, /*filename=*/"", dummy_args);
EXPECT_EQ(0U, service_in_old_memory->flags());
EXPECT_EQ(0, service_in_old_memory->pid());
@@ -58,7 +58,8 @@
}
Service* service_in_old_memory2 = new (old_memory) Service(
- "test_old_memory", 0U, 0U, 0U, std::vector<gid_t>(), 0U, "", nullptr, dummy_args);
+ "test_old_memory", 0U, 0U, 0U, std::vector<gid_t>(), 0U, "",
+ nullptr, /*filename=*/"", dummy_args);
EXPECT_EQ(0U, service_in_old_memory2->flags());
EXPECT_EQ(0, service_in_old_memory2->pid());
diff --git a/init/subcontext.cpp b/init/subcontext.cpp
index 7aa4a9d..961e006 100644
--- a/init/subcontext.cpp
+++ b/init/subcontext.cpp
@@ -250,7 +250,11 @@
Fork();
}
-bool Subcontext::PathMatchesSubcontext(const std::string& path) {
+bool Subcontext::PathMatchesSubcontext(const std::string& path) const {
+ auto apex_name = GetApexNameFromFileName(path);
+ if (!apex_name.empty()) {
+ return std::find(apex_list_.begin(), apex_list_.end(), apex_name) != apex_list_.end();
+ }
for (const auto& prefix : path_prefixes_) {
if (StartsWith(path, prefix)) {
return true;
@@ -259,6 +263,10 @@
return false;
}
+void Subcontext::SetApexList(std::vector<std::string>&& apex_list) {
+ apex_list_ = std::move(apex_list);
+}
+
Result<SubcontextReply> Subcontext::TransmitMessage(const SubcontextCommand& subcontext_command) {
if (auto result = SendMessage(socket_, subcontext_command); !result.ok()) {
Restart();
@@ -370,6 +378,9 @@
}
void SubcontextTerminate() {
+ if (!subcontext) {
+ return;
+ }
subcontext_terminated_by_shutdown = true;
kill(subcontext->pid(), SIGTERM);
}
diff --git a/init/subcontext.h b/init/subcontext.h
index cb4138e..8acc032 100644
--- a/init/subcontext.h
+++ b/init/subcontext.h
@@ -46,7 +46,8 @@
Result<void> Execute(const std::vector<std::string>& args);
Result<std::vector<std::string>> ExpandArgs(const std::vector<std::string>& args);
void Restart();
- bool PathMatchesSubcontext(const std::string& path);
+ bool PathMatchesSubcontext(const std::string& path) const;
+ void SetApexList(std::vector<std::string>&& apex_list);
const std::string& context() const { return context_; }
pid_t pid() const { return pid_; }
@@ -56,6 +57,7 @@
Result<SubcontextReply> TransmitMessage(const SubcontextCommand& subcontext_command);
std::vector<std::string> path_prefixes_;
+ std::vector<std::string> apex_list_;
std::string context_;
pid_t pid_;
android::base::unique_fd socket_;
diff --git a/init/test_upgrade_mte/Android.bp b/init/test_upgrade_mte/Android.bp
new file mode 100644
index 0000000..1bfc76c
--- /dev/null
+++ b/init/test_upgrade_mte/Android.bp
@@ -0,0 +1,41 @@
+// Copyright (C) 2022 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package {
+ default_applicable_licenses: ["system_core_init_license"],
+}
+
+cc_binary {
+ name: "mte_upgrade_test_helper",
+ srcs: ["mte_upgrade_test_helper.cpp"],
+ sanitize: {
+ memtag_heap: true,
+ diag: {
+ memtag_heap: false,
+ },
+ },
+ init_rc: [
+ "mte_upgrade_test.rc",
+ ],
+}
+
+java_test_host {
+ name: "mte_upgrade_test",
+ libs: ["tradefed"],
+ static_libs: ["frameworks-base-hostutils", "cts-install-lib-host"],
+ srcs: ["src/**/MteUpgradeTest.java", ":libtombstone_proto-src"],
+ data: [":mte_upgrade_test_helper", "mte_upgrade_test.rc" ],
+ test_config: "AndroidTest.xml",
+ test_suites: ["general-tests"],
+}
diff --git a/init/test_upgrade_mte/AndroidTest.xml b/init/test_upgrade_mte/AndroidTest.xml
new file mode 100644
index 0000000..b89cde8
--- /dev/null
+++ b/init/test_upgrade_mte/AndroidTest.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2022 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.
+-->
+<configuration description="Runs the MTE upgrade tests">
+ <option name="test-suite-tag" value="init_test_upgrade_mte" />
+ <option name="test-suite-tag" value="apct" />
+ <target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer"/>
+ <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer">
+ <option name="cleanup" value="true" />
+ <option name="remount-system" value="true" />
+ <option name="push" value="mte_upgrade_test.rc->/system/etc/init/mte_upgrade_test.rc" />
+ <option name="push" value="mte_upgrade_test_helper->/system/bin/mte_upgrade_test_helper" />
+ <option name="push" value="mte_upgrade_test_helper->/data/local/tmp/app_process64" />
+ </target_preparer>
+ <test class="com.android.tradefed.testtype.HostTest" >
+ <option name="jar" value="mte_upgrade_test.jar" />
+ </test>
+</configuration>
\ No newline at end of file
diff --git a/init/test_upgrade_mte/mte_upgrade_test.rc b/init/test_upgrade_mte/mte_upgrade_test.rc
new file mode 100644
index 0000000..a3e596c
--- /dev/null
+++ b/init/test_upgrade_mte/mte_upgrade_test.rc
@@ -0,0 +1,24 @@
+# Copyright (C) 2022 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.
+
+service mte_upgrade_test_helper /system/bin/mte_upgrade_test_helper ${sys.mte_crash_test_uuid}
+ class late_start
+ disabled
+ seclabel u:r:su:s0
+
+service mte_upgrade_test_helper_overridden /system/bin/mte_upgrade_test_helper ${sys.mte_crash_test_uuid}
+ class late_start
+ disabled
+ seclabel u:r:su:s0
+ setenv BIONIC_MEMTAG_UPGRADE_SECS 0
diff --git a/init/test_upgrade_mte/mte_upgrade_test_helper.cpp b/init/test_upgrade_mte/mte_upgrade_test_helper.cpp
new file mode 100644
index 0000000..10af06b
--- /dev/null
+++ b/init/test_upgrade_mte/mte_upgrade_test_helper.cpp
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2022 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 <linux/prctl.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/prctl.h>
+#include <time.h>
+#include <unistd.h>
+
+int MaybeDowngrade() {
+ int res = prctl(PR_GET_TAGGED_ADDR_CTRL, 0, 0, 0, 0);
+ if (res == -1) return 1;
+ if (static_cast<unsigned long>(res) & PR_MTE_TCF_ASYNC) return 2;
+ time_t t = time(nullptr);
+ while (time(nullptr) - t < 100) {
+ res = prctl(PR_GET_TAGGED_ADDR_CTRL, 0, 0, 0, 0);
+ if (static_cast<unsigned long>(res) & PR_MTE_TCF_ASYNC) {
+ return 0;
+ }
+ sleep(1);
+ }
+ return 3;
+}
+
+int main(int argc, char** argv) {
+ if (argc == 2 && strcmp(argv[1], "--check-downgrade") == 0) {
+ return MaybeDowngrade();
+ }
+ int res = prctl(PR_GET_TAGGED_ADDR_CTRL, 0, 0, 0, 0);
+ if (res == -1) abort();
+ if (argc == 2 && strcmp(argv[1], "--get-mode") == 0) {
+ if (res & PR_MTE_TCF_ASYNC) {
+ return 1;
+ }
+ if (res & PR_MTE_TCF_SYNC) {
+ return 2;
+ }
+ abort();
+ }
+
+ if (res & PR_MTE_TCF_ASYNC && res & PR_MTE_TCF_SYNC) {
+ // Disallow automatic upgrade from ASYNC mode.
+ if (prctl(PR_SET_TAGGED_ADDR_CTRL, res & ~PR_MTE_TCF_SYNC, 0, 0, 0) == -1) abort();
+ }
+ volatile char* f = (char*)malloc(1);
+ f[17] = 'x';
+ char buf[1];
+ read(1, buf, 1);
+ return 0;
+}
diff --git a/init/test_upgrade_mte/src/com/android/tests/init/MteUpgradeTest.java b/init/test_upgrade_mte/src/com/android/tests/init/MteUpgradeTest.java
new file mode 100644
index 0000000..f4e4a9c
--- /dev/null
+++ b/init/test_upgrade_mte/src/com/android/tests/init/MteUpgradeTest.java
@@ -0,0 +1,151 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.tests.init;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.junit.Assume.assumeTrue;
+
+import com.android.server.os.TombstoneProtos.Tombstone;
+import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
+import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test;
+import com.android.tradefed.util.CommandResult;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.InputStream;
+import java.util.ArrayList;
+
+@RunWith(DeviceJUnit4ClassRunner.class)
+public class MteUpgradeTest extends BaseHostJUnit4Test {
+ @Before
+ public void setUp() throws Exception {
+ CommandResult result =
+ getDevice().executeShellV2Command("/system/bin/mte_upgrade_test_helper --checking");
+ assumeTrue("mte_upgrade_test_binary needs to segfault", result.getExitCode() == 139);
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ // Easier here than in a finally in testCrash, and doesn't really hurt.
+ getDevice().executeShellV2Command("stop mte_upgrade_test_helper");
+ getDevice().executeShellV2Command("stop mte_upgrade_test_helper_overridden");
+ getDevice().setProperty("sys.mte_crash_test_uuid", "");
+ }
+
+ Tombstone parseTombstone(String tombstonePath) throws Exception {
+ File tombstoneFile = getDevice().pullFile(tombstonePath);
+ InputStream istr = new FileInputStream(tombstoneFile);
+ Tombstone tombstoneProto;
+ try {
+ tombstoneProto = Tombstone.parseFrom(istr);
+ } finally {
+ istr.close();
+ }
+ return tombstoneProto;
+ }
+
+ @Test
+ public void testCrash() throws Exception {
+ String uuid = java.util.UUID.randomUUID().toString();
+ getDevice().reboot();
+ assertThat(getDevice().setProperty("sys.mte_crash_test_uuid", uuid)).isTrue();
+
+ CommandResult result = getDevice().executeShellV2Command("start mte_upgrade_test_helper");
+ assertThat(result.getExitCode()).isEqualTo(0);
+ java.lang.Thread.sleep(20000);
+ String[] tombstonesAfter = getDevice().getChildren("/data/tombstones");
+ ArrayList<String> segvCodeNames = new ArrayList<String>();
+ for (String tombstone : tombstonesAfter) {
+ if (!tombstone.endsWith(".pb")) {
+ continue;
+ }
+ String tombstoneFilename = "/data/tombstones/" + tombstone;
+ Tombstone tombstoneProto = parseTombstone(tombstoneFilename);
+ if (!tombstoneProto.getCommandLineList().stream().anyMatch(x -> x.contains(uuid))) {
+ continue;
+ }
+ assertThat(tombstoneProto.getSignalInfo().getName()).isEqualTo("SIGSEGV");
+ segvCodeNames.add(tombstoneProto.getSignalInfo().getCodeName());
+ getDevice().deleteFile(tombstoneFilename);
+ // remove the non .pb file as well.
+ getDevice().deleteFile(tombstoneFilename.substring(0, tombstoneFilename.length() - 3));
+ }
+ assertThat(segvCodeNames.size()).isAtLeast(3);
+ assertThat(segvCodeNames.get(0)).isEqualTo("SEGV_MTEAERR");
+ assertThat(segvCodeNames.get(1)).isEqualTo("SEGV_MTESERR");
+ assertThat(segvCodeNames.get(2)).isEqualTo("SEGV_MTEAERR");
+ }
+
+ @Test
+ public void testCrashOverridden() throws Exception {
+ String uuid = java.util.UUID.randomUUID().toString();
+ getDevice().reboot();
+ assertThat(getDevice().setProperty("sys.mte_crash_test_uuid", uuid)).isTrue();
+
+ CommandResult result =
+ getDevice().executeShellV2Command("start mte_upgrade_test_helper_overridden");
+ assertThat(result.getExitCode()).isEqualTo(0);
+ java.lang.Thread.sleep(20000);
+ String[] tombstonesAfter = getDevice().getChildren("/data/tombstones");
+ ArrayList<String> segvCodeNames = new ArrayList<String>();
+ for (String tombstone : tombstonesAfter) {
+ if (!tombstone.endsWith(".pb")) {
+ continue;
+ }
+ String tombstoneFilename = "/data/tombstones/" + tombstone;
+ Tombstone tombstoneProto = parseTombstone(tombstoneFilename);
+ if (!tombstoneProto.getCommandLineList().stream().anyMatch(x -> x.contains(uuid))) {
+ continue;
+ }
+ assertThat(tombstoneProto.getSignalInfo().getName()).isEqualTo("SIGSEGV");
+ segvCodeNames.add(tombstoneProto.getSignalInfo().getCodeName());
+ getDevice().deleteFile(tombstoneFilename);
+ // remove the non .pb file as well.
+ getDevice().deleteFile(tombstoneFilename.substring(0, tombstoneFilename.length() - 3));
+ }
+ assertThat(segvCodeNames.size()).isAtLeast(3);
+ assertThat(segvCodeNames.get(0)).isEqualTo("SEGV_MTEAERR");
+ assertThat(segvCodeNames.get(1)).isEqualTo("SEGV_MTEAERR");
+ assertThat(segvCodeNames.get(2)).isEqualTo("SEGV_MTEAERR");
+ }
+
+ @Test
+ public void testDowngrade() throws Exception {
+ CommandResult result =
+ getDevice()
+ .executeShellV2Command(
+ "MEMTAG_OPTIONS=async BIONIC_MEMTAG_UPGRADE_SECS=5"
+ + " /system/bin/mte_upgrade_test_helper --check-downgrade");
+ assertThat(result.getExitCode()).isEqualTo(0);
+ }
+
+ @Test
+ public void testAppProcess() throws Exception {
+ CommandResult result =
+ getDevice()
+ .executeShellV2Command(
+ "MEMTAG_OPTIONS=async BIONIC_MEMTAG_UPGRADE_SECS=5"
+ + " /data/local/tmp/app_process64 --get-mode");
+ assertThat(result.getExitCode()).isEqualTo(1); // ASYNC
+ }
+}
diff --git a/init/ueventd.cpp b/init/ueventd.cpp
index c6bf708..586e2cf 100644
--- a/init/ueventd.cpp
+++ b/init/ueventd.cpp
@@ -298,21 +298,31 @@
static UeventdConfiguration GetConfiguration() {
auto hardware = android::base::GetProperty("ro.hardware", "");
- std::vector<std::string> legacy_paths{"/vendor/ueventd.rc", "/odm/ueventd.rc",
- "/ueventd." + hardware + ".rc"};
+
+ struct LegacyPathInfo {
+ std::string legacy_path;
+ std::string preferred;
+ };
+ std::vector<LegacyPathInfo> legacy_paths{
+ {"/vendor/ueventd.rc", "/vendor/etc/ueventd.rc"},
+ {"/odm/ueventd.rc", "/odm/etc/ueventd.rc"},
+ {"/ueventd." + hardware + ".rc", "another ueventd.rc file"}};
std::vector<std::string> canonical{"/system/etc/ueventd.rc"};
if (android::base::GetIntProperty("ro.product.first_api_level", 10000) < __ANDROID_API_T__) {
// TODO: Remove these legacy paths once Android S is no longer supported.
- canonical.insert(canonical.end(), legacy_paths.begin(), legacy_paths.end());
+ for (const auto& info : legacy_paths) {
+ canonical.push_back(info.legacy_path);
+ }
} else {
// Warn if newer device is using legacy paths.
- for (const auto& path : legacy_paths) {
- if (access(path.c_str(), F_OK) == 0) {
+ for (const auto& info : legacy_paths) {
+ if (access(info.legacy_path.c_str(), F_OK) == 0) {
LOG(FATAL_WITHOUT_ABORT)
<< "Legacy ueventd configuration file detected and will not be parsed: "
- << path;
+ << info.legacy_path << ". Please move your configuration to "
+ << info.preferred << " instead.";
}
}
}
diff --git a/init/util.cpp b/init/util.cpp
index d1e518b..2d40142 100644
--- a/init/util.cpp
+++ b/init/util.cpp
@@ -30,6 +30,7 @@
#include <time.h>
#include <unistd.h>
+#include <map>
#include <thread>
#include <android-base/file.h>
@@ -61,6 +62,8 @@
const std::string kDefaultAndroidDtDir("/proc/device-tree/firmware/android/");
+const std::string kDataDirPrefix("/data/");
+
void (*trigger_shutdown)(const std::string& command) = nullptr;
// DecodeUid() - decodes and returns the given string, which can be either the
@@ -458,58 +461,34 @@
return {};
}
-static FscryptAction FscryptInferAction(const std::string& dir) {
- const std::string prefix = "/data/";
-
- if (!android::base::StartsWith(dir, prefix)) {
- return FscryptAction::kNone;
- }
-
- // Special-case /data/media/obb per b/64566063
- if (dir == "/data/media/obb") {
- // Try to set policy on this directory, but if it is non-empty this may fail.
- return FscryptAction::kAttempt;
- }
-
- // Only set policy on first level /data directories
- // To make this less restrictive, consider using a policy file.
- // However this is overkill for as long as the policy is simply
- // to apply a global policy to all /data folders created via makedir
- if (dir.find_first_of('/', prefix.size()) != std::string::npos) {
- return FscryptAction::kNone;
- }
-
- // Special case various directories that must not be encrypted,
- // often because their subdirectories must be encrypted.
- // This isn't a nice way to do this, see b/26641735
- std::vector<std::string> directories_to_exclude = {
- "lost+found", "system_ce", "system_de", "misc_ce", "misc_de",
- "vendor_ce", "vendor_de", "media", "data", "user",
- "user_de", "apex", "preloads", "app-staging", "gsi",
- };
- for (const auto& d : directories_to_exclude) {
- if ((prefix + d) == dir) {
- return FscryptAction::kNone;
+// Remove unnecessary slashes so that any later checks (e.g., the check for
+// whether the path is a top-level directory in /data) don't get confused.
+std::string CleanDirPath(const std::string& path) {
+ std::string result;
+ result.reserve(path.length());
+ // Collapse duplicate slashes, e.g. //data//foo// => /data/foo/
+ for (char c : path) {
+ if (c != '/' || result.empty() || result.back() != '/') {
+ result += c;
}
}
- // Empty these directories if policy setting fails.
- std::vector<std::string> wipe_on_failure = {
- "rollback", "rollback-observer", // b/139193659
- };
- for (const auto& d : wipe_on_failure) {
- if ((prefix + d) == dir) {
- return FscryptAction::kDeleteIfNecessary;
- }
+ // Remove trailing slash, e.g. /data/foo/ => /data/foo
+ if (result.length() > 1 && result.back() == '/') {
+ result.pop_back();
}
- return FscryptAction::kRequire;
+ return result;
}
Result<MkdirOptions> ParseMkdir(const std::vector<std::string>& args) {
+ std::string path = CleanDirPath(args[1]);
+ const bool is_toplevel_data_dir =
+ StartsWith(path, kDataDirPrefix) &&
+ path.find_first_of('/', kDataDirPrefix.size()) == std::string::npos;
+ FscryptAction fscrypt_action =
+ is_toplevel_data_dir ? FscryptAction::kRequire : FscryptAction::kNone;
mode_t mode = 0755;
Result<uid_t> uid = -1;
Result<gid_t> gid = -1;
- FscryptAction fscrypt_inferred_action = FscryptInferAction(args[1]);
- FscryptAction fscrypt_action = fscrypt_inferred_action;
std::string ref_option = "ref";
bool set_option_encryption = false;
bool set_option_key = false;
@@ -574,24 +553,17 @@
if (set_option_key && fscrypt_action == FscryptAction::kNone) {
return Error() << "Key option set but encryption action is none";
}
- const std::string prefix = "/data/";
- if (StartsWith(args[1], prefix) &&
- args[1].find_first_of('/', prefix.size()) == std::string::npos) {
+ if (is_toplevel_data_dir) {
if (!set_option_encryption) {
- LOG(WARNING) << "Top-level directory needs encryption action, eg mkdir " << args[1]
+ LOG(WARNING) << "Top-level directory needs encryption action, eg mkdir " << path
<< " <mode> <uid> <gid> encryption=Require";
}
if (fscrypt_action == FscryptAction::kNone) {
- LOG(INFO) << "Not setting encryption policy on: " << args[1];
+ LOG(INFO) << "Not setting encryption policy on: " << path;
}
}
- if (fscrypt_action != fscrypt_inferred_action) {
- LOG(WARNING) << "Inferred action different from explicit one, expected "
- << static_cast<int>(fscrypt_inferred_action) << " but got "
- << static_cast<int>(fscrypt_action);
- }
- return MkdirOptions{args[1], mode, *uid, *gid, fscrypt_action, ref_option};
+ return MkdirOptions{path, mode, *uid, *gid, fscrypt_action, ref_option};
}
Result<MountAllOptions> ParseMountAll(const std::vector<std::string>& args) {
@@ -762,5 +734,72 @@
return is_microdroid;
}
+bool Has32BitAbi() {
+ static bool has = !android::base::GetProperty("ro.product.cpu.abilist32", "").empty();
+ return has;
+}
+
+std::string GetApexNameFromFileName(const std::string& path) {
+ static const std::string kApexDir = "/apex/";
+ if (StartsWith(path, kApexDir)) {
+ auto begin = kApexDir.size();
+ auto end = path.find('/', begin);
+ return path.substr(begin, end - begin);
+ }
+ return "";
+}
+
+std::vector<std::string> FilterVersionedConfigs(const std::vector<std::string>& configs,
+ int active_sdk) {
+ std::vector<std::string> filtered_configs;
+
+ std::map<std::string, std::pair<std::string, int>> script_map;
+ for (const auto& c : configs) {
+ int sdk = 0;
+ const std::vector<std::string> parts = android::base::Split(c, ".");
+ std::string base;
+ if (parts.size() < 2) {
+ continue;
+ }
+
+ // parts[size()-1], aka the suffix, should be "rc" or "#rc"
+ // any other pattern gets discarded
+
+ const auto& suffix = parts[parts.size() - 1];
+ if (suffix == "rc") {
+ sdk = 0;
+ } else {
+ char trailer[9] = {0};
+ int r = sscanf(suffix.c_str(), "%d%8s", &sdk, trailer);
+ if (r != 2) {
+ continue;
+ }
+ if (strlen(trailer) > 2 || strcmp(trailer, "rc") != 0) {
+ continue;
+ }
+ }
+
+ if (sdk < 0 || sdk > active_sdk) {
+ continue;
+ }
+
+ base = parts[0];
+ for (unsigned int i = 1; i < parts.size() - 1; i++) {
+ base = base + "." + parts[i];
+ }
+
+ // is this preferred over what we already have
+ auto it = script_map.find(base);
+ if (it == script_map.end() || it->second.second < sdk) {
+ script_map[base] = std::make_pair(c, sdk);
+ }
+ }
+
+ for (const auto& m : script_map) {
+ filtered_configs.push_back(m.second.first);
+ }
+ return filtered_configs;
+}
+
} // namespace init
} // namespace android
diff --git a/init/util.h b/init/util.h
index bf53675..0181bf0 100644
--- a/init/util.h
+++ b/init/util.h
@@ -69,6 +69,7 @@
bool IsLegalPropertyName(const std::string& name);
Result<void> IsLegalPropertyValue(const std::string& name, const std::string& value);
+std::string CleanDirPath(const std::string& path);
struct MkdirOptions {
std::string target;
@@ -105,5 +106,14 @@
void SetDefaultMountNamespaceReady();
bool IsMicrodroid();
+bool Has32BitAbi();
+
+std::string GetApexNameFromFileName(const std::string& path);
+
+// Compare all files */path.#rc and */path.rc with the same path prefix.
+// Keep the one with the highest # that doesn't exceed the system's SDK.
+// (.rc == .0rc for ranking purposes)
+std::vector<std::string> FilterVersionedConfigs(const std::vector<std::string>& configs,
+ int active_sdk);
} // namespace init
} // namespace android
diff --git a/init/util_test.cpp b/init/util_test.cpp
index 565e7d4..e8144c3 100644
--- a/init/util_test.cpp
+++ b/init/util_test.cpp
@@ -170,5 +170,18 @@
EXPECT_TRUE(is_dir(path1.c_str()));
}
+TEST(util, CleanDirPath) {
+ EXPECT_EQ("", CleanDirPath(""));
+ EXPECT_EQ("/", CleanDirPath("/"));
+ EXPECT_EQ("/", CleanDirPath("//"));
+ EXPECT_EQ("/foo", CleanDirPath("/foo"));
+ EXPECT_EQ("/foo", CleanDirPath("//foo"));
+ EXPECT_EQ("/foo", CleanDirPath("/foo/"));
+ EXPECT_EQ("/foo/bar", CleanDirPath("/foo/bar"));
+ EXPECT_EQ("/foo/bar", CleanDirPath("/foo/bar/"));
+ EXPECT_EQ("/foo/bar", CleanDirPath("/foo/bar////"));
+ EXPECT_EQ("/foo/bar", CleanDirPath("//foo//bar"));
+}
+
} // namespace init
} // namespace android
diff --git a/libcutils/Android.bp b/libcutils/Android.bp
index c8bfb01..fde30ad 100644
--- a/libcutils/Android.bp
+++ b/libcutils/Android.bp
@@ -168,6 +168,9 @@
target: {
linux_bionic: {
enabled: true,
+ static_libs: [
+ "libasync_safe",
+ ],
},
not_windows: {
srcs: libcutils_nonwindows_sources + [
@@ -190,6 +193,9 @@
],
},
android: {
+ static_libs: [
+ "libasync_safe",
+ ],
srcs: libcutils_nonwindows_sources + [
"android_reboot.cpp",
"ashmem-dev.cpp",
diff --git a/libcutils/TEST_MAPPING b/libcutils/TEST_MAPPING
index cca7d93..6477502 100644
--- a/libcutils/TEST_MAPPING
+++ b/libcutils/TEST_MAPPING
@@ -4,7 +4,7 @@
"name": "libcutils_test"
}
],
- "hwasan-postsubmit": [
+ "hwasan-presubmit": [
{
"name": "libcutils_test"
}
diff --git a/libcutils/include/cutils/native_handle.h b/libcutils/include/cutils/native_handle.h
index 4f07456..e46e7cd 100644
--- a/libcutils/include/cutils/native_handle.h
+++ b/libcutils/include/cutils/native_handle.h
@@ -49,18 +49,28 @@
typedef const native_handle_t* buffer_handle_t;
/*
- * native_handle_close
- *
- * closes the file descriptors contained in this native_handle_t
- *
+ * Closes the file descriptors contained in this native_handle_t, which may
+ * either be untagged or tagged for ownership by this native_handle_t via
+ * native_handle_set_tag(). Mixing untagged and tagged fds in the same
+ * native_handle_t is not permitted and triggers an fdsan exception, but
+ * native_handle_set_fdsan_tag() can be used to bring consistency if this is
+ * intentional.
+ *
+ * If it's known that fds are tagged, prefer native_handle_close_with_tag() for
+ * better safety.
+ *
* return 0 on success, or a negative error code on failure
- *
*/
int native_handle_close(const native_handle_t* h);
/*
- * native_handle_init
- *
+ * Equivalent to native_handle_close(), but throws an fdsan exception if the fds
+ * are untagged. Use if it's known that the fds in this native_handle_t were
+ * previously tagged via native_handle_set_tag().
+ */
+int native_handle_close_with_tag(const native_handle_t* h);
+
+/*
* Initializes a native_handle_t from storage. storage must be declared with
* NATIVE_HANDLE_DECLARE_STORAGE. numFds and numInts must not respectively
* exceed maxFds and maxInts used to declare the storage.
@@ -68,33 +78,42 @@
native_handle_t* native_handle_init(char* storage, int numFds, int numInts);
/*
- * native_handle_create
- *
- * creates a native_handle_t and initializes it. must be destroyed with
+ * Creates a native_handle_t and initializes it. Must be destroyed with
* native_handle_delete(). Note that numFds must be <= NATIVE_HANDLE_MAX_FDS,
* numInts must be <= NATIVE_HANDLE_MAX_INTS, and both must be >= 0.
- *
*/
native_handle_t* native_handle_create(int numFds, int numInts);
/*
- * native_handle_clone
- *
- * creates a native_handle_t and initializes it from another native_handle_t.
+ * Updates the fdsan tag for any file descriptors contained in the supplied
+ * handle to indicate that they are owned by this handle and should only be
+ * closed via native_handle_close()/native_handle_close_with_tag(). Each fd in
+ * the handle must have a tag of either 0 (unset) or the tag associated with
+ * this handle, otherwise an fdsan exception will be triggered.
+ */
+void native_handle_set_fdsan_tag(const native_handle_t* handle);
+
+/*
+ * Clears the fdsan tag for any file descriptors contained in the supplied
+ * native_handle_t. Use if this native_handle_t is giving up ownership of its
+ * fds, but the fdsan tags were previously set. Each fd in the handle must have
+ * a tag of either 0 (unset) or the tag associated with this handle, otherwise
+ * an fdsan exception will be triggered.
+ */
+void native_handle_unset_fdsan_tag(const native_handle_t* handle);
+
+/*
+ * Creates a native_handle_t and initializes it from another native_handle_t.
* Must be destroyed with native_handle_delete().
- *
*/
native_handle_t* native_handle_clone(const native_handle_t* handle);
/*
- * native_handle_delete
- *
- * frees a native_handle_t allocated with native_handle_create().
+ * Frees a native_handle_t allocated with native_handle_create().
* This ONLY frees the memory allocated for the native_handle_t, but doesn't
* close the file descriptors; which can be achieved with native_handle_close().
- *
+ *
* return 0 on success, or a negative error code on failure
- *
*/
int native_handle_delete(native_handle_t* h);
diff --git a/libcutils/include/cutils/trace.h b/libcutils/include/cutils/trace.h
index 98ae0d4..3867f34 100644
--- a/libcutils/include/cutils/trace.h
+++ b/libcutils/include/cutils/trace.h
@@ -214,6 +214,7 @@
* provided, which is the name of the row where this async event should be
* recorded. The track name, name, and cookie used to begin an event must be
* used to end it.
+ * The cookie here must be unique on the track_name level, not the name level.
*/
#define ATRACE_ASYNC_FOR_TRACK_BEGIN(track_name, name, cookie) \
atrace_async_for_track_begin(ATRACE_TAG, track_name, name, cookie)
@@ -229,13 +230,13 @@
* Trace the end of an asynchronous event.
* This should correspond to a previous ATRACE_ASYNC_FOR_TRACK_BEGIN.
*/
-#define ATRACE_ASYNC_FOR_TRACK_END(track_name, name, cookie) \
- atrace_async_for_track_end(ATRACE_TAG, track_name, name, cookie)
+#define ATRACE_ASYNC_FOR_TRACK_END(track_name, cookie) \
+ atrace_async_for_track_end(ATRACE_TAG, track_name, cookie)
static inline void atrace_async_for_track_end(uint64_t tag, const char* track_name,
- const char* name, int32_t cookie) {
+ int32_t cookie) {
if (CC_UNLIKELY(atrace_is_tag_enabled(tag))) {
- void atrace_async_for_track_end_body(const char*, const char*, int32_t);
- atrace_async_for_track_end_body(track_name, name, cookie);
+ void atrace_async_for_track_end_body(const char*, int32_t);
+ atrace_async_for_track_end_body(track_name, cookie);
}
}
diff --git a/libcutils/include/private/android_filesystem_config.h b/libcutils/include/private/android_filesystem_config.h
index bdb8075..da5005c 100644
--- a/libcutils/include/private/android_filesystem_config.h
+++ b/libcutils/include/private/android_filesystem_config.h
@@ -41,9 +41,11 @@
*/
#define AID_ROOT 0 /* traditional unix root user */
-/* The following are for LTP and should only be used for testing */
-#define AID_DAEMON 1 /* traditional unix daemon owner */
-#define AID_BIN 2 /* traditional unix binaries owner */
+
+/* The following are for tests like LTP and should only be used for testing. */
+#define AID_DAEMON 1 /* Traditional unix daemon owner. */
+#define AID_BIN 2 /* Traditional unix binaries owner. */
+#define AID_SYS 3 /* A group with the same gid on Linux/macOS/Android. */
#define AID_SYSTEM 1000 /* system server */
diff --git a/libcutils/native_handle.cpp b/libcutils/native_handle.cpp
index 5804ab1..b85c93b 100644
--- a/libcutils/native_handle.cpp
+++ b/libcutils/native_handle.cpp
@@ -22,13 +22,74 @@
#include <string.h>
#include <unistd.h>
+// Needs to come after stdlib includes to capture the __BIONIC__ definition
+#ifdef __BIONIC__
+#include <android/fdsan.h>
+#endif
+
+namespace {
+
+#if !defined(__BIONIC__)
+// fdsan stubs when not linked against bionic
+#define ANDROID_FDSAN_OWNER_TYPE_NATIVE_HANDLE 0
+
+uint64_t android_fdsan_create_owner_tag(int /*type*/, uint64_t /*tag*/) {
+ return 0;
+}
+uint64_t android_fdsan_get_owner_tag(int /*fd*/) {
+ return 0;
+}
+int android_fdsan_close_with_tag(int fd, uint64_t /*tag*/) {
+ return close(fd);
+}
+void android_fdsan_exchange_owner_tag(int /*fd*/, uint64_t /*expected_tag*/, uint64_t /*tag*/) {}
+#endif // !__BIONIC__
+
+uint64_t get_fdsan_tag(const native_handle_t* handle) {
+ return android_fdsan_create_owner_tag(ANDROID_FDSAN_OWNER_TYPE_NATIVE_HANDLE,
+ reinterpret_cast<uint64_t>(handle));
+}
+
+int close_internal(const native_handle_t* h, bool allowUntagged) {
+ if (!h) return 0;
+
+ if (h->version != sizeof(native_handle_t)) return -EINVAL;
+
+ const int numFds = h->numFds;
+ uint64_t tag;
+ if (allowUntagged && numFds > 0 && android_fdsan_get_owner_tag(h->data[0]) == 0) {
+ tag = 0;
+ } else {
+ tag = get_fdsan_tag(h);
+ }
+ int saved_errno = errno;
+ for (int i = 0; i < numFds; ++i) {
+ android_fdsan_close_with_tag(h->data[i], tag);
+ }
+ errno = saved_errno;
+ return 0;
+}
+
+void swap_fdsan_tags(const native_handle_t* handle, uint64_t expected_tag, uint64_t new_tag) {
+ if (!handle || handle->version != sizeof(native_handle_t)) return;
+
+ for (int i = 0; i < handle->numFds; i++) {
+ // allow for idempotence to make the APIs easier to use
+ if (android_fdsan_get_owner_tag(handle->data[i]) != new_tag) {
+ android_fdsan_exchange_owner_tag(handle->data[i], expected_tag, new_tag);
+ }
+ }
+}
+
+} // anonymous namespace
+
native_handle_t* native_handle_init(char* storage, int numFds, int numInts) {
- if ((uintptr_t) storage % alignof(native_handle_t)) {
+ if ((uintptr_t)storage % alignof(native_handle_t)) {
errno = EINVAL;
return NULL;
}
- native_handle_t* handle = (native_handle_t*) storage;
+ native_handle_t* handle = (native_handle_t*)storage;
handle->version = sizeof(native_handle_t);
handle->numFds = numFds;
handle->numInts = numInts;
@@ -52,6 +113,14 @@
return h;
}
+void native_handle_set_fdsan_tag(const native_handle_t* handle) {
+ swap_fdsan_tags(handle, 0, get_fdsan_tag(handle));
+}
+
+void native_handle_unset_fdsan_tag(const native_handle_t* handle) {
+ swap_fdsan_tags(handle, get_fdsan_tag(handle), 0);
+}
+
native_handle_t* native_handle_clone(const native_handle_t* handle) {
native_handle_t* clone = native_handle_create(handle->numFds, handle->numInts);
if (clone == NULL) return NULL;
@@ -81,15 +150,9 @@
}
int native_handle_close(const native_handle_t* h) {
- if (!h) return 0;
+ return close_internal(h, /*allowUntagged=*/true);
+}
- if (h->version != sizeof(native_handle_t)) return -EINVAL;
-
- int saved_errno = errno;
- const int numFds = h->numFds;
- for (int i = 0; i < numFds; ++i) {
- close(h->data[i]);
- }
- errno = saved_errno;
- return 0;
+int native_handle_close_with_tag(const native_handle_t* h) {
+ return close_internal(h, /*allowUntagged=*/false);
}
diff --git a/libcutils/trace-container.cpp b/libcutils/trace-container.cpp
index 6202662..eae6155 100644
--- a/libcutils/trace-container.cpp
+++ b/libcutils/trace-container.cpp
@@ -240,15 +240,15 @@
WRITE_MSG("G|%d|", "|%" PRId32, track_name, name, cookie);
}
-void atrace_async_for_track_end_body(const char* track_name, const char* name, int32_t cookie) {
+void atrace_async_for_track_end_body(const char* track_name, int32_t cookie) {
if (CC_LIKELY(atrace_use_container_sock)) {
- WRITE_MSG_IN_CONTAINER("H", "|", "|%d", track_name, name, cookie);
+ WRITE_MSG_IN_CONTAINER("H", "|", "|%d", "", track_name, cookie);
return;
}
if (atrace_marker_fd < 0) return;
- WRITE_MSG("H|%d|", "|%" PRId32, track_name, name, cookie);
+ WRITE_MSG("H|%d|", "|%" PRId32, "", track_name, cookie);
}
void atrace_instant_body(const char* name) {
diff --git a/libcutils/trace-dev.cpp b/libcutils/trace-dev.cpp
index e9583fb..1827e32 100644
--- a/libcutils/trace-dev.cpp
+++ b/libcutils/trace-dev.cpp
@@ -93,8 +93,8 @@
WRITE_MSG("G|%d|", "|%" PRId32, track_name, name, cookie);
}
-void atrace_async_for_track_end_body(const char* track_name, const char* name, int32_t cookie) {
- WRITE_MSG("H|%d|", "|%" PRId32, track_name, name, cookie);
+void atrace_async_for_track_end_body(const char* track_name, int32_t cookie) {
+ WRITE_MSG("H|%d|", "|%" PRId32, "", track_name, cookie);
}
void atrace_instant_body(const char* name) {
diff --git a/libcutils/trace-dev_test.cpp b/libcutils/trace-dev_test.cpp
index d4a907d..3dea5ff 100644
--- a/libcutils/trace-dev_test.cpp
+++ b/libcutils/trace-dev_test.cpp
@@ -306,112 +306,52 @@
}
TEST_F(TraceDevTest, atrace_async_for_track_end_body_normal) {
- atrace_async_for_track_end_body("fake_track", "fake_name", 12345);
+ atrace_async_for_track_end_body("fake_track", 12345);
ASSERT_EQ(0, lseek(atrace_marker_fd, 0, SEEK_SET));
std::string actual;
ASSERT_TRUE(android::base::ReadFdToString(atrace_marker_fd, &actual));
- std::string expected = android::base::StringPrintf("H|%d|fake_track|fake_name|12345", getpid());
+ std::string expected = android::base::StringPrintf("H|%d|fake_track|12345", getpid());
ASSERT_STREQ(expected.c_str(), actual.c_str());
}
-TEST_F(TraceDevTest, atrace_async_for_track_end_body_exact_track_name) {
- const int name_size = 5;
+TEST_F(TraceDevTest, atrace_async_for_track_end_body_exact) {
std::string expected = android::base::StringPrintf("H|%d|", getpid());
std::string track_name =
- MakeName(ATRACE_MESSAGE_LENGTH - expected.length() - 1 - name_size - 6);
- atrace_async_for_track_end_body(track_name.c_str(), "name", 12345);
+ MakeName(ATRACE_MESSAGE_LENGTH - expected.length() - 7);
+ atrace_async_for_track_end_body(track_name.c_str(), 12345);
ASSERT_EQ(ATRACE_MESSAGE_LENGTH - 1, lseek(atrace_marker_fd, 0, SEEK_CUR));
ASSERT_EQ(0, lseek(atrace_marker_fd, 0, SEEK_SET));
std::string actual;
ASSERT_TRUE(android::base::ReadFdToString(atrace_marker_fd, &actual));
- expected += track_name + "|name|12345";
+ expected += track_name + "|12345";
ASSERT_STREQ(expected.c_str(), actual.c_str());
- // Add a single character and verify name truncation
+ // Add a single character and verify we get the exact same value as before.
ASSERT_EQ(0, lseek(atrace_marker_fd, 0, SEEK_SET));
track_name += '*';
- expected = android::base::StringPrintf("H|%d|", getpid());
- expected += track_name + "|nam|12345";
- atrace_async_for_track_end_body(track_name.c_str(), "name", 12345);
+ atrace_async_for_track_end_body(track_name.c_str(), 12345);
EXPECT_EQ(ATRACE_MESSAGE_LENGTH - 1, lseek(atrace_marker_fd, 0, SEEK_CUR));
ASSERT_EQ(0, lseek(atrace_marker_fd, 0, SEEK_SET));
ASSERT_TRUE(android::base::ReadFdToString(atrace_marker_fd, &actual));
ASSERT_STREQ(expected.c_str(), actual.c_str());
}
-TEST_F(TraceDevTest, atrace_async_for_track_end_body_truncated_track_name) {
+TEST_F(TraceDevTest, atrace_async_for_track_end_body_truncated) {
std::string expected = android::base::StringPrintf("H|%d|", getpid());
std::string track_name = MakeName(2 * ATRACE_MESSAGE_LENGTH);
- atrace_async_for_track_end_body(track_name.c_str(), "name", 12345);
+ atrace_async_for_track_end_body(track_name.c_str(), 12345);
ASSERT_EQ(ATRACE_MESSAGE_LENGTH - 1, lseek(atrace_marker_fd, 0, SEEK_CUR));
ASSERT_EQ(0, lseek(atrace_marker_fd, 0, SEEK_SET));
std::string actual;
ASSERT_TRUE(android::base::ReadFdToString(atrace_marker_fd, &actual));
- int expected_len = ATRACE_MESSAGE_LENGTH - expected.length() - 9;
- expected += android::base::StringPrintf("%.*s|n|12345", expected_len, track_name.c_str());
- ASSERT_STREQ(expected.c_str(), actual.c_str());
-}
-
-TEST_F(TraceDevTest, atrace_async_for_track_end_body_exact_name) {
- const int track_name_size = 11;
- std::string expected = android::base::StringPrintf("H|%d|", getpid());
- std::string name =
- MakeName(ATRACE_MESSAGE_LENGTH - expected.length() - 1 - track_name_size - 6);
- atrace_async_for_track_end_body("track_name", name.c_str(), 12345);
-
- ASSERT_EQ(ATRACE_MESSAGE_LENGTH - 1, lseek(atrace_marker_fd, 0, SEEK_CUR));
- ASSERT_EQ(0, lseek(atrace_marker_fd, 0, SEEK_SET));
-
- std::string actual;
- ASSERT_TRUE(android::base::ReadFdToString(atrace_marker_fd, &actual));
- expected += "track_name|" + name + "|12345";
- ASSERT_STREQ(expected.c_str(), actual.c_str());
-
- // Add a single character and verify we get the same value as before.
- ASSERT_EQ(0, lseek(atrace_marker_fd, 0, SEEK_SET));
- name += '*';
- atrace_async_for_track_end_body("track_name", name.c_str(), 12345);
- EXPECT_EQ(ATRACE_MESSAGE_LENGTH - 1, lseek(atrace_marker_fd, 0, SEEK_CUR));
- ASSERT_EQ(0, lseek(atrace_marker_fd, 0, SEEK_SET));
- ASSERT_TRUE(android::base::ReadFdToString(atrace_marker_fd, &actual));
- ASSERT_STREQ(expected.c_str(), actual.c_str());
-}
-
-TEST_F(TraceDevTest, atrace_async_for_track_end_body_truncated_name) {
- std::string expected = android::base::StringPrintf("H|%d|track_name|", getpid());
- std::string name = MakeName(2 * ATRACE_MESSAGE_LENGTH);
- atrace_async_for_track_end_body("track_name", name.c_str(), 12345);
-
- ASSERT_EQ(ATRACE_MESSAGE_LENGTH - 1, lseek(atrace_marker_fd, 0, SEEK_CUR));
- ASSERT_EQ(0, lseek(atrace_marker_fd, 0, SEEK_SET));
-
- std::string actual;
- ASSERT_TRUE(android::base::ReadFdToString(atrace_marker_fd, &actual));
- int expected_len = ATRACE_MESSAGE_LENGTH - expected.length() - 1 - 6;
- expected += android::base::StringPrintf("%.*s|12345", expected_len, name.c_str());
- ASSERT_STREQ(expected.c_str(), actual.c_str());
-}
-
-TEST_F(TraceDevTest, atrace_async_for_track_end_body_truncated_both) {
- std::string expected = android::base::StringPrintf("H|%d|", getpid());
- std::string name = MakeName(2 * ATRACE_MESSAGE_LENGTH);
- std::string track_name = MakeName(2 * ATRACE_MESSAGE_LENGTH);
- atrace_async_for_track_end_body(track_name.c_str(), name.c_str(), 12345);
-
- ASSERT_EQ(ATRACE_MESSAGE_LENGTH - 1, lseek(atrace_marker_fd, 0, SEEK_CUR));
- ASSERT_EQ(0, lseek(atrace_marker_fd, 0, SEEK_SET));
-
- std::string actual;
- ASSERT_TRUE(android::base::ReadFdToString(atrace_marker_fd, &actual));
- int expected_len = ATRACE_MESSAGE_LENGTH - expected.length() - 3 - 6;
- expected += android::base::StringPrintf("%.*s|%.1s|12345", expected_len, track_name.c_str(),
- name.c_str());
+ int expected_len = ATRACE_MESSAGE_LENGTH - expected.length() - 7;
+ expected += android::base::StringPrintf("%.*s|12345", expected_len, track_name.c_str());
ASSERT_STREQ(expected.c_str(), actual.c_str());
}
diff --git a/libcutils/trace-host.cpp b/libcutils/trace-host.cpp
index c2a379b..e9f58c3 100644
--- a/libcutils/trace-host.cpp
+++ b/libcutils/trace-host.cpp
@@ -30,8 +30,7 @@
void atrace_async_end_body(const char* /*name*/, int32_t /*cookie*/) {}
void atrace_async_for_track_begin_body(const char* /*track_name*/, const char* /*name*/,
int32_t /*cookie*/) {}
-void atrace_async_for_track_end_body(const char* /*track_name*/, const char* /*name*/,
- int32_t /*cookie*/) {}
+void atrace_async_for_track_end_body(const char* /*track_name*/, int32_t /*cookie*/) {}
void atrace_instant_body(const char* /*name*/) {}
void atrace_instant_for_track_body(const char* /*track_name*/, const char* /*name*/) {}
void atrace_int_body(const char* /*name*/, int32_t /*value*/) {}
diff --git a/libmodprobe/libmodprobe.cpp b/libmodprobe/libmodprobe.cpp
index 3054d2b..e071c96 100644
--- a/libmodprobe/libmodprobe.cpp
+++ b/libmodprobe/libmodprobe.cpp
@@ -440,12 +440,12 @@
}
// Another option to load kernel modules. load in independent modules in parallel
-// and then load modules which only have soft dependency, third update dependency list of other
-// remaining modules, repeat these steps until all modules are loaded.
+// and then update dependency list of other remaining modules, repeat these steps
+// until all modules are loaded.
bool Modprobe::LoadModulesParallel(int num_threads) {
bool ret = true;
+ int count = -1;
std::map<std::string, std::set<std::string>> mod_with_deps;
- std::map<std::string, std::set<std::string>> mod_with_softdeps;
// Get dependencies
for (const auto& module : module_load_) {
@@ -458,26 +458,36 @@
// Get soft dependencies
for (const auto& [it_mod, it_softdep] : module_pre_softdep_) {
- mod_with_softdeps[MakeCanonical(it_mod)].emplace(it_softdep);
+ if (mod_with_deps.find(MakeCanonical(it_softdep)) != mod_with_deps.end()) {
+ mod_with_deps[MakeCanonical(it_mod)].emplace(
+ GetDependencies(MakeCanonical(it_softdep))[0]);
+ }
}
// Get soft post dependencies
for (const auto& [it_mod, it_softdep] : module_post_softdep_) {
- mod_with_softdeps[MakeCanonical(it_mod)].emplace(it_softdep);
+ if (mod_with_deps.find(MakeCanonical(it_softdep)) != mod_with_deps.end()) {
+ mod_with_deps[MakeCanonical(it_softdep)].emplace(
+ GetDependencies(MakeCanonical(it_mod))[0]);
+ }
}
- while (!mod_with_deps.empty()) {
+ while (!mod_with_deps.empty() && count != module_loaded_.size()) {
std::vector<std::thread> threads;
std::vector<std::string> mods_path_to_load;
- std::vector<std::string> mods_with_softdep_to_load;
std::mutex vector_lock;
+ count = module_loaded_.size();
- // Find independent modules and modules only having soft dependencies
+ // Find independent modules
for (const auto& [it_mod, it_dep] : mod_with_deps) {
- if (it_dep.size() == 1 && mod_with_softdeps[it_mod].empty()) {
- mods_path_to_load.emplace_back(*(it_dep.begin()));
- } else if (it_dep.size() == 1) {
- mods_with_softdep_to_load.emplace_back(it_mod);
+ if (it_dep.size() == 1) {
+ if (module_options_[it_mod].find("load_sequential=1") != std::string::npos) {
+ if (!LoadWithAliases(it_mod, true) && !IsBlocklisted(it_mod)) {
+ return false;
+ }
+ } else {
+ mods_path_to_load.emplace_back(it_mod);
+ }
}
}
@@ -485,12 +495,16 @@
auto thread_function = [&] {
std::unique_lock lk(vector_lock);
while (!mods_path_to_load.empty()) {
- auto mod_path_to_load = std::move(mods_path_to_load.back());
+ auto ret_load = true;
+ auto mod_to_load = std::move(mods_path_to_load.back());
mods_path_to_load.pop_back();
lk.unlock();
- ret &= Insmod(mod_path_to_load, "");
+ ret_load &= LoadWithAliases(mod_to_load, true);
lk.lock();
+ if (!ret_load && !IsBlocklisted(mod_to_load)) {
+ ret &= ret_load;
+ }
}
};
@@ -502,21 +516,12 @@
thread.join();
}
- // Since we cannot assure if these soft dependencies tree are overlap,
- // we loaded these modules one by one.
- for (auto dep = mods_with_softdep_to_load.rbegin(); dep != mods_with_softdep_to_load.rend();
- dep++) {
- ret &= LoadWithAliases(*dep, true);
- }
+ if (!ret) return ret;
std::lock_guard guard(module_loaded_lock_);
// Remove loaded module form mod_with_deps and soft dependencies of other modules
for (const auto& module_loaded : module_loaded_) {
mod_with_deps.erase(module_loaded);
-
- for (auto& [mod, softdeps] : mod_with_softdeps) {
- softdeps.erase(module_loaded);
- }
}
// Remove loaded module form dependencies of other modules which are not loaded yet
diff --git a/libnetutils/dhcpclient.c b/libnetutils/dhcpclient.c
index 11c116a..ad6898c 100644
--- a/libnetutils/dhcpclient.c
+++ b/libnetutils/dhcpclient.c
@@ -45,7 +45,7 @@
typedef unsigned long long msecs_t;
#if VERBOSE
-void dump_dhcp_msg();
+void dump_dhcp_msg(dhcp_msg *msg, int len);
#endif
msecs_t get_msecs(void)
diff --git a/libpackagelistparser/TEST_MAPPING b/libpackagelistparser/TEST_MAPPING
index d69a7fb..f4a7761 100644
--- a/libpackagelistparser/TEST_MAPPING
+++ b/libpackagelistparser/TEST_MAPPING
@@ -4,7 +4,7 @@
"name": "libpackagelistparser_test"
}
],
- "hwasan-postsubmit": [
+ "hwasan-presubmit": [
{
"name": "libpackagelistparser_test"
}
diff --git a/libprocessgroup/Android.bp b/libprocessgroup/Android.bp
index 7b0e0d3..c6a0737 100644
--- a/libprocessgroup/Android.bp
+++ b/libprocessgroup/Android.bp
@@ -2,6 +2,17 @@
default_applicable_licenses: ["Android-Apache-2.0"],
}
+cc_defaults {
+ name: "libprocessgroup_defaults",
+ cpp_std: "gnu++20",
+ cflags: [
+ "-Wall",
+ "-Werror",
+ "-Wexit-time-destructors",
+ "-Wno-unused-parameter",
+ ],
+}
+
cc_library_headers {
name: "libprocessgroup_headers",
vendor_available: true,
@@ -62,11 +73,7 @@
export_header_lib_headers: [
"libprocessgroup_headers",
],
- cflags: [
- "-Wall",
- "-Werror",
- "-Wexit-time-destructors",
- ],
+ defaults: ["libprocessgroup_defaults"],
apex_available: [
"//apex_available:platform",
"//apex_available:anyapex",
@@ -77,12 +84,7 @@
cc_test {
name: "task_profiles_test",
host_supported: true,
- cflags: [
- "-Wall",
- "-Werror",
- "-Wexit-time-destructors",
- "-Wno-unused-parameter",
- ],
+ defaults: ["libprocessgroup_defaults"],
srcs: [
"task_profiles_test.cpp",
],
diff --git a/libprocessgroup/include/processgroup/processgroup.h b/libprocessgroup/include/processgroup/processgroup.h
index 39b9f3f..45a723f 100644
--- a/libprocessgroup/include/processgroup/processgroup.h
+++ b/libprocessgroup/include/processgroup/processgroup.h
@@ -18,7 +18,10 @@
#include <sys/cdefs.h>
#include <sys/types.h>
+#include <initializer_list>
+#include <span>
#include <string>
+#include <string_view>
#include <vector>
__BEGIN_DECLS
@@ -33,6 +36,19 @@
bool SetTaskProfiles(int tid, const std::vector<std::string>& profiles, bool use_fd_cache = false);
bool SetProcessProfiles(uid_t uid, pid_t pid, const std::vector<std::string>& profiles);
+__END_DECLS
+
+bool SetTaskProfiles(int tid, std::initializer_list<std::string_view> profiles,
+ bool use_fd_cache = false);
+bool SetProcessProfiles(uid_t uid, pid_t pid, std::initializer_list<std::string_view> profiles);
+#if _LIBCPP_STD_VER > 17
+bool SetTaskProfiles(int tid, std::span<const std::string_view> profiles,
+ bool use_fd_cache = false);
+bool SetProcessProfiles(uid_t uid, pid_t pid, std::span<const std::string_view> profiles);
+#endif
+
+__BEGIN_DECLS
+
#ifndef __ANDROID_VNDK__
bool SetProcessProfilesCached(uid_t uid, pid_t pid, const std::vector<std::string>& profiles);
diff --git a/libprocessgroup/processgroup.cpp b/libprocessgroup/processgroup.cpp
index 267e62c..bdda102 100644
--- a/libprocessgroup/processgroup.cpp
+++ b/libprocessgroup/processgroup.cpp
@@ -148,14 +148,35 @@
}
bool SetProcessProfiles(uid_t uid, pid_t pid, const std::vector<std::string>& profiles) {
+ return TaskProfiles::GetInstance().SetProcessProfiles(
+ uid, pid, std::span<const std::string>(profiles), false);
+}
+
+bool SetProcessProfiles(uid_t uid, pid_t pid, std::initializer_list<std::string_view> profiles) {
+ return TaskProfiles::GetInstance().SetProcessProfiles(
+ uid, pid, std::span<const std::string_view>(profiles), false);
+}
+
+bool SetProcessProfiles(uid_t uid, pid_t pid, std::span<const std::string_view> profiles) {
return TaskProfiles::GetInstance().SetProcessProfiles(uid, pid, profiles, false);
}
bool SetProcessProfilesCached(uid_t uid, pid_t pid, const std::vector<std::string>& profiles) {
- return TaskProfiles::GetInstance().SetProcessProfiles(uid, pid, profiles, true);
+ return TaskProfiles::GetInstance().SetProcessProfiles(
+ uid, pid, std::span<const std::string>(profiles), true);
}
bool SetTaskProfiles(int tid, const std::vector<std::string>& profiles, bool use_fd_cache) {
+ return TaskProfiles::GetInstance().SetTaskProfiles(tid, std::span<const std::string>(profiles),
+ use_fd_cache);
+}
+
+bool SetTaskProfiles(int tid, std::initializer_list<std::string_view> profiles, bool use_fd_cache) {
+ return TaskProfiles::GetInstance().SetTaskProfiles(
+ tid, std::span<const std::string_view>(profiles), use_fd_cache);
+}
+
+bool SetTaskProfiles(int tid, std::span<const std::string_view> profiles, bool use_fd_cache) {
return TaskProfiles::GetInstance().SetTaskProfiles(tid, profiles, use_fd_cache);
}
@@ -166,12 +187,12 @@
// https://chromium-review.googlesource.com/c/chromiumos/platform/crosvm/+/3574427/5/src/linux/android.rs#12
extern "C" bool android_set_process_profiles(uid_t uid, pid_t pid, size_t num_profiles,
const char* profiles[]) {
- std::vector<std::string> profiles_;
+ std::vector<std::string_view> profiles_;
profiles_.reserve(num_profiles);
for (size_t i = 0; i < num_profiles; i++) {
profiles_.emplace_back(profiles[i]);
}
- return SetProcessProfiles(uid, pid, profiles_);
+ return SetProcessProfiles(uid, pid, std::span<const std::string_view>(profiles_));
}
static std::string ConvertUidToPath(const char* cgroup, uid_t uid) {
@@ -187,10 +208,6 @@
auto uid_pid_path = ConvertUidPidToPath(cgroup, uid, pid);
auto uid_path = ConvertUidToPath(cgroup, uid);
- if (retries == 0) {
- retries = 1;
- }
-
while (retries--) {
ret = rmdir(uid_pid_path.c_str());
if (!ret || errno != EBUSY) break;
@@ -463,12 +480,13 @@
<< " in " << static_cast<int>(ms) << "ms";
}
- int err = RemoveProcessGroup(cgroup, uid, initialPid, retries);
+ // 400 retries correspond to 2 secs max timeout
+ int err = RemoveProcessGroup(cgroup, uid, initialPid, 400);
if (isMemoryCgroupSupported() && UsePerAppMemcg()) {
std::string memcg_apps_path;
if (CgroupGetMemcgAppsPath(&memcg_apps_path) &&
- RemoveProcessGroup(memcg_apps_path.c_str(), uid, initialPid, retries) < 0) {
+ RemoveProcessGroup(memcg_apps_path.c_str(), uid, initialPid, 400) < 0) {
return -1;
}
}
diff --git a/libprocessgroup/profiles/task_profiles.json b/libprocessgroup/profiles/task_profiles.json
index 4092c1a..15f95fc 100644
--- a/libprocessgroup/profiles/task_profiles.json
+++ b/libprocessgroup/profiles/task_profiles.json
@@ -76,6 +76,21 @@
"Name": "FreezerState",
"Controller": "freezer",
"File": "cgroup.freeze"
+ },
+ {
+ "Name": "BfqWeight",
+ "Controller": "io",
+ "File": "io.bfq.weight"
+ },
+ {
+ "Name": "CfqGroupIdle",
+ "Controller": "io",
+ "File": "io.group_idle"
+ },
+ {
+ "Name": "CfqWeight",
+ "Controller": "io",
+ "File": "io.weight"
}
],
@@ -444,6 +459,33 @@
{
"Controller": "blkio",
"Path": "background"
+ }
+ },
+ {
+ "Name": "SetAttribute",
+ "Params":
+ {
+ "Name": "BfqWeight",
+ "Value": "10",
+ "Optional": "true"
+ }
+ },
+ {
+ "Name": "SetAttribute",
+ "Params":
+ {
+ "Name": "CfqGroupIdle",
+ "Value": "0",
+ "Optional": "true"
+ }
+ },
+ {
+ "Name": "SetAttribute",
+ "Params":
+ {
+ "Name": "CfqWeight",
+ "Value": "200",
+ "Optional": "true"
}
}
]
@@ -457,6 +499,33 @@
{
"Controller": "blkio",
"Path": ""
+ }
+ },
+ {
+ "Name": "SetAttribute",
+ "Params":
+ {
+ "Name": "BfqWeight",
+ "Value": "100",
+ "Optional": "true"
+ }
+ },
+ {
+ "Name": "SetAttribute",
+ "Params":
+ {
+ "Name": "CfqGroupIdle",
+ "Value": "0",
+ "Optional": "true"
+ }
+ },
+ {
+ "Name": "SetAttribute",
+ "Params":
+ {
+ "Name": "CfqWeight",
+ "Value": "1000",
+ "Optional": "true"
}
}
]
@@ -470,6 +539,33 @@
{
"Controller": "blkio",
"Path": ""
+ }
+ },
+ {
+ "Name": "SetAttribute",
+ "Params":
+ {
+ "Name": "BfqWeight",
+ "Value": "100",
+ "Optional": "true"
+ }
+ },
+ {
+ "Name": "SetAttribute",
+ "Params":
+ {
+ "Name": "CfqGroupIdle",
+ "Value": "0",
+ "Optional": "true"
+ }
+ },
+ {
+ "Name": "SetAttribute",
+ "Params":
+ {
+ "Name": "CfqWeight",
+ "Value": "1000",
+ "Optional": "true"
}
}
]
@@ -483,6 +579,33 @@
{
"Controller": "blkio",
"Path": ""
+ }
+ },
+ {
+ "Name": "SetAttribute",
+ "Params":
+ {
+ "Name": "BfqWeight",
+ "Value": "100",
+ "Optional": "true"
+ }
+ },
+ {
+ "Name": "SetAttribute",
+ "Params":
+ {
+ "Name": "CfqGroupIdle",
+ "Value": "0",
+ "Optional": "true"
+ }
+ },
+ {
+ "Name": "SetAttribute",
+ "Params":
+ {
+ "Name": "CfqWeight",
+ "Value": "1000",
+ "Optional": "true"
}
}
]
@@ -647,7 +770,7 @@
"Profiles": [ "ServicePerformance", "LowIoPriority", "TimerSlackNormal" ]
},
{
- "Name": "VMCompilationPerformance",
+ "Name": "SCHED_SP_COMPUTE",
"Profiles": [ "HighPerformance", "ProcessCapacityHigh", "LowIoPriority", "TimerSlackNormal" ]
},
{
diff --git a/libprocessgroup/setup/cgroup_map_write.cpp b/libprocessgroup/setup/cgroup_map_write.cpp
index 3831ef2..304248a 100644
--- a/libprocessgroup/setup/cgroup_map_write.cpp
+++ b/libprocessgroup/setup/cgroup_map_write.cpp
@@ -410,7 +410,7 @@
// Make sure we do this only one time. No need for std::call_once because
// init is a single-threaded process
if (access(CGROUPS_RC_PATH, F_OK) == 0) {
- LOG(WARNING) << "Attempt to call SetupCgroups more than once";
+ LOG(WARNING) << "Attempt to call CgroupSetup() more than once";
return true;
}
diff --git a/libprocessgroup/task_profiles.cpp b/libprocessgroup/task_profiles.cpp
index e1c5934..744710f 100644
--- a/libprocessgroup/task_profiles.cpp
+++ b/libprocessgroup/task_profiles.cpp
@@ -786,7 +786,7 @@
return true;
}
-TaskProfile* TaskProfiles::GetProfile(const std::string& name) const {
+TaskProfile* TaskProfiles::GetProfile(std::string_view name) const {
auto iter = profiles_.find(name);
if (iter != profiles_.end()) {
@@ -795,7 +795,7 @@
return nullptr;
}
-const IProfileAttribute* TaskProfiles::GetAttribute(const std::string& name) const {
+const IProfileAttribute* TaskProfiles::GetAttribute(std::string_view name) const {
auto iter = attributes_.find(name);
if (iter != attributes_.end()) {
@@ -804,8 +804,9 @@
return nullptr;
}
-bool TaskProfiles::SetProcessProfiles(uid_t uid, pid_t pid,
- const std::vector<std::string>& profiles, bool use_fd_cache) {
+template <typename T>
+bool TaskProfiles::SetProcessProfiles(uid_t uid, pid_t pid, std::span<const T> profiles,
+ bool use_fd_cache) {
bool success = true;
for (const auto& name : profiles) {
TaskProfile* profile = GetProfile(name);
@@ -825,8 +826,8 @@
return success;
}
-bool TaskProfiles::SetTaskProfiles(int tid, const std::vector<std::string>& profiles,
- bool use_fd_cache) {
+template <typename T>
+bool TaskProfiles::SetTaskProfiles(int tid, std::span<const T> profiles, bool use_fd_cache) {
bool success = true;
for (const auto& name : profiles) {
TaskProfile* profile = GetProfile(name);
@@ -845,3 +846,14 @@
}
return success;
}
+
+template bool TaskProfiles::SetProcessProfiles(uid_t uid, pid_t pid,
+ std::span<const std::string> profiles,
+ bool use_fd_cache);
+template bool TaskProfiles::SetProcessProfiles(uid_t uid, pid_t pid,
+ std::span<const std::string_view> profiles,
+ bool use_fd_cache);
+template bool TaskProfiles::SetTaskProfiles(int tid, std::span<const std::string> profiles,
+ bool use_fd_cache);
+template bool TaskProfiles::SetTaskProfiles(int tid, std::span<const std::string_view> profiles,
+ bool use_fd_cache);
diff --git a/libprocessgroup/task_profiles.h b/libprocessgroup/task_profiles.h
index df08f65..85b3f91 100644
--- a/libprocessgroup/task_profiles.h
+++ b/libprocessgroup/task_profiles.h
@@ -18,9 +18,12 @@
#include <sys/cdefs.h>
#include <sys/types.h>
+#include <functional>
#include <map>
#include <mutex>
+#include <span>
#include <string>
+#include <string_view>
#include <vector>
#include <android-base/unique_fd.h>
@@ -206,18 +209,19 @@
// Should be used by all users
static TaskProfiles& GetInstance();
- TaskProfile* GetProfile(const std::string& name) const;
- const IProfileAttribute* GetAttribute(const std::string& name) const;
+ TaskProfile* GetProfile(std::string_view name) const;
+ const IProfileAttribute* GetAttribute(std::string_view name) const;
void DropResourceCaching(ProfileAction::ResourceCacheType cache_type) const;
- bool SetProcessProfiles(uid_t uid, pid_t pid, const std::vector<std::string>& profiles,
- bool use_fd_cache);
- bool SetTaskProfiles(int tid, const std::vector<std::string>& profiles, bool use_fd_cache);
+ template <typename T>
+ bool SetProcessProfiles(uid_t uid, pid_t pid, std::span<const T> profiles, bool use_fd_cache);
+ template <typename T>
+ bool SetTaskProfiles(int tid, std::span<const T> profiles, bool use_fd_cache);
private:
- std::map<std::string, std::shared_ptr<TaskProfile>> profiles_;
- std::map<std::string, std::unique_ptr<IProfileAttribute>> attributes_;
-
TaskProfiles();
bool Load(const CgroupMap& cg_map, const std::string& file_name);
+
+ std::map<std::string, std::shared_ptr<TaskProfile>, std::less<>> profiles_;
+ std::map<std::string, std::unique_ptr<IProfileAttribute>, std::less<>> attributes_;
};
diff --git a/libsparse/img2simg.cpp b/libsparse/img2simg.cpp
index 3e24cc0..51580f7 100644
--- a/libsparse/img2simg.cpp
+++ b/libsparse/img2simg.cpp
@@ -38,24 +38,41 @@
#endif
void usage() {
- fprintf(stderr, "Usage: img2simg <raw_image_file> <sparse_image_file> [<block_size>]\n");
+ fprintf(stderr, "Usage: img2simg [-s] <raw_image_file> <sparse_image_file> [<block_size>]\n");
}
int main(int argc, char* argv[]) {
+ char *arg_in;
+ char *arg_out;
+ enum sparse_read_mode mode = SPARSE_READ_MODE_NORMAL;
+ int extra;
int in;
+ int opt;
int out;
int ret;
struct sparse_file* s;
unsigned int block_size = 4096;
off64_t len;
- if (argc < 3 || argc > 4) {
+ while ((opt = getopt(argc, argv, "s")) != -1) {
+ switch (opt) {
+ case 's':
+ mode = SPARSE_READ_MODE_HOLE;
+ break;
+ default:
+ usage();
+ exit(-1);
+ }
+ }
+
+ extra = argc - optind;
+ if (extra < 2 || extra > 3) {
usage();
exit(-1);
}
- if (argc == 4) {
- block_size = atoi(argv[3]);
+ if (extra == 3) {
+ block_size = atoi(argv[optind + 2]);
}
if (block_size < 1024 || block_size % 4 != 0) {
@@ -63,22 +80,24 @@
exit(-1);
}
- if (strcmp(argv[1], "-") == 0) {
+ arg_in = argv[optind];
+ if (strcmp(arg_in, "-") == 0) {
in = STDIN_FILENO;
} else {
- in = open(argv[1], O_RDONLY | O_BINARY);
+ in = open(arg_in, O_RDONLY | O_BINARY);
if (in < 0) {
- fprintf(stderr, "Cannot open input file %s\n", argv[1]);
+ fprintf(stderr, "Cannot open input file %s\n", arg_in);
exit(-1);
}
}
- if (strcmp(argv[2], "-") == 0) {
+ arg_out = argv[optind + 1];
+ if (strcmp(arg_out, "-") == 0) {
out = STDOUT_FILENO;
} else {
- out = open(argv[2], O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, 0664);
+ out = open(arg_out, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, 0664);
if (out < 0) {
- fprintf(stderr, "Cannot open output file %s\n", argv[2]);
+ fprintf(stderr, "Cannot open output file %s\n", arg_out);
exit(-1);
}
}
@@ -93,7 +112,7 @@
}
sparse_file_verbose(s);
- ret = sparse_file_read(s, in, SPARSE_READ_MODE_NORMAL, false);
+ ret = sparse_file_read(s, in, mode, false);
if (ret) {
fprintf(stderr, "Failed to read file\n");
exit(-1);
diff --git a/libsparse/sparse_fuzzer.cpp b/libsparse/sparse_fuzzer.cpp
index 235d15d..663c812 100644
--- a/libsparse/sparse_fuzzer.cpp
+++ b/libsparse/sparse_fuzzer.cpp
@@ -23,5 +23,7 @@
if (!file) {
return 0;
}
- return sparse_file_callback(file, false, false, WriteCallback, nullptr);
+ int32_t result = sparse_file_callback(file, false, false, WriteCallback, nullptr);
+ sparse_file_destroy(file);
+ return result;
}
diff --git a/libsparse/sparse_read.cpp b/libsparse/sparse_read.cpp
index 028b6be..44f7557 100644
--- a/libsparse/sparse_read.cpp
+++ b/libsparse/sparse_read.cpp
@@ -670,7 +670,7 @@
int64_t len;
int ret;
- s = sparse_file_import(fd, verbose, crc);
+ s = sparse_file_import(fd, false, crc);
if (s) {
return s;
}
@@ -686,6 +686,9 @@
if (!s) {
return nullptr;
}
+ if (verbose) {
+ sparse_file_verbose(s);
+ }
ret = sparse_file_read_normal(s, fd);
if (ret < 0) {
diff --git a/libstats/pull_lazy/TEST_MAPPING b/libstats/pull_lazy/TEST_MAPPING
index 92f1e6a..0282a03 100644
--- a/libstats/pull_lazy/TEST_MAPPING
+++ b/libstats/pull_lazy/TEST_MAPPING
@@ -4,7 +4,7 @@
"name" : "libstatspull_lazy_test"
}
],
- "hwasan-postsubmit" : [
+ "hwasan-presubmit" : [
{
"name" : "libstatspull_lazy_test"
}
diff --git a/libstats/pull_rust/Android.bp b/libstats/pull_rust/Android.bp
index 4ffa98d..a3ef131 100644
--- a/libstats/pull_rust/Android.bp
+++ b/libstats/pull_rust/Android.bp
@@ -22,6 +22,10 @@
name: "libstatspull_bindgen",
wrapper_src: "statslog.h",
crate_name: "statspull_bindgen",
+ visibility: [
+ "//frameworks/proto_logging/stats/stats_log_api_gen",
+ "//packages/modules/Virtualization/libs/statslog_virtualization",
+ ],
source_stem: "bindings",
bindgen_flags: [
"--size_t-is-usize",
@@ -48,7 +52,7 @@
apex_available: [
"//apex_available:platform",
"com.android.virt",
- ]
+ ],
}
rust_library {
diff --git a/libstats/push_compat/statsd_writer.c b/libstats/push_compat/statsd_writer.c
index 04d3b46..4818d11 100644
--- a/libstats/push_compat/statsd_writer.c
+++ b/libstats/push_compat/statsd_writer.c
@@ -61,7 +61,7 @@
static int statsdOpen();
static void statsdClose();
static int statsdWrite(struct timespec* ts, struct iovec* vec, size_t nr);
-static void statsdNoteDrop();
+static void statsdNoteDrop(int error, int tag);
struct android_log_transport_write statsdLoggerWrite = {
.name = "statsd",
diff --git a/libstats/socket_lazy/TEST_MAPPING b/libstats/socket_lazy/TEST_MAPPING
index b182660..03506cd 100644
--- a/libstats/socket_lazy/TEST_MAPPING
+++ b/libstats/socket_lazy/TEST_MAPPING
@@ -4,7 +4,7 @@
"name" : "libstatssocket_lazy_test"
}
],
- "hwasan-postsubmit" : [
+ "hwasan-presubmit" : [
{
"name" : "libstatssocket_lazy_test"
}
diff --git a/libsync/Android.bp b/libsync/Android.bp
index 99c88cf..b6b4a6e 100644
--- a/libsync/Android.bp
+++ b/libsync/Android.bp
@@ -27,6 +27,9 @@
name: "libsync",
symbol_file: "libsync.map.txt",
first_version: "26",
+ export_header_libs: [
+ "libsync_headers",
+ ],
}
cc_defaults {
diff --git a/libsync/libsync.map.txt b/libsync/libsync.map.txt
index aac6b57..32df91e 100644
--- a/libsync/libsync.map.txt
+++ b/libsync/libsync.map.txt
@@ -19,7 +19,7 @@
sync_merge; # introduced=26
sync_file_info; # introduced=26
sync_file_info_free; # introduced=26
- sync_wait; # llndk apex
+ sync_wait; # llndk systemapi
sync_fence_info; # llndk
sync_pt_info; # llndk
sync_fence_info_free; # llndk
diff --git a/libutils/Android.bp b/libutils/Android.bp
index 019a368..7939e82 100644
--- a/libutils/Android.bp
+++ b/libutils/Android.bp
@@ -44,14 +44,6 @@
export_include_dirs: ["include"],
target: {
- android: {
- header_libs: ["libbacktrace_headers"],
- export_header_lib_headers: ["libbacktrace_headers"],
- },
- host_linux: {
- header_libs: ["libbacktrace_headers"],
- export_header_lib_headers: ["libbacktrace_headers"],
- },
linux_bionic: {
enabled: true,
},
@@ -201,7 +193,7 @@
shared_libs: [
"libutils",
- "libbacktrace",
+ "libunwindstack",
],
target: {
diff --git a/libutils/CallStack.cpp b/libutils/CallStack.cpp
index fe6f33d..f19ba6a 100644
--- a/libutils/CallStack.cpp
+++ b/libutils/CallStack.cpp
@@ -20,7 +20,7 @@
#include <utils/Errors.h>
#include <utils/Log.h>
-#include <backtrace/Backtrace.h>
+#include <unwindstack/AndroidUnwinder.h>
#define CALLSTACK_WEAK // Don't generate weak definitions.
#include <utils/CallStack.h>
@@ -39,14 +39,25 @@
}
void CallStack::update(int32_t ignoreDepth, pid_t tid) {
+ if (ignoreDepth < 0) {
+ ignoreDepth = 0;
+ }
+
mFrameLines.clear();
- std::unique_ptr<Backtrace> backtrace(Backtrace::Create(BACKTRACE_CURRENT_PROCESS, tid));
- if (!backtrace->Unwind(ignoreDepth)) {
- ALOGW("%s: Failed to unwind callstack.", __FUNCTION__);
+ unwindstack::AndroidLocalUnwinder unwinder;
+ unwindstack::AndroidUnwinderData data;
+ std::optional<pid_t> tid_val;
+ if (tid != -1) {
+ *tid_val = tid;
}
- for (size_t i = 0; i < backtrace->NumFrames(); i++) {
- mFrameLines.push_back(String8(backtrace->FormatFrameData(i).c_str()));
+ if (!unwinder.Unwind(tid_val, data)) {
+ ALOGW("%s: Failed to unwind callstack: %s", __FUNCTION__, data.GetErrorString().c_str());
+ }
+ for (size_t i = ignoreDepth; i < data.frames.size(); i++) {
+ auto& frame = data.frames[i];
+ frame.num -= ignoreDepth;
+ mFrameLines.push_back(String8(unwinder.FormatFrame(frame).c_str()));
}
}
diff --git a/libutils/RefBase.cpp b/libutils/RefBase.cpp
index 4ddac3d..ed5b2a9 100644
--- a/libutils/RefBase.cpp
+++ b/libutils/RefBase.cpp
@@ -149,6 +149,29 @@
// Same for weak counts.
#define BAD_WEAK(c) ((c) == 0 || ((c) & (~MAX_COUNT)) != 0)
+// name kept because prebuilts used to use it from inlining sp<> code
+void sp_report_stack_pointer() { LOG_ALWAYS_FATAL("RefBase used with stack pointer argument"); }
+
+// Check whether address is definitely on the calling stack. We actually check whether it is on
+// the same 4K page as the frame pointer.
+//
+// Assumptions:
+// - Pages are never smaller than 4K (MIN_PAGE_SIZE)
+// - Malloced memory never shares a page with a stack.
+//
+// It does not appear safe to broaden this check to include adjacent pages; apparently this code
+// is used in environments where there may not be a guard page below (at higher addresses than)
+// the bottom of the stack.
+static void check_not_on_stack(const void* ptr) {
+ static constexpr int MIN_PAGE_SIZE = 0x1000; // 4K. Safer than including sys/user.h.
+ static constexpr uintptr_t MIN_PAGE_MASK = ~static_cast<uintptr_t>(MIN_PAGE_SIZE - 1);
+ uintptr_t my_frame_address =
+ reinterpret_cast<uintptr_t>(__builtin_frame_address(0 /* this frame */));
+ if (((reinterpret_cast<uintptr_t>(ptr) ^ my_frame_address) & MIN_PAGE_MASK) == 0) {
+ sp_report_stack_pointer();
+ }
+}
+
// ---------------------------------------------------------------------------
class RefBase::weakref_impl : public RefBase::weakref_type
@@ -432,6 +455,8 @@
return;
}
+ check_not_on_stack(this);
+
int32_t old __unused = refs->mStrong.fetch_sub(INITIAL_STRONG_VALUE, std::memory_order_relaxed);
// A decStrong() must still happen after us.
ALOG_ASSERT(old > INITIAL_STRONG_VALUE, "0x%x too small", old);
@@ -744,21 +769,27 @@
if (mRefs->mWeak.load(std::memory_order_relaxed) == 0) {
delete mRefs;
}
- } else if (mRefs->mStrong.load(std::memory_order_relaxed) == INITIAL_STRONG_VALUE) {
- // We never acquired a strong reference on this object.
+ } else {
+ int32_t strongs = mRefs->mStrong.load(std::memory_order_relaxed);
- // TODO: make this fatal, but too much code in Android manages RefBase with
- // new/delete manually (or using other mechanisms such as std::make_unique).
- // However, this is dangerous because it's also common for code to use the
- // sp<T>(T*) constructor, assuming that if the object is around, it is already
- // owned by an sp<>.
- ALOGW("RefBase: Explicit destruction, weak count = %d (in %p). Use sp<> to manage this "
- "object.",
- mRefs->mWeak.load(), this);
+ if (strongs == INITIAL_STRONG_VALUE) {
+ // We never acquired a strong reference on this object.
+
+ // It would be nice to make this fatal, but many places use RefBase on the stack.
+ // However, this is dangerous because it's also common for code to use the
+ // sp<T>(T*) constructor, assuming that if the object is around, it is already
+ // owned by an sp<>.
+ ALOGW("RefBase: Explicit destruction, weak count = %d (in %p). Use sp<> to manage this "
+ "object.",
+ mRefs->mWeak.load(), this);
#if CALLSTACK_ENABLED
- CallStack::logStack(LOG_TAG);
+ CallStack::logStack(LOG_TAG);
#endif
+ } else if (strongs != 0) {
+ LOG_ALWAYS_FATAL("RefBase: object %p with strong count %d deleted. Double owned?", this,
+ strongs);
+ }
}
// For debugging purposes, clear mRefs. Ineffective against outstanding wp's.
const_cast<weakref_impl*&>(mRefs) = nullptr;
@@ -766,6 +797,8 @@
void RefBase::extendObjectLifetime(int32_t mode)
{
+ check_not_on_stack(this);
+
// Must be happens-before ordered with respect to construction or any
// operation that could destroy the object.
mRefs->mFlags.fetch_or(mode, std::memory_order_relaxed);
diff --git a/libutils/RefBase_test.cpp b/libutils/RefBase_test.cpp
index 93f9654..aed3b9b 100644
--- a/libutils/RefBase_test.cpp
+++ b/libutils/RefBase_test.cpp
@@ -265,6 +265,21 @@
delete foo;
}
+TEST(RefBase, DoubleOwnershipDeath) {
+ bool isDeleted;
+ auto foo = sp<Foo>::make(&isDeleted);
+
+ // if something else thinks it owns foo, should die
+ EXPECT_DEATH(delete foo.get(), "");
+
+ EXPECT_FALSE(isDeleted);
+}
+
+TEST(RefBase, StackOwnershipDeath) {
+ bool isDeleted;
+ EXPECT_DEATH({ Foo foo(&isDeleted); foo.incStrong(nullptr); }, "");
+}
+
// Set up a situation in which we race with visit2AndRremove() to delete
// 2 strong references. Bar destructor checks that there are no early
// deletions and prior updates are visible to destructor.
diff --git a/libutils/StrongPointer.cpp b/libutils/StrongPointer.cpp
index ef46723..ba52502 100644
--- a/libutils/StrongPointer.cpp
+++ b/libutils/StrongPointer.cpp
@@ -21,7 +21,4 @@
namespace android {
void sp_report_race() { LOG_ALWAYS_FATAL("sp<> assignment detected data race"); }
-
-void sp_report_stack_pointer() { LOG_ALWAYS_FATAL("sp<> constructed with stack pointer argument"); }
-
}
diff --git a/libutils/include/utils/CallStack.h b/libutils/include/utils/CallStack.h
index 7a4a345..fe4d4f5 100644
--- a/libutils/include/utils/CallStack.h
+++ b/libutils/include/utils/CallStack.h
@@ -20,7 +20,6 @@
#include <memory>
#include <android/log.h>
-#include <backtrace/backtrace_constants.h>
#include <utils/String8.h>
#include <utils/Vector.h>
@@ -59,7 +58,7 @@
// Immediately collect the stack traces for the specified thread.
// The default is to dump the stack of the current call.
- void update(int32_t ignoreDepth = 1, pid_t tid = BACKTRACE_CURRENT_THREAD);
+ void update(int32_t ignoreDepth = 1, pid_t tid = -1);
// Dump a stack trace to the log using the supplied logtag.
void log(const char* logtag,
diff --git a/libutils/include/utils/LruCache.h b/libutils/include/utils/LruCache.h
index 36775d0..b4243a3 100644
--- a/libutils/include/utils/LruCache.h
+++ b/libutils/include/utils/LruCache.h
@@ -84,13 +84,13 @@
const TKey& getKey() const final { return key; }
};
- struct HashForEntry : public std::unary_function<KeyedEntry*, hash_t> {
+ struct HashForEntry {
size_t operator() (const KeyedEntry* entry) const {
return hash_type(entry->getKey());
};
};
- struct EqualityForHashedEntries : public std::unary_function<KeyedEntry*, hash_t> {
+ struct EqualityForHashedEntries {
bool operator() (const KeyedEntry* lhs, const KeyedEntry* rhs) const {
return lhs->getKey() == rhs->getKey();
};
diff --git a/libutils/include/utils/RefBase.h b/libutils/include/utils/RefBase.h
index e07f574..f3acd6f 100644
--- a/libutils/include/utils/RefBase.h
+++ b/libutils/include/utils/RefBase.h
@@ -779,6 +779,40 @@
} // namespace android
+namespace libutilsinternal {
+template <typename T, typename = void>
+struct is_complete_type : std::false_type {};
+
+template <typename T>
+struct is_complete_type<T, decltype(void(sizeof(T)))> : std::true_type {};
+} // namespace libutilsinternal
+
+namespace std {
+
+// Define `RefBase` specific versions of `std::make_shared` and
+// `std::make_unique` to block people from using them. Using them to allocate
+// `RefBase` objects results in double ownership. Use
+// `sp<T>::make(...)` instead.
+//
+// Note: We exclude incomplete types because `std::is_base_of` is undefined in
+// that case.
+
+template <typename T, typename... Args,
+ typename std::enable_if<libutilsinternal::is_complete_type<T>::value, bool>::value = true,
+ typename std::enable_if<std::is_base_of<android::RefBase, T>::value, bool>::value = true>
+shared_ptr<T> make_shared(Args...) { // SEE COMMENT ABOVE.
+ static_assert(!std::is_base_of<android::RefBase, T>::value, "Must use RefBase with sp<>");
+}
+
+template <typename T, typename... Args,
+ typename std::enable_if<libutilsinternal::is_complete_type<T>::value, bool>::value = true,
+ typename std::enable_if<std::is_base_of<android::RefBase, T>::value, bool>::value = true>
+unique_ptr<T> make_unique(Args...) { // SEE COMMENT ABOVE.
+ static_assert(!std::is_base_of<android::RefBase, T>::value, "Must use RefBase with sp<>");
+}
+
+} // namespace std
+
// ---------------------------------------------------------------------------
#endif // ANDROID_REF_BASE_H
diff --git a/libutils/include/utils/StrongPointer.h b/libutils/include/utils/StrongPointer.h
index bb1941b..54aa691 100644
--- a/libutils/include/utils/StrongPointer.h
+++ b/libutils/include/utils/StrongPointer.h
@@ -120,7 +120,6 @@
template<typename Y> friend class sp;
template<typename Y> friend class wp;
void set_pointer(T* ptr);
- static inline void check_not_on_stack(const void* ptr);
T* m_ptr;
};
@@ -185,32 +184,10 @@
// For code size reasons, we do not want these inlined or templated.
void sp_report_race();
-void sp_report_stack_pointer();
// ---------------------------------------------------------------------------
// No user serviceable parts below here.
-// Check whether address is definitely on the calling stack. We actually check whether it is on
-// the same 4K page as the frame pointer.
-//
-// Assumptions:
-// - Pages are never smaller than 4K (MIN_PAGE_SIZE)
-// - Malloced memory never shares a page with a stack.
-//
-// It does not appear safe to broaden this check to include adjacent pages; apparently this code
-// is used in environments where there may not be a guard page below (at higher addresses than)
-// the bottom of the stack.
-template <typename T>
-void sp<T>::check_not_on_stack(const void* ptr) {
- static constexpr int MIN_PAGE_SIZE = 0x1000; // 4K. Safer than including sys/user.h.
- static constexpr uintptr_t MIN_PAGE_MASK = ~static_cast<uintptr_t>(MIN_PAGE_SIZE - 1);
- uintptr_t my_frame_address =
- reinterpret_cast<uintptr_t>(__builtin_frame_address(0 /* this frame */));
- if (((reinterpret_cast<uintptr_t>(ptr) ^ my_frame_address) & MIN_PAGE_MASK) == 0) {
- sp_report_stack_pointer();
- }
-}
-
// TODO: Ideally we should find a way to increment the reference count before running the
// constructor, so that generating an sp<> to this in the constructor is no longer dangerous.
template <typename T>
@@ -219,14 +196,13 @@
T* t = new T(std::forward<Args>(args)...);
sp<T> result;
result.m_ptr = t;
- t->incStrong(t); // bypass check_not_on_stack for heap allocation
+ t->incStrong(t);
return result;
}
template <typename T>
sp<T> sp<T>::fromExisting(T* other) {
if (other) {
- check_not_on_stack(other);
other->incStrongRequireStrong(other);
sp<T> result;
result.m_ptr = other;
@@ -240,7 +216,6 @@
sp<T>::sp(T* other)
: m_ptr(other) {
if (other) {
- check_not_on_stack(other);
other->incStrong(this);
}
}
@@ -249,7 +224,6 @@
template <typename U>
sp<T>::sp(U* other) : m_ptr(other) {
if (other) {
- check_not_on_stack(other);
(static_cast<T*>(other))->incStrong(this);
}
}
@@ -258,7 +232,6 @@
sp<T>& sp<T>::operator=(T* other) {
T* oldPtr(*const_cast<T* volatile*>(&m_ptr));
if (other) {
- check_not_on_stack(other);
other->incStrong(this);
}
if (oldPtr) oldPtr->decStrong(this);
diff --git a/libutils/include/utils/Thread.h b/libutils/include/utils/Thread.h
index fc67656..5cf6b47 100644
--- a/libutils/include/utils/Thread.h
+++ b/libutils/include/utils/Thread.h
@@ -42,7 +42,9 @@
{
public:
// Create a Thread object, but doesn't create or start the associated
- // thread. See the run() method.
+ // thread. See the run() method. This object must be used with RefBase/sp,
+ // like any other RefBase object, because they are conventionally promoted
+ // from bare pointers (Thread::run is particularly problematic here).
explicit Thread(bool canCallJava = true);
virtual ~Thread();
diff --git a/libvndksupport/libvndksupport.map.txt b/libvndksupport/libvndksupport.map.txt
index a44ed18..1d94b9d 100644
--- a/libvndksupport/libvndksupport.map.txt
+++ b/libvndksupport/libvndksupport.map.txt
@@ -1,8 +1,8 @@
LIBVNDKSUPPORT {
global:
- android_is_in_vendor_process; # llndk apex
- android_load_sphal_library; # llndk apex
- android_unload_sphal_library; # llndk apex
+ android_is_in_vendor_process; # llndk systemapi
+ android_load_sphal_library; # llndk systemapi
+ android_unload_sphal_library; # llndk systemapi
local:
*;
};
diff --git a/mkbootfs/mkbootfs.c b/mkbootfs/mkbootfs.c
index 58153f3..05d1940 100644
--- a/mkbootfs/mkbootfs.c
+++ b/mkbootfs/mkbootfs.c
@@ -24,8 +24,7 @@
** - device notes, pipes, etc are not supported (error)
*/
-void die(const char *why, ...)
-{
+static void die(const char* why, ...) {
va_list ap;
va_start(ap, why);
@@ -42,7 +41,7 @@
};
static struct fs_config_entry* canned_config = NULL;
-static char *target_out_path = NULL;
+static const char* target_out_path = NULL;
/* Each line in the canned file should be a path plus three ints (uid,
* gid, mode). */
@@ -273,8 +272,7 @@
}
}
-void archive(const char *start, const char *prefix)
-{
+static void archive(const char* start, const char* prefix) {
char in[8192];
char out[8192];
@@ -294,7 +292,7 @@
char line[CANNED_LINE_LENGTH];
FILE* f = fopen(filename, "r");
- if (f == NULL) die("failed to open canned file");
+ if (f == NULL) die("failed to open canned file '%s'", filename);
while (fgets(line, CANNED_LINE_LENGTH, f) != NULL) {
if (!line[0]) break;
@@ -332,6 +330,13 @@
int main(int argc, char *argv[])
{
+ if (argc == 1) {
+ fprintf(stderr,
+ "usage: %s [-d TARGET_OUTPUT_PATH] [-f CANNED_CONFIGURATION_PATH] DIRECTORIES...\n",
+ argv[0]);
+ exit(1);
+ }
+
argc--;
argv++;
diff --git a/property_service/TEST_MAPPING b/property_service/TEST_MAPPING
index 20f6c84..7b02b51 100644
--- a/property_service/TEST_MAPPING
+++ b/property_service/TEST_MAPPING
@@ -4,7 +4,7 @@
"name": "propertyinfoserializer_tests"
}
],
- "hwasan-postsubmit": [
+ "hwasan-presubmit": [
{
"name": "propertyinfoserializer_tests"
}
diff --git a/rootdir/init.rc b/rootdir/init.rc
index cd71aa8..60bf57b 100644
--- a/rootdir/init.rc
+++ b/rootdir/init.rc
@@ -86,31 +86,9 @@
mkdir /dev/sys/fs 0755 system system
mkdir /dev/sys/block 0755 system system
-# Run boringssl self test for each ABI so that later processes can skip it. http://b/139348610
-on early-init && property:ro.product.cpu.abilist32=*
- exec_start boringssl_self_test32
-on early-init && property:ro.product.cpu.abilist64=*
- exec_start boringssl_self_test64
-on property:apexd.status=ready && property:ro.product.cpu.abilist32=*
- exec_start boringssl_self_test_apex32
-on property:apexd.status=ready && property:ro.product.cpu.abilist64=*
- exec_start boringssl_self_test_apex64
-
-service boringssl_self_test32 /system/bin/boringssl_self_test32
- reboot_on_failure reboot,boringssl-self-check-failed
- stdio_to_kmsg
-
-service boringssl_self_test64 /system/bin/boringssl_self_test64
- reboot_on_failure reboot,boringssl-self-check-failed
- stdio_to_kmsg
-
-service boringssl_self_test_apex32 /apex/com.android.conscrypt/bin/boringssl_self_test32
- reboot_on_failure reboot,boringssl-self-check-failed
- stdio_to_kmsg
-
-service boringssl_self_test_apex64 /apex/com.android.conscrypt/bin/boringssl_self_test64
- reboot_on_failure reboot,boringssl-self-check-failed
- stdio_to_kmsg
+ # Create location for fs_mgr to store abbreviated output from filesystem
+ # checker programs.
+ mkdir /dev/fscklogs 0770 root system
on init
sysclktz 0
@@ -443,10 +421,6 @@
mount bpf bpf /sys/fs/bpf nodev noexec nosuid
- # Create location for fs_mgr to store abbreviated output from filesystem
- # checker programs.
- mkdir /dev/fscklogs 0770 root system
-
# pstore/ramoops previous console log
mount pstore pstore /sys/fs/pstore nodev noexec nosuid
chown system log /sys/fs/pstore
@@ -502,6 +476,33 @@
start hwservicemanager
start vndservicemanager
+# Run boringssl self test for each ABI. Any failures trigger reboot to firmware.
+on init && property:ro.product.cpu.abilist32=*
+ exec_start boringssl_self_test32
+on init && property:ro.product.cpu.abilist64=*
+ exec_start boringssl_self_test64
+on property:apexd.status=ready && property:ro.product.cpu.abilist32=*
+ exec_start boringssl_self_test_apex32
+on property:apexd.status=ready && property:ro.product.cpu.abilist64=*
+ exec_start boringssl_self_test_apex64
+
+service boringssl_self_test32 /system/bin/boringssl_self_test32
+ reboot_on_failure reboot,boringssl-self-check-failed
+ stdio_to_kmsg
+
+service boringssl_self_test64 /system/bin/boringssl_self_test64
+ reboot_on_failure reboot,boringssl-self-check-failed
+ stdio_to_kmsg
+
+service boringssl_self_test_apex32 /apex/com.android.conscrypt/bin/boringssl_self_test32
+ reboot_on_failure reboot,boringssl-self-check-failed
+ stdio_to_kmsg
+
+service boringssl_self_test_apex64 /apex/com.android.conscrypt/bin/boringssl_self_test64
+ reboot_on_failure reboot,boringssl-self-check-failed
+ stdio_to_kmsg
+
+
# Healthd can trigger a full boot from charger mode by signaling this
# property when the power button is held.
on property:sys.boot_from_charger_mode=1
@@ -534,6 +535,10 @@
# /data, which in turn can only be loaded when system properties are present.
trigger post-fs-data
+ # APEXes are ready to use. apex-ready is a public trigger similar to apexd.status=ready which
+ # is a system-private property.
+ trigger apex-ready
+
# Should be before netd, but after apex, properties and logging is available.
trigger load_bpf_programs
@@ -684,8 +689,6 @@
copy /data/system/entropy.dat /dev/urandom
mkdir /data/vendor 0771 root root encryption=Require
- mkdir /data/vendor_ce 0771 root root encryption=None
- mkdir /data/vendor_de 0771 root root encryption=None
mkdir /data/vendor/hardware 0771 root root
# Start tombstoned early to be able to store tombstones.
@@ -720,9 +723,13 @@
# Multi-installed APEXes are selected using persist props.
# Load persist properties and override properties (if enabled) from /data,
# before starting apexd.
+ # /data/property should be created before `load_persist_props`
+ mkdir /data/property 0700 root root encryption=Require
load_persist_props
+
start logd
start logd-reinit
+
# Some existing vendor rc files use 'on load_persist_props_action' to know
# when persist props are ready. These are difficult to change due to GRF,
# so continue triggering this action here even though props are already loaded
@@ -734,6 +741,13 @@
# To handle userspace reboots as well as devices that use FDE, make sure
# that apexd is started cleanly here (set apexd.status="") and that it is
# restarted if it's already running.
+ #
+ # /data/apex uses encryption=None because direct I/O support is needed on
+ # APEX files, but some devices don't support direct I/O on encrypted files.
+ # Also, APEXes are public information, similar to the system image.
+ # /data/apex/decompressed and /data/apex/ota_reserved override this setting;
+ # they are encrypted so that files in them can be hard-linked into
+ # /data/rollback which is encrypted.
mkdir /data/apex 0755 root system encryption=None
mkdir /data/apex/active 0755 root system
mkdir /data/apex/backup 0700 root system
@@ -777,7 +791,6 @@
mkdir /data/misc/carrierid 0770 system radio
mkdir /data/misc/apns 0770 system radio
mkdir /data/misc/emergencynumberdb 0770 system radio
- mkdir /data/misc/zoneinfo 0775 system system
mkdir /data/misc/network_watchlist 0774 system system
mkdir /data/misc/textclassifier 0771 system system
mkdir /data/misc/vpn 0770 system vpn
@@ -830,19 +843,19 @@
exec - virtualizationservice system -- /bin/rm -rf /data/misc/virtualizationservice
mkdir /data/misc/virtualizationservice 0770 system system
+ # /data/preloads uses encryption=None because it only contains preloaded
+ # files that are public information, similar to the system image.
mkdir /data/preloads 0775 system system encryption=None
# For security reasons, /data/local/tmp should always be empty.
# Do not place files or directories in /data/local/tmp
mkdir /data/local/tmp 0771 shell shell
mkdir /data/local/traces 0777 shell shell
- mkdir /data/data 0771 system system encryption=None
mkdir /data/app-private 0771 system system encryption=Require
mkdir /data/app-ephemeral 0771 system system encryption=Require
mkdir /data/app-asec 0700 root root encryption=Require
mkdir /data/app-lib 0771 system system encryption=Require
mkdir /data/app 0771 system system encryption=Require
- mkdir /data/property 0700 root root encryption=Require
# create directory for updated font files.
mkdir /data/fonts/ 0771 root root encryption=Require
@@ -874,7 +887,10 @@
chown system system /data/resource-cache
chmod 0771 /data/resource-cache
- # create the lost+found directories, so as to enforce our permissions
+ # Ensure that lost+found exists and has the correct permissions. Linux
+ # filesystems expect this directory to exist; it's where the fsck tool puts
+ # any recovered files that weren't present in any directory. It must be
+ # unencrypted, as fsck must be able to write to it.
mkdir /data/lost+found 0770 root root encryption=None
# create directory for DRM plug-ins - give drm the read/write access to
@@ -902,21 +918,26 @@
mkdir /data/system/heapdump 0700 system system
mkdir /data/system/users 0775 system system
- mkdir /data/system_de 0770 system system encryption=None
- mkdir /data/system_ce 0770 system system encryption=None
-
- mkdir /data/misc_de 01771 system misc encryption=None
+ # Create the parent directories of the user CE and DE storage directories.
+ # These parent directories must use encryption=None, since each of their
+ # subdirectories uses a different encryption policy (a per-user one), and
+ # encryption policies apply recursively. These directories should never
+ # contain any subdirectories other than the per-user ones. /data/media/obb
+ # is an exception that exists for legacy reasons.
+ mkdir /data/media 0770 media_rw media_rw encryption=None
mkdir /data/misc_ce 01771 system misc encryption=None
-
+ mkdir /data/misc_de 01771 system misc encryption=None
+ mkdir /data/system_ce 0770 system system encryption=None
+ mkdir /data/system_de 0770 system system encryption=None
mkdir /data/user 0711 system system encryption=None
mkdir /data/user_de 0711 system system encryption=None
+ mkdir /data/vendor_ce 0771 root root encryption=None
+ mkdir /data/vendor_de 0771 root root encryption=None
- # Unlink /data/user/0 if we previously symlink it to /data/data
- rm /data/user/0
-
- # Bind mount /data/user/0 to /data/data
- mkdir /data/user/0 0700 system system encryption=None
- mount none /data/data /data/user/0 bind rec
+ # Set the casefold flag on /data/media. For upgrades, a restorecon can be
+ # needed first to relabel the directory from media_rw_data_file.
+ restorecon /data/media
+ exec - media_rw media_rw -- /system/bin/chattr +F /data/media
# A tmpfs directory, which will contain all apps CE DE data directory that
# bind mount from the original source.
@@ -929,8 +950,10 @@
mkdir /data_mirror/data_ce/null 0700 root root
mkdir /data_mirror/data_de/null 0700 root root
- # Bind mount CE and DE data directory to mirror's default volume directory
- mount none /data/user /data_mirror/data_ce/null bind rec
+ # Bind mount CE and DE data directory to mirror's default volume directory.
+ # The 'slave' option (MS_SLAVE) is needed to cause the later bind mount of
+ # /data/data onto /data/user/0 to propagate to /data_mirror/data_ce/null/0.
+ mount none /data/user /data_mirror/data_ce/null bind rec slave
mount none /data/user_de /data_mirror/data_de/null bind rec
# Create mirror directory for jit profiles
@@ -963,11 +986,6 @@
wait_for_prop apexd.status activated
perform_apex_config
- # Special-case /data/media/obb per b/64566063
- mkdir /data/media 0770 media_rw media_rw encryption=None
- exec - media_rw media_rw -- /system/bin/chattr +F /data/media
- mkdir /data/media/obb 0770 media_rw media_rw encryption=Attempt
-
# Create directories for boot animation.
mkdir /data/bootanim 0755 system system encryption=DeleteIfNecessary
@@ -1006,10 +1024,6 @@
# completed and apexd.status becomes "ready".
exec_start apexd-snapshotde
- # Check any timezone data in /data is newer than the copy in the time zone data
- # module, delete if not.
- exec - system system -- /system/bin/tzdatacheck /apex/com.android.tzdata/etc/tz /data/misc/zoneinfo
-
# sys.memfd_use set to false by default, which keeps it disabled
# until it is confirmed that apps and vendor processes don't make
# IOCTLs on ashmem fds any more.
@@ -1131,10 +1145,6 @@
chown system system /sys/devices/system/cpu/cpufreq/interactive/io_is_busy
chmod 0660 /sys/devices/system/cpu/cpufreq/interactive/io_is_busy
- # Assume SMP uses shared cpufreq policy for all CPUs
- chown system system /sys/devices/system/cpu/cpu0/cpufreq/scaling_max_freq
- chmod 0660 /sys/devices/system/cpu/cpu0/cpufreq/scaling_max_freq
-
chown system system /sys/class/leds/vibrator/trigger
chown system system /sys/class/leds/vibrator/activate
chown system system /sys/class/leds/vibrator/brightness
@@ -1159,6 +1169,7 @@
chown system system /sys/kernel/ipv4/tcp_rmem_def
chown system system /sys/kernel/ipv4/tcp_rmem_max
chown root radio /proc/cmdline
+ chown root system /proc/bootconfig
# Define default initial receive window size in segments.
setprop net.tcp_def_init_rwnd 60
@@ -1302,6 +1313,7 @@
on userspace-reboot-resume
trigger userspace-reboot-fs-remount
trigger post-fs-data
+ trigger apex-ready
trigger zygote-start
trigger early-boot
trigger boot
diff --git a/set-verity-state/Android.bp b/set-verity-state/Android.bp
index 2f0cb10..f40118b 100644
--- a/set-verity-state/Android.bp
+++ b/set-verity-state/Android.bp
@@ -9,19 +9,31 @@
srcs: ["set-verity-state.cpp"],
shared_libs: [
"libbase",
+ "libbinder",
"libcrypto",
"libcrypto_utils",
- "libcutils",
- "libfec",
"libfs_mgr_binder",
- "liblog",
"libutils",
],
static_libs: [
"libavb_user",
],
+ header_libs: [
+ "libcutils_headers",
+ ],
cflags: ["-Werror"],
+ cppflags: [
+ "-DALLOW_DISABLE_VERITY=0",
+ ],
+ product_variables: {
+ debuggable: {
+ cppflags: [
+ "-UALLOW_DISABLE_VERITY",
+ "-DALLOW_DISABLE_VERITY=1",
+ ],
+ },
+ },
symlinks: [
"enable-verity",
"disable-verity",
diff --git a/set-verity-state/OWNERS b/set-verity-state/OWNERS
new file mode 100644
index 0000000..e849450
--- /dev/null
+++ b/set-verity-state/OWNERS
@@ -0,0 +1,3 @@
+dvander@google.com
+yochiang@google.com
+bowgotsai@google.com
diff --git a/set-verity-state/set-verity-state.cpp b/set-verity-state/set-verity-state.cpp
index 52a7f74..3c0df79 100644
--- a/set-verity-state/set-verity-state.cpp
+++ b/set-verity-state/set-verity-state.cpp
@@ -14,242 +14,230 @@
* limitations under the License.
*/
-#include <errno.h>
-#include <fcntl.h>
-#include <inttypes.h>
-#include <libavb_user/libavb_user.h>
-#include <stdarg.h>
+#include <getopt.h>
#include <stdio.h>
-#include <sys/mount.h>
-#include <sys/stat.h>
-#include <unistd.h>
#include <android-base/file.h>
#include <android-base/logging.h>
#include <android-base/properties.h>
-#include <android-base/stringprintf.h>
-#include <android-base/unique_fd.h>
-#include <fs_mgr.h>
+#include <binder/ProcessState.h>
+#include <cutils/android_reboot.h>
#include <fs_mgr_overlayfs.h>
-#include <fstab/fstab.h>
-#include <log/log_properties.h>
+#include <libavb_user/libavb_user.h>
-#include "fec/io.h"
+using namespace std::string_literals;
+
+namespace {
+
+void print_usage() {
+ printf(
+ "Usage:\n"
+ "\tdisable-verity\n"
+ "\tenable-verity\n"
+ "\tset-verity-state [0|1]\n"
+ "Options:\n"
+ "\t-h --help\tthis help\n"
+ "\t-R --reboot\tautomatic reboot if needed for new settings to take effect\n"
+ "\t-v --verbose\tbe noisy\n");
+}
#ifdef ALLOW_DISABLE_VERITY
-static const bool kAllowDisableVerity = true;
+const bool kAllowDisableVerity = true;
#else
-static const bool kAllowDisableVerity = false;
+const bool kAllowDisableVerity = false;
#endif
-using android::base::unique_fd;
-
-static void suggest_run_adb_root() {
- if (getuid() != 0) printf("Maybe run adb root?\n");
-}
-
-static bool make_block_device_writable(const std::string& dev) {
- unique_fd fd(open(dev.c_str(), O_RDONLY | O_CLOEXEC));
- if (fd == -1) {
- return false;
- }
-
- int OFF = 0;
- bool result = (ioctl(fd.get(), BLKROSET, &OFF) != -1);
- return result;
-}
-
-/* Turn verity on/off */
-static bool set_verity_enabled_state(const char* block_device, const char* mount_point,
- bool enable) {
- if (!make_block_device_writable(block_device)) {
- printf("Could not make block device %s writable (%s).\n", block_device, strerror(errno));
- return false;
- }
-
- fec::io fh(block_device, O_RDWR);
-
- if (!fh) {
- printf("Could not open block device %s (%s).\n", block_device, strerror(errno));
- suggest_run_adb_root();
- return false;
- }
-
- fec_verity_metadata metadata;
-
- if (!fh.get_verity_metadata(metadata)) {
- printf("Couldn't find verity metadata!\n");
- return false;
- }
-
- if (!enable && metadata.disabled) {
- printf("Verity already disabled on %s\n", mount_point);
- return false;
- }
-
- if (enable && !metadata.disabled) {
- printf("Verity already enabled on %s\n", mount_point);
- return false;
- }
-
- if (!fh.set_verity_status(enable)) {
- printf("Could not set verity %s flag on device %s with error %s\n",
- enable ? "enabled" : "disabled", block_device, strerror(errno));
- return false;
- }
-
- auto change = false;
- errno = 0;
- if (enable ? fs_mgr_overlayfs_teardown(mount_point, &change)
- : fs_mgr_overlayfs_setup(nullptr, mount_point, &change)) {
- if (change) {
- printf("%s overlayfs for %s\n", enable ? "disabling" : "using", mount_point);
- }
- } else if (errno) {
- int expected_errno = enable ? EBUSY : ENOENT;
- if (errno != expected_errno) {
- printf("Overlayfs %s for %s failed with error %s\n", enable ? "teardown" : "setup",
- mount_point, strerror(errno));
- }
- }
- printf("Verity %s on %s\n", enable ? "enabled" : "disabled", mount_point);
- return true;
-}
-
/* Helper function to get A/B suffix, if any. If the device isn't
* using A/B the empty string is returned. Otherwise either "_a",
* "_b", ... is returned.
*/
-static std::string get_ab_suffix() {
+std::string get_ab_suffix() {
return android::base::GetProperty("ro.boot.slot_suffix", "");
}
-static bool is_avb_device_locked() {
+bool is_avb_device_locked() {
return android::base::GetProperty("ro.boot.vbmeta.device_state", "") == "locked";
}
-static bool overlayfs_setup(bool enable) {
- auto change = false;
- errno = 0;
- if (enable ? fs_mgr_overlayfs_teardown(nullptr, &change)
- : fs_mgr_overlayfs_setup(nullptr, nullptr, &change)) {
- if (change) {
- printf("%s overlayfs\n", enable ? "disabling" : "using");
- }
- } else if (errno) {
- printf("Overlayfs %s failed with error %s\n", enable ? "teardown" : "setup", strerror(errno));
- suggest_run_adb_root();
- }
- return change;
+bool is_debuggable() {
+ return android::base::GetBoolProperty("ro.debuggable", false);
}
-/* Use AVB to turn verity on/off */
-static bool set_avb_verity_enabled_state(AvbOps* ops, bool enable_verity) {
- std::string ab_suffix = get_ab_suffix();
- bool verity_enabled;
-
- if (is_avb_device_locked()) {
- printf("Device is locked. Please unlock the device first\n");
- return false;
- }
-
- if (!avb_user_verity_get(ops, ab_suffix.c_str(), &verity_enabled)) {
- printf("Error getting verity state. Try adb root first?\n");
- return false;
- }
-
- if ((verity_enabled && enable_verity) || (!verity_enabled && !enable_verity)) {
- printf("verity is already %s\n", verity_enabled ? "enabled" : "disabled");
- return false;
- }
-
- if (!avb_user_verity_set(ops, ab_suffix.c_str(), enable_verity)) {
- printf("Error setting verity\n");
- return false;
- }
-
- overlayfs_setup(enable_verity);
- printf("Successfully %s verity\n", enable_verity ? "enabled" : "disabled");
- return true;
-}
-
-int main(int argc, char* argv[]) {
- if (argc == 0) {
- LOG(FATAL) << "set-verity-state called with empty argv";
- }
-
- std::optional<bool> enable_opt;
- std::string procname = android::base::Basename(argv[0]);
- if (procname == "enable-verity") {
- enable_opt = true;
- } else if (procname == "disable-verity") {
- enable_opt = false;
- }
-
- if (!enable_opt.has_value()) {
- if (argc != 2) {
- printf("usage: %s [1|0]\n", argv[0]);
- return 1;
- }
-
- if (strcmp(argv[1], "1") == 0) {
- enable_opt = true;
- } else if (strcmp(argv[1], "0") == 0) {
- enable_opt = false;
- } else {
- printf("usage: %s [1|0]\n", argv[0]);
- return 1;
- }
- }
-
- bool enable = enable_opt.value();
-
- bool any_changed = false;
-
+bool is_using_avb() {
// Figure out if we're using VB1.0 or VB2.0 (aka AVB) - by
// contract, androidboot.vbmeta.digest is set by the bootloader
// when using AVB).
- bool using_avb = !android::base::GetProperty("ro.boot.vbmeta.digest", "").empty();
+ return !android::base::GetProperty("ro.boot.vbmeta.digest", "").empty();
+}
- // If using AVB, dm-verity is used on any build so we want it to
- // be possible to disable/enable on any build (except USER). For
- // VB1.0 dm-verity is only enabled on certain builds.
- if (!using_avb) {
- if (!kAllowDisableVerity) {
- printf("%s only works for userdebug builds\n", argv[0]);
+[[noreturn]] void reboot(const std::string& name) {
+ LOG(INFO) << "Rebooting device for new settings to take effect";
+ ::sync();
+ android::base::SetProperty(ANDROID_RB_PROPERTY, "reboot," + name);
+ ::sleep(60);
+ LOG(ERROR) << "Failed to reboot";
+ ::exit(1);
+}
+
+bool overlayfs_setup(bool enable) {
+ auto want_reboot = false;
+ errno = 0;
+ if (enable ? fs_mgr_overlayfs_setup(nullptr, &want_reboot)
+ : fs_mgr_overlayfs_teardown(nullptr, &want_reboot)) {
+ if (want_reboot) {
+ LOG(INFO) << (enable ? "Enabled" : "Disabled") << " overlayfs";
}
+ } else {
+ LOG(ERROR) << "Failed to " << (enable ? "enable" : "disable") << " overlayfs";
+ }
+ return want_reboot;
+}
- if (!android::base::GetBoolProperty("ro.secure", false)) {
- overlayfs_setup(enable);
- printf("verity not enabled - ENG build\n");
- return 0;
+struct SetVerityStateResult {
+ bool success = false;
+ bool want_reboot = false;
+};
+
+/* Use AVB to turn verity on/off */
+SetVerityStateResult SetVerityState(bool enable_verity) {
+ std::string ab_suffix = get_ab_suffix();
+ bool verity_enabled = false;
+
+ if (is_avb_device_locked()) {
+ LOG(ERROR) << "Device must be bootloader unlocked to change verity state";
+ return {};
+ }
+
+ std::unique_ptr<AvbOps, decltype(&avb_ops_user_free)> ops(avb_ops_user_new(), &avb_ops_user_free);
+ if (!ops) {
+ LOG(ERROR) << "Error getting AVB ops";
+ return {};
+ }
+
+ if (!avb_user_verity_get(ops.get(), ab_suffix.c_str(), &verity_enabled)) {
+ LOG(ERROR) << "Error getting verity state";
+ return {};
+ }
+
+ if ((verity_enabled && enable_verity) || (!verity_enabled && !enable_verity)) {
+ LOG(INFO) << "Verity is already " << (verity_enabled ? "enabled" : "disabled");
+ return {.success = true, .want_reboot = false};
+ }
+
+ if (!avb_user_verity_set(ops.get(), ab_suffix.c_str(), enable_verity)) {
+ LOG(ERROR) << "Error setting verity state";
+ return {};
+ }
+
+ LOG(INFO) << "Successfully " << (enable_verity ? "enabled" : "disabled") << " verity";
+ return {.success = true, .want_reboot = true};
+}
+
+class MyLogger {
+ public:
+ explicit MyLogger(bool verbose) : verbose_(verbose) {}
+
+ void operator()(android::base::LogId id, android::base::LogSeverity severity, const char* tag,
+ const char* file, unsigned int line, const char* message) {
+ // Hide log starting with '[fs_mgr]' unless it's an error.
+ if (verbose_ || severity >= android::base::ERROR || message[0] != '[') {
+ fprintf(stderr, "%s\n", message);
+ }
+ logd_(id, severity, tag, file, line, message);
+ }
+
+ private:
+ android::base::LogdLogger logd_;
+ bool verbose_;
+};
+
+} // namespace
+
+int main(int argc, char* argv[]) {
+ bool auto_reboot = false;
+ bool verbose = false;
+
+ struct option longopts[] = {
+ {"help", no_argument, nullptr, 'h'},
+ {"reboot", no_argument, nullptr, 'R'},
+ {"verbose", no_argument, nullptr, 'v'},
+ {0, 0, nullptr, 0},
+ };
+ for (int opt; (opt = ::getopt_long(argc, argv, "hRv", longopts, nullptr)) != -1;) {
+ switch (opt) {
+ case 'h':
+ print_usage();
+ return 0;
+ case 'R':
+ auto_reboot = true;
+ break;
+ case 'v':
+ verbose = true;
+ break;
+ default:
+ print_usage();
+ return 1;
}
}
- // Should never be possible to disable dm-verity on a USER build
- // regardless of using AVB or VB1.0.
- if (!__android_log_is_debuggable()) {
- printf("verity cannot be disabled/enabled - USER build\n");
- return 0;
+ android::base::InitLogging(argv, MyLogger(verbose));
+
+ bool enable_verity = false;
+ const std::string progname = getprogname();
+ if (progname == "enable-verity") {
+ enable_verity = true;
+ } else if (progname == "disable-verity") {
+ enable_verity = false;
+ } else if (optind < argc && (argv[optind] == "1"s || argv[optind] == "0"s)) {
+ // progname "set-verity-state"
+ enable_verity = (argv[optind] == "1"s);
+ } else {
+ print_usage();
+ return 1;
}
- if (using_avb) {
- // Yep, the system is using AVB.
- AvbOps* ops = avb_ops_user_new();
- if (ops == nullptr) {
- printf("Error getting AVB ops\n");
- return 1;
+ if (!kAllowDisableVerity || !is_debuggable()) {
+ errno = EPERM;
+ PLOG(ERROR) << "Cannot disable/enable verity on user build";
+ return 1;
+ }
+
+ if (getuid() != 0) {
+ errno = EACCES;
+ PLOG(ERROR) << "Must be running as root (adb root)";
+ return 1;
+ }
+
+ if (!is_using_avb()) {
+ LOG(ERROR) << "Expected AVB device, VB1.0 is no longer supported";
+ return 1;
+ }
+
+ int exit_code = 0;
+ bool want_reboot = false;
+
+ auto ret = SetVerityState(enable_verity);
+ if (ret.success) {
+ want_reboot |= ret.want_reboot;
+ } else {
+ exit_code = 1;
+ }
+
+ // Disable any overlayfs unconditionally if we want verity enabled.
+ // Enable overlayfs only if verity is successfully disabled or is already disabled.
+ if (enable_verity || ret.success) {
+ // Start a threadpool to service waitForService() callbacks as
+ // fs_mgr_overlayfs_* might call waitForService() to get the image service.
+ android::ProcessState::self()->startThreadPool();
+ want_reboot |= overlayfs_setup(!enable_verity);
+ }
+
+ if (want_reboot) {
+ if (auto_reboot) {
+ reboot(progname);
}
- if (set_avb_verity_enabled_state(ops, enable)) {
- any_changed = true;
- }
- avb_ops_user_free(ops);
- }
- if (!any_changed) any_changed = overlayfs_setup(enable);
-
- if (any_changed) {
- printf("Now reboot your device for settings to take effect\n");
+ printf("Reboot the device for new settings to take effect\n");
}
- return 0;
+ return exit_code;
}
diff --git a/trusty/OWNERS b/trusty/OWNERS
index 5c4e03a..61b97c6 100644
--- a/trusty/OWNERS
+++ b/trusty/OWNERS
@@ -1,9 +1,11 @@
armellel@google.com
arve@android.com
+danielangell@google.com
gmar@google.com
marcone@google.com
mmaurer@google.com
ncbray@google.com
swillden@google.com
+thurston@google.com
trong@google.com
wenhaowang@google.com
diff --git a/trusty/apploader/apploader.cpp b/trusty/apploader/apploader.cpp
index 278499f..17d083c 100644
--- a/trusty/apploader/apploader.cpp
+++ b/trusty/apploader/apploader.cpp
@@ -226,6 +226,9 @@
case APPLOADER_ERR_POLICY_VIOLATION:
LOG(ERROR) << "Error: loading denied by policy engine";
break;
+ case APPLOADER_ERR_NOT_ENCRYPTED:
+ LOG(ERROR) << "Error: unmet application encryption requirement";
+ break;
default:
LOG(ERROR) << "Unrecognized error: " << resp.error;
break;
diff --git a/trusty/apploader/apploader_ipc.h b/trusty/apploader/apploader_ipc.h
index 306596e..f037692 100644
--- a/trusty/apploader/apploader_ipc.h
+++ b/trusty/apploader/apploader_ipc.h
@@ -45,6 +45,10 @@
* @APPLOADER_ERR_INTERNAL: miscellaneous or internal apploader
* error not covered by the above
* @APPLOADER_ERR_INVALID_VERSION: invalid application version
+ * @APPLOADER_ERR_POLICY_VIOLATION: signature verification succeeded but
+ * key+manifest combination not allowed
+ * by app loader policy engine
+ * @APPLOADER_ERR_NOT_ENCRYPTED: unmet application encryption requirement
*/
enum apploader_error : uint32_t {
APPLOADER_NO_ERROR = 0,
@@ -57,6 +61,7 @@
APPLOADER_ERR_INTERNAL,
APPLOADER_ERR_INVALID_VERSION,
APPLOADER_ERR_POLICY_VIOLATION,
+ APPLOADER_ERR_NOT_ENCRYPTED,
};
/**
diff --git a/trusty/keymaster/keymint/TEST_MAPPING b/trusty/keymaster/keymint/TEST_MAPPING
index ae24fb4..6671355 100644
--- a/trusty/keymaster/keymint/TEST_MAPPING
+++ b/trusty/keymaster/keymint/TEST_MAPPING
@@ -4,7 +4,7 @@
"name" : "vts_treble_vintf_framework_test"
}
],
- "hwasan-postsubmit" : [
+ "hwasan-presubmit" : [
{
"name" : "vts_treble_vintf_framework_test"
}
diff --git a/trusty/libtrusty-rs/Android.bp b/trusty/libtrusty-rs/Android.bp
index bc1dcf6..4fc162b 100644
--- a/trusty/libtrusty-rs/Android.bp
+++ b/trusty/libtrusty-rs/Android.bp
@@ -19,6 +19,7 @@
rust_library {
name: "libtrusty-rs",
crate_name: "trusty",
+ vendor_available: true,
srcs: [
"src/lib.rs"
],
diff --git a/trusty/metrics/metrics_test.cpp b/trusty/metrics/metrics_test.cpp
index 407ddf2..0c6db7f 100644
--- a/trusty/metrics/metrics_test.cpp
+++ b/trusty/metrics/metrics_test.cpp
@@ -31,7 +31,7 @@
using android::base::unique_fd;
static void TriggerCrash() {
- size_t num_retries = 3;
+ size_t num_retries = 6;
int fd = -1;
for (size_t i = 0; i < num_retries; i++) {
@@ -61,6 +61,18 @@
virtual void SetUp() override {
auto ret = Open();
ASSERT_TRUE(ret.ok()) << ret.error();
+
+ /* Drain events (if any) and reset state */
+ DrainEvents();
+ crashed_app_.clear();
+ event_drop_count_ = 0;
+ }
+
+ void DrainEvents() {
+ while (WaitForEvent(1000 /* 1 second timeout */).ok()) {
+ auto ret = HandleEvent();
+ ASSERT_TRUE(ret.ok()) << ret.error();
+ }
}
void WaitForAndHandleEvent() {
@@ -79,6 +91,9 @@
TriggerCrash();
WaitForAndHandleEvent();
+ /* Check that no event was dropped. */
+ ASSERT_EQ(event_drop_count_, 0);
+
/* Check that correct TA crashed. */
ASSERT_EQ(crashed_app_, "36f5b435-5bd3-4526-8b76-200e3a7e79f3:crasher");
}
@@ -110,6 +125,9 @@
auto ret = HandleEvent();
ASSERT_TRUE(ret.ok()) << ret.error();
+ /* Check that no event was dropped. */
+ ASSERT_EQ(event_drop_count_, 0);
+
/* Check that correct TA crashed. */
ASSERT_EQ(crashed_app_, "36f5b435-5bd3-4526-8b76-200e3a7e79f3:crasher");
}
diff --git a/trusty/storage/interface/include/trusty/interface/storage.h b/trusty/storage/interface/include/trusty/interface/storage.h
index 3f1dcb8..255ade1 100644
--- a/trusty/storage/interface/include/trusty/interface/storage.h
+++ b/trusty/storage/interface/include/trusty/interface/storage.h
@@ -70,6 +70,9 @@
* @STORAGE_ERR_TRANSACT returned by various operations to indicate that current transaction
* is in error state. Such state could be only cleared by sending
* STORAGE_END_TRANSACTION message.
+ * @STORAGE_ERR_SYNC_FAILURE indicates that the current operation failed to sync
+ * to disk. Only returned if STORAGE_MSG_FLAG_PRE_COMMIT or
+ * STORAGE_MSG_FLAG_POST_COMMIT was set for the request.
*/
enum storage_err {
STORAGE_NO_ERROR = 0,
@@ -80,6 +83,7 @@
STORAGE_ERR_NOT_FOUND = 5,
STORAGE_ERR_EXIST = 6,
STORAGE_ERR_TRANSACT = 7,
+ STORAGE_ERR_SYNC_FAILURE = 8,
};
/**
diff --git a/trusty/storage/proxy/proxy.c b/trusty/storage/proxy/proxy.c
index 2620034..b970406 100644
--- a/trusty/storage/proxy/proxy.c
+++ b/trusty/storage/proxy/proxy.c
@@ -70,56 +70,14 @@
exit(code);
}
-static int drop_privs(void) {
- struct __user_cap_header_struct capheader;
- struct __user_cap_data_struct capdata[2];
-
- if (prctl(PR_SET_KEEPCAPS, 1, 0, 0, 0) < 0) {
- return -1;
- }
-
- /*
- * ensure we're running as the system user
- */
- if (setgid(AID_SYSTEM) != 0) {
- return -1;
- }
-
- if (setuid(AID_SYSTEM) != 0) {
- return -1;
- }
-
- /*
- * drop all capabilities except SYS_RAWIO
- */
- memset(&capheader, 0, sizeof(capheader));
- memset(&capdata, 0, sizeof(capdata));
- capheader.version = _LINUX_CAPABILITY_VERSION_3;
- capheader.pid = 0;
-
- capdata[CAP_TO_INDEX(CAP_SYS_RAWIO)].permitted = CAP_TO_MASK(CAP_SYS_RAWIO);
- capdata[CAP_TO_INDEX(CAP_SYS_RAWIO)].effective = CAP_TO_MASK(CAP_SYS_RAWIO);
-
- if (capset(&capheader, &capdata[0]) < 0) {
- return -1;
- }
-
- /*
- * No access for group and other. We need execute access for user to create
- * an accessible directory.
- */
- umask(S_IRWXG | S_IRWXO);
-
- return 0;
-}
-
static int handle_req(struct storage_msg* msg, const void* req, size_t req_len) {
int rc;
- if ((msg->flags & STORAGE_MSG_FLAG_POST_COMMIT) && (msg->cmd != STORAGE_RPMB_SEND)) {
+ if ((msg->flags & STORAGE_MSG_FLAG_POST_COMMIT) && msg->cmd != STORAGE_RPMB_SEND &&
+ msg->cmd != STORAGE_FILE_WRITE) {
/*
- * handling post commit messages on non rpmb commands are not
- * implemented as there is no use case for this yet.
+ * handling post commit messages on commands other than rpmb and write
+ * operations are not implemented as there is no use case for this yet.
*/
ALOGE("cmd 0x%x: post commit option is not implemented\n", msg->cmd);
msg->result = STORAGE_ERR_UNIMPLEMENTED;
@@ -129,7 +87,7 @@
if (msg->flags & STORAGE_MSG_FLAG_PRE_COMMIT) {
rc = storage_sync_checkpoint();
if (rc < 0) {
- msg->result = STORAGE_ERR_GENERIC;
+ msg->result = STORAGE_ERR_SYNC_FAILURE;
return ipc_respond(msg, NULL, 0);
}
}
@@ -260,8 +218,11 @@
int main(int argc, char* argv[]) {
int rc;
- /* drop privileges */
- if (drop_privs() < 0) return EXIT_FAILURE;
+ /*
+ * No access for group and other. We need execute access for user to create
+ * an accessible directory.
+ */
+ umask(S_IRWXG | S_IRWXO);
/* parse arguments */
parse_args(argc, argv);
diff --git a/trusty/storage/proxy/storage.c b/trusty/storage/proxy/storage.c
index c00c399..c531cfd 100644
--- a/trusty/storage/proxy/storage.c
+++ b/trusty/storage/proxy/storage.c
@@ -407,6 +407,14 @@
goto err_response;
}
+ if (msg->flags & STORAGE_MSG_FLAG_POST_COMMIT) {
+ rc = storage_sync_checkpoint();
+ if (rc < 0) {
+ msg->result = STORAGE_ERR_SYNC_FAILURE;
+ goto err_response;
+ }
+ }
+
msg->result = STORAGE_NO_ERROR;
err_response:
diff --git a/trusty/test/binder/aidl/ByteEnum.aidl b/trusty/test/binder/aidl/ByteEnum.aidl
new file mode 100644
index 0000000..d3a13ac
--- /dev/null
+++ b/trusty/test/binder/aidl/ByteEnum.aidl
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2022 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.
+ */
+
+/*
+ * Hello, world!
+ */
+@Backing(type="byte")
+enum ByteEnum {
+ // Comment about FOO.
+ FOO = 1,
+ BAR = 2,
+ BAZ,
+}
diff --git a/trusty/test/binder/aidl/ITestService.aidl b/trusty/test/binder/aidl/ITestService.aidl
new file mode 100644
index 0000000..c6a99c8
--- /dev/null
+++ b/trusty/test/binder/aidl/ITestService.aidl
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+import ByteEnum;
+import IntEnum;
+import LongEnum;
+
+interface ITestService {
+ const @utf8InCpp String PORT = "com.android.trusty.binder.test.service";
+
+ const int TEST_CONSTANT = 42;
+ const int TEST_CONSTANT2 = -42;
+ const int TEST_CONSTANT3 = +42;
+ const int TEST_CONSTANT4 = +4;
+ const int TEST_CONSTANT5 = -4;
+ const int TEST_CONSTANT6 = -0;
+ const int TEST_CONSTANT7 = +0;
+ const int TEST_CONSTANT8 = 0;
+ const int TEST_CONSTANT9 = 0x56;
+ const int TEST_CONSTANT10 = 0xa5;
+ const int TEST_CONSTANT11 = 0xFA;
+ const int TEST_CONSTANT12 = 0xffffffff;
+
+ const byte BYTE_TEST_CONSTANT = 17;
+ const long LONG_TEST_CONSTANT = 1L << 40;
+
+ const String STRING_TEST_CONSTANT = "foo";
+ const String STRING_TEST_CONSTANT2 = "bar";
+
+ // Test that primitives work as parameters and return types.
+ boolean RepeatBoolean(boolean token);
+ byte RepeatByte(byte token);
+ char RepeatChar(char token);
+ int RepeatInt(int token);
+ long RepeatLong(long token);
+ float RepeatFloat(float token);
+ double RepeatDouble(double token);
+ String RepeatString(String token);
+ ByteEnum RepeatByteEnum(ByteEnum token);
+ IntEnum RepeatIntEnum(IntEnum token);
+ LongEnum RepeatLongEnum(LongEnum token);
+
+ // Test that arrays work as parameters and return types.
+ boolean[] ReverseBoolean(in boolean[] input, out boolean[] repeated);
+ byte[] ReverseByte(in byte[] input, out byte[] repeated);
+ char[] ReverseChar(in char[] input, out char[] repeated);
+ int[] ReverseInt(in int[] input, out int[] repeated);
+ long[] ReverseLong(in long[] input, out long[] repeated);
+ float[] ReverseFloat(in float[] input, out float[] repeated);
+ double[] ReverseDouble(in double[] input, out double[] repeated);
+ String[] ReverseString(in String[] input, out String[] repeated);
+ ByteEnum[] ReverseByteEnum(in ByteEnum[] input, out ByteEnum[] repeated);
+ IntEnum[] ReverseIntEnum(in IntEnum[] input, out IntEnum[] repeated);
+ LongEnum[] ReverseLongEnum(in LongEnum[] input, out LongEnum[] repeated);
+}
diff --git a/trusty/test/binder/aidl/IntEnum.aidl b/trusty/test/binder/aidl/IntEnum.aidl
new file mode 100644
index 0000000..120e44f
--- /dev/null
+++ b/trusty/test/binder/aidl/IntEnum.aidl
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2022 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.
+ */
+
+@JavaDerive(toString=true)
+@Backing(type="int")
+enum IntEnum {
+ FOO = 1000,
+ BAR = 2000,
+ BAZ,
+ /** @deprecated do not use this */
+ QUX,
+}
diff --git a/trusty/test/binder/aidl/LongEnum.aidl b/trusty/test/binder/aidl/LongEnum.aidl
new file mode 100644
index 0000000..0e9e933
--- /dev/null
+++ b/trusty/test/binder/aidl/LongEnum.aidl
@@ -0,0 +1,22 @@
+/*
+ * Copyright (C) 2022 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.
+ */
+
+@Backing(type="long")
+enum LongEnum {
+ FOO = 100000000000,
+ BAR = 200000000000,
+ BAZ,
+}
diff --git a/trusty/test/binder/aidl/rules.mk b/trusty/test/binder/aidl/rules.mk
new file mode 100644
index 0000000..6154abb
--- /dev/null
+++ b/trusty/test/binder/aidl/rules.mk
@@ -0,0 +1,26 @@
+# Copyright (C) 2022 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.
+#
+
+LOCAL_DIR := $(GET_LOCAL_DIR)
+
+MODULE := $(LOCAL_DIR)
+
+MODULE_AIDLS := \
+ $(LOCAL_DIR)/ByteEnum.aidl \
+ $(LOCAL_DIR)/IntEnum.aidl \
+ $(LOCAL_DIR)/ITestService.aidl \
+ $(LOCAL_DIR)/LongEnum.aidl \
+
+include make/aidl.mk
diff --git a/trusty/utils/acvp/acvp_ipc.h b/trusty/utils/acvp/acvp_ipc.h
index 300e05a..fc1c9d7 100644
--- a/trusty/utils/acvp/acvp_ipc.h
+++ b/trusty/utils/acvp/acvp_ipc.h
@@ -45,7 +45,7 @@
* This must be at least as long as the longest reply from the ACVP service
* (currently the reply from getConfig()).
*/
-#define ACVP_MIN_SHARED_MEMORY 16384
+#define ACVP_MIN_SHARED_MEMORY 32768
/**
* acvp_req - Request for the Trusty ACVP app