Replace all parse_mount_info usage with Rust

This commit is contained in:
topjohnwu
2024-02-27 03:14:26 -08:00
parent af6965eefa
commit 1a70796339
8 changed files with 278 additions and 61 deletions
+1
View File
@@ -14,3 +14,4 @@ cxx-gen = { workspace = true }
base = { path = "../base" }
magiskpolicy = { path = "../sepolicy" }
cxx = { workspace = true }
procfs = { workspace = true }
+4
View File
@@ -1,11 +1,13 @@
#![feature(format_args_nl)]
use logging::setup_klog;
use mount::{is_device_mounted, switch_root};
use rootdir::inject_magisk_rc;
// Has to be pub so all symbols in that crate is included
pub use magiskpolicy;
mod logging;
mod mount;
mod rootdir;
#[cxx::bridge]
@@ -14,6 +16,8 @@ pub mod ffi {
extern "Rust" {
fn setup_klog();
fn inject_magisk_rc(fd: i32, tmp_dir: Utf8CStrRef);
fn switch_root(path: Utf8CStrRef);
fn is_device_mounted(dev: u64, mnt_point: &mut Vec<u8>) -> bool;
}
unsafe extern "C++" {
+6 -31
View File
@@ -100,29 +100,6 @@ static dev_t setup_block() {
return 0;
}
static void switch_root(const string &path) {
LOGD("Switch root to %s\n", path.data());
int root = xopen("/", O_RDONLY);
for (set<string, greater<>> mounts; auto &info : parse_mount_info("self")) {
if (info.target == "/" || info.target == path)
continue;
if (auto last_mount = mounts.upper_bound(info.target);
last_mount != mounts.end() && info.target.starts_with(*last_mount + '/')) {
continue;
}
mounts.emplace(info.target);
auto new_path = path + info.target;
xmkdir(new_path.data(), 0755);
xmount(info.target.data(), new_path.data(), nullptr, MS_MOVE, nullptr);
}
chdir(path.data());
xmount(path.data(), "/", nullptr, MS_MOVE, nullptr);
chroot(".");
LOGD("Cleaning rootfs\n");
frm_rf(root);
}
#define PREINITMNT MIRRDIR "/preinit"
static void mount_preinit_dir(string preinit_dev) {
@@ -137,13 +114,11 @@ static void mount_preinit_dir(string preinit_dev) {
xmkdir(PREINITMNT, 0);
bool mounted = false;
// First, find if it is already mounted
for (auto &info : parse_mount_info("self")) {
if (info.root == "/" && info.device == dev) {
// Already mounted, just bind mount
xmount(info.target.data(), PREINITMNT, nullptr, MS_BIND, nullptr);
mounted = true;
break;
}
rust::Vec<uint8_t> mnt_point;
if (rust::is_device_mounted(dev, mnt_point)) {
// Already mounted, just bind mount
xmount((const char *) mnt_point.data(), PREINITMNT, nullptr, MS_BIND, nullptr);
mounted = true;
}
// Since we are mounting the block device directly, make sure to ONLY mount the partitions
@@ -212,7 +187,7 @@ mount_root:
}
}
switch_root("/system_root");
rust::switch_root("/system_root");
// Make dev writable
xmount("tmpfs", "/dev", "tmpfs", 0, "mode=755");
+93
View File
@@ -0,0 +1,93 @@
use std::collections::BTreeSet;
use std::ops::Bound::{Excluded, Unbounded};
use std::path::{Path, PathBuf};
use std::{fs, ptr};
use procfs::process::Process;
use base::{
cstr, debug, libc, raw_cstr, Directory, LibcReturn, LoggedError, LoggedResult, StringExt,
Utf8CStr,
};
pub fn switch_root(path: &Utf8CStr) {
fn inner(path: &Utf8CStr) -> LoggedResult<()> {
debug!("Switching root to {}", path);
let mut rootfs = Directory::open(cstr!("/"))?;
let procfs = Process::myself()?;
let mut mounts: BTreeSet<PathBuf> = BTreeSet::new();
for info in procfs.mountinfo()?.0.into_iter() {
let mut target = info.mount_point;
if target == Path::new("/") || target == Path::new(path) {
continue;
}
let iter = mounts.range::<Path, _>((Unbounded, Excluded(target.as_path())));
if let Some(last_mount) = iter.last() {
if Path::new(path).starts_with(last_mount) {
continue;
}
}
let mut new_path = PathBuf::from(path);
new_path.push(target.strip_prefix("/").unwrap());
fs::create_dir(&new_path).ok(); /* Error is OK */
unsafe {
libc::mount(
target.nul_terminate().as_ptr().cast(),
new_path.nul_terminate().as_ptr().cast(),
ptr::null(),
libc::MS_MOVE,
ptr::null(),
)
.as_os_err()?;
}
// Record all moved paths
mounts.insert(target);
}
unsafe {
libc::chdir(path.as_ptr()).as_os_err()?;
libc::mount(
path.as_ptr(),
raw_cstr!("/"),
ptr::null(),
libc::MS_MOVE,
ptr::null(),
)
.as_os_err()?;
libc::chroot(raw_cstr!(".")).as_os_err()?;
}
debug!("Cleaning rootfs");
rootfs.remove_all()?;
Ok(())
}
inner(path).ok();
}
pub fn is_device_mounted(dev: u64, mnt_point: &mut Vec<u8>) -> bool {
fn inner(dev: u64, mount_point: &mut Vec<u8>) -> LoggedResult<()> {
let procfs = Process::myself()?;
for mut info in procfs.mountinfo()?.0 {
if info.root != "/" {
continue;
}
let mut iter = info.majmin.split(':').map(|s| s.parse::<u32>());
let maj = match iter.next() {
Some(Ok(s)) => s,
_ => continue,
};
let min = match iter.next() {
Some(Ok(s)) => s,
_ => continue,
};
if dev == libc::makedev(maj, min).into() {
*mount_point = info.mount_point.nul_terminate().to_vec();
return Ok(());
}
}
Err(LoggedError::default())
}
inner(dev, mnt_point).is_ok()
}