diff --git a/manager/app/src/main/java/com/sukisu/ultra/ui/MainActivity.kt b/manager/app/src/main/java/com/sukisu/ultra/ui/MainActivity.kt index b6ce2aed..5b439f8d 100644 --- a/manager/app/src/main/java/com/sukisu/ultra/ui/MainActivity.kt +++ b/manager/app/src/main/java/com/sukisu/ultra/ui/MainActivity.kt @@ -34,7 +34,6 @@ import com.sukisu.ultra.ui.util.install import com.sukisu.ultra.ui.viewmodel.HomeViewModel import com.sukisu.ultra.ui.viewmodel.SuperUserViewModel import com.sukisu.ultra.ui.webui.initPlatform -import io.sukisu.ultra.UltraToolInstall import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.launch import zako.zako.zako.zakoui.activity.component.BottomBar @@ -197,7 +196,6 @@ class MainActivity : ComponentActivity() { val isManager = Natives.becomeManager(packageName) if (isManager) { install() - UltraToolInstall.tryToInstall() } } diff --git a/manager/app/src/main/java/io/sukisu/ultra/UltraShellHelper.java b/manager/app/src/main/java/io/sukisu/ultra/UltraShellHelper.java deleted file mode 100644 index f829f614..00000000 --- a/manager/app/src/main/java/io/sukisu/ultra/UltraShellHelper.java +++ /dev/null @@ -1,29 +0,0 @@ -package io.sukisu.ultra; - -import java.util.ArrayList; - -import com.sukisu.ultra.ui.util.KsuCli; - -public class UltraShellHelper { - public static String runCmd(String cmds) { - StringBuilder sb = new StringBuilder(); - for(String str : KsuCli.INSTANCE.getGLOBAL_MNT_SHELL() - .newJob() - .add(cmds) - .to(new ArrayList<>(), null) - .exec() - .getOut()) { - sb.append(str).append("\n"); - } - return sb.toString(); - } - - public static boolean isPathExists(String path) { - String result = runCmd("test -f '" + path + "' && echo 'exists'"); - return result.contains("exists"); - } - - public static void CopyFileTo(String path, String target) { - runCmd("cp -f '" + path + "' '" + target + "' 2>&1"); - } -} diff --git a/manager/app/src/main/java/io/sukisu/ultra/UltraToolInstall.java b/manager/app/src/main/java/io/sukisu/ultra/UltraToolInstall.java deleted file mode 100644 index 0dd0a8b1..00000000 --- a/manager/app/src/main/java/io/sukisu/ultra/UltraToolInstall.java +++ /dev/null @@ -1,17 +0,0 @@ -package io.sukisu.ultra; - -import static com.sukisu.ultra.ui.util.KsuCliKt.*; -import android.annotation.SuppressLint; - -public class UltraToolInstall { - private static final String OUTSIDE_SUSFSD_PATH = "/data/adb/ksu/bin/susfsd"; - - @SuppressLint("SetWorldReadable") - public static void tryToInstall() { - String SuSFSDaemonPath = getSuSFSDaemonPath(); - if (UltraShellHelper.isPathExists(OUTSIDE_SUSFSD_PATH)) { - UltraShellHelper.CopyFileTo(SuSFSDaemonPath, OUTSIDE_SUSFSD_PATH); - UltraShellHelper.runCmd("chmod a+rx " + OUTSIDE_SUSFSD_PATH); - } - } -} diff --git a/userspace/ksud/src/kpm.rs b/userspace/ksud/src/kpm.rs index 85f377a5..83248b6d 100644 --- a/userspace/ksud/src/kpm.rs +++ b/userspace/ksud/src/kpm.rs @@ -1,14 +1,17 @@ use anyhow::{anyhow, bail, Result}; use libc::{c_int, c_ulong, prctl}; use notify::{RecursiveMode, Watcher}; -use std::ffi::{CString, OsStr}; -use std::fs; -use std::os::unix::fs::PermissionsExt; -use std::path::{Path, PathBuf}; -use std::ptr; +use std::{ + ffi::{CStr, CString, OsStr}, + path::{Path, PathBuf}, + os::unix::fs::PermissionsExt, + fs, + ptr, +}; pub const KPM_DIR: &str = "/data/adb/kpm"; +// SukiSU KPM prctl command space const KSU_OPTIONS: c_int = 0xdeadbeef_u32 as c_int; const SUKISU_KPM_LOAD: c_int = 28; const SUKISU_KPM_UNLOAD: c_int = 29; @@ -18,239 +21,224 @@ const SUKISU_KPM_INFO: c_int = 32; const SUKISU_KPM_CONTROL:c_int = 33; const SUKISU_KPM_VERSION:c_int = 34; +/// Convert raw kernel return code to `Result`. #[inline(always)] -fn check_out(out: c_int) -> Result { - if out < 0 { - bail!("KPM error: {}", std::io::Error::from_raw_os_error(-out)); +fn check_out(rc: c_int) -> Result { + if rc < 0 { + bail!("KPM error: {}", std::io::Error::from_raw_os_error(-rc)); } - Ok(out) + Ok(rc) } +/// Load a `.kpm` into kernel space. pub fn kpm_load(path: &str, args: Option<&str>) -> Result<()> { let path_c = CString::new(path)?; let args_c = args.map(CString::new).transpose()?; - let mut out: c_int = -1; + let mut rc: c_int = -1; + // SAFETY: pointers live through the prctl; null-check done by CString. unsafe { prctl( KSU_OPTIONS, SUKISU_KPM_LOAD, path_c.as_ptr() as c_ulong, - args_c.as_ref() - .map_or(ptr::null(), |s| s.as_ptr()) as c_ulong, - &mut out as *mut c_int as c_ulong, + args_c.as_ref().map_or(ptr::null(), |s| s.as_ptr()) as c_ulong, + &mut rc as *mut c_int as c_ulong, ); } - check_out(out)?; + check_out(rc)?; println!("Success"); Ok(()) } +/// Unload by module name. pub fn kpm_unload(name: &str) -> Result<()> { let name_c = CString::new(name)?; - let mut out: c_int = -1; + let mut rc = -1; unsafe { - prctl( - KSU_OPTIONS, - SUKISU_KPM_UNLOAD, - name_c.as_ptr() as c_ulong, - 0, - &mut out as *mut c_int as c_ulong, - ); + prctl(KSU_OPTIONS, SUKISU_KPM_UNLOAD, name_c.as_ptr() as c_ulong, 0, &mut rc as *mut _ as c_ulong); } - check_out(out)?; + check_out(rc)?; Ok(()) } +/// Return loaded module count. pub fn kpm_num() -> Result { - let mut out: c_int = -1; - unsafe { - prctl( - KSU_OPTIONS, - SUKISU_KPM_NUM, - 0, - 0, - &mut out as *mut c_int as c_ulong, - ); - } - let n = check_out(out)?; - println!("{}", n); + let mut rc = -1; + unsafe { prctl(KSU_OPTIONS, SUKISU_KPM_NUM, 0, 0, &mut rc as *mut _ as c_ulong) }; + let n = check_out(rc)?; + println!("{n}"); Ok(n) } +/// Print name list of loaded modules. pub fn kpm_list() -> Result<()> { let mut buf = vec![0u8; 1024]; - let mut out: c_int = -1; + let mut rc = -1; unsafe { prctl( KSU_OPTIONS, SUKISU_KPM_LIST, buf.as_mut_ptr() as c_ulong, buf.len() as c_ulong, - &mut out as *mut c_int as c_ulong, + &mut rc as *mut _ as c_ulong, ); } - check_out(out)?; - print!("{}", buf2string(&buf)); + check_out(rc)?; + print!("{}", buf2str(&buf)); Ok(()) } +/// Print single module info. pub fn kpm_info(name: &str) -> Result<()> { let name_c = CString::new(name)?; let mut buf = vec![0u8; 256]; - let mut out: c_int = -1; + let mut rc = -1; unsafe { prctl( KSU_OPTIONS, SUKISU_KPM_INFO, name_c.as_ptr() as c_ulong, buf.as_mut_ptr() as c_ulong, - &mut out as *mut c_int as c_ulong, + &mut rc as *mut _ as c_ulong, ); } - check_out(out)?; - println!("{}", buf2string(&buf)); + check_out(rc)?; + println!("{}", buf2str(&buf)); Ok(()) } +/// Send control string to a module; returns kernel answer. pub fn kpm_control(name: &str, args: &str) -> Result { let name_c = CString::new(name)?; let args_c = CString::new(args)?; - let mut out: c_int = -1; + let mut rc = -1; unsafe { prctl( KSU_OPTIONS, SUKISU_KPM_CONTROL, name_c.as_ptr() as c_ulong, args_c.as_ptr() as c_ulong, - &mut out as *mut c_int as c_ulong, + &mut rc as *mut _ as c_ulong, ); } - check_out(out)?; - Ok(out) + check_out(rc).map(|v| v as i32) } +/// Print loader version string. pub fn kpm_version_loader() -> Result<()> { let mut buf = vec![0u8; 1024]; - let mut out: c_int = -1; + let mut rc = -1; unsafe { prctl( KSU_OPTIONS, SUKISU_KPM_VERSION, buf.as_mut_ptr() as c_ulong, buf.len() as c_ulong, - &mut out as *mut c_int as c_ulong, + &mut rc as *mut _ as c_ulong, ); } - check_out(out)?; - print!("{}", buf2string(&buf)); + check_out(rc)?; + print!("{}", buf2str(&buf)); Ok(()) } +/// Validate loader version; empty or "Error*" => fail. pub fn check_kpm_version() -> Result { let mut buf = vec![0u8; 1024]; - let mut out: c_int = -1; + let mut rc = -1; unsafe { prctl( KSU_OPTIONS, SUKISU_KPM_VERSION, buf.as_mut_ptr() as c_ulong, buf.len() as c_ulong, - &mut out as *mut c_int as c_ulong, + &mut rc as *mut _ as c_ulong, ); } - check_out(out)?; - let ver = buf2string(&buf); + check_out(rc)?; + let ver = buf2str(&buf); if ver.is_empty() || ver.starts_with("Error") { - bail!("KPM: Invalid version response: {}", ver); + bail!("KPM: invalid version response: {ver}"); } - log::info!("KPM: Version check result: {}", ver); + log::info!("KPM: version check ok: {ver}"); Ok(ver) } +/// Create `/data/adb/kpm` with 0o777 if missing. pub fn ensure_kpm_dir() -> Result<()> { - let path = Path::new(KPM_DIR); - if !path.exists() { - fs::create_dir_all(path)?; - } - let meta = fs::metadata(path)?; + fs::create_dir_all(KPM_DIR)?; + let meta = fs::metadata(KPM_DIR)?; if meta.permissions().mode() & 0o777 != 0o777 { - fs::set_permissions(path, fs::Permissions::from_mode(0o777))?; + fs::set_permissions(KPM_DIR, fs::Permissions::from_mode(0o777))?; } Ok(()) } +/// Start file watcher for hot-(un)load. pub fn start_kpm_watcher() -> Result<()> { - check_kpm_version()?; // 版本不对直接返回 + check_kpm_version()?; // bails if loader too old ensure_kpm_dir()?; if crate::utils::is_safe_mode() { - log::warn!("KPM: Safe mode – removing all KPM modules"); + log::warn!("KPM: safe-mode – removing all modules"); remove_all_kpms()?; return Ok(()); } - let mut watcher = notify::recommended_watcher(|res| match res { - Ok(event) => handle_kpm_event(event), - Err(e) => log::error!("KPM: File monitor error: {:?}", e), + let mut watcher = notify::recommended_watcher(|res: Result<_, _>| match res { + Ok(evt) => handle_kpm_event(evt), + Err(e) => log::error!("KPM: watcher error: {e:?}"), })?; watcher.watch(Path::new(KPM_DIR), RecursiveMode::NonRecursive)?; - log::info!("KPM: File watcher started on {}", KPM_DIR); + log::info!("KPM: watcher active on {KPM_DIR}"); Ok(()) } -fn handle_kpm_event(event: notify::Event) { - match event.kind { - notify::EventKind::Create(_) => { - for p in event.paths { - if p.extension() == Some(OsStr::new("kpm")) { - if let Err(e) = load_kpm(&p) { - log::warn!("KPM: Failed to load {}: {}", p.display(), e); - } - } +fn handle_kpm_event(evt: notify::Event) { + if let notify::EventKind::Create(_) = evt.kind { + for p in evt.paths { + if p.extension() == Some(OsStr::new("kpm")) && load_kpm(&p).is_err() { + log::warn!("KPM: failed to load {}", p.display()); } } - notify::EventKind::Modify(_) => { - for p in event.paths { - log::info!("KPM: Modified file: {}", p.display()); - } - } - _ => {} } } +/// Load single `.kpm` file. pub fn load_kpm(path: &Path) -> Result<()> { - let path_str = path.to_str().ok_or_else(|| anyhow!("Invalid path"))?; - kpm_load(path_str, None) + let s = path.to_str().ok_or_else(|| anyhow!("bad path"))?; + kpm_load(s, None) } +/// Unload module and delete file. pub fn unload_kpm(name: &str) -> Result<()> { kpm_unload(name)?; if let Some(p) = find_kpm_file(name)? { - fs::remove_file(&p).ok(); - log::info!("KPM: Deleted file {}", p.display()); + let _ = fs::remove_file(&p); + log::info!("KPM: deleted {}", p.display()); } Ok(()) } +/// Locate `/data/adb/kpm/.kpm`. fn find_kpm_file(name: &str) -> Result> { let dir = Path::new(KPM_DIR); - if !dir.exists() { + if !dir.is_dir() { return Ok(None); } for entry in fs::read_dir(dir)? { let p = entry?.path(); - if p.extension() == Some(OsStr::new("kpm")) - && p.file_stem().map_or(false, |s| s == name) - { + if p.extension() == Some(OsStr::new("kpm")) && p.file_stem() == Some(OsStr::new(name)) { return Ok(Some(p)); } } Ok(None) } +/// Remove every `.kpm` file and unload it. pub fn remove_all_kpms() -> Result<()> { let dir = Path::new(KPM_DIR); - if !dir.exists() { + if !dir.is_dir() { return Ok(()); } for entry in fs::read_dir(dir)? { @@ -258,7 +246,7 @@ pub fn remove_all_kpms() -> Result<()> { if p.extension() == Some(OsStr::new("kpm")) { if let Some(name) = p.file_stem().and_then(|s| s.to_str()) { if let Err(e) = unload_kpm(name) { - log::error!("KPM: Failed to unload {}: {}", name, e); + log::error!("KPM: unload {name} failed: {e}"); } } } @@ -266,11 +254,12 @@ pub fn remove_all_kpms() -> Result<()> { Ok(()) } +/// Bulk-load existing `.kpm`s at boot. pub fn load_kpm_modules() -> Result<()> { check_kpm_version()?; ensure_kpm_dir()?; let dir = Path::new(KPM_DIR); - if !dir.exists() { + if !dir.is_dir() { return Ok(()); } let (mut ok, mut ng) = (0, 0); @@ -280,20 +269,18 @@ pub fn load_kpm_modules() -> Result<()> { match load_kpm(&p) { Ok(_) => ok += 1, Err(e) => { - log::warn!("KPM: Failed to load {}: {}", p.display(), e); + log::warn!("KPM: load {} failed: {e}", p.display()); ng += 1; } } } } - log::info!("KPM: Load done – ok: {}, failed: {}", ok, ng); + log::info!("KPM: bulk-load done – ok: {ok}, failed: {ng}"); Ok(()) } -fn buf2string(buf: &[u8]) -> String { - unsafe { - std::ffi::CStr::from_ptr(buf.as_ptr() as *const _) - .to_string_lossy() - .into_owned() - } +/// Convert zero-padded kernel buffer to owned String. +fn buf2str(buf: &[u8]) -> String { + // SAFETY: buffer is always NUL-terminated by kernel. + unsafe { CStr::from_ptr(buf.as_ptr().cast()).to_string_lossy().into_owned() } } \ No newline at end of file