diff --git a/native/src/Android.mk b/native/src/Android.mk index 12eb79935..3e366896a 100644 --- a/native/src/Android.mk +++ b/native/src/Android.mk @@ -22,7 +22,7 @@ LOCAL_SRC_FILES := \ core/sqlite.cpp \ core/thread.cpp \ core/core-rs.cpp \ - core/resetprop/resetprop.cpp \ + core/resetprop/sys.cpp \ core/su/su.cpp \ core/zygisk/entry.cpp \ core/zygisk/module.cpp \ @@ -123,7 +123,7 @@ LOCAL_STATIC_LIBRARIES := \ LOCAL_SRC_FILES := \ core/applet_stub.cpp \ - core/resetprop/resetprop.cpp \ + core/resetprop/sys.cpp \ core/core-rs.cpp LOCAL_CFLAGS := -DAPPLET_STUB_MAIN=resetprop_main diff --git a/native/src/Cargo.lock b/native/src/Cargo.lock index ea1df6cd3..c9db8f848 100644 --- a/native/src/Cargo.lock +++ b/native/src/Cargo.lock @@ -601,6 +601,7 @@ dependencies = [ name = "magisk" version = "0.0.0" dependencies = [ + "argh", "base", "bit-set", "bytemuck", diff --git a/native/src/core/Cargo.toml b/native/src/core/Cargo.toml index c5accc014..240efb1fe 100644 --- a/native/src/core/Cargo.toml +++ b/native/src/core/Cargo.toml @@ -25,3 +25,4 @@ quick-protobuf = { workspace = true } bytemuck = { workspace = true, features = ["derive"] } thiserror = { workspace = true } bit-set = { workspace = true } +argh = { workspace = true } diff --git a/native/src/core/applet_stub.cpp b/native/src/core/applet_stub.cpp index 271834c6a..2e796368e 100644 --- a/native/src/core/applet_stub.cpp +++ b/native/src/core/applet_stub.cpp @@ -1,7 +1,4 @@ -#include - -#include -#include +#include int main(int argc, char *argv[]) { if (argc < 1) diff --git a/native/src/core/applets.cpp b/native/src/core/applets.cpp index 17e799f30..38d6322e5 100644 --- a/native/src/core/applets.cpp +++ b/native/src/core/applets.cpp @@ -1,9 +1,7 @@ #include -#include #include -#include -#include +#include using namespace std; diff --git a/native/src/core/daemon.rs b/native/src/core/daemon.rs index f86db6755..1632e6050 100644 --- a/native/src/core/daemon.rs +++ b/native/src/core/daemon.rs @@ -2,12 +2,13 @@ use crate::consts::{MAGISK_FULL_VER, MAGISK_PROC_CON, MAIN_CONFIG, ROOTMNT, ROOT use crate::db::Sqlite3; use crate::ffi::{ DbEntryKey, ModuleInfo, RequestCode, check_key_combo, exec_common_scripts, exec_module_scripts, - get_magisk_tmp, get_prop, initialize_denylist, set_prop, setup_magisk_env, + get_magisk_tmp, initialize_denylist, setup_magisk_env, }; use crate::logging::{magisk_logging, setup_logfile, start_log_daemon}; use crate::module::disable_modules; use crate::mount::{clean_mounts, setup_preinit_dir}; use crate::package::ManagerInfo; +use crate::resetprop::{get_prop, set_prop}; use crate::selinux::restore_tmpcon; use crate::su::SuInfo; use crate::zygisk::ZygiskState; @@ -121,8 +122,8 @@ impl MagiskD { .log() .ok(); let safe_mode = boot_cnt >= 2 - || get_prop(cstr!("persist.sys.safemode"), true) == "1" - || get_prop(cstr!("ro.sys.safemode"), false) == "1" + || get_prop(cstr!("persist.sys.safemode")) == "1" + || get_prop(cstr!("ro.sys.safemode")) == "1" || check_key_combo(); if safe_mode { @@ -232,9 +233,9 @@ pub fn daemon_entry() { magisk_logging(); info!("Magisk {} daemon started", MAGISK_FULL_VER); - let is_emulator = get_prop(cstr!("ro.kernel.qemu"), false) == "1" - || get_prop(cstr!("ro.boot.qemu"), false) == "1" - || get_prop(cstr!("ro.product.device"), false).contains("vsoc"); + let is_emulator = get_prop(cstr!("ro.kernel.qemu")) == "1" + || get_prop(cstr!("ro.boot.qemu")) == "1" + || get_prop(cstr!("ro.product.device")).contains("vsoc"); // Load config status let magisk_tmp = get_magisk_tmp(); @@ -265,7 +266,7 @@ pub fn daemon_entry() { } if sdk_int < 0 { // In case some devices do not store this info in build.prop, fallback to getprop - sdk_int = get_prop(cstr!("ro.build.version.sdk"), false) + sdk_int = get_prop(cstr!("ro.build.version.sdk")) .parse::() .unwrap_or(-1); } @@ -278,13 +279,13 @@ pub fn daemon_entry() { switch_cgroup("/acct", pid); switch_cgroup("/dev/cg2_bpf", pid); switch_cgroup("/sys/fs/cgroup", pid); - if get_prop(cstr!("ro.config.per_app_memcg"), false) != "false" { + if get_prop(cstr!("ro.config.per_app_memcg")) != "false" { switch_cgroup("/dev/memcg/apps", pid); } // Samsung workaround #7887 if cstr!("/system_ext/app/mediatek-res/mediatek-res.apk").exists() { - set_prop(cstr!("ro.vendor.mtk_model"), cstr!("0"), false); + set_prop(cstr!("ro.vendor.mtk_model"), cstr!("0")); } // Cleanup pre-init mounts @@ -346,14 +347,14 @@ fn check_data() -> bool { if !mnt { return false; } - let crypto = get_prop(cstr!("ro.crypto.state"), false); + let crypto = get_prop(cstr!("ro.crypto.state")); return if !crypto.is_empty() { if crypto != "encrypted" { // Unencrypted, we can directly access data true } else { // Encrypted, check whether vold is started - !get_prop(cstr!("init.svc.vold"), false).is_empty() + !get_prop(cstr!("init.svc.vold")).is_empty() } } else { // ro.crypto.state is not set, assume it's unencrypted diff --git a/native/src/core/include/core.hpp b/native/src/core/include/core.hpp index 77d0e48d0..2e80282e9 100644 --- a/native/src/core/include/core.hpp +++ b/native/src/core/include/core.hpp @@ -17,6 +17,11 @@ #define to_app_id(uid) (uid % AID_USER_OFFSET) #define to_user_id(uid) (uid / AID_USER_OFFSET) +// Multi-call entrypoints +int magisk_main(int argc, char *argv[]); +int su_client_main(int argc, char *argv[]); +int zygisk_main(int argc, char *argv[]); + // Return codes for daemon enum class RespondCode : int { ERROR = -1, diff --git a/native/src/core/include/resetprop.hpp b/native/src/core/include/resetprop.hpp deleted file mode 100644 index a4eb2b746..000000000 --- a/native/src/core/include/resetprop.hpp +++ /dev/null @@ -1,30 +0,0 @@ -#pragma once - -#include -#include - -struct prop_info; - -struct prop_callback { - virtual ~prop_callback() = default; - virtual void exec(const char *name, const char *value, uint32_t serial) = 0; - void exec(Utf8CStr name, Utf8CStr value) { - exec(name.data(), value.data(), INT_MAX); - } - void read(const prop_info *pi); -}; - -// System properties -std::string get_prop(const char *name, bool persist = false); -int delete_prop(const char *name, bool persist = false); -int set_prop(const char *name, const char *value, bool skip_svc = false); -void load_prop_file(const char *filename, bool skip_svc = false); - -// Rust bindings -rust::String get_prop_rs(Utf8CStr name, bool persist); -static inline int set_prop_rs(Utf8CStr name, Utf8CStr value, bool skip_svc) { - return set_prop(name.data(), value.data(), skip_svc); -} -static inline void load_prop_file_rs(Utf8CStr filename, bool skip_svc) { - load_prop_file(filename.data(), skip_svc); -} \ No newline at end of file diff --git a/native/src/core/lib.rs b/native/src/core/lib.rs index f1a93fbe6..2bbf9ab3c 100644 --- a/native/src/core/lib.rs +++ b/native/src/core/lib.rs @@ -15,7 +15,7 @@ use derive::Decodable; use logging::{android_logging, setup_logfile, zygisk_close_logd, zygisk_get_logd, zygisk_logging}; use module::remove_modules; use mount::{find_preinit_device, revert_unmount}; -use resetprop::{persist_delete_prop, persist_get_prop, persist_get_props, persist_set_prop}; +use resetprop::{get_prop, resetprop_main}; use selinux::{lgetfilecon, lsetfilecon, restorecon, setfilecon}; use socket::{recv_fd, recv_fds, send_fd, send_fds}; use std::fs::File; @@ -164,19 +164,6 @@ pub mod ffi { fn get_text(self: &DbValues, index: i32) -> &str; fn bind_text(self: Pin<&mut DbStatement>, index: i32, val: &str) -> i32; fn bind_int64(self: Pin<&mut DbStatement>, index: i32, val: i64) -> i32; - - include!("include/resetprop.hpp"); - - #[cxx_name = "prop_callback"] - type PropCallback; - fn exec(self: Pin<&mut PropCallback>, name: Utf8CStrRef, value: Utf8CStrRef); - - #[cxx_name = "get_prop_rs"] - fn get_prop(name: Utf8CStrRef, persist: bool) -> String; - #[cxx_name = "set_prop_rs"] - fn set_prop(name: Utf8CStrRef, value: Utf8CStrRef, skip_svc: bool) -> i32; - #[cxx_name = "load_prop_file_rs"] - fn load_prop_file(filename: Utf8CStrRef, skip_svc: bool); } extern "Rust" { @@ -189,10 +176,6 @@ pub mod ffi { fn revert_unmount(pid: i32); fn remove_modules(); fn zygisk_should_load_module(flags: u32) -> bool; - unsafe fn persist_get_prop(name: Utf8CStrRef, prop_cb: Pin<&mut PropCallback>); - unsafe fn persist_get_props(prop_cb: Pin<&mut PropCallback>); - unsafe fn persist_delete_prop(name: Utf8CStrRef) -> bool; - unsafe fn persist_set_prop(name: Utf8CStrRef, value: Utf8CStrRef) -> bool; fn send_fd(socket: i32, fd: i32) -> bool; fn send_fds(socket: i32, fds: &[i32]) -> bool; fn recv_fd(socket: i32) -> i32; @@ -206,6 +189,9 @@ pub mod ffi { fn setfilecon(path: Utf8CStrRef, con: Utf8CStrRef) -> bool; fn lsetfilecon(path: Utf8CStrRef, con: Utf8CStrRef) -> bool; + fn get_prop(name: Utf8CStrRef) -> String; + unsafe fn resetprop_main(argc: i32, argv: *mut *mut c_char) -> i32; + #[namespace = "rust"] fn daemon_entry(); } diff --git a/native/src/core/module.rs b/native/src/core/module.rs index 42aa00b52..da8a11e31 100644 --- a/native/src/core/module.rs +++ b/native/src/core/module.rs @@ -1,7 +1,8 @@ use crate::consts::{MODULEMNT, MODULEROOT, MODULEUPGRADE, WORKERDIR}; use crate::daemon::MagiskD; -use crate::ffi::{ModuleInfo, exec_module_scripts, exec_script, get_magisk_tmp, load_prop_file}; +use crate::ffi::{ModuleInfo, exec_module_scripts, exec_script, get_magisk_tmp}; use crate::mount::setup_module_mount; +use crate::resetprop::load_prop_file; use base::{ DirEntry, Directory, FsPathBuilder, LibcReturn, LoggedResult, OsResultStatic, ResultExt, SilentLogExt, Utf8CStr, Utf8CStrBuf, Utf8CString, WalkResult, clone_attr, cstr, debug, error, @@ -804,8 +805,7 @@ impl MagiskD { // Read props let prop = module_paths.append("system.prop"); if prop.module().exists() { - // Do NOT go through property service as it could cause boot lock - load_prop_file(prop.module(), true); + load_prop_file(prop.module()); } } { diff --git a/native/src/core/mount.rs b/native/src/core/mount.rs index b568b05bd..52842d882 100644 --- a/native/src/core/mount.rs +++ b/native/src/core/mount.rs @@ -12,7 +12,8 @@ use base::{ }; use crate::consts::{MODULEMNT, MODULEROOT, PREINITDEV, PREINITMIRR, WORKERDIR}; -use crate::ffi::{get_magisk_tmp, get_prop, resolve_preinit_dir, switch_mnt_ns}; +use crate::ffi::{get_magisk_tmp, resolve_preinit_dir, switch_mnt_ns}; +use crate::resetprop::get_prop; pub fn setup_preinit_dir() { let magisk_tmp = get_magisk_tmp(); @@ -109,11 +110,11 @@ enum EncryptType { } pub fn find_preinit_device() -> String { - let encrypt_type = if get_prop(cstr!("ro.crypto.state"), false) != "encrypted" { + let encrypt_type = if get_prop(cstr!("ro.crypto.state")) != "encrypted" { EncryptType::None - } else if get_prop(cstr!("ro.crypto.type"), false) == "block" { + } else if get_prop(cstr!("ro.crypto.type")) == "block" { EncryptType::Block - } else if get_prop(cstr!("ro.crypto.metadata.enabled"), false) == "true" { + } else if get_prop(cstr!("ro.crypto.metadata.enabled")) == "true" { EncryptType::Metadata } else { EncryptType::File diff --git a/native/src/core/resetprop.rs b/native/src/core/resetprop.rs deleted file mode 100644 index bf4fa863a..000000000 --- a/native/src/core/resetprop.rs +++ /dev/null @@ -1,4 +0,0 @@ -pub use persist::{persist_delete_prop, persist_get_prop, persist_get_props, persist_set_prop}; - -mod persist; -mod proto; diff --git a/native/src/core/resetprop/cli.rs b/native/src/core/resetprop/cli.rs new file mode 100644 index 000000000..c6244a986 --- /dev/null +++ b/native/src/core/resetprop/cli.rs @@ -0,0 +1,325 @@ +use super::{ + PropInfo, PropReader, SYS_PROP, + persist::{persist_delete_prop, persist_get_all_props, persist_get_prop, persist_set_prop}, +}; +use argh::{EarlyExit, FromArgs, MissingRequirements}; +use base::libc::PROP_VALUE_MAX; +use base::{ + BufReadExt, CmdArgs, EarlyExitExt, LogLevel, LoggedResult, ResultExt, Utf8CStr, Utf8CStrBuf, + Utf8CString, cstr, debug, libc, log_err, set_log_level_state, +}; +use std::collections::BTreeMap; +use std::ffi::c_char; +use std::io::BufReader; + +#[derive(FromArgs, Default)] +struct ResetProp { + #[argh(switch, short = 'v')] + verbose: bool, + #[argh(switch, short = 'w')] + wait_mode: bool, + #[argh(switch, short = 'p')] + persist: bool, + #[argh(switch, short = 'P')] + persist_only: bool, + #[argh(switch, short = 'Z')] + context: bool, + #[argh(switch, short = 'n')] + skip_svc: bool, + #[argh(option, short = 'f')] + file: Option, + #[argh(option, long = "delete", short = 'd')] + delete_key: Option, + #[argh(positional)] + args: Vec, +} + +fn print_usage(cmd: &str) { + eprintln!( + r#"resetprop - System Property Manipulation Tool + +Usage: {cmd} [flags] [arguments...] + +Read mode arguments: + (no arguments) print all properties + NAME get property of NAME + +Write mode arguments: + NAME VALUE set property NAME as VALUE + -f,--file FILE load and set properties from FILE + -d,--delete NAME delete property + +Wait mode arguments (toggled with -w): + NAME wait until property NAME changes + NAME OLD_VALUE if value of property NAME is not OLD_VALUE, get value + or else wait until property NAME changes + +General flags: + -h,--help show this message + -v print verbose output to stderr + -w switch to wait mode + +Read mode flags: + -p also read persistent properties from storage + -P only read persistent properties from storage + -Z get property context instead of value + +Write mode flags: + -n set properties bypassing property_service + -p always write persistent prop changes to storage +"# + ); +} + +impl ResetProp { + fn get(&self, key: &Utf8CStr) -> Option { + if self.context { + return Some(SYS_PROP.get_context(key).to_string()); + } + + let mut val = if !self.persist_only { + SYS_PROP.find(key).map(|info| { + let mut v = String::new(); + info.read(&mut PropReader::Value(&mut v)); + debug!("resetprop: get prop [{key}]=[{v}]"); + v + }) + } else { + None + }; + + if val.is_none() && (self.persist || self.persist_only) && key.starts_with("persist.") { + val = persist_get_prop(key).ok(); + } + + if val.is_none() { + debug!("resetprop: prop [{key}] does not exist"); + } + + val + } + + fn print_all(&self) { + let mut map: BTreeMap = BTreeMap::new(); + if !self.persist_only { + SYS_PROP.for_each(&mut PropReader::List(&mut map)); + } + if self.persist || self.persist_only { + persist_get_all_props(&mut PropReader::List(&mut map)).log_ok(); + } + for (mut k, v) in map.into_iter() { + if self.context { + println!( + "[{k}]: [{}]", + SYS_PROP.get_context(Utf8CStr::from_string(&mut k)) + ); + } else { + println!("[{k}]: [{v}]"); + } + } + } + + fn set(&self, key: &Utf8CStr, val: &Utf8CStr) { + let mut skip_svc = self.skip_svc; + let mut info = SYS_PROP.find_mut(key); + + // Delete existing read-only properties if they are or will be long properties, + // which cannot directly go through __system_property_update + if key.starts_with("ro.") { + skip_svc = true; + if let Some(pi) = &info + && (pi.is_long() || val.len() >= PROP_VALUE_MAX as usize) + { + // Skip pruning nodes as we will add it back ASAP + SYS_PROP.delete(key, false); + info = None; + } + } + + #[allow(unused_variables)] + let msg = if skip_svc { + "direct modification" + } else { + "property_service" + }; + + if let Some(pi) = info { + if skip_svc { + pi.update(val); + } else { + SYS_PROP.set(key, val); + } + debug!("resetprop: update prop [{key}]=[{val}] by {msg}"); + } else { + if skip_svc { + SYS_PROP.add(key, val); + } else { + SYS_PROP.set(key, val); + } + debug!("resetprop: create prop [{key}]=[{val}] by {msg}"); + } + + // When bypassing property_service, persistent props won't be stored in storage. + // Explicitly handle this situation. + if skip_svc && self.persist && key.starts_with("persist.") { + persist_set_prop(key, val).log_ok(); + } + } + + fn delete(&self, key: &Utf8CStr) -> bool { + debug!("resetprop: delete prop [{key}]"); + let mut ret = false; + ret |= SYS_PROP.delete(key, true); + if self.persist && key.starts_with("persist.") { + ret |= persist_delete_prop(key).is_ok() + } + ret + } + + fn wait(&self) { + let key = &self.args[0]; + let val = self.args.get(1).map(|s| &**s); + + // Find PropInfo + let info: &PropInfo; + loop { + let i = SYS_PROP.find(key); + if let Some(i) = i { + info = i; + break; + } else { + debug!("resetprop: waiting for prop [{key}] to exist"); + let mut serial = SYS_PROP.area_serial(); + SYS_PROP.wait(None, serial, &mut serial); + } + } + + if let Some(val) = val { + let mut curr_val = String::new(); + let mut serial = 0; + loop { + let mut r = PropReader::ValueSerial(&mut curr_val, &mut serial); + SYS_PROP.read(info, &mut r); + if *val != *curr_val { + debug!("resetprop: get prop [{key}]=[{curr_val}]"); + break; + } + debug!("resetprop: waiting for prop [{key}]!=[{val}]"); + SYS_PROP.wait(Some(info), serial, &mut serial); + } + } + } + + fn load_file(&self, file: &Utf8CStr) -> LoggedResult<()> { + let fd = file.open(libc::O_RDONLY | libc::O_CLOEXEC)?; + let mut key = cstr::buf::dynamic(128); + let mut val = cstr::buf::dynamic(128); + BufReader::new(fd).for_each_prop(|k, v| { + key.clear(); + val.clear(); + key.push_str(k); + val.push_str(v); + self.set(&key, &val); + true + }); + Ok(()) + } + + fn run(self) -> LoggedResult<()> { + if self.wait_mode { + self.wait(); + } else if let Some(file) = &self.file { + self.load_file(file)?; + } else if let Some(key) = &self.delete_key { + if !self.delete(key) { + return log_err!(); + } + } else { + match self.args.len() { + 0 => self.print_all(), + 1 => { + if let Some(val) = self.get(&self.args[0]) { + println!("{val}"); + } else { + return log_err!(); + } + } + 2 => self.set(&self.args[0], &self.args[1]), + _ => unreachable!(), + } + } + Ok(()) + } +} + +pub fn resetprop_main(argc: i32, argv: *mut *mut c_char) -> i32 { + set_log_level_state(LogLevel::Debug, false); + let cmds = CmdArgs::new(argc, argv.cast()); + let cmds = cmds.as_slice(); + + let cli = ResetProp::from_args(&[cmds[0]], &cmds[1..]) + .and_then(|cli| { + let mut special_mode = 0; + if cli.wait_mode { + if cli.args.is_empty() { + let mut missing = MissingRequirements::default(); + missing.missing_positional_arg("NAME"); + missing.err_on_any()?; + } + special_mode += 1; + } + if cli.file.is_some() { + special_mode += 1; + } + if cli.delete_key.is_some() { + special_mode += 1; + } + if special_mode > 1 { + return Err(EarlyExit::from( + "Multiple operation mode detected!\n".to_string(), + )); + } + if cli.args.len() > 2 { + return Err(EarlyExit::from(format!( + "Unrecognized argument: {}\n", + cli.args[2] + ))); + } + Ok(cli) + }) + .on_early_exit(|| print_usage(cmds[0])); + + if cli.verbose { + set_log_level_state(LogLevel::Debug, true); + } + + if cli.run().is_ok() { 0 } else { 1 } +} + +// Magisk's own helper functions + +pub fn set_prop(key: &Utf8CStr, val: &Utf8CStr) { + let prop = ResetProp { + // All Magisk's internal usage should skip property_service + skip_svc: true, + ..Default::default() + }; + prop.set(key, val); +} + +pub fn load_prop_file(file: &Utf8CStr) { + let prop = ResetProp { + // All Magisk's internal usage should skip property_service + skip_svc: true, + ..Default::default() + }; + prop.load_file(file).ok(); +} + +pub fn get_prop(key: &Utf8CStr) -> String { + let prop = ResetProp { + persist: key.starts_with("persist."), + ..Default::default() + }; + prop.get(key).unwrap_or_default() +} diff --git a/native/src/core/resetprop/mod.rs b/native/src/core/resetprop/mod.rs new file mode 100644 index 000000000..3380951b2 --- /dev/null +++ b/native/src/core/resetprop/mod.rs @@ -0,0 +1,181 @@ +use base::libc::c_char; +use base::{Utf8CStr, libc}; +pub use cli::{get_prop, load_prop_file, resetprop_main, set_prop}; +use libc::timespec; +use std::collections::BTreeMap; +use std::ffi::CStr; +use std::ptr; +use std::sync::LazyLock; + +mod cli; +mod persist; +mod proto; + +static SYS_PROP: LazyLock = LazyLock::new(|| unsafe { get_sys_prop() }); + +#[repr(C)] +struct PropInfo { + _private: cxx::private::Opaque, +} + +type CharPtr = *const c_char; +type ReadCallback = unsafe extern "C" fn(&mut PropReader, CharPtr, CharPtr, u32); +type ForEachCallback = unsafe extern "C" fn(&PropInfo, &mut PropReader); + +enum PropReader<'a> { + Value(&'a mut String), + ValueSerial(&'a mut String, &'a mut u32), + List(&'a mut BTreeMap), +} + +impl PropReader<'_> { + fn put_cstr(&mut self, key: CharPtr, val: CharPtr, serial: u32) { + let key = unsafe { CStr::from_ptr(key) }; + let val = unsafe { CStr::from_ptr(val) }; + match self { + PropReader::Value(v) => { + **v = String::from_utf8_lossy(val.to_bytes()).into_owned(); + } + PropReader::ValueSerial(v, s) => { + **v = String::from_utf8_lossy(val.to_bytes()).into_owned(); + **s = serial; + } + PropReader::List(map) => { + map.insert( + String::from_utf8_lossy(key.to_bytes()).into_owned(), + String::from_utf8_lossy(val.to_bytes()).into_owned(), + ); + } + } + } + + fn put_str(&mut self, key: String, val: String, serial: u32) { + match self { + PropReader::Value(v) => { + **v = val; + } + PropReader::ValueSerial(v, s) => { + **v = val; + **s = serial; + } + PropReader::List(map) => { + map.insert(key, val); + } + } + } +} + +unsafe extern "C" { + // SAFETY: the improper_ctypes warning is about PropReader. We only pass PropReader + // to C functions as raw pointers, and all actual usage happens on the Rust side. + #[allow(improper_ctypes)] + fn get_sys_prop() -> SysProp; + + fn prop_info_is_long(info: &PropInfo) -> bool; + #[link_name = "__system_property_find2"] + fn sys_prop_find(key: CharPtr) -> Option<&'static mut PropInfo>; + #[link_name = "__system_property_update2"] + fn sys_prop_update(info: &mut PropInfo, val: CharPtr, val_len: u32) -> i32; + #[link_name = "__system_property_add2"] + fn sys_prop_add(key: CharPtr, key_len: u32, val: CharPtr, val_len: u32) -> i32; + #[link_name = "__system_property_delete"] + fn sys_prop_delete(key: CharPtr, prune: bool) -> i32; + #[link_name = "__system_property_get_context"] + fn sys_prop_get_context(key: CharPtr) -> CharPtr; + #[link_name = "__system_property_area_serial2"] + fn sys_prop_area_serial() -> u32; +} + +#[repr(C)] +struct SysProp { + set: unsafe extern "C" fn(CharPtr, CharPtr) -> i32, + find: unsafe extern "C" fn(CharPtr) -> Option<&'static PropInfo>, + read_callback: unsafe extern "C" fn(&PropInfo, ReadCallback, &mut PropReader) -> i32, + foreach: unsafe extern "C" fn(ForEachCallback, &mut PropReader) -> i32, + wait: unsafe extern "C" fn(Option<&PropInfo>, u32, &mut u32, *const timespec) -> i32, +} + +// Safe abstractions over raw C APIs + +impl PropInfo { + fn read(&self, reader: &mut PropReader) { + SYS_PROP.read(self, reader); + } + + fn update(&mut self, val: &Utf8CStr) { + SYS_PROP.update(self, val); + } + + fn is_long(&self) -> bool { + unsafe { prop_info_is_long(self) } + } +} + +impl SysProp { + fn read(&self, info: &PropInfo, reader: &mut PropReader) { + unsafe extern "C" fn read_fn(r: &mut PropReader, key: CharPtr, val: CharPtr, serial: u32) { + r.put_cstr(key, val, serial); + } + unsafe { + (self.read_callback)(info, read_fn, reader); + } + } + + fn find(&self, key: &Utf8CStr) -> Option<&'static PropInfo> { + unsafe { (self.find)(key.as_ptr()) } + } + + fn find_mut(&self, key: &Utf8CStr) -> Option<&'static mut PropInfo> { + unsafe { sys_prop_find(key.as_ptr()) } + } + + fn set(&self, key: &Utf8CStr, val: &Utf8CStr) { + unsafe { + (self.set)(key.as_ptr(), val.as_ptr()); + } + } + + fn add(&self, key: &Utf8CStr, val: &Utf8CStr) { + unsafe { + sys_prop_add( + key.as_ptr(), + key.len() as u32, + val.as_ptr(), + val.len() as u32, + ); + } + } + + fn update(&self, info: &mut PropInfo, val: &Utf8CStr) { + unsafe { + sys_prop_update(info, val.as_ptr(), val.len() as u32); + } + } + + fn delete(&self, key: &Utf8CStr, prune: bool) -> bool { + unsafe { sys_prop_delete(key.as_ptr(), prune) == 0 } + } + + fn for_each(&self, reader: &mut PropReader) { + unsafe extern "C" fn for_each_fn(info: &PropInfo, vals: &mut PropReader) { + SYS_PROP.read(info, vals); + } + unsafe { + (self.foreach)(for_each_fn, reader); + } + } + + fn wait(&self, info: Option<&PropInfo>, old_serial: u32, new_serial: &mut u32) { + unsafe { + (self.wait)(info, old_serial, new_serial, ptr::null()); + } + } + + fn get_context(&self, key: &Utf8CStr) -> &'static Utf8CStr { + unsafe { Utf8CStr::from_ptr_unchecked(sys_prop_get_context(key.as_ptr())) } + } + + fn area_serial(&self) -> u32 { + unsafe { sys_prop_area_serial() } + } +} diff --git a/native/src/core/resetprop/persist.rs b/native/src/core/resetprop/persist.rs index aab9cd362..9e1c6c8f6 100644 --- a/native/src/core/resetprop/persist.rs +++ b/native/src/core/resetprop/persist.rs @@ -3,12 +3,11 @@ use std::{ fs::File, io::{BufWriter, Write}, os::fd::FromRawFd, - pin::Pin, }; use quick_protobuf::{BytesReader, MessageRead, MessageWrite, Writer}; -use crate::ffi::PropCallback; +use crate::resetprop::PropReader; use crate::resetprop::proto::persistent_properties::{ PersistentProperties, mod_PersistentProperties::PersistentPropertyRecord, }; @@ -16,7 +15,7 @@ use base::const_format::concatcp; use base::libc::{O_CLOEXEC, O_RDONLY}; use base::{ Directory, FsPathBuilder, LibcReturn, LoggedResult, MappedFile, SilentLogExt, Utf8CStr, - Utf8CStrBuf, WalkResult, clone_attr, cstr, debug, libc::mkstemp, + Utf8CStrBuf, WalkResult, clone_attr, cstr, debug, libc::mkstemp, log_err, }; const PERSIST_PROP_DIR: &str = "/data/property"; @@ -24,7 +23,7 @@ const PERSIST_PROP: &str = concatcp!(PERSIST_PROP_DIR, "/persistent_properties") trait PropExt { fn find_index(&self, name: &Utf8CStr) -> Result; - fn find(&mut self, name: &Utf8CStr) -> LoggedResult<&mut PersistentPropertyRecord>; + fn find(self, name: &Utf8CStr) -> Option; } impl PropExt for PersistentProperties { @@ -33,9 +32,9 @@ impl PropExt for PersistentProperties { .binary_search_by(|p| p.name.as_deref().cmp(&Some(name.as_str()))) } - fn find(&mut self, name: &Utf8CStr) -> LoggedResult<&mut PersistentPropertyRecord> { - let idx = self.find_index(name).silent()?; - Ok(&mut self.properties[idx]) + fn find(self, name: &Utf8CStr) -> Option { + let idx = self.find_index(name).ok()?; + self.properties.into_iter().nth(idx) } } @@ -108,92 +107,80 @@ fn proto_write_props(props: &PersistentProperties) -> LoggedResult<()> { Ok(()) } -pub fn persist_get_prop(name: &Utf8CStr, prop_cb: Pin<&mut PropCallback>) { - let res: LoggedResult<()> = try { - if check_proto() { - let mut props = proto_read_props()?; - let prop = props.find(name)?; +pub(super) fn persist_get_prop(key: &Utf8CStr) -> LoggedResult { + if check_proto() { + let props = proto_read_props()?; + let prop = props.find(key).silent()?; + if let PersistentPropertyRecord { + name: Some(_), + value: Some(v), + } = prop + { + return Ok(v); + } + } else { + let value = file_get_prop(key)?; + debug!("resetprop: get persist prop [{}]=[{}]", key, value); + return Ok(value); + } + log_err!() +} + +pub(super) fn persist_get_all_props(reader: &mut PropReader) -> LoggedResult<()> { + if check_proto() { + let props = proto_read_props()?; + props.properties.into_iter().for_each(|prop| { if let PersistentPropertyRecord { name: Some(n), value: Some(v), } = prop { - prop_cb.exec(Utf8CStr::from_string(n), Utf8CStr::from_string(v)); + reader.put_str(n, v, 0); } - } else { - let mut value = file_get_prop(name)?; - prop_cb.exec(name, Utf8CStr::from_string(&mut value)); - debug!("resetprop: found prop [{}] = [{}]", name, value); - } - }; - res.ok(); -} - -pub fn persist_get_props(mut prop_cb: Pin<&mut PropCallback>) { - let res: LoggedResult<()> = try { - if check_proto() { - let mut props = proto_read_props()?; - props.properties.iter_mut().for_each(|prop| { - if let PersistentPropertyRecord { - name: Some(n), - value: Some(v), - } = prop - { - prop_cb - .as_mut() - .exec(Utf8CStr::from_string(n), Utf8CStr::from_string(v)); - } - }); - } else { - let mut dir = Directory::open(cstr!(PERSIST_PROP_DIR))?; - dir.pre_order_walk(|e| { - if e.is_file() - && let Ok(mut value) = file_get_prop(e.name()) - { - prop_cb - .as_mut() - .exec(e.name(), Utf8CStr::from_string(&mut value)); - } - // Do not traverse recursively - Ok(WalkResult::Skip) - })?; - } - }; - res.ok(); -} - -pub fn persist_delete_prop(name: &Utf8CStr) -> bool { - let res: LoggedResult<()> = try { - if check_proto() { - let mut props = proto_read_props()?; - let idx = props.find_index(name).silent()?; - props.properties.remove(idx); - proto_write_props(&props)?; - } else { - file_set_prop(name, None)?; - } - }; - res.is_ok() -} - -pub fn persist_set_prop(name: &Utf8CStr, value: &Utf8CStr) -> bool { - let res: LoggedResult<()> = try { - if check_proto() { - let mut props = proto_read_props()?; - match props.find_index(name) { - Ok(idx) => props.properties[idx].value = Some(value.to_string()), - Err(idx) => props.properties.insert( - idx, - PersistentPropertyRecord { - name: Some(name.to_string()), - value: Some(value.to_string()), - }, - ), + }); + } else { + let mut dir = Directory::open(cstr!(PERSIST_PROP_DIR))?; + dir.pre_order_walk(|e| { + if e.is_file() + && let Ok(value) = file_get_prop(e.name()) + { + reader.put_str(e.name().to_string(), value, 0); } - proto_write_props(&props)?; - } else { - file_set_prop(name, Some(value))?; - } - }; - res.is_ok() + // Do not traverse recursively + Ok(WalkResult::Skip) + })?; + } + Ok(()) +} + +pub(super) fn persist_delete_prop(key: &Utf8CStr) -> LoggedResult<()> { + if check_proto() { + let mut props = proto_read_props()?; + let idx = props.find_index(key).silent()?; + props.properties.remove(idx); + proto_write_props(&props)?; + } else { + file_set_prop(key, None)?; + } + Ok(()) +} + +pub(super) fn persist_set_prop(key: &Utf8CStr, val: &Utf8CStr) -> LoggedResult<()> { + if check_proto() { + let mut props = proto_read_props()?; + match props.find_index(key) { + Ok(idx) => props.properties[idx].value = Some(val.to_string()), + Err(idx) => props.properties.insert( + idx, + PersistentPropertyRecord { + name: Some(key.to_string()), + value: Some(val.to_string()), + }, + ), + } + proto_write_props(&props)?; + } else { + file_set_prop(key, Some(val))?; + } + Ok(()) } diff --git a/native/src/core/resetprop/resetprop.cpp b/native/src/core/resetprop/resetprop.cpp deleted file mode 100644 index b8f0ad274..000000000 --- a/native/src/core/resetprop/resetprop.cpp +++ /dev/null @@ -1,473 +0,0 @@ -#include -#include -#include -#include - -#include -#include -#include - -#include -#include - -using namespace std; - -#ifdef APPLET_STUB_MAIN -#define system_property_set __system_property_set -#define system_property_find __system_property_find -#define system_property_read_callback __system_property_read_callback -#define system_property_foreach __system_property_foreach -#define system_property_wait __system_property_wait -#else -static int (*system_property_set)(const char*, const char*); -static int (*system_property_read)(const prop_info*, char*, char*); -static const prop_info *(*system_property_find)(const char*); -static void (*system_property_read_callback)( - const prop_info*, void (*)(void*, const char*, const char*, uint32_t), void*); -static int (*system_property_foreach)(void (*)(const prop_info*, void*), void*); -static bool (*system_property_wait)(const prop_info*, uint32_t, uint32_t*, const struct timespec*); -#endif - -struct PropFlags { - void setSkipSvc() { flags |= 1; } - void setPersist() { flags |= (1 << 1); } - void setContext() { flags |= (1 << 2); } - void setPersistOnly() { flags |= (1 << 3); setPersist(); } - void setWait() { flags |= (1 << 4); } - bool isSkipSvc() const { return flags & 1; } - bool isPersist() const { return flags & (1 << 1); } - bool isContext() const { return flags & (1 << 2); } - bool isPersistOnly() const { return flags & (1 << 3); } - bool isWait() const { return flags & (1 << 4); } -private: - uint32_t flags = 0; -}; - -[[noreturn]] static void usage(char* arg0) { - fprintf(stderr, -R"EOF(resetprop - System Property Manipulation Tool - -Usage: %s [flags] [arguments...] - -Read mode arguments: - (no arguments) print all properties - NAME get property of NAME - -Write mode arguments: - NAME VALUE set property NAME as VALUE - -f,--file FILE load and set properties from FILE - -d,--delete NAME delete property - -Wait mode arguments (toggled with -w): - NAME wait until property NAME changes - NAME OLD_VALUE if value of property NAME is not OLD_VALUE, get value - or else wait until property NAME changes - -General flags: - -h,--help show this message - -v print verbose output to stderr - -w switch to wait mode - -Read mode flags: - -p also read persistent props from storage - -P only read persistent props from storage - -Z get property context instead of value - -Write mode flags: - -n set properties bypassing property_service - -p always write persistent prop changes to storage - -)EOF", arg0); - exit(1); -} - -static bool check_legal_property_name(const char *name) { - int namelen = strlen(name); - - if (namelen < 1) goto illegal; - if (name[0] == '.') goto illegal; - if (name[namelen - 1] == '.') goto illegal; - - /* Only allow alphanumeric, plus '.', '-', '@', ':', or '_' */ - /* Don't allow ".." to appear in a property name */ - for (size_t i = 0; i < namelen; i++) { - if (name[i] == '.') { - // i=0 is guaranteed to never have a dot. See above. - if (name[i-1] == '.') goto illegal; - continue; - } - if (name[i] == '_' || name[i] == '-' || name[i] == '@' || name[i] == ':') continue; - if (name[i] >= 'a' && name[i] <= 'z') continue; - if (name[i] >= 'A' && name[i] <= 'Z') continue; - if (name[i] >= '0' && name[i] <= '9') continue; - goto illegal; - } - - return true; - -illegal: - LOGE("Illegal property name: [%s]\n", name); - return false; -} - -void prop_callback::read(const prop_info *pi) { - auto fn = [](void *cb, const char *name, const char *value, uint32_t serial) { - static_cast(cb)->exec(name, value, serial); - }; - system_property_read_callback(pi, fn, this); -} - -template -struct prop_to_string : prop_callback { - void exec(const char *, const char *value, uint32_t s) override { - val = value; - serial = s; - } - StringType val; - uint32_t serial = 0; -}; - -template<> void prop_to_string::exec(const char *, const char *value, uint32_t s) { - // We do not want to crash when values are not UTF-8 - val = rust::String::lossy(value); - serial = s; -} - -static bool str_starts(std::string_view s, std::string_view ss) { - return s.starts_with(ss); -} - -static int set_prop(const char *name, const char *value, PropFlags flags) { - if (!check_legal_property_name(name)) - return 1; - - auto pi = const_cast(__system_property_find(name)); - - // Delete existing read-only properties if they are or will be long properties, - // which cannot directly go through __system_property_update - if (str_starts(name, "ro.")) { - if (pi != nullptr && (pi->is_long() || strlen(value) >= PROP_VALUE_MAX)) { - // Skip pruning nodes as we will add it back ASAP - __system_property_delete(name, false); - pi = nullptr; - } - flags.setSkipSvc(); - } - - const char *msg = flags.isSkipSvc() ? "direct modification" : "property_service"; - - int ret; - if (pi != nullptr) { - if (flags.isSkipSvc()) { - ret = __system_property_update(pi, value, strlen(value)); - } else { - ret = system_property_set(name, value); - } - LOGD("resetprop: update prop [%s]: [%s] by %s\n", name, value, msg); - } else { - if (flags.isSkipSvc()) { - ret = __system_property_add(name, strlen(name), value, strlen(value)); - } else { - ret = system_property_set(name, value); - } - LOGD("resetprop: create prop [%s]: [%s] by %s\n", name, value, msg); - } - - // When bypassing property_service, persistent props won't be stored in storage. - // Explicitly handle this situation. - if (ret == 0 && flags.isSkipSvc() && flags.isPersist() && str_starts(name, "persist.")) { - ret = persist_set_prop(name, value) ? 0 : 1; - } - - if (ret) { - LOGW("resetprop: set prop error\n"); - } - - return ret; -} - -template -static StringType get_prop(const char *name, PropFlags flags) { - if (!check_legal_property_name(name)) - return {}; - - prop_to_string cb; - - if (flags.isContext()) { - auto context = __system_property_get_context(name) ?: ""; - LOGD("resetprop: prop context [%s]: [%s]\n", name, context); - cb.exec(name, context, -1); - return cb.val; - } - - if (!flags.isPersistOnly()) { - if (auto pi = system_property_find(name)) { - cb.read(pi); - LOGD("resetprop: get prop [%s]: [%s]\n", name, cb.val.c_str()); - } - } - - if (cb.val.empty() && flags.isPersist() && str_starts(name, "persist.")) - persist_get_prop(name, cb); - if (cb.val.empty()) - LOGD("resetprop: prop [%s] does not exist\n", name); - - return cb.val; -} - -template -static StringType wait_prop(const char *name, const char *old_value) { - if (!check_legal_property_name(name)) - return {}; - - const prop_info *pi; - auto serial = __system_property_area_serial(); - while (!(pi = system_property_find(name))) { - LOGD("resetprop: waiting for prop [%s] to exist\n", name); - system_property_wait(nullptr, serial, &serial, nullptr); - } - - prop_to_string cb; - cb.read(pi); - - while (old_value == nullptr || cb.val == old_value) { - LOGD("resetprop: waiting for prop [%s]\n", name); - uint32_t new_serial; - system_property_wait(pi, cb.serial, &new_serial, nullptr); - cb.read(pi); - if (old_value == nullptr) break; - } - - LOGD("resetprop: get prop [%s]: [%s]\n", name, cb.val.c_str()); - return cb.val; -} - -struct prop_collector : prop_callback { - void exec(const char *name, const char *value, uint32_t) override { - list.insert({name, value}); - } - map list; -}; - -static void print_props(PropFlags flags) { - prop_collector collector; - if (!flags.isPersistOnly()) { - system_property_foreach([](const prop_info *pi, void *cb) { - static_cast(cb)->read(pi); - }, &collector); - } - if (flags.isPersist()) - persist_get_props(collector); - for (auto &[key, val] : collector.list) { - const char *v = flags.isContext() ? - (__system_property_get_context(key.data()) ?: "") : - val.data(); - printf("[%s]: [%s]\n", key.data(), v); - } -} - -static int delete_prop(const char *name, PropFlags flags) { - if (!check_legal_property_name(name)) - return 1; - - LOGD("resetprop: delete prop [%s]\n", name); - - int ret = __system_property_delete(name, true); - if (flags.isPersist() && str_starts(name, "persist.")) { - if (persist_delete_prop(name)) - ret = 0; - } - return ret; -} - -static void load_file(const char *filename, PropFlags flags) { - LOGD("resetprop: Parse prop file [%s]\n", filename); - parse_prop_file(filename, [=](auto key, auto val) -> bool { - set_prop(key.data(), val.data(), flags); - return true; - }); -} - -struct Initialize { - Initialize() { -#ifndef APPLET_STUB_MAIN -#define DLOAD(name) (*(void **) &name = dlsym(RTLD_DEFAULT, "__" #name)) - // Load platform implementations - DLOAD(system_property_set); - DLOAD(system_property_read); - DLOAD(system_property_find); - DLOAD(system_property_read_callback); - DLOAD(system_property_foreach); - DLOAD(system_property_wait); -#undef DLOAD - if (system_property_wait == nullptr) { - // The platform API only exist on API 26+ - system_property_wait = __system_property_wait; - } - if (system_property_read_callback == nullptr) { - // The platform API only exist on API 26+, create a polyfill - system_property_read_callback = [](const prop_info *pi, auto fn, void *cookie) { - char name[PROP_NAME_MAX]; - char value[PROP_VALUE_MAX]; - name[0] = '\0'; - value[0] = '\0'; - system_property_read(pi, name, value); - fn(cookie, name, value, pi->serial); - }; - } -#endif - if (__system_properties_init()) { - LOGE("resetprop: __system_properties_init error\n"); - } - } -}; - -static void InitOnce() { - static Initialize init; -} - -#define consume_next(val) \ -if (argc != 2) usage(argv0); \ -val = argv[1]; \ -stop_parse = true; \ - -int resetprop_main(int argc, char *argv[]) { - PropFlags flags; - char *argv0 = argv[0]; - set_log_level_state(LogLevel::Debug, false); - - const char *prop_file = nullptr; - const char *prop_to_rm = nullptr; - - --argc; - ++argv; - - // Parse flags and -- options - while (argc && argv[0][0] == '-') { - bool stop_parse = false; - for (int idx = 1; true; ++idx) { - switch (argv[0][idx]) { - case '-': - if (argv[0] == "--file"sv) { - consume_next(prop_file); - } else if (argv[0] == "--delete"sv) { - consume_next(prop_to_rm); - } else { - usage(argv0); - } - break; - case 'd': - consume_next(prop_to_rm); - continue; - case 'f': - consume_next(prop_file); - continue; - case 'n': - flags.setSkipSvc(); - continue; - case 'p': - flags.setPersist(); - continue; - case 'P': - flags.setPersistOnly(); - continue; - case 'v': - set_log_level_state(LogLevel::Debug, true); - continue; - case 'Z': - flags.setContext(); - continue; - case 'w': - flags.setWait(); - continue; - case '\0': - break; - default: - usage(argv0); - } - break; - } - --argc; - ++argv; - if (stop_parse) - break; - } - - InitOnce(); - - if (prop_to_rm) { - return delete_prop(prop_to_rm, flags); - } - - if (prop_file) { - load_file(prop_file, flags); - return 0; - } - - if (flags.isWait()) { - if (argc == 0) usage(argv0); - auto val = wait_prop(argv[0], argv[1]); - if (val.empty()) - return 1; - printf("%s\n", val.data()); - return 0; - } - - switch (argc) { - case 0: - print_props(flags); - return 0; - case 1: { - auto val = get_prop(argv[0], flags); - if (val.empty()) - return 1; - printf("%s\n", val.data()); - return 0; - } - case 2: - return set_prop(argv[0], argv[1], flags); - default: - usage(argv0); - } -} - -/*************** - * Public APIs - ****************/ - -template -static StringType get_prop_impl(const char *name, bool persist) { - InitOnce(); - PropFlags flags; - if (persist) flags.setPersist(); - return get_prop(name, flags); -} - -rust::String get_prop_rs(Utf8CStr name, bool persist) { - return get_prop_impl(name.data(), persist); -} - -string get_prop(const char *name, bool persist) { - return get_prop_impl(name, persist); -} - -int delete_prop(const char *name, bool persist) { - InitOnce(); - PropFlags flags; - if (persist) flags.setPersist(); - return delete_prop(name, flags); -} - -int set_prop(const char *name, const char *value, bool skip_svc) { - InitOnce(); - PropFlags flags; - if (skip_svc) flags.setSkipSvc(); - return set_prop(name, value, flags); -} - -void load_prop_file(const char *filename, bool skip_svc) { - InitOnce(); - PropFlags flags; - if (skip_svc) flags.setSkipSvc(); - load_file(filename, flags); -} diff --git a/native/src/core/resetprop/sys.cpp b/native/src/core/resetprop/sys.cpp new file mode 100644 index 000000000..c2a1c0986 --- /dev/null +++ b/native/src/core/resetprop/sys.cpp @@ -0,0 +1,55 @@ +#include + +#include +#include + +#include +#include + +using namespace std; + +// This has to keep in sync with SysProp in mod.rs +struct SysProp { + int (*set)(const char*, const char*); + const prop_info *(*find)(const char*); + void (*read_callback)(const prop_info*, void (*)(void*, const char*, const char*, uint32_t), void*); + int (*foreach)(void (*)(const prop_info*, void*), void*); + bool (*wait)(const prop_info*, uint32_t, uint32_t*, const timespec*); +}; + +extern "C" bool prop_info_is_long(const prop_info &info) { + return info.is_long(); +} + +extern "C" SysProp get_sys_prop() { + SysProp prop{}; +#ifdef APPLET_STUB_MAIN + // Use internal implementation + prop.set = __system_property_set; + prop.find = __system_property_find; + prop.read_callback = __system_property_read_callback; + prop.foreach = __system_property_foreach; + prop.wait = __system_property_wait; +#else +#define DLOAD(name) (*(void **) &prop.name = dlsym(RTLD_DEFAULT, "__system_property_" #name)) + // Dynamic load platform implementation + DLOAD(set); + DLOAD(find); + DLOAD(read_callback); + DLOAD(foreach); + DLOAD(wait); +#undef DLOAD + if (prop.wait == nullptr) { + // This platform API only exist on API 26+ + prop.wait = __system_property_wait; + } + if (prop.read_callback == nullptr) { + // This platform API only exist on API 26+ + prop.read_callback = __system_property_read_callback; + } +#endif + if (__system_properties_init()) { + LOGE("resetprop: __system_properties_init error\n"); + } + return prop; +} diff --git a/native/src/core/zygisk/daemon.rs b/native/src/core/zygisk/daemon.rs index e21548eca..fe5ef5905 100644 --- a/native/src/core/zygisk/daemon.rs +++ b/native/src/core/zygisk/daemon.rs @@ -1,8 +1,7 @@ use crate::consts::MODULEROOT; use crate::daemon::{MagiskD, to_user_id}; -use crate::ffi::{ - ZygiskRequest, ZygiskStateFlags, get_magisk_tmp, get_prop, set_prop, update_deny_flags, -}; +use crate::ffi::{ZygiskRequest, ZygiskStateFlags, get_magisk_tmp, update_deny_flags}; +use crate::resetprop::{get_prop, set_prop}; use crate::socket::{IpcRead, UnixSocketExt}; use base::libc::{O_CLOEXEC, O_CREAT, O_RDONLY, STDOUT_FILENO}; use base::{ @@ -132,18 +131,18 @@ impl ZygiskState { if !self.lib_name.is_empty() { return; } - let orig = get_prop(NBPROP, false); + let orig = get_prop(NBPROP); self.lib_name = if orig.is_empty() || orig == "0" { ZYGISKLDR.to_string() } else { orig + ZYGISKLDR }; - set_prop(NBPROP, Utf8CStr::from_string(&mut self.lib_name), false); + set_prop(NBPROP, Utf8CStr::from_string(&mut self.lib_name)); // Whether Huawei's Maple compiler is enabled. // If so, system server will be created by a special Zygote which ignores the native bridge // and make system server out of our control. Avoid it by disabling. - if get_prop(cstr!("ro.maple.enable"), false) == "1" { - set_prop(cstr!("ro.maple.enable"), cstr!("0"), false); + if get_prop(cstr!("ro.maple.enable")) == "1" { + set_prop(cstr!("ro.maple.enable"), cstr!("0")); } } @@ -152,7 +151,7 @@ impl ZygiskState { if self.lib_name.len() > ZYGISKLDR.len() { orig = self.lib_name[ZYGISKLDR.len()..].to_string(); } - set_prop(NBPROP, Utf8CStr::from_string(&mut orig), false); + set_prop(NBPROP, Utf8CStr::from_string(&mut orig)); self.lib_name.clear(); } } diff --git a/native/src/core/zygisk/entry.cpp b/native/src/core/zygisk/entry.cpp index 15cdf9bff..ccb4e6400 100644 --- a/native/src/core/zygisk/entry.cpp +++ b/native/src/core/zygisk/entry.cpp @@ -2,7 +2,6 @@ #include #include -#include #include #include diff --git a/native/src/core/zygisk/hook.cpp b/native/src/core/zygisk/hook.cpp index 667474976..2ab3c3af5 100644 --- a/native/src/core/zygisk/hook.cpp +++ b/native/src/core/zygisk/hook.cpp @@ -366,7 +366,7 @@ void HookContext::post_native_bridge_load(void *handle) { auto nb = get_prop(NBPROP); auto len = sizeof(ZYGISKLDR) - 1; if (nb.size() > len) { - arg.load_native_bridge(nb.data() + len, arg.callbacks); + arg.load_native_bridge(nb.c_str() + len, arg.callbacks); } runtime_callbacks = arg.callbacks; } diff --git a/native/src/include/consts.hpp b/native/src/include/consts.hpp index a94d70db9..d340f3aae 100644 --- a/native/src/include/consts.hpp +++ b/native/src/include/consts.hpp @@ -37,9 +37,3 @@ constexpr const char *applet_names[] = { "su", "resetprop", nullptr }; extern int SDK_INT; #define APP_DATA_DIR (SDK_INT >= 24 ? "/data/user_de" : "/data/user") - -// Multi-call entrypoints -int magisk_main(int argc, char *argv[]); -int su_client_main(int argc, char *argv[]); -int resetprop_main(int argc, char *argv[]); -int zygisk_main(int argc, char *argv[]);