Reorganize magiskinit code

This commit is contained in:
topjohnwu
2025-03-24 01:35:16 -07:00
committed by John Wu
parent 0bbfe7f44d
commit c09a792958
4 changed files with 131 additions and 103 deletions
+9 -8
View File
@@ -1,15 +1,15 @@
use crate::ffi::backup_init;
use crate::{
ffi::{magisk_proxy_main, BootConfig, MagiskInit},
ffi::{BootConfig, MagiskInit, magisk_proxy_main},
logging::setup_klog,
};
use base::{
debug, info,
FsPath, LibcReturn, LoggedResult, ResultExt, debug, info,
libc::{basename, getpid, mount, umask},
path, raw_cstr, FsPath, LibcReturn, LoggedResult, ResultExt,
path, raw_cstr,
};
use std::{
ffi::{c_char, CStr},
ffi::{CStr, c_char},
ptr::null,
};
@@ -38,8 +38,9 @@ impl MagiskInit {
pub(crate) fn legacy_system_as_root(&mut self) {
info!("Legacy SAR Init");
self.prepare_data();
if self.mount_system_root() {
self.redirect_second_stage();
let is_two_stage = self.mount_system_root();
if is_two_stage {
self.patch_init_for_second_stage();
} else {
self.patch_ro_root();
}
@@ -70,8 +71,8 @@ impl MagiskInit {
// If the backup init is missing, this means that the boot ramdisk
// was created from scratch, and the real init is in a separate CPIO,
// which is guaranteed to be placed at /system/bin/init.
path!("/system/bin/init")
.symlink_to(path!("/init"))
path!("/init")
.create_symlink_to(path!("/system/bin/init"))
.log_ok();
}
}
+93 -68
View File
@@ -9,75 +9,106 @@ use base::{
};
use std::{ffi::c_long, io::Write, ptr::null};
impl MagiskInit {
pub(crate) fn first_stage(&self) {
info!("First Stage Init");
self.prepare_data();
const RAMFS_MAGIC: u64 = 0x858458f6;
if !path!("/sdcard").exists() && !path!("/first_stage_ramdisk/sdcard").exists() {
if self.config.force_normal_boot {
path!("/first_stage_ramdisk/storage/self")
.mkdirs(0o755)
.log_ok();
path!("/system/system/bin/init")
.symlink_to(path!("/first_stage_ramdisk/storage/self/primary"))
.log_ok();
debug!(
"Symlink /first_stage_ramdisk/storage/self/primary -> /system/system/bin/init"
);
path!("/first_stage_ramdisk/sdcard")
.create(O_RDONLY | O_CREAT | O_CLOEXEC, 0)
.log_ok();
} else {
path!("/storage/self").mkdirs(0o755).log_ok();
path!("/system/system/bin/init")
.symlink_to(path!("/storage/self/primary"))
.log_ok();
debug!("Symlink /storage/self/primary -> /system/system/bin/init");
}
path!("/init").rename_to(path!("/sdcard")).log_ok();
// Try to keep magiskinit in rootfs for samsung RKP
if unsafe {
fn patch_init_path(init: &mut MappedFile) {
let from = "/system/bin/init";
let to = "/data/magiskinit";
// Redirect original init to magiskinit
let v = init.patch(from.as_bytes(), to.as_bytes());
#[allow(unused_variables)]
for off in &v {
debug!("Patch @ {:#010X} [{}] -> [{}]", off, from, to);
}
}
impl MagiskInit {
fn hijack_init_with_switch_root(&self) {
// We make use of original init's `SwitchRoot` to help us bind mount
// magiskinit to /system/bin/init to hijack second stage init.
//
// Two important assumption about 2SI:
// - The second stage init is always /system/bin/init
// - After `SwitchRoot`, /sdcard is always a symlink to `/storage/self/primary`.
//
// `SwitchRoot` will perform the following:
// - Recursive move all mounts under `/` to `/system`
// - chroot to `/system`
//
// The trick here is that in Magisk's first stage init, we can mount magiskinit to /sdcard,
// and create a symlink at /storage/self/primary pointing to /system/system/bin/init.
//
// During init's `SwitchRoot`, it will mount move /sdcard (which is magiskinit)
// to /system/sdcard, which is a symlink to /storage/self/primary, which is a
// symlink to /system/system/bin/init, which will eventually become /system/bin/init after
// chroot to /system. The effective result is that we coerce the original init into bind
// mounting magiskinit to /system/bin/init, successfully hijacking the second stage init.
//
// An edge case is that some devices (like meizu) use 2SI but does not switch root.
// In that case, they must already have a /sdcard in ramfs, thus we can check if
// /sdcard exists and fallback to using hexpatch.
if self.config.force_normal_boot {
path!("/first_stage_ramdisk/storage/self")
.mkdirs(0o755)
.log_ok();
path!("/first_stage_ramdisk/storage/self/primary")
.create_symlink_to(path!("/system/system/bin/init"))
.log_ok();
debug!("Symlink /first_stage_ramdisk/storage/self/primary -> /system/system/bin/init");
path!("/first_stage_ramdisk/sdcard")
.create(O_RDONLY | O_CREAT | O_CLOEXEC, 0)
.log_ok();
} else {
path!("/storage/self").mkdirs(0o755).log_ok();
path!("/storage/self/primary")
.create_symlink_to(path!("/system/system/bin/init"))
.log_ok();
debug!("Symlink /storage/self/primary -> /system/system/bin/init");
}
path!("/init").rename_to(path!("/sdcard")).log_ok();
// First try to mount magiskinit from rootfs to workaround Samsung RKP
if unsafe {
mount(
raw_cstr!("/sdcard"),
raw_cstr!("/sdcard"),
null(),
MS_BIND,
null(),
)
} == 0
{
debug!("Bind mount /sdcard -> /sdcard");
} else {
// Binding mounting from rootfs is not supported before Linux 3.12
unsafe {
mount(
raw_cstr!("/sdcard"),
raw_cstr!("/data/magiskinit"),
raw_cstr!("/sdcard"),
null(),
MS_BIND,
null(),
)
} == 0
{
debug!("Bind mount /sdcard -> /sdcard");
} else {
// rootfs before 3.12
unsafe {
mount(
raw_cstr!("/data/magiskinit"),
raw_cstr!("/sdcard"),
null(),
MS_BIND,
null(),
)
};
debug!("Bind mount /data/magiskinit -> /sdcard");
}
};
debug!("Bind mount /data/magiskinit -> /sdcard");
}
}
pub(crate) fn first_stage(&self) {
info!("First Stage Init");
self.prepare_data();
if !path!("/sdcard").exists() && !path!("/first_stage_ramdisk/sdcard").exists() {
self.hijack_init_with_switch_root();
self.restore_ramdisk_init();
} else {
self.restore_ramdisk_init();
// fallback to hexpatch if /sdcard exists
match MappedFile::open_rw(cstr!("/init")) {
Ok(mut map) => {
let from = "/system/bin/init";
let to = "/data/magiskinit";
// Redirect original init to magiskinit
let v = map.patch(from.as_bytes(), to.as_bytes());
#[allow(unused_variables)]
for off in &v {
debug!("Patch @ {:#010X} [{}] -> [{}]", off, from, to);
}
}
Ok(mut map) => patch_init_path(&mut map),
_ => {
error!("Failed to open /init for hexpatch");
}
@@ -85,21 +116,13 @@ impl MagiskInit {
}
}
pub(crate) fn redirect_second_stage(&self) {
pub(crate) fn patch_init_for_second_stage(&self) {
let src = path!("/init");
let dest = path!("/data/init");
// Patch init binary
match MappedFile::open(src) {
Ok(mut map) => {
let from = "/system/bin/init";
let to = "/data/magiskinit";
// Redirect original init to magiskinit
let v = map.patch(from.as_bytes(), to.as_bytes());
#[allow(unused_variables)]
for off in &v {
debug!("Patch @ {:#010X} [{}] -> [{}]", off, from, to);
}
patch_init_path(&mut map);
match dest.create(O_CREAT | O_WRONLY, 0) {
Ok(mut dest) => {
dest.write_all(map.as_ref()).log_ok();
@@ -135,11 +158,13 @@ impl MagiskInit {
// Some weird devices like meizu, uses 2SI but still have legacy rootfs
let mut sfs: statfs = std::mem::zeroed();
statfs(raw_cstr!("/"), &mut sfs);
if sfs.f_type == 0x858458f6 || sfs.f_type as c_long == TMPFS_MAGIC {
if sfs.f_type as u64 == RAMFS_MAGIC || sfs.f_type as c_long == TMPFS_MAGIC {
// We are still on rootfs, so make sure we will execute the init of the 2nd stage
let init_path = path!("/init");
init_path.remove().ok();
path!("/system/bin/init").symlink_to(init_path).log_ok();
init_path
.create_symlink_to(path!("/system/bin/init"))
.log_ok();
self.patch_rw_root();
} else {
self.patch_ro_root();