ksud: backup stock image and use it when restore (#1619)

This commit is contained in:
5ec1cff
2024-04-14 00:45:06 +08:00
committed by GitHub
parent 1be266b6f6
commit 60dd52afd1
8 changed files with 243 additions and 63 deletions

View File

@@ -12,9 +12,21 @@ import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.automirrored.filled.ArrowBack import androidx.compose.material.icons.automirrored.filled.ArrowBack
import androidx.compose.material.icons.filled.Refresh import androidx.compose.material.icons.filled.Refresh
import androidx.compose.material.icons.filled.Save import androidx.compose.material.icons.filled.Save
import androidx.compose.material3.* import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.runtime.* import androidx.compose.material3.ExtendedFloatingActionButton
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Text
import androidx.compose.material3.TopAppBar
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.saveable.rememberSaveable import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.runtime.setValue
import androidx.compose.ui.ExperimentalComposeUiApi import androidx.compose.ui.ExperimentalComposeUiApi
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.input.key.Key import androidx.compose.ui.input.key.Key
@@ -37,9 +49,17 @@ import me.weishu.kernelsu.ui.util.LocalSnackbarHost
import me.weishu.kernelsu.ui.util.installBoot import me.weishu.kernelsu.ui.util.installBoot
import me.weishu.kernelsu.ui.util.installModule import me.weishu.kernelsu.ui.util.installModule
import me.weishu.kernelsu.ui.util.reboot import me.weishu.kernelsu.ui.util.reboot
import me.weishu.kernelsu.ui.util.restoreBoot
import java.io.File import java.io.File
import java.text.SimpleDateFormat import java.text.SimpleDateFormat
import java.util.* import java.util.Date
import java.util.Locale
enum class FlashingStatus {
FLASHING,
SUCCESS,
FAILED
}
/** /**
* @author weishu * @author weishu
@@ -57,19 +77,24 @@ fun FlashScreen(navigator: DestinationsNavigator, flashIt: FlashIt) {
val snackBarHost = LocalSnackbarHost.current val snackBarHost = LocalSnackbarHost.current
val scope = rememberCoroutineScope() val scope = rememberCoroutineScope()
val scrollState = rememberScrollState() val scrollState = rememberScrollState()
var flashing by rememberSaveable {
mutableStateOf(FlashingStatus.FLASHING)
}
LaunchedEffect(Unit) { LaunchedEffect(Unit) {
if (text.isNotEmpty()) { if (text.isNotEmpty()) {
return@LaunchedEffect return@LaunchedEffect
} }
withContext(Dispatchers.IO) { withContext(Dispatchers.IO) {
flashIt(flashIt, onFinish = { showReboot -> flashIt(flashIt, onFinish = { showReboot, code ->
if (code != 0) {
text += "Error: exit code = $code.\nPlease save and check the log.\n"
}
if (showReboot) { if (showReboot) {
for (i in 0..2) { text += "\n\n\n"
text += "\n"
}
showFloatAction = true showFloatAction = true
} }
flashing = if (code == 0) FlashingStatus.SUCCESS else FlashingStatus.FAILED
}, onStdout = { }, onStdout = {
text += "$it\n" text += "$it\n"
logContent.append(it).append("\n") logContent.append(it).append("\n")
@@ -82,6 +107,7 @@ fun FlashScreen(navigator: DestinationsNavigator, flashIt: FlashIt) {
Scaffold( Scaffold(
topBar = { topBar = {
TopBar( TopBar(
flashing,
onBack = { onBack = {
navigator.popBackStack() navigator.popBackStack()
}, },
@@ -146,10 +172,12 @@ sealed class FlashIt : Parcelable {
FlashIt() FlashIt()
data class FlashModule(val uri: Uri) : FlashIt() data class FlashModule(val uri: Uri) : FlashIt()
data object FlashRestore : FlashIt()
} }
fun flashIt( fun flashIt(
flashIt: FlashIt, onFinish: (Boolean) -> Unit, flashIt: FlashIt, onFinish: (Boolean, Int) -> Unit,
onStdout: (String) -> Unit, onStdout: (String) -> Unit,
onStderr: (String) -> Unit onStderr: (String) -> Unit
) { ) {
@@ -164,14 +192,26 @@ fun flashIt(
) )
is FlashIt.FlashModule -> installModule(flashIt.uri, onFinish, onStdout, onStderr) is FlashIt.FlashModule -> installModule(flashIt.uri, onFinish, onStdout, onStderr)
FlashIt.FlashRestore -> restoreBoot(onFinish, onStdout, onStderr)
} }
} }
@OptIn(ExperimentalMaterial3Api::class) @OptIn(ExperimentalMaterial3Api::class)
@Composable @Composable
private fun TopBar(onBack: () -> Unit = {}, onSave: () -> Unit = {}) { private fun TopBar(status: FlashingStatus, onBack: () -> Unit = {}, onSave: () -> Unit = {}) {
TopAppBar( TopAppBar(
title = { Text(stringResource(R.string.install)) }, title = {
Text(
stringResource(
when (status) {
FlashingStatus.FLASHING -> R.string.flashing
FlashingStatus.SUCCESS -> R.string.flash_success
FlashingStatus.FAILED -> R.string.flash_failed
}
)
)
},
navigationIcon = { navigationIcon = {
IconButton( IconButton(
onClick = onBack onClick = onBack

View File

@@ -123,11 +123,19 @@ fun InstallScreen(navigator: DestinationsNavigator) {
installMethod = method installMethod = method
} }
Row( Column(
modifier = Modifier modifier = Modifier
.fillMaxWidth() .fillMaxWidth()
.padding(16.dp) .padding(16.dp)
) { ) {
(lkmSelection as? LkmSelection.LkmUri)?.let {
Text(
stringResource(
id = R.string.selected_lkm,
it.uri.lastPathSegment ?: "(file)"
)
)
}
Button(modifier = Modifier.fillMaxWidth(), Button(modifier = Modifier.fillMaxWidth(),
enabled = installMethod != null, enabled = installMethod != null,
onClick = { onClick = {

View File

@@ -63,6 +63,7 @@ import me.weishu.kernelsu.ui.component.rememberConfirmDialog
import me.weishu.kernelsu.ui.component.rememberCustomDialog import me.weishu.kernelsu.ui.component.rememberCustomDialog
import me.weishu.kernelsu.ui.component.rememberLoadingDialog import me.weishu.kernelsu.ui.component.rememberLoadingDialog
import me.weishu.kernelsu.ui.screen.destinations.AppProfileTemplateScreenDestination import me.weishu.kernelsu.ui.screen.destinations.AppProfileTemplateScreenDestination
import me.weishu.kernelsu.ui.screen.destinations.FlashScreenDestination
import me.weishu.kernelsu.ui.util.getBugreportFile import me.weishu.kernelsu.ui.util.getBugreportFile
import me.weishu.kernelsu.ui.util.shrinkModules import me.weishu.kernelsu.ui.util.shrinkModules
@@ -214,7 +215,7 @@ fun SettingScreen(navigator: DestinationsNavigator) {
val lkmMode = Natives.version >= Natives.MINIMAL_SUPPORTED_KERNEL_LKM && Natives.isLkmMode val lkmMode = Natives.version >= Natives.MINIMAL_SUPPORTED_KERNEL_LKM && Natives.isLkmMode
if (lkmMode) { if (lkmMode) {
UninstallItem { UninstallItem(navigator) {
loadingDialog.withLoading(it) loadingDialog.withLoading(it)
} }
} }
@@ -237,7 +238,10 @@ fun SettingScreen(navigator: DestinationsNavigator) {
} }
@Composable @Composable
fun UninstallItem(withLoading: suspend (suspend () -> Unit) -> Unit) { fun UninstallItem(
navigator: DestinationsNavigator,
withLoading: suspend (suspend () -> Unit) -> Unit
) {
val context = LocalContext.current val context = LocalContext.current
val scope = rememberCoroutineScope() val scope = rememberCoroutineScope()
val uninstallConfirmDialog = rememberConfirmDialog() val uninstallConfirmDialog = rememberConfirmDialog()
@@ -255,7 +259,9 @@ fun UninstallItem(withLoading: suspend (suspend () -> Unit) -> Unit) {
when (uninstallType) { when (uninstallType) {
UninstallType.TEMPORARY -> showTodo() UninstallType.TEMPORARY -> showTodo()
UninstallType.PERMANENT -> showTodo() UninstallType.PERMANENT -> showTodo()
UninstallType.RESTORE_STOCK_IMAGE -> showTodo() UninstallType.RESTORE_STOCK_IMAGE -> navigator.navigate(
FlashScreenDestination(FlashIt.FlashRestore)
)
UninstallType.NONE -> Unit UninstallType.NONE -> Unit
} }
} }

View File

@@ -106,7 +106,10 @@ fun uninstallModule(id: String): Boolean {
} }
fun installModule( fun installModule(
uri: Uri, onFinish: (Boolean) -> Unit, onStdout: (String) -> Unit, onStderr: (String) -> Unit uri: Uri,
onFinish: (Boolean, Int) -> Unit,
onStdout: (String) -> Unit,
onStderr: (String) -> Unit
): Boolean { ): Boolean {
val resolver = ksuApp.contentResolver val resolver = ksuApp.contentResolver
with(resolver.openInputStream(uri)) { with(resolver.openInputStream(uri)) {
@@ -137,11 +140,38 @@ fun installModule(
file.delete() file.delete()
onFinish(result.isSuccess) onFinish(result.isSuccess, result.code)
return result.isSuccess return result.isSuccess
} }
} }
fun restoreBoot(
onFinish: (Boolean, Int) -> Unit, onStdout: (String) -> Unit, onStderr: (String) -> Unit
): Boolean {
val shell = createRootShell()
val magiskboot = File(ksuApp.applicationInfo.nativeLibraryDir, "libmagiskboot.so")
val stdoutCallback: CallbackList<String?> = object : CallbackList<String?>() {
override fun onAddElement(s: String?) {
onStdout(s ?: "")
}
}
val stderrCallback: CallbackList<String?> = object : CallbackList<String?>() {
override fun onAddElement(s: String?) {
onStderr(s ?: "")
}
}
val result =
shell.newJob().add("${getKsuDaemonPath()} boot-restore -f --magiskboot $magiskboot")
.to(stdoutCallback, stderrCallback)
.exec()
onFinish(result.isSuccess, result.code)
return result.isSuccess
}
suspend fun shrinkModules(): Boolean = withContext(Dispatchers.IO) { suspend fun shrinkModules(): Boolean = withContext(Dispatchers.IO) {
execKsud("module shrink", true) execKsud("module shrink", true)
} }
@@ -157,7 +187,7 @@ fun installBoot(
bootUri: Uri?, bootUri: Uri?,
lkm: LkmSelection, lkm: LkmSelection,
ota: Boolean, ota: Boolean,
onFinish: (Boolean) -> Unit, onFinish: (Boolean, Int) -> Unit,
onStdout: (String) -> Unit, onStdout: (String) -> Unit,
onStderr: (String) -> Unit, onStderr: (String) -> Unit,
): Boolean { ): Boolean {
@@ -238,7 +268,7 @@ fun installBoot(
lkmFile?.delete() lkmFile?.delete()
// if boot uri is empty, it is direct install, when success, we should show reboot button // if boot uri is empty, it is direct install, when success, we should show reboot button
onFinish(bootUri == null && result.isSuccess) onFinish(bootUri == null && result.isSuccess, result.code)
return result.isSuccess return result.isSuccess
} }

View File

@@ -124,4 +124,8 @@
<string name="settings_uninstall_temporary_message">临时卸载 KernelSU下次重启后恢复</string> <string name="settings_uninstall_temporary_message">临时卸载 KernelSU下次重启后恢复</string>
<string name="settings_uninstall_permanent_message">完全并永久移除 KernelSU 和所有模块</string> <string name="settings_uninstall_permanent_message">完全并永久移除 KernelSU 和所有模块</string>
<string name="settings_restore_stock_image_message">恢复原厂镜像,一般在 OTA 前使用;如需卸载请使用“永久卸载”</string> <string name="settings_restore_stock_image_message">恢复原厂镜像,一般在 OTA 前使用;如需卸载请使用“永久卸载”</string>
<string name="flashing">刷写中</string>
<string name="flash_success">刷写完成</string>
<string name="flash_failed">刷写失败</string>
<string name="selected_lkm">选择的 LKM %s</string>
</resources> </resources>

View File

@@ -126,4 +126,8 @@
<string name="settings_uninstall_temporary_message">Temporarily uninstall KernelSU, restore to original state after next reboot.</string> <string name="settings_uninstall_temporary_message">Temporarily uninstall KernelSU, restore to original state after next reboot.</string>
<string name="settings_uninstall_permanent_message">Uninstalling KernelSU(Root and all modules) completely and permanently.</string> <string name="settings_uninstall_permanent_message">Uninstalling KernelSU(Root and all modules) completely and permanently.</string>
<string name="settings_restore_stock_image_message">Restore the stock factory image (if a backup exists), usually used before OTA; if you need to uninstall KernelSU, please use \"Permanent Uninstall\".</string> <string name="settings_restore_stock_image_message">Restore the stock factory image (if a backup exists), usually used before OTA; if you need to uninstall KernelSU, please use \"Permanent Uninstall\".</string>
<string name="flashing">Flashing</string>
<string name="flash_success">Flash success</string>
<string name="flash_failed">Flash failed</string>
<string name="selected_lkm">Selected lkm: %s</string>
</resources> </resources>

View File

@@ -1,17 +1,18 @@
#[cfg(unix)] #[cfg(unix)]
use std::os::unix::fs::PermissionsExt; use std::os::unix::fs::PermissionsExt;
use std::path::Path;
use std::path::PathBuf;
use std::process::Command;
use std::process::Stdio;
use anyhow::anyhow; use anyhow::anyhow;
use anyhow::bail; use anyhow::bail;
use anyhow::ensure; use anyhow::ensure;
use anyhow::Context; use anyhow::Context;
use anyhow::Result; use anyhow::Result;
use std::path::Path;
use std::path::PathBuf;
use std::process::Command;
use std::process::Stdio;
use which::which; use which::which;
use crate::defs::{KSU_BACKUP_DIR, KSU_BACKUP_FILE_PREFIX};
use crate::{assets, utils}; use crate::{assets, utils};
#[cfg(target_os = "android")] #[cfg(target_os = "android")]
@@ -152,8 +153,7 @@ pub fn restore(
magiskboot_path: Option<PathBuf>, magiskboot_path: Option<PathBuf>,
flash: bool, flash: bool,
) -> Result<()> { ) -> Result<()> {
let workding_dir = let workding_dir = tempdir::TempDir::new("KernelSU").context("create temp dir failed")?;
tempdir::TempDir::new("KernelSU").with_context(|| "create temp dir failed")?;
let magiskboot = find_magiskboot(magiskboot_path, workding_dir.path())?; let magiskboot = find_magiskboot(magiskboot_path, workding_dir.path())?;
let (bootimage, bootdevice) = find_boot_image(&image, false, false, workding_dir.path())?; let (bootimage, bootdevice) = find_boot_image(&image, false, false, workding_dir.path())?;
@@ -171,29 +171,57 @@ pub fn restore(
let is_kernelsu_patched = is_kernelsu_patched(&magiskboot, workding_dir.path())?; let is_kernelsu_patched = is_kernelsu_patched(&magiskboot, workding_dir.path())?;
ensure!(is_kernelsu_patched, "boot image is not patched by KernelSU"); ensure!(is_kernelsu_patched, "boot image is not patched by KernelSU");
// remove kernelsu.ko let mut new_boot = None;
do_cpio_cmd(&magiskboot, workding_dir.path(), "rm kernelsu.ko")?; let mut from_backup = false;
// if init.real is exist, restore it #[cfg(target_os = "android")]
let status = do_cpio_cmd(&magiskboot, workding_dir.path(), "exists init.real").is_ok(); if do_cpio_cmd(&magiskboot, workding_dir.path(), "exists orig.ksu").is_ok() {
if status { do_cpio_cmd(
do_cpio_cmd(&magiskboot, workding_dir.path(), "mv init.real init")?; &magiskboot,
workding_dir.path(),
"extract orig.ksu orig.ksu",
)?;
let sha = std::fs::read(workding_dir.path().join("orig.ksu"))?;
let sha = String::from_utf8(sha)?;
let sha = sha.trim();
let backup_path = format!("{KSU_BACKUP_DIR}/{KSU_BACKUP_FILE_PREFIX}{sha}");
if Path::new(&backup_path).is_file() {
new_boot = Some(PathBuf::from(backup_path));
from_backup = true;
} else {
println!("Warning: no backup {KSU_BACKUP_DIR}/{KSU_BACKUP_FILE_PREFIX}{sha} found!");
}
} else { } else {
let ramdisk = workding_dir.path().join("ramdisk.cpio"); println!("Warning: no backup found!");
std::fs::remove_file(ramdisk)?;
} }
println!("- Repacking boot image"); if new_boot.is_none() {
let status = Command::new(&magiskboot) // remove kernelsu.ko
.current_dir(workding_dir.path()) do_cpio_cmd(&magiskboot, workding_dir.path(), "rm kernelsu.ko")?;
.stdout(Stdio::null())
.stderr(Stdio::null()) // if init.real exists, restore it
.arg("repack") let status = do_cpio_cmd(&magiskboot, workding_dir.path(), "exists init.real").is_ok();
.arg(bootimage.display().to_string()) if status {
.status()?; do_cpio_cmd(&magiskboot, workding_dir.path(), "mv init.real init")?;
ensure!(status.success(), "magiskboot repack failed"); } else {
let ramdisk = workding_dir.path().join("ramdisk.cpio");
std::fs::remove_file(ramdisk)?;
}
println!("- Repacking boot image");
let status = Command::new(&magiskboot)
.current_dir(workding_dir.path())
.stdout(Stdio::null())
.stderr(Stdio::null())
.arg("repack")
.arg(bootimage.display().to_string())
.status()?;
ensure!(status.success(), "magiskboot repack failed");
new_boot = Some(workding_dir.path().join("new-boot.img"));
}
let new_boot = new_boot.unwrap();
let new_boot = workding_dir.path().join("new-boot.img");
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()?;
@@ -203,16 +231,19 @@ pub fn restore(
now.format("%Y%m%d_%H%M%S") now.format("%Y%m%d_%H%M%S")
)); ));
if 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) std::fs::copy(&new_boot, &output_image).context("copy out new boot failed")?;
.with_context(|| "copy out new boot failed".to_string())?;
} }
println!("- Output file is written to"); println!("- Output file is written to");
println!("- {}", output_image.display().to_string().trim_matches('"')); println!("- {}", output_image.display().to_string().trim_matches('"'));
} }
if flash { if flash {
println!("- Flashing new boot image"); if from_backup {
flash_boot(bootdevice, new_boot)?; println!("- Flashing new boot image from {}", new_boot.display());
} else {
println!("- Flashing new boot image");
}
flash_boot(&bootdevice, new_boot)?;
} }
println!("- Done!"); println!("- Done!");
Ok(()) Ok(())
@@ -265,12 +296,13 @@ fn do_patch(
); );
} }
let workding_dir = let workding_dir = tempdir::TempDir::new("KernelSU").context("create temp dir failed")?;
tempdir::TempDir::new("KernelSU").with_context(|| "create temp dir failed")?;
let (bootimage, bootdevice) = let (bootimage, bootdevice) =
find_boot_image(&image, ota, is_replace_kernel, workding_dir.path())?; find_boot_image(&image, ota, is_replace_kernel, workding_dir.path())?;
let bootimage = bootimage.display().to_string();
// try extract magiskboot/bootctl // try extract magiskboot/bootctl
let _ = assets::ensure_binaries(false); let _ = assets::ensure_binaries(false);
@@ -279,20 +311,20 @@ fn do_patch(
if let Some(kernel) = kernel { if let Some(kernel) = kernel {
std::fs::copy(kernel, workding_dir.path().join("kernel")) std::fs::copy(kernel, workding_dir.path().join("kernel"))
.with_context(|| "copy kernel from failed".to_string())?; .context("copy kernel from failed")?;
} }
println!("- Preparing assets"); println!("- Preparing assets");
let kmod_file = workding_dir.path().join("kernelsu.ko"); let kmod_file = workding_dir.path().join("kernelsu.ko");
if let Some(kmod) = kmod { if let Some(kmod) = kmod {
std::fs::copy(kmod, kmod_file).with_context(|| "copy kernel module failed".to_string())?; std::fs::copy(kmod, kmod_file).context("copy kernel module failed")?;
} else { } else {
// If kmod is not specified, extract from assets // If kmod is not specified, extract from assets
let kmi = if let Some(kmi) = kmi { let kmi = if let Some(kmi) = kmi {
kmi kmi
} else { } else {
get_current_kmi().with_context(|| "Unknown KMI, please choose LKM manually")? get_current_kmi().context("Unknown KMI, please choose LKM manually")?
}; };
println!("- KMI: {kmi}"); println!("- KMI: {kmi}");
let name = format!("{kmi}_kernelsu.ko"); let name = format!("{kmi}_kernelsu.ko");
@@ -302,9 +334,9 @@ fn do_patch(
let init_file = workding_dir.path().join("init"); let init_file = workding_dir.path().join("init");
if let Some(init) = init { if let Some(init) = init {
std::fs::copy(init, init_file).with_context(|| "copy init failed".to_string())?; std::fs::copy(init, init_file).context("copy init failed")?;
} else { } else {
assets::copy_assets_to_file("ksuinit", init_file).with_context(|| "copy ksuinit failed")?; assets::copy_assets_to_file("ksuinit", init_file).context("copy ksuinit failed")?;
} }
// magiskboot unpack boot.img // magiskboot unpack boot.img
@@ -318,7 +350,7 @@ fn do_patch(
.stdout(Stdio::null()) .stdout(Stdio::null())
.stderr(Stdio::null()) .stderr(Stdio::null())
.arg("unpack") .arg("unpack")
.arg(bootimage.display().to_string()) .arg(&bootimage)
.status()?; .status()?;
ensure!(status.success(), "magiskboot unpack failed"); ensure!(status.success(), "magiskboot unpack failed");
@@ -331,12 +363,45 @@ fn do_patch(
println!("- Adding KernelSU LKM"); println!("- Adding KernelSU LKM");
let is_kernelsu_patched = is_kernelsu_patched(&magiskboot, workding_dir.path())?; let is_kernelsu_patched = is_kernelsu_patched(&magiskboot, workding_dir.path())?;
#[cfg(target_os = "android")]
let mut backup = None;
if !is_kernelsu_patched { if !is_kernelsu_patched {
// kernelsu.ko is not exist, backup init if necessary // kernelsu.ko is not exist, backup init if necessary
let status = do_cpio_cmd(&magiskboot, workding_dir.path(), "exists init"); let status = do_cpio_cmd(&magiskboot, workding_dir.path(), "exists init");
if status.is_ok() { if status.is_ok() {
do_cpio_cmd(&magiskboot, workding_dir.path(), "mv init init.real")?; do_cpio_cmd(&magiskboot, workding_dir.path(), "mv init init.real")?;
} }
println!("- Backup stock boot image");
// magiskboot cpio ramdisk.cpio 'add 0755 orig.ksu'
let output = Command::new(&magiskboot)
.current_dir(workding_dir.path())
.arg("sha1")
.arg(&bootimage)
.output()?;
ensure!(
output.status.success(),
"Cannot calculate sha1 of original boot!"
);
#[cfg(target_os = "android")]
{
let output = String::from_utf8(output.stdout)?;
let output = output.trim();
let output = format!("{KSU_BACKUP_FILE_PREFIX}{output}");
let target = format!("{KSU_BACKUP_DIR}/{output}");
std::fs::copy(&bootimage, &target).with_context(|| format!("backup to {target}"))?;
std::fs::write(workding_dir.path().join("orig.ksu"), output.as_bytes())
.context("write sha1")?;
do_cpio_cmd(
&magiskboot,
workding_dir.path(),
"add 0755 orig.ksu orig.ksu",
)?;
println!("- Stock image has been backup to");
println!("- {target}");
backup = Some(output);
}
} }
do_cpio_cmd(&magiskboot, workding_dir.path(), "add 0755 init init")?; do_cpio_cmd(&magiskboot, workding_dir.path(), "add 0755 init init")?;
@@ -353,7 +418,7 @@ fn do_patch(
.stdout(Stdio::null()) .stdout(Stdio::null())
.stderr(Stdio::null()) .stderr(Stdio::null())
.arg("repack") .arg("repack")
.arg(bootimage.display().to_string()) .arg(&bootimage)
.status()?; .status()?;
ensure!(status.success(), "magiskboot repack failed"); ensure!(status.success(), "magiskboot repack failed");
let new_boot = workding_dir.path().join("new-boot.img"); let new_boot = workding_dir.path().join("new-boot.img");
@@ -368,8 +433,7 @@ fn do_patch(
)); ));
if std::fs::rename(&new_boot, &output_image).is_err() { if std::fs::rename(&new_boot, &output_image).is_err() {
std::fs::copy(&new_boot, &output_image) std::fs::copy(&new_boot, &output_image).context("copy out new boot failed")?;
.with_context(|| "copy out new boot failed".to_string())?;
} }
println!("- Output file is written to"); println!("- Output file is written to");
println!("- {}", output_image.display().to_string().trim_matches('"')); println!("- {}", output_image.display().to_string().trim_matches('"'));
@@ -377,27 +441,48 @@ fn do_patch(
if flash { if flash {
println!("- Flashing new boot image"); println!("- Flashing new boot image");
flash_boot(bootdevice, new_boot)?; flash_boot(&bootdevice, new_boot)?;
if ota { if ota {
post_ota()?; post_ota()?;
} }
} }
#[cfg(target_os = "android")]
if let Some(backup) = backup {
println!("- Clean up backup");
if let Ok(dir) = std::fs::read_dir("/data") {
for entry in dir.flatten() {
let path = entry.path();
if path.is_file() {
if let Some(name) = path.file_name() {
let name = name.to_string_lossy().to_string();
if name != backup
&& name.starts_with(KSU_BACKUP_FILE_PREFIX)
&& std::fs::remove_file(path).is_ok()
{
println!("- removed {name}");
}
}
}
}
}
}
println!("- Done!"); println!("- Done!");
Ok(()) Ok(())
} }
fn flash_boot(bootdevice: Option<String>, new_boot: PathBuf) -> Result<()> { fn flash_boot(bootdevice: &Option<String>, new_boot: PathBuf) -> Result<()> {
let Some(bootdevice) = bootdevice else { let Some(bootdevice) = bootdevice else {
bail!("boot device not found") bail!("boot device not found")
}; };
let status = Command::new("blockdev") let status = Command::new("blockdev")
.arg("--setrw") .arg("--setrw")
.arg(&bootdevice) .arg(bootdevice)
.status()?; .status()?;
ensure!(status.success(), "set boot device rw failed"); ensure!(status.success(), "set boot device rw failed");
dd(new_boot, &bootdevice).with_context(|| "flash boot failed")?; dd(new_boot, bootdevice).context("flash boot failed")?;
Ok(()) Ok(())
} }
@@ -413,7 +498,7 @@ fn find_magiskboot(magiskboot_path: Option<PathBuf>, workding_dir: &Path) -> Res
} else { } else {
let magiskboot_path = workding_dir.join("magiskboot"); let magiskboot_path = workding_dir.join("magiskboot");
assets::copy_assets_to_file("magiskboot", &magiskboot_path) assets::copy_assets_to_file("magiskboot", &magiskboot_path)
.with_context(|| "copy magiskboot failed")?; .context("copy magiskboot failed")?;
magiskboot_path magiskboot_path
}; };
ensure!(magiskboot.exists(), "{magiskboot:?} is not exist"); ensure!(magiskboot.exists(), "{magiskboot:?} is not exist");

View File

@@ -38,3 +38,6 @@ pub const SKIP_MOUNT_FILE_NAME: &str = "skip_mount";
pub const VERSION_CODE: &str = include_str!(concat!(env!("OUT_DIR"), "/VERSION_CODE")); pub const VERSION_CODE: &str = include_str!(concat!(env!("OUT_DIR"), "/VERSION_CODE"));
pub const VERSION_NAME: &str = include_str!(concat!(env!("OUT_DIR"), "/VERSION_NAME")); pub const VERSION_NAME: &str = include_str!(concat!(env!("OUT_DIR"), "/VERSION_NAME"));
pub const KSU_BACKUP_DIR: &str = "/data";
pub const KSU_BACKUP_FILE_PREFIX: &str = "ksu_backup_";