From a30ae06861006210d2cee6e646cce633f9351917 Mon Sep 17 00:00:00 2001
From: LoveSy <shana@zju.edu.cn>
Date: Thu, 1 Feb 2024 00:22:25 +0800
Subject: [PATCH] Use new mount api

---
 userspace/ksud/Cargo.lock   | 145 -------------------------
 userspace/ksud/Cargo.toml   |   1 -
 userspace/ksud/src/event.rs |   2 +
 userspace/ksud/src/mount.rs | 211 ++++++++++++++++++++----------------
 4 files changed, 122 insertions(+), 237 deletions(-)

diff --git a/userspace/ksud/Cargo.lock b/userspace/ksud/Cargo.lock
index 69bc91178168..f3236670a508 100644
--- a/userspace/ksud/Cargo.lock
+++ b/userspace/ksud/Cargo.lock
@@ -121,25 +121,6 @@ version = "1.6.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b"
 
-[[package]]
-name = "bindgen"
-version = "0.60.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "062dddbc1ba4aca46de6338e2bf87771414c335f7b2f2036e8f3e9befebf88e6"
-dependencies = [
- "bitflags 1.3.2",
- "cexpr",
- "clang-sys",
- "lazy_static",
- "lazycell",
- "peeking_take_while",
- "proc-macro2",
- "quote",
- "regex",
- "rustc-hash",
- "shlex",
-]
-
 [[package]]
 name = "bitflags"
 version = "1.3.2"
@@ -209,15 +190,6 @@ dependencies = [
  "jobserver",
 ]
 
-[[package]]
-name = "cexpr"
-version = "0.6.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6fac387a98bb7c37292057cffc56d62ecb629900026402633ae9160df93a8766"
-dependencies = [
- "nom",
-]
-
 [[package]]
 name = "cfg-if"
 version = "0.1.10"
@@ -254,17 +226,6 @@ dependencies = [
  "generic-array",
 ]
 
-[[package]]
-name = "clang-sys"
-version = "1.7.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "67523a3b4be3ce1989d607a828d036249522dd9c1c8de7f4dd2dae43a37369d1"
-dependencies = [
- "glob",
- "libc",
- "libloading",
-]
-
 [[package]]
 name = "clap"
 version = "4.1.4"
@@ -618,12 +579,6 @@ version = "0.27.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "b6c80984affa11d98d1b88b66ac8853f143217b399d3c74116778ff8fdb4ed2e"
 
-[[package]]
-name = "glob"
-version = "0.3.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b"
-
 [[package]]
 name = "heck"
 version = "0.4.1"
@@ -862,7 +817,6 @@ dependencies = [
  "serde",
  "serde_json",
  "sha256",
- "sys-mount",
  "tempdir",
  "which",
  "zip 0.6.4",
@@ -875,12 +829,6 @@ version = "1.4.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
 
-[[package]]
-name = "lazycell"
-version = "1.3.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55"
-
 [[package]]
 name = "libc"
 version = "0.2.152"
@@ -907,16 +855,6 @@ dependencies = [
  "rle-decode-fast",
 ]
 
-[[package]]
-name = "libloading"
-version = "0.8.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c571b676ddfc9a8c12f1f3d3085a7b163966a8fd8098a90640953ce5f6170161"
-dependencies = [
- "cfg-if 1.0.0",
- "windows-sys 0.48.0",
-]
-
 [[package]]
 name = "libm"
 version = "0.2.6"
@@ -950,16 +888,6 @@ version = "0.4.20"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f"
 
-[[package]]
-name = "loopdev"
-version = "0.5.0"
-source = "git+https://github.com/tiann/loopdev?branch=loopfix#b6ca5e3ea163f66239f6a835874fe231b2a9286f"
-dependencies = [
- "bindgen",
- "errno 0.2.8",
- "libc",
-]
-
 [[package]]
 name = "memchr"
 version = "2.7.1"
@@ -1080,12 +1008,6 @@ dependencies = [
  "sha2",
 ]
 
-[[package]]
-name = "peeking_take_while"
-version = "0.1.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "19b17cddbe7ec3f8bc800887bab5e717348c95ea2ca0b1bf0837fb964dc67099"
-
 [[package]]
 name = "pin-project-lite"
 version = "0.2.9"
@@ -1349,12 +1271,6 @@ version = "0.1.23"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76"
 
-[[package]]
-name = "rustc-hash"
-version = "1.1.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2"
-
 [[package]]
 name = "rustix"
 version = "0.36.7"
@@ -1457,23 +1373,6 @@ dependencies = [
  "tokio",
 ]
 
-[[package]]
-name = "shlex"
-version = "1.3.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
-
-[[package]]
-name = "smart-default"
-version = "0.6.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "133659a15339456eeeb07572eb02a91c91e9815e9cbc89566944d2c8d3efdbf6"
-dependencies = [
- "proc-macro2",
- "quote",
- "syn 1.0.107",
-]
-
 [[package]]
 name = "strsim"
 version = "0.10.0"
@@ -1508,19 +1407,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"
@@ -1599,37 +1485,6 @@ dependencies = [
  "pin-project-lite",
 ]
 
-[[package]]
-name = "tracing"
-version = "0.1.40"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef"
-dependencies = [
- "pin-project-lite",
- "tracing-attributes",
- "tracing-core",
-]
-
-[[package]]
-name = "tracing-attributes"
-version = "0.1.27"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7"
-dependencies = [
- "proc-macro2",
- "quote",
- "syn 2.0.48",
-]
-
-[[package]]
-name = "tracing-core"
-version = "0.1.32"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54"
-dependencies = [
- "once_cell",
-]
-
 [[package]]
 name = "typenum"
 version = "1.16.0"
diff --git a/userspace/ksud/Cargo.toml b/userspace/ksud/Cargo.toml
index 3a38b43b751e..0adf08e4793c 100644
--- a/userspace/ksud/Cargo.toml
+++ b/userspace/ksud/Cargo.toml
@@ -39,7 +39,6 @@ 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"] }
diff --git a/userspace/ksud/src/event.rs b/userspace/ksud/src/event.rs
index 6b1ddb3cfeb0..c50fb7e9f17c 100644
--- a/userspace/ksud/src/event.rs
+++ b/userspace/ksud/src/event.rs
@@ -1,5 +1,7 @@
 use anyhow::{bail, Context, Result};
 use log::{info, warn};
+#[cfg(target_os = "android")]
+use std::path::PathBuf;
 use std::{collections::HashMap, path::Path};
 
 use crate::module::prune_modules;
diff --git a/userspace/ksud/src/mount.rs b/userspace/ksud/src/mount.rs
index 3a93eac7867e..3a4ff18662d7 100644
--- a/userspace/ksud/src/mount.rs
+++ b/userspace/ksud/src/mount.rs
@@ -1,11 +1,15 @@
-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, AsRawFd},
+    fs::{access, makedev, mknodat, Access, FileType, Mode, CWD},
+    mount::*,
+};
 
 use crate::defs::KSU_OVERLAY_SOURCE;
 use log::{info, warn};
