diff --git a/userspace/ksud/Cargo.lock b/userspace/ksud/Cargo.lock index df1810100ee2..6278826ea834 100644 --- a/userspace/ksud/Cargo.lock +++ b/userspace/ksud/Cargo.lock @@ -256,9 +256,9 @@ dependencies = [ [[package]] name = "clang-sys" -version = "1.4.0" +version = "1.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa2e27ae6ab525c3d369ded447057bca5438d86dc3a68f6faafb8269ba82ebf3" +checksum = "67523a3b4be3ce1989d607a828d036249522dd9c1c8de7f4dd2dae43a37369d1" dependencies = [ "glob", "libc", @@ -853,15 +853,16 @@ dependencies = [ "jwalk", "libc", "log", + "loopdev", "nom", "procfs", "regex", "retry", "rust-embed", + "rustix 0.38.30", "serde", "serde_json", "sha256", - "sys-mount", "tempdir", "which", "zip 0.6.4", @@ -908,12 +909,12 @@ dependencies = [ [[package]] name = "libloading" -version = "0.7.4" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b67380fd3b2fbe7527a606e18729d21c6f3951633d0500574c4dc22d2d638b9f" +checksum = "c571b676ddfc9a8c12f1f3d3085a7b163966a8fd8098a90640953ce5f6170161" dependencies = [ "cfg-if 1.0.0", - "winapi", + "windows-sys 0.48.0", ] [[package]] @@ -1376,8 +1377,10 @@ checksum = "322394588aaf33c24007e8bb3238ee3e4c5c09c084ab32bc73890b99ff326bca" dependencies = [ "bitflags 2.4.1", "errno 0.3.8", + "itoa", "libc", "linux-raw-sys 0.4.13", + "once_cell", "windows-sys 0.52.0", ] @@ -1456,20 +1459,9 @@ dependencies = [ [[package]] name = "shlex" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43b2853a4d09f215c24cc5489c992ce46052d359b5109343cbafbf26bc62f8a3" - -[[package]] -name = "smart-default" -version = "0.6.0" +version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "133659a15339456eeeb07572eb02a91c91e9815e9cbc89566944d2c8d3efdbf6" -dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.107", -] +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" [[package]] name = "strsim" @@ -1505,19 +1497,6 @@ dependencies = [ "unicode-ident", ] -[[package]] -name = "sys-mount" -version = "2.0.2" -source = "git+https://github.com/tiann/sys-mount?branch=loopfix#c7c4048e4a4ffdf8b108a85956363a75f2c554f0" -dependencies = [ - "bitflags 1.3.2", - "libc", - "loopdev", - "smart-default", - "thiserror", - "tracing", -] - [[package]] name = "tempdir" version = "0.3.7" @@ -1596,38 +1575,6 @@ dependencies = [ "pin-project-lite", ] -[[package]] -name = "tracing" -version = "0.1.37" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ce8c33a8d48bd45d624a6e523445fd21ec13d3653cd51f681abf67418f54eb8" -dependencies = [ - "cfg-if 1.0.0", - "pin-project-lite", - "tracing-attributes", - "tracing-core", -] - -[[package]] -name = "tracing-attributes" -version = "0.1.23" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4017f8f45139870ca7e672686113917c71c7a6e02d4924eda67186083c03081a" -dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.107", -] - -[[package]] -name = "tracing-core" -version = "0.1.30" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "24eb03ba0eab1fd845050058ce5e616558e8f8d8fca633e6b163fe25c797213a" -dependencies = [ - "once_cell", -] - [[package]] name = "typenum" version = "1.16.0" diff --git a/userspace/ksud/Cargo.toml b/userspace/ksud/Cargo.toml index 2eaf2d065fe9..cfeba42b5d7c 100644 --- a/userspace/ksud/Cargo.toml +++ b/userspace/ksud/Cargo.toml @@ -39,10 +39,11 @@ chrono = "0.4" hole-punch = { git = "https://github.com/tiann/hole-punch" } [target.'cfg(any(target_os = "android", target_os = "linux"))'.dependencies] -sys-mount = { git = "https://github.com/tiann/sys-mount", branch = "loopfix" } +rustix = { version = "0.38", features = ["all-apis"] } # 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"] } procfs = "0.16" +loopdev = { git = "https://github.com/tiann/loopdev", branch = "loopfix" } [target.'cfg(target_os = "android")'.dependencies] android_logger = "0.13" diff --git a/userspace/ksud/src/event.rs b/userspace/ksud/src/event.rs index 244827a6b0c8..c50fb7e9f17c 100644 --- a/userspace/ksud/src/event.rs +++ b/userspace/ksud/src/event.rs @@ -1,5 +1,6 @@ use anyhow::{bail, Context, Result}; use log::{info, warn}; +#[cfg(target_os = "android")] use std::path::PathBuf; use std::{collections::HashMap, path::Path}; diff --git a/userspace/ksud/src/ksu.rs b/userspace/ksud/src/ksu.rs index 05939922058c..6d917ee6814a 100644 --- a/userspace/ksud/src/ksu.rs +++ b/userspace/ksud/src/ksu.rs @@ -14,6 +14,12 @@ use crate::{ utils::{self, umask}, }; +#[cfg(any(target_os = "linux", target_os = "android"))] +use rustix::{ + process::getuid, + thread::{set_thread_res_gid, set_thread_res_uid, Gid, Uid}, +}; + pub const KERNEL_SU_OPTION: u32 = 0xDEAD_BEEF; const CMD_GRANT_ROOT: u64 = 0; @@ -65,8 +71,13 @@ fn set_identity(uid: u32, gid: u32, groups: &[u32]) { if !groups.is_empty() { libc::setgroups(groups.len(), groups.as_ptr()); } - libc::setresgid(gid, gid, gid); - libc::setresuid(uid, uid, uid); + } + #[cfg(any(target_os = "linux", target_os = "android"))] + { + let gid = unsafe { Gid::from_raw(gid) }; + let uid = unsafe { Uid::from_raw(uid) }; + set_thread_res_gid(gid, gid, gid).ok(); + set_thread_res_uid(uid, uid, uid).ok(); } } @@ -203,7 +214,7 @@ pub fn root_shell() -> Result<()> { } // use current uid if no user specified, these has been done in kernel! - let mut uid = unsafe { libc::getuid() }; + let mut uid = getuid().as_raw(); if free_idx < matches.free.len() { let name = &matches.free[free_idx]; uid = unsafe { diff --git a/userspace/ksud/src/mount.rs b/userspace/ksud/src/mount.rs index 3a93eac7867e..4b93f8c8c6a3 100644 --- a/userspace/ksud/src/mount.rs +++ b/userspace/ksud/src/mount.rs @@ -1,11 +1,11 @@ -use anyhow::{bail, Ok, Result}; +use anyhow::{anyhow, bail, Ok, Result}; #[cfg(any(target_os = "linux", target_os = "android"))] use anyhow::Context; #[cfg(any(target_os = "linux", target_os = "android"))] use retry::delay::NoDelay; #[cfg(any(target_os = "linux", target_os = "android"))] -use sys_mount::{unmount, FilesystemType, Mount, MountFlags, Unmount, UnmountFlags}; +use rustix::{fd::AsFd, fs::CWD, mount::*}; use crate::defs::KSU_OVERLAY_SOURCE; use log::{info, warn}; @@ -14,49 +14,32 @@ use procfs::process::Process; use std::path::Path; pub struct AutoMountExt4 { - mnt: String, - #[cfg(any(target_os = "linux", target_os = "android"))] - mount: Option, + target: String, auto_umount: bool, } impl AutoMountExt4 { #[cfg(any(target_os = "linux", target_os = "android"))] - pub fn try_new(src: &str, mnt: &str, auto_umount: bool) -> Result { - let result = Mount::builder() - .fstype(FilesystemType::from("ext4")) - .flags(MountFlags::empty()) - .create_loop(true) - .mount(src, mnt) - .map(|mount| { - Ok(Self { - mnt: mnt.to_string(), - mount: Some(mount), - auto_umount, - }) - }); - if let Err(e) = result { - println!("- Mount failed: {e}, retry with system mount"); - let result = std::process::Command::new("mount") - .arg("-t") - .arg("ext4") - .arg(src) - .arg(mnt) - .status(); - if let Err(e) = result { - Err(anyhow::anyhow!( - "mount partition: {src} -> {mnt} failed: {e}" - )) - } else { - Ok(Self { - mnt: mnt.to_string(), - mount: None, - auto_umount, - }) - } - } else { - result.unwrap() - } + pub fn try_new(source: &str, target: &str, auto_umount: bool) -> Result { + let new_loopback = loopdev::LoopControl::open()?.next_free()?; + new_loopback.with().attach(source)?; + let lo = new_loopback.path().ok_or(anyhow!("no loop"))?; + let fs = fsopen("ext4", FsOpenFlags::FSOPEN_CLOEXEC)?; + let fs = fs.as_fd(); + fsconfig_set_string(fs, "source", lo)?; + fsconfig_create(fs)?; + let mount = fsmount(fs, FsMountFlags::FSMOUNT_CLOEXEC, MountAttrFlags::empty())?; + move_mount( + mount.as_fd(), + "", + CWD, + target, + MoveMountFlags::MOVE_MOUNT_F_EMPTY_PATH, + )?; + Ok(Self { + target: target.to_string(), + auto_umount, + }) } #[cfg(not(any(target_os = "linux", target_os = "android")))] @@ -66,18 +49,8 @@ impl AutoMountExt4 { #[cfg(any(target_os = "linux", target_os = "android"))] pub fn umount(&self) -> Result<()> { - if let Some(ref mount) = self.mount { - mount - .unmount(UnmountFlags::empty()) - .map_err(|e| anyhow::anyhow!(e)) - } else { - let result = std::process::Command::new("umount").arg(&self.mnt).status(); - if let Err(e) = result { - Err(anyhow::anyhow!("umount: {} failed: {e}", self.mnt)) - } else { - Ok(()) - } - } + unmount(self.target.as_str(), UnmountFlags::DETACH)?; + Ok(()) } } @@ -86,7 +59,7 @@ impl Drop for AutoMountExt4 { fn drop(&mut self) { log::info!( "AutoMountExt4 drop: {}, auto_umount: {}", - self.mnt, + self.target, self.auto_umount ); if self.auto_umount { @@ -98,18 +71,7 @@ impl Drop for AutoMountExt4 { #[allow(dead_code)] #[cfg(any(target_os = "linux", target_os = "android"))] fn mount_image(src: &str, target: &str, autodrop: bool) -> Result<()> { - if autodrop { - Mount::builder() - .fstype(FilesystemType::from("ext4")) - .create_loop(true) - .mount_autodrop(src, target, UnmountFlags::empty()) - .with_context(|| format!("Failed to do mount: {src} -> {target}"))?; - } else { - Mount::builder() - .fstype(FilesystemType::from("ext4")) - .mount(src, target) - .with_context(|| format!("Failed to do mount: {src} -> {target}"))?; - } + AutoMountExt4::try_new(src, target, autodrop)?; Ok(()) } @@ -146,28 +108,37 @@ fn mount_overlayfs( dest.as_ref().display(), options ); - Mount::builder() - .fstype(FilesystemType::from("overlay")) - .data(&options) - .flags(MountFlags::RDONLY) - .mount(KSU_OVERLAY_SOURCE, dest.as_ref()) - .with_context(|| { - format!( - "mount overlayfs on {} options {} failed", - dest.as_ref().display(), - options - ) - })?; + let fs = fsopen("overlay", FsOpenFlags::FSOPEN_CLOEXEC)?; + let fs = fs.as_fd(); + fsconfig_set_string(fs, "lowerdir", lower_dirs.join(":"))?; + fsconfig_set_string(fs, "source", KSU_OVERLAY_SOURCE)?; + fsconfig_create(fs)?; + let mount = fsmount(fs, FsMountFlags::FSMOUNT_CLOEXEC, MountAttrFlags::empty())?; + move_mount( + mount.as_fd(), + "", + CWD, + dest.as_ref(), + MoveMountFlags::MOVE_MOUNT_F_EMPTY_PATH, + )?; Ok(()) } #[cfg(any(target_os = "linux", target_os = "android"))] pub fn mount_tmpfs(dest: impl AsRef) -> Result<()> { info!("mount tmpfs on {}", dest.as_ref().display()); - Mount::builder() - .fstype(FilesystemType::from("tmpfs")) - .mount(KSU_OVERLAY_SOURCE, dest.as_ref()) - .with_context(|| format!("mount tmpfs on {} failed", dest.as_ref().display()))?; + let fs = fsopen("tmpfs", FsOpenFlags::FSOPEN_CLOEXEC)?; + let fs = fs.as_fd(); + fsconfig_set_string(fs, "source", KSU_OVERLAY_SOURCE)?; + fsconfig_create(fs)?; + let mount = fsmount(fs, FsMountFlags::FSMOUNT_CLOEXEC, MountAttrFlags::empty())?; + move_mount( + mount.as_fd(), + "", + CWD, + dest.as_ref(), + MoveMountFlags::MOVE_MOUNT_F_EMPTY_PATH, + )?; Ok(()) } @@ -178,16 +149,12 @@ fn bind_mount(from: impl AsRef, to: impl AsRef) -> Result<()> { 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() - ) - })?; + let tree = open_tree( + CWD, + from.as_ref(), + OpenTreeFlags::OPEN_TREE_CLOEXEC | OpenTreeFlags::OPEN_TREE_CLONE, + )?; + move_mount(tree.as_fd(), "", CWD, to.as_ref(), MoveMountFlags::empty())?; Ok(()) } diff --git a/userspace/ksud/src/utils.rs b/userspace/ksud/src/utils.rs index 77b1aa75bd8c..dc782475b7a1 100644 --- a/userspace/ksud/src/utils.rs +++ b/userspace/ksud/src/utils.rs @@ -15,6 +15,12 @@ use std::os::unix::prelude::PermissionsExt; use hole_punch::*; use std::io::{Read, Seek, SeekFrom}; +#[cfg(any(target_os = "linux", target_os = "android"))] +use rustix::{ + process, + thread::{move_into_link_name_space, unshare, LinkNameSpaceType, UnshareFlags}, +}; + pub fn ensure_clean_dir(dir: &str) -> Result<()> { let path = Path::new(dir); log::debug!("ensure_clean_dir: {}", path.display()); @@ -115,24 +121,23 @@ pub fn get_zip_uncompressed_size(zip_path: &str) -> Result { #[cfg(any(target_os = "linux", target_os = "android"))] pub fn switch_mnt_ns(pid: i32) -> Result<()> { - use anyhow::ensure; - use std::os::fd::AsRawFd; + use rustix::{ + fd::AsFd, + fs::{open, Mode, OFlags}, + }; let path = format!("/proc/{pid}/ns/mnt"); - let fd = std::fs::File::open(path)?; + let fd = open(path, OFlags::RDONLY, Mode::from_raw_mode(0))?; let current_dir = std::env::current_dir(); - let ret = unsafe { libc::setns(fd.as_raw_fd(), libc::CLONE_NEWNS) }; + move_into_link_name_space(fd.as_fd(), Some(LinkNameSpaceType::Mount))?; if let std::result::Result::Ok(current_dir) = current_dir { let _ = std::env::set_current_dir(current_dir); } - 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"); + unshare(UnshareFlags::NEWNS)?; Ok(()) } @@ -164,7 +169,7 @@ pub fn switch_cgroups() { #[cfg(any(target_os = "linux", target_os = "android"))] pub fn umask(mask: u32) { - unsafe { libc::umask(mask) }; + process::umask(rustix::fs::Mode::from_raw_mode(mask)); } #[cfg(not(any(target_os = "linux", target_os = "android")))]