ksud: fix issues found by clippy (#167)

These issues are mostly found by `cargo clippy -- -W clippy::pedantic`.
This commit is contained in:
skbeh
2023-02-03 09:45:07 +08:00
committed by GitHub
parent bea93f6ad7
commit 219ea1c458
13 changed files with 217 additions and 245 deletions

View File

@@ -19,6 +19,7 @@ jobs:
include: include:
- target: aarch64-linux-android - target: aarch64-linux-android
- target: x86_64-linux-android - target: x86_64-linux-android
- target: x86_64-pc-windows-gnu # only for build
uses: ./.github/workflows/ksud.yml uses: ./.github/workflows/ksud.yml
with: with:
target: ${{ matrix.target }} target: ${{ matrix.target }}

View File

@@ -32,9 +32,12 @@ rust-embed = { version = "6.4.2", features = [
"compression", # must clean build after updating binaries "compression", # must clean build after updating binaries
] } ] }
[target.'cfg(target_os = "android")'.dependencies] [target.'cfg(unix)'.dependencies]
sys-mount = "2.0.1" sys-mount = "2.0.1"
# some android specific dependencies which compiles under unix are also listed here for convenience of coding
android-properties = { version = "0.2.2", features = ["bionic-deprecated"] } android-properties = { version = "0.2.2", features = ["bionic-deprecated"] }
[target.'cfg(target_os = "android")'.dependencies]
android_logger = "0.12" android_logger = "0.12"
[profile.release] [profile.release]

View File

