From 6dc0dfe785ba174197597c563d28a4b999a7a380 Mon Sep 17 00:00:00 2001 From: Mark Salyzyn Date: Tue, 26 Feb 2019 12:36:47 -0800 Subject: [PATCH 1/6] fs_mgr: overlayfs: test: add inRecovery check Handle a device in recovery mode gracefully. Handle a device that fails to boot into a commanded state more gracefully. Deal with regression where die() calls restore(), and we can not have this under the conditions where we are ignoring the error in a subshell. Test: adb-remount-test.sh Bug: 118225373 Bug: 123079041 Change-Id: Ie37beb245d0ec55eb00757cdb93da34ff9c42827 --- fs_mgr/tests/adb-remount-test.sh | 167 +++++++++++++++++++++---------- 1 file changed, 113 insertions(+), 54 deletions(-) diff --git a/fs_mgr/tests/adb-remount-test.sh b/fs_mgr/tests/adb-remount-test.sh index fd53ed4b4..bbe80abd7 100755 --- a/fs_mgr/tests/adb-remount-test.sh +++ b/fs_mgr/tests/adb-remount-test.sh @@ -70,19 +70,37 @@ inFastboot() { Returns: true if device is in adb mode" ] inAdb() { adb devices | - grep -v -e 'List of devices attached' -e '^$' | + grep -v -e 'List of devices attached' -e '^$' -e "[${SPACE}${TAB}]recovery\$" | if [ -n "${ANDROID_SERIAL}" ]; then grep "^${ANDROID_SERIAL}[${SPACE}${TAB}]" > /dev/null else wc -l | grep '^1$' >/dev/null fi } +[ "USAGE: inRecovery + +Returns: true if device is in recovery mode" ] +inRecovery() { + local list="`adb devices | + grep -v -e 'List of devices attached' -e '^$'`" + if [ -n "${ANDROID_SERIAL}" ]; then + echo "${list}" | + grep "^${ANDROID_SERIAL}[${SPACE}${TAB}][${SPACE}${TAB}]*recovery\$" >/dev/null + return ${?} + fi + if echo "${list}" | wc -l | grep '^1$' >/dev/null; then + echo "${list}" | + grep "[${SPACE}${TAB}]recovery\$" >/dev/null + return ${?} + fi + false +} [ "USAGE: adb_sh /dev/stdout 2>/dev/stderr Returns: true if the command succeeded" ] adb_sh() { - args= + local args= for i in "${@}"; do [ -z "${args}" ] || args="${args} " if [ X"${i}" != X"${i#\'}" ]; then @@ -143,10 +161,10 @@ adb_su() { Returns: content of file to stdout with carriage returns skipped, true of the file exists" ] adb_cat() { - OUTPUT="`adb_sh cat ${1} &1`" - retval=${?} + local OUTPUT="`adb_sh cat ${1} &1`" + local ret=${?} echo "${OUTPUT}" | tr -d '\r' - return ${retval} + return ${ret} } [ "USAGE: adb_reboot @@ -165,7 +183,7 @@ format_duration() { echo unknown return fi - duration="${1}" + local duration="${1}" if [ X"${duration}" != X"${duration%s}" ]; then duration=${duration%s} elif [ X"${duration}" != X"${duration%m}" ]; then @@ -175,9 +193,9 @@ format_duration() { elif [ X"${duration}" != X"${duration%d}" ]; then duration=`expr ${duration%d} \* 86400` fi - seconds=`expr ${duration} % 60` - minutes=`expr \( ${duration} / 60 \) % 60` - hours=`expr ${duration} / 3600` + local seconds=`expr ${duration} % 60` + local minutes=`expr \( ${duration} / 60 \) % 60` + local hours=`expr ${duration} / 3600` if [ 0 -eq ${minutes} -a 0 -eq ${hours} ]; then if [ 1 -eq ${duration} ]; then echo 1 second @@ -205,10 +223,10 @@ Returns: waits until the device has returned for adb or optional timeout" ] adb_wait() { if [ -n "${1}" ]; then echo -n ". . . waiting `format_duration ${1}`" ${ANDROID_SERIAL} ${USB_ADDRESS} "${CR}" - timeout --preserve-status --signal=KILL ${1} adb wait-for-device - retval=${?} + timeout --preserve-status --signal=KILL ${1} adb wait-for-device 2>/dev/null + local ret=${?} echo -n " ${CR}" - return ${retval} + return ${ret} else adb wait-for-device fi @@ -216,12 +234,15 @@ adb_wait() { [ "USAGE: usb_status > stdout -If adb_wait failed, check if device is in fastboot mode and report status +If adb_wait failed, check if device is in adb, recovery or fastboot mode +and report status string. Returns: \"(USB stack borken?)\", \"(In fastboot mode)\" or \"(in adb mode)\"" ] usb_status() { if inFastboot; then echo "(In fastboot mode)" + elif inRecovery; then + echo "(In recovery mode)" elif inAdb; then echo "(In adb mode)" else @@ -238,15 +259,47 @@ fastboot_wait() { if [ -n "${1}" ]; then echo -n ". . . waiting `format_duration ${1}`" ${ANDROID_SERIAL} ${USB_ADDRESS} "${CR}" timeout --preserve-status --signal=KILL ${1} fastboot wait-for-device >/dev/null 2>/dev/null - retval=${?} + local ret=${?} echo -n " ${CR}" - ( exit ${retval} ) + ( exit ${ret} ) else fastboot wait-for-device >/dev/null 2>/dev/null fi || inFastboot } +[ "USAGE: recovery_wait [timeout] + +Returns: waits until the device has returned for recovery or optional timeout" ] +recovery_wait() { + if [ -n "${1}" ]; then + echo -n ". . . waiting `format_duration ${1}`" ${ANDROID_SERIAL} ${USB_ADDRESS} "${CR}" + timeout --preserve-status --signal=KILL ${1} adb wait-for-recovery 2>/dev/null + local ret=${?} + echo -n " ${CR}" + return ${ret} + else + adb wait-for-recovery + fi +} + +[ "any_wait [timeout] + +Returns: waits until a device has returned or optional timeout" ] +any_wait() { + ( + adb_wait ${1} & + adb_pid=${!} + fastboot_wait ${1} & + fastboot_pid=${!} + recovery_wait ${1} & + recovery_pid=${!} + wait -n + kill "${adb_pid}" "${fastboot_pid}" "${recovery_pid}" + ) >/dev/null 2>/dev/null + inFastboot || inAdb || inRecovery +} + [ "USAGE: adb_root NB: This can be flakey on devices due to USB state @@ -277,11 +330,11 @@ adb_unroot() { Returns: true if var output matches expected" ] fastboot_getvar() { - O=`fastboot getvar ${1} 2>&1` - err=${?} + local O=`fastboot getvar ${1} 2>&1` + local ret=${?} O="${O#< waiting for * >?}" O="${O%%?Finished. Total time: *}" - if [ 0 -ne ${err} ]; then + if [ 0 -ne ${ret} ]; then echo ${O} >&2 false return @@ -325,7 +378,7 @@ test_duration() { echo "${BLUE}[ INFO ]${NORMAL} end `date`" [ -n "${start_time}" ] || return end_time=`date +%s` - diff_time=`expr ${end_time} - ${start_time}` + local diff_time=`expr ${end_time} - ${start_time}` echo "${BLUE}[ INFO ]${NORMAL} duration `format_duration ${diff_time}`" fi >&2 } @@ -358,8 +411,8 @@ die() { Returns true if (regex) lval matches rval" ] EXPECT_EQ() { - lval="${1}" - rval="${2}" + local lval="${1}" + local rval="${2}" shift 2 if ! ( echo X"${rval}" | grep '^X'"${lval}"'$' >/dev/null 2>/dev/null ); then if [ `echo ${lval}${rval}${*} | wc -c` -gt 50 -o "${rval}" != "${rval% @@ -404,10 +457,10 @@ EXPECT_EQ() { Exits if (regex) lval mismatches rval" ] check_eq() { - left="${1}" - right="${2}" + local lval="${1}" + local rval="${2}" shift 2 - EXPECT_EQ "${left}" "${right}" || + EXPECT_EQ "${lval}" "${rval}" || die "${@}" } @@ -498,15 +551,16 @@ if ${print_time}; then fi inFastboot && die "device in fastboot mode" +inRecovery && die "device in recovery mode" if ! inAdb; then - echo "${ORANGE}[ WARNING ]${NORMAL} device not in adb mode" + echo "${ORANGE}[ WARNING ]${NORMAL} device not in adb mode" >&2 adb_wait 2m fi inAdb || die "specified device not in adb mode" isDebuggable || die "device not a debug build" enforcing=true if ! adb_su getenforce /dev/null; then - echo "${ORANGE}[ WARNING ]${NORMAL} device does not have sepolicy in enforcing mode" + echo "${ORANGE}[ WARNING ]${NORMAL} device does not have sepolicy in enforcing mode" >&2 enforcing=false fi @@ -577,7 +631,7 @@ adb_sh ls -d /sys/module/overlay /dev/null 2>/dev/null || ) || overlayfs_supported=false if ${overlayfs_supported}; then - adb_su ls /sys/module/overlay/parameters/override_creds /dev/null && + adb_su ls /sys/module/overlay/parameters/override_creds /dev/null 2>/dev/null && echo "${GREEN}[ OK ]${NORMAL} overlay module supports override_creds" >&2 || case `adb_sh uname -r &2 fi -B="`adb_cat /system/hello`" || - die "re-read /system/hello after reboot" -check_eq "${A}" "${B}" /system after reboot -echo "${GREEN}[ OK ]${NORMAL} /system content remains after reboot" >&2 -# Only root can read vendor if sepolicy permissions are as expected. if ${enforcing}; then - adb_unroot - B="`adb_cat /vendor/hello`" && - die "re-read /vendor/hello after reboot w/o root" + adb_unroot || + 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 fi -adb_root && - B="`adb_cat /vendor/hello`" || - die "re-read /vendor/hello after reboot" +B="`adb_cat /system/hello`" +check_eq "${A}" "${B}" /system after reboot +echo "${GREEN}[ OK ]${NORMAL} /system content remains after reboot" >&2 +# Only root can read vendor if sepolicy permissions are as expected. +adb_root || + die "adb root" +B="`adb_cat /vendor/hello`" check_eq "${A}" "${B}" vendor after reboot echo "${GREEN}[ OK ]${NORMAL} /vendor content remains after reboot" >&2 @@ -901,8 +954,9 @@ elif [ -z "${ANDROID_HOST_OUT}" ]; then else adb reboot-fastboot || die "fastbootd not supported (wrong adb in path?)" - fastboot_wait 2m || - die "reboot into fastboot to flash vendor `usb_status`" + any_wait 2m && + inFastboot || + die "reboot into fastboot to flash vendor `usb_status` (bad bootloader?)" fastboot flash vendor || ( fastboot reboot && false) || die "fastboot flash vendor" @@ -956,12 +1010,11 @@ else if ${is_userspace_fastboot}; then die "overlay supposed to be minus /vendor takeover after flash vendor" else - echo "${ORANGE}[ WARNING ]${NORMAL} user fastboot missing, ignoring a failure" - ( die "overlay supposed to be minus /vendor takeover after flash vendor" ) + echo "${ORANGE}[ WARNING ]${NORMAL} user fastboot missing required to invalidate, ignoring a failure" >&2 + echo "${ORANGE}[ WARNING ]${NORMAL} overlay supposed to be minus /vendor takeover after flash vendor" >&2 fi fi - B="`adb_cat /system/hello`" || - die "re-read /system/hello after flash vendor" + B="`adb_cat /system/hello`" check_eq "${A}" "${B}" system after flash vendor adb_root || die "adb root" @@ -969,13 +1022,21 @@ else if ${is_userspace_fastboot} || ! ${overlayfs_needed}; then die "re-read /vendor/hello after flash vendor" else - echo "${ORANGE}[ WARNING ]${NORMAL} user fastboot missing, ignoring a failure" - ( die "re-read /vendor/hello after flash vendor" ) + echo "${ORANGE}[ WARNING ]${NORMAL} user fastboot missing required to invalidate, ignoring a failure" >&2 + echo "${ORANGE}[ WARNING ]${NORMAL} re-read /vendor/hello after flash vendor" >&2 fi if ${is_userspace_fastboot} || ! ${overlayfs_needed}; then - check_eq "cat: /vendor/hello: No such file or directory" "${B}" vendor after flash vendor + check_eq "cat: /vendor/hello: No such file or directory" "${B}" \ + vendor content after flash vendor else - ( check_eq "cat: /vendor/hello: No such file or directory" "${B}" vendor after flash vendor ) + ( + echo "${ORANGE}[ WARNING ]${NORMAL} user fastboot missing required to invalidate, ignoring a failure" >&2 + restore() { + true + } + check_eq "cat: /vendor/hello: No such file or directory" "${B}" \ + vendor content after flash vendor + ) fi fi @@ -1003,12 +1064,10 @@ echo "${H}" adb_sh rm /system/hello Date: Wed, 20 Feb 2019 08:32:36 -0800 Subject: [PATCH 2/6] fs_mgr: add remount command There is currently no good option for callers to setup overlayfs on-device, it is automated as part of the adb services. Add a remount command that does what is needed that simulates the salient behaviors of the adb remount command. Clean up some noise restoring device to original state when done. Test: adb-remount-test.sh Bug: 122602260 Change-Id: Idf213800a8182cb1c51600c8f574df8a8cd68d4a --- fs_mgr/Android.bp | 23 +++ fs_mgr/fs_mgr_remount.cpp | 281 +++++++++++++++++++++++++++++++ fs_mgr/tests/adb-remount-test.sh | 30 ++++ 3 files changed, 334 insertions(+) create mode 100644 fs_mgr/fs_mgr_remount.cpp diff --git a/fs_mgr/Android.bp b/fs_mgr/Android.bp index 974e13e44..a476cd85d 100644 --- a/fs_mgr/Android.bp +++ b/fs_mgr/Android.bp @@ -111,3 +111,26 @@ cc_library_static { "libgsi_headers", ], } + +cc_binary { + name: "remount", + defaults: ["fs_mgr_defaults"], + shared_libs: [ + "libbase", + "libfs_mgr", + ], + srcs: [ + "fs_mgr_remount.cpp", + ], + cppflags: [ + "-DALLOW_ADBD_DISABLE_VERITY=0", + ], + product_variables: { + debuggable: { + cppflags: [ + "-UALLOW_ADBD_DISABLE_VERITY", + "-DALLOW_ADBD_DISABLE_VERITY=1", + ], + }, + }, +} diff --git a/fs_mgr/fs_mgr_remount.cpp b/fs_mgr/fs_mgr_remount.cpp new file mode 100644 index 000000000..66756ab6f --- /dev/null +++ b/fs_mgr/fs_mgr_remount.cpp @@ -0,0 +1,281 @@ +/* + * 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 +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +namespace { + +[[noreturn]] void usage(int exit_status) { + LOG(INFO) << getprogname() + << " [-h]\n" + "\t-h --help\tthis help\n" + "\n" + "Remount all partitions read-write.\n" + "Verity must be disabled."; + + ::exit(exit_status); +} + +bool remountable_partition(const android::fs_mgr::FstabEntry& entry) { + if (entry.fs_mgr_flags.vold_managed) return false; + if (entry.fs_mgr_flags.recovery_only) return false; + if (entry.fs_mgr_flags.slot_select_other) return false; + if (!(entry.flags & MS_RDONLY)) return false; + if (entry.fs_type == "vfat") return false; + return true; +} + +const std::string system_mount_point(const android::fs_mgr::FstabEntry& entry) { + if (entry.mount_point == "/") return "/system"; + return entry.mount_point; +} + +const android::fs_mgr::FstabEntry* is_wrapped(const android::fs_mgr::Fstab& overlayfs_candidates, + const android::fs_mgr::FstabEntry& entry) { + auto mount_point = system_mount_point(entry); + auto it = std::find_if(overlayfs_candidates.begin(), overlayfs_candidates.end(), + [&mount_point](const auto& entry) { + return android::base::StartsWith(mount_point, + system_mount_point(entry) + "/"); + }); + if (it == overlayfs_candidates.end()) return nullptr; + return &(*it); +} + +void try_unmount_bionic(android::fs_mgr::Fstab* mounts) { + static constexpr const char* kBionic = "/bionic"; + + auto entry = GetEntryForMountPoint(mounts, kBionic); + if (!entry) return; + + struct statfs buf; + if (::statfs(kBionic, &buf) == -1) { + PLOG(ERROR) << "statfs of " << kBionic; + return; + } + if (buf.f_flags & MS_RDONLY) { + // /bionic is on a read-only partition; can happen for + // non-system-as-root-devices. Don' try to unmount. + return; + } + fs_mgr_set_blk_ro(entry->blk_device, false); + if (::mount(entry->blk_device.c_str(), entry->mount_point.c_str(), entry->fs_type.c_str(), + MS_REMOUNT, nullptr) == -1) { + PLOG(ERROR) << "remount of " << kBionic; + } +} + +void MyLogger(android::base::LogId, android::base::LogSeverity severity, const char*, const char*, + unsigned int, const char* message) { + static const char log_characters[] = "VD\0WEFF"; + if (severity < sizeof(log_characters)) { + auto severity_char = log_characters[severity]; + if (severity_char) fprintf(stderr, "%c ", severity_char); + } + fprintf(stderr, "%s\n", message); +} + +} // namespace + +int main(int argc, char* argv[]) { + android::base::InitLogging(argv, MyLogger); + + enum { + SUCCESS, + NOT_USERDEBUG, + BADARG, + NOT_ROOT, + NO_FSTAB, + VERITY_PARTITION, + BAD_OVERLAY, + NO_MOUNTS, + REMOUNT_FAILED, + } retval = SUCCESS; + + // If somehow this executable is delivered on a "user" build, it can + // not function, so providing a clear message to the caller rather than + // letting if fall through and provide a lot of confusing failure messages. + if (!ALLOW_ADBD_DISABLE_VERITY || (android::base::GetProperty("ro.debuggable", "0") != "1")) { + LOG(ERROR) << "only functions on userdebug or eng builds"; + return NOT_USERDEBUG; + } + + struct option longopts[] = { + {"help", no_argument, nullptr, 'h'}, + {0, 0, nullptr, 0}, + }; + for (int opt; (opt = ::getopt_long(argc, argv, "h", longopts, nullptr)) != -1;) { + switch (opt) { + default: + LOG(ERROR) << "Bad Argument -" << char(opt); + usage(BADARG); + break; + case 'h': + usage(SUCCESS); + break; + } + } + + if (argc > optind) { + LOG(ERROR) << "Bad Argument " << argv[optind]; + usage(BADARG); + } + + // Make sure we are root. + if (::getuid() != 0) { + LOG(ERROR) << "must be run as root"; + return NOT_ROOT; + } + + android::fs_mgr::Fstab fstab; + auto fstab_read = android::fs_mgr::ReadDefaultFstab(&fstab); + if (!fstab_read || fstab.empty()) { + PLOG(ERROR) << "Failed to read default fstab"; + return NO_FSTAB; + } + + // Generate the list of supported overlayfs mount points. + auto overlayfs_candidates = fs_mgr_overlayfs_candidate_list(fstab); + + // Generate the all remountable partitions sub-list + android::fs_mgr::Fstab partitions; + for (auto const& entry : fstab) { + if (!remountable_partition(entry)) continue; + if (overlayfs_candidates.empty() || + GetEntryForMountPoint(&overlayfs_candidates, entry.mount_point) || + (is_wrapped(overlayfs_candidates, entry) == nullptr)) { + partitions.emplace_back(entry); + } + } + + // Check verity and optionally setup overlayfs backing. + for (auto it = partitions.begin(); it != partitions.end();) { + auto& entry = *it; + auto& mount_point = entry.mount_point; + if (fs_mgr_is_verity_enabled(entry)) { + LOG(ERROR) << "Verity enabled on " << mount_point << ", skipping"; + retval = VERITY_PARTITION; + it = partitions.erase(it); + continue; + } + + auto change = false; + errno = 0; + if (fs_mgr_overlayfs_setup(nullptr, mount_point.c_str(), &change)) { + if (change) { + LOG(INFO) << "Using overlayfs for " << mount_point; + } + } else if (errno) { + PLOG(ERROR) << "Overlayfs setup for " << mount_point << " failed, skipping"; + retval = BAD_OVERLAY; + it = partitions.erase(it); + continue; + } + ++it; + } + + if (partitions.empty()) { + LOG(WARNING) << "No partitions to remount"; + return retval; + } + + // Mount overlayfs. + if (!fs_mgr_overlayfs_mount_all(&partitions)) { + retval = BAD_OVERLAY; + PLOG(ERROR) << "Can not mount overlayfs for partitions"; + } + + // Get actual mounts _after_ overlayfs has been added. + android::fs_mgr::Fstab mounts; + if (!android::fs_mgr::ReadFstabFromFile("/proc/mounts", &mounts) || mounts.empty()) { + PLOG(ERROR) << "Failed to read /proc/mounts"; + retval = NO_MOUNTS; + } + + // Remount selected partitions. + for (auto& entry : partitions) { + // unlock the r/o key for the mount point device + if (entry.fs_mgr_flags.logical) { + fs_mgr_update_logical_partition(&entry); + } + auto blk_device = entry.blk_device; + auto mount_point = entry.mount_point; + + for (auto it = mounts.rbegin(); it != mounts.rend(); ++it) { + auto& rentry = *it; + if (mount_point == rentry.mount_point) { + blk_device = rentry.blk_device; + break; + } + if ((mount_point == "/") && (rentry.mount_point == "/system")) { + if (blk_device != "/dev/root") blk_device = rentry.blk_device; + mount_point = "/system"; + break; + } + } + fs_mgr_set_blk_ro(blk_device, false); + + // Now remount! + if (::mount(blk_device.c_str(), mount_point.c_str(), entry.fs_type.c_str(), MS_REMOUNT, + nullptr) == 0) { + continue; + } + if ((errno == EINVAL) && (mount_point != entry.mount_point)) { + mount_point = entry.mount_point; + if (::mount(blk_device.c_str(), mount_point.c_str(), entry.fs_type.c_str(), MS_REMOUNT, + nullptr) == 0) { + continue; + } + } + // If errno = EROFS at this point, we are dealing with r/o + // filesystem types like squashfs, erofs or ext4 dedupe. We will + // consider such a device that does not have CONFIG_OVERLAY_FS + // in the kernel as a misconfigured and take no action. + // + // ext4 dedupe _can_ be worked around by performing a reboot into + // recovery and fsck'ing. However the current decision is to not + // reboot to reserve only one shell command to do so (reboot). In + // the future, if this is a problem, a -R flag could be introduced + // to give permission to do so and as a convenience also implement + // verity disable operations. We will require this functionality + // in order for adb remount to call this executable instead of its + // current internal code that recognizes the -R flag and logistics. + PLOG(ERROR) << "failed to remount partition dev:" << blk_device << " mnt:" << mount_point; + retval = REMOUNT_FAILED; + } + + try_unmount_bionic(&mounts); + + return retval; +} diff --git a/fs_mgr/tests/adb-remount-test.sh b/fs_mgr/tests/adb-remount-test.sh index bbe80abd7..19562151d 100755 --- a/fs_mgr/tests/adb-remount-test.sh +++ b/fs_mgr/tests/adb-remount-test.sh @@ -1140,6 +1140,36 @@ adb_sh grep " /vendor .* rw," /proc/mounts >/dev/null &2 +# Prerequisite is a prepped device from above. +adb_reboot && + adb_wait 2m || + die "lost device after reboot to ro state (USB stack broken?)" +adb_sh grep " /vendor .* rw," /proc/mounts >/dev/null /dev/null &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 + adb_su rm -rf /${d}/overlay /dev/null /dev/null &2 + restore err=${?} From 75941f2c9c68c0e815cd48afe37eec271eb88d80 Mon Sep 17 00:00:00 2001 From: Mark Salyzyn Date: Tue, 12 Feb 2019 15:26:34 -0800 Subject: [PATCH 3/6] fs_mgr: remount: log to stderr and logd Logging normally goes to stderr, also send output redirected to the Android Logger. Required if command is exec'd, useful if commanded from shell. Test: manual confirmation of both outputs. Bug: 122602260 Change-Id: Ibc2e14bd4fad561514c0c33741da8ca6f00af3f3 --- fs_mgr/fs_mgr_remount.cpp | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/fs_mgr/fs_mgr_remount.cpp b/fs_mgr/fs_mgr_remount.cpp index 66756ab6f..1cec54327 100644 --- a/fs_mgr/fs_mgr_remount.cpp +++ b/fs_mgr/fs_mgr_remount.cpp @@ -96,14 +96,17 @@ void try_unmount_bionic(android::fs_mgr::Fstab* mounts) { } } -void MyLogger(android::base::LogId, android::base::LogSeverity severity, const char*, const char*, - unsigned int, const char* message) { +void MyLogger(android::base::LogId id, android::base::LogSeverity severity, const char* tag, + const char* file, unsigned int line, const char* message) { static const char log_characters[] = "VD\0WEFF"; if (severity < sizeof(log_characters)) { auto severity_char = log_characters[severity]; if (severity_char) fprintf(stderr, "%c ", severity_char); } fprintf(stderr, "%s\n", message); + + static auto logd = android::base::LogdLogger(); + logd(id, severity, tag, file, line, message); } } // namespace From 8c07db6a6b619965abae3485df5443022f4f9d95 Mon Sep 17 00:00:00 2001 From: Mark Salyzyn Date: Thu, 24 Jan 2019 11:08:10 -0800 Subject: [PATCH 4/6] fs_mgr: remount: add -T fstab argument Allow selection of an fstab file. This is to mirror the built-in functionality associated with init mount_all command. Test: adb-remount-test.sh Bug: 122602260 Change-Id: I5fc2f3707c1dafd687c826eaccbaab03a408035b --- fs_mgr/fs_mgr_remount.cpp | 25 +++++++++++++++++++++---- 1 file changed, 21 insertions(+), 4 deletions(-) diff --git a/fs_mgr/fs_mgr_remount.cpp b/fs_mgr/fs_mgr_remount.cpp index 1cec54327..6e9ab2a77 100644 --- a/fs_mgr/fs_mgr_remount.cpp +++ b/fs_mgr/fs_mgr_remount.cpp @@ -38,8 +38,9 @@ namespace { [[noreturn]] void usage(int exit_status) { LOG(INFO) << getprogname() - << " [-h]\n" + << " [-h] [-T fstab_file]\n" "\t-h --help\tthis help\n" + "\t-T --fstab\tcustom fstab file location\n" "\n" "Remount all partitions read-write.\n" "Verity must be disabled."; @@ -134,12 +135,22 @@ int main(int argc, char* argv[]) { return NOT_USERDEBUG; } + const char* fstab_file = nullptr; + struct option longopts[] = { + {"fstab", required_argument, nullptr, 'T'}, {"help", no_argument, nullptr, 'h'}, {0, 0, nullptr, 0}, }; - for (int opt; (opt = ::getopt_long(argc, argv, "h", longopts, nullptr)) != -1;) { + for (int opt; (opt = ::getopt_long(argc, argv, "hT:", longopts, nullptr)) != -1;) { switch (opt) { + case 'T': + if (fstab_file) { + LOG(ERROR) << "Cannot supply two fstabs: -T " << fstab_file << " -T" << optarg; + usage(BADARG); + } + fstab_file = optarg; + break; default: LOG(ERROR) << "Bad Argument -" << char(opt); usage(BADARG); @@ -161,10 +172,16 @@ int main(int argc, char* argv[]) { return NOT_ROOT; } + // Read the selected fstab. android::fs_mgr::Fstab fstab; - auto fstab_read = android::fs_mgr::ReadDefaultFstab(&fstab); + auto fstab_read = false; + if (fstab_file) { + fstab_read = android::fs_mgr::ReadFstabFromFile(fstab_file, &fstab); + } else { + fstab_read = android::fs_mgr::ReadDefaultFstab(&fstab); + } if (!fstab_read || fstab.empty()) { - PLOG(ERROR) << "Failed to read default fstab"; + PLOG(ERROR) << "Failed to read fstab"; return NO_FSTAB; } From 1188ce4e1dd4355a340ef7d00e105fb3d7475909 Mon Sep 17 00:00:00 2001 From: Mark Salyzyn Date: Fri, 25 Jan 2019 11:05:40 -0800 Subject: [PATCH 5/6] fs_mgr: remount: add -R argument The -R flag tells remount it can reboot to disable verity or to run fsck on an ext4 deduped filesystem, or both. Testing may include a manual component because adb-remount-test.sh needs to run from a device in an enable-verity state to test this. Only recognizes chained avb. Test: adb-remount-test.sh Bug: 122602260 Change-Id: I6ce4372532d9b933dcca9e2bec544d525b76c4d9 --- fs_mgr/Android.bp | 7 ++++ fs_mgr/fs_mgr_remount.cpp | 72 +++++++++++++++++++++++++------- fs_mgr/tests/adb-remount-test.sh | 30 +++++++++++++ 3 files changed, 94 insertions(+), 15 deletions(-) diff --git a/fs_mgr/Android.bp b/fs_mgr/Android.bp index a476cd85d..0a048f9c0 100644 --- a/fs_mgr/Android.bp +++ b/fs_mgr/Android.bp @@ -115,10 +115,17 @@ cc_library_static { cc_binary { name: "remount", defaults: ["fs_mgr_defaults"], + static_libs: [ + "libavb_user", + ], shared_libs: [ + "libbootloader_message", "libbase", "libfs_mgr", ], + header_libs: [ + "libcutils_headers", + ], srcs: [ "fs_mgr_remount.cpp", ], diff --git a/fs_mgr/fs_mgr_remount.cpp b/fs_mgr/fs_mgr_remount.cpp index 6e9ab2a77..105d18c2f 100644 --- a/fs_mgr/fs_mgr_remount.cpp +++ b/fs_mgr/fs_mgr_remount.cpp @@ -16,6 +16,7 @@ #include #include +#include #include #include #include @@ -30,6 +31,8 @@ #include #include #include +#include +#include #include #include #include @@ -38,12 +41,13 @@ namespace { [[noreturn]] void usage(int exit_status) { LOG(INFO) << getprogname() - << " [-h] [-T fstab_file]\n" + << " [-h] [-R] [-T fstab_file]\n" "\t-h --help\tthis help\n" + "\t-R --reboot\tdisable verity & reboot to facilitate remount\n" "\t-T --fstab\tcustom fstab file location\n" "\n" "Remount all partitions read-write.\n" - "Verity must be disabled."; + "-R notwithstanding, verity must be disabled."; ::exit(exit_status); } @@ -110,6 +114,18 @@ void MyLogger(android::base::LogId id, android::base::LogSeverity severity, cons logd(id, severity, tag, file, line, message); } +[[noreturn]] void reboot(bool dedupe) { + if (dedupe) { + LOG(INFO) << "The device will now reboot to recovery and attempt un-deduplication."; + } else { + LOG(INFO) << "Successfully disabled verity\nrebooting device"; + } + ::sync(); + android::base::SetProperty(ANDROID_RB_PROPERTY, dedupe ? "reboot,recovery" : "reboot,remount"); + ::sleep(60); + ::exit(0); // SUCCESS +} + } // namespace int main(int argc, char* argv[]) { @@ -136,14 +152,19 @@ int main(int argc, char* argv[]) { } const char* fstab_file = nullptr; + auto can_reboot = false; struct option longopts[] = { {"fstab", required_argument, nullptr, 'T'}, {"help", no_argument, nullptr, 'h'}, + {"reboot", no_argument, nullptr, 'R'}, {0, 0, nullptr, 0}, }; - for (int opt; (opt = ::getopt_long(argc, argv, "hT:", longopts, nullptr)) != -1;) { + for (int opt; (opt = ::getopt_long(argc, argv, "hRT:", longopts, nullptr)) != -1;) { switch (opt) { + case 'R': + can_reboot = true; + break; case 'T': if (fstab_file) { LOG(ERROR) << "Cannot supply two fstabs: -T " << fstab_file << " -T" << optarg; @@ -200,11 +221,32 @@ int main(int argc, char* argv[]) { } // Check verity and optionally setup overlayfs backing. + auto reboot_later = false; for (auto it = partitions.begin(); it != partitions.end();) { auto& entry = *it; auto& mount_point = entry.mount_point; if (fs_mgr_is_verity_enabled(entry)) { - LOG(ERROR) << "Verity enabled on " << mount_point << ", skipping"; + LOG(WARNING) << "Verity enabled on " << mount_point; + if (can_reboot && + (android::base::GetProperty("ro.boot.vbmeta.devices_state", "") != "locked")) { + if (AvbOps* ops = avb_ops_user_new()) { + auto ret = avb_user_verity_set( + ops, android::base::GetProperty("ro.boot.slot_suffix", "").c_str(), + false); + avb_ops_user_free(ops); + if (ret) { + if (fs_mgr_overlayfs_valid() == OverlayfsValidResult::kNotSupported) { + retval = VERITY_PARTITION; + // w/o overlayfs available, also check for dedupe + reboot_later = true; + ++it; + continue; + } + reboot(false); + } + } + } + LOG(ERROR) << "Skipping " << mount_point; retval = VERITY_PARTITION; it = partitions.erase(it); continue; @@ -278,23 +320,23 @@ int main(int argc, char* argv[]) { continue; } } + PLOG(WARNING) << "failed to remount partition dev:" << blk_device << " mnt:" << mount_point; // If errno = EROFS at this point, we are dealing with r/o // filesystem types like squashfs, erofs or ext4 dedupe. We will // consider such a device that does not have CONFIG_OVERLAY_FS - // in the kernel as a misconfigured and take no action. - // - // ext4 dedupe _can_ be worked around by performing a reboot into - // recovery and fsck'ing. However the current decision is to not - // reboot to reserve only one shell command to do so (reboot). In - // the future, if this is a problem, a -R flag could be introduced - // to give permission to do so and as a convenience also implement - // verity disable operations. We will require this functionality - // in order for adb remount to call this executable instead of its - // current internal code that recognizes the -R flag and logistics. - PLOG(ERROR) << "failed to remount partition dev:" << blk_device << " mnt:" << mount_point; + // in the kernel as a misconfigured; except for ext4 dedupe. + if ((errno == EROFS) && can_reboot) { + const std::vector msg = {"--fsck_unshare_blocks"}; + std::string err; + if (write_bootloader_message(msg, &err)) reboot(true); + LOG(ERROR) << "Failed to set bootloader message: " << err; + errno = EROFS; + } retval = REMOUNT_FAILED; } + if (reboot_later) reboot(false); + try_unmount_bionic(&mounts); return retval; diff --git a/fs_mgr/tests/adb-remount-test.sh b/fs_mgr/tests/adb-remount-test.sh index 19562151d..1ded95441 100755 --- a/fs_mgr/tests/adb-remount-test.sh +++ b/fs_mgr/tests/adb-remount-test.sh @@ -617,6 +617,19 @@ if [ "orange" = "`get_property ro.boot.verifiedbootstate`" -a \ adb_reboot && adb_wait 2m } + + echo "${GREEN}[ RUN ]${NORMAL} Testing adb shell su root remount -R command" >&2 + + adb_su remount -R &2 fi echo "${GREEN}[ RUN ]${NORMAL} Testing kernel support for overlayfs" >&2 @@ -1173,6 +1186,23 @@ echo "${GREEN}[ OK ]${NORMAL} remount command works from scratch" >&2 restore err=${?} +if [ ${err} = 0 ] && ${overlayfs_supported}; then + echo "${GREEN}[ RUN ]${NORMAL} test 'adb remount -R'" >&2 + adb_root && + adb remount -R && + adb_wait 2m || + die "adb remount -R" + if [ "orange" != "`get_property ro.boot.verifiedbootstate`" -o \ + "2" = "`get_property partition.system.verified`" ]; then + die "remount -R command failed to disable verity" + fi + + echo "${GREEN}[ OK ]${NORMAL} 'adb remount -R' command" >&2 + + restore + err=${?} +fi + restore() { true } From 4469fb1c06a506c0f09890faa63203a70462d257 Mon Sep 17 00:00:00 2001 From: Mark Salyzyn Date: Wed, 30 Jan 2019 10:19:15 -0800 Subject: [PATCH 6/6] fs_mgr_remount: support legacy devices (marlin) Deal with first version of verity for legacy products. Test: system/core/fs_mgr/tests/adb-remount-test.sh Bug: 120448575 Bug: 123079041 Change-Id: I7a2dd8309cbb19751fdbb05d4efc30c486615e04 --- fs_mgr/Android.bp | 2 ++ fs_mgr/fs_mgr_remount.cpp | 5 +++++ 2 files changed, 7 insertions(+) diff --git a/fs_mgr/Android.bp b/fs_mgr/Android.bp index 0a048f9c0..4ee962442 100644 --- a/fs_mgr/Android.bp +++ b/fs_mgr/Android.bp @@ -121,6 +121,8 @@ cc_binary { shared_libs: [ "libbootloader_message", "libbase", + "libcrypto", + "libfec", "libfs_mgr", ], header_libs: [ diff --git a/fs_mgr/fs_mgr_remount.cpp b/fs_mgr/fs_mgr_remount.cpp index 105d18c2f..c0e0ccd8f 100644 --- a/fs_mgr/fs_mgr_remount.cpp +++ b/fs_mgr/fs_mgr_remount.cpp @@ -33,6 +33,7 @@ #include #include #include +#include #include #include #include @@ -243,6 +244,9 @@ int main(int argc, char* argv[]) { continue; } reboot(false); + } else if (fs_mgr_set_blk_ro(entry.blk_device, false)) { + fec::io fh(entry.blk_device.c_str(), O_RDWR); + if (fh && fh.set_verity_status(false)) reboot_later = true; } } } @@ -268,6 +272,7 @@ int main(int argc, char* argv[]) { } if (partitions.empty()) { + if (reboot_later) reboot(false); LOG(WARNING) << "No partitions to remount"; return retval; }