Stop using polymorphism in magiskinit

This commit is contained in:
topjohnwu 2024-12-03 02:17:57 -08:00
parent c8e9ce7627
commit 3c6889505b
7 changed files with 125 additions and 187 deletions

View File

@ -11,8 +11,6 @@
using namespace std;
vector<string> mount_list;
template<char... cs> using chars = integer_sequence<char, cs...>;
// If quoted, parsing ends when we find char in [breaks]
@ -180,22 +178,7 @@ if (access(file_name, R_OK) == 0) { \
} \
}
BootConfig::BootConfig() {
// Get kernel data using procfs and sysfs
if (access("/proc/cmdline", F_OK) != 0) {
xmkdir("/proc", 0755);
xmount("proc", "/proc", "proc", 0, nullptr);
mount_list.emplace_back("/proc");
}
if (access("/sys/block", F_OK) != 0) {
xmkdir("/sys", 0755);
xmount("sysfs", "/sys", "sysfs", 0, nullptr);
mount_list.emplace_back("/sys");
}
// Log to kernel
rust::setup_klog();
void BootConfig::init() {
set(parse_cmdline(full_read("/proc/cmdline")));
set(parse_bootconfig(full_read("/proc/bootconfig")));
@ -234,7 +217,7 @@ bool check_two_stage() {
return init.contains("selinux_setup");
}
void unxz_init(const char *init_xz, const char *init) {
static void unxz_init(const char *init_xz, const char *init) {
LOGD("unxz %s -> %s\n", init_xz, init);
int fd = xopen(init, O_WRONLY | O_CREAT, 0777);
fd_stream ch(fd);

View File

@ -60,16 +60,67 @@ void restore_ramdisk_init() {
}
}
class RecoveryInit : public BaseInit {
public:
using BaseInit::BaseInit;
void start() override {
LOGD("Ramdisk is recovery, abort\n");
restore_ramdisk_init();
rm_rf("/.backup");
exec_init();
MagiskInit::MagiskInit(char **argv) : argv(argv), config{} {
// Get kernel data using procfs and sysfs
if (access("/proc/cmdline", F_OK) != 0) {
xmkdir("/proc", 0755);
xmount("proc", "/proc", "proc", 0, nullptr);
mount_list.emplace_back("/proc");
}
};
if (access("/sys/block", F_OK) != 0) {
xmkdir("/sys", 0755);
xmount("sysfs", "/sys", "sysfs", 0, nullptr);
mount_list.emplace_back("/sys");
}
// Log to kernel
rust::setup_klog();
// Load kernel configs
config.init();
}
static void recovery() {
LOGI("Ramdisk is recovery, abort\n");
restore_ramdisk_init();
rm_rf("/.backup");
}
void MagiskInit::legacy_system_as_root() {
LOGI("Legacy SAR Init\n");
prepare_data();
bool is_two_stage = mount_system_root();
if (is_two_stage)
redirect_second_stage();
else
patch_ro_root();
}
void MagiskInit::rootfs() {
LOGI("RootFS Init\n");
prepare_data();
LOGD("Restoring /init\n");
rename(backup_init(), "/init");
patch_rw_root();
}
void MagiskInit::start() {
if (argv[1] != nullptr && argv[1] == "selinux_setup"sv)
second_stage();
else if (config.skip_initramfs)
legacy_system_as_root();
else if (config.force_normal_boot)
first_stage();
else if (access("/sbin/recovery", F_OK) == 0 || access("/system/bin/recovery", F_OK) == 0)
recovery();
else if (check_two_stage())
first_stage();
else
rootfs();
// Finally execute the original init
exec_init();
}
int main(int argc, char *argv[]) {
umask(0);
@ -81,23 +132,6 @@ int main(int argc, char *argv[]) {
if (getpid() != 1)
return 1;
BaseInit *init;
BootConfig config;
if (argc > 1 && argv[1] == "selinux_setup"sv)
init = new SecondStageInit(argv, &config);
else if (config.skip_initramfs)
init = new LegacySARInit(argv, &config);
else if (config.force_normal_boot)
init = new FirstStageInit(argv, &config);
else if (access("/sbin/recovery", F_OK) == 0 || access("/system/bin/recovery", F_OK) == 0)
init = new RecoveryInit(argv, &config);
else if (check_two_stage())
init = new FirstStageInit(argv, &config);
else
init = new RootFSInit(argv, &config);
// Run the main routine
init->start();
exit(1);
MagiskInit init(argv);
init.start();
}

View File

@ -6,18 +6,18 @@
using kv_pairs = std::vector<std::pair<std::string, std::string>>;
struct BootConfig {
bool skip_initramfs = false;
bool force_normal_boot = false;
bool rootwait = false;
bool emulator = false;
char slot[3]{};
char dt_dir[64]{};
char fstab_suffix[32]{};
char hardware[32]{};
char hardware_plat[32]{};
bool skip_initramfs;
bool force_normal_boot;
bool rootwait;
bool emulator;
char slot[3];
char dt_dir[64];
char fstab_suffix[32];
char hardware[32];
char hardware_plat[32];
kv_pairs partition_map;
BootConfig();
void init();
private:
void set(const kv_pairs &);
void print();
@ -27,121 +27,45 @@ private:
#define INIT_PATH "/system/bin/init"
#define REDIR_PATH "/data/magiskinit"
extern std::vector<std::string> mount_list;
int magisk_proxy_main(int argc, char *argv[]);
bool unxz(out_stream &strm, rust::Slice<const uint8_t> bytes);
bool check_two_stage();
const char *backup_init();
void restore_ramdisk_init();
/***************
* Base classes
***************/
class BaseInit {
protected:
BootConfig *config;
char **argv;
[[noreturn]] void exec_init();
void prepare_data();
dev_t find_block(const char *partname);
void collect_devices();
public:
BaseInit(char *argv[], BootConfig *config) : config(config), argv(argv) {}
virtual ~BaseInit() = default;
virtual void start() = 0;
};
class MagiskInit : public BaseInit {
class MagiskInit {
private:
std::string preinit_dev;
std::vector<std::string> mount_list;
char **argv;
BootConfig config;
void parse_config_file();
void patch_sepolicy(const char *in, const char *out);
bool hijack_sepolicy();
// Setup mounts and environment
void setup_tmp(const char *path);
void collect_devices();
void mount_preinit_dir();
protected:
void prepare_data();
dev_t find_block(const char *partname);
bool mount_system_root();
// Setup and patch root directory
void parse_config_file();
void patch_rw_root();
void patch_ro_root();
// Two stage init
void redirect_second_stage();
void first_stage();
void second_stage();
// SELinux
void patch_sepolicy(const char *in, const char *out);
bool hijack_sepolicy();
[[noreturn]] void exec_init();
void legacy_system_as_root();
void rootfs();
public:
using BaseInit::BaseInit;
};
/***************
* 2 Stage Init
***************/
class FirstStageInit : public BaseInit {
private:
void prepare();
public:
FirstStageInit(char *argv[], BootConfig *config) : BaseInit(argv, config) {
LOGD("%s\n", __FUNCTION__);
};
void start() override {
prepare();
exec_init();
}
};
class SecondStageInit : public MagiskInit {
private:
bool prepare();
public:
SecondStageInit(char *argv[], BootConfig *config) : MagiskInit(argv, config) {
LOGD("%s\n", __FUNCTION__);
};
void start() override {
bool is_rootfs = prepare();
if (is_rootfs)
patch_rw_root();
else
patch_ro_root();
exec_init();
}
};
/*************
* Legacy SAR
*************/
class LegacySARInit : public MagiskInit {
private:
bool mount_system_root();
void first_stage_prep();
public:
LegacySARInit(char *argv[], BootConfig *config) : MagiskInit(argv, config) {
LOGD("%s\n", __FUNCTION__);
};
void start() override {
prepare_data();
bool is_two_stage = mount_system_root();
if (is_two_stage)
first_stage_prep();
else
patch_ro_root();
exec_init();
}
};
/************
* Initramfs
************/
class RootFSInit : public MagiskInit {
private:
void prepare();
public:
RootFSInit(char *argv[], BootConfig *config) : MagiskInit(argv, config) {
LOGD("%s\n", __FUNCTION__);
}
void start() override {
prepare();
patch_rw_root();
exec_init();
}
explicit MagiskInit(char *argv[]);
void start();
};

View File

@ -45,7 +45,7 @@ static void parse_device(devinfo *dev, const char *uevent) {
});
}
void BaseInit::collect_devices() {
void MagiskInit::collect_devices() {
char path[PATH_MAX];
devinfo dev{};
if (auto dir = xopen_dir("/sys/dev/block"); dir) {
@ -59,9 +59,9 @@ void BaseInit::collect_devices() {
auto name = rtrim(full_read(path));
strscpy(dev.dmname, name.data(), sizeof(dev.dmname));
}
if (auto it = std::ranges::find_if(config->partition_map, [&](const auto &i) {
if (auto it = std::ranges::find_if(config.partition_map, [&](const auto &i) {
return i.first == dev.devname;
}); dev.partname[0] == '\0' && it != config->partition_map.end()) {
}); dev.partname[0] == '\0' && it != config.partition_map.end()) {
// use androidboot.partition_map as partname fallback.
strscpy(dev.partname, it->second.data(), sizeof(dev.partname));
}
@ -72,7 +72,7 @@ void BaseInit::collect_devices() {
}
}
dev_t BaseInit::find_block(const char *partname) {
dev_t MagiskInit::find_block(const char *partname) {
if (dev_list.empty())
collect_devices();
@ -143,7 +143,7 @@ void MagiskInit::mount_preinit_dir() {
}
}
bool LegacySARInit::mount_system_root() {
bool MagiskInit::mount_system_root() {
LOGD("Mounting system_root\n");
// there's no /dev in stub cpio
@ -163,13 +163,13 @@ bool LegacySARInit::mount_system_root() {
// Try normal partname
char sys_part[32];
sprintf(sys_part, "system%s", config->slot);
sprintf(sys_part, "system%s", config.slot);
dev = find_block(sys_part);
if (dev > 0)
goto mount_root;
// Poll forever if rootwait was given in cmdline
} while (config->rootwait);
} while (config.rootwait);
// We don't really know what to do at this point...
LOGE("Cannot find root partition, abort\n");
@ -198,7 +198,7 @@ mount_root:
// For API 28 AVD, it uses legacy SAR setup that requires
// special hacks in magiskinit to work properly.
if (!is_two_stage && config->emulator) {
if (!is_two_stage && config.emulator) {
avd_hack = true;
// These values are hardcoded for API 28 AVD
auto vendor_dev = find_block("vendor");
@ -210,7 +210,7 @@ mount_root:
return is_two_stage;
}
void BaseInit::exec_init() {
void MagiskInit::exec_init() {
// Unmount in reverse order
for (auto &p : reversed(mount_list)) {
if (xumount2(p.data(), MNT_DETACH) == 0)
@ -220,12 +220,12 @@ void BaseInit::exec_init() {
exit(1);
}
void BaseInit::prepare_data() {
void MagiskInit::prepare_data() {
LOGD("Setup data tmp\n");
xmkdir("/data", 0755);
xmount("magisk", "/data", "tmpfs", 0, "mode=755");
cp_afc("/init", "/data/magiskinit");
cp_afc("/init", REDIR_PATH);
cp_afc("/.backup", "/data/.backup");
cp_afc("/overlay.d", "/data/overlay.d");
}

View File

@ -347,12 +347,6 @@ void MagiskInit::patch_ro_root() {
chdir("/");
}
void RootFSInit::prepare() {
prepare_data();
LOGD("Restoring /init\n");
rename(backup_init(), "/init");
}
#define PRE_TMPSRC "/magisk"
#define PRE_TMPDIR PRE_TMPSRC "/tmp"

View File

@ -64,7 +64,7 @@ bool MagiskInit::hijack_sepolicy() {
// This only happens on Android 8.0 - 9.0
char buf[4096];
ssprintf(buf, sizeof(buf), "%s/fstab/compatible", config->dt_dir);
ssprintf(buf, sizeof(buf), "%s/fstab/compatible", config.dt_dir);
dt_compat = full_read(buf);
if (dt_compat.empty()) {
// Device does not do early mount and uses monolithic policy
@ -106,7 +106,7 @@ bool MagiskInit::hijack_sepolicy() {
int fd = xopen(MOCK_COMPAT, O_WRONLY);
char buf[4096];
ssprintf(buf, sizeof(buf), "%s/fstab/compatible", config->dt_dir);
ssprintf(buf, sizeof(buf), "%s/fstab/compatible", config.dt_dir);
xumount2(buf, MNT_DETACH);
hijack();

View File

@ -8,12 +8,13 @@
using namespace std;
void FirstStageInit::prepare() {
void MagiskInit::first_stage() {
LOGI("First Stage Init\n");
prepare_data();
if (struct stat st{}; fstatat(-1, "/sdcard", &st, AT_SYMLINK_NOFOLLOW) != 0 &&
fstatat(-1, "/first_stage_ramdisk/sdcard", &st, AT_SYMLINK_NOFOLLOW) != 0) {
if (config->force_normal_boot) {
if (config.force_normal_boot) {
xmkdirs("/first_stage_ramdisk/storage/self", 0755);
xsymlink("/system/system/bin/init", "/first_stage_ramdisk/storage/self/primary");
LOGD("Symlink /first_stage_ramdisk/storage/self/primary -> /system/system/bin/init\n");
@ -29,8 +30,8 @@ void FirstStageInit::prepare() {
LOGD("Bind mount /sdcard -> /sdcard\n");
} else {
// rootfs before 3.12
xmount("/data/magiskinit", "/sdcard", nullptr, MS_BIND, nullptr);
LOGD("Bind mount /sdcard -> /data/magiskinit\n");
xmount(REDIR_PATH, "/sdcard", nullptr, MS_BIND, nullptr);
LOGD("Bind mount " REDIR_PATH " -> /sdcard\n");
}
restore_ramdisk_init();
} else {
@ -44,7 +45,7 @@ void FirstStageInit::prepare() {
}
}
void LegacySARInit::first_stage_prep() {
void MagiskInit::redirect_second_stage() {
// Patch init binary
int src = xopen("/init", O_RDONLY);
int dest = xopen("/data/init", O_CREAT | O_WRONLY, 0);
@ -61,7 +62,8 @@ void LegacySARInit::first_stage_prep() {
xmount("/data/init", "/init", nullptr, MS_BIND, nullptr);
}
bool SecondStageInit::prepare() {
void MagiskInit::second_stage() {
LOGI("Second Stage Init\n");
umount2("/init", MNT_DETACH);
umount2(INIT_PATH, MNT_DETACH); // just in case
unlink("/data/init");
@ -76,7 +78,8 @@ bool SecondStageInit::prepare() {
// We are still on rootfs, so make sure we will execute the init of the 2nd stage
unlink("/init");
xsymlink(INIT_PATH, "/init");
return true;
patch_rw_root();
} else {
patch_ro_root();
}
return false;
}