diff --git a/userspace/ksud/src/defs.rs b/userspace/ksud/src/defs.rs index c7b377a6386a..3ce7eab15ac8 100644 --- a/userspace/ksud/src/defs.rs +++ b/userspace/ksud/src/defs.rs @@ -17,11 +17,13 @@ pub const MAGISKBOOT_PATH: &str = concatcp!(BINARY_DIR, "magiskboot"); pub const DAEMON_LINK_PATH: &str = concatcp!(BINARY_DIR, "ksud"); pub const MODULE_DIR: &str = concatcp!(ADB_DIR, "modules/"); +pub const MODULE_UPDATE_DIR: &str = concatcp!(ADB_DIR, "modules_update/"); pub const METAMODULE_DIR: &str = concatcp!(ADB_DIR, "metamodule/"); pub const MODULE_WEB_DIR: &str = "webroot"; pub const MODULE_ACTION_SH: &str = "action.sh"; pub const DISABLE_FILE_NAME: &str = "disable"; +pub const UPDATE_FILE_NAME: &str = "update"; pub const REMOVE_FILE_NAME: &str = "remove"; // Metamodule support diff --git a/userspace/ksud/src/init_event.rs b/userspace/ksud/src/init_event.rs index 5fd5418e615e..002e1c293f86 100644 --- a/userspace/ksud/src/init_event.rs +++ b/userspace/ksud/src/init_event.rs @@ -2,7 +2,7 @@ use anyhow::{Context, Result}; use log::{info, warn}; use std::path::Path; -use crate::module::prune_modules; +use crate::module::{handle_updated_modules, prune_modules}; use crate::utils::is_safe_mode; use crate::{ assets, defs, ksucalls, metamodule, restorecon, @@ -54,6 +54,10 @@ pub fn on_post_data_fs() -> Result<()> { warn!("prune modules failed: {e}"); } + if let Err(e) = handle_updated_modules() { + warn!("handle updated modules failed: {e}"); + } + if let Err(e) = restorecon::restorecon() { warn!("restorecon failed: {e}"); } diff --git a/userspace/ksud/src/installer.sh b/userspace/ksud/src/installer.sh index 182027ba0c76..00ad86d74012 100644 --- a/userspace/ksud/src/installer.sh +++ b/userspace/ksud/src/installer.sh @@ -368,6 +368,7 @@ install_module() { [ ! -f $TMPDIR/module.prop ] && abort "! Unable to extract zip file!" local MODDIRNAME=modules + $BOOTMODE && MODDIRNAME=modules_update local MODULEROOT=$NVBASE/$MODDIRNAME MODID=`grep_prop id $TMPDIR/module.prop` MODNAME=`grep_prop name $TMPDIR/module.prop` @@ -439,6 +440,13 @@ install_module() { handle_partition product handle_partition odm + if $BOOTMODE; then + mktouch $NVBASE/modules/$MODID/update + rm -rf $NVBASE/modules/$MODID/remove 2>/dev/null + rm -rf $NVBASE/modules/$MODID/disable 2>/dev/null + cp -af $MODPATH/module.prop $NVBASE/modules/$MODID/module.prop + fi + # Remove stuff that doesn't belong to modules and clean up any empty directories rm -rf \ $MODPATH/system/placeholder $MODPATH/customize.sh \ diff --git a/userspace/ksud/src/metamodule.rs b/userspace/ksud/src/metamodule.rs index d73ec15de44b..a45c0157a648 100644 --- a/userspace/ksud/src/metamodule.rs +++ b/userspace/ksud/src/metamodule.rs @@ -12,6 +12,7 @@ use std::{ process::Command, }; +use crate::module::ModuleType::All; use crate::{assets, defs}; /// Determine whether the provided module properties mark it as a metamodule @@ -53,7 +54,7 @@ pub fn get_metamodule_path() -> Option { // Fallback: search for metamodule=1 in modules directory let mut result = None; - let _ = crate::module::foreach_module(false, |module_path| { + let _ = crate::module::foreach_module(All, |module_path| { if let Ok(props) = crate::module::read_module_prop(module_path) && is_metamodule(&props) { diff --git a/userspace/ksud/src/module.rs b/userspace/ksud/src/module.rs index 58fa90229771..b85022b78d4f 100644 --- a/userspace/ksud/src/module.rs +++ b/userspace/ksud/src/module.rs @@ -12,6 +12,7 @@ use is_executable::is_executable; use java_properties::PropertiesIter; use log::{info, warn}; +use std::fs::{copy, rename}; use std::{ collections::HashMap, env::var as env_var, @@ -23,6 +24,8 @@ use std::{ }; use zip_extensions::zip_extract_file_to_memory; +use crate::defs::{MODULE_DIR, MODULE_UPDATE_DIR, UPDATE_FILE_NAME}; +use crate::module::ModuleType::{Active, All}; #[cfg(unix)] use std::os::unix::{prelude::PermissionsExt, process::CommandExt}; @@ -82,11 +85,21 @@ fn ensure_boot_completed() -> Result<()> { Ok(()) } +#[derive(PartialEq, Eq)] +pub(crate) enum ModuleType { + All, + Active, + Updated, +} + pub(crate) fn foreach_module( - active_only: bool, + module_type: ModuleType, mut f: impl FnMut(&Path) -> Result<()>, ) -> Result<()> { - let modules_dir = Path::new(defs::MODULE_DIR); + let modules_dir = Path::new(match module_type { + ModuleType::Updated => MODULE_UPDATE_DIR, + _ => defs::MODULE_DIR, + }); let dir = std::fs::read_dir(modules_dir)?; for entry in dir.flatten() { let path = entry.path(); @@ -95,11 +108,11 @@ pub(crate) fn foreach_module( continue; } - if active_only && path.join(defs::DISABLE_FILE_NAME).exists() { + if module_type == Active && path.join(defs::DISABLE_FILE_NAME).exists() { info!("{} is disabled, skip", path.display()); continue; } - if active_only && path.join(defs::REMOVE_FILE_NAME).exists() { + if module_type == Active && path.join(defs::REMOVE_FILE_NAME).exists() { warn!("{} is removed, skip", path.display()); continue; } @@ -111,7 +124,7 @@ pub(crate) fn foreach_module( } fn foreach_active_module(f: impl FnMut(&Path) -> Result<()>) -> Result<()> { - foreach_module(true, f) + foreach_module(Active, f) } pub fn load_sepolicy_rule() -> Result<()> { @@ -218,7 +231,7 @@ pub fn load_system_prop() -> Result<()> { } pub fn prune_modules() -> Result<()> { - foreach_module(false, |module| { + foreach_module(All, |module| { if !module.join(defs::REMOVE_FILE_NAME).exists() { return Ok(()); } @@ -273,6 +286,29 @@ pub fn prune_modules() -> Result<()> { Ok(()) } +pub fn handle_updated_modules() -> Result<()> { + let modules_root = Path::new(MODULE_DIR); + foreach_module(ModuleType::Updated, |module| { + if !module.is_dir() { + return Ok(()); + } + + if let Some(name) = module.file_name() { + let old_dir = modules_root.join(name); + if old_dir.exists() + && let Err(e) = remove_dir_all(&old_dir) + { + log::error!("Failed to remove old {}: {}", old_dir.display(), e); + } + if let Err(e) = rename(module, &old_dir) { + log::error!("Failed to move new module {}: {}", module.display(), e); + } + } + Ok(()) + })?; + Ok(()) +} + fn _install_module(zip: &str) -> Result<()> { ensure_boot_completed()?; @@ -308,8 +344,8 @@ fn _install_module(zip: &str) -> Result<()> { // Check if this module is a metamodule let is_metamodule = metamodule::is_metamodule(&module_prop); - // All modules (including metamodules) are installed to MODULE_DIR - let target_dir = Path::new(defs::MODULE_DIR).join(module_id); + // All modules (including metamodules) are installed to MODULE_UPDATE_DIR + let updated_dir = Path::new(defs::MODULE_UPDATE_DIR).join(module_id); if is_metamodule { info!("Installing metamodule: {}", module_id); @@ -352,22 +388,22 @@ fn _install_module(zip: &str) -> Result<()> { ); // Ensure module directory exists and set SELinux context - ensure_dir_exists(defs::MODULE_DIR)?; - setsyscon(defs::MODULE_DIR)?; + ensure_dir_exists(defs::MODULE_UPDATE_DIR)?; + setsyscon(defs::MODULE_UPDATE_DIR)?; // Prepare target directory - println!("- Installing to {}", target_dir.display()); - ensure_clean_dir(&target_dir)?; - info!("target dir: {}", target_dir.display()); + println!("- Installing to {}", updated_dir.display()); + ensure_clean_dir(&updated_dir)?; + info!("target dir: {}", updated_dir.display()); // Extract zip to target directory println!("- Extracting module files"); let file = File::open(zip)?; let mut archive = zip::ZipArchive::new(file)?; - archive.extract(&target_dir)?; + archive.extract(&updated_dir)?; // Set permission and selinux context for $MOD/system - let module_system_dir = target_dir.join("system"); + let module_system_dir = updated_dir.join("system"); if module_system_dir.exists() { #[cfg(unix)] set_permissions(&module_system_dir, Permissions::from_mode(0o755))?; @@ -378,10 +414,18 @@ fn _install_module(zip: &str) -> Result<()> { println!("- Running module installer"); exec_install_script(zip, is_metamodule)?; + let module_dir = Path::new(MODULE_DIR).join(module_id); + ensure_dir_exists(&module_dir)?; + copy( + updated_dir.join("module.prop"), + module_dir.join("module.prop"), + )?; + ensure_file_exists(module_dir.join(UPDATE_FILE_NAME))?; + // Create symlink for metamodule if is_metamodule { println!("- Creating metamodule symlink"); - metamodule::ensure_symlink(&target_dir)?; + metamodule::ensure_symlink(&module_dir)?; } println!("- Module installed successfully!");