Merge changes I264d0891,I1af733e7,Iae344c1e
* changes:
Added init_ueventHandler_fuzzer
Added init_property_fuzzer
Added init_parser_fuzzer
diff --git a/bootstat/boot_reason_test.sh b/bootstat/boot_reason_test.sh
index 299b5d4..845b303 100755
--- a/bootstat/boot_reason_test.sh
+++ b/bootstat/boot_reason_test.sh
@@ -367,8 +367,6 @@
adb logcat -b all ${timestamp} |
grep bootstat[^e] |
grep -v -F "bootstat: Service started: /system/bin/bootstat --record_boot_complete${match}
-bootstat: Failed to read /data/misc/bootstat/post_decrypt_time_elapsed: No such file or directory
-bootstat: Failed to parse boot time record: /data/misc/bootstat/post_decrypt_time_elapsed
bootstat: Service started: /system/bin/bootstat --record_boot_reason
bootstat: Service started: /system/bin/bootstat --set_system_boot_reason
bootstat: Service started: /system/bin/bootstat --record_time_since_factory_reset
@@ -388,7 +386,6 @@
init : processing action (sys.boot_completed=1 && sys.bootstat.first_boot_completed=0) from (/system/etc/init/bootstat.rc
(/system/bin/bootstat --record_boot_complete --record_boot_reason --record_time_since_factory_reset -l)'
(/system/bin/bootstat --set_system_boot_reason --record_boot_complete --record_boot_reason --record_time_since_factory_reset -l)'
- (/system/bin/bootstat -r post_decrypt_time_elapsed)'
init : Command 'exec - system log -- /system/bin/bootstat --record_boot_complete' action=sys.boot_completed=1 && sys.bootstat.first_boot_completed=0 (/system/etc/init/bootstat.rc:
init : Command 'exec - system log -- /system/bin/bootstat --record_boot_reason' action=sys.boot_completed=1 && sys.bootstat.first_boot_completed=0 (/system/etc/init/bootstat.rc:
init : Command 'exec - system log -- /system/bin/bootstat --record_time_since_factory_reset' action=sys.boot_completed=1 && sys.bootstat.first_boot_completed=0 (/system/etc/init/bootstat.rc:
diff --git a/bootstat/bootstat.cpp b/bootstat/bootstat.cpp
index 589b99a..844357c 100644
--- a/bootstat/bootstat.cpp
+++ b/bootstat/bootstat.cpp
@@ -67,15 +67,9 @@
{"boot_complete",
{android::util::BOOT_TIME_EVENT_ELAPSED_TIME_REPORTED,
android::util::BOOT_TIME_EVENT_ELAPSED_TIME__EVENT__BOOT_COMPLETE}},
- {"boot_decryption_complete",
- {android::util::BOOT_TIME_EVENT_ELAPSED_TIME_REPORTED,
- android::util::BOOT_TIME_EVENT_ELAPSED_TIME__EVENT__BOOT_COMPLETE_ENCRYPTION}},
{"boot_complete_no_encryption",
{android::util::BOOT_TIME_EVENT_ELAPSED_TIME_REPORTED,
android::util::BOOT_TIME_EVENT_ELAPSED_TIME__EVENT__BOOT_COMPLETE_NO_ENCRYPTION}},
- {"boot_complete_post_decrypt",
- {android::util::BOOT_TIME_EVENT_ELAPSED_TIME_REPORTED,
- android::util::BOOT_TIME_EVENT_ELAPSED_TIME__EVENT__BOOT_COMPLETE_POST_DECRYPT}},
{"factory_reset_boot_complete",
{android::util::BOOT_TIME_EVENT_ELAPSED_TIME_REPORTED,
android::util::BOOT_TIME_EVENT_ELAPSED_TIME__EVENT__FACTORY_RESET_BOOT_COMPLETE}},
@@ -83,22 +77,12 @@
{android::util::BOOT_TIME_EVENT_ELAPSED_TIME_REPORTED,
android::util::
BOOT_TIME_EVENT_ELAPSED_TIME__EVENT__FACTORY_RESET_BOOT_COMPLETE_NO_ENCRYPTION}},
- {"factory_reset_boot_complete_post_decrypt",
- {android::util::BOOT_TIME_EVENT_ELAPSED_TIME_REPORTED,
- android::util::
- BOOT_TIME_EVENT_ELAPSED_TIME__EVENT__FACTORY_RESET_BOOT_COMPLETE_POST_DECRYPT}},
{"ota_boot_complete",
{android::util::BOOT_TIME_EVENT_ELAPSED_TIME_REPORTED,
android::util::BOOT_TIME_EVENT_ELAPSED_TIME__EVENT__OTA_BOOT_COMPLETE}},
{"ota_boot_complete_no_encryption",
{android::util::BOOT_TIME_EVENT_ELAPSED_TIME_REPORTED,
android::util::BOOT_TIME_EVENT_ELAPSED_TIME__EVENT__OTA_BOOT_COMPLETE_NO_ENCRYPTION}},
- {"ota_boot_complete_post_decrypt",
- {android::util::BOOT_TIME_EVENT_ELAPSED_TIME_REPORTED,
- android::util::BOOT_TIME_EVENT_ELAPSED_TIME__EVENT__OTA_BOOT_COMPLETE_POST_DECRYPT}},
- {"post_decrypt_time_elapsed",
- {android::util::BOOT_TIME_EVENT_ELAPSED_TIME_REPORTED,
- android::util::BOOT_TIME_EVENT_ELAPSED_TIME__EVENT__POST_DECRYPT}},
// DURATION
{"absolute_boot_time",
{android::util::BOOT_TIME_EVENT_DURATION_REPORTED,
@@ -1313,8 +1297,7 @@
return android::base::boot_clock::now().time_since_epoch() - GetBootTimeOffset();
}
-// Records several metrics related to the time it takes to boot the device,
-// including disambiguating boot time on encrypted or non-encrypted devices.
+// Records several metrics related to the time it takes to boot the device.
void RecordBootComplete() {
BootEventRecordStore boot_event_store;
BootEventRecordStore::BootEventRecord record;
@@ -1341,25 +1324,15 @@
return;
}
- // post_decrypt_time_elapsed is only logged on encrypted devices.
- if (boot_event_store.GetBootEvent("post_decrypt_time_elapsed", &record)) {
- // Log the amount of time elapsed until the device is decrypted, which
- // includes the variable amount of time the user takes to enter the
- // decryption password.
- boot_event_store.AddBootEventWithValue("boot_decryption_complete", uptime_s.count());
+ // The *_no_encryption events are emitted unconditionally, since they are left
+ // over from a time when encryption meant "full-disk encryption". But Android
+ // now always uses file-based encryption instead of full-disk encryption. At
+ // some point, these misleading and redundant events should be removed.
+ boot_event_store.AddBootEventWithValue(boot_complete_prefix + "_no_encryption",
+ uptime_s.count());
- // Subtract the decryption time to normalize the boot cycle timing.
- std::chrono::seconds boot_complete = std::chrono::seconds(uptime_s.count() - record.second);
- boot_event_store.AddBootEventWithValue(boot_complete_prefix + "_post_decrypt",
- boot_complete.count());
- } else {
- boot_event_store.AddBootEventWithValue(boot_complete_prefix + "_no_encryption",
- uptime_s.count());
- }
-
- // Record the total time from device startup to boot complete, regardless of
- // encryption state.
- // Note: we are recording seconds here even though the field in statsd atom specifies
+ // Record the total time from device startup to boot complete. Note: we are
+ // recording seconds here even though the field in statsd atom specifies
// milliseconds.
boot_event_store.AddBootEventWithValue(boot_complete_prefix, uptime_s.count());
diff --git a/bootstat/bootstat.rc b/bootstat/bootstat.rc
index a350fe7..23f01d1 100644
--- a/bootstat/bootstat.rc
+++ b/bootstat/bootstat.rc
@@ -33,7 +33,6 @@
chown system log /data/misc/bootstat/last_boot_time_utc
chown system log /data/misc/bootstat/ota_boot_complete
chown system log /data/misc/bootstat/ota_boot_complete_no_encryption
- chown system log /data/misc/bootstat/post_decrypt_time_elapsed
chown system log /data/misc/bootstat/ro.boottime.init
chown system log /data/misc/bootstat/ro.boottime.init.cold_boot_wait
chown system log /data/misc/bootstat/ro.boottime.init.selinux
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/debuggerd_test.cpp b/debuggerd/debuggerd_test.cpp
index ed90ab4..aca476f 100644
--- a/debuggerd/debuggerd_test.cpp
+++ b/debuggerd/debuggerd_test.cpp
@@ -1403,7 +1403,7 @@
// We can't actually generate a backtrace, just make sure that the process terminates.
}
-__attribute__((noinline)) extern "C" bool raise_debugger_signal(DebuggerdDumpType dump_type) {
+__attribute__((__noinline__)) extern "C" bool raise_debugger_signal(DebuggerdDumpType dump_type) {
siginfo_t siginfo;
siginfo.si_code = SI_QUEUE;
siginfo.si_pid = getpid();
diff --git a/debuggerd/handler/debuggerd_handler.cpp b/debuggerd/handler/debuggerd_handler.cpp
index 92e7675..c64de0e 100644
--- a/debuggerd/handler/debuggerd_handler.cpp
+++ b/debuggerd/handler/debuggerd_handler.cpp
@@ -24,6 +24,7 @@
#include <sched.h>
#include <signal.h>
#include <stddef.h>
+#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
@@ -51,7 +52,6 @@
#include "handler/fallback.h"
-using ::android::base::GetBoolProperty;
using ::android::base::ParseBool;
using ::android::base::ParseBoolResult;
using ::android::base::Pipe;
@@ -87,10 +87,31 @@
return syscall(__NR_gettid);
}
+static bool property_parse_bool(const char* name) {
+ const prop_info* pi = __system_property_find(name);
+ if (!pi) return false;
+ bool cookie = false;
+ __system_property_read_callback(
+ pi,
+ [](void* cookie, const char*, const char* value, uint32_t) {
+ *reinterpret_cast<bool*>(cookie) = ParseBool(value) == ParseBoolResult::kTrue;
+ },
+ &cookie);
+ return cookie;
+}
+
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) ||
+ char process_sysprop_name[512];
+ async_safe_format_buffer(process_sysprop_name, sizeof(process_sysprop_name),
+ "persist.device_config.memory_safety_native.permissive.process.%s",
+ getprogname());
+ // DO NOT REPLACE this with GetBoolProperty. That uses std::string which allocates, so it is
+ // not async-safe (and this functiong gets used in a signal handler).
+ return property_parse_bool("persist.sys.mte.permissive") ||
+ property_parse_bool("persist.device_config.memory_safety_native.permissive.default") ||
+ property_parse_bool(process_sysprop_name) ||
(permissive_env && ParseBool(permissive_env) == ParseBoolResult::kTrue);
}
@@ -623,6 +644,18 @@
async_safe_format_log(ANDROID_LOG_ERROR, "libc",
"MTE ERROR DETECTED BUT RUNNING IN PERMISSIVE MODE. CONTINUING.");
pthread_mutex_unlock(&crash_mutex);
+ } else if (info->si_signo == SIGSEGV && info->si_code == SEGV_MTEAERR && getppid() == 1) {
+ // Back channel to init (see system/core/init/service.cpp) to signal that
+ // this process crashed due to an ASYNC MTE fault and should be considered
+ // for upgrade to SYNC mode. We are re-using the ART profiler signal, which
+ // is always handled (ignored in native processes, handled for generating a
+ // dump in ART processes), so a process will never crash from this signal
+ // except from here.
+ // The kernel is not particularly receptive to adding this information:
+ // https://lore.kernel.org/all/20220909180617.374238-1-fmayer@google.com/, so we work around
+ // like this.
+ info->si_signo = BIONIC_SIGNAL_ART_PROFILER;
+ resend_signal(info);
}
#endif
else {
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/proto/tombstone.proto b/debuggerd/proto/tombstone.proto
index a0f2f82..f0d3d3f 100644
--- a/debuggerd/proto/tombstone.proto
+++ b/debuggerd/proto/tombstone.proto
@@ -1,3 +1,12 @@
+//
+// Protobuf definition for Android tombstones.
+//
+// An app can get hold of these for any `REASON_CRASH_NATIVE` instance of
+// `android.app.ApplicationExitInfo`.
+//
+// https://developer.android.com/reference/android/app/ApplicationExitInfo#getTraceInputStream()
+//
+
syntax = "proto3";
option java_package = "com.android.server.os";
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
index 5ff2b5b..0203adc 100644
--- a/debuggerd/test_permissive_mte/src/com/android/tests/debuggerd/PermissiveMteTest.java
+++ b/debuggerd/test_permissive_mte/src/com/android/tests/debuggerd/PermissiveMteTest.java
@@ -97,4 +97,33 @@
}
assertThat(numberTombstones).isEqualTo(1);
}
+ @Test
+ public void testCrashProperty() throws Exception {
+ String prevValue = getDevice().getProperty("persist.sys.mte.permissive");
+ if (prevValue == null) {
+ prevValue = "";
+ }
+ assertThat(getDevice().setProperty("persist.sys.mte.permissive", "1")).isTrue();
+ CommandResult result =
+ getDevice().executeShellV2Command("/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);
+ assertThat(getDevice().setProperty("persist.sys.mte.permissive", prevValue)).isTrue();
+ }
}
diff --git a/fastboot/constants.h b/fastboot/constants.h
index b732c76..f6fc74e 100644
--- a/fastboot/constants.h
+++ b/fastboot/constants.h
@@ -42,7 +42,7 @@
#define RESPONSE_DATA "DATA"
#define RESPONSE_INFO "INFO"
-#define FB_COMMAND_SZ 64
+#define FB_COMMAND_SZ 4096
#define FB_RESPONSE_SZ 256
#define FB_VAR_VERSION "version"
diff --git a/fastboot/device/commands.cpp b/fastboot/device/commands.cpp
index 823783e..3799d1f 100644
--- a/fastboot/device/commands.cpp
+++ b/fastboot/device/commands.cpp
@@ -40,6 +40,8 @@
#include <storage_literals/storage_literals.h>
#include <uuid/uuid.h>
+#include <bootloader_message/bootloader_message.h>
+
#include "BootControlClient.h"
#include "constants.h"
#include "fastboot_device.h"
@@ -154,6 +156,14 @@
return true;
}
+static void PostWipeData() {
+ std::string err;
+ // Reset mte state of device.
+ if (!WriteMiscMemtagMessage({}, &err)) {
+ LOG(ERROR) << "Failed to reset MTE state: " << err;
+ }
+}
+
const std::unordered_map<std::string, std::function<bool(FastbootDevice*)>> kSpecialVars = {
{"all", GetVarAll},
{"dmesg", GetDmesg},
@@ -232,6 +242,7 @@
//Perform oem PostWipeData if Android userdata partition has been erased
bool support_oem_postwipedata = false;
if (partition_name == "userdata") {
+ PostWipeData();
support_oem_postwipedata = OemPostWipeData(device);
}
@@ -610,6 +621,10 @@
if (ret < 0) {
return device->WriteStatus(FastbootResult::FAIL, strerror(-ret));
}
+ if (partition_name == "userdata") {
+ PostWipeData();
+ }
+
return device->WriteStatus(FastbootResult::OKAY, "Flashing succeeded");
}
diff --git a/fastboot/device/usb.cpp b/fastboot/device/usb.cpp
index b77d772..75a687f 100644
--- a/fastboot/device/usb.cpp
+++ b/fastboot/device/usb.cpp
@@ -161,6 +161,16 @@
if (num_bufs == 1 && aiob->events[0].res == -EINTR) {
continue;
}
+ if (read && aiob->events[0].res == -EPIPE) {
+ // On initial connection, some clients will send a ClearFeature(HALT) to
+ // attempt to resynchronize host and device after the fastboot server is killed.
+ // On newer device kernels, the reads we've already dispatched will be cancelled.
+ // Instead of treating this as a failure, which will tear down the interface and
+ // lead to the client doing the same thing again, just resubmit if this happens
+ // before we've actually read anything.
+ PLOG(ERROR) << "aio: got -EPIPE on first read attempt. Re-submitting read... ";
+ continue;
+ }
int ret = 0;
for (int i = 0; i < num_bufs; i++) {
if (aiob->events[i].res < 0) {
diff --git a/fastboot/fuzzy_fastboot/main.cpp b/fastboot/fuzzy_fastboot/main.cpp
index 074306b..e635937 100644
--- a/fastboot/fuzzy_fastboot/main.cpp
+++ b/fastboot/fuzzy_fastboot/main.cpp
@@ -901,19 +901,19 @@
<< "Device did not respond with failure after sending length " << s.size()
<< " string of random ASCII chars";
if (ret == IO_ERROR) EXPECT_EQ(transport->Reset(), 0) << "USB reset failed";
- std::string s1 = RandomString(1000, rand_legal);
+ std::string s1 = RandomString(10000, rand_legal);
ret = fb->RawCommand(s1);
EXPECT_TRUE(ret == DEVICE_FAIL || ret == IO_ERROR)
<< "Device did not respond with failure after sending length " << s1.size()
<< " string of random ASCII chars";
if (ret == IO_ERROR) EXPECT_EQ(transport->Reset(), 0) << "USB reset failed";
- std::string s2 = RandomString(1000, rand_illegal);
+ std::string s2 = RandomString(10000, rand_illegal);
ret = fb->RawCommand(s2);
EXPECT_TRUE(ret == DEVICE_FAIL || ret == IO_ERROR)
<< "Device did not respond with failure after sending length " << s2.size()
<< " string of random non-ASCII chars";
if (ret == IO_ERROR) EXPECT_EQ(transport->Reset(), 0) << "USB reset failed";
- std::string s3 = RandomString(1000, rand_char);
+ std::string s3 = RandomString(10000, rand_char);
ret = fb->RawCommand(s3);
EXPECT_TRUE(ret == DEVICE_FAIL || ret == IO_ERROR)
<< "Device did not respond with failure after sending length " << s3.size()
@@ -935,7 +935,7 @@
TEST_F(Fuzz, CommandTooLarge) {
for (const std::string& s : CMDS) {
- std::string rs = RandomString(1000, rand_char);
+ std::string rs = RandomString(10000, rand_char);
RetCode ret;
ret = fb->RawCommand(s + rs);
EXPECT_TRUE(ret == DEVICE_FAIL || ret == IO_ERROR)
diff --git a/fs_mgr/Android.bp b/fs_mgr/Android.bp
index 49761ac..45cd3c0 100644
--- a/fs_mgr/Android.bp
+++ b/fs_mgr/Android.bp
@@ -254,3 +254,39 @@
"clean_scratch_files",
],
}
+
+cc_binary {
+ name: "set-verity-state",
+ srcs: ["set-verity-state.cpp"],
+ shared_libs: [
+ "libbase",
+ "libbinder",
+ "libcrypto",
+ "libcrypto_utils",
+ "libfs_mgr_binder",
+ "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/fs_mgr/OWNERS b/fs_mgr/OWNERS
index c6f9054..6f1059b 100644
--- a/fs_mgr/OWNERS
+++ b/fs_mgr/OWNERS
@@ -2,3 +2,4 @@
bowgotsai@google.com
dvander@google.com
elsk@google.com
+yochiang@google.com
diff --git a/fs_mgr/fs_mgr.cpp b/fs_mgr/fs_mgr.cpp
index 1f54f5b..27137a2 100644
--- a/fs_mgr/fs_mgr.cpp
+++ b/fs_mgr/fs_mgr.cpp
@@ -22,6 +22,7 @@
#include <fcntl.h>
#include <inttypes.h>
#include <libgen.h>
+#include <selinux/selinux.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
@@ -30,6 +31,7 @@
#include <sys/stat.h>
#include <sys/swap.h>
#include <sys/types.h>
+#include <sys/utsname.h>
#include <sys/wait.h>
#include <time.h>
#include <unistd.h>
@@ -903,9 +905,9 @@
// 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;
}
@@ -933,9 +935,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;
@@ -2359,3 +2361,49 @@
return true;
}
+
+bool fs_mgr_filesystem_available(const std::string& filesystem) {
+ std::string filesystems;
+ if (!android::base::ReadFileToString("/proc/filesystems", &filesystems)) return false;
+ return filesystems.find("\t" + filesystem + "\n") != std::string::npos;
+}
+
+std::string fs_mgr_get_context(const std::string& mount_point) {
+ char* ctx = nullptr;
+ if (getfilecon(mount_point.c_str(), &ctx) == -1) {
+ PERROR << "getfilecon " << mount_point;
+ return "";
+ }
+
+ std::string context(ctx);
+ free(ctx);
+ return context;
+}
+
+OverlayfsValidResult fs_mgr_overlayfs_valid() {
+ // Overlayfs available in the kernel, and patched for override_creds?
+ if (access("/sys/module/overlay/parameters/override_creds", F_OK) == 0) {
+ return OverlayfsValidResult::kOverrideCredsRequired;
+ }
+ if (!fs_mgr_filesystem_available("overlay")) {
+ return OverlayfsValidResult::kNotSupported;
+ }
+ struct utsname uts;
+ if (uname(&uts) == -1) {
+ return OverlayfsValidResult::kNotSupported;
+ }
+ int major, minor;
+ if (sscanf(uts.release, "%d.%d", &major, &minor) != 2) {
+ return OverlayfsValidResult::kNotSupported;
+ }
+ if (major < 4) {
+ return OverlayfsValidResult::kOk;
+ }
+ if (major > 4) {
+ return OverlayfsValidResult::kNotSupported;
+ }
+ if (minor > 3) {
+ return OverlayfsValidResult::kNotSupported;
+ }
+ return OverlayfsValidResult::kOk;
+}
diff --git a/fs_mgr/fs_mgr_fstab.cpp b/fs_mgr/fs_mgr_fstab.cpp
index 43961da..76ef9e4 100644
--- a/fs_mgr/fs_mgr_fstab.cpp
+++ b/fs_mgr/fs_mgr_fstab.cpp
@@ -309,6 +309,12 @@
if (ReadFileToString("/sys/class/block/" + arg + "/queue/zoned", &zoned) &&
android::base::StartsWith(zoned, "host-managed")) {
entry->zoned_device = "/dev/block/" + arg;
+
+ // atgc in f2fs does not support a zoned device
+ auto options = Split(entry->fs_options, ",");
+ options.erase(std::remove(options.begin(), options.end(), "atgc"), options.end());
+ entry->fs_options = android::base::Join(options, ",");
+ LINFO << "Removed ATGC in fs_options as " << entry->fs_options;
} else {
LWARNING << "Warning: cannot find the zoned device: " << arg;
}
diff --git a/fs_mgr/fs_mgr_overlayfs.cpp b/fs_mgr/fs_mgr_overlayfs.cpp
index 354d02a..cc09d09 100644
--- a/fs_mgr/fs_mgr_overlayfs.cpp
+++ b/fs_mgr/fs_mgr_overlayfs.cpp
@@ -56,6 +56,7 @@
#include <storage_literals/storage_literals.h>
#include "fs_mgr_priv.h"
+#include "fs_mgr_priv_overlayfs.h"
#include "libfiemap/utility.h"
using namespace std::literals;
@@ -68,69 +69,12 @@
namespace {
bool fs_mgr_access(const std::string& path) {
- auto save_errno = errno;
- auto ret = access(path.c_str(), F_OK) == 0;
- errno = save_errno;
- return ret;
-}
-
-// determine if a filesystem is available
-bool fs_mgr_overlayfs_filesystem_available(const std::string& filesystem) {
- std::string filesystems;
- if (!android::base::ReadFileToString("/proc/filesystems", &filesystems)) return false;
- return filesystems.find("\t" + filesystem + "\n") != std::string::npos;
+ return access(path.c_str(), F_OK) == 0;
}
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 {};
-}
-
-bool fs_mgr_overlayfs_mount_all(Fstab*) {
- return false;
-}
-
-bool fs_mgr_overlayfs_setup(const char*, bool* change, bool) {
- if (change) *change = false;
- return false;
-}
-
-bool fs_mgr_overlayfs_teardown(const char*, bool* change) {
- if (change) *change = false;
- return false;
-}
-
-bool fs_mgr_overlayfs_is_setup() {
- return false;
-}
-
-namespace android {
-namespace fs_mgr {
-
-void MapScratchPartitionIfNeeded(Fstab*, const std::function<bool(const std::set<std::string>&)>&) {
-}
-
-void CleanupOldScratchFiles() {}
-
-void TeardownAllOverlayForMountPoint(const std::string&) {}
-
-} // namespace fs_mgr
-} // namespace android
-
-#else // ALLOW_ADBD_DISABLE_VERITY == 0
-
-namespace {
-
bool fs_mgr_in_recovery() {
// Check the existence of recovery binary instead of using the compile time
// __ANDROID_RECOVERY__ macro.
@@ -148,10 +92,7 @@
// is not well-defined. In this case, just return false as being in recovery
// implies not running a DSU system.
if (fs_mgr_in_recovery()) return false;
- auto saved_errno = errno;
- auto ret = android::gsi::IsGsiRunning();
- errno = saved_errno;
- return ret;
+ return android::gsi::IsGsiRunning();
}
// list of acceptable overlayfs backing storage
@@ -194,9 +135,8 @@
// If we have access issues to find out space remaining, return true
// to prevent us trying to override with overlayfs.
struct statvfs vst;
- auto save_errno = errno;
if (statvfs(mount_point.c_str(), &vst)) {
- errno = save_errno;
+ PLOG(ERROR) << "statvfs " << mount_point;
return true;
}
@@ -242,6 +182,28 @@
return true;
}
+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) ||
+ (fs.f_type != EXT4_SUPER_MAGIC)) {
+ return false;
+ }
+
+ android::base::unique_fd fd(open(dev.c_str(), O_RDONLY | O_CLOEXEC));
+ if (fd < 0) return false;
+
+ struct ext4_super_block sb;
+ if ((TEMP_FAILURE_RETRY(lseek64(fd, 1024, SEEK_SET)) < 0) ||
+ (TEMP_FAILURE_RETRY(read(fd, &sb, sizeof(sb))) < 0)) {
+ return false;
+ }
+
+ struct fs_info info;
+ if (ext4_parse_sb(&sb, &info) < 0) return false;
+
+ return (info.feat_ro_compat & EXT4_FEATURE_RO_COMPAT_SHARED_BLOCKS) != 0;
+}
+
bool fs_mgr_overlayfs_enabled(FstabEntry* entry) {
// readonly filesystem, can not be mount -o remount,rw
// for squashfs, erofs or if free space is (near) zero making such a remount
@@ -257,26 +219,21 @@
}
// check if ext4 de-dupe
- auto save_errno = errno;
auto has_shared_blocks = fs_mgr_has_shared_blocks(entry->mount_point, entry->blk_device);
if (!has_shared_blocks && (entry->mount_point == "/system")) {
has_shared_blocks = fs_mgr_has_shared_blocks("/", entry->blk_device);
}
- errno = save_errno;
return has_shared_blocks;
}
bool fs_mgr_rm_all(const std::string& path, bool* change = nullptr, int level = 0) {
- auto save_errno = errno;
std::unique_ptr<DIR, decltype(&closedir)> dir(opendir(path.c_str()), closedir);
if (!dir) {
if (errno == ENOENT) {
- errno = save_errno;
return true;
}
PERROR << "opendir " << path << " depth=" << level;
if ((errno == EPERM) && (level != 0)) {
- errno = save_errno;
return true;
}
return false;
@@ -288,9 +245,7 @@
auto file = path + "/" + entry->d_name;
if (entry->d_type == DT_UNKNOWN) {
struct stat st;
- save_errno = errno;
if (!lstat(file.c_str(), &st) && (st.st_mode & S_IFDIR)) entry->d_type = DT_DIR;
- errno = save_errno;
}
if (entry->d_type == DT_DIR) {
ret &= fs_mgr_rm_all(file, change, level + 1);
@@ -364,85 +319,102 @@
bool fs_mgr_rw_access(const std::string& path) {
if (path.empty()) return false;
- auto save_errno = errno;
- auto ret = access(path.c_str(), R_OK | W_OK) == 0;
- errno = save_errno;
- return ret;
+ return access(path.c_str(), R_OK | W_OK) == 0;
}
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() {
@@ -462,30 +434,33 @@
return false;
}
-void fs_mgr_overlayfs_umount_scratch() {
- // Lazy umount will allow us to move on and possibly later
- // establish a new fresh mount without requiring a reboot should
- // the developer wish to restart. Old references should melt
- // away or have no data. Main goal is to shut the door on the
- // current overrides with an expectation of a subsequent reboot,
- // thus any errors here are ignored.
- umount2(kScratchMountPoint.c_str(), MNT_DETACH);
- LINFO << "umount(" << kScratchMountPoint << ")";
- rmdir(kScratchMountPoint.c_str());
+// Returns true if immediate unmount succeeded and the scratch mount point was
+// removed.
+bool fs_mgr_overlayfs_umount_scratch() {
+ if (umount(kScratchMountPoint.c_str()) != 0) {
+ return false;
+ }
+ if (rmdir(kScratchMountPoint.c_str()) != 0 && errno != ENOENT) {
+ PLOG(ERROR) << "rmdir " << kScratchMountPoint;
+ }
+ return true;
}
-bool fs_mgr_overlayfs_teardown_scratch(const std::string& overlay, bool* change) {
+OverlayfsTeardownResult fs_mgr_overlayfs_teardown_scratch(const std::string& overlay,
+ bool* change) {
// umount and delete kScratchMountPoint storage if we have logical partitions
- if (overlay != kScratchMountPoint) return true;
+ if (overlay != kScratchMountPoint) {
+ return OverlayfsTeardownResult::Ok;
+ }
// Validation check.
if (fs_mgr_is_dsu_running()) {
LERROR << "Destroying DSU scratch is not allowed.";
- return false;
+ return OverlayfsTeardownResult::Error;
}
- auto save_errno = errno;
- if (fs_mgr_overlayfs_already_mounted(kScratchMountPoint, false)) {
+ bool was_mounted = fs_mgr_overlayfs_already_mounted(kScratchMountPoint, false);
+ if (was_mounted) {
fs_mgr_overlayfs_umount_scratch();
}
@@ -493,42 +468,55 @@
auto images = IImageManager::Open("remount", 10s);
if (images && images->BackingImageExists(partition_name)) {
-#if defined __ANDROID_RECOVERY__
if (!images->DisableImage(partition_name)) {
- return false;
+ return OverlayfsTeardownResult::Error;
}
-#else
- if (!images->UnmapImageIfExists(partition_name) ||
- !images->DeleteBackingImage(partition_name)) {
- return false;
+ if (was_mounted) {
+ // If overlayfs was mounted, don't bother trying to unmap since
+ // it'll fail and create error spam.
+ return OverlayfsTeardownResult::Busy;
}
-#endif
+ if (!images->UnmapImageIfExists(partition_name)) {
+ return OverlayfsTeardownResult::Busy;
+ }
+ if (!images->DeleteBackingImage(partition_name)) {
+ return OverlayfsTeardownResult::Busy;
+ }
+
+ // No need to check super partition, if we knew we had a scratch device
+ // in /data.
+ return OverlayfsTeardownResult::Ok;
}
auto slot_number = fs_mgr_overlayfs_slot_number();
auto super_device = fs_mgr_overlayfs_super_device(slot_number);
- if (!fs_mgr_rw_access(super_device)) return true;
+ if (!fs_mgr_rw_access(super_device)) {
+ return OverlayfsTeardownResult::Ok;
+ }
auto builder = MetadataBuilder::New(super_device, slot_number);
if (!builder) {
- errno = save_errno;
- return true;
+ return OverlayfsTeardownResult::Ok;
}
if (builder->FindPartition(partition_name) == nullptr) {
- errno = save_errno;
- return true;
+ return OverlayfsTeardownResult::Ok;
}
builder->RemovePartition(partition_name);
auto metadata = builder->Export();
if (metadata && UpdatePartitionTable(super_device, *metadata.get(), slot_number)) {
if (change) *change = true;
- if (!DestroyLogicalPartition(partition_name)) return false;
+ if (!DestroyLogicalPartition(partition_name)) {
+ return OverlayfsTeardownResult::Error;
+ }
} else {
LERROR << "delete partition " << overlay;
- return false;
+ return OverlayfsTeardownResult::Error;
}
- errno = save_errno;
- return true;
+
+ if (was_mounted) {
+ return OverlayfsTeardownResult::Busy;
+ }
+ return OverlayfsTeardownResult::Ok;
}
bool fs_mgr_overlayfs_teardown_one(const std::string& overlay, const std::string& mount_point,
@@ -546,27 +534,20 @@
const auto newpath = cleanup_all ? overlay + "/." + kOverlayTopDir.substr(1) + ".teardown"
: top + "/." + partition_name + ".teardown";
auto ret = fs_mgr_rm_all(newpath);
- auto save_errno = errno;
if (!rename(oldpath.c_str(), newpath.c_str())) {
if (change) *change = true;
} else if (errno != ENOENT) {
ret = false;
PERROR << "mv " << oldpath << " " << newpath;
- } else {
- errno = save_errno;
}
ret &= fs_mgr_rm_all(newpath, change);
- save_errno = errno;
if (!rmdir(newpath.c_str())) {
if (change) *change = true;
} else if (errno != ENOENT) {
ret = false;
PERROR << "rmdir " << newpath;
- } else {
- errno = save_errno;
}
if (!cleanup_all) {
- save_errno = errno;
if (!rmdir(top.c_str())) {
if (change) *change = true;
cleanup_all = true;
@@ -585,10 +566,8 @@
}
}
}
- errno = save_errno;
} else if (errno == ENOENT) {
cleanup_all = true;
- errno = save_errno;
} else {
ret = false;
PERROR << "rmdir " << top;
@@ -699,7 +678,6 @@
if (options.empty()) return false;
auto retval = true;
- auto save_errno = errno;
struct move_entry {
std::string mount_point;
@@ -729,21 +707,22 @@
}
// 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);
@@ -753,7 +732,6 @@
}
if (!fs_mgr_overlayfs_move_mount(new_entry.mount_point, new_entry.dir)) {
retval = false;
- save_errno = errno;
if (new_entry.shared_flag) {
fs_mgr_overlayfs_set_shared_mount(new_entry.mount_point, true);
}
@@ -777,7 +755,6 @@
options.c_str());
if (ret) {
retval = false;
- save_errno = errno;
PERROR << report << ret;
} else {
LINFO << report << ret;
@@ -791,11 +768,9 @@
if (!fs_mgr_overlayfs_move_mount(entry.dir, entry.mount_point)) {
retval = false;
- save_errno = errno;
} else if (entry.shared_flag &&
!fs_mgr_overlayfs_set_shared_mount(entry.mount_point, true)) {
retval = false;
- save_errno = errno;
}
rmdir(entry.dir.c_str());
}
@@ -806,50 +781,45 @@
fs_mgr_overlayfs_set_shared_mount(mount_point, true);
}
- errno = save_errno;
return retval;
}
// Mount kScratchMountPoint
-bool fs_mgr_overlayfs_mount_scratch(const std::string& device_path, const std::string mnt_type,
- bool readonly = false) {
+bool MountScratch(const std::string& device_path, 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;
+ std::vector<const char*> filesystem_candidates;
+ if (fs_mgr_is_f2fs(device_path)) {
+ filesystem_candidates = {"f2fs", "ext4"};
+ } else if (fs_mgr_is_ext4(device_path)) {
+ filesystem_candidates = {"ext4", "f2fs"};
+ } else {
+ 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;
entry.blk_device = device_path;
entry.mount_point = kScratchMountPoint;
- entry.fs_type = mnt_type;
- if ((mnt_type == "f2fs") && !f2fs) entry.fs_type = "ext4";
- if ((mnt_type == "ext4") && !ext4) entry.fs_type = "f2fs";
entry.flags = MS_NOATIME | MS_RDONLY;
- auto mounted = true;
if (!readonly) {
- if (entry.fs_type == "ext4") {
- // check if ext4 de-dupe
- entry.flags |= MS_RDONLY;
- auto save_errno = errno;
- mounted = fs_mgr_do_mount_one(entry) == 0;
- if (mounted) {
- mounted = !fs_mgr_has_shared_blocks(entry.mount_point, entry.blk_device);
- fs_mgr_overlayfs_umount_scratch();
- }
- errno = save_errno;
- }
entry.flags &= ~MS_RDONLY;
entry.flags |= MS_SYNCHRONOUS;
entry.fs_options = "nodiscard";
@@ -859,60 +829,27 @@
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) {
- entry.fs_type = "ext4";
- mounted = fs_mgr_do_mount_one(entry) == 0;
- } else if ((entry.fs_type == "ext4") && f2fs) {
- entry.fs_type = "f2fs";
- mounted = fs_mgr_do_mount_one(entry) == 0;
+ bool mounted = false;
+ for (auto fs_type : filesystem_candidates) {
+ entry.fs_type = fs_type;
+ if (fs_mgr_do_mount_one(entry) == 0) {
+ mounted = true;
+ break;
}
- 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");
const std::string kMkExt4("/system/bin/mke2fs");
-// Only a suggestion for _first_ try during mounting
-std::string fs_mgr_overlayfs_scratch_mount_type() {
- if (!access(kMkF2fs.c_str(), X_OK) && fs_mgr_overlayfs_filesystem_available("f2fs")) {
- return "f2fs";
- }
- if (!access(kMkExt4.c_str(), X_OK) && fs_mgr_overlayfs_filesystem_available("ext4")) {
- return "ext4";
- }
- return "auto";
-}
-
-// Note: we do not check access() here except for the super partition, since
-// in first-stage init we wouldn't have registed by-name symlinks for "other"
-// partitions that won't be mounted.
-static std::string GetPhysicalScratchDevice() {
- auto slot_number = fs_mgr_overlayfs_slot_number();
- auto super_device = fs_mgr_overlayfs_super_device(slot_number);
- auto path = fs_mgr_overlayfs_super_device(slot_number == 0);
- if (super_device != path) {
- return path;
- }
- if (fs_mgr_access(super_device)) {
- // Do not try to use system_other on a DAP device.
- return "";
- }
-
- auto other_slot = fs_mgr_get_other_slot_suffix();
- if (!other_slot.empty()) {
- return kPhysicalDevice + "system" + other_slot;
- }
- return "";
-}
-
// Note: The scratch partition of DSU is managed by gsid, and should be initialized during
// first-stage-mount. Just check if the DM device for DSU scratch partition is created or not.
static std::string GetDsuScratchDevice() {
@@ -949,28 +886,30 @@
return device;
}
- // There is no dynamic scratch, so try and find a physical one.
- return GetPhysicalScratchDevice();
+ return "";
}
-bool fs_mgr_overlayfs_make_scratch(const std::string& scratch_device, const std::string& mnt_type) {
+bool MakeScratchFilesystem(const std::string& scratch_device) {
// Force mkfs by design for overlay support of adb remount, simplify and
// thus do not rely on fsck to correct problems that could creep in.
+ auto fs_type = ""s;
auto command = ""s;
- if (mnt_type == "f2fs") {
+ if (!access(kMkF2fs.c_str(), X_OK) && fs_mgr_filesystem_available("f2fs")) {
+ fs_type = "f2fs";
command = kMkF2fs + " -w 4096 -f -d1 -l" + android::base::Basename(kScratchMountPoint);
- } else if (mnt_type == "ext4") {
+ } else if (!access(kMkExt4.c_str(), X_OK) && fs_mgr_filesystem_available("ext4")) {
+ fs_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";
+ LERROR << "No supported mkfs command or filesystem driver available, supported filesystems "
+ "are: f2fs, ext4";
return false;
}
command += " " + scratch_device + " >/dev/null 2>/dev/null </dev/null";
fs_mgr_set_blk_ro(scratch_device, false);
auto ret = system(command.c_str());
if (ret) {
- LERROR << "make " << mnt_type << " filesystem on " << scratch_device << " return=" << ret;
+ LERROR << "make " << fs_type << " filesystem on " << scratch_device << " return=" << ret;
return false;
}
return true;
@@ -995,8 +934,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();
@@ -1069,8 +1007,6 @@
LERROR << "add partition " << partition_name;
return false;
}
-
- if (change) *change = true;
}
if (changed || partition_create) {
@@ -1084,8 +1020,6 @@
if (!CreateLogicalPartition(params, scratch_device)) {
return false;
}
-
- if (change) *change = true;
} else if (scratch_device->empty()) {
*scratch_device = GetBootScratchDevice();
}
@@ -1115,9 +1049,8 @@
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) {
@@ -1130,8 +1063,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.
@@ -1158,7 +1089,7 @@
return true;
}
-static bool CanUseSuperPartition(const Fstab& fstab, bool* is_virtual_ab) {
+static bool CanUseSuperPartition(const Fstab& fstab) {
auto slot_number = fs_mgr_overlayfs_slot_number();
auto super_device = fs_mgr_overlayfs_super_device(slot_number);
if (!fs_mgr_rw_access(super_device) || !fs_mgr_overlayfs_has_logical(fstab)) {
@@ -1168,80 +1099,101 @@
if (!metadata) {
return false;
}
- *is_virtual_ab = !!(metadata->header.flags & LP_HEADER_FLAG_VIRTUAL_AB_DEVICE);
return true;
}
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;
}
- // Try a physical partition first.
- *scratch_device = GetPhysicalScratchDevice();
- if (!scratch_device->empty() && fs_mgr_rw_access(*scratch_device)) {
- *partition_exists = true;
- return true;
+ // Try ImageManager on /data first.
+ bool can_use_data = false;
+ if (FilesystemHasReliablePinning("/data", &can_use_data) && can_use_data) {
+ return CreateScratchOnData(scratch_device, partition_exists);
}
-
// If that fails, see if we can land on super.
- bool is_virtual_ab;
- 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 CreateDynamicScratch(scratch_device, partition_exists, change);
+ if (CanUseSuperPartition(fstab)) {
+ 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;
}
// If the partition exists, assume first that it can be mounted.
- 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 (MountScratch(scratch_device)) {
+ if (fs_mgr_access(kScratchMountPoint + kOverlayTopDir) ||
+ fs_mgr_filesystem_has_space(kScratchMountPoint)) {
return true;
}
+ // declare it useless, no overrides and no free space
+ if (!fs_mgr_overlayfs_umount_scratch()) {
+ LOG(ERROR) << "Unable to unmount scratch partition";
+ return false;
+ }
}
- // 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 (!MakeScratchFilesystem(scratch_device)) {
+ LOG(ERROR) << "Failed to format scratch partition";
+ return false;
+ }
- if (change) *change = true;
-
- return fs_mgr_overlayfs_mount_scratch(scratch_device, mnt_type);
+ return MountScratch(scratch_device);
}
-bool fs_mgr_overlayfs_invalid() {
- if (fs_mgr_overlayfs_valid() == OverlayfsValidResult::kNotSupported) return true;
+#if ALLOW_ADBD_DISABLE_VERITY
+constexpr bool kAllowOverlayfs = true;
+#else
+constexpr bool kAllowOverlayfs = false;
+#endif
+// NOTE: OverlayfsSetupAllowed() must be "stricter" than OverlayfsTeardownAllowed().
+// Setup is allowed only if teardown is also allowed.
+bool OverlayfsSetupAllowed(bool verbose = false) {
+ if (!kAllowOverlayfs) {
+ if (verbose) {
+ LOG(ERROR) << "Overlayfs remounts can only be used in debuggable builds";
+ }
+ return false;
+ }
+ // Check mandatory kernel patches.
+ if (fs_mgr_overlayfs_valid() == OverlayfsValidResult::kNotSupported) {
+ if (verbose) {
+ LOG(ERROR) << "Kernel does not support overlayfs";
+ }
+ return false;
+ }
// in recovery or fastbootd, not allowed!
- return fs_mgr_in_recovery();
+ if (fs_mgr_in_recovery()) {
+ if (verbose) {
+ LOG(ERROR) << "Unsupported overlayfs setup from recovery";
+ }
+ return false;
+ }
+ return true;
+}
+
+constexpr bool OverlayfsTeardownAllowed() {
+ // Never allow on non-debuggable build.
+ return kAllowOverlayfs;
}
} // namespace
@@ -1323,56 +1275,53 @@
if (!WaitForFile(scratch_device, 10s)) {
return;
}
- const auto mount_type = fs_mgr_overlayfs_scratch_mount_type();
- if (!fs_mgr_overlayfs_mount_scratch(scratch_device, mount_type, true /* readonly */)) {
+ if (!MountScratch(scratch_device, true /* readonly */)) {
return;
}
auto has_overlayfs_dir = fs_mgr_access(kScratchMountPoint + kOverlayTopDir);
fs_mgr_overlayfs_umount_scratch();
if (has_overlayfs_dir) {
- fs_mgr_overlayfs_mount_scratch(scratch_device, mount_type);
+ MountScratch(scratch_device);
}
}
bool fs_mgr_overlayfs_mount_all(Fstab* fstab) {
- auto ret = false;
- if (fs_mgr_overlayfs_invalid()) return ret;
-
+ if (!OverlayfsSetupAllowed()) {
+ return false;
+ }
+ auto ret = true;
auto scratch_can_be_mounted = true;
for (const auto& entry : fs_mgr_overlayfs_candidate_list(*fstab)) {
if (fs_mgr_is_verity_enabled(entry)) continue;
auto mount_point = fs_mgr_mount_point(entry.mount_point);
if (fs_mgr_overlayfs_already_mounted(mount_point)) {
- ret = true;
continue;
}
if (scratch_can_be_mounted) {
scratch_can_be_mounted = false;
TryMountScratch();
}
- if (fs_mgr_overlayfs_mount(mount_point)) ret = true;
+ ret &= fs_mgr_overlayfs_mount(mount_point);
}
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* 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 (!OverlayfsSetupAllowed(/*verbose=*/true)) {
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 &&
@@ -1380,9 +1329,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;
@@ -1390,12 +1338,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 (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;
@@ -1405,17 +1361,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 {
@@ -1517,59 +1477,72 @@
return true;
}
-// Returns false if teardown not permitted, errno set to last error.
-// If something is altered, set *change.
-bool fs_mgr_overlayfs_teardown(const char* mount_point, bool* change) {
- if (change) *change = false;
- auto ret = true;
+static OverlayfsTeardownResult TeardownMountsAndScratch(const char* mount_point,
+ bool* want_reboot) {
+ bool should_destroy_scratch = false;
+ auto rv = OverlayfsTeardownResult::Ok;
+ for (const auto& overlay_mount_point : OverlayMountPoints()) {
+ auto ok = fs_mgr_overlayfs_teardown_one(
+ overlay_mount_point, mount_point ? fs_mgr_mount_point(mount_point) : "",
+ want_reboot,
+ overlay_mount_point == kScratchMountPoint ? &should_destroy_scratch : nullptr);
+ if (!ok) {
+ rv = OverlayfsTeardownResult::Error;
+ }
+ }
+ // Do not attempt to destroy DSU scratch if within a DSU system,
+ // because DSU scratch partition is managed by gsid.
+ if (should_destroy_scratch && !fs_mgr_is_dsu_running()) {
+ auto rv = fs_mgr_overlayfs_teardown_scratch(kScratchMountPoint, want_reboot);
+ if (rv != OverlayfsTeardownResult::Ok) {
+ return rv;
+ }
+ }
+ // And now that we did what we could, lets inform
+ // caller that there may still be more to do.
+ if (!fs_mgr_boot_completed()) {
+ LOG(ERROR) << "Cannot teardown overlayfs before persistent properties are ready";
+ return OverlayfsTeardownResult::Error;
+ }
+ return rv;
+}
+
+// Returns false if teardown not permitted. If something is altered, set *want_reboot.
+OverlayfsTeardownResult fs_mgr_overlayfs_teardown(const char* mount_point, bool* want_reboot) {
+ if (!OverlayfsTeardownAllowed()) {
+ // Nothing to teardown.
+ return OverlayfsTeardownResult::Ok;
+ }
// If scratch exists, but is not mounted, lets gain access to clean
// specific override entries.
auto mount_scratch = false;
if ((mount_point != nullptr) && !fs_mgr_overlayfs_already_mounted(kScratchMountPoint, false)) {
std::string scratch_device = GetBootScratchDevice();
if (!scratch_device.empty()) {
- mount_scratch = fs_mgr_overlayfs_mount_scratch(scratch_device,
- fs_mgr_overlayfs_scratch_mount_type());
+ mount_scratch = MountScratch(scratch_device);
}
}
- bool should_destroy_scratch = false;
- for (const auto& overlay_mount_point : OverlayMountPoints()) {
- ret &= fs_mgr_overlayfs_teardown_one(
- overlay_mount_point, mount_point ? fs_mgr_mount_point(mount_point) : "", change,
- overlay_mount_point == kScratchMountPoint ? &should_destroy_scratch : nullptr);
- }
- // Do not attempt to destroy DSU scratch if within a DSU system,
- // because DSU scratch partition is managed by gsid.
- if (should_destroy_scratch && !fs_mgr_is_dsu_running()) {
- ret &= fs_mgr_overlayfs_teardown_scratch(kScratchMountPoint, change);
- }
- if (fs_mgr_overlayfs_valid() == OverlayfsValidResult::kNotSupported) {
- // After obligatory teardown to make sure everything is clean, but if
- // we didn't want overlayfs in the first place, we do not want to
- // waste time on a reboot (or reboot request message).
- if (change) *change = false;
- }
- // And now that we did what we could, lets inform
- // caller that there may still be more to do.
- if (!fs_mgr_boot_completed()) {
- errno = EBUSY;
- PERROR << "teardown";
- ret = false;
- }
+
+ auto rv = TeardownMountsAndScratch(mount_point, want_reboot);
+
if (mount_scratch) {
- fs_mgr_overlayfs_umount_scratch();
+ if (!fs_mgr_overlayfs_umount_scratch()) {
+ return OverlayfsTeardownResult::Busy;
+ }
}
- return ret;
+ return rv;
}
bool fs_mgr_overlayfs_is_setup() {
+ if (!OverlayfsSetupAllowed()) {
+ return false;
+ }
if (fs_mgr_overlayfs_already_mounted(kScratchMountPoint, false)) return true;
Fstab fstab;
if (!ReadDefaultFstab(&fstab)) {
return false;
}
- if (fs_mgr_overlayfs_invalid()) return false;
for (const auto& entry : fs_mgr_overlayfs_candidate_list(fstab)) {
if (fs_mgr_is_verity_enabled(entry)) continue;
if (fs_mgr_overlayfs_already_mounted(fs_mgr_mount_point(entry.mount_point))) return true;
@@ -1582,7 +1555,7 @@
void MapScratchPartitionIfNeeded(Fstab* fstab,
const std::function<bool(const std::set<std::string>&)>& init) {
- if (fs_mgr_overlayfs_invalid()) {
+ if (!OverlayfsSetupAllowed()) {
return;
}
if (GetEntryForMountPoint(fstab, kScratchMountPoint) != nullptr) {
@@ -1619,6 +1592,9 @@
}
void CleanupOldScratchFiles() {
+ if (!OverlayfsTeardownAllowed()) {
+ return;
+ }
if (!ScratchIsOnData()) {
return;
}
@@ -1628,6 +1604,9 @@
}
void TeardownAllOverlayForMountPoint(const std::string& mount_point) {
+ if (!OverlayfsTeardownAllowed()) {
+ return;
+ }
if (!fs_mgr_in_recovery()) {
LERROR << __FUNCTION__ << "(): must be called within recovery.";
return;
@@ -1658,10 +1637,11 @@
}
}
+ // Note if we just disabled scratch, this mount will fail.
if (auto info = EnsureScratchMapped(); info.has_value()) {
// Map scratch device, mount kScratchMountPoint and teardown kScratchMountPoint.
fs_mgr_overlayfs_umount_scratch();
- if (fs_mgr_overlayfs_mount_scratch(info->device, fs_mgr_overlayfs_scratch_mount_type())) {
+ if (MountScratch(info->device)) {
bool should_destroy_scratch = false;
fs_mgr_overlayfs_teardown_one(kScratchMountPoint, teardown_dir, ignore_change,
&should_destroy_scratch);
@@ -1676,7 +1656,7 @@
std::string scratch_device;
if (MapDsuScratchDevice(&scratch_device)) {
fs_mgr_overlayfs_umount_scratch();
- if (fs_mgr_overlayfs_mount_scratch(scratch_device, fs_mgr_overlayfs_scratch_mount_type())) {
+ if (MountScratch(scratch_device)) {
fs_mgr_overlayfs_teardown_one(kScratchMountPoint, teardown_dir, ignore_change);
fs_mgr_overlayfs_umount_scratch();
}
@@ -1687,15 +1667,11 @@
} // namespace fs_mgr
} // namespace android
-#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;
@@ -1710,64 +1686,3 @@
}
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) ||
- (fs.f_type != EXT4_SUPER_MAGIC)) {
- return false;
- }
-
- android::base::unique_fd fd(open(dev.c_str(), O_RDONLY | O_CLOEXEC));
- if (fd < 0) return false;
-
- struct ext4_super_block sb;
- if ((TEMP_FAILURE_RETRY(lseek64(fd, 1024, SEEK_SET)) < 0) ||
- (TEMP_FAILURE_RETRY(read(fd, &sb, sizeof(sb))) < 0)) {
- return false;
- }
-
- struct fs_info info;
- if (ext4_parse_sb(&sb, &info) < 0) return false;
-
- return (info.feat_ro_compat & EXT4_FEATURE_RO_COMPAT_SHARED_BLOCKS) != 0;
-}
-
-std::string fs_mgr_get_context(const std::string& mount_point) {
- char* ctx = nullptr;
- if (getfilecon(mount_point.c_str(), &ctx) == -1) {
- return "";
- }
-
- std::string context(ctx);
- free(ctx);
- return context;
-}
-
-OverlayfsValidResult fs_mgr_overlayfs_valid() {
- // Overlayfs available in the kernel, and patched for override_creds?
- if (fs_mgr_access("/sys/module/overlay/parameters/override_creds")) {
- return OverlayfsValidResult::kOverrideCredsRequired;
- }
- if (!fs_mgr_overlayfs_filesystem_available("overlay")) {
- return OverlayfsValidResult::kNotSupported;
- }
- struct utsname uts;
- if (uname(&uts) == -1) {
- return OverlayfsValidResult::kNotSupported;
- }
- int major, minor;
- if (sscanf(uts.release, "%d.%d", &major, &minor) != 2) {
- return OverlayfsValidResult::kNotSupported;
- }
- if (major < 4) {
- return OverlayfsValidResult::kOk;
- }
- if (major > 4) {
- return OverlayfsValidResult::kNotSupported;
- }
- if (minor > 3) {
- return OverlayfsValidResult::kNotSupported;
- }
- return OverlayfsValidResult::kOk;
-}
diff --git a/fs_mgr/fs_mgr_priv.h b/fs_mgr/fs_mgr_priv.h
index c5e477c..46f54cc 100644
--- a/fs_mgr/fs_mgr_priv.h
+++ b/fs_mgr/fs_mgr_priv.h
@@ -99,6 +99,16 @@
bool fs_mgr_teardown_verity(android::fs_mgr::FstabEntry* fstab);
+bool fs_mgr_filesystem_available(const std::string& filesystem);
+std::string fs_mgr_get_context(const std::string& mount_point);
+
+enum class OverlayfsValidResult {
+ kNotSupported = 0,
+ kOk,
+ kOverrideCredsRequired,
+};
+OverlayfsValidResult fs_mgr_overlayfs_valid();
+
namespace android {
namespace fs_mgr {
bool UnmapDevice(const std::string& name);
diff --git a/fs_mgr/fs_mgr_priv_overlayfs.h b/fs_mgr/fs_mgr_priv_overlayfs.h
new file mode 100644
index 0000000..45b954d
--- /dev/null
+++ b/fs_mgr/fs_mgr_priv_overlayfs.h
@@ -0,0 +1,49 @@
+/*
+ * 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 <fstab/fstab.h>
+
+bool fs_mgr_overlayfs_already_mounted(const std::string& mount_point, bool overlay_only = true);
+bool fs_mgr_wants_overlayfs(android::fs_mgr::FstabEntry* entry);
+android::fs_mgr::Fstab fs_mgr_overlayfs_candidate_list(const android::fs_mgr::Fstab& fstab);
+
+// 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 OverlayfsTeardownResult {
+ Ok,
+ Busy, // Indicates that overlays are still in use.
+ Error
+};
+OverlayfsTeardownResult fs_mgr_overlayfs_teardown(const char* mount_point = nullptr,
+ bool* want_reboot = nullptr);
+
+namespace android {
+namespace fs_mgr {
+
+void CleanupOldScratchFiles();
+
+} // namespace fs_mgr
+} // namespace android
diff --git a/fs_mgr/fs_mgr_remount.cpp b/fs_mgr/fs_mgr_remount.cpp
index 4a927d0..54c1c2c 100644
--- a/fs_mgr/fs_mgr_remount.cpp
+++ b/fs_mgr/fs_mgr_remount.cpp
@@ -42,6 +42,8 @@
#include <libavb_user/libavb_user.h>
#include <libgsi/libgsid.h>
+#include "fs_mgr_priv_overlayfs.h"
+
using namespace std::literals;
using android::fs_mgr::Fstab;
using android::fs_mgr::FstabEntry;
@@ -317,15 +319,15 @@
}
if (fs_mgr_wants_overlayfs(&entry)) {
- bool change = false;
+ bool want_reboot = false;
bool force = result->disabled_verity;
- if (!fs_mgr_overlayfs_setup(mount_point.c_str(), &change, force)) {
+ 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 (change) {
+ if (want_reboot) {
LOG(INFO) << "Using overlayfs for " << mount_point;
result->reboot_later = true;
result->setup_overlayfs = true;
@@ -451,10 +453,9 @@
}
// Mount overlayfs.
- errno = 0;
- if (!fs_mgr_overlayfs_mount_all(&partitions) && errno) {
- PLOG(ERROR) << "Can not mount overlayfs for partitions";
- return BAD_OVERLAY;
+ if (!fs_mgr_overlayfs_mount_all(&partitions)) {
+ LOG(WARNING) << "Cannot mount overlayfs for some partitions";
+ // Continue regardless to handle raw remount case.
}
// Get actual mounts _after_ overlayfs has been added.
diff --git a/fs_mgr/fs_mgr_vendor_overlay.cpp b/fs_mgr/fs_mgr_vendor_overlay.cpp
index 1372511..6b32b4d 100644
--- a/fs_mgr/fs_mgr_vendor_overlay.cpp
+++ b/fs_mgr/fs_mgr_vendor_overlay.cpp
@@ -25,7 +25,6 @@
#include <android-base/logging.h>
#include <android-base/properties.h>
-#include <fs_mgr_overlayfs.h>
#include <fs_mgr_vendor_overlay.h>
#include <fstab/fstab.h>
diff --git a/fs_mgr/include/fs_mgr_overlayfs.h b/fs_mgr/include/fs_mgr_overlayfs.h
index ec1d78f..bdaabbf 100644
--- a/fs_mgr/include/fs_mgr_overlayfs.h
+++ b/fs_mgr/include/fs_mgr_overlayfs.h
@@ -17,38 +17,21 @@
#pragma once
#include <functional>
+#include <set>
+#include <string>
#include <fstab/fstab.h>
-#include <set>
-#include <string>
-#include <vector>
+// Keep the list short and only add interfaces that must be exported public.
-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* 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);
-
-enum class OverlayfsValidResult {
- kNotSupported = 0,
- kOk,
- kOverrideCredsRequired,
-};
-OverlayfsValidResult fs_mgr_overlayfs_valid();
namespace android {
namespace fs_mgr {
void MapScratchPartitionIfNeeded(Fstab* fstab,
const std::function<bool(const std::set<std::string>&)>& init);
-void CleanupOldScratchFiles();
// Teardown overlays of all sources (cache dir, scratch device, DSU) for |mount_point|.
// Teardown all overlays if |mount_point| is empty.
diff --git a/fs_mgr/libfiemap/binder.cpp b/fs_mgr/libfiemap/binder.cpp
index 003e6ed..41e534a 100644
--- a/fs_mgr/libfiemap/binder.cpp
+++ b/fs_mgr/libfiemap/binder.cpp
@@ -195,9 +195,14 @@
return true;
}
-bool ImageManagerBinder::DisableImage(const std::string&) {
- LOG(ERROR) << __PRETTY_FUNCTION__ << " is not available over binder";
- return false;
+bool ImageManagerBinder::DisableImage(const std::string& name) {
+ auto status = manager_->disableImage(name);
+ if (!status.isOk()) {
+ LOG(ERROR) << __PRETTY_FUNCTION__
+ << " binder returned: " << status.exceptionMessage().string();
+ return false;
+ }
+ return true;
}
bool ImageManagerBinder::RemoveDisabledImages() {
diff --git a/fs_mgr/libfiemap/include/libfiemap/image_manager.h b/fs_mgr/libfiemap/include/libfiemap/image_manager.h
index 00dd661..0619c96 100644
--- a/fs_mgr/libfiemap/include/libfiemap/image_manager.h
+++ b/fs_mgr/libfiemap/include/libfiemap/image_manager.h
@@ -112,9 +112,6 @@
// Mark an image as disabled. This is useful for marking an image as
// will-be-deleted in recovery, since recovery cannot mount /data.
- //
- // This is not available in binder, since it is intended for recovery.
- // When binder is available, images can simply be removed.
virtual bool DisableImage(const std::string& name) = 0;
// Remove all images that been marked as disabled.
diff --git a/fs_mgr/libsnapshot/snapshot.cpp b/fs_mgr/libsnapshot/snapshot.cpp
index bc3efd9..59abd6f 100644
--- a/fs_mgr/libsnapshot/snapshot.cpp
+++ b/fs_mgr/libsnapshot/snapshot.cpp
@@ -1498,7 +1498,6 @@
if (UpdateUsesUserSnapshots(lock) && !device()->IsTestDevice()) {
if (snapuserd_client_) {
snapuserd_client_->DetachSnapuserd();
- snapuserd_client_->CloseConnection();
snapuserd_client_ = nullptr;
}
}
@@ -2794,7 +2793,6 @@
if (snapuserd_client_) {
LOG(INFO) << "Shutdown snapuserd daemon";
snapuserd_client_->DetachSnapuserd();
- snapuserd_client_->CloseConnection();
snapuserd_client_ = nullptr;
}
@@ -3317,7 +3315,7 @@
}
if (snapuserd_client) {
snapuserd_client->DetachSnapuserd();
- snapuserd_client->CloseConnection();
+ snapuserd_client = nullptr;
}
}
diff --git a/fs_mgr/libsnapshot/snapshot_test.cpp b/fs_mgr/libsnapshot/snapshot_test.cpp
index 2233a38..2c01cf6 100644
--- a/fs_mgr/libsnapshot/snapshot_test.cpp
+++ b/fs_mgr/libsnapshot/snapshot_test.cpp
@@ -107,6 +107,12 @@
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();
SetupProperties();
@@ -152,10 +158,14 @@
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() {
@@ -487,6 +497,7 @@
android::fiemap::IImageManager* image_manager_ = nullptr;
std::string fake_super_;
bool snapuserd_required_ = false;
+ std::string test_name_;
};
TEST_F(SnapshotTest, CreateSnapshot) {
@@ -1003,6 +1014,8 @@
void TearDown() override {
RETURN_IF_NON_VIRTUAL_AB();
+ LOG(INFO) << "Tearing down SnapshotUpdateTest test: " << test_name_;
+
Cleanup();
SnapshotTest::TearDown();
}
@@ -2797,7 +2810,6 @@
return;
}
snapuserd_client->DetachSnapuserd();
- snapuserd_client->CloseConnection();
}
} // namespace snapshot
diff --git a/fs_mgr/libsnapshot/snapuserd/include/snapuserd/snapuserd_client.h b/fs_mgr/libsnapshot/snapuserd/include/snapuserd/snapuserd_client.h
index 9a69d58..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.
diff --git a/fs_mgr/libsnapshot/snapuserd/snapuserd.rc b/fs_mgr/libsnapshot/snapuserd/snapuserd.rc
index 2750096..522fe08 100644
--- a/fs_mgr/libsnapshot/snapuserd/snapuserd.rc
+++ b/fs_mgr/libsnapshot/snapuserd/snapuserd.rc
@@ -4,6 +4,7 @@
disabled
user root
group root system
+ task_profiles OtaProfiles
seclabel u:r:snapuserd:s0
service snapuserd_proxy /system/bin/snapuserd -socket-handoff
diff --git a/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_dm_user.cpp b/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_dm_user.cpp
index 1e300d2..0d0f711 100644
--- a/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_dm_user.cpp
+++ b/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_dm_user.cpp
@@ -253,6 +253,11 @@
bool Worker::RunThread() {
SNAP_LOG(INFO) << "Processing snapshot I/O requests....";
+
+ if (setpriority(PRIO_PROCESS, gettid(), kNiceValueForMergeThreads)) {
+ SNAP_PLOG(ERROR) << "Failed to set priority for TID: " << gettid();
+ }
+
// Start serving IO
while (true) {
if (!ProcessIORequest()) {
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 1bf33c8..d437d32 100644
--- a/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_server.cpp
+++ b/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_server.cpp
@@ -462,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;
@@ -650,6 +649,12 @@
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();
diff --git a/fs_mgr/libsnapshot/utility.h b/fs_mgr/libsnapshot/utility.h
index 16aa81a..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:
diff --git a/fs_mgr/libsnapshot/vts_ota_config_test.cpp b/fs_mgr/libsnapshot/vts_ota_config_test.cpp
old mode 100644
new mode 100755
index 02bcc34..d387eb3
--- a/fs_mgr/libsnapshot/vts_ota_config_test.cpp
+++ b/fs_mgr/libsnapshot/vts_ota_config_test.cpp
@@ -22,6 +22,9 @@
}
TEST(VAB, Enabled) {
+ if (!android::base::GetBoolProperty("ro.build.ab_update", false) && (GetVsrLevel() < __ANDROID_API_T__)) {
+ GTEST_SKIP();
+ }
ASSERT_TRUE(android::base::GetBoolProperty("ro.virtual_ab.enabled", false));
if (GetVsrLevel() < __ANDROID_API_T__) {
GTEST_SKIP();
diff --git a/fs_mgr/set-verity-state.cpp b/fs_mgr/set-verity-state.cpp
new file mode 100644
index 0000000..84ee01f
--- /dev/null
+++ b/fs_mgr/set-verity-state.cpp
@@ -0,0 +1,258 @@
+/*
+ * Copyright (C) 2019 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 <getopt.h>
+#include <stdio.h>
+
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <android-base/properties.h>
+#include <binder/ProcessState.h>
+#include <cutils/android_reboot.h>
+#include <fs_mgr_overlayfs.h>
+#include <libavb_user/libavb_user.h>
+
+#include "fs_mgr_priv_overlayfs.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
+const bool kAllowDisableVerity = true;
+#else
+const bool kAllowDisableVerity = false;
+#endif
+
+static bool SetupOrTeardownOverlayfs(bool enable) {
+ bool want_reboot = false;
+ if (enable) {
+ if (!fs_mgr_overlayfs_setup(nullptr, &want_reboot)) {
+ LOG(ERROR) << "Overlayfs setup failed.";
+ return want_reboot;
+ }
+ if (want_reboot) {
+ printf("enabling overlayfs\n");
+ }
+ } else {
+ auto rv = fs_mgr_overlayfs_teardown(nullptr, &want_reboot);
+ if (rv == OverlayfsTeardownResult::Error) {
+ LOG(ERROR) << "Overlayfs teardown failed.";
+ return want_reboot;
+ }
+ if (rv == OverlayfsTeardownResult::Busy) {
+ LOG(ERROR) << "Overlayfs is still active until reboot.";
+ return true;
+ }
+ if (want_reboot) {
+ printf("disabling overlayfs\n");
+ }
+ }
+ return want_reboot;
+}
+
+/* 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.
+ */
+std::string get_ab_suffix() {
+ return android::base::GetProperty("ro.boot.slot_suffix", "");
+}
+
+bool is_avb_device_locked() {
+ return android::base::GetProperty("ro.boot.vbmeta.device_state", "") == "locked";
+}
+
+bool is_debuggable() {
+ return android::base::GetBoolProperty("ro.debuggable", 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).
+ return !android::base::GetProperty("ro.boot.vbmeta.digest", "").empty();
+}
+
+[[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);
+}
+
+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;
+ }
+ }
+
+ 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 (!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 |= SetupOrTeardownOverlayfs(!enable_verity);
+ }
+
+ if (want_reboot) {
+ if (auto_reboot) {
+ reboot(progname);
+ }
+ printf("Reboot the device for new settings to take effect\n");
+ }
+
+ return exit_code;
+}
diff --git a/fs_mgr/tests/Android.bp b/fs_mgr/tests/Android.bp
index fdc0d8e..b9bae25 100644
--- a/fs_mgr/tests/Android.bp
+++ b/fs_mgr/tests/Android.bp
@@ -53,8 +53,9 @@
}
sh_binary_host {
- name: "adb-remount-test.sh",
+ name: "adb-remount-test",
src: "adb-remount-test.sh",
+ filename_from_src: true,
target: {
darwin: {
enabled: false,
@@ -68,7 +69,7 @@
sh_test {
name: "adb-remount-sh",
src: "adb-remount-test.sh",
- filename: "adb-remount-test.sh",
+ filename_from_src: true,
test_suites: ["general-tests"],
test_config: "adb-remount-sh.xml",
}
diff --git a/fs_mgr/tests/adb-remount-test.sh b/fs_mgr/tests/adb-remount-test.sh
index 91024d1..eba4f6e 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,408 +991,393 @@
;;
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`
+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 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"
+
+scratch_on_super=false
if ${overlayfs_needed}; then
- if [ ${ret} != 0 ]; then
- die -t ${T} "overlay takeover failed"
+ is_overlayfs_mounted /system ||
+ die -t "${T}" "expected overlay to takeover /system after remount"
+
+ # Collect information about the scratch device if we have one
+ M=$(adb_sh cat /proc/mounts </dev/null |
+ awk '$2 == "/mnt/scratch" { print $1, $3; exit }')
+ if [ -n "${M}" ]; then
+ 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})"
+
+ # Detect scratch partition backed by super?
+ for b in "/dev/block/by-name/super"{,_${ACTIVE_SLOT}}; do
+ if adb_test -e "${b}"; then
+ device=$(adb_su realpath "${b}")
+ D=$(adb_su stat -c '0x%t 0x%T' "${device}")
+ major=$(echo "${D}" | awk '{ print $1 }')
+ minor=$(echo "${D}" | awk '{ print $2 }')
+ super_devt=$(( major )):$(( minor ))
+ if adb_su dmctl table scratch | tail +2 | grep -q -w "${super_devt}"; then
+ scratch_on_super=true
+ fi
+ break
+ fi
+ done
+
+ if ${scratch_on_super}; then
+ LOG INFO "using dynamic scratch partition on super"
+ else
+ LOG INFO "using dynamic scratch partition on /data (VAB device)"
+ fi
+ LOG INFO "scratch device ${scratch_device} filesystem ${scratch_filesystem} size ${scratch_size}KiB"
+ else
+ LOG INFO "cannot find any scratch device mounted on /mnt/scratch, using scratch on /cache"
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
+ LOG INFO "/${d}/overlay is setup"
fi
done
- 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,') ||
+ data_device=$(adb_sh awk '$2 == "/data" { print $1; exit }' /proc/mounts)
+ # 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 |
- 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
+
+ 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 "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
+ 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
- [ -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)"
else
- if [ ${ret} = 0 ]; then
- die -t ${T} "unexpected overlay takeover"
- fi
+ is_overlayfs_mounted && die -t "${T}" "unexpected overlay takeover"
fi
-# Check something.
+LOG OK "adb remount RW"
-echo "${GREEN}[ RUN ]${NORMAL} push content to ${MOUNTS}" >&2
+################################################################################
+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_INO=`adb_sh stat --format=%i /system/hello </dev/null`
VENDOR_INO=`adb_sh stat --format=%i /vendor/hello </dev/null`
check_ne "${SYSTEM_INO}" "${VENDOR_INO}" vendor and system inode
-# 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
@@ -1373,25 +1385,23 @@
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_INO}" "`adb_sh stat --format=%i /system/hello </dev/null`" system inode after reboot
@@ -1400,200 +1410,154 @@
# 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
- 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"
- 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"
- 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
- fastboot format ${scratch_partition} &&
- ( fastboot reboot || true) &&
- die "fastboot can format ${scratch_partition}"
+
+ if ${scratch_on_super}; then
+ fastboot_getvar partition-type:scratch raw ||
+ die "fastboot cannot see parameter partition-type:scratch"
+ fastboot_getvar has-slot:scratch no ||
+ die "fastboot cannot see parameter has-slot:scratch"
+ fastboot_getvar is-logical:scratch yes ||
+ die "fastboot cannot see parameter is-logical:scratch"
+ LOG INFO "expect fastboot erase scratch to fail"
+ fastboot erase scratch && die "fastboot can erase scratch"
+ LOG INFO "expect fastboot format scratch to fail"
+ fastboot format scratch && die "fastboot can format scratch"
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_INO}" "`adb_sh stat --format=%i /system/hello </dev/null`" system inode 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
+################################################################################
+if ${is_bootloader_fastboot} && ${scratch_on_super}; then
- echo "${GREEN}[ RUN ]${NORMAL} test fastboot flash to ${scratch_partition} recovery" >&2
+ LOG RUN "test fastboot flash to scratch 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}
+ fastboot flash --force scratch ${img}
err=${?}
- cleanup
- cleanup() {
- true
- }
fastboot reboot ||
die "can not reboot out of fastboot"
[ 0 -eq ${err} ] ||
- die "fastboot flash ${scratch_partition}"
+ die "fastboot flash scratch"
adb_wait ${ADB_WAIT} &&
adb_root ||
- die "did not reboot after flashing empty ${scratch_partition} `usb_status`"
+ die "did not reboot after flashing empty scratch $(usb_status)"
T=`adb_date`
D=`adb disable-verity 2>&1`
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`
@@ -1602,135 +1566,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 "recreated scratch" ||
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/gatekeeperd/Android.bp b/gatekeeperd/Android.bp
index 0aedc58..838f734 100644
--- a/gatekeeperd/Android.bp
+++ b/gatekeeperd/Android.bp
@@ -43,6 +43,8 @@
"libutils",
"libcrypto",
"libhidlbase",
+ "lib_android_keymaster_keymint_utils",
+ "android.hardware.gatekeeper-V1-ndk",
"android.hardware.gatekeeper@1.0",
"libgatekeeper_aidl",
"android.security.authorization-ndk",
diff --git a/gatekeeperd/gatekeeperd.cpp b/gatekeeperd/gatekeeperd.cpp
index 8792c83..76fcd55 100644
--- a/gatekeeperd/gatekeeperd.cpp
+++ b/gatekeeperd/gatekeeperd.cpp
@@ -25,6 +25,7 @@
#include <unistd.h>
#include <memory>
+#include <KeyMintUtils.h>
#include <android-base/logging.h>
#include <android-base/properties.h>
#include <android/binder_ibinder.h>
@@ -38,6 +39,7 @@
#include <log/log.h>
#include <utils/String16.h>
+#include <aidl/android/hardware/gatekeeper/IGatekeeper.h>
#include <aidl/android/hardware/security/keymint/HardwareAuthToken.h>
#include <aidl/android/security/authorization/IKeystoreAuthorization.h>
#include <android/hardware/gatekeeper/1.0/IGatekeeper.h>
@@ -49,27 +51,35 @@
using android::hardware::gatekeeper::V1_0::GatekeeperStatusCode;
using android::hardware::gatekeeper::V1_0::IGatekeeper;
+using AidlGatekeeperEnrollResp = aidl::android::hardware::gatekeeper::GatekeeperEnrollResponse;
+using AidlGatekeeperVerifyResp = aidl::android::hardware::gatekeeper::GatekeeperVerifyResponse;
+using AidlIGatekeeper = aidl::android::hardware::gatekeeper::IGatekeeper;
+
using ::android::binder::Status;
using ::android::service::gatekeeper::BnGateKeeperService;
using GKResponse = ::android::service::gatekeeper::GateKeeperResponse;
using GKResponseCode = ::android::service::gatekeeper::ResponseCode;
using ::aidl::android::hardware::security::keymint::HardwareAuthenticatorType;
using ::aidl::android::hardware::security::keymint::HardwareAuthToken;
+using ::aidl::android::hardware::security::keymint::km_utils::authToken2AidlVec;
using ::aidl::android::security::authorization::IKeystoreAuthorization;
namespace android {
static const String16 KEYGUARD_PERMISSION("android.permission.ACCESS_KEYGUARD_SECURE_STORAGE");
static const String16 DUMP_PERMISSION("android.permission.DUMP");
+constexpr const char gatekeeperServiceName[] = "android.hardware.gatekeeper.IGatekeeper/default";
class GateKeeperProxy : public BnGateKeeperService {
public:
GateKeeperProxy() {
clear_state_if_needed_done = false;
hw_device = IGatekeeper::getService();
+ ::ndk::SpAIBinder ks2Binder(AServiceManager_getService(gatekeeperServiceName));
+ aidl_hw_device = AidlIGatekeeper::fromBinder(ks2Binder);
is_running_gsi = android::base::GetBoolProperty(android::gsi::kGsiBootedProp, false);
- if (!hw_device) {
+ if (!aidl_hw_device && !hw_device) {
LOG(ERROR) << "Could not find Gatekeeper device, which makes me very sad.";
}
}
@@ -95,7 +105,9 @@
if (mark_cold_boot() && !is_running_gsi) {
ALOGI("cold boot: clearing state");
- if (hw_device) {
+ if (aidl_hw_device) {
+ aidl_hw_device->deleteAllUsers();
+ } else if (hw_device) {
hw_device->deleteAllUsers([](const GatekeeperResponse&) {});
}
}
@@ -150,7 +162,7 @@
uint32_t adjust_userId(uint32_t userId) {
static constexpr uint32_t kGsiOffset = 1000000;
CHECK(userId < kGsiOffset);
- CHECK(hw_device != nullptr);
+ CHECK((aidl_hw_device != nullptr) || (hw_device != nullptr));
if (is_running_gsi) {
return userId + kGsiOffset;
}
@@ -176,7 +188,7 @@
// need a desired password to enroll
if (desiredPassword.size() == 0) return GK_ERROR;
- if (!hw_device) {
+ if (!aidl_hw_device && !hw_device) {
LOG(ERROR) << "has no HAL to talk to";
return GK_ERROR;
}
@@ -185,9 +197,13 @@
android::hardware::hidl_vec<uint8_t> curPwd;
if (currentPasswordHandle && currentPassword) {
- if (currentPasswordHandle->size() != sizeof(gatekeeper::password_handle_t)) {
- LOG(INFO) << "Password handle has wrong length";
- return GK_ERROR;
+ if (hw_device) {
+ // Hidl Implementations expects passwordHandle to be in
+ // gatekeeper::password_handle_t format.
+ if (currentPasswordHandle->size() != sizeof(gatekeeper::password_handle_t)) {
+ LOG(INFO) << "Password handle has wrong length";
+ return GK_ERROR;
+ }
}
curPwdHandle.setToExternal(const_cast<uint8_t*>(currentPasswordHandle->data()),
currentPasswordHandle->size());
@@ -199,7 +215,27 @@
newPwd.setToExternal(const_cast<uint8_t*>(desiredPassword.data()), desiredPassword.size());
uint32_t hw_userId = adjust_userId(userId);
- Return<void> hwRes = hw_device->enroll(
+ uint64_t secureUserId = 0;
+ if (aidl_hw_device) {
+ // AIDL gatekeeper service
+ AidlGatekeeperEnrollResp rsp;
+ auto result = aidl_hw_device->enroll(hw_userId, curPwdHandle, curPwd, newPwd, &rsp);
+ if (!result.isOk()) {
+ LOG(ERROR) << "enroll transaction failed";
+ return GK_ERROR;
+ }
+ if (rsp.statusCode >= AidlIGatekeeper::STATUS_OK) {
+ *gkResponse = GKResponse::ok({rsp.data.begin(), rsp.data.end()});
+ secureUserId = static_cast<uint64_t>(rsp.secureUserId);
+ } else if (rsp.statusCode == AidlIGatekeeper::ERROR_RETRY_TIMEOUT &&
+ rsp.timeoutMs > 0) {
+ *gkResponse = GKResponse::retry(rsp.timeoutMs);
+ } else {
+ *gkResponse = GKResponse::error();
+ }
+ } else if (hw_device) {
+ // HIDL gatekeeper service
+ Return<void> hwRes = hw_device->enroll(
hw_userId, curPwdHandle, curPwd, newPwd,
[&gkResponse](const GatekeeperResponse& rsp) {
if (rsp.code >= GatekeeperStatusCode::STATUS_OK) {
@@ -211,22 +247,26 @@
*gkResponse = GKResponse::error();
}
});
- if (!hwRes.isOk()) {
- LOG(ERROR) << "enroll transaction failed";
- return GK_ERROR;
+ if (!hwRes.isOk()) {
+ LOG(ERROR) << "enroll transaction failed";
+ return GK_ERROR;
+ }
+ if (gkResponse->response_code() == GKResponseCode::OK) {
+ if (gkResponse->payload().size() != sizeof(gatekeeper::password_handle_t)) {
+ LOG(ERROR) << "HAL returned password handle of invalid length "
+ << gkResponse->payload().size();
+ return GK_ERROR;
+ }
+
+ const gatekeeper::password_handle_t* handle =
+ reinterpret_cast<const gatekeeper::password_handle_t*>(
+ gkResponse->payload().data());
+ secureUserId = handle->user_id;
+ }
}
if (gkResponse->response_code() == GKResponseCode::OK && !gkResponse->should_reenroll()) {
- if (gkResponse->payload().size() != sizeof(gatekeeper::password_handle_t)) {
- LOG(ERROR) << "HAL returned password handle of invalid length "
- << gkResponse->payload().size();
- return GK_ERROR;
- }
-
- const gatekeeper::password_handle_t* handle =
- reinterpret_cast<const gatekeeper::password_handle_t*>(
- gkResponse->payload().data());
- store_sid(userId, handle->user_id);
+ store_sid(userId, secureUserId);
GKResponse verifyResponse;
// immediately verify this password so we don't ask the user to enter it again
@@ -260,15 +300,18 @@
// can't verify if we're missing either param
if (enrolledPasswordHandle.size() == 0 || providedPassword.size() == 0) return GK_ERROR;
- if (!hw_device) return GK_ERROR;
-
- if (enrolledPasswordHandle.size() != sizeof(gatekeeper::password_handle_t)) {
- LOG(INFO) << "Password handle has wrong length";
+ if (!aidl_hw_device && !hw_device) {
+ LOG(ERROR) << "has no HAL to talk to";
return GK_ERROR;
}
- const gatekeeper::password_handle_t* handle =
- reinterpret_cast<const gatekeeper::password_handle_t*>(
- enrolledPasswordHandle.data());
+
+ if (hw_device) {
+ // Hidl Implementations expects passwordHandle to be in gatekeeper::password_handle_t
+ if (enrolledPasswordHandle.size() != sizeof(gatekeeper::password_handle_t)) {
+ LOG(INFO) << "Password handle has wrong length";
+ return GK_ERROR;
+ }
+ }
uint32_t hw_userId = adjust_userId(userId);
android::hardware::hidl_vec<uint8_t> curPwdHandle;
@@ -278,13 +321,36 @@
enteredPwd.setToExternal(const_cast<uint8_t*>(providedPassword.data()),
providedPassword.size());
- Return<void> hwRes = hw_device->verify(
+ uint64_t secureUserId = 0;
+ if (aidl_hw_device) {
+ // AIDL gatekeeper service
+ AidlGatekeeperVerifyResp rsp;
+ auto result =
+ aidl_hw_device->verify(hw_userId, challenge, curPwdHandle, enteredPwd, &rsp);
+ if (!result.isOk()) {
+ LOG(ERROR) << "verify transaction failed";
+ return GK_ERROR;
+ }
+ if (rsp.statusCode >= AidlIGatekeeper::STATUS_OK) {
+ secureUserId = rsp.hardwareAuthToken.userId;
+ // Serialize HardwareAuthToken to a vector as hw_auth_token_t.
+ *gkResponse = GKResponse::ok(authToken2AidlVec(rsp.hardwareAuthToken),
+ rsp.statusCode ==
+ AidlIGatekeeper::STATUS_REENROLL /* reenroll */);
+ } else if (rsp.statusCode == AidlIGatekeeper::ERROR_RETRY_TIMEOUT) {
+ *gkResponse = GKResponse::retry(rsp.timeoutMs);
+ } else {
+ *gkResponse = GKResponse::error();
+ }
+ } else if (hw_device) {
+ // HIDL gatekeeper service
+ Return<void> hwRes = hw_device->verify(
hw_userId, challenge, curPwdHandle, enteredPwd,
[&gkResponse](const GatekeeperResponse& rsp) {
if (rsp.code >= GatekeeperStatusCode::STATUS_OK) {
*gkResponse = GKResponse::ok(
- {rsp.data.begin(), rsp.data.end()},
- rsp.code == GatekeeperStatusCode::STATUS_REENROLL /* reenroll */);
+ {rsp.data.begin(), rsp.data.end()},
+ rsp.code == GatekeeperStatusCode::STATUS_REENROLL /* reenroll */);
} else if (rsp.code == GatekeeperStatusCode::ERROR_RETRY_TIMEOUT) {
*gkResponse = GKResponse::retry(rsp.timeout);
} else {
@@ -292,9 +358,14 @@
}
});
- if (!hwRes.isOk()) {
- LOG(ERROR) << "verify transaction failed";
- return GK_ERROR;
+ if (!hwRes.isOk()) {
+ LOG(ERROR) << "verify transaction failed";
+ return GK_ERROR;
+ }
+ const gatekeeper::password_handle_t* handle =
+ reinterpret_cast<const gatekeeper::password_handle_t*>(
+ enrolledPasswordHandle.data());
+ secureUserId = handle->user_id;
}
if (gkResponse->response_code() == GKResponseCode::OK) {
@@ -333,7 +404,7 @@
}
}
- maybe_store_sid(userId, handle->user_id);
+ maybe_store_sid(userId, secureUserId);
}
return Status::ok();
@@ -354,8 +425,10 @@
}
clear_sid(userId);
- if (hw_device) {
- uint32_t hw_userId = adjust_userId(userId);
+ uint32_t hw_userId = adjust_userId(userId);
+ if (aidl_hw_device) {
+ aidl_hw_device->deleteUser(hw_userId);
+ } else if (hw_device) {
hw_device->deleteUser(hw_userId, [](const GatekeeperResponse&) {});
}
return Status::ok();
@@ -382,7 +455,7 @@
return PERMISSION_DENIED;
}
- if (hw_device == NULL) {
+ if (aidl_hw_device == nullptr && hw_device == nullptr) {
const char* result = "Device not available";
write(fd, result, strlen(result) + 1);
} else {
@@ -394,6 +467,9 @@
}
private:
+ // AIDL gatekeeper service.
+ std::shared_ptr<AidlIGatekeeper> aidl_hw_device;
+ // HIDL gatekeeper service.
sp<IGatekeeper> hw_device;
bool clear_state_if_needed_done;
@@ -414,8 +490,8 @@
android::sp<android::IServiceManager> sm = android::defaultServiceManager();
android::sp<android::GateKeeperProxy> proxy = new android::GateKeeperProxy();
- android::status_t ret = sm->addService(
- android::String16("android.service.gatekeeper.IGateKeeperService"), proxy);
+ android::status_t ret =
+ sm->addService(android::String16("android.service.gatekeeper.IGateKeeperService"), proxy);
if (ret != android::OK) {
ALOGE("Couldn't register binder service!");
return -1;
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_draw.cpp b/healthd/healthd_draw.cpp
index 3e73fcd..7c79319 100644
--- a/healthd/healthd_draw.cpp
+++ b/healthd/healthd_draw.cpp
@@ -99,7 +99,7 @@
gr_fb_blank(blank, drm);
}
-/* support screen rotation for foldable phone */
+// support screen rotation for foldable phone
void HealthdDraw::rotate_screen(int drm) {
if (!graphics_available) return;
if (drm == 0)
@@ -108,6 +108,11 @@
gr_rotate(GRRotation::NONE /* Portrait mode */);
}
+// detect dual display
+bool HealthdDraw::has_multiple_connectors() {
+ return graphics_available && gr_has_multiple_connectors();
+}
+
void HealthdDraw::clear_screen(void) {
if (!graphics_available) return;
gr_color(0, 0, 0, 255);
diff --git a/healthd/healthd_draw.h b/healthd/healthd_draw.h
index 3d4abbd..016db8e 100644
--- a/healthd/healthd_draw.h
+++ b/healthd/healthd_draw.h
@@ -38,6 +38,9 @@
// Rotate screen.
virtual void rotate_screen(int drm);
+ // Detect dual display
+ virtual bool has_multiple_connectors();
+
static std::unique_ptr<HealthdDraw> Create(animation *anim);
protected:
diff --git a/healthd/healthd_mode_charger.cpp b/healthd/healthd_mode_charger.cpp
index e305a86..1ce174b 100644
--- a/healthd/healthd_mode_charger.cpp
+++ b/healthd/healthd_mode_charger.cpp
@@ -289,6 +289,18 @@
anim->run = false;
}
+void Charger::BlankSecScreen() {
+ int drm = drm_ == DRM_INNER ? 1 : 0;
+
+ if (!init_screen_) {
+ /* blank the secondary screen */
+ healthd_draw_->blank_screen(false, drm);
+ healthd_draw_->redraw_screen(&batt_anim_, surf_unknown_);
+ healthd_draw_->blank_screen(true, drm);
+ init_screen_ = true;
+ }
+}
+
void Charger::UpdateScreenState(int64_t now) {
int disp_time;
@@ -315,6 +327,9 @@
reset_animation(&batt_anim_);
next_screen_transition_ = -1;
healthd_draw_->blank_screen(true, static_cast<int>(drm_));
+ if (healthd_draw_->has_multiple_connectors()) {
+ BlankSecScreen();
+ }
screen_blanked_ = true;
LOGV("[%" PRId64 "] animation done\n", now);
if (configuration_->ChargerIsOnline()) {
diff --git a/healthd/include_charger/charger/healthd_mode_charger.h b/healthd/include_charger/charger/healthd_mode_charger.h
index 82e4ddf..c463b92 100644
--- a/healthd/include_charger/charger/healthd_mode_charger.h
+++ b/healthd/include_charger/charger/healthd_mode_charger.h
@@ -108,9 +108,11 @@
void InitAnimation();
int RequestEnableSuspend();
int RequestDisableSuspend();
+ void BlankSecScreen();
bool have_battery_state_ = false;
bool screen_blanked_ = false;
+ bool init_screen_ = false;
int64_t next_screen_transition_ = 0;
int64_t next_key_check_ = 0;
int64_t next_pwr_check_ = 0;
diff --git a/init/Android.bp b/init/Android.bp
index 856fe3e..f0e362c 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",
@@ -160,6 +161,7 @@
},
static_libs: [
"libavb",
+ "libbootloader_message",
"libc++fs",
"libcgrouprc_format",
"libfsverity_init",
@@ -180,7 +182,6 @@
],
shared_libs: [
"libbase",
- "libbootloader_message",
"libcrypto",
"libcutils",
"libdl",
@@ -199,6 +200,7 @@
"libutils",
"libziparchive",
],
+ header_libs: ["bionic_libc_platform_headers"],
bootstrap: true,
visibility: [":__subpackages__"],
}
@@ -217,6 +219,7 @@
"selinux_policy_version",
],
srcs: init_common_sources + init_device_sources,
+ export_include_dirs: ["."],
generated_sources: [
"apex-info-list",
],
@@ -246,6 +249,10 @@
],
},
},
+ visibility: [
+ "//system/apex/apexd",
+ "//frameworks/native/cmds/installd",
+ ],
}
phony {
@@ -269,7 +276,7 @@
"init.rc",
"ueventd.rc",
"e2fsdroid",
- "extra_free_kbytes.sh",
+ "extra_free_kbytes",
"make_f2fs",
"mke2fs",
"sload_f2fs",
@@ -523,6 +530,7 @@
"libcap",
],
export_include_dirs: ["test_utils/include"], // for tests
+ header_libs: ["bionic_libc_platform_headers"],
}
// Host Verifier
@@ -601,6 +609,7 @@
}
sh_binary {
- name: "extra_free_kbytes.sh",
+ name: "extra_free_kbytes",
src: "extra_free_kbytes.sh",
+ filename_from_src: true,
}
diff --git a/init/README.md b/init/README.md
index fed81db..7b3d32a 100644
--- a/init/README.md
+++ b/init/README.md
@@ -352,9 +352,10 @@
`socket <name> <type> <perm> [ <user> [ <group> [ <seclabel> ] ] ]`
> Create a UNIX domain socket named /dev/socket/_name_ and pass its fd to the
- launched process. _type_ must be "dgram", "stream" or "seqpacket". _type_
- may end with "+passcred" to enable SO_PASSCRED on the socket. User and
- group default to 0. 'seclabel' is the SELinux security context for the
+ launched process. The socket is created synchronously when the service starts.
+ _type_ must be "dgram", "stream" or "seqpacket". _type_ may end with "+passcred"
+ to enable SO_PASSCRED on the socket or "+listen" to synchronously make it a listening socket.
+ User and group default to 0. 'seclabel' is the SELinux security context for the
socket. It defaults to the service security context, as specified by
seclabel or computed based on the service executable file security context.
For native executables see libcutils android\_get\_control\_socket().
diff --git a/init/TEST_MAPPING b/init/TEST_MAPPING
index 36ca379..402b501 100644
--- a/init/TEST_MAPPING
+++ b/init/TEST_MAPPING
@@ -8,6 +8,14 @@
},
{
"name": "MicrodroidHostTestCases"
+ },
+ {
+ "name": "CtsSecurityHostTestCases",
+ "options": [
+ {
+ "include-filter": "android.security.cts.SeamendcHostTest"
+ }
+ ]
}
],
"hwasan-presubmit": [
@@ -19,6 +27,14 @@
},
{
"name": "MicrodroidHostTestCases"
+ },
+ {
+ "name": "CtsSecurityHostTestCases",
+ "options": [
+ {
+ "include-filter": "android.security.cts.SeamendcHostTest"
+ }
+ ]
}
]
}
diff --git a/init/action_manager.h b/init/action_manager.h
index 2746a7c..68912a8 100644
--- a/init/action_manager.h
+++ b/init/action_manager.h
@@ -49,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/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 38f6f39..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"
@@ -1279,48 +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 =
- CreateApexConfigParser(ActionManager::GetInstance(), ServiceList::GetInstance());
- 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.
*/
@@ -1351,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/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 202a86a..107e99a 100644
--- a/init/first_stage_init.cpp
+++ b/init/first_stage_init.cpp
@@ -295,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/init.cpp b/init/init.cpp
index be99a1c..ce668d7 100644
--- a/init/init.cpp
+++ b/init/init.cpp
@@ -63,8 +63,10 @@
#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"
@@ -82,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"
@@ -446,11 +449,47 @@
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) {
- std::string prop_name = "init.apex." + apex_name;
- // TODO(b/232114573) remove services and actions read from the apex
- // TODO(b/232799709) kill services from the apex
- SetProperty(prop_name, "unloaded");
+ if (StopServicesFromApex(apex_name) > 0) {
+ return Error() << "Unable to stop all service from " << apex_name;
+ }
+ RemoveServiceAndActionFromApex(apex_name);
return {};
}
@@ -474,14 +513,14 @@
}
static Result<void> DoLoadApex(const std::string& apex_name) {
- std::string prop_name = "init.apex." + apex_name;
- // TODO(b/232799709) read .rc files from the apex
+ if(auto result = ParseApexConfigs(apex_name); !result.ok()) {
+ return result.error();
+ }
if (auto result = UpdateApexLinkerConfig(apex_name); !result.ok()) {
return result.error();
}
- SetProperty(prop_name, "loaded");
return {};
}
diff --git a/init/init.h b/init/init.h
index 5220535..063632a 100644
--- a/init/init.h
+++ b/init/init.h
@@ -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 5651a83..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"
@@ -37,6 +41,7 @@
using android::base::GetIntProperty;
using android::base::GetProperty;
using android::base::SetProperty;
+using android::base::StringReplace;
using android::base::WaitForProperty;
using namespace std::literals;
@@ -188,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
@@ -338,20 +570,6 @@
EXPECT_EQ(2, num_executed);
}
-TEST(init, RespondToCtlApexMessages) {
- if (getuid() != 0) {
- GTEST_SKIP() << "Skipping test, must be run as root.";
- return;
- }
-
- std::string apex_name = "com.android.apex.cts.shim";
- SetProperty("ctl.apex_unload", apex_name);
- EXPECT_TRUE(WaitForProperty("init.apex." + apex_name, "unloaded", 10s));
-
- SetProperty("ctl.apex_load", apex_name);
- EXPECT_TRUE(WaitForProperty("init.apex." + apex_name, "loaded", 10s));
-}
-
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/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 7e92538..c2ba8d5 100644
--- a/init/property_service.cpp
+++ b/init/property_service.cpp
@@ -1404,7 +1404,8 @@
StartSendingMessages();
if (auto result = CreateSocket(PROP_SERVICE_NAME, SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK,
- false, 0666, 0, 0, {});
+ /*passcred=*/false, /*should_listen=*/false, 0666, /*uid=*/0,
+ /*gid=*/0, /*socketcon=*/{});
result.ok()) {
property_set_fd = *result;
} else {
diff --git a/init/reboot.cpp b/init/reboot.cpp
index 4e4bfd8..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 << ")"
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 b36584b..a633048 100644
--- a/init/service.cpp
+++ b/init/service.cpp
@@ -36,10 +36,16 @@
#include <processgroup/processgroup.h>
#include <selinux/selinux.h>
+#include <string>
+
#include "lmkd_service.h"
#include "service_list.h"
#include "util.h"
+#if defined(__BIONIC__)
+#include <bionic/reserved_signals.h>
+#endif
+
#ifdef INIT_FULL_SOURCES
#include <ApexProperties.sysprop.h>
#include <android/api-level.h>
@@ -53,6 +59,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;
@@ -320,6 +327,34 @@
mount_namespace_.has_value() && *mount_namespace_ == NS_DEFAULT;
const bool is_process_updatable = use_default_mount_ns && is_apex_updatable;
+#if defined(__BIONIC__) && defined(SEGV_MTEAERR)
+ // As a precaution, we only upgrade a service once per reboot, to limit
+ // the potential impact.
+ //
+ // BIONIC_SIGNAL_ART_PROFILER is a magic value used by deuggerd to signal
+ // that the process crashed with SIGSEGV and SEGV_MTEAERR. This signal will
+ // never be seen otherwise in a crash, because it always gets handled by the
+ // profiling signal handlers in bionic. See also
+ // debuggerd/handler/debuggerd_handler.cpp.
+ bool should_upgrade_mte = siginfo.si_code != CLD_EXITED &&
+ siginfo.si_status == BIONIC_SIGNAL_ART_PROFILER && !upgraded_mte_;
+
+ if (should_upgrade_mte) {
+ constexpr int kDefaultUpgradeSecs = 60;
+ int secs = GetIntProperty("persist.device_config.memory_safety_native.upgrade_secs.default",
+ kDefaultUpgradeSecs);
+ secs = GetIntProperty(
+ "persist.device_config.memory_safety_native.upgrade_secs.service." + name_, secs);
+ if (secs > 0) {
+ LOG(INFO) << "Upgrading service " << name_ << " to sync MTE for " << secs << " seconds";
+ once_environment_vars_.emplace_back("BIONIC_MEMTAG_UPGRADE_SECS", std::to_string(secs));
+ upgraded_mte_ = true;
+ } else {
+ LOG(INFO) << "Not upgrading service " << name_ << " to sync MTE due to device config";
+ }
+ }
+#endif
+
// If we crash > 4 times in 'fatal_crash_window_' minutes or before boot_completed,
// reboot into bootloader or set crashing property
boot_clock::time_point now = boot_clock::now();
@@ -484,6 +519,9 @@
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);
}
@@ -628,6 +666,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);
@@ -653,6 +693,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();
}
diff --git a/init/service.h b/init/service.h
index c14b312..ab19865 100644
--- a/init/service.h
+++ b/init/service.h
@@ -143,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;
@@ -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.
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 32c57c4..24a2024 100644
--- a/init/service_parser.cpp
+++ b/init/service_parser.cpp
@@ -434,11 +434,14 @@
<< "' instead.";
}
- if (types.size() > 1) {
- if (types.size() == 2 && types[1] == "passcred") {
+ for (size_t i = 1; i < types.size(); i++) {
+ if (types[i] == "passcred") {
socket.passcred = true;
+ } else if (types[i] == "listen") {
+ socket.listen = true;
} else {
- return Error() << "Only 'passcred' may be used to modify the socket type";
+ return Error() << "Unknown socket type decoration '" << types[i]
+ << "'. Known values are ['passcred', 'listen']";
}
}
diff --git a/init/service_utils.cpp b/init/service_utils.cpp
index eed5c65..d19f5ee 100644
--- a/init/service_utils.cpp
+++ b/init/service_utils.cpp
@@ -168,7 +168,8 @@
Result<Descriptor> SocketDescriptor::Create(const std::string& global_context) const {
const auto& socket_context = context.empty() ? global_context : context;
- auto result = CreateSocket(name, type | SOCK_CLOEXEC, passcred, perm, uid, gid, socket_context);
+ auto result = CreateSocket(name, type | SOCK_CLOEXEC, passcred, listen, perm, uid, gid,
+ socket_context);
if (!result.ok()) {
return result.error();
}
diff --git a/init/service_utils.h b/init/service_utils.h
index 9b65dca..65a2012 100644
--- a/init/service_utils.h
+++ b/init/service_utils.h
@@ -54,6 +54,7 @@
int perm = 0;
std::string context;
bool passcred = false;
+ bool listen = false;
bool persist = false;
// Create() creates the named unix domain socket in /dev/socket and returns a Descriptor object.
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/util.cpp b/init/util.cpp
index bfc3fb6..3d42855 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>
@@ -88,8 +89,8 @@
* daemon. We communicate the file descriptor's value via the environment
* variable ANDROID_SOCKET_ENV_PREFIX<name> ("ANDROID_SOCKET_foo").
*/
-Result<int> CreateSocket(const std::string& name, int type, bool passcred, mode_t perm, uid_t uid,
- gid_t gid, const std::string& socketcon) {
+Result<int> CreateSocket(const std::string& name, int type, bool passcred, bool should_listen,
+ mode_t perm, uid_t uid, gid_t gid, const std::string& socketcon) {
if (!socketcon.empty()) {
if (setsockcreatecon(socketcon.c_str()) == -1) {
return ErrnoError() << "setsockcreatecon(\"" << socketcon << "\") failed";
@@ -144,6 +145,9 @@
if (fchmodat(AT_FDCWD, addr.sun_path, perm, AT_SYMLINK_NOFOLLOW)) {
return ErrnoError() << "Failed to fchmodat socket '" << addr.sun_path << "'";
}
+ if (should_listen && listen(fd, /* use OS maximum */ 1 << 30)) {
+ return ErrnoError() << "Failed to listen on socket '" << addr.sun_path << "'";
+ }
LOG(INFO) << "Created socket '" << addr.sun_path << "'"
<< ", mode " << std::oct << perm << std::dec
@@ -748,5 +752,57 @@
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 daec470..e58e70e 100644
--- a/init/util.h
+++ b/init/util.h
@@ -44,8 +44,8 @@
extern void (*trigger_shutdown)(const std::string& command);
-Result<int> CreateSocket(const std::string& name, int type, bool passcred, mode_t perm, uid_t uid,
- gid_t gid, const std::string& socketcon);
+Result<int> CreateSocket(const std::string& name, int type, bool passcred, bool should_listen,
+ mode_t perm, uid_t uid, gid_t gid, const std::string& socketcon);
Result<std::string> ReadFile(const std::string& path);
Result<void> WriteFile(const std::string& path, const std::string& content);
@@ -109,5 +109,11 @@
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/janitors/OWNERS b/janitors/OWNERS
index 3e32c26..e132f0b 100644
--- a/janitors/OWNERS
+++ b/janitors/OWNERS
@@ -2,5 +2,5 @@
ccross@google.com
dwillemsen@google.com
enh@google.com
-hhb@google.com
narayan@google.com
+sadafebrahimi@google.com
\ No newline at end of file
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/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/private/android_filesystem_config.h b/libcutils/include/private/android_filesystem_config.h
index bdb8075..1e035bb 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 */
@@ -138,6 +140,7 @@
#define AID_JC_IDENTITYCRED 1089 /* Javacard Identity Cred HAL - to manage omapi ARA rules */
#define AID_SDK_SANDBOX 1090 /* SDK sandbox virtual UID */
#define AID_SECURITY_LOG_WRITER 1091 /* write to security log */
+#define AID_PRNG_SEEDER 1092 /* PRNG seeder daemon */
/* Changes to this file must be made in AOSP, *not* in internal branches. */
#define AID_SHELL 2000 /* adb and debug shell user */
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/libprocessgroup/cgrouprc/libcgrouprc.map.txt b/libprocessgroup/cgrouprc/libcgrouprc.map.txt
index ce5c419..b62b10f 100644
--- a/libprocessgroup/cgrouprc/libcgrouprc.map.txt
+++ b/libprocessgroup/cgrouprc/libcgrouprc.map.txt
@@ -1,18 +1,18 @@
LIBCGROUPRC { # introduced=29
global:
- ACgroupFile_getVersion;
- ACgroupFile_getControllerCount;
- ACgroupFile_getController;
- ACgroupController_getVersion;
- ACgroupController_getName;
- ACgroupController_getPath;
+ ACgroupFile_getVersion; # llndk systemapi
+ ACgroupFile_getControllerCount; # llndk systemapi
+ ACgroupFile_getController; # llndk systemapi
+ ACgroupController_getVersion; # llndk systemapi
+ ACgroupController_getName; # llndk systemapi
+ ACgroupController_getPath; # llndk systemapi
local:
*;
};
LIBCGROUPRC_30 { # introduced=30
global:
- ACgroupController_getFlags;
+ ACgroupController_getFlags; # llndk systemapi
local:
*;
};
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 51c810e..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) {
diff --git a/libprocessgroup/profiles/task_profiles.json b/libprocessgroup/profiles/task_profiles.json
index 8589a8d..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"
}
}
]
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/Android.bp b/libsparse/Android.bp
index 02bfee6..8e83e16 100644
--- a/libsparse/Android.bp
+++ b/libsparse/Android.bp
@@ -80,16 +80,12 @@
}
python_binary_host {
- name: "simg_dump.py",
+ name: "simg_dump",
main: "simg_dump.py",
srcs: ["simg_dump.py"],
version: {
- py2: {
- enabled: false,
- },
py3: {
embedded_launcher: true,
- enabled: true,
},
},
}
diff --git a/libsparse/simg_dump.py b/libsparse/simg_dump.py
index 8811a52..47537ca 100755
--- a/libsparse/simg_dump.py
+++ b/libsparse/simg_dump.py
@@ -158,7 +158,7 @@
curtype = format("Fill with 0x%08X" % (fill))
if showhash:
h = hashlib.sha1()
- data = fill_bin * (blk_sz / 4);
+ data = fill_bin * (blk_sz // 4);
for block in range(chunk_sz):
h.update(data)
curhash = h.hexdigest()
diff --git a/libstats/pull_rust/Android.bp b/libstats/pull_rust/Android.bp
index a3ef131..85a38f8 100644
--- a/libstats/pull_rust/Android.bp
+++ b/libstats/pull_rust/Android.bp
@@ -51,6 +51,7 @@
min_sdk_version: "apex_inherit",
apex_available: [
"//apex_available:platform",
+ "com.android.resolv",
"com.android.virt",
],
}
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/libutils/Android.bp b/libutils/Android.bp
index 7939e82..c744b53 100644
--- a/libutils/Android.bp
+++ b/libutils/Android.bp
@@ -299,6 +299,7 @@
srcs: [
"BitSet_test.cpp",
+ "CallStack_test.cpp",
"Errors_test.cpp",
"FileMap_test.cpp",
"LruCache_test.cpp",
@@ -319,11 +320,14 @@
"SystemClock_test.cpp",
],
shared_libs: [
- "libz",
- "liblog",
- "libcutils",
- "libutils",
"libbase",
+ "libcutils",
+ "liblog",
+ "liblzma",
+ "libunwindstack",
+ "libutils",
+ "libutilscallstack",
+ "libz",
],
},
linux: {
@@ -334,9 +338,12 @@
},
host: {
static_libs: [
- "libutils",
- "liblog",
"libbase",
+ "liblog",
+ "liblzma",
+ "libunwindstack_no_dex",
+ "libutils",
+ "libutilscallstack",
],
},
},
diff --git a/libutils/CallStack.cpp b/libutils/CallStack.cpp
index f19ba6a..4dcb35b 100644
--- a/libutils/CallStack.cpp
+++ b/libutils/CallStack.cpp
@@ -49,7 +49,7 @@
unwindstack::AndroidUnwinderData data;
std::optional<pid_t> tid_val;
if (tid != -1) {
- *tid_val = tid;
+ tid_val = tid;
}
if (!unwinder.Unwind(tid_val, data)) {
ALOGW("%s: Failed to unwind callstack: %s", __FUNCTION__, data.GetErrorString().c_str());
diff --git a/libutils/CallStack_test.cpp b/libutils/CallStack_test.cpp
new file mode 100644
index 0000000..2cfaf61
--- /dev/null
+++ b/libutils/CallStack_test.cpp
@@ -0,0 +1,63 @@
+/*
+ * 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 <unistd.h>
+
+#include <thread>
+
+#include <android-base/threads.h>
+#include <gtest/gtest.h>
+#include <utils/CallStack.h>
+
+__attribute__((__noinline__)) extern "C" void CurrentCaller(android::String8& backtrace) {
+ android::CallStack cs;
+ cs.update();
+ backtrace = cs.toString();
+}
+
+TEST(CallStackTest, current_backtrace) {
+ android::String8 backtrace;
+ CurrentCaller(backtrace);
+
+ ASSERT_NE(-1, backtrace.find("(CurrentCaller")) << "Full backtrace:\n" << backtrace;
+}
+
+__attribute__((__noinline__)) extern "C" void ThreadBusyWait(std::atomic<pid_t>* tid,
+ volatile bool* done) {
+ *tid = android::base::GetThreadId();
+ while (!*done) {
+ }
+}
+
+TEST(CallStackTest, thread_backtrace) {
+ // Use a volatile to avoid any problems unwinding since sometimes
+ // accessing a std::atomic does not include unwind data at every
+ // instruction and leads to failed unwinds.
+ volatile bool done = false;
+ std::atomic<pid_t> tid = -1;
+ std::thread thread([&tid, &done]() { ThreadBusyWait(&tid, &done); });
+
+ while (tid == -1) {
+ }
+
+ android::CallStack cs;
+ cs.update(0, tid);
+
+ done = true;
+ thread.join();
+
+ ASSERT_NE(-1, cs.toString().find("(ThreadBusyWait")) << "Full backtrace:\n" << cs.toString();
+}
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/rootdir/init.rc b/rootdir/init.rc
index 660f18c..ec760d3 100644
--- a/rootdir/init.rc
+++ b/rootdir/init.rc
@@ -723,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
@@ -792,6 +796,7 @@
mkdir /data/misc/vpn 0770 system vpn
mkdir /data/misc/shared_relro 0771 shared_relro shared_relro
mkdir /data/misc/systemkeys 0700 system system
+ mkdir /data/misc/threadnetwork 0770 thread_network thread_network
mkdir /data/misc/wifi 0770 wifi wifi
mkdir /data/misc/wifi/sockets 0770 wifi wifi
mkdir /data/misc/wifi/wpa_supplicant 0770 wifi wifi
@@ -852,7 +857,6 @@
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
diff --git a/rootdir/init.zygote64.rc b/rootdir/init.zygote64.rc
index 5bde5f4..b6ca5c0 100644
--- a/rootdir/init.zygote64.rc
+++ b/rootdir/init.zygote64.rc
@@ -1,4 +1,4 @@
-service zygote /system/bin/app_process64 -Xzygote /system/bin --zygote --start-system-server
+service zygote /system/bin/app_process64 -Xzygote /system/bin --zygote --start-system-server --socket-name=zygote
class main
priority -20
user root
@@ -13,5 +13,5 @@
onrestart restart media.tuner
onrestart restart netd
onrestart restart wificond
- task_profiles ProcessCapacityHigh
+ task_profiles ProcessCapacityHigh MaxPerformance
critical window=${zygote.critical_window.minute:-off} target=zygote-fatal
diff --git a/rootdir/init.zygote64_32.rc b/rootdir/init.zygote64_32.rc
index efb30d6..109bf6c 100644
--- a/rootdir/init.zygote64_32.rc
+++ b/rootdir/init.zygote64_32.rc
@@ -1,20 +1,4 @@
-service zygote /system/bin/app_process64 -Xzygote /system/bin --zygote --start-system-server --socket-name=zygote
- class main
- priority -20
- user root
- group root readproc reserved_disk
- socket zygote stream 660 root system
- socket usap_pool_primary stream 660 root system
- onrestart exec_background - system system -- /system/bin/vdc volume abort_fuse
- onrestart write /sys/power/state on
- onrestart restart audioserver
- onrestart restart cameraserver
- onrestart restart media
- onrestart restart media.tuner
- onrestart restart netd
- onrestart restart wificond
- task_profiles ProcessCapacityHigh MaxPerformance
- critical window=${zygote.critical_window.minute:-off} target=zygote-fatal
+import /system/etc/init/hw/init.zygote64.rc
service zygote_secondary /system/bin/app_process32 -Xzygote /system/bin --zygote --socket-name=zygote_secondary --enable-lazy-preload
class main
diff --git a/rootdir/ueventd.rc b/rootdir/ueventd.rc
index a140c8c..4ec59af 100644
--- a/rootdir/ueventd.rc
+++ b/rootdir/ueventd.rc
@@ -37,6 +37,8 @@
/dev/tty 0666 root root
/dev/random 0666 root root
/dev/urandom 0666 root root
+# Aside from kernel threads, only prng_seeder needs access to HW RNG
+/dev/hw_random 0400 prng_seeder prng_seeder
/dev/ashmem* 0666 root root
/dev/binder 0666 root root
/dev/hwbinder 0666 root root
diff --git a/set-verity-state/.clang-format b/set-verity-state/.clang-format
deleted file mode 120000
index fd0645f..0000000
--- a/set-verity-state/.clang-format
+++ /dev/null
@@ -1 +0,0 @@
-../.clang-format-2
\ No newline at end of file
diff --git a/set-verity-state/Android.bp b/set-verity-state/Android.bp
deleted file mode 100644
index f40118b..0000000
--- a/set-verity-state/Android.bp
+++ /dev/null
@@ -1,41 +0,0 @@
-// Copyright 2019 The Android Open Source Project
-
-package {
- default_applicable_licenses: ["Android-Apache-2.0"],
-}
-
-cc_binary {
- name: "set-verity-state",
- srcs: ["set-verity-state.cpp"],
- shared_libs: [
- "libbase",
- "libbinder",
- "libcrypto",
- "libcrypto_utils",
- "libfs_mgr_binder",
- "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
deleted file mode 100644
index e849450..0000000
--- a/set-verity-state/OWNERS
+++ /dev/null
@@ -1,3 +0,0 @@
-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
deleted file mode 100644
index de9a452..0000000
--- a/set-verity-state/set-verity-state.cpp
+++ /dev/null
@@ -1,243 +0,0 @@
-/*
- * Copyright (C) 2019 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 <getopt.h>
-#include <stdio.h>
-
-#include <android-base/file.h>
-#include <android-base/logging.h>
-#include <android-base/properties.h>
-#include <binder/ProcessState.h>
-#include <cutils/android_reboot.h>
-#include <fs_mgr_overlayfs.h>
-#include <libavb_user/libavb_user.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
-const bool kAllowDisableVerity = true;
-#else
-const bool kAllowDisableVerity = false;
-#endif
-
-/* 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.
- */
-std::string get_ab_suffix() {
- return android::base::GetProperty("ro.boot.slot_suffix", "");
-}
-
-bool is_avb_device_locked() {
- return android::base::GetProperty("ro.boot.vbmeta.device_state", "") == "locked";
-}
-
-bool is_debuggable() {
- return android::base::GetBoolProperty("ro.debuggable", 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).
- return !android::base::GetProperty("ro.boot.vbmeta.digest", "").empty();
-}
-
-[[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 change = false;
- errno = 0;
- if (enable ? fs_mgr_overlayfs_setup(nullptr, &change)
- : fs_mgr_overlayfs_teardown(nullptr, &change)) {
- if (change) {
- LOG(INFO) << (enable ? "Enabled" : "Disabled") << " overlayfs";
- }
- } else {
- LOG(ERROR) << "Failed to " << (enable ? "enable" : "disable") << " overlayfs";
- }
- return change;
-}
-
-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;
- }
- }
-
- 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 (!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);
- }
- printf("Reboot the device for new settings to take effect\n");
- }
-
- return exit_code;
-}
diff --git a/storaged/storaged.cpp b/storaged/storaged.cpp
index cefef6e..ba79ff7 100644
--- a/storaged/storaged.cpp
+++ b/storaged/storaged.cpp
@@ -27,6 +27,7 @@
#include <fstream>
#include <sstream>
#include <string>
+#include <utility>
#include <aidl/android/hardware/health/BnHealthInfoCallback.h>
#include <android-base/file.h>
@@ -62,7 +63,7 @@
constexpr ssize_t benchmark_unit_size = 16 * 1024; // 16KB
-constexpr ssize_t min_benchmark_size = 128 * 1024; // 128KB
+constexpr size_t min_benchmark_size = 128 * 1024; // 128KB
} // namespace
@@ -244,9 +245,10 @@
proto.ParseFromString(ss.str());
const UidIOUsage& uid_io_usage = proto.uid_io_usage();
- uint32_t computed_crc = crc32(current_version,
- reinterpret_cast<const Bytef*>(uid_io_usage.SerializeAsString().c_str()),
- uid_io_usage.ByteSize());
+ uint32_t computed_crc =
+ crc32(current_version,
+ reinterpret_cast<const Bytef*>(uid_io_usage.SerializeAsString().c_str()),
+ uid_io_usage.ByteSizeLong());
if (proto.crc() != computed_crc) {
LOG(WARNING) << "CRC mismatch in " << proto_file;
return;
@@ -264,31 +266,29 @@
const UidIOUsage& uid_io_usage = proto->uid_io_usage();
proto->set_crc(crc32(current_version,
- reinterpret_cast<const Bytef*>(uid_io_usage.SerializeAsString().c_str()),
- uid_io_usage.ByteSize()));
+ reinterpret_cast<const Bytef*>(uid_io_usage.SerializeAsString().c_str()),
+ uid_io_usage.ByteSizeLong()));
uint32_t pagesize = sysconf(_SC_PAGESIZE);
if (user_id == USER_SYSTEM) {
proto->set_padding("", 1);
vector<char> padding;
- ssize_t size = ROUND_UP(MAX(min_benchmark_size, proto->ByteSize()),
- pagesize);
- padding = vector<char>(size - proto->ByteSize(), 0xFD);
+ ssize_t size = ROUND_UP(std::max(min_benchmark_size, proto->ByteSizeLong()), pagesize);
+ padding = vector<char>(size - proto->ByteSizeLong(), 0xFD);
proto->set_padding(padding.data(), padding.size());
- while (!IS_ALIGNED(proto->ByteSize(), pagesize)) {
+ while (!IS_ALIGNED(proto->ByteSizeLong(), pagesize)) {
padding.push_back(0xFD);
proto->set_padding(padding.data(), padding.size());
}
}
char* data = nullptr;
- if (posix_memalign(reinterpret_cast<void**>(&data),
- pagesize, proto->ByteSize())) {
- PLOG(ERROR) << "Faied to alloc aligned buffer (size: " << proto->ByteSize() << ")";
+ if (posix_memalign(reinterpret_cast<void**>(&data), pagesize, proto->ByteSizeLong())) {
+ PLOG(ERROR) << "Faied to alloc aligned buffer (size: " << proto->ByteSizeLong() << ")";
return data;
}
- proto->SerializeToArray(data, proto->ByteSize());
+ proto->SerializeToArray(data, proto->ByteSizeLong());
return data;
}
@@ -314,7 +314,7 @@
while (size > 0) {
start = steady_clock::now();
- ret = write(fd, data, MIN(benchmark_unit_size, size));
+ ret = write(fd, data, std::min(benchmark_unit_size, size));
if (ret <= 0) {
PLOG(ERROR) << "Faied to write tmp file: " << tmp_file;
return;
@@ -352,7 +352,7 @@
unique_ptr<char> proto_data(prepare_proto(user_id, proto));
if (proto_data == nullptr) return;
- flush_proto_data(user_id, proto_data.get(), proto->ByteSize());
+ flush_proto_data(user_id, proto_data.get(), proto->ByteSizeLong());
}
void storaged_t::flush_protos(unordered_map<int, StoragedProto>* protos) {
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/Android.bp b/trusty/keymaster/Android.bp
index 0e916ef..adc9fdf 100644
--- a/trusty/keymaster/Android.bp
+++ b/trusty/keymaster/Android.bp
@@ -123,7 +123,6 @@
],
required: [
"android.hardware.hardware_keystore.xml",
- "RemoteProvisioner",
],
}
diff --git a/trusty/trusty-base.mk b/trusty/trusty-base.mk
index d40a59e..0609709 100644
--- a/trusty/trusty-base.mk
+++ b/trusty/trusty-base.mk
@@ -25,7 +25,8 @@
PRODUCT_PACKAGES += \
android.hardware.security.keymint-service.trusty \
android.hardware.gatekeeper@1.0-service.trusty \
- trusty_apploader
+ trusty_apploader \
+ RemoteProvisioner
PRODUCT_PROPERTY_OVERRIDES += \
ro.hardware.keystore_desede=true \
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