Reland: "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. Change when relanding: the original change skipped the move mount from /first_stage_ramdisk to / and only did a chroot instead. This was a mistake that resulted in the subsequent move mount of /system to / to mount over the '/' directory instead of moving that mount. This change uses a bind mount of /first_stage_ramdisk to itself instead of skipping the first move mount. Bug: 114062208 Test: AVB works with blueline_mainline Change-Id: I65207edfe98531892da2eafcbff19b438c9c64fe
This commit is contained in:
parent
223114008c
commit
866c08c0ac
3 changed files with 91 additions and 71 deletions
|
@ -17,6 +17,7 @@
|
|||
#include "first_stage_mount.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <sys/mount.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <chrono>
|
||||
|
@ -123,18 +124,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
|
||||
|
@ -403,11 +394,6 @@ 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;
|
||||
}
|
||||
|
|
|
@ -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,41 @@ 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() must be called with a mount point as the target, so we bind mount the
|
||||
// target directory to itself here.
|
||||
if (mount("/first_stage_ramdisk", "/first_stage_ramdisk", nullptr, MS_BIND, nullptr) != 0) {
|
||||
LOG(FATAL) << "Could not bind mount /first_stage_ramdisk to itself";
|
||||
}
|
||||
SwitchRoot("/first_stage_ramdisk");
|
||||
}
|
||||
|
||||
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;
|
||||
|
|
|
@ -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};
|
||||
|
@ -112,24 +72,16 @@ std::vector<std::string> GetMounts(const std::string& new_root) {
|
|||
void SwitchRoot(const std::string& new_root) {
|
||||
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 << "'";
|
||||
}
|
||||
|
@ -141,10 +93,6 @@ void SwitchRoot(const std::string& 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
|
||||
|
|
Loading…
Reference in a new issue