Files
SukiSU-Ultra/userspace/ksud/src/utils.rs
2023-03-01 11:41:53 +08:00

132 lines
3.7 KiB
Rust

use anyhow::{bail, Context, Error, Ok, Result};
use std::{
fs::{create_dir_all, write, File},
io::ErrorKind::AlreadyExists,
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<()> {
let path = Path::new(dir);
log::debug!("ensure_clean_dir: {}", path.display());
if path.exists() {
log::debug!("ensure_clean_dir: {} exists, remove it", path.display());
std::fs::remove_dir_all(path)?;
}
Ok(std::fs::create_dir_all(path)?)
}
pub fn ensure_file_exists<T: AsRef<Path>>(file: T) -> Result<()> {
match File::options().write(true).create_new(true).open(&file) {
std::result::Result::Ok(_) => Ok(()),
Err(err) => {
if err.kind() == AlreadyExists && file.as_ref().is_file() {
Ok(())
} else {
Err(Error::from(err))
.with_context(|| format!("{} is not a regular file", file.as_ref().display()))
}
}
}
}
pub fn ensure_dir_exists<T: AsRef<Path>>(dir: T) -> Result<()> {
let result = create_dir_all(&dir).map_err(Error::from);
if dir.as_ref().is_dir() {
result
} else if result.is_ok() {
bail!("{} is not a regular directory", dir.as_ref().display())
} else {
result
}
}
pub fn ensure_binary<T: AsRef<Path>>(path: T, contents: &[u8]) -> Result<()> {
if path.as_ref().exists() {
return Ok(());
}
ensure_dir_exists(path.as_ref().parent().ok_or_else(|| {
anyhow::anyhow!(
"{} does not have parent directory",
path.as_ref().to_string_lossy()
)
})?)?;
write(&path, contents)?;
#[cfg(unix)]
set_permissions(&path, Permissions::from_mode(0o755))?;
Ok(())
}
#[cfg(any(target_os = "linux", target_os = "android"))]
pub fn getprop(prop: &str) -> Option<String> {
android_properties::getprop(prop).value()
}
#[cfg(not(any(target_os = "linux", target_os = "android")))]
pub fn getprop(_prop: &str) -> Option<String> {
unimplemented!()
}
pub fn is_safe_mode() -> bool {
let safemode = getprop("persist.sys.safemode")
.filter(|prop| prop == "1")
.is_some()
|| getprop("ro.sys.safemode")
.filter(|prop| prop == "1")
.is_some();
log::info!("safemode: {}", safemode);
if safemode {
return true;
}
let safemode = crate::ksu::check_kernel_safemode();
log::info!("kernel_safemode: {}", safemode);
safemode
}
pub fn get_zip_uncompressed_size(zip_path: &str) -> Result<u64> {
let mut zip = zip::ZipArchive::new(std::fs::File::open(zip_path)?)?;
let total: u64 = (0..zip.len())
.map(|i| zip.by_index(i).unwrap().size())
.sum();
Ok(total)
}
#[cfg(any(target_os = "linux", target_os = "android"))]
pub fn switch_mnt_ns(pid: i32) -> Result<()> {
use anyhow::ensure;
use std::os::fd::AsRawFd;
let path = format!("/proc/{}/ns/mnt", pid);
let fd = std::fs::File::open(path)?;
let ret = unsafe { libc::setns(fd.as_raw_fd(), libc::CLONE_NEWNS) };
ensure!(ret == 0, "switch mnt ns failed");
Ok(())
}
#[cfg(any(target_os = "linux", target_os = "android"))]
pub fn unshare_mnt_ns() -> Result<()> {
use anyhow::ensure;
let ret = unsafe { libc::unshare(libc::CLONE_NEWNS) };
ensure!(ret == 0, "unshare mnt ns failed");
Ok(())
}
#[cfg(any(target_os = "linux", target_os = "android"))]
pub fn umask(mask: u32) {
unsafe { libc::umask(mask) };
}
#[cfg(not(any(target_os = "linux", target_os = "android")))]
pub fn umask(_mask: u32) {
unimplemented!("umask is not supported on this platform")
}
pub fn has_magisk() -> bool {
which::which("magisk").is_ok()
}