diff --git a/init/first_stage_mount.cpp b/init/first_stage_mount.cpp index 13a9d0809..a8b786260 100644 --- a/init/first_stage_mount.cpp +++ b/init/first_stage_mount.cpp @@ -17,6 +17,7 @@ #include "first_stage_mount.h" #include +#include #include #include @@ -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; } diff --git a/init/init_first_stage.cpp b/init/init_first_stage.cpp index d81ca5c35..c2c686885 100644 --- a/init/init_first_stage.cpp +++ b/init/init_first_stage.cpp @@ -14,6 +14,8 @@ * limitations under the License. */ +#include +#include #include #include #include @@ -26,19 +28,72 @@ #include #include +#include #include #include #include #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{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{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; diff --git a/init/switch_root.cpp b/init/switch_root.cpp index 0e59b576b..575b67f38 100644 --- a/init/switch_root.cpp +++ b/init/switch_root.cpp @@ -16,7 +16,6 @@ #include "switch_root.h" -#include #include #include #include @@ -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{fdopendir(fd), closedir}; - if (subdir) { - FreeRamdisk(subdir.get(), dev); - } else { - close(fd); - } - } - } - } - unlinkat(dfd, de->d_name, is_dir ? AT_REMOVEDIR : 0); - } -} - std::vector GetMounts(const std::string& new_root) { auto fp = std::unique_ptr{setmntent("/proc/mounts", "re"), endmntent}; @@ -112,24 +72,16 @@ std::vector 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{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