init: chroot from recovery to /first_stage_ramdisk

When using the recovery image as a trampoline to boot the system,
first chroot from the recovery image to /first_stage_ramdisk, to
minimize differences between these two boot paths.

Primary motivation is due to the fact that the basename of each mount
point is used by device-manager to name its nodes, and the previous
code that created used /system_recovery_mount as the mount point for
system.img broke AVB.  Instead of hacking around that issue, this
change unified mounting for the recovery trampoline and true first
stage ramdisk paths.

Bug: 114062208
Test: AVB works with blueline_mainline
Change-Id: Iffb154962b6e160150917e068f1e7d0bf7cb84e7
This commit is contained in:
Tom Cherry 2018-11-06 10:32:53 -08:00
parent 166ae693d4
commit 56999b41af
4 changed files with 93 additions and 76 deletions

View file

@ -17,6 +17,7 @@
#include "first_stage_mount.h"
#include <stdlib.h>
#include <sys/mount.h>
#include <unistd.h>
#include <chrono>
@ -122,18 +123,8 @@ static inline bool IsDtVbmetaCompatible() {
return is_android_dt_value_expected("vbmeta/compatible", "android,vbmeta");
}
static bool ForceNormalBoot() {
static bool force_normal_boot = []() {
std::string cmdline;
android::base::ReadFileToString("/proc/cmdline", &cmdline);
return cmdline.find("androidboot.force_normal_boot=1") != std::string::npos;
}();
return force_normal_boot;
}
static bool IsRecoveryMode() {
return !ForceNormalBoot() && access("/system/bin/recovery", F_OK) == 0;
return access("/system/bin/recovery", F_OK) == 0;
}
// Class Definitions
@ -371,16 +362,11 @@ bool FirstStageMount::MountPartitions() {
[](const auto& rec) { return rec->mount_point == "/system"s; });
if (system_partition != mount_fstab_recs_.end()) {
if (ForceNormalBoot()) {
free((*system_partition)->mount_point);
(*system_partition)->mount_point = strdup("/system_recovery_mount");
}
if (!MountPartition(*system_partition)) {
return false;
}
SwitchRoot((*system_partition)->mount_point);
SwitchRoot((*system_partition)->mount_point, true);
mount_fstab_recs_.erase(system_partition);
}

View file

