use std::fs; #[cfg(any(target_os = "linux", target_os = "android"))] use std::os::fd::RawFd; use std::sync::OnceLock; // Event constants const EVENT_POST_FS_DATA: u32 = 1; const EVENT_BOOT_COMPLETED: u32 = 2; const EVENT_MODULE_MOUNTED: u32 = 3; const KSU_IOCTL_GRANT_ROOT: u32 = 0x00004b01; // _IOC(_IOC_NONE, 'K', 1, 0) const KSU_IOCTL_GET_INFO: u32 = 0x80004b02; // _IOC(_IOC_READ, 'K', 2, 0) const KSU_IOCTL_REPORT_EVENT: u32 = 0x40004b03; // _IOC(_IOC_WRITE, 'K', 3, 0) const KSU_IOCTL_SET_SEPOLICY: u32 = 0xc0004b04; // _IOC(_IOC_READ|_IOC_WRITE, 'K', 4, 0) const KSU_IOCTL_CHECK_SAFEMODE: u32 = 0x80004b05; // _IOC(_IOC_READ, 'K', 5, 0) const KSU_IOCTL_GET_FEATURE: u32 = 0xc0004b0d; // _IOC(_IOC_READ|_IOC_WRITE, 'K', 13, 0) const KSU_IOCTL_SET_FEATURE: u32 = 0x40004b0e; // _IOC(_IOC_WRITE, 'K', 14, 0) const KSU_IOCTL_PROXY_FILE: u32 = 0x00004b0f; // _IOC(_IOC_NONE, 'K', 15, 0) #[allow(dead_code)] const KSU_IOCTL_KPM: u32 = 0xc0004bc8; // _IOC(_IOC_READ|_IOC_WRITE, 'K', 200, 0) #[allow(dead_code)] const KSU_IOCTL_UMOUNT_MANAGER: u32 = 0xc0004b6b; // _IOC(_IOC_READ|_IOC_WRITE, 'K', 107, 0) #[repr(C)] #[derive(Clone, Copy, Default)] struct GetInfoCmd { version: u32, flags: u32, } #[repr(C)] struct ReportEventCmd { event: u32, } #[repr(C)] #[derive(Clone, Copy, Default)] pub struct SetSepolicyCmd { pub cmd: u64, pub arg: u64, } #[repr(C)] #[derive(Clone, Copy, Default)] struct CheckSafemodeCmd { in_safe_mode: u8, } #[repr(C)] #[derive(Clone, Copy, Default)] struct GetFeatureCmd { feature_id: u32, value: u64, supported: u8, } #[repr(C)] #[derive(Clone, Copy, Default)] struct SetFeatureCmd { feature_id: u32, value: u64, } #[repr(C)] #[derive(Clone, Copy, Default)] struct ProxyFileCmd { fd: i32, flags: u32, } // Global driver fd cache #[cfg(any(target_os = "linux", target_os = "android"))] static DRIVER_FD: OnceLock = OnceLock::new(); #[cfg(any(target_os = "linux", target_os = "android"))] static INFO_CACHE: OnceLock = OnceLock::new(); const KSU_INSTALL_MAGIC1: u32 = 0xDEADBEEF; const KSU_INSTALL_MAGIC2: u32 = 0xCAFEBABE; #[cfg(any(target_os = "linux", target_os = "android"))] fn scan_driver_fd() -> Option { let fd_dir = fs::read_dir("/proc/self/fd").ok()?; for entry in fd_dir.flatten() { if let Ok(fd_num) = entry.file_name().to_string_lossy().parse::() { let link_path = format!("/proc/self/fd/{}", fd_num); if let Ok(target) = fs::read_link(&link_path) { let target_str = target.to_string_lossy(); if target_str.contains("[ksu_driver]") { return Some(fd_num); } } } } None } // Get cached driver fd #[cfg(any(target_os = "linux", target_os = "android"))] fn init_driver_fd() -> Option { let fd = scan_driver_fd(); if fd.is_none() { let mut fd = -1; unsafe { libc::syscall( libc::SYS_reboot, KSU_INSTALL_MAGIC1, KSU_INSTALL_MAGIC2, 0, &mut fd, ); }; if fd >= 0 { Some(fd) } else { None } } else { fd } } // ioctl wrapper using libc #[cfg(any(target_os = "linux", target_os = "android"))] fn ksuctl(request: u32, arg: *mut T) -> std::io::Result { use std::io; let fd = *DRIVER_FD.get_or_init(|| init_driver_fd().unwrap_or(-1)); unsafe { #[cfg(not(target_env = "gnu"))] let ret = libc::ioctl(fd as libc::c_int, request as i32, arg); #[cfg(target_env = "gnu")] let ret = libc::ioctl(fd as libc::c_int, request as u64, arg); if ret < 0 { Err(io::Error::last_os_error()) } else { Ok(ret) } } } #[cfg(not(any(target_os = "linux", target_os = "android")))] fn ksuctl(_request: u32, _arg: *mut T) -> std::io::Result { Err(std::io::Error::from_raw_os_error(libc::ENOSYS)) } // API implementations #[cfg(any(target_os = "linux", target_os = "android"))] fn get_info() -> GetInfoCmd { *INFO_CACHE.get_or_init(|| { let mut cmd = GetInfoCmd { version: 0, flags: 0, }; let _ = ksuctl(KSU_IOCTL_GET_INFO, &mut cmd as *mut _); cmd }) } #[cfg(any(target_os = "linux", target_os = "android"))] pub fn get_version() -> i32 { get_info().version as i32 } #[cfg(not(any(target_os = "linux", target_os = "android")))] pub fn get_version() -> i32 { 0 } #[cfg(any(target_os = "linux", target_os = "android"))] pub fn grant_root() -> std::io::Result<()> { ksuctl(KSU_IOCTL_GRANT_ROOT, std::ptr::null_mut::())?; Ok(()) } #[cfg(not(any(target_os = "linux", target_os = "android")))] pub fn grant_root() -> std::io::Result<()> { Err(std::io::Error::from_raw_os_error(libc::ENOSYS)) } #[cfg(any(target_os = "linux", target_os = "android"))] fn report_event(event: u32) { let mut cmd = ReportEventCmd { event }; let _ = ksuctl(KSU_IOCTL_REPORT_EVENT, &mut cmd as *mut _); } #[cfg(not(any(target_os = "linux", target_os = "android")))] fn report_event(_event: u32) {} pub fn report_post_fs_data() { report_event(EVENT_POST_FS_DATA); } pub fn report_boot_complete() { report_event(EVENT_BOOT_COMPLETED); } pub fn report_module_mounted() { report_event(EVENT_MODULE_MOUNTED); } #[cfg(any(target_os = "linux", target_os = "android"))] pub fn check_kernel_safemode() -> bool { let mut cmd = CheckSafemodeCmd { in_safe_mode: 0 }; let _ = ksuctl(KSU_IOCTL_CHECK_SAFEMODE, &mut cmd as *mut _); cmd.in_safe_mode != 0 } #[cfg(not(any(target_os = "linux", target_os = "android")))] pub fn check_kernel_safemode() -> bool { false } pub fn set_sepolicy(cmd: &SetSepolicyCmd) -> std::io::Result<()> { let mut ioctl_cmd = *cmd; ksuctl(KSU_IOCTL_SET_SEPOLICY, &mut ioctl_cmd as *mut _)?; Ok(()) } /// Get feature value and support status from kernel /// Returns (value, supported) pub fn get_feature(feature_id: u32) -> std::io::Result<(u64, bool)> { let mut cmd = GetFeatureCmd { feature_id, value: 0, supported: 0, }; ksuctl(KSU_IOCTL_GET_FEATURE, &mut cmd as *mut _)?; Ok((cmd.value, cmd.supported != 0)) } /// Set feature value in kernel pub fn set_feature(feature_id: u32, value: u64) -> std::io::Result<()> { let mut cmd = SetFeatureCmd { feature_id, value }; ksuctl(KSU_IOCTL_SET_FEATURE, &mut cmd as *mut _)?; Ok(()) } pub fn proxy_file(fd: RawFd) -> std::io::Result { let mut cmd = ProxyFileCmd { fd, flags: 0 }; let result = ksuctl(KSU_IOCTL_PROXY_FILE, &mut cmd as *mut _)?; Ok(result) } #[repr(C)] #[derive(Clone, Copy, Default)] #[allow(dead_code)] pub struct KsuKpmCmd { pub arg2: u64, pub arg3: u64, pub arg4: u64, pub arg5: u64, } #[allow(dead_code)] pub fn kpm_ioctl(cmd: &mut KsuKpmCmd) -> std::io::Result<()> { ksuctl(KSU_IOCTL_KPM, cmd as *mut _)?; Ok(()) } #[repr(C)] #[derive(Clone, Copy)] #[allow(dead_code)] pub struct UmountManagerCmd { pub operation: u32, pub path: [u8; 256], pub check_mnt: u8, pub flags: i32, pub count: u32, pub entries_ptr: u64, } #[allow(dead_code)] impl Default for UmountManagerCmd { fn default() -> Self { UmountManagerCmd { operation: 0, path: [0; 256], check_mnt: 0, flags: 0, count: 0, entries_ptr: 0, } } } #[cfg(any(target_os = "linux", target_os = "android"))] #[allow(dead_code)] pub fn umount_manager_ioctl(cmd: &UmountManagerCmd) -> std::io::Result<()> { let mut ioctl_cmd = *cmd; ksuctl(KSU_IOCTL_UMOUNT_MANAGER, &mut ioctl_cmd as *mut _)?; Ok(()) } #[cfg(not(any(target_os = "linux", target_os = "android")))] #[allow(dead_code)] pub fn umount_manager_ioctl(_cmd: &UmountManagerCmd) -> std::io::Result<()> { Err(std::io::Error::from_raw_os_error(libc::ENOSYS)) }