@@ -14,49 +18,90 @@ use procfs::process::Process;
 use std::path::Path;
 
 pub struct AutoMountExt4 {
-    mnt: String,
-    #[cfg(any(target_os = "linux", target_os = "android"))]
-    mount: Option<Mount>,
+    target: String,
     auto_umount: bool,
 }
+#[repr(C)]
+struct LoopInfo {
+    lo_number: core::ffi::c_int,
+    lo_device: libc::dev_t,
+    lo_inode: core::ffi::c_ulong,
+    lo_rdevice: libc::dev_t,
+    lo_offset: core::ffi::c_int,
+    lo_encrypt_type: core::ffi::c_int,
+    lo_encrypt_key_size: core::ffi::c_int,
+    lo_flags: core::ffi::c_int,
+    lo_file_name: [libc::c_uchar; 32],
+    lo_encrypt_key: [libc::c_uchar; 32],
+    lo_init: [core::ffi::c_ulong; 2],
+    reserved: [libc::c_char; 4],
+}
 
 impl AutoMountExt4 {
     #[cfg(any(target_os = "linux", target_os = "android"))]
-    pub fn try_new(src: &str, mnt: &str, auto_umount: bool) -> Result<Self> {
-        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<Self> {
+        use libc::ENXIO;
+        use rustix::{
+            fs::open,
+            fs::{unlink, OFlags},
+        };
+
+        let lo = (0..=0xfffff)
+            .find_map(|i| {
+                let path = format!("/dev/block/loop{i}");
+                let mut created = false;
+                if access(&path, Access::EXISTS).is_err() {
+                    if mknodat(
+                        CWD,
+                        &path,
+                        FileType::BlockDevice,
+                        Mode::from_raw_mode(0o644),
+                        makedev(7, i),
+                    )
+                    .is_err()
+                    {
+                        return None;
+                    }
+                    created = true;
+                }
+                let res = open(&path, OFlags::RDONLY, Mode::from_raw_mode(0))
+                    .ok()
+                    .and_then(|fd| unsafe {
+                        let loop_info: LoopInfo = std::mem::zeroed();
+                        if libc::ioctl(fd.as_raw_fd(), 0x4C03, &loop_info as *const _) < 0
+                            && libc::__errno_location().read() == ENXIO
+                        {
+                            Some(path.clone())
+                        } else {
+                            None
+                        }
+                    });
+                if res.is_none() && created {
+                    unlink(&path).ok();
+                }
+                res
+            })
+            .ok_or(anyhow!("failed to find a free loop device"))?;
+        let lfd = open(&lo, OFlags::RDWR, Mode::from_raw_mode(0))?;
+        unsafe { libc::ioctl(lfd.as_raw_fd(), 0x4C00, source.as_ptr()) };
+        let loop_info: LoopInfo = unsafe { std::mem::zeroed() };
+        unsafe { libc::ioctl(lfd.as_raw_fd(), 0x4C02, &loop_info as *const _) };
+        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 +111,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 +121,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 +133,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 +170,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<Path>) -> 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 +211,12 @@ fn bind_mount(from: impl AsRef<Path>, to: impl AsRef<Path>) -> 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(())
 }