@ -14,6 +14,8 @@
* limitations under the License.
*/
#include <dirent.h>
#include <fcntl.h>
#include <paths.h>
#include <stdlib.h>
#include <sys/mount.h>
@ -26,19 +28,72 @@
#include <vector>
#include <android-base/chrono_utils.h>
#include <android-base/file.h>
#include <android-base/logging.h>
#include <cutils/android_reboot.h>
#include <private/android_filesystem_config.h>
#include "first_stage_mount.h"
#include "reboot_utils.h"
#include "switch_root.h"
#include "util.h"
using android::base::boot_clock;
using namespace std::literals;
namespace android {
namespace init {
namespace {
void FreeRamdisk(DIR* dir, dev_t dev) {
int dfd = dirfd(dir);
dirent* de;
while ((de = readdir(dir)) != nullptr) {
if (de->d_name == "."s || de->d_name == ".."s) {
continue;
}
bool is_dir = false;
if (de->d_type == DT_DIR || de->d_type == DT_UNKNOWN) {
struct stat info;
if (fstatat(dfd, de->d_name, &info, AT_SYMLINK_NOFOLLOW) != 0) {
continue;
}
if (info.st_dev != dev) {
continue;
}
if (S_ISDIR(info.st_mode)) {
is_dir = true;
auto fd = openat(dfd, de->d_name, O_RDONLY | O_DIRECTORY);
if (fd >= 0) {
auto subdir =
std::unique_ptr<DIR, decltype(&closedir)>{fdopendir(fd), closedir};
if (subdir) {
FreeRamdisk(subdir.get(), dev);
} else {
close(fd);
}
}
}
}
unlinkat(dfd, de->d_name, is_dir ? AT_REMOVEDIR : 0);
}
}
bool ForceNormalBoot() {
std::string cmdline;
android::base::ReadFileToString("/proc/cmdline", &cmdline);
return cmdline.find("androidboot.force_normal_boot=1") != std::string::npos;
}
} // namespace
int main(int argc, char** argv) {
if (REBOOT_BOOTLOADER_ON_PANIC) {
InstallRebootSignalHandlers();
@ -117,10 +172,36 @@ int main(int argc, char** argv) {
LOG(INFO) << "init first stage started!";
auto old_root_dir = std::unique_ptr<DIR, decltype(&closedir)>{opendir("/"), closedir};
if (!old_root_dir) {
PLOG(ERROR) << "Could not opendir(\"/\"), not freeing ramdisk";
}
struct stat old_root_info;
if (stat("/", &old_root_info) != 0) {
PLOG(ERROR) << "Could not stat(\"/\"), not freeing ramdisk";
old_root_dir.reset();
}
if (ForceNormalBoot()) {
mkdir("/first_stage_ramdisk", 0755);
SwitchRoot("/first_stage_ramdisk", false);
}
if (!DoFirstStageMount()) {
LOG(FATAL) << "Failed to mount required partitions early ...";
}
struct stat new_root_info;
if (stat("/", &new_root_info) != 0) {
PLOG(ERROR) << "Could not stat(\"/\"), not freeing ramdisk";
old_root_dir.reset();
}
if (old_root_dir && old_root_info.st_dev != new_root_info.st_dev) {
FreeRamdisk(old_root_dir.get(), old_root_info.st_dev);
}
SetInitAvbVersionInRecovery();
static constexpr uint32_t kNanosecondsPerMillisecond = 1e6;

View file

@ -16,7 +16,6 @@
#include "switch_root.h"
#include <dirent.h>
#include <fcntl.h>
#include <mntent.h>
#include <sys/mount.h>
@ -35,45 +34,6 @@ namespace init {
namespace {
void FreeRamdisk(DIR* dir, dev_t dev) {
int dfd = dirfd(dir);
dirent* de;
while ((de = readdir(dir)) != nullptr) {
if (de->d_name == "."s || de->d_name == ".."s) {
continue;
}
bool is_dir = false;
if (de->d_type == DT_DIR || de->d_type == DT_UNKNOWN) {
struct stat info;
if (fstatat(dfd, de->d_name, &info, AT_SYMLINK_NOFOLLOW) != 0) {
continue;
}
if (info.st_dev != dev) {
continue;
}
if (S_ISDIR(info.st_mode)) {
is_dir = true;
auto fd = openat(dfd, de->d_name, O_RDONLY | O_DIRECTORY);
if (fd >= 0) {
auto subdir =
std::unique_ptr<DIR, decltype(&closedir)>{fdopendir(fd), closedir};
if (subdir) {
FreeRamdisk(subdir.get(), dev);
} else {
close(fd);
}
}
}
}
unlinkat(dfd, de->d_name, is_dir ? AT_REMOVEDIR : 0);
}
}
std::vector<std::string> GetMounts(const std::string& new_root) {
auto fp = std::unique_ptr<std::FILE, decltype(&endmntent)>{setmntent("/proc/mounts", "re"),
endmntent};
@ -109,42 +69,32 @@ std::vector<std::string> GetMounts(const std::string& new_root) {
} // namespace
void SwitchRoot(const std::string& new_root) {
void SwitchRoot(const std::string& new_root, bool move_root_mount) {
auto mounts = GetMounts(new_root);
LOG(INFO) << "Switching root to '" << new_root << "'";
for (const auto& mount_path : mounts) {
auto new_mount_path = new_root + mount_path;
mkdir(new_mount_path.c_str(), 0755);
if (mount(mount_path.c_str(), new_mount_path.c_str(), nullptr, MS_MOVE, nullptr) != 0) {
PLOG(FATAL) << "Unable to move mount at '" << mount_path << "'";
}
}
auto old_root_dir = std::unique_ptr<DIR, decltype(&closedir)>{opendir("/"), closedir};
if (!old_root_dir) {
PLOG(ERROR) << "Could not opendir(\"/\"), not freeing ramdisk";
}
struct stat old_root_info;
if (stat("/", &old_root_info) != 0) {
PLOG(ERROR) << "Could not stat(\"/\"), not freeing ramdisk";
old_root_dir.reset();
}
if (chdir(new_root.c_str()) != 0) {
PLOG(FATAL) << "Could not chdir to new_root, '" << new_root << "'";
}
if (mount(new_root.c_str(), "/", nullptr, MS_MOVE, nullptr) != 0) {
PLOG(FATAL) << "Unable to move root mount to new_root, '" << new_root << "'";
if (move_root_mount) {
if (mount(new_root.c_str(), "/", nullptr, MS_MOVE, nullptr) != 0) {
PLOG(FATAL) << "Unable to move root mount to new_root, '" << new_root << "'";
}
}
if (chroot(".") != 0) {
PLOG(FATAL) << "Unable to chroot to new root";
}
if (old_root_dir) {
FreeRamdisk(old_root_dir.get(), old_root_info.st_dev);
}
}
} // namespace init

View file

@ -21,7 +21,7 @@
namespace android {
namespace init {
void SwitchRoot(const std::string& new_root);
void SwitchRoot(const std::string& new_root, bool move_root_mount);
} // namespace init
} // namespace android