diff --git a/native/src/Android.mk b/native/src/Android.mk index ef2f23dd1..3fb9a5d37 100644 --- a/native/src/Android.mk +++ b/native/src/Android.mk @@ -63,7 +63,6 @@ LOCAL_STATIC_LIBRARIES := \ libinit-rs LOCAL_SRC_FILES := \ - init/init.cpp \ init/mount.cpp \ init/rootdir.cpp \ init/getinfo.cpp \ diff --git a/native/src/init/getinfo.cpp b/native/src/init/getinfo.cpp index f08975dd0..d56d742c5 100644 --- a/native/src/init/getinfo.cpp +++ b/native/src/init/getinfo.cpp @@ -192,7 +192,7 @@ void BootConfig::init() noexcept { print(); } -bool check_two_stage() { +bool MagiskInit::check_two_stage() const noexcept { if (access("/first_stage_ramdisk", F_OK) == 0) return true; if (access("/second_stage_resources", F_OK) == 0) @@ -206,19 +206,3 @@ bool check_two_stage() { mmap_data init(backup_init()); return init.contains("selinux_setup"); } - -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); - unxz(ch, mmap_data{init_xz}); - close(fd); - clone_attr(init_xz, init); - unlink(init_xz); -} - -const char *backup_init() { - if (access("/.backup/init.xz", F_OK) == 0) - unxz_init("/.backup/init.xz", "/.backup/init"); - return "/.backup/init"; -} diff --git a/native/src/init/init.cpp b/native/src/init/init.cpp deleted file mode 100644 index ec00297fd..000000000 --- a/native/src/init/init.cpp +++ /dev/null @@ -1,116 +0,0 @@ -#include -#include -#include -#include - -#include - -#include - -#include "init.hpp" - -using namespace std; - -#ifdef USE_CRT0 -__BEGIN_DECLS -int tiny_vfprintf(FILE *stream, const char *format, va_list arg); -int vfprintf(FILE *stream, const char *format, va_list arg) { - return tiny_vfprintf(stream, format, arg); -} -__END_DECLS -#endif - -bool unxz(out_stream &strm, rust::Slice bytes) { - uint8_t out[8192]; - xz_crc32_init(); - size_t size = bytes.size(); - struct xz_dec *dec = xz_dec_init(XZ_DYNALLOC, 1 << 26); - run_finally finally([&] { xz_dec_end(dec); }); - struct xz_buf b = { - .in = bytes.data(), - .in_pos = 0, - .in_size = size, - .out = out, - .out_pos = 0, - .out_size = sizeof(out) - }; - enum xz_ret ret; - do { - ret = xz_dec_run(dec, &b); - if (ret != XZ_OK && ret != XZ_STREAM_END) - return false; - strm.write(out, b.out_pos); - b.out_pos = 0; - } while (b.in_pos != size); - return true; - -} - -void restore_ramdisk_init() { - unlink("/init"); - - const char *orig_init = backup_init(); - if (access(orig_init, F_OK) == 0) { - xrename(orig_init, "/init"); - } else { - // 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. - xsymlink(INIT_PATH, "/init"); - } -} - -static void recovery() { - LOGI("Ramdisk is recovery, abort\n"); - restore_ramdisk_init(); - rm_rf("/.backup"); -} - -void MagiskInit::legacy_system_as_root() noexcept { - 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() noexcept { - LOGI("RootFS Init\n"); - prepare_data(); - LOGD("Restoring /init\n"); - rename(backup_init(), "/init"); - patch_rw_root(); -} - -void MagiskInit::start() noexcept { - 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); - - auto name = basename(argv[0]); - if (name == "magisk"sv) - return magisk_proxy_main(argc, argv); - - if (getpid() != 1) - return 1; - - rust::start_magisk_init(argv); -} diff --git a/native/src/init/init.hpp b/native/src/init/init.hpp index b8aa65f6b..7ebbbb187 100644 --- a/native/src/init/init.hpp +++ b/native/src/init/init.hpp @@ -1,14 +1,10 @@ #include #include -#include "init-rs.hpp" - #define DEFAULT_DT_DIR "/proc/device-tree/firmware/android" #define INIT_PATH "/system/bin/init" #define REDIR_PATH "/data/magiskinit" int magisk_proxy_main(int argc, char *argv[]); -bool unxz(out_stream &strm, rust::Slice bytes); -bool check_two_stage(); -const char *backup_init(); -void restore_ramdisk_init(); + +#include "init-rs.hpp" diff --git a/native/src/init/init.rs b/native/src/init/init.rs new file mode 100644 index 000000000..1e74b659c --- /dev/null +++ b/native/src/init/init.rs @@ -0,0 +1,154 @@ +use crate::ffi::{magisk_proxy_main, BootConfig, MagiskInit}; +use crate::logging::setup_klog; +use base::{ + cstr, debug, info, + libc::{basename, getpid, mount, umask}, + raw_cstr, FsPath, LibcReturn, LoggedResult, ResultExt, Utf8CStr, +}; +use std::ffi::{c_char, CStr}; +use std::ptr::null as nullptr; + +impl MagiskInit { + fn new(argv: *mut *mut c_char) -> Self { + Self { + preinit_dev: String::new(), + mount_list: Vec::new(), + argv, + config: BootConfig { + skip_initramfs: false, + force_normal_boot: false, + rootwait: false, + emulator: false, + slot: [0; 3], + dt_dir: [0; 64], + fstab_suffix: [0; 32], + hardware: [0; 32], + hardware_plat: [0; 32], + partition_map: Vec::new(), + }, + } + } + + 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(); + } else { + self.patch_ro_root(); + } + } + + pub(crate) fn rootfs(&mut self) { + info!("RootFS Init"); + self.prepare_data(); + debug!("Restoring /init\n"); + FsPath::from(cstr!("/.backup/init")) + .rename_to(FsPath::from(cstr!("/init"))) + .log() + .ok(); + self.patch_rw_root(); + } + + pub(crate) fn recovery(&self) { + info!("Ramdisk is recovery, abort"); + self.restore_ramdisk_init(); + FsPath::from(cstr!("/.backup")).remove_all().ok(); + } + + pub(crate) fn restore_ramdisk_init(&self) { + FsPath::from(cstr!("/init")).remove().ok(); + + let orig_init = FsPath::from(unsafe { Utf8CStr::from_ptr_unchecked(self.backup_init()) }); + + if orig_init.exists() { + orig_init.rename_to(FsPath::from(cstr!("/init"))).log().ok(); + } else { + // 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. + FsPath::from(cstr!("/system/bin/init")) + .symlink_to(FsPath::from(cstr!("/init"))) + .log() + .ok(); + } + } + + fn start(&mut self) -> LoggedResult<()> { + if !FsPath::from(cstr!("/proc/cmdline")).exists() { + FsPath::from(cstr!("/proc")).mkdir(0o755)?; + unsafe { + mount( + raw_cstr!("proc"), + raw_cstr!("/proc"), + raw_cstr!("proc"), + 0, + nullptr(), + ) + } + .as_os_err()?; + self.mount_list.push("/proc".to_string()); + } + if !FsPath::from(cstr!("/sys/block")).exists() { + FsPath::from(cstr!("/sys")).mkdir(0o755)?; + unsafe { + mount( + raw_cstr!("sysfs"), + raw_cstr!("/sys"), + raw_cstr!("sysfs"), + 0, + nullptr(), + ) + } + .as_os_err()?; + self.mount_list.push("/sys".to_string()); + } + + setup_klog(); + + self.config.init(); + + let argv1 = unsafe { *self.argv.offset(1) }; + if !argv1.is_null() && unsafe { CStr::from_ptr(argv1) == c"selinux_setup" } { + self.second_stage(); + } else if self.config.skip_initramfs { + self.legacy_system_as_root(); + } else if self.config.force_normal_boot { + self.first_stage(); + } else if FsPath::from(cstr!("/sbin/recovery")).exists() + || FsPath::from(cstr!("/system/bin/recovery")).exists() + { + self.recovery(); + } else if self.check_two_stage() { + self.first_stage(); + } else { + self.rootfs(); + } + + // Finally execute the original init + self.exec_init(); + + Ok(()) + } +} + +#[no_mangle] +pub unsafe extern "C" fn main( + argc: i32, + argv: *mut *mut c_char, + _envp: *const *const c_char, +) -> i32 { + umask(0); + + let name = basename(*argv); + + if CStr::from_ptr(name) == c"magisk" { + return magisk_proxy_main(argc, argv); + } + + if getpid() == 1 { + MagiskInit::new(argv).start().log().ok(); + } + + 1 +} diff --git a/native/src/init/lib.rs b/native/src/init/lib.rs index 5b62030a5..ee28a1781 100644 --- a/native/src/init/lib.rs +++ b/native/src/init/lib.rs @@ -2,23 +2,17 @@ #![feature(once_cell_try)] #![feature(try_blocks)] -use base::{ - cstr, - libc::{mount}, - raw_cstr, FsPath, LibcReturn, LoggedResult, -}; use logging::setup_klog; use mount::{is_device_mounted, switch_root}; use rootdir::{collect_overlay_contexts, inject_magisk_rc, reset_overlay_contexts}; // Has to be pub so all symbols in that crate is included -use crate::ffi::{BootConfig, MagiskInit}; pub use magiskpolicy; -use std::ptr::null as nullptr; mod logging; mod mount; mod rootdir; mod getinfo; +mod init; #[cxx::bridge] pub mod ffi { @@ -55,21 +49,25 @@ pub mod ffi { fn is_device_mounted(dev: u64, target: Pin<&mut CxxString>) -> bool; fn collect_overlay_contexts(src: Utf8CStrRef); fn reset_overlay_contexts(); - unsafe fn start_magisk_init(argv: *mut *mut c_char); } extern "Rust" { fn print(self: &BootConfig); fn prepare_data(self: &MagiskInit); - fn exec_init(self: &MagiskInit); + fn legacy_system_as_root(self: &mut MagiskInit); + fn restore_ramdisk_init(self: &MagiskInit); } unsafe extern "C++" { include!("../base/include/base.hpp"); + include!("init.hpp"); #[namespace = "rust"] #[cxx_name = "Utf8CStr"] type Utf8CStrRef<'a> = base::ffi::Utf8CStrRef<'a>; + + unsafe fn magisk_proxy_main(argc: i32, argv: *mut *mut c_char) -> i32; + fn init(self: &mut BootConfig); type kv_pairs; fn set(self: &mut BootConfig, config: &kv_pairs); @@ -93,67 +91,7 @@ pub mod ffi { // SELinux unsafe fn patch_sepolicy(self: &MagiskInit, in_: *const c_char, out: *const c_char); fn hijack_sepolicy(self: &mut MagiskInit) -> bool; - fn legacy_system_as_root(self: &mut MagiskInit); - fn rootfs(self: &mut MagiskInit); - fn start(self: &mut MagiskInit); + fn backup_init(self: &MagiskInit) -> *const c_char; + fn check_two_stage(self: &MagiskInit) -> bool; } } - -pub(crate) fn start_magisk_init(argv: *mut *mut std::ffi::c_char) { - fn inner(argv: *mut *mut std::ffi::c_char) -> LoggedResult<()> { - let mut init = MagiskInit { - preinit_dev: String::new(), - mount_list: Vec::new(), - argv, - config: BootConfig { - skip_initramfs: false, - force_normal_boot: false, - rootwait: false, - emulator: false, - slot: [0; 3], - dt_dir: [0; 64], - fstab_suffix: [0; 32], - hardware: [0; 32], - hardware_plat: [0; 32], - partition_map: Vec::new(), - }, - }; - if !FsPath::from(cstr!("/proc/cmdline")).exists() { - FsPath::from(cstr!("/proc")).mkdir(0o755)?; - unsafe { - mount( - raw_cstr!("proc"), - raw_cstr!("/proc"), - raw_cstr!("proc"), - 0, - nullptr(), - ) - } - .as_os_err()?; - init.mount_list.push("/proc".to_string()); - } - if !FsPath::from(cstr!("/sys/block")).exists() { - FsPath::from(cstr!("/sys")).mkdir(0o755)?; - unsafe { - mount( - raw_cstr!("sysfs"), - raw_cstr!("/sys"), - raw_cstr!("sysfs"), - 0, - nullptr(), - ) - } - .as_os_err()?; - init.mount_list.push("/sys".to_string()); - } - - setup_klog(); - - init.config.init(); - - init.start(); - Ok(()) - } - - inner(argv).ok(); -} diff --git a/native/src/init/rootdir.cpp b/native/src/init/rootdir.cpp index c8df11f3a..4826f6f06 100644 --- a/native/src/init/rootdir.cpp +++ b/native/src/init/rootdir.cpp @@ -6,6 +6,7 @@ #include #include #include +#include #include "init.hpp" @@ -17,6 +18,31 @@ static string magic_mount_list; #define NEW_INITRC_DIR "/system/etc/init/hw" #define INIT_RC "init.rc" +static bool unxz(out_stream &strm, rust::Slice bytes) { + uint8_t out[8192]; + xz_crc32_init(); + size_t size = bytes.size(); + struct xz_dec *dec = xz_dec_init(XZ_DYNALLOC, 1 << 26); + run_finally finally([&] { xz_dec_end(dec); }); + struct xz_buf b = { + .in = bytes.data(), + .in_pos = 0, + .in_size = size, + .out = out, + .out_pos = 0, + .out_size = sizeof(out) + }; + enum xz_ret ret; + do { + ret = xz_dec_run(dec, &b); + if (ret != XZ_OK && ret != XZ_STREAM_END) + return false; + strm.write(out, b.out_pos); + b.out_pos = 0; + } while (b.in_pos != size); + return true; +} + static void magic_mount(const string &sdir, const string &ddir = "") { auto dir = xopen_dir(sdir.data()); if (!dir) return; @@ -426,3 +452,28 @@ int magisk_proxy_main(int argc, char *argv[]) { execve("/sbin/magisk", argv, environ); return 1; } + +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); + unxz(ch, mmap_data{init_xz}); + close(fd); + clone_attr(init_xz, init); + unlink(init_xz); +} + +const char *MagiskInit::backup_init() const noexcept { + if (access("/.backup/init.xz", F_OK) == 0) + unxz_init("/.backup/init.xz", "/.backup/init"); + return "/.backup/init"; +} + +#ifdef USE_CRT0 +__BEGIN_DECLS +int tiny_vfprintf(FILE *stream, const char *format, va_list arg); +int vfprintf(FILE *stream, const char *format, va_list arg) { + return tiny_vfprintf(stream, format, arg); +} +__END_DECLS +#endif