@@ -1,6 +1,5 @@
use std::io::{Read, Seek, SeekFrom};
use anyhow::{ensure, Result}; use anyhow::{ensure, Result};
use std::io::{Read, Seek, SeekFrom};
pub fn get_apk_signature(apk: &str) -> Result<(u32, u32)> { pub fn get_apk_signature(apk: &str) -> Result<(u32, u32)> {
let mut buffer = [0u8; 0x10]; let mut buffer = [0u8; 0x10];
@@ -17,11 +16,11 @@ pub fn get_apk_signature(apk: &str) -> Result<(u32, u32)> {
f.read_exact(&mut n)?; f.read_exact(&mut n)?;
let n = u16::from_le_bytes(n); let n = u16::from_le_bytes(n);
if n as i64 == i { if i64::from(n) == i {
f.seek(SeekFrom::Current(-22))?; f.seek(SeekFrom::Current(-22))?;
f.read_exact(&mut size4)?; f.read_exact(&mut size4)?;
if u32::from_le_bytes(size4) ^ 0xcafebabeu32 == 0xccfbf1eeu32 { if u32::from_le_bytes(size4) ^ 0xcafe_babe_u32 == 0xccfb_f1ee_u32 {
if i > 0 { if i > 0 {
println!("warning: comment length is {i}"); println!("warning: comment length is {i}");
} }
@@ -37,14 +36,14 @@ pub fn get_apk_signature(apk: &str) -> Result<(u32, u32)> {
f.seek(SeekFrom::Current(12))?; f.seek(SeekFrom::Current(12))?;
// offset // offset
f.read_exact(&mut size4)?; f.read_exact(&mut size4)?;
f.seek(SeekFrom::Start(u32::from_le_bytes(size4) as u64 - 0x18))?; f.seek(SeekFrom::Start(u64::from(u32::from_le_bytes(size4)) - 0x18))?;
f.read_exact(&mut size8)?; f.read_exact(&mut size8)?;
f.read_exact(&mut buffer)?; f.read_exact(&mut buffer)?;
ensure!(&buffer == b"APK Sig Block 42", "Can not found sig block"); ensure!(&buffer == b"APK Sig Block 42", "Can not found sig block");
let pos = u32::from_le_bytes(size4) as u64 - (u64::from_le_bytes(size8) + 0x8); let pos = u64::from(u32::from_le_bytes(size4)) - (u64::from_le_bytes(size8) + 0x8);
f.seek(SeekFrom::Start(pos))?; f.seek(SeekFrom::Start(pos))?;
f.read_exact(&mut size_of_block)?; f.read_exact(&mut size_of_block)?;
@@ -62,7 +61,7 @@ pub fn get_apk_signature(apk: &str) -> Result<(u32, u32)> {
f.read_exact(&mut id)?; // id f.read_exact(&mut id)?; // id
let id = u32::from_le_bytes(id); let id = u32::from_le_bytes(id);
if (id ^ 0xdeadbeefu32) == 0xafa439f5u32 || (id ^ 0xdeadbeefu32) == 0x2efed62fu32 { if (id ^ 0xdead_beef_u32) == 0xafa4_39f5_u32 || (id ^ 0xdead_beef_u32) == 0x2efe_d62f_u32 {
f.read_exact(&mut size4)?; // signer-sequence length f.read_exact(&mut size4)?; // signer-sequence length
f.read_exact(&mut size4)?; // signer length f.read_exact(&mut size4)?; // signer length
f.read_exact(&mut size4)?; // signed data length f.read_exact(&mut size4)?; // signed data length
@@ -70,7 +69,7 @@ pub fn get_apk_signature(apk: &str) -> Result<(u32, u32)> {
f.read_exact(&mut size4)?; // digests-sequcence length f.read_exact(&mut size4)?; // digests-sequcence length
let pos = u32::from_le_bytes(size4); let pos = u32::from_le_bytes(size4);
f.seek(SeekFrom::Current(pos as i64))?; f.seek(SeekFrom::Current(i64::from(pos)))?;
// offset += 0x4 + pos; // offset += 0x4 + pos;
f.read_exact(&mut size4)?; // certificates length f.read_exact(&mut size4)?; // certificates length
@@ -83,18 +82,20 @@ pub fn get_apk_signature(apk: &str) -> Result<(u32, u32)> {
let j = u32::from_le_bytes(size4); let j = u32::from_le_bytes(size4);
for _ in 0..j { for _ in 0..j {
f.read_exact(&mut c)?; f.read_exact(&mut c)?;
hash = hash.wrapping_mul(31).wrapping_add(c[0] as i8 as i32); hash = hash.wrapping_mul(31).wrapping_add(i32::from(c[0] as i8));
} }
// offset += j; // offset += j;
let out_size = j; let out_size = j;
let out_hash = (hash as u32) ^ 0x14131211u32; let out_hash = (hash as u32) ^ 0x1413_1211_u32;
return Ok((out_size, out_hash)); return Ok((out_size, out_hash));
} }
f.seek(SeekFrom::Current(i64::from_le_bytes(size8) - offset as i64))?; f.seek(SeekFrom::Current(
i64::from_le_bytes(size8) - i64::from(offset),
))?;
} }
Err(anyhow::anyhow!("Unknown error")) Err(anyhow::anyhow!("Unknown error"))

View File

@@ -1,7 +1,9 @@
use anyhow::{Ok, Result}; use anyhow::{Ok, Result};
use clap::Parser; use clap::Parser;
#[cfg(target_os="android")]
#[cfg(target_os = "android")]
use android_logger::Config; use android_logger::Config;
#[cfg(target_os = "android")]
use log::LevelFilter; use log::LevelFilter;
use crate::{apk_sign, debug, event, module}; use crate::{apk_sign, debug, event, module};
@@ -120,14 +122,14 @@ enum Module {
} }
pub fn run() -> Result<()> { pub fn run() -> Result<()> {
#[cfg(target_os="android")] #[cfg(target_os = "android")]
android_logger::init_once( android_logger::init_once(
Config::default() Config::default()
.with_max_level(LevelFilter::Trace) // limit log level .with_max_level(LevelFilter::Trace) // limit log level
.with_tag("KernelSU") // logs will show under mytag tag .with_tag("KernelSU"), // logs will show under mytag tag
); );
#[cfg(not(target_os="android"))] #[cfg(not(target_os = "android"))]
env_logger::init(); env_logger::init();
let cli = Args::parse(); let cli = Args::parse();
@@ -139,16 +141,13 @@ pub fn run() -> Result<()> {
Commands::PostFsData => event::on_post_data_fs(), Commands::PostFsData => event::on_post_data_fs(),
Commands::BootCompleted => event::on_boot_completed(), Commands::BootCompleted => event::on_boot_completed(),
Commands::Module { command } => { Commands::Module { command } => match command {
Module::Install { zip } => module::install_module(&zip),
match command { Module::Uninstall { id } => module::uninstall_module(&id),
Module::Install { zip } => module::install_module(zip), Module::Enable { id } => module::enable_module(&id),
Module::Uninstall { id } => module::uninstall_module(id), Module::Disable { id } => module::disable_module(&id),
Module::Enable { id } => module::enable_module(id), Module::List => module::list_modules(),
Module::Disable { id } => module::disable_module(id), },
Module::List => module::list_modules(),
}
}
Commands::Install => event::install(), Commands::Install => event::install(),
Commands::Sepolicy { command } => match command { Commands::Sepolicy { command } => match command {
Sepolicy::Patch { sepolicy } => crate::sepolicy::live_patch(&sepolicy), Sepolicy::Patch { sepolicy } => crate::sepolicy::live_patch(&sepolicy),
@@ -168,7 +167,7 @@ pub fn run() -> Result<()> {
Ok(()) Ok(())
} }
Debug::Su => crate::ksu::grant_root(), Debug::Su => crate::ksu::grant_root(),
Debug::Test => todo!() Debug::Test => todo!(),
}, },
}; };

View File

@@ -55,7 +55,7 @@ pub fn set_manager(pkg: &str) -> Result<()> {
); );
let path = get_apk_path(pkg).with_context(|| format!("{pkg} does not exist!"))?; let path = get_apk_path(pkg).with_context(|| format!("{pkg} does not exist!"))?;
let sign = get_apk_signature(path.as_str())?; let sign = get_apk_signature(&path)?;
set_kernel_param(sign.0, sign.1)?; set_kernel_param(sign.0, sign.1)?;
Ok(()) Ok(())
} }

View File

@@ -1,11 +1,11 @@
use anyhow::{bail, Context, Result};
use log::{info, warn};
use std::{collections::HashMap, path::Path}; use std::{collections::HashMap, path::Path};
use crate::{ use crate::{
assets, defs, mount, assets, defs, mount,
utils::{ensure_clean_dir, ensure_dir_exists}, utils::{ensure_clean_dir, ensure_dir_exists},
}; };
use anyhow::{bail, Context, Result};
use log::{info, warn};
fn mount_partition(partition: &str, lowerdir: &mut Vec<String>) -> Result<()> { fn mount_partition(partition: &str, lowerdir: &mut Vec<String>) -> Result<()> {
if lowerdir.is_empty() { if lowerdir.is_empty() {
@@ -28,7 +28,7 @@ fn mount_partition(partition: &str, lowerdir: &mut Vec<String>) -> Result<()> {
mount::mount_overlay(&lowerdir, &lowest_dir) mount::mount_overlay(&lowerdir, &lowest_dir)
} }
pub fn do_systemless_mount(module_dir: &str) -> Result<()> { pub fn mount_systemlessly(module_dir: &str) -> Result<()> {
// construct overlay mount params // construct overlay mount params
let dir = std::fs::read_dir(module_dir); let dir = std::fs::read_dir(module_dir);
let Ok(dir) = dir else { let Ok(dir) = dir else {
@@ -40,7 +40,7 @@ pub fn do_systemless_mount(module_dir: &str) -> Result<()> {
let partition = vec!["vendor", "product", "system_ext", "odm", "oem"]; let partition = vec!["vendor", "product", "system_ext", "odm", "oem"];
let mut partition_lowerdir: HashMap<String, Vec<String>> = HashMap::new(); let mut partition_lowerdir: HashMap<String, Vec<String>> = HashMap::new();
for ele in &partition { for ele in &partition {
partition_lowerdir.insert(ele.to_string(), Vec::new()); partition_lowerdir.insert((*ele).to_string(), Vec::new());
} }
for entry in dir.flatten() { for entry in dir.flatten() {
@@ -55,7 +55,7 @@ pub fn do_systemless_mount(module_dir: &str) -> Result<()> {
} }
let module_system = Path::new(&module).join("system"); let module_system = Path::new(&module).join("system");
if !module_system.as_path().exists() { if !module_system.exists() {
info!("module: {} has no system overlay.", module.display()); info!("module: {} has no system overlay.", module.display());
continue; continue;
} }
@@ -133,12 +133,14 @@ pub fn on_post_data_fs() -> Result<()> {
} }
// mount systemless overlay // mount systemless overlay
if let Err(e) = do_systemless_mount(module_dir) { if let Err(e) = mount_systemlessly(module_dir) {
warn!("do systemless mount failed: {}", e); warn!("do systemless mount failed: {}", e);
} }
// module mounted, exec modules post-fs-data scripts // module mounted, exec modules post-fs-data scripts
if !crate::utils::is_safe_mode() { if crate::utils::is_safe_mode() {
warn!("safe mode, skip module post-fs-data scripts");
} else {
// todo: Add timeout // todo: Add timeout
if let Err(e) = crate::module::exec_common_scripts("post-fs-data.d", true) { if let Err(e) = crate::module::exec_common_scripts("post-fs-data.d", true) {
warn!("exec common post-fs-data scripts failed: {}", e); warn!("exec common post-fs-data scripts failed: {}", e);
@@ -149,8 +151,6 @@ pub fn on_post_data_fs() -> Result<()> {
if let Err(e) = crate::module::load_system_prop() { if let Err(e) = crate::module::load_system_prop() {
warn!("load system.prop failed: {}", e); warn!("load system.prop failed: {}", e);
} }
} else {
warn!("safe mode, skip module post-fs-data scripts");
} }
Ok(()) Ok(())
@@ -158,15 +158,15 @@ pub fn on_post_data_fs() -> Result<()> {
pub fn on_services() -> Result<()> { pub fn on_services() -> Result<()> {
// exec modules service.sh scripts // exec modules service.sh scripts
if !crate::utils::is_safe_mode() { if crate::utils::is_safe_mode() {
warn!("safe mode, skip module service scripts");
} else {
if let Err(e) = crate::module::exec_common_scripts("service.d", false) { if let Err(e) = crate::module::exec_common_scripts("service.d", false) {
warn!("exec common service scripts failed: {}", e); warn!("exec common service scripts failed: {}", e);
} }
if let Err(e) = crate::module::exec_services() { if let Err(e) = crate::module::exec_services() {
warn!("exec service scripts failed: {}", e); warn!("exec service scripts failed: {}", e);
} }
} else {
warn!("safe mode, skip module service scripts");
} }
Ok(()) Ok(())

View File

@@ -1,9 +1,11 @@
#![allow(dead_code, unused_mut, unused_variables, unused_imports)] use anyhow::Result;
use anyhow::{ensure, Result}; #[cfg(unix)]
use anyhow::ensure;
#[cfg(unix)]
use std::os::unix::process::CommandExt; use std::os::unix::process::CommandExt;
pub const KERNEL_SU_OPTION: u32 = 0xDEADBEEF; pub const KERNEL_SU_OPTION: u32 = 0xDEAD_BEEF;
const CMD_GRANT_ROOT: u64 = 0; const CMD_GRANT_ROOT: u64 = 0;
// const CMD_BECOME_MANAGER: u64 = 1; // const CMD_BECOME_MANAGER: u64 = 1;
@@ -18,45 +20,52 @@ pub const CMD_SET_SEPOLICY: u64 = 8;
const EVENT_POST_FS_DATA: u64 = 1; const EVENT_POST_FS_DATA: u64 = 1;
const EVENT_BOOT_COMPLETED: u64 = 2; const EVENT_BOOT_COMPLETED: u64 = 2;
#[cfg(target_os = "android")] #[cfg(unix)]
pub fn grant_root() -> Result<()> { pub fn grant_root() -> Result<()> {
let mut result: u32 = 0; let mut result: u32 = 0;
unsafe { unsafe {
#[allow(clippy::cast_possible_wrap)]
libc::prctl( libc::prctl(
KERNEL_SU_OPTION as i32, KERNEL_SU_OPTION as i32, // supposed to overflow
CMD_GRANT_ROOT, CMD_GRANT_ROOT,
0, 0,
0, 0,
&mut result as *mut _ as *mut libc::c_void, std::ptr::addr_of_mut!(result).cast::<libc::c_void>(),
); );
} }
ensure!(result == KERNEL_SU_OPTION, "grant root failed"); ensure!(result == KERNEL_SU_OPTION, "grant root failed");
return Err(std::process::Command::new("sh").exec().into()); Err(std::process::Command::new("sh").exec().into())
} }
#[cfg(not(target_os = "android"))] #[cfg(not(unix))]
pub fn grant_root() -> Result<()> { pub fn grant_root() -> Result<()> {
unimplemented!("grant_root is only available on android"); unimplemented!("grant_root is only available on android");
} }
pub fn get_version() -> i32 { pub fn get_version() -> i32 {
let mut result: i32 = 0; let mut result: i32 = 0;
#[cfg(target_os = "android")] #[cfg(unix)]
unsafe { unsafe {
#[allow(clippy::cast_possible_wrap)]
libc::prctl( libc::prctl(
KERNEL_SU_OPTION as i32, KERNEL_SU_OPTION as i32, // supposed to overflow
CMD_GET_VERSION, CMD_GET_VERSION,
&mut result as *mut _ as *mut libc::c_void, std::ptr::addr_of_mut!(result).cast::<libc::c_void>(),
); );
} }
result result
} }
fn report_event(event: u64) { fn report_event(event: u64) {
#[cfg(target_os = "android")] #[cfg(unix)]
unsafe { unsafe {
libc::prctl(KERNEL_SU_OPTION as i32, CMD_REPORT_EVENT, event); #[allow(clippy::cast_possible_wrap)]
libc::prctl(
KERNEL_SU_OPTION as i32, // supposed to overflow
CMD_REPORT_EVENT,
event,
);
} }
} }

View File

@@ -1,15 +1,15 @@
mod apk_sign; mod apk_sign;
mod assets;
mod cli; mod cli;
mod debug; mod debug;
mod defs; mod defs;
mod event; mod event;
mod ksu; mod ksu;
mod module; mod module;
mod restorecon;
mod utils;
mod sepolicy;
mod assets;
mod mount; mod mount;
mod restorecon;
mod sepolicy;
mod utils;
fn main() -> anyhow::Result<()> { fn main() -> anyhow::Result<()> {
cli::run() cli::run()

View File

@@ -1,26 +1,29 @@
#[allow(clippy::wildcard_imports)]
use crate::utils::*;
use crate::{ use crate::{
assets, defs, mount, assets, defs, mount,
restorecon::{restore_syscon, setsyscon}, restorecon::{restore_syscon, setsyscon},
sepolicy, sepolicy,
utils::*,
}; };
use anyhow::{anyhow, bail, ensure, Context, Result};
use const_format::concatcp; use const_format::concatcp;
use is_executable::is_executable;
use java_properties::PropertiesIter; use java_properties::PropertiesIter;
use log::{debug, info, warn}; use log::{info, warn};
use std::{ use std::{
collections::HashMap, collections::HashMap,
env::var as env_var, env::var as env_var,
fs::{remove_dir_all, set_permissions, File, OpenOptions, Permissions}, fs::{remove_dir_all, set_permissions, File, OpenOptions, Permissions},
io::{Cursor, Read, Write}, io::{Cursor, Write},
os::unix::{prelude::PermissionsExt, process::CommandExt},
path::{Path, PathBuf}, path::{Path, PathBuf},
process::{Command, Stdio}, process::{Command, Stdio},
str::FromStr, str::FromStr,
}; };
use zip_extensions::zip_extract_file_to_memory; use zip_extensions::zip_extract_file_to_memory;
use anyhow::{bail, ensure, Context, Result}; #[cfg(unix)]
use std::os::unix::{prelude::PermissionsExt, process::CommandExt};
const UTIL_FUNCTIONS: &str = include_str!("./installer.sh"); const UTIL_FUNCTIONS: &str = include_str!("./installer.sh");
const INSTALL_MODULE_SCRIPT: &str = const INSTALL_MODULE_SCRIPT: &str =
@@ -161,17 +164,6 @@ fn switch_cgroups() {
} }
} }
fn is_executable(path: &Path) -> bool {
let mut buffer = [0u8; 2];
is_executable::is_executable(path)
&& File::open(path).unwrap().read_exact(&mut buffer).is_ok()
&& (
buffer == [0x23, 0x21] // shebang #!
|| buffer == [0x7f, 0x45]
// ELF magic number 0x7F 'E'
)
}
pub fn load_sepolicy_rule() -> Result<()> { pub fn load_sepolicy_rule() -> Result<()> {
let modules_dir = Path::new(defs::MODULE_DIR); let modules_dir = Path::new(defs::MODULE_DIR);
let dir = std::fs::read_dir(modules_dir)?; let dir = std::fs::read_dir(modules_dir)?;
@@ -197,6 +189,40 @@ pub fn load_sepolicy_rule() -> Result<()> {
Ok(()) Ok(())
} }
fn exec_script<T: AsRef<Path>>(path: T, wait: bool) -> Result<()> {
info!("exec {}", path.as_ref().display());
let mut command = &mut Command::new(assets::BUSYBOX_PATH);
#[cfg(unix)]
{
command = command.process_group(0);
command = unsafe {
command.pre_exec(|| {
// ignore the error?
switch_cgroups();
Ok(())
})
};
}
command = command
.current_dir(path.as_ref().parent().unwrap())
.arg("sh")
.arg(path.as_ref())
.env("ASH_STANDALONE", "1")
.env(
"PATH",
format!("{}:{}", env_var("PATH").unwrap(), defs::BINARY_DIR),
)
.env("KSU", "true");
let result = if wait {
command.status().map(|_| ())
} else {
command.spawn().map(|_| ())
};
result.map_err(|err| anyhow!("Failed to exec {}: {}", path.as_ref().display(), err))
}
/// execute every modules' post-fs-data.sh /// execute every modules' post-fs-data.sh
pub fn exec_post_fs_data() -> Result<()> { pub fn exec_post_fs_data() -> Result<()> {
let modules_dir = Path::new(defs::MODULE_DIR); let modules_dir = Path::new(defs::MODULE_DIR);
@@ -213,31 +239,8 @@ pub fn exec_post_fs_data() -> Result<()> {
if !post_fs_data.exists() { if !post_fs_data.exists() {
continue; continue;
} }
info!("exec {} post-fs-data.sh", path.display());
let mut command = Command::new(assets::BUSYBOX_PATH); exec_script(path, true)?;
let command = command.arg("sh");
let command = command.arg(&post_fs_data);
let command = command
.process_group(0)
.current_dir(path)
.env("ASH_STANDALONE", "1")
.env(
"PATH",
format!("{}:{}", env_var("PATH").unwrap(), defs::BINARY_DIR),
)
.env("KSU", "true");
let command = unsafe {
command.pre_exec(|| {
// ignore the error?
switch_cgroups();
Ok(())
})
};
command
.status()
.with_context(|| format!("Failed to exec {}", post_fs_data.display()))?;
} }
Ok(()) Ok(())
@@ -263,37 +266,7 @@ pub fn exec_common_scripts(dir: &str, wait: bool) -> Result<()> {
continue; continue;
} }
info!("exec {}", path.display()); exec_script(path, wait)?;
let mut command = Command::new(assets::BUSYBOX_PATH);
let command = command.arg("sh");
let command = command.arg(&path);
let command = command
.process_group(0)
.current_dir(&script_dir)
.env("ASH_STANDALONE", "1")
.env(
"PATH",
format!("{}:{}", env_var("PATH").unwrap(), defs::BINARY_DIR),
)
.env("KSU", "true");
let command = unsafe {
command.pre_exec(|| {
switch_cgroups();
Ok(())
})
};
if !wait {
command
.spawn() // don't wait
.with_context(|| format!("Failed to exec {}", path.display()))?;
} else {
command
.status()
.with_context(|| format!("Failed to exec {}", path.display()))?;
}
} }
Ok(()) Ok(())
@@ -315,31 +288,8 @@ pub fn exec_services() -> Result<()> {
if !service.exists() { if !service.exists() {
continue; continue;
} }
info!("exec {} service.sh", path.display());
let mut command = Command::new(assets::BUSYBOX_PATH); exec_script(path, false)?;
let command = command.arg("sh");
let command = command.arg(&service);
let command = command
.process_group(0)
.current_dir(path)
.env("ASH_STANDALONE", "1")
.env(
"PATH",
format!("{}:{}", env_var("PATH").unwrap(), defs::BINARY_DIR),
)
.env("KSU", "true");
let command = unsafe {
command.pre_exec(|| {
// ignore the error?
switch_cgroups();
Ok(())
})
};
command
.spawn() // don't wait
.with_context(|| format!("Failed to exec {}", service.display()))?;
} }
Ok(()) Ok(())
@@ -374,7 +324,7 @@ pub fn load_system_prop() -> Result<()> {
Ok(()) Ok(())
} }
fn do_install_module(zip: String) -> Result<()> { fn _install_module(zip: &str) -> Result<()> {
ensure_boot_completed()?; ensure_boot_completed()?;
// print banner // print banner
@@ -389,7 +339,7 @@ fn do_install_module(zip: String) -> Result<()> {
// read the module_id from zip, if faild if will return early. // read the module_id from zip, if faild if will return early.
let mut buffer: Vec<u8> = Vec::new(); let mut buffer: Vec<u8> = Vec::new();
let entry_path = PathBuf::from_str("module.prop")?; let entry_path = PathBuf::from_str("module.prop")?;
let zip_path = PathBuf::from_str(&zip)?; let zip_path = PathBuf::from_str(zip)?;
zip_extract_file_to_memory(&zip_path, &entry_path, &mut buffer)?; zip_extract_file_to_memory(&zip_path, &entry_path, &mut buffer)?;
let mut module_prop = HashMap::new(); let mut module_prop = HashMap::new();
@@ -419,7 +369,7 @@ fn do_install_module(zip: String) -> Result<()> {
} }
let default_reserve_size = 64 * 1024 * 1024; let default_reserve_size = 64 * 1024 * 1024;
let zip_uncompressed_size = get_zip_uncompressed_size(&zip)?; let zip_uncompressed_size = get_zip_uncompressed_size(zip)?;
let grow_size = default_reserve_size + zip_uncompressed_size; let grow_size = default_reserve_size + zip_uncompressed_size;
info!( info!(
@@ -501,16 +451,17 @@ fn do_install_module(zip: String) -> Result<()> {
info!("module dir: {}", module_dir); info!("module dir: {}", module_dir);
// unzip the image and move it to modules_update/<id> dir // unzip the image and move it to modules_update/<id> dir
let file = File::open(&zip)?; let file = File::open(zip)?;
let mut archive = zip::ZipArchive::new(file)?; let mut archive = zip::ZipArchive::new(file)?;
archive.extract(&module_dir)?; archive.extract(&module_dir)?;
exec_install_script(&zip)?; exec_install_script(zip)?;
// set permission and selinux context for $MOD/system // set permission and selinux context for $MOD/system
let module_system_dir = PathBuf::from(module_dir).join("system"); let module_system_dir = PathBuf::from(module_dir).join("system");
if module_system_dir.exists() { if module_system_dir.exists() {
let path = module_system_dir.to_str().unwrap(); let path = module_system_dir.to_str().unwrap();
#[cfg(unix)]
set_permissions(&module_system_dir, Permissions::from_mode(0o755))?; set_permissions(&module_system_dir, Permissions::from_mode(0o755))?;
restore_syscon(path)?; restore_syscon(path)?;
} }
@@ -531,8 +482,8 @@ fn do_install_module(zip: String) -> Result<()> {
Ok(()) Ok(())
} }
pub fn install_module(zip: String) -> Result<()> { pub fn install_module(zip: &str) -> Result<()> {
let result = do_install_module(zip); let result = _install_module(zip);
if let Err(ref e) = result { if let Err(ref e) = result {
// error happened, do some cleanup! // error happened, do some cleanup!
let _ = std::fs::remove_file(defs::MODULE_UPDATE_TMP_IMG); let _ = std::fs::remove_file(defs::MODULE_UPDATE_TMP_IMG);
@@ -542,7 +493,7 @@ pub fn install_module(zip: String) -> Result<()> {
result result
} }
fn do_module_update<F>(update_dir: &str, id: &str, func: F) -> Result<()> fn update_module<F>(update_dir: &str, id: &str, func: F) -> Result<()>
where where
F: Fn(&str, &str) -> Result<()>, F: Fn(&str, &str) -> Result<()>,
{ {
@@ -587,8 +538,8 @@ where
result result
} }
pub fn uninstall_module(id: String) -> Result<()> { pub fn uninstall_module(id: &str) -> Result<()> {
do_module_update(defs::MODULE_UPDATE_TMP_DIR, &id, |mid, update_dir| { update_module(defs::MODULE_UPDATE_TMP_DIR, id, |mid, update_dir| {
let dir = Path::new(update_dir); let dir = Path::new(update_dir);
ensure!(dir.exists(), "No module installed"); ensure!(dir.exists(), "No module installed");
@@ -621,13 +572,13 @@ pub fn uninstall_module(id: String) -> Result<()> {
remove_dir_all(target_module)?; remove_dir_all(target_module)?;
} }
let _ = mark_module_state(&id, defs::REMOVE_FILE_NAME, true); let _ = mark_module_state(id, defs::REMOVE_FILE_NAME, true);
Ok(()) Ok(())
}) })
} }
fn do_enable_module(module_dir: &str, mid: &str, enable: bool) -> Result<()> { fn _enable_module(module_dir: &str, mid: &str, enable: bool) -> Result<()> {
let src_module_path = format!("{module_dir}/{mid}"); let src_module_path = format!("{module_dir}/{mid}");
let src_module = Path::new(&src_module_path); let src_module = Path::new(&src_module_path);
ensure!(src_module.exists(), "module: {} not found!", mid); ensure!(src_module.exists(), "module: {} not found!", mid);
@@ -648,19 +599,19 @@ fn do_enable_module(module_dir: &str, mid: &str, enable: bool) -> Result<()> {
Ok(()) Ok(())
} }
pub fn enable_module(id: String) -> Result<()> { pub fn enable_module(id: &str) -> Result<()> {
do_module_update(defs::MODULE_UPDATE_TMP_DIR, &id, |mid, update_dir| { update_module(defs::MODULE_UPDATE_TMP_DIR, id, |mid, update_dir| {
do_enable_module(update_dir, mid, true) _enable_module(update_dir, mid, true)
}) })
} }
pub fn disable_module(id: String) -> Result<()> { pub fn disable_module(id: &str) -> Result<()> {
do_module_update(defs::MODULE_UPDATE_TMP_DIR, &id, |mid, update_dir| { update_module(defs::MODULE_UPDATE_TMP_DIR, id, |mid, update_dir| {
do_enable_module(update_dir, mid, false) _enable_module(update_dir, mid, false)
}) })
} }
fn do_list_modules(path: &str) -> Vec<HashMap<String, String>> { fn _list_modules(path: &str) -> Vec<HashMap<String, String>> {
// first check enabled modules // first check enabled modules
let dir = std::fs::read_dir(path); let dir = std::fs::read_dir(path);
let Ok(dir) = dir else { let Ok(dir) = dir else {
@@ -681,7 +632,7 @@ fn do_list_modules(path: &str) -> Vec<HashMap<String, String>> {
warn!("Failed to read file: {}", module_prop.display()); warn!("Failed to read file: {}", module_prop.display());
continue; continue;
}; };
let mut module_prop_map = HashMap::new(); let mut module_prop_map: HashMap<String, String> = HashMap::new();
let encoding = encoding::all::UTF_8; let encoding = encoding::all::UTF_8;
let result = let result =
PropertiesIter::new_with_encoding(Cursor::new(content), encoding).read_into(|k, v| { PropertiesIter::new_with_encoding(Cursor::new(content), encoding).read_into(|k, v| {
@@ -693,9 +644,9 @@ fn do_list_modules(path: &str) -> Vec<HashMap<String, String>> {
let update = path.join(defs::UPDATE_FILE_NAME).exists(); let update = path.join(defs::UPDATE_FILE_NAME).exists();
let remove = path.join(defs::REMOVE_FILE_NAME).exists(); let remove = path.join(defs::REMOVE_FILE_NAME).exists();
module_prop_map.insert("enabled".to_string(), enabled.to_string()); module_prop_map.insert("enabled".to_owned(), enabled.to_string());
module_prop_map.insert("update".to_string(), update.to_string()); module_prop_map.insert("update".to_owned(), update.to_string());
module_prop_map.insert("remove".to_string(), remove.to_string()); module_prop_map.insert("remove".to_owned(), remove.to_string());
if result.is_err() { if result.is_err() {
warn!("Failed to parse module.prop: {}", module_prop.display()); warn!("Failed to parse module.prop: {}", module_prop.display());
@@ -708,7 +659,7 @@ fn do_list_modules(path: &str) -> Vec<HashMap<String, String>> {
} }
pub fn list_modules() -> Result<()> { pub fn list_modules() -> Result<()> {
let modules = do_list_modules(defs::MODULE_DIR); let modules = _list_modules(defs::MODULE_DIR);
println!("{}", serde_json::to_string_pretty(&modules)?); println!("{}", serde_json::to_string_pretty(&modules)?);
Ok(()) Ok(())
} }

View File

@@ -1,23 +1,21 @@
use anyhow::Result; use anyhow::Result;
#[cfg(target_os = "android")] #[cfg(unix)]
use anyhow::{Context, Ok}; use anyhow::{Context, Ok};
#[cfg(target_os = "android")] #[cfg(unix)]
use retry::delay::NoDelay; use retry::delay::NoDelay;
#[cfg(target_os = "android")] #[cfg(unix)]
use sys_mount::{unmount, FilesystemType, Mount, MountFlags, Unmount, UnmountFlags}; use sys_mount::{unmount, FilesystemType, Mount, MountFlags, Unmount, UnmountFlags};
#[allow(dead_code)]
pub struct AutoMountExt4 { pub struct AutoMountExt4 {
mnt: String, mnt: String,
#[cfg(target_os = "android")] #[cfg(unix)]
mount: Option<Mount>, mount: Option<Mount>,
auto_umount: bool, auto_umount: bool,
} }
#[allow(dead_code)]
impl AutoMountExt4 { impl AutoMountExt4 {
#[cfg(target_os = "android")] #[cfg(unix)]
pub fn try_new(src: &str, mnt: &str, auto_umount: bool) -> Result<Self> { pub fn try_new(src: &str, mnt: &str, auto_umount: bool) -> Result<Self> {
let result = Mount::builder() let result = Mount::builder()
.fstype(FilesystemType::from("ext4")) .fstype(FilesystemType::from("ext4"))
@@ -27,11 +25,11 @@ impl AutoMountExt4 {
Ok(Self { Ok(Self {
mnt: mnt.to_string(), mnt: mnt.to_string(),
mount: Some(mount), mount: Some(mount),
auto_umount auto_umount,
}) })
}); });
if let Err(e) = result { if let Err(e) = result {
println!("- Mount failed: {}, retry with system mount", e); println!("- Mount failed: {e}, retry with system mount");
let result = std::process::Command::new("mount") let result = std::process::Command::new("mount")
.arg("-t") .arg("-t")
.arg("ext4") .arg("ext4")
@@ -46,7 +44,7 @@ impl AutoMountExt4 {
Ok(Self { Ok(Self {
mnt: mnt.to_string(), mnt: mnt.to_string(),
mount: None, mount: None,
auto_umount auto_umount,
}) })
} }
} else { } else {
@@ -54,41 +52,45 @@ impl AutoMountExt4 {
} }
} }
#[cfg(not(target_os = "android"))] #[cfg(not(unix))]
pub fn try_new(_src: &str, _mnt: &str, _auto_umount: bool) -> Result<Self> { pub fn try_new(_src: &str, _mnt: &str, _auto_umount: bool) -> Result<Self> {
unimplemented!() unimplemented!()
} }
#[cfg(target_os = "android")] #[cfg(unix)]
pub fn umount(&self) -> Result<()> { pub fn umount(&self) -> Result<()> {
match self.mount { if let Some(ref mount) = self.mount {
Some(ref mount) => mount mount
.unmount(UnmountFlags::empty()) .unmount(UnmountFlags::empty())
.map_err(|e| anyhow::anyhow!(e)), .map_err(|e| anyhow::anyhow!(e))
None => { } else {
let result = std::process::Command::new("umount").arg(&self.mnt).status(); let result = std::process::Command::new("umount").arg(&self.mnt).status();
if let Err(e) = result { if let Err(e) = result {
Err(anyhow::anyhow!("umount: {} failed: {e}", self.mnt)) Err(anyhow::anyhow!("umount: {} failed: {e}", self.mnt))
} else { } else {
Ok(()) Ok(())
}
} }
} }
} }
} }
#[cfg(target_os = "android")] #[cfg(unix)]
impl Drop for AutoMountExt4 { impl Drop for AutoMountExt4 {
fn drop(&mut self) { fn drop(&mut self) {
log::info!("AutoMountExt4 drop: {}, auto_umount: {}", self.mnt, self.auto_umount); log::info!(
"AutoMountExt4 drop: {}, auto_umount: {}",
self.mnt,
self.auto_umount
);
if self.auto_umount { if self.auto_umount {
let _ = self.umount(); let _ = self.umount();
} }
} }
} }
#[cfg(target_os = "android")] #[allow(dead_code)]
fn do_mount_image(src: &str, target: &str, autodrop: bool) -> Result<()> { #[cfg(unix)]
fn mount_image(src: &str, target: &str, autodrop: bool) -> Result<()> {
if autodrop { if autodrop {
Mount::builder() Mount::builder()
.fstype(FilesystemType::from("ext4")) .fstype(FilesystemType::from("ext4"))
@@ -103,23 +105,24 @@ fn do_mount_image(src: &str, target: &str, autodrop: bool) -> Result<()> {
Ok(()) Ok(())
} }
#[cfg(target_os = "android")] #[allow(dead_code)]
#[cfg(unix)]
pub fn mount_ext4(src: &str, target: &str, autodrop: bool) -> Result<()> { pub fn mount_ext4(src: &str, target: &str, autodrop: bool) -> Result<()> {
// umount target first. // umount target first.
let _ = umount_dir(target); let _ = umount_dir(target);
let result = retry::retry(NoDelay.take(3), || do_mount_image(src, target, autodrop)); let result = retry::retry(NoDelay.take(3), || mount_image(src, target, autodrop));
result result
.map_err(|e| anyhow::anyhow!("mount partition: {src} -> {target} failed: {e}")) .map_err(|e| anyhow::anyhow!("mount partition: {src} -> {target} failed: {e}"))
.map(|_| ()) .map(|_| ())
} }
#[cfg(target_os = "android")] #[cfg(unix)]
pub fn umount_dir(src: &str) -> Result<()> { pub fn umount_dir(src: &str) -> Result<()> {
unmount(src, UnmountFlags::empty()).with_context(|| format!("Failed to umount {src}"))?; unmount(src, UnmountFlags::empty()).with_context(|| format!("Failed to umount {src}"))?;
Ok(()) Ok(())
} }
#[cfg(target_os = "android")] #[cfg(unix)]
pub fn mount_overlay(lowerdir: &str, mnt: &str) -> Result<()> { pub fn mount_overlay(lowerdir: &str, mnt: &str) -> Result<()> {
Mount::builder() Mount::builder()
.fstype(FilesystemType::from("overlay")) .fstype(FilesystemType::from("overlay"))
@@ -130,17 +133,17 @@ pub fn mount_overlay(lowerdir: &str, mnt: &str) -> Result<()> {
.map_err(|e| anyhow::anyhow!("mount partition: {mnt} overlay failed: {e}")) .map_err(|e| anyhow::anyhow!("mount partition: {mnt} overlay failed: {e}"))
} }
#[cfg(not(target_os = "android"))] #[cfg(not(unix))]
pub fn mount_ext4(_src: &str, _target: &str, _autodrop: bool) -> Result<()> { pub fn mount_ext4(_src: &str, _target: &str, _autodrop: bool) -> Result<()> {
unimplemented!() unimplemented!()
} }
#[cfg(not(target_os = "android"))] #[cfg(not(unix))]
pub fn umount_dir(_src: &str) -> Result<()> { pub fn umount_dir(_src: &str) -> Result<()> {
unimplemented!() unimplemented!()
} }
#[cfg(not(target_os = "android"))] #[cfg(not(unix))]
pub fn mount_overlay(_lowerdir: &str, _mnt: &str) -> Result<()> { pub fn mount_overlay(_lowerdir: &str, _mnt: &str) -> Result<()> {
unimplemented!() unimplemented!()
} }

View File

@@ -1,33 +1,37 @@
use anyhow::{Context, Ok, Result}; use anyhow::Result;
#[cfg(target_os = "android")]
use extattr::{setxattr, Flags as XattrFlags};
use jwalk::{Parallelism::Serial, WalkDir}; use jwalk::{Parallelism::Serial, WalkDir};
#[cfg(unix)]
use anyhow::{Context, Ok};
#[cfg(unix)]
use extattr::{setxattr, Flags as XattrFlags};
const SYSTEM_CON: &str = "u:object_r:system_file:s0"; const SYSTEM_CON: &str = "u:object_r:system_file:s0";
const _ADB_CON: &str = "u:object_r:adb_data_file:s0"; const _ADB_CON: &str = "u:object_r:adb_data_file:s0";
pub fn setcon(path: &str, con: &str) -> Result<()> { pub fn setcon(path: &str, con: &str) -> Result<()> {
#[cfg(target_os = "android")] #[cfg(unix)]
setxattr(path, "security.selinux", con, XattrFlags::empty()) setxattr(path, "security.selinux", con, XattrFlags::empty())
.with_context(|| format!("Failed to change SELinux context for {path}"))?; .with_context(|| format!("Failed to change SELinux context for {path}"))?;
Ok(()) Ok(())
} }
#[cfg(unix)]
pub fn setsyscon(path: &str) -> Result<()> { pub fn setsyscon(path: &str) -> Result<()> {
setcon(path, SYSTEM_CON) setcon(path, SYSTEM_CON)
} }
#[cfg(not(unix))]
pub fn setsyscon(_path: &str) -> Result<()> {
unimplemented!()
}
pub fn restore_syscon(dir: &str) -> Result<()> { pub fn restore_syscon(dir: &str) -> Result<()> {
for dir_entry in WalkDir::new(dir).parallelism(Serial) { for dir_entry in WalkDir::new(dir).parallelism(Serial) {
if let Some(path) = dir_entry.ok().map(|dir_entry| dir_entry.path()) { if let Some(path) = dir_entry.ok().map(|dir_entry| dir_entry.path()) {
#[cfg(target_os = "android")] #[cfg(unix)]
setxattr(&path, "security.selinux", SYSTEM_CON, XattrFlags::empty()).with_context( setxattr(&path, "security.selinux", SYSTEM_CON, XattrFlags::empty()).with_context(
|| { || format!("Failed to change SELinux context for {}", path.display()),
format!(
"Failed to change SELinux context for {}",
path.to_str().unwrap()
)
},
)?; )?;
} }
} }

View File

@@ -345,7 +345,7 @@ impl<'a> PolicyStatement<'a> {
} }
} }
fn parse_sepolicy<'a, 'b>(input: &'b str) -> Result<Vec<PolicyStatement<'a>>> fn parse_sepolicy<'a, 'b>(input: &'b str) -> Vec<PolicyStatement<'a>>
where where
'b: 'a, 'b: 'a,
{ {
@@ -356,7 +356,7 @@ where
statements.push(statement); statements.push(statement);
} }
} }
Ok(statements) statements
} }
const SEPOLICY_MAX_LEN: usize = 128; const SEPOLICY_MAX_LEN: usize = 128;
@@ -401,6 +401,7 @@ impl TryFrom<&str> for PolicyObject {
/// normal statement would be expand to atomic statement, for example: /// normal statement would be expand to atomic statement, for example:
/// allow domain1 domain2:file1 { read write }; would be expand to two atomic statement /// allow domain1 domain2:file1 { read write }; would be expand to two atomic statement
/// allow domain1 domain2:file1 read;allow domain1 domain2:file1 write; /// allow domain1 domain2:file1 read;allow domain1 domain2:file1 write;
#[allow(clippy::too_many_arguments)]
#[derive(Debug, new)] #[derive(Debug, new)]
struct AtomicStatement { struct AtomicStatement {
cmd: u32, cmd: u32,
@@ -552,8 +553,7 @@ impl<'a> TryFrom<&'a TypeAttr<'a>> for Vec<AtomicStatement> {
impl<'a> TryFrom<&'a Attr<'a>> for Vec<AtomicStatement> { impl<'a> TryFrom<&'a Attr<'a>> for Vec<AtomicStatement> {
type Error = anyhow::Error; type Error = anyhow::Error;
fn try_from(perm: &'a Attr<'a>) -> Result<Self> { fn try_from(perm: &'a Attr<'a>) -> Result<Self> {
let mut result = vec![]; let result = vec![AtomicStatement {
result.push(AtomicStatement {
cmd: CMD_ATTR, cmd: CMD_ATTR,
subcmd: 0, subcmd: 0,
sepol1: perm.name.try_into()?, sepol1: perm.name.try_into()?,
@@ -563,7 +563,7 @@ impl<'a> TryFrom<&'a Attr<'a>> for Vec<AtomicStatement> {
sepol5: PolicyObject::None, sepol5: PolicyObject::None,
sepol6: PolicyObject::None, sepol6: PolicyObject::None,
sepol7: PolicyObject::None, sepol7: PolicyObject::None,
}); }];
Ok(result) Ok(result)
} }
} }
@@ -670,9 +670,8 @@ struct FfiPolicy {
fn to_c_ptr(pol: &PolicyObject) -> *const libc::c_char { fn to_c_ptr(pol: &PolicyObject) -> *const libc::c_char {
match pol { match pol {
PolicyObject::None => std::ptr::null(), PolicyObject::None | PolicyObject::All => std::ptr::null(),
PolicyObject::All => std::ptr::null(), PolicyObject::One(s) => s.as_ptr().cast::<libc::c_char>(),
PolicyObject::One(s) => s.as_ptr() as *const libc::c_char,
} }
} }
@@ -692,7 +691,7 @@ impl From<AtomicStatement> for FfiPolicy {
} }
} }
#[cfg(target_os = "android")] #[cfg(unix)]
fn apply_one_rule<'a>(statement: &'a PolicyStatement<'a>) -> Result<()> { fn apply_one_rule<'a>(statement: &'a PolicyStatement<'a>) -> Result<()> {
let policies: Vec<AtomicStatement> = statement.try_into()?; let policies: Vec<AtomicStatement> = statement.try_into()?;
@@ -700,12 +699,13 @@ fn apply_one_rule<'a>(statement: &'a PolicyStatement<'a>) -> Result<()> {
let mut result: u32 = 0; let mut result: u32 = 0;
let cpolicy = FfiPolicy::from(policy); let cpolicy = FfiPolicy::from(policy);
unsafe { unsafe {
#[allow(clippy::cast_possible_wrap)]
libc::prctl( libc::prctl(
crate::ksu::KERNEL_SU_OPTION as i32, crate::ksu::KERNEL_SU_OPTION as i32, // supposed to overflow
crate::ksu::CMD_SET_SEPOLICY, crate::ksu::CMD_SET_SEPOLICY,
0, 0,
&cpolicy as *const _ as *const libc::c_void, std::ptr::addr_of!(cpolicy).cast::<libc::c_void>(),
&mut result as *mut _ as *mut libc::c_void, std::ptr::addr_of_mut!(result).cast::<libc::c_void>(),
); );
} }
@@ -717,13 +717,13 @@ fn apply_one_rule<'a>(statement: &'a PolicyStatement<'a>) -> Result<()> {
Ok(()) Ok(())
} }
#[cfg(not(target_os = "android"))] #[cfg(not(unix))]
fn apply_one_rule<'a>(_statement: &'a PolicyStatement<'a>) -> Result<()> { fn apply_one_rule<'a>(_statement: &'a PolicyStatement<'a>) -> Result<()> {
unimplemented!() unimplemented!()
} }
pub fn live_patch(policy: &str) -> Result<()> { pub fn live_patch(policy: &str) -> Result<()> {
let result = parse_sepolicy(policy.trim())?; let result = parse_sepolicy(policy.trim());
for statement in result { for statement in result {
println!("{statement:?}"); println!("{statement:?}");
apply_one_rule(&statement)?; apply_one_rule(&statement)?;

View File

@@ -1,11 +1,15 @@
use anyhow::{bail, Context, Error, Ok, Result}; use anyhow::{bail, Context, Error, Ok, Result};
use std::{ use std::{
fs::{create_dir_all, set_permissions, write, File, Permissions}, fs::{create_dir_all, write, File},
io::ErrorKind::AlreadyExists, io::ErrorKind::AlreadyExists,
os::unix::prelude::PermissionsExt,
path::Path, path::Path,
}; };
#[allow(unused_imports)]
use std::fs::{set_permissions, Permissions};
#[cfg(unix)]
use std::os::unix::prelude::PermissionsExt;
pub fn ensure_clean_dir(dir: &str) -> Result<()> { pub fn ensure_clean_dir(dir: &str) -> Result<()> {
let path = Path::new(dir); let path = Path::new(dir);
log::debug!("ensure_clean_dir: {}", path.display()); log::debug!("ensure_clean_dir: {}", path.display());
@@ -23,9 +27,8 @@ pub fn ensure_file_exists<T: AsRef<Path>>(file: T) -> Result<()> {
if err.kind() == AlreadyExists && file.as_ref().is_file() { if err.kind() == AlreadyExists && file.as_ref().is_file() {
Ok(()) Ok(())
} else { } else {
Err(Error::from(err)).with_context(|| { Err(Error::from(err))
format!("{} is not a regular file", file.as_ref().to_str().unwrap()) .with_context(|| format!("{} is not a regular file", file.as_ref().display()))
})
} }
} }
} }
@@ -36,10 +39,7 @@ pub fn ensure_dir_exists<T: AsRef<Path>>(dir: T) -> Result<()> {
if dir.as_ref().is_dir() { if dir.as_ref().is_dir() {
result result
} else if result.is_ok() { } else if result.is_ok() {
bail!( bail!("{} is not a regular directory", dir.as_ref().display())
"{} is not a regular directory",
dir.as_ref().to_str().unwrap()
)
} else { } else {
result result
} }
@@ -58,16 +58,17 @@ pub fn ensure_binary<T: AsRef<Path>>(path: T, contents: &[u8]) -> Result<()> {
})?)?; })?)?;
write(&path, contents)?; write(&path, contents)?;
#[cfg(unix)]
set_permissions(&path, Permissions::from_mode(0o755))?; set_permissions(&path, Permissions::from_mode(0o755))?;
Ok(()) Ok(())
} }
#[cfg(target_os = "android")] #[cfg(unix)]
pub fn getprop(prop: &str) -> Option<String> { pub fn getprop(prop: &str) -> Option<String> {
android_properties::getprop(prop).value() android_properties::getprop(prop).value()
} }
#[cfg(not(target_os = "android"))] #[cfg(not(unix))]
pub fn getprop(_prop: &str) -> Option<String> { pub fn getprop(_prop: &str) -> Option<String> {
unimplemented!() unimplemented!()
} }