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:
parent
166ae693d4
commit
56999b41af
4 changed files with 93 additions and 76 deletions
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in a new issue