ksud: refactor module mount (#384)
This commit is contained in:
@@ -37,7 +37,7 @@ fun getBugreportFile(context: Context): File {
|
|||||||
shell.newJob().add("tar -czf ${pstoreFile.absolutePath} /sys/fs/pstore").exec()
|
shell.newJob().add("tar -czf ${pstoreFile.absolutePath} /sys/fs/pstore").exec()
|
||||||
shell.newJob().add("tar -czf ${diagFile.absolutePath} /data/vendor/diag").exec()
|
shell.newJob().add("tar -czf ${diagFile.absolutePath} /data/vendor/diag").exec()
|
||||||
|
|
||||||
shell.newJob().add("cat /proc/mounts > ${mountsFile.absolutePath}").exec()
|
shell.newJob().add("cat /proc/1/mountinfo > ${mountsFile.absolutePath}").exec()
|
||||||
shell.newJob().add("cat /proc/filesystems > ${fileSystemsFile.absolutePath}").exec()
|
shell.newJob().add("cat /proc/filesystems > ${fileSystemsFile.absolutePath}").exec()
|
||||||
shell.newJob().add("ls -alRZ /data/adb > ${ksuFileTree.absolutePath}").exec()
|
shell.newJob().add("ls -alRZ /data/adb > ${ksuFileTree.absolutePath}").exec()
|
||||||
shell.newJob().add("cat /data/system/packages.list > ${appListFile.absolutePath}").exec()
|
shell.newJob().add("cat /data/system/packages.list > ${appListFile.absolutePath}").exec()
|
||||||
@@ -84,4 +84,4 @@ fun getBugreportFile(context: Context): File {
|
|||||||
shell.newJob().add("chmod 0644 ${targetFile.absolutePath}").exec()
|
shell.newJob().add("chmod 0644 ${targetFile.absolutePath}").exec()
|
||||||
|
|
||||||
return targetFile
|
return targetFile
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -72,6 +72,8 @@ enum Debug {
|
|||||||
/// Get kernel version
|
/// Get kernel version
|
||||||
Version,
|
Version,
|
||||||
|
|
||||||
|
Mount,
|
||||||
|
|
||||||
/// For testing
|
/// For testing
|
||||||
Test,
|
Test,
|
||||||
}
|
}
|
||||||
@@ -187,6 +189,7 @@ pub fn run() -> Result<()> {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
Debug::Su => crate::ksu::grant_root(),
|
Debug::Su => crate::ksu::grant_root(),
|
||||||
|
Debug::Mount => event::mount_systemlessly(defs::MODULE_DIR),
|
||||||
Debug::Test => todo!(),
|
Debug::Test => todo!(),
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ pub const WORKING_DIR: &str = concatcp!(ADB_DIR, "ksu/");
|
|||||||
pub const BINARY_DIR: &str = concatcp!(WORKING_DIR, "bin/");
|
pub const BINARY_DIR: &str = concatcp!(WORKING_DIR, "bin/");
|
||||||
|
|
||||||
pub const KSURC_PATH: &str = concatcp!(WORKING_DIR, ".ksurc");
|
pub const KSURC_PATH: &str = concatcp!(WORKING_DIR, ".ksurc");
|
||||||
pub const STOCK_MNT_ROOT: &str = concatcp!(WORKING_DIR, ".mnt");
|
pub const KSU_OVERLAY_SOURCE: &str = "KSU";
|
||||||
pub const DAEMON_PATH: &str = concatcp!(ADB_DIR, "ksud");
|
pub const DAEMON_PATH: &str = concatcp!(ADB_DIR, "ksud");
|
||||||
|
|
||||||
#[cfg(target_os = "android")]
|
#[cfg(target_os = "android")]
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
use anyhow::{bail, Context, Result};
|
use anyhow::{bail, Context, Result};
|
||||||
use log::{info, warn};
|
use log::{info, warn};
|
||||||
|
use std::path::PathBuf;
|
||||||
use std::{collections::HashMap, path::Path};
|
use std::{collections::HashMap, path::Path};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
@@ -7,44 +8,21 @@ use crate::{
|
|||||||
utils::{self, ensure_clean_dir, ensure_dir_exists},
|
utils::{self, ensure_clean_dir, ensure_dir_exists},
|
||||||
};
|
};
|
||||||
|
|
||||||
fn mount_partition(partition: &str, lowerdir: &mut Vec<String>) -> Result<()> {
|
fn mount_partition(partition: &str, lowerdir: &Vec<String>) -> Result<()> {
|
||||||
if lowerdir.is_empty() {
|
if lowerdir.is_empty() {
|
||||||
warn!("partition: {partition} lowerdir is empty");
|
warn!("partition: {partition} lowerdir is empty");
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let partition = format!("/{partition}");
|
||||||
|
|
||||||
// if /partition is a symlink and linked to /system/partition, then we don't need to overlay it separately
|
// if /partition is a symlink and linked to /system/partition, then we don't need to overlay it separately
|
||||||
if Path::new(&format!("/{partition}")).read_link().is_ok() {
|
if Path::new(&partition).read_link().is_ok() {
|
||||||
warn!("partition: {partition} is a symlink");
|
warn!("partition: {partition} is a symlink");
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
|
||||||
// handle stock mounts under /partition, we should restore the mount point after overlay
|
mount::mount_overlay(&partition, lowerdir)
|
||||||
// because the overlayfs mount will "overlay" the bind mount such as /vendor/bt_firmware, /vendor/dsp
|
|
||||||
// which will cause the system bootloop or bluetooth/dsp not working
|
|
||||||
let stock_mount = mount::StockMount::new(&format!("/{partition}/"))
|
|
||||||
.with_context(|| format!("get stock mount of partition: {partition} failed"))?;
|
|
||||||
|
|
||||||
// add /partition as the lowerest dir
|
|
||||||
let lowest_dir = format!("/{partition}");
|
|
||||||
lowerdir.push(lowest_dir.clone());
|
|
||||||
|
|
||||||
let lowerdir = lowerdir.join(":");
|
|
||||||
info!("partition: {partition} lowerdir: {lowerdir}");
|
|
||||||
|
|
||||||
let result = mount::mount_overlay(&lowerdir, &lowest_dir);
|
|
||||||
|
|
||||||
if let Err(e) = stock_mount.remount() {
|
|
||||||
if result.is_ok() {
|
|
||||||
// if mount overlay ok but stock remount failed, we should umount overlay
|
|
||||||
warn!("remount stock failed: {:?}, umount overlay {lowest_dir}", e);
|
|
||||||
if mount::umount_dir(&lowest_dir).is_err() {
|
|
||||||
warn!("umount overlay {lowest_dir} failed");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
result
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn mount_systemlessly(module_dir: &str) -> Result<()> {
|
pub fn mount_systemlessly(module_dir: &str) -> Result<()> {
|
||||||
@@ -74,7 +52,7 @@ pub fn mount_systemlessly(module_dir: &str) -> Result<()> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let module_system = Path::new(&module).join("system");
|
let module_system = Path::new(&module).join("system");
|
||||||
if module_system.exists() {
|
if module_system.is_dir() {
|
||||||
system_lowerdir.push(format!("{}", module_system.display()));
|
system_lowerdir.push(format!("{}", module_system.display()));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -82,23 +60,22 @@ pub fn mount_systemlessly(module_dir: &str) -> Result<()> {
|
|||||||
// if /partition is a mountpoint, we would move it to $MODPATH/$partition when install
|
// if /partition is a mountpoint, we would move it to $MODPATH/$partition when install
|
||||||
// otherwise it must be a symlink and we don't need to overlay!
|
// otherwise it must be a symlink and we don't need to overlay!
|
||||||
let part_path = Path::new(&module).join(part);
|
let part_path = Path::new(&module).join(part);
|
||||||
if !part_path.exists() {
|
if part_path.is_dir() {
|
||||||
continue;
|
if let Some(v) = partition_lowerdir.get_mut(*part) {
|
||||||
}
|
v.push(format!("{}", part_path.display()));
|
||||||
if let Some(v) = partition_lowerdir.get_mut(*part) {
|
}
|
||||||
v.push(format!("{}", part_path.display()));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// mount /system first
|
// mount /system first
|
||||||
if let Err(e) = mount_partition("system", &mut system_lowerdir) {
|
if let Err(e) = mount_partition("system", &system_lowerdir) {
|
||||||
warn!("mount system failed: {:#}", e);
|
warn!("mount system failed: {:#}", e);
|
||||||
}
|
}
|
||||||
|
|
||||||
// mount other partitions
|
// mount other partitions
|
||||||
for (k, mut v) in partition_lowerdir {
|
for (k, v) in partition_lowerdir {
|
||||||
if let Err(e) = mount_partition(&k, &mut v) {
|
if let Err(e) = mount_partition(&k, &v) {
|
||||||
warn!("mount {k} failed: {:#}", e);
|
warn!("mount {k} failed: {:#}", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -184,17 +161,11 @@ pub fn on_post_data_fs() -> Result<()> {
|
|||||||
warn!("load system.prop failed: {}", e);
|
warn!("load system.prop failed: {}", e);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Finally, we should do systemless mount
|
|
||||||
// But we should backup all stock overlayfs and remount them after module mounted
|
|
||||||
let stock_overlay = mount::StockOverlay::new();
|
|
||||||
|
|
||||||
// mount moduke systemlessly by overlay
|
// mount moduke systemlessly by overlay
|
||||||
if let Err(e) = mount_systemlessly(module_dir) {
|
if let Err(e) = mount_systemlessly(module_dir) {
|
||||||
warn!("do systemless mount failed: {}", e);
|
warn!("do systemless mount failed: {}", e);
|
||||||
}
|
}
|
||||||
|
|
||||||
stock_overlay.mount_all();
|
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -257,7 +228,6 @@ pub fn install() -> Result<()> {
|
|||||||
|
|
||||||
#[cfg(target_os = "android")]
|
#[cfg(target_os = "android")]
|
||||||
fn link_ksud_to_bin() -> Result<()> {
|
fn link_ksud_to_bin() -> Result<()> {
|
||||||
use std::path::PathBuf;
|
|
||||||
let ksu_bin = PathBuf::from(defs::DAEMON_PATH);
|
let ksu_bin = PathBuf::from(defs::DAEMON_PATH);
|
||||||
let ksu_bin_link = PathBuf::from(defs::DAEMON_LINK_PATH);
|
let ksu_bin_link = PathBuf::from(defs::DAEMON_LINK_PATH);
|
||||||
if ksu_bin.exists() && !ksu_bin_link.exists() {
|
if ksu_bin.exists() && !ksu_bin_link.exists() {
|
||||||
|
|||||||
@@ -7,12 +7,17 @@ use retry::delay::NoDelay;
|
|||||||
#[cfg(any(target_os = "linux", target_os = "android"))]
|
#[cfg(any(target_os = "linux", target_os = "android"))]
|
||||||
use sys_mount::{unmount, FilesystemType, Mount, MountFlags, Unmount, UnmountFlags};
|
use sys_mount::{unmount, FilesystemType, Mount, MountFlags, Unmount, UnmountFlags};
|
||||||
|
|
||||||
|
use crate::defs::KSU_OVERLAY_SOURCE;
|
||||||
|
use log::{info, warn};
|
||||||
#[cfg(any(target_os = "linux", target_os = "android"))]
|
#[cfg(any(target_os = "linux", target_os = "android"))]
|
||||||
use procfs::process::{MountInfo, Process};
|
use procfs::process::Process;
|
||||||
#[cfg(any(target_os = "linux", target_os = "android"))]
|
#[cfg(any(target_os = "linux", target_os = "android"))]
|
||||||
use std::collections::HashSet;
|
use std::fs::File;
|
||||||
|
#[cfg(any(target_os = "linux", target_os = "android"))]
|
||||||
use crate::utils;
|
use std::os::fd::AsRawFd;
|
||||||
|
#[cfg(any(target_os = "linux", target_os = "android"))]
|
||||||
|
use std::os::unix::fs::OpenOptionsExt;
|
||||||
|
use std::path::Path;
|
||||||
|
|
||||||
pub struct AutoMountExt4 {
|
pub struct AutoMountExt4 {
|
||||||
mnt: String,
|
mnt: String,
|
||||||
@@ -130,14 +135,136 @@ pub fn umount_dir(src: &str) -> Result<()> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(any(target_os = "linux", target_os = "android"))]
|
#[cfg(any(target_os = "linux", target_os = "android"))]
|
||||||
pub fn mount_overlay(lowerdir: &str, mnt: &str) -> Result<()> {
|
fn mount_overlayfs(
|
||||||
|
lower_dirs: &[String],
|
||||||
|
lowest: impl AsRef<Path>,
|
||||||
|
dest: impl AsRef<Path>,
|
||||||
|
) -> Result<()> {
|
||||||
|
let options = format!(
|
||||||
|
"lowerdir={}:{}",
|
||||||
|
lower_dirs.join(":"),
|
||||||
|
lowest.as_ref().display()
|
||||||
|
);
|
||||||
|
info!(
|
||||||
|
"mount overlayfs on {}, options={}",
|
||||||
|
dest.as_ref().display(),
|
||||||
|
options
|
||||||
|
);
|
||||||
Mount::builder()
|
Mount::builder()
|
||||||
.fstype(FilesystemType::from("overlay"))
|
.fstype(FilesystemType::from("overlay"))
|
||||||
.flags(MountFlags::RDONLY)
|
.data(&options)
|
||||||
.data(&format!("lowerdir={lowerdir}"))
|
.mount(KSU_OVERLAY_SOURCE, dest.as_ref())
|
||||||
.mount("overlay", mnt)
|
.with_context(|| {
|
||||||
.map(|_| ())
|
format!(
|
||||||
.map_err(|e| anyhow::anyhow!("mount partition: {mnt} overlay failed: {e}"))
|
"mount overlayfs on {} options {} failed",
|
||||||
|
dest.as_ref().display(),
|
||||||
|
options
|
||||||
|
)
|
||||||
|
})?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(any(target_os = "linux", target_os = "android"))]
|
||||||
|
fn bind_mount(from: impl AsRef<Path>, to: impl AsRef<Path>) -> Result<()> {
|
||||||
|
info!(
|
||||||
|
"bind mount {} -> {}",
|
||||||
|
from.as_ref().display(),
|
||||||
|
to.as_ref().display()
|
||||||
|
);
|
||||||
|
Mount::builder()
|
||||||
|
.flags(MountFlags::BIND)
|
||||||
|
.mount(from.as_ref(), to.as_ref())
|
||||||
|
.with_context(|| {
|
||||||
|
format!(
|
||||||
|
"bind mount failed: {} -> {}",
|
||||||
|
from.as_ref().display(),
|
||||||
|
to.as_ref().display()
|
||||||
|
)
|
||||||
|
})?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(any(target_os = "linux", target_os = "android"))]
|
||||||
|
fn mount_overlay_child(
|
||||||
|
mount_point: &str,
|
||||||
|
relative: &String,
|
||||||
|
module_roots: &Vec<String>,
|
||||||
|
stock_root: &String,
|
||||||
|
) -> Result<()> {
|
||||||
|
if !module_roots
|
||||||
|
.iter()
|
||||||
|
.any(|lower| Path::new(&format!("{}{}", lower, relative)).exists())
|
||||||
|
{
|
||||||
|
return bind_mount(stock_root, mount_point);
|
||||||
|
}
|
||||||
|
if !Path::new(&stock_root).is_dir() {
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
let mut lower_dirs: Vec<String> = vec![];
|
||||||
|
for lower in module_roots {
|
||||||
|
let lower_dir = format!("{}{}", lower, relative);
|
||||||
|
let path = Path::new(&lower_dir);
|
||||||
|
if path.is_dir() {
|
||||||
|
lower_dirs.push(lower_dir);
|
||||||
|
} else if path.exists() {
|
||||||
|
// stock root has been blocked by this file
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if lower_dirs.is_empty() {
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
// merge modules and stock
|
||||||
|
if let Err(e) = mount_overlayfs(&lower_dirs, stock_root, mount_point) {
|
||||||
|
warn!("failed: {:#}, fallback to bind mount", e);
|
||||||
|
bind_mount(stock_root, mount_point)?;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(any(target_os = "linux", target_os = "android"))]
|
||||||
|
pub fn mount_overlay(root: &String, module_roots: &Vec<String>) -> Result<()> {
|
||||||
|
info!("mount overlay for {}", root);
|
||||||
|
let stock_root = File::options()
|
||||||
|
.read(true)
|
||||||
|
.custom_flags(libc::O_PATH)
|
||||||
|
.open(root)?;
|
||||||
|
let stock_root = format!("/proc/self/fd/{}", stock_root.as_raw_fd());
|
||||||
|
|
||||||
|
// collect child mounts before mounting the root
|
||||||
|
let mounts = Process::myself()?
|
||||||
|
.mountinfo()
|
||||||
|
.with_context(|| "get mountinfo")?;
|
||||||
|
let mut mount_seq = mounts
|
||||||
|
.iter()
|
||||||
|
.filter(|m| {
|
||||||
|
m.mount_point.starts_with(root) && !Path::new(&root).starts_with(&m.mount_point)
|
||||||
|
})
|
||||||
|
.map(|m| m.mount_point.to_str())
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
mount_seq.sort();
|
||||||
|
mount_seq.dedup();
|
||||||
|
|
||||||
|
mount_overlayfs(module_roots, root, root).with_context(|| "mount overlayfs for root failed")?;
|
||||||
|
for mount_point in mount_seq.iter() {
|
||||||
|
let Some(mount_point) = mount_point else {
|
||||||
|
continue;
|
||||||
|
};
|
||||||
|
let relative = mount_point.replacen(root, "", 1);
|
||||||
|
let stock_root: String = format!("{}{}", stock_root, relative);
|
||||||
|
if !Path::new(&stock_root).exists() {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if let Err(e) = mount_overlay_child(mount_point, &relative, module_roots, &stock_root) {
|
||||||
|
warn!(
|
||||||
|
"failed to mount overlay for child {}: {:#}, revert",
|
||||||
|
mount_point, e
|
||||||
|
);
|
||||||
|
umount_dir(root).with_context(|| format!("failed to revert {}", root))?;
|
||||||
|
bail!(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(not(any(target_os = "linux", target_os = "android")))]
|
#[cfg(not(any(target_os = "linux", target_os = "android")))]
|
||||||
@@ -151,213 +278,6 @@ pub fn umount_dir(_src: &str) -> Result<()> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(not(any(target_os = "linux", target_os = "android")))]
|
#[cfg(not(any(target_os = "linux", target_os = "android")))]
|
||||||
pub fn mount_overlay(_lowerdir: &str, _mnt: &str) -> Result<()> {
|
pub fn mount_overlay(_dest: &String, _lower_dirs: &Vec<String>) -> Result<()> {
|
||||||
unimplemented!()
|
unimplemented!()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct StockOverlay {
|
|
||||||
#[cfg(any(target_os = "linux", target_os = "android"))]
|
|
||||||
mountinfos: Vec<MountInfo>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(not(any(target_os = "linux", target_os = "android")))]
|
|
||||||
impl StockOverlay {
|
|
||||||
pub fn new() -> Self {
|
|
||||||
unimplemented!()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn mount_all(&self) {
|
|
||||||
unimplemented!()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(any(target_os = "linux", target_os = "android"))]
|
|
||||||
impl StockOverlay {
|
|
||||||
pub fn new() -> Self {
|
|
||||||
if let std::result::Result::Ok(process) = Process::myself() {
|
|
||||||
if let std::result::Result::Ok(mountinfos) = process.mountinfo() {
|
|
||||||
let overlay_mounts = mountinfos
|
|
||||||
.into_iter()
|
|
||||||
.filter(|m| m.fs_type == "overlay")
|
|
||||||
.collect::<Vec<_>>();
|
|
||||||
return Self {
|
|
||||||
mountinfos: overlay_mounts,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Self { mountinfos: vec![] }
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn mount_all(&self) {
|
|
||||||
log::info!("stock overlay: mount all: {:?}", self.mountinfos);
|
|
||||||
for mount in self.mountinfos.clone() {
|
|
||||||
let Some(mnt) = mount.mount_point.to_str() else {
|
|
||||||
log::warn!("Failed to get mount point");
|
|
||||||
continue;
|
|
||||||
};
|
|
||||||
|
|
||||||
if mnt == "/system" {
|
|
||||||
log::warn!("stock overlay found /system, skip!");
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
let (_flags, b): (HashSet<_>, HashSet<_>) = mount
|
|
||||||
.mount_options
|
|
||||||
.into_iter()
|
|
||||||
.chain(mount.super_options)
|
|
||||||
.partition(|(_, m)| m.is_none());
|
|
||||||
|
|
||||||
let mut overlay_opts = vec![];
|
|
||||||
for (opt, val) in b {
|
|
||||||
if let Some(val) = val {
|
|
||||||
overlay_opts.push(format!("{opt}={val}"));
|
|
||||||
} else {
|
|
||||||
log::warn!("opt empty: {}", opt);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
let overlay_data = overlay_opts.join(",");
|
|
||||||
let result = Mount::builder()
|
|
||||||
.fstype(FilesystemType::from("overlay"))
|
|
||||||
.flags(MountFlags::RDONLY)
|
|
||||||
.data(&overlay_data)
|
|
||||||
.mount("overlay", mnt);
|
|
||||||
if let Err(e) = result {
|
|
||||||
log::error!(
|
|
||||||
"stock mount overlay: {} failed: {}",
|
|
||||||
mount.mount_point.display(),
|
|
||||||
e
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
log::info!(
|
|
||||||
"stock mount :{} overlay_opts: {}",
|
|
||||||
mount.mount_point.display(),
|
|
||||||
overlay_opts.join(",")
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// some ROMs mount device(ext4,exfat) to /vendor, when we do overlay mount, it will overlay
|
|
||||||
// the stock mounts, these mounts include bt_firmware, wifi_firmware, etc.
|
|
||||||
// so we to remount these mounts when we do overlay mount.
|
|
||||||
// this is a workaround, we should find a better way to do this.
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub struct StockMount {
|
|
||||||
mnt: String,
|
|
||||||
#[cfg(any(target_os = "linux", target_os = "android"))]
|
|
||||||
mountlist: Vec<(proc_mounts::MountInfo, std::path::PathBuf)>,
|
|
||||||
#[cfg(any(target_os = "linux", target_os = "android"))]
|
|
||||||
rootmount: sys_mount::Mount,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(any(target_os = "linux", target_os = "android"))]
|
|
||||||
impl StockMount {
|
|
||||||
pub fn new(mnt: &str) -> Result<Self> {
|
|
||||||
let mountlist = proc_mounts::MountList::new()?;
|
|
||||||
let mut mounts = mountlist
|
|
||||||
.destination_starts_with(std::path::Path::new(mnt))
|
|
||||||
.filter(|m| m.fstype != "overlay" && m.fstype != "rootfs")
|
|
||||||
.collect::<Vec<_>>();
|
|
||||||
mounts.sort_by(|a, b| b.dest.cmp(&a.dest)); // inverse order
|
|
||||||
|
|
||||||
let mntroot = std::path::Path::new(crate::defs::STOCK_MNT_ROOT);
|
|
||||||
utils::ensure_dir_exists(mntroot)?;
|
|
||||||
log::info!("stock mount root: {}", mntroot.display());
|
|
||||||
|
|
||||||
let rootdir = mntroot.join(
|
|
||||||
mnt.strip_prefix('/')
|
|
||||||
.ok_or(anyhow::anyhow!("invalid mnt: {}!", mnt))?,
|
|
||||||
);
|
|
||||||
utils::ensure_dir_exists(&rootdir)?;
|
|
||||||
let rootmount = Mount::builder().fstype("tmpfs").mount("tmpfs", &rootdir)?;
|
|
||||||
|
|
||||||
let mut ms = vec![];
|
|
||||||
for m in mounts {
|
|
||||||
let dest = &m.dest;
|
|
||||||
if dest == std::path::Path::new(mnt) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
let path = rootdir.join(dest.strip_prefix("/")?);
|
|
||||||
log::info!("rootdir: {}, path: {}", rootdir.display(), path.display());
|
|
||||||
if dest.is_dir() {
|
|
||||||
utils::ensure_dir_exists(&path)
|
|
||||||
.with_context(|| format!("Failed to create dir: {}", path.display(),))?;
|
|
||||||
} else if dest.is_file() {
|
|
||||||
if !path.exists() {
|
|
||||||
let parent = path
|
|
||||||
.parent()
|
|
||||||
.with_context(|| format!("Failed to get parent: {}", path.display()))?;
|
|
||||||
utils::ensure_dir_exists(parent).with_context(|| {
|
|
||||||
format!("Failed to create parent: {}", parent.display())
|
|
||||||
})?;
|
|
||||||
std::fs::File::create(&path)
|
|
||||||
.with_context(|| format!("Failed to create file: {}", path.display(),))?;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
bail!("unknown file type: {:?}", dest)
|
|
||||||
}
|
|
||||||
log::info!("bind stock mount: {} -> {}", dest.display(), path.display());
|
|
||||||
Mount::builder()
|
|
||||||
.flags(MountFlags::BIND)
|
|
||||||
.mount(dest, &path)
|
|
||||||
.with_context(|| {
|
|
||||||
format!("Failed to mount: {} -> {}", dest.display(), path.display())
|
|
||||||
})?;
|
|
||||||
|
|
||||||
ms.push((m.clone(), path));
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(Self {
|
|
||||||
mnt: mnt.to_string(),
|
|
||||||
mountlist: ms,
|
|
||||||
rootmount,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// Yes, we move self here!
|
|
||||||
pub fn remount(self) -> Result<()> {
|
|
||||||
log::info!("remount stock for {} : {:?}", self.mnt, self.mountlist);
|
|
||||||
let mut result = Ok(());
|
|
||||||
for (m, src) in self.mountlist {
|
|
||||||
let dst = m.dest;
|
|
||||||
|
|
||||||
log::info!("begin remount: {} -> {}", src.display(), dst.display());
|
|
||||||
let mount_result = Mount::builder()
|
|
||||||
.flags(MountFlags::BIND | MountFlags::MOVE)
|
|
||||||
.mount(&src, &dst);
|
|
||||||
if let Err(e) = mount_result {
|
|
||||||
log::error!("remount failed: {}", e);
|
|
||||||
result = Err(e.into());
|
|
||||||
} else {
|
|
||||||
log::info!(
|
|
||||||
"remount {}({}) -> {} succeed!",
|
|
||||||
m.source.display(),
|
|
||||||
src.display(),
|
|
||||||
dst.display()
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// umount the root tmpfs mount
|
|
||||||
if let Err(e) = self.rootmount.unmount(UnmountFlags::DETACH) {
|
|
||||||
log::warn!("umount root mount failed: {}", e);
|
|
||||||
}
|
|
||||||
|
|
||||||
result
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(not(any(target_os = "linux", target_os = "android")))]
|
|
||||||
impl StockMount {
|
|
||||||
pub fn new(mnt: &str) -> Result<Self> {
|
|
||||||
Ok(Self {
|
|
||||||
mnt: mnt.to_string(),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn remount(&self) -> Result<()> {
|
|
||||||
unimplemented!()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
Reference in New Issue
Block a user