ksud: refine boot patch, add --out-name arg to boot-patch and boot-restore command (#2982)

This commit is contained in:
5ec1cff
2025-11-24 02:04:27 +08:00
committed by ShirkNeko
parent 2e067fa5d1
commit ad5042b66e
3 changed files with 304 additions and 312 deletions

View File

@@ -12,10 +12,9 @@ use which::which;
use crate::{ use crate::{
assets, assets,
defs::{self, BACKUP_FILENAME, KSU_BACKUP_DIR, KSU_BACKUP_FILE_PREFIX}, defs::{BACKUP_FILENAME, KSU_BACKUP_DIR, KSU_BACKUP_FILE_PREFIX},
utils, utils,
}; };
#[cfg(target_os = "android")] #[cfg(target_os = "android")]
fn ensure_gki_kernel() -> Result<()> { fn ensure_gki_kernel() -> Result<()> {
let version = get_kernel_version()?; let version = get_kernel_version()?;
@@ -81,7 +80,7 @@ fn parse_kmi_from_modules() -> Result<String> {
return parse_kmi(&line); return parse_kmi(&line);
} }
} }
anyhow::bail!("Parse KMI from modules failed") bail!("Parse KMI from modules failed")
} }
#[cfg(target_os = "android")] #[cfg(target_os = "android")]
@@ -142,10 +141,7 @@ fn parse_kmi_from_boot(magiskboot: &Path, image: &PathBuf, workdir: &Path) -> Re
.context("Failed to execute magiskboot command")?; .context("Failed to execute magiskboot command")?;
if !status.success() { if !status.success() {
bail!( bail!("magiskboot unpack failed with status: {:?}", status);
"magiskboot unpack failed with status: {:?}",
status.code().unwrap()
);
} }
parse_kmi_from_kernel(&image_path, workdir) parse_kmi_from_kernel(&image_path, workdir)
@@ -206,11 +202,33 @@ fn dd<P: AsRef<Path>, Q: AsRef<Path>>(ifile: P, ofile: Q) -> Result<()> {
Ok(()) Ok(())
} }
pub fn restore( #[derive(clap::Args, Debug)]
image: Option<PathBuf>, pub struct BootRestoreArgs {
magiskboot_path: Option<PathBuf>, /// boot image path, if not specified, will try to find the boot image automatically
flash: bool, #[arg(short, long)]
) -> Result<()> { pub boot: Option<PathBuf>,
/// Flash it to boot partition after restore
#[arg(short, long, default_value = "false")]
pub flash: bool,
/// magiskboot path, if not specified, will search from $PATH
#[arg(long, default_value = None)]
pub magiskboot: Option<PathBuf>,
/// File name of the output.
#[arg(long, default_value = None)]
pub out_name: Option<String>,
}
pub fn restore(args: BootRestoreArgs) -> Result<()> {
let BootRestoreArgs {
boot: image,
flash,
magiskboot: magiskboot_path,
out_name,
} = args;
let tmpdir = tempfile::Builder::new() let tmpdir = tempfile::Builder::new()
.prefix("KernelSU") .prefix("KernelSU")
.tempdir() .tempdir()
@@ -283,38 +301,39 @@ pub fn restore(
println!("- Backup info is absent!"); println!("- Backup info is absent!");
} }
if new_boot.is_none() { let new_boot = new_boot.map_or_else(
// remove kernelsu.ko || -> Result<_> {
do_cpio_cmd(&magiskboot, workdir, ramdisk, "rm kernelsu.ko")?; // remove kernelsu.ko
do_cpio_cmd(&magiskboot, workdir, ramdisk, "rm kernelsu.ko")?;
// if init.real exists, restore it // if init.real exists, restore it
let status = do_cpio_cmd(&magiskboot, workdir, ramdisk, "exists init.real").is_ok(); let status = do_cpio_cmd(&magiskboot, workdir, ramdisk, "exists init.real").is_ok();
if status { if status {
do_cpio_cmd(&magiskboot, workdir, ramdisk, "mv init.real init")?; do_cpio_cmd(&magiskboot, workdir, ramdisk, "mv init.real init")?;
} }
println!("- Repacking boot image"); println!("- Repacking boot image");
let status = Command::new(&magiskboot) let status = Command::new(&magiskboot)
.current_dir(workdir) .current_dir(workdir)
.stdout(Stdio::null()) .stdout(Stdio::null())
.stderr(Stdio::null()) .stderr(Stdio::null())
.arg("repack") .arg("repack")
.arg(&bootimage) .arg(&bootimage)
.status()?; .status()?;
ensure!(status.success(), "magiskboot repack failed"); ensure!(status.success(), "magiskboot repack failed");
new_boot = Some(workdir.join("new-boot.img")); Ok(workdir.join("new-boot.img"))
} },
Ok,
let new_boot = new_boot.unwrap(); )?;
if image.is_some() { if image.is_some() {
// if image is specified, write to output file // if image is specified, write to output file
let output_dir = std::env::current_dir()?; let output_dir = std::env::current_dir()?;
let now = chrono::Utc::now(); let name = out_name.unwrap_or_else(|| {
let output_image = output_dir.join(format!( let now = chrono::Utc::now();
"kernelsu_restore_{}.img", format!("kernelsu_restore_{}.img", now.format("%Y%m%d_%H%M%S"))
now.format("%Y%m%d_%H%M%S") });
)); let output_image = output_dir.join(name);
if from_backup || std::fs::rename(&new_boot, &output_image).is_err() { if from_backup || std::fs::rename(&new_boot, &output_image).is_err() {
std::fs::copy(&new_boot, &output_image).context("copy out new boot failed")?; std::fs::copy(&new_boot, &output_image).context("copy out new boot failed")?;
@@ -334,217 +353,252 @@ pub fn restore(
Ok(()) Ok(())
} }
#[allow(clippy::too_many_arguments)] #[derive(clap::Args, Debug)]
pub fn patch( pub struct BootPatchArgs {
image: Option<PathBuf>, /// boot image path, if not specified, will try to find the boot image automatically
kernel: Option<PathBuf>, #[arg(short, long)]
kmod: Option<PathBuf>, pub boot: Option<PathBuf>,
init: Option<PathBuf>,
ota: bool, /// kernel image path to replace
flash: bool, #[arg(short, long)]
out: Option<PathBuf>, pub kernel: Option<PathBuf>,
magiskboot: Option<PathBuf>,
kmi: Option<String>, /// LKM module path to replace, if not specified, will use the builtin one
partition: Option<String>, #[arg(short, long)]
) -> Result<()> { pub module: Option<PathBuf>,
let result = do_patch(
image, kernel, kmod, init, ota, flash, out, magiskboot, kmi, partition, /// init to be replaced
); #[arg(short, long, requires("module"))]
if let Err(ref e) = result { pub init: Option<PathBuf>,
println!("- Install Error: {e}");
} /// will use another slot when boot image is not specified
result #[arg(short = 'u', long, default_value = "false")]
pub ota: bool,
/// Flash it to boot partition after patch
#[arg(short, long, default_value = "false")]
pub flash: bool,
/// output path, if not specified, will use current directory
#[arg(short, long, default_value = None)]
pub out: Option<PathBuf>,
/// magiskboot path, if not specified, will search from $PATH
#[arg(long, default_value = None)]
pub magiskboot: Option<PathBuf>,
/// KMI version, if specified, will use the specified KMI
#[arg(long, default_value = None)]
pub kmi: Option<String>,
/// target partition override (init_boot | boot | vendor_boot)
#[arg(long, default_value = None)]
pub partition: Option<String>,
/// File name of the output.
#[arg(long, default_value = None)]
pub out_name: Option<String>,
} }
#[allow(clippy::too_many_arguments, clippy::needless_pass_by_value)] pub fn patch(args: BootPatchArgs) -> Result<()> {
fn do_patch( let inner = move || {
image: Option<PathBuf>, let BootPatchArgs {
kernel: Option<PathBuf>, boot: image,
kmod: Option<PathBuf>, init,
init: Option<PathBuf>, kernel,
ota: bool, module: kmod,
flash: bool, ota,
out: Option<PathBuf>, flash,
magiskboot_path: Option<PathBuf>, out,
kmi: Option<String>, magiskboot: magiskboot_path,
partition: Option<String>, kmi,
) -> Result<()> { partition,
println!(include_str!("banner")); out_name,
} = args;
let patch_file = image.is_some(); println!(include_str!("banner"));
#[cfg(target_os = "android")] let patch_file = image.is_some();
if !patch_file {
ensure_gki_kernel()?;
}
let is_replace_kernel = kernel.is_some(); #[cfg(target_os = "android")]
if !patch_file {
ensure_gki_kernel()?;
}
if is_replace_kernel { let is_replace_kernel = kernel.is_some();
ensure!(
init.is_none() && kmod.is_none(),
"init and module must not be specified."
);
}
let tmpdir = tempfile::Builder::new() if is_replace_kernel {
.prefix("KernelSU") ensure!(
.tempdir() init.is_none() && kmod.is_none(),
.context("create temp dir failed")?; "init and module must not be specified."
let workdir = tmpdir.path(); );
}
// extract magiskboot let tmpdir = tempfile::Builder::new()
let magiskboot = find_magiskboot(magiskboot_path, workdir)?; .prefix("KernelSU")
.tempdir()
.context("create temp dir failed")?;
let workdir = tmpdir.path();
let kmi = if let Some(kmi) = kmi { // extract magiskboot
kmi let magiskboot = find_magiskboot(magiskboot_path, workdir)?;
} else {
match get_current_kmi() { let kmi = kmi.map_or_else(
Ok(value) => value, || -> Result<_> {
Err(e) => { Ok(match get_current_kmi() {
println!("- {e}"); Ok(value) => value,
if let Some(image_path) = &image { Err(e) => {
println!( println!("- {e}");
"- Trying to auto detect KMI version for {}", if let Some(image_path) = &image {
image_path.to_str().unwrap() println!(
); "- Trying to auto detect KMI version for {}",
parse_kmi_from_boot(&magiskboot, image_path, tmpdir.path())? image_path.display()
} else if let Some(kernel_path) = &kernel { );
println!( parse_kmi_from_boot(&magiskboot, image_path, tmpdir.path())?
"- Trying to auto detect KMI version for {}", } else if let Some(kernel_path) = &kernel {
kernel_path.to_str().unwrap() println!(
); "- Trying to auto detect KMI version for {}",
parse_kmi_from_kernel(kernel_path, tmpdir.path())? kernel_path.display()
} else { );
String::new() parse_kmi_from_kernel(kernel_path, tmpdir.path())?
} } else {
String::new()
}
}
})
},
Ok,
)?;
let (bootimage, bootdevice) =
find_boot_image(&image, &kmi, ota, is_replace_kernel, workdir, &partition)?;
let bootimage = bootimage.as_path();
// try extract magiskboot/bootctl
let _ = assets::ensure_binaries(false);
if let Some(kernel) = kernel {
std::fs::copy(kernel, workdir.join("kernel")).context("copy kernel from failed")?;
}
println!("- Preparing assets");
let kmod_file = workdir.join("kernelsu.ko");
if let Some(kmod) = kmod {
std::fs::copy(kmod, kmod_file).context("copy kernel module failed")?;
} else {
// If kmod is not specified, extract from assets
println!("- KMI: {kmi}");
let name = format!("{kmi}_kernelsu.ko");
assets::copy_assets_to_file(&name, kmod_file)
.with_context(|| format!("Failed to copy {name}"))?;
}
let init_file = workdir.join("init");
if let Some(init) = init {
std::fs::copy(init, init_file).context("copy init failed")?;
} else {
assets::copy_assets_to_file("ksuinit", init_file).context("copy ksuinit failed")?;
}
println!("- Unpacking boot image");
let status = Command::new(&magiskboot)
.current_dir(workdir)
.stdout(Stdio::null())
.stderr(Stdio::null())
.arg("unpack")
.arg(bootimage)
.status()?;
ensure!(status.success(), "magiskboot unpack failed");
let mut ramdisk = workdir.join("ramdisk.cpio");
if !ramdisk.exists() {
ramdisk = workdir.join("vendor_ramdisk").join("init_boot.cpio");
}
if !ramdisk.exists() {
ramdisk = workdir.join("vendor_ramdisk").join("ramdisk.cpio");
}
if !ramdisk.exists() {
println!("- No ramdisk, create by default");
ramdisk = "ramdisk.cpio".into();
}
let ramdisk = ramdisk.as_path();
let is_magisk_patched = is_magisk_patched(&magiskboot, workdir, ramdisk)?;
ensure!(!is_magisk_patched, "Cannot work with Magisk patched image");
println!("- Adding KernelSU LKM");
let is_kernelsu_patched = is_kernelsu_patched(&magiskboot, workdir, ramdisk)?;
let need_backup = if is_kernelsu_patched {
false
} else {
// kernelsu.ko is not exist, backup init if necessary
let status = do_cpio_cmd(&magiskboot, workdir, ramdisk, "exists init");
if status.is_ok() {
do_cpio_cmd(&magiskboot, workdir, ramdisk, "mv init init.real")?;
}
flash
};
do_cpio_cmd(&magiskboot, workdir, ramdisk, "add 0755 init init")?;
do_cpio_cmd(
&magiskboot,
workdir,
ramdisk,
"add 0755 kernelsu.ko kernelsu.ko",
)?;
#[cfg(target_os = "android")]
if need_backup && let Err(e) = do_backup(&magiskboot, workdir, ramdisk, bootimage) {
println!("- Backup stock image failed: {e}");
}
println!("- Repacking boot image");
// magiskboot repack boot.img
let status = Command::new(&magiskboot)
.current_dir(workdir)
.stdout(Stdio::null())
.stderr(Stdio::null())
.arg("repack")
.arg(bootimage)
.status()?;
ensure!(status.success(), "magiskboot repack failed");
let new_boot = workdir.join("new-boot.img");
if patch_file {
// if image is specified, write to output file
let output_dir = out.unwrap_or(std::env::current_dir()?);
let name = out_name.unwrap_or_else(|| {
let now = chrono::Utc::now();
format!("kernelsu_patched_{}.img", now.format("%Y%m%d_%H%M%S"))
});
let output_image = output_dir.join(name);
if std::fs::rename(&new_boot, &output_image).is_err() {
std::fs::copy(&new_boot, &output_image).context("copy out new boot failed")?;
}
println!("- Output file is written to");
println!("- {}", output_image.display().to_string().trim_matches('"'));
}
if flash {
println!("- Flashing new boot image");
flash_boot(&bootdevice, new_boot)?;
if ota {
post_ota()?;
} }
} }
println!("- Done!");
Ok(())
}; };
let (bootimage, bootdevice) = let result = inner();
find_boot_image(&image, &kmi, ota, is_replace_kernel, workdir, &partition)?; if let Err(ref e) = result {
println!("- Patch Error: {e}");
let bootimage = bootimage.as_path();
// try extract magiskboot/bootctl
let _ = assets::ensure_binaries(false);
if let Some(kernel) = kernel {
std::fs::copy(kernel, workdir.join("kernel")).context("copy kernel from failed")?;
} }
result
println!("- Preparing assets");
let kmod_file = workdir.join("kernelsu.ko");
if let Some(kmod) = kmod {
std::fs::copy(kmod, kmod_file).context("copy kernel module failed")?;
} else {
// If kmod is not specified, extract from assets
println!("- KMI: {kmi}");
let name = format!("{kmi}_kernelsu.ko");
assets::copy_assets_to_file(&name, kmod_file)
.with_context(|| format!("Failed to copy {name}"))?;
}
let init_file = workdir.join("init");
if let Some(init) = init {
std::fs::copy(init, init_file).context("copy init failed")?;
} else {
assets::copy_assets_to_file("ksuinit", init_file).context("copy ksuinit failed")?;
}
println!("- Unpacking boot image");
let status = Command::new(&magiskboot)
.current_dir(workdir)
.stdout(Stdio::null())
.stderr(Stdio::null())
.arg("unpack")
.arg(bootimage)
.status()?;
ensure!(status.success(), "magiskboot unpack failed");
let mut ramdisk = workdir.join("ramdisk.cpio");
if !ramdisk.exists() {
ramdisk = workdir.join("vendor_ramdisk").join("init_boot.cpio");
}
if !ramdisk.exists() {
ramdisk = workdir.join("vendor_ramdisk").join("ramdisk.cpio");
}
if !ramdisk.exists() {
println!("- No ramdisk, create by default");
ramdisk = "ramdisk.cpio".into();
}
let ramdisk = ramdisk.as_path();
let is_magisk_patched = is_magisk_patched(&magiskboot, workdir, ramdisk)?;
ensure!(!is_magisk_patched, "Cannot work with Magisk patched image");
println!("- Adding KernelSU LKM");
let is_kernelsu_patched = is_kernelsu_patched(&magiskboot, workdir, ramdisk)?;
let need_backup = if is_kernelsu_patched {
false
} else {
// kernelsu.ko is not exist, backup init if necessary
let status = do_cpio_cmd(&magiskboot, workdir, ramdisk, "exists init");
if status.is_ok() {
do_cpio_cmd(&magiskboot, workdir, ramdisk, "mv init init.real")?;
}
flash
};
do_cpio_cmd(&magiskboot, workdir, ramdisk, "add 0755 init init")?;
do_cpio_cmd(
&magiskboot,
workdir,
ramdisk,
"add 0755 kernelsu.ko kernelsu.ko",
)?;
#[cfg(target_os = "android")]
if need_backup && let Err(e) = do_backup(&magiskboot, workdir, ramdisk, bootimage) {
println!("- Backup stock image failed: {e}");
}
println!("- Repacking boot image");
// magiskboot repack boot.img
let status = Command::new(&magiskboot)
.current_dir(workdir)
.stdout(Stdio::null())
.stderr(Stdio::null())
.arg("repack")
.arg(bootimage)
.status()?;
ensure!(status.success(), "magiskboot repack failed");
let new_boot = workdir.join("new-boot.img");
if patch_file {
// if image is specified, write to output file
let output_dir = out.unwrap_or(std::env::current_dir()?);
let now = chrono::Utc::now();
let output_image = output_dir.join(format!(
"kernelsu_patched_{}.img",
now.format("%Y%m%d_%H%M%S")
));
if std::fs::rename(&new_boot, &output_image).is_err() {
std::fs::copy(&new_boot, &output_image).context("copy out new boot failed")?;
}
println!("- Output file is written to");
println!("- {}", output_image.display().to_string().trim_matches('"'));
}
if flash {
println!("- Flashing new boot image");
flash_boot(&bootdevice, new_boot)?;
if ota {
post_ota()?;
}
}
println!("- Done!");
Ok(())
} }
#[cfg(target_os = "android")] #[cfg(target_os = "android")]
@@ -592,7 +646,7 @@ fn do_backup(magiskboot: &Path, workdir: &Path, cpio_path: &Path, image: &Path)
fn clean_backup(sha1: &str) -> Result<()> { fn clean_backup(sha1: &str) -> Result<()> {
println!("- Clean up backup"); println!("- Clean up backup");
let backup_name = format!("{KSU_BACKUP_FILE_PREFIX}{sha1}"); let backup_name = format!("{KSU_BACKUP_FILE_PREFIX}{sha1}");
let dir = std::fs::read_dir(defs::KSU_BACKUP_DIR)?; let dir = std::fs::read_dir(KSU_BACKUP_DIR)?;
for entry in dir.flatten() { for entry in dir.flatten() {
let path = entry.path(); let path = entry.path();
if !path.is_file() { if !path.is_file() {
@@ -744,7 +798,7 @@ pub fn list_available_partitions() -> Vec<String> {
candidates candidates
.into_iter() .into_iter()
.filter(|name| Path::new(&format!("/dev/block/by-name/{name}{slot_suffix}")).exists()) .filter(|name| Path::new(&format!("/dev/block/by-name/{name}{slot_suffix}")).exists())
.map(std::string::ToString::to_string) .map(ToString::to_string)
.collect() .collect()
} }
@@ -773,7 +827,7 @@ fn post_ota() -> Result<()> {
.arg(format!("set-active-boot-slot {target_slot}")) .arg(format!("set-active-boot-slot {target_slot}"))
.status()?; .status()?;
let post_fs_data = std::path::Path::new(ADB_DIR).join("post-fs-data.d"); let post_fs_data = Path::new(ADB_DIR).join("post-fs-data.d");
utils::ensure_dir_exists(&post_fs_data)?; utils::ensure_dir_exists(&post_fs_data)?;
let post_ota_sh = post_fs_data.join("post_ota.sh"); let post_ota_sh = post_fs_data.join("post_ota.sh");

View File

@@ -7,6 +7,7 @@ use android_logger::Config;
#[cfg(target_os = "android")] #[cfg(target_os = "android")]
use log::LevelFilter; use log::LevelFilter;
use crate::boot_patch::{BootPatchArgs, BootRestoreArgs};
use crate::{apk_sign, assets, debug, defs, init_event, ksucalls, module, module_config, utils}; use crate::{apk_sign, assets, debug, defs, init_event, ksucalls, module, module_config, utils};
/// KernelSU userspace cli /// KernelSU userspace cli
@@ -66,62 +67,10 @@ enum Commands {
}, },
/// Patch boot or init_boot images to apply KernelSU /// Patch boot or init_boot images to apply KernelSU
BootPatch { BootPatch(BootPatchArgs),
/// boot image path, if not specified, will try to find the boot image automatically
#[arg(short, long)]
boot: Option<PathBuf>,
/// kernel image path to replace
#[arg(short, long)]
kernel: Option<PathBuf>,
/// LKM module path to replace, if not specified, will use the builtin one
#[arg(short, long)]
module: Option<PathBuf>,
/// init to be replaced
#[arg(short, long, requires("module"))]
init: Option<PathBuf>,
/// will use another slot when boot image is not specified
#[arg(short = 'u', long, default_value = "false")]
ota: bool,
/// Flash it to boot partition after patch
#[arg(short, long, default_value = "false")]
flash: bool,
/// output path, if not specified, will use current directory
#[arg(short, long, default_value = None)]
out: Option<PathBuf>,
/// magiskboot path, if not specified, will search from $PATH
#[arg(long, default_value = None)]
magiskboot: Option<PathBuf>,
/// KMI version, if specified, will use the specified KMI
#[arg(long, default_value = None)]
kmi: Option<String>,
/// target partition override (init_boot | boot | vendor_boot)
#[arg(long, default_value = None)]
partition: Option<String>,
},
/// Restore boot or init_boot images patched by KernelSU /// Restore boot or init_boot images patched by KernelSU
BootRestore { BootRestore(BootRestoreArgs),
/// boot image path, if not specified, will try to find the boot image automatically
#[arg(short, long)]
boot: Option<PathBuf>,
/// Flash it to boot partition after patch
#[arg(short, long, default_value = "false")]
flash: bool,
/// magiskboot path, if not specified, will search from $PATH
#[arg(long, default_value = None)]
magiskboot: Option<PathBuf>,
},
/// Show boot information /// Show boot information
BootInfo { BootInfo {
@@ -703,20 +652,7 @@ pub fn run() -> Result<()> {
}, },
}, },
Commands::BootPatch { Commands::BootPatch(boot_patch) => crate::boot_patch::patch(boot_patch),
boot,
init,
kernel,
module,
ota,
flash,
out,
magiskboot,
kmi,
partition,
} => crate::boot_patch::patch(
boot, kernel, module, init, ota, flash, out, magiskboot, kmi, partition,
),
Commands::BootInfo { command } => match command { Commands::BootInfo { command } => match command {
BootInfo::CurrentKmi => { BootInfo::CurrentKmi => {
@@ -758,11 +694,7 @@ pub fn run() -> Result<()> {
return Ok(()); return Ok(());
} }
}, },
Commands::BootRestore { Commands::BootRestore(boot_restore) => crate::boot_patch::restore(boot_restore),
boot,
magiskboot,
flash,
} => crate::boot_patch::restore(boot, magiskboot, flash),
Commands::Kernel { command } => match command { Commands::Kernel { command } => match command {
Kernel::NukeExt4Sysfs { mnt } => ksucalls::nuke_ext4_sysfs(&mnt), Kernel::NukeExt4Sysfs { mnt } => ksucalls::nuke_ext4_sysfs(&mnt),
Kernel::Umount { command } => match command { Kernel::Umount { command } => match command {

View File

@@ -17,6 +17,7 @@ use std::os::unix::prelude::PermissionsExt;
use std::path::PathBuf; use std::path::PathBuf;
use crate::boot_patch::BootRestoreArgs;
#[cfg(any(target_os = "linux", target_os = "android"))] #[cfg(any(target_os = "linux", target_os = "android"))]
use rustix::{ use rustix::{
process, process,
@@ -217,7 +218,12 @@ pub fn uninstall(magiskboot_path: Option<PathBuf>) -> Result<()> {
std::fs::remove_file(defs::DAEMON_PATH).ok(); std::fs::remove_file(defs::DAEMON_PATH).ok();
std::fs::remove_dir_all(defs::MODULE_DIR).ok(); std::fs::remove_dir_all(defs::MODULE_DIR).ok();
println!("- Restore boot image.."); println!("- Restore boot image..");
boot_patch::restore(None, magiskboot_path, true)?; boot_patch::restore(BootRestoreArgs {
boot: None,
flash: true,
magiskboot: magiskboot_path,
out_name: None,
})?;
println!("- Uninstall KernelSU manager.."); println!("- Uninstall KernelSU manager..");
Command::new("pm") Command::new("pm")
.args(["uninstall", "com.sukisu.ultra"]) .args(["uninstall", "com.sukisu.ultra"])