From ba6f29557eca5b1518764036f410c9858ad8c339 Mon Sep 17 00:00:00 2001 From: weishu Date: Fri, 21 Nov 2025 13:24:11 +0800 Subject: [PATCH] meta-overlayfs: Moved to module repo --- .github/workflows/meta-overlay.yml | 56 --- userspace/meta-overlayfs/.gitignore | 4 - userspace/meta-overlayfs/Cargo.toml | 24 -- userspace/meta-overlayfs/README.md | 58 --- userspace/meta-overlayfs/build.sh | 122 ------ userspace/meta-overlayfs/metamodule/.gitkeep | 0 .../meta-overlayfs/metamodule/customize.sh | 67 ---- .../metamodule/meta-overlayfs-changelog.md | 4 - .../meta-overlayfs/metamodule/metainstall.sh | 100 ----- .../meta-overlayfs/metamodule/metamount.sh | 65 --- .../metamodule/metauninstall.sh | 35 -- .../meta-overlayfs/metamodule/module.prop | 8 - .../meta-overlayfs/metamodule/post-mount.sh | 3 - .../meta-overlayfs/metamodule/uninstall.sh | 24 -- .../meta-overlayfs/metamodule/update.json | 6 - userspace/meta-overlayfs/src/defs.rs | 17 - userspace/meta-overlayfs/src/main.rs | 35 -- userspace/meta-overlayfs/src/mount.rs | 376 ------------------ userspace/meta-overlayfs/src/xcp.rs | 90 ----- 19 files changed, 1094 deletions(-) delete mode 100644 .github/workflows/meta-overlay.yml delete mode 100644 userspace/meta-overlayfs/.gitignore delete mode 100644 userspace/meta-overlayfs/Cargo.toml delete mode 100644 userspace/meta-overlayfs/README.md delete mode 100644 userspace/meta-overlayfs/build.sh delete mode 100644 userspace/meta-overlayfs/metamodule/.gitkeep delete mode 100644 userspace/meta-overlayfs/metamodule/customize.sh delete mode 100644 userspace/meta-overlayfs/metamodule/meta-overlayfs-changelog.md delete mode 100644 userspace/meta-overlayfs/metamodule/metainstall.sh delete mode 100644 userspace/meta-overlayfs/metamodule/metamount.sh delete mode 100644 userspace/meta-overlayfs/metamodule/metauninstall.sh delete mode 100644 userspace/meta-overlayfs/metamodule/module.prop delete mode 100644 userspace/meta-overlayfs/metamodule/post-mount.sh delete mode 100644 userspace/meta-overlayfs/metamodule/uninstall.sh delete mode 100644 userspace/meta-overlayfs/metamodule/update.json delete mode 100644 userspace/meta-overlayfs/src/defs.rs delete mode 100644 userspace/meta-overlayfs/src/main.rs delete mode 100644 userspace/meta-overlayfs/src/mount.rs delete mode 100644 userspace/meta-overlayfs/src/xcp.rs diff --git a/.github/workflows/meta-overlay.yml b/.github/workflows/meta-overlay.yml deleted file mode 100644 index 74ef3fe3..00000000 --- a/.github/workflows/meta-overlay.yml +++ /dev/null @@ -1,56 +0,0 @@ -name: Build meta-overlayfs - -on: - push: - branches: [ "main", "dev", "ci", "miuix" ] - paths: - - '.github/workflows/meta-overlay.yml' - - 'userspace/meta-overlayfs/**' - pull_request: - branches: [ "main", "dev", "miuix" ] - paths: - - '.github/workflows/meta-overlay.yml' - - 'userspace/meta-overlayfs/**' - workflow_call: - -jobs: - build: - runs-on: ubuntu-latest - defaults: - run: - shell: bash - working-directory: userspace/meta-overlayfs - - steps: - - name: Checkout - uses: actions/checkout@v5 - with: - fetch-depth: 0 - - - name: Setup Rust - run: | - rustup update stable - rustup target add aarch64-linux-android - rustup target add x86_64-linux-android - - - name: Cache Cargo - uses: Swatinem/rust-cache@v2 - with: - workspaces: userspace/meta-overlayfs - cache-targets: false - - - name: Install cross - run: | - RUSTFLAGS="" cargo install cross --git https://github.com/cross-rs/cross --rev 66845c1 - - - name: Build meta-overlayfs metamodule - run: chmod +x build.sh && ./build.sh - - - name: Upload artifact - uses: actions/upload-artifact@v5 - with: - name: meta-overlayfs - path: | - userspace/meta-overlayfs/target/meta-overlayfs-*.zip - userspace/meta-overlayfs/metamodule/meta-overlayfs-changelog.md - if-no-files-found: error diff --git a/userspace/meta-overlayfs/.gitignore b/userspace/meta-overlayfs/.gitignore deleted file mode 100644 index 6bfc5c70..00000000 --- a/userspace/meta-overlayfs/.gitignore +++ /dev/null @@ -1,4 +0,0 @@ -/target -/out -Cargo.lock -*.log diff --git a/userspace/meta-overlayfs/Cargo.toml b/userspace/meta-overlayfs/Cargo.toml deleted file mode 100644 index 7160c4fd..00000000 --- a/userspace/meta-overlayfs/Cargo.toml +++ /dev/null @@ -1,24 +0,0 @@ -[package] -name = "meta-overlayfs" -version = "1.0.0" -edition = "2024" -authors = ["KernelSU Developers"] -description = "An implementation of a metamodule using OverlayFS for KernelSU" -license = "GPL-3.0" - -[dependencies] -anyhow = "1" -log = "0.4" -env_logger = { version = "0.11", default-features = false } -hole-punch = { git = "https://github.com/tiann/hole-punch" } - -[target.'cfg(any(target_os = "linux", target_os = "android"))'.dependencies] -rustix = { git = "https://github.com/Kernel-SU/rustix.git", rev = "4a53fbc7cb7a07cabe87125cc21dbc27db316259", features = ["all-apis"] } -procfs = "0.17" - -[profile.release] -strip = true -opt-level = "z" # Minimize binary size -lto = true # Link-time optimization -codegen-units = 1 # Maximum optimization -panic = "abort" # Reduce binary size diff --git a/userspace/meta-overlayfs/README.md b/userspace/meta-overlayfs/README.md deleted file mode 100644 index ace6f528..00000000 --- a/userspace/meta-overlayfs/README.md +++ /dev/null @@ -1,58 +0,0 @@ -# meta-overlayfs - -Official overlayfs mount handler for KernelSU metamodules. - -## Installation - -```bash -adb push meta-overlayfs-v1.0.0.zip /sdcard/ -adb shell su -c 'ksud module install /sdcard/meta-overlayfs-v1.0.0.zip' -adb reboot -``` - -Or install via KernelSU Manager → Modules. - -**Note**: The metamodule is now installed as a regular module to `/data/adb/modules/meta-overlay/`, with a symlink created at `/data/adb/metamodule` pointing to it. - -## How It Works - -Uses dual-directory architecture for ext4 image support: - -- **Metadata**: `/data/adb/modules/` - Contains `module.prop`, `disable`, `skip_mount` markers -- **Content**: `/data/adb/metamodule/mnt/` - Contains `system/`, `vendor/` etc. directories from ext4 images - -Scans metadata directory for enabled modules, then mounts their content directories as overlayfs layers. - -### Supported Partitions - -system, vendor, product, system_ext, odm, oem - -### Read-Write Layer - -Optional upperdir/workdir support via `/data/adb/modules/.rw/`: - -```bash -mkdir -p /data/adb/modules/.rw/system/{upperdir,workdir} -``` - -## Environment Variables - -- `MODULE_METADATA_DIR` - Metadata location (default: `/data/adb/modules/`) -- `MODULE_CONTENT_DIR` - Content location (default: `/data/adb/metamodule/mnt/`) -- `RUST_LOG` - Log level (debug, info, warn, error) - -## Architecture - -Automatically selects aarch64 or x86_64 binary during installation (~500KB). - -## Building - -```bash -./build.sh -``` - -Output: `target/meta-overlayfs-v1.0.0.zip` - -## License - -GPL-3.0 diff --git a/userspace/meta-overlayfs/build.sh b/userspace/meta-overlayfs/build.sh deleted file mode 100644 index 4618bba3..00000000 --- a/userspace/meta-overlayfs/build.sh +++ /dev/null @@ -1,122 +0,0 @@ -#!/bin/bash -set -e - -# Configuration -VERSION=$(grep '^version' Cargo.toml | head -1 | sed 's/.*"\(.*\)".*/\1/') -OUTPUT_DIR="target" -METAMODULE_DIR="metamodule" -MODULE_PROP_FILE="$METAMODULE_DIR/module.prop" -UPDATE_JSON_FILE="$METAMODULE_DIR/update.json" -MODULE_OUTPUT_DIR="$OUTPUT_DIR/module" - -MODULE_VERSION=$(grep -m1 '^version=' "$MODULE_PROP_FILE" | cut -d'=' -f2- | tr -d '\r') -MODULE_VERSION_CODE=$(grep -m1 '^versionCode=' "$MODULE_PROP_FILE" | cut -d'=' -f2- | tr -d '\r') - -if [ -z "$MODULE_VERSION" ] || [ -z "$MODULE_VERSION_CODE" ]; then - echo "Error: Failed to read module version information from $MODULE_PROP_FILE" - exit 1 -fi - -echo "==========================================" -echo "Building meta-overlayfs v${VERSION}" -echo "==========================================" - -# Detect build tool -if command -v cross >/dev/null 2>&1; then - BUILD_TOOL="cross" - echo "Using cross for compilation" -else - BUILD_TOOL="cargo-ndk" - echo "Using cargo ndk for compilation" - if ! command -v cargo-ndk >/dev/null 2>&1; then - echo "Error: Neither cross nor cargo-ndk found!" - echo "Please install one of them:" - echo " - cross: cargo install cross" - echo " - cargo-ndk: cargo install cargo-ndk" - exit 1 - fi -fi - -# Clean output directory -echo "Cleaning output directory..." -rm -rf "$OUTPUT_DIR" -mkdir -p "$MODULE_OUTPUT_DIR" - -# Build for multiple architectures -echo "" -echo "Building for aarch64-linux-android..." -if [ "$BUILD_TOOL" = "cross" ]; then - cross build --release --target aarch64-linux-android -else - cargo ndk build -t arm64-v8a --release -fi - -echo "" -echo "Building for x86_64-linux-android..." -if [ "$BUILD_TOOL" = "cross" ]; then - cross build --release --target x86_64-linux-android -else - cargo ndk build -t x86_64 --release -fi - -# Copy binaries -echo "" -echo "Copying binaries..." -cp target/aarch64-linux-android/release/meta-overlayfs \ - "$MODULE_OUTPUT_DIR/meta-overlayfs-aarch64" -cp target/x86_64-linux-android/release/meta-overlayfs \ - "$MODULE_OUTPUT_DIR/meta-overlayfs-x86_64" - -# Copy metamodule files -echo "Copying metamodule files..." -cp "$METAMODULE_DIR"/module.prop "$MODULE_OUTPUT_DIR/" -cp "$METAMODULE_DIR"/*.sh "$MODULE_OUTPUT_DIR/" - -# Set permissions -echo "Setting permissions..." -chmod 755 "$MODULE_OUTPUT_DIR"/*.sh -chmod 755 "$MODULE_OUTPUT_DIR"/meta-overlayfs-* - -# Display binary sizes -echo "" -echo "Binary sizes:" -echo " aarch64: $(du -h "$MODULE_OUTPUT_DIR"/meta-overlayfs-aarch64 | awk '{print $1}')" -echo " x86_64: $(du -h "$MODULE_OUTPUT_DIR"/meta-overlayfs-x86_64 | awk '{print $1}')" - -# Package -echo "" -echo "Packaging..." -cd "$MODULE_OUTPUT_DIR" -ZIP_NAME="meta-overlayfs-v${MODULE_VERSION}.zip" -zip -r "../$ZIP_NAME" . -cd ../.. - -echo "" -echo "Generating update.json..." -if ! LATEST_TAG=$(git describe --tags --abbrev=0 2>/dev/null); then - echo "Error: Unable to determine the latest git tag." - exit 1 -fi - -ZIP_URL="https://github.com/tiann/KernelSU/releases/download/${LATEST_TAG}/${ZIP_NAME}" -CHANGELOG_URL="https://github.com/tiann/KernelSU/releases/download/${LATEST_TAG}/meta-overlayfs-changelog.md" - -cat > "$UPDATE_JSON_FILE" </dev/null 2>&1 || \ - abort "! Failed to format ext4 image" - - ui_print "- Image created successfully (sparse file)" -fi - -ui_print "- Installation complete" diff --git a/userspace/meta-overlayfs/metamodule/meta-overlayfs-changelog.md b/userspace/meta-overlayfs/metamodule/meta-overlayfs-changelog.md deleted file mode 100644 index 56ff0d0c..00000000 --- a/userspace/meta-overlayfs/metamodule/meta-overlayfs-changelog.md +++ /dev/null @@ -1,4 +0,0 @@ - -# v1.1.0 Changelog - -Fix bootloop diff --git a/userspace/meta-overlayfs/metamodule/metainstall.sh b/userspace/meta-overlayfs/metamodule/metainstall.sh deleted file mode 100644 index c3cc98e8..00000000 --- a/userspace/meta-overlayfs/metamodule/metainstall.sh +++ /dev/null @@ -1,100 +0,0 @@ -#!/system/bin/sh -############################################ -# meta-overlayfs metainstall.sh -# Module installation hook for ext4 image support -############################################ - -# Constants -IMG_FILE="/data/adb/metamodule/modules.img" -MNT_DIR="/data/adb/metamodule/mnt" - -# Ensure ext4 image is mounted -ensure_image_mounted() { - if ! mountpoint -q "$MNT_DIR" 2>/dev/null; then - ui_print "- Mounting modules image" - mkdir -p "$MNT_DIR" - mount -t ext4 -o loop,rw,noatime "$IMG_FILE" "$MNT_DIR" || { - abort "! Failed to mount modules image" - } - ui_print "- Image mounted successfully" - else - ui_print "- Image already mounted" - fi -} - -# Determine whether this module should be moved into the ext4 image. -# We only relocate payloads that expose system/ overlays and do not opt out via skip_mount. -module_requires_overlay_move() { - if [ -f "$MODPATH/skip_mount" ]; then - ui_print "- skip_mount flag detected; keeping files under /data/adb/modules" - return 1 - fi - - if [ ! -d "$MODPATH/system" ]; then - ui_print "- No system/ directory detected; keeping files under /data/adb/modules" - return 1 - fi - - return 0 -} - -# Copy SELinux contexts from src tree to destination by mirroring each entry. -copy_selinux_contexts() { - command -v chcon >/dev/null 2>&1 || return 0 - - SRC="$1" - DST="$2" - - if [ -z "$SRC" ] || [ -z "$DST" ] || [ ! -e "$SRC" ] || [ ! -e "$DST" ]; then - return 0 - fi - - chcon --reference="$SRC" "$DST" 2>/dev/null || true - - find "$SRC" -print | while IFS= read -r PATH_SRC; do - if [ "$PATH_SRC" = "$SRC" ]; then - continue - fi - REL_PATH="${PATH_SRC#"${SRC}/"}" - PATH_DST="$DST/$REL_PATH" - if [ -e "$PATH_DST" ] || [ -L "$PATH_DST" ]; then - chcon --reference="$PATH_SRC" "$PATH_DST" 2>/dev/null || true - fi - done -} - -# Post-installation: move partition directories to ext4 image -post_install_to_image() { - ui_print "- Copying module content to image" - - set_perm "$MNT_DIR" 0 0 0755 0644 - - MOD_IMG_DIR="$MNT_DIR/$MODID" - mkdir -p "$MOD_IMG_DIR" - set_perm "$MOD_IMG_DIR" 0 0 0755 0644 - - # Move all partition directories - for partition in system vendor product system_ext odm oem; do - if [ -d "$MODPATH/$partition" ]; then - ui_print "- Copying $partition/" - cp -af "$MODPATH/$partition" "$MOD_IMG_DIR/" || { - ui_print "! Warning: Failed to move $partition" - continue - } - copy_selinux_contexts "$MODPATH/$partition" "$MOD_IMG_DIR/$partition" - fi - done -} - -ui_print "- Using meta-overlayfs metainstall" - -install_module - -if module_requires_overlay_move; then - ensure_image_mounted - post_install_to_image -else - ui_print "- Skipping move to modules image" -fi - -ui_print "- Installation complete" diff --git a/userspace/meta-overlayfs/metamodule/metamount.sh b/userspace/meta-overlayfs/metamodule/metamount.sh deleted file mode 100644 index de6a6151..00000000 --- a/userspace/meta-overlayfs/metamodule/metamount.sh +++ /dev/null @@ -1,65 +0,0 @@ -#!/system/bin/sh -# meta-overlayfs Module Mount Handler -# This script is the entry point for dual-directory module mounting - -MODDIR="${0%/*}" -IMG_FILE="$MODDIR/modules.img" -MNT_DIR="$MODDIR/mnt" - -# Log function -log() { - echo "[meta-overlayfs] $1" -} - -log "Starting module mount process" - -# Ensure ext4 image is mounted -if ! mountpoint -q "$MNT_DIR" 2>/dev/null; then - log "Image not mounted, mounting now..." - - # Check if image file exists - if [ ! -f "$IMG_FILE" ]; then - log "ERROR: Image file not found at $IMG_FILE" - exit 1 - fi - - # Create mount point - mkdir -p "$MNT_DIR" - - # Mount the ext4 image - mount -t ext4 -o loop,rw,noatime "$IMG_FILE" "$MNT_DIR" || { - log "ERROR: Failed to mount image" - exit 1 - } - log "Image mounted successfully at $MNT_DIR" -else - log "Image already mounted at $MNT_DIR" -fi - -# Binary path (architecture-specific binary selected during installation) -BINARY="$MODDIR/meta-overlayfs" - -if [ ! -f "$BINARY" ]; then - log "ERROR: Binary not found: $BINARY" - exit 1 -fi - -# Set dual-directory environment variables -export MODULE_METADATA_DIR="/data/adb/modules" -export MODULE_CONTENT_DIR="$MNT_DIR" - -log "Metadata directory: $MODULE_METADATA_DIR" -log "Content directory: $MODULE_CONTENT_DIR" -log "Executing $BINARY" - -# Execute the mount binary -"$BINARY" -EXIT_CODE=$? - -if [ $EXIT_CODE -ne 0 ]; then - log "Mount failed with exit code $EXIT_CODE" - exit $EXIT_CODE -fi - -log "Mount completed successfully" -exit 0 diff --git a/userspace/meta-overlayfs/metamodule/metauninstall.sh b/userspace/meta-overlayfs/metamodule/metauninstall.sh deleted file mode 100644 index f30df49c..00000000 --- a/userspace/meta-overlayfs/metamodule/metauninstall.sh +++ /dev/null @@ -1,35 +0,0 @@ -#!/system/bin/sh -############################################ -# mm-overlayfs metauninstall.sh -# Module uninstallation hook for ext4 image cleanup -############################################ - -# Constants -MNT_DIR="/data/adb/metamodule/mnt" - -if [ -z "$MODULE_ID" ]; then - echo "! Error: MODULE_ID not provided" - exit 1 -fi - -echo "- Cleaning up module content from image: $MODULE_ID" - -# Check if image is mounted -if ! mountpoint -q "$MNT_DIR" 2>/dev/null; then - echo "! Warning: Image not mounted, skipping cleanup" - exit 0 -fi - -# Remove module content from image -MOD_IMG_DIR="$MNT_DIR/$MODULE_ID" -if [ -d "$MOD_IMG_DIR" ]; then - echo " Removing $MOD_IMG_DIR" - rm -rf "$MOD_IMG_DIR" || { - echo "! Warning: Failed to remove module content from image" - } - echo "- Module content removed from image" -else - echo "- No module content found in image, skipping" -fi - -exit 0 diff --git a/userspace/meta-overlayfs/metamodule/module.prop b/userspace/meta-overlayfs/metamodule/module.prop deleted file mode 100644 index 7e12cb8d..00000000 --- a/userspace/meta-overlayfs/metamodule/module.prop +++ /dev/null @@ -1,8 +0,0 @@ -id=meta-overlayfs -metamodule=1 -name=OverlayFS MetaModule -version=1.1.0 -versionCode=1100 -author=KernelSU Developers -description=An implementation of a metamodule using OverlayFS for KernelSU -updateJson=https://raw.githubusercontent.com/tiann/KernelSU/refs/heads/main/userspace/meta-overlayfs/metamodule/update.json \ No newline at end of file diff --git a/userspace/meta-overlayfs/metamodule/post-mount.sh b/userspace/meta-overlayfs/metamodule/post-mount.sh deleted file mode 100644 index 6234b6ab..00000000 --- a/userspace/meta-overlayfs/metamodule/post-mount.sh +++ /dev/null @@ -1,3 +0,0 @@ -#!/system/bin/sh - -ksud kernel nuke-ext4-sysfs /data/adb/modules/meta-overlayfs/mnt diff --git a/userspace/meta-overlayfs/metamodule/uninstall.sh b/userspace/meta-overlayfs/metamodule/uninstall.sh deleted file mode 100644 index 90d41d4e..00000000 --- a/userspace/meta-overlayfs/metamodule/uninstall.sh +++ /dev/null @@ -1,24 +0,0 @@ -#!/system/bin/sh -############################################ -# mm-overlayfs uninstall.sh -# Cleanup script for metamodule removal -############################################ - -MODDIR="${0%/*}" -MNT_DIR="$MODDIR/mnt" - -echo "- Uninstalling metamodule..." - -# Unmount the ext4 image if mounted -if mountpoint -q "$MNT_DIR" 2>/dev/null; then - echo "- Unmounting image..." - umount "$MNT_DIR" 2>/dev/null || { - echo "- Warning: Failed to unmount cleanly" - umount -l "$MNT_DIR" 2>/dev/null - } - echo "- Image unmounted" -fi - -echo "- Uninstall complete" - -exit 0 diff --git a/userspace/meta-overlayfs/metamodule/update.json b/userspace/meta-overlayfs/metamodule/update.json deleted file mode 100644 index de2f7d67..00000000 --- a/userspace/meta-overlayfs/metamodule/update.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "version": "1.1.0", - "versionCode": 1100, - "zipUrl": "https://github.com/tiann/KernelSU/releases/download/v2.1.2/meta-overlayfs-v1.1.0.zip", - "changelog": "https://github.com/tiann/KernelSU/releases/download/v2.1.2/meta-overlayfs-changelog.md" -} diff --git a/userspace/meta-overlayfs/src/defs.rs b/userspace/meta-overlayfs/src/defs.rs deleted file mode 100644 index d54c5e67..00000000 --- a/userspace/meta-overlayfs/src/defs.rs +++ /dev/null @@ -1,17 +0,0 @@ -// Constants for KernelSU module mounting - -// Dual-directory support for ext4 image -pub const MODULE_METADATA_DIR: &str = "/data/adb/modules/"; -pub const MODULE_CONTENT_DIR: &str = "/data/adb/metamodule/mnt/"; - -// Legacy constant (for backwards compatibility) -pub const _MODULE_DIR: &str = "/data/adb/modules/"; - -// Status marker files -pub const DISABLE_FILE_NAME: &str = "disable"; -pub const _REMOVE_FILE_NAME: &str = "remove"; -pub const SKIP_MOUNT_FILE_NAME: &str = "skip_mount"; - -// System directories -pub const SYSTEM_RW_DIR: &str = "/data/adb/modules/.rw/"; -pub const KSU_OVERLAY_SOURCE: &str = "KSU"; diff --git a/userspace/meta-overlayfs/src/main.rs b/userspace/meta-overlayfs/src/main.rs deleted file mode 100644 index 2d764f07..00000000 --- a/userspace/meta-overlayfs/src/main.rs +++ /dev/null @@ -1,35 +0,0 @@ -use anyhow::Result; -use log::info; - -mod defs; -mod mount; -mod xcp; - -fn main() -> Result<()> { - let args: Vec = std::env::args().collect(); - if matches!(args.get(1), Some(cmd) if cmd == "xcp") { - return xcp::run(&args[2..]); - } - - // Initialize logger - env_logger::builder() - .filter_level(log::LevelFilter::Info) - .init(); - - info!("meta-overlayfs v{}", env!("CARGO_PKG_VERSION")); - - // Dual-directory support: metadata + content - let metadata_dir = std::env::var("MODULE_METADATA_DIR") - .unwrap_or_else(|_| defs::MODULE_METADATA_DIR.to_string()); - let content_dir = std::env::var("MODULE_CONTENT_DIR") - .unwrap_or_else(|_| defs::MODULE_CONTENT_DIR.to_string()); - - info!("Metadata directory: {}", metadata_dir); - info!("Content directory: {}", content_dir); - - // Execute dual-directory mounting - mount::mount_modules_systemlessly(&metadata_dir, &content_dir)?; - - info!("Mount completed successfully"); - Ok(()) -} diff --git a/userspace/meta-overlayfs/src/mount.rs b/userspace/meta-overlayfs/src/mount.rs deleted file mode 100644 index 07fd92f6..00000000 --- a/userspace/meta-overlayfs/src/mount.rs +++ /dev/null @@ -1,376 +0,0 @@ -// Overlayfs mounting implementation -// Migrated from ksud/src/mount.rs and ksud/src/init_event.rs - -use anyhow::{Context, Result, bail}; -use log::{info, warn}; -use std::collections::HashMap; -use std::path::{Path, PathBuf}; - -#[cfg(any(target_os = "linux", target_os = "android"))] -use procfs::process::Process; -#[cfg(any(target_os = "linux", target_os = "android"))] -use rustix::{fd::AsFd, fs::CWD, mount::*}; - -use crate::defs::{DISABLE_FILE_NAME, KSU_OVERLAY_SOURCE, SKIP_MOUNT_FILE_NAME, SYSTEM_RW_DIR}; - -#[cfg(any(target_os = "linux", target_os = "android"))] -pub fn mount_overlayfs( - lower_dirs: &[String], - lowest: &str, - upperdir: Option, - workdir: Option, - dest: impl AsRef, -) -> Result<()> { - let lowerdir_config = lower_dirs - .iter() - .map(|s| s.as_ref()) - .chain(std::iter::once(lowest)) - .collect::>() - .join(":"); - info!( - "mount overlayfs on {:?}, lowerdir={}, upperdir={:?}, workdir={:?}", - dest.as_ref(), - lowerdir_config, - upperdir, - workdir - ); - - let upperdir = upperdir - .filter(|up| up.exists()) - .map(|e| e.display().to_string()); - let workdir = workdir - .filter(|wd| wd.exists()) - .map(|e| e.display().to_string()); - - let result = (|| { - let fs = fsopen("overlay", FsOpenFlags::FSOPEN_CLOEXEC)?; - let fs = fs.as_fd(); - fsconfig_set_string(fs, "lowerdir", &lowerdir_config)?; - if let (Some(upperdir), Some(workdir)) = (&upperdir, &workdir) { - fsconfig_set_string(fs, "upperdir", upperdir)?; - fsconfig_set_string(fs, "workdir", workdir)?; - } - 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, - ) - })(); - - if let Err(e) = result { - warn!("fsopen mount failed: {e:#}, fallback to mount"); - let mut data = format!("lowerdir={lowerdir_config}"); - if let (Some(upperdir), Some(workdir)) = (upperdir, workdir) { - data = format!("{data},upperdir={upperdir},workdir={workdir}"); - } - mount( - KSU_OVERLAY_SOURCE, - dest.as_ref(), - "overlay", - MountFlags::empty(), - data, - )?; - } - Ok(()) -} - -#[cfg(any(target_os = "linux", target_os = "android"))] -pub fn bind_mount(from: impl AsRef, to: impl AsRef) -> Result<()> { - info!( - "bind mount {} -> {}", - from.as_ref().display(), - to.as_ref().display() - ); - let tree = open_tree( - CWD, - from.as_ref(), - OpenTreeFlags::OPEN_TREE_CLOEXEC - | OpenTreeFlags::OPEN_TREE_CLONE - | OpenTreeFlags::AT_RECURSIVE, - )?; - move_mount( - tree.as_fd(), - "", - CWD, - to.as_ref(), - MoveMountFlags::MOVE_MOUNT_F_EMPTY_PATH, - )?; - Ok(()) -} - -#[cfg(any(target_os = "linux", target_os = "android"))] -fn mount_overlay_child( - mount_point: &str, - relative: &String, - module_roots: &Vec, - 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 = 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, None, None, mount_point) { - warn!("failed: {e:#}, fallback to bind mount"); - bind_mount(stock_root, mount_point)?; - } - Ok(()) -} - -#[cfg(any(target_os = "linux", target_os = "android"))] -pub fn mount_overlay( - root: &String, - module_roots: &Vec, - workdir: Option, - upperdir: Option, -) -> Result<()> { - info!("mount overlay for {root}"); - std::env::set_current_dir(root).with_context(|| format!("failed to chdir to {root}"))?; - let stock_root = "."; - - // collect child mounts before mounting the root - let mounts = Process::myself()? - .mountinfo() - .with_context(|| "get mountinfo")?; - let mut mount_seq = mounts - .0 - .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::>(); - mount_seq.sort(); - mount_seq.dedup(); - - mount_overlayfs(module_roots, root, upperdir, workdir, 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 {mount_point}: {e:#}, revert"); - umount_dir(root).with_context(|| format!("failed to revert {root}"))?; - bail!(e); - } - } - Ok(()) -} - -#[cfg(any(target_os = "linux", target_os = "android"))] -pub fn umount_dir(src: impl AsRef) -> Result<()> { - unmount(src.as_ref(), UnmountFlags::empty()) - .with_context(|| format!("Failed to umount {}", src.as_ref().display()))?; - Ok(()) -} - -#[cfg(not(any(target_os = "linux", target_os = "android")))] -pub fn mount_overlay( - _root: &String, - _module_roots: &Vec, - _workdir: Option, - _upperdir: Option, -) -> Result<()> { - unimplemented!("mount_overlay is only supported on Linux/Android") -} - -#[cfg(not(any(target_os = "linux", target_os = "android")))] -pub fn mount_overlayfs( - _lower_dirs: &[String], - _lowest: &str, - _upperdir: Option, - _workdir: Option, - _dest: impl AsRef, -) -> Result<()> { - unimplemented!("mount_overlayfs is only supported on Linux/Android") -} - -#[cfg(not(any(target_os = "linux", target_os = "android")))] -pub fn bind_mount(_from: impl AsRef, _to: impl AsRef) -> Result<()> { - unimplemented!("bind_mount is only supported on Linux/Android") -} - -// ========== Mount coordination logic (from init_event.rs) ========== - -#[cfg(any(target_os = "linux", target_os = "android"))] -fn mount_partition(partition_name: &str, lowerdir: &Vec) -> Result<()> { - if lowerdir.is_empty() { - warn!("partition: {partition_name} lowerdir is empty"); - return Ok(()); - } - - let partition = format!("/{partition_name}"); - - // if /partition is a symlink and linked to /system/partition, then we don't need to overlay it separately - if Path::new(&partition).read_link().is_ok() { - warn!("partition: {partition} is a symlink"); - return Ok(()); - } - - let mut workdir = None; - let mut upperdir = None; - let system_rw_dir = Path::new(SYSTEM_RW_DIR); - if system_rw_dir.exists() { - workdir = Some(system_rw_dir.join(partition_name).join("workdir")); - upperdir = Some(system_rw_dir.join(partition_name).join("upperdir")); - } - - mount_overlay(&partition, lowerdir, workdir, upperdir) -} - -/// Collect enabled module IDs from metadata directory -/// -/// Reads module list and status from metadata directory, returns enabled module IDs -#[cfg(any(target_os = "linux", target_os = "android"))] -fn collect_enabled_modules(metadata_dir: &str) -> Result> { - let dir = std::fs::read_dir(metadata_dir) - .with_context(|| format!("Failed to read metadata directory: {}", metadata_dir))?; - - let mut enabled = Vec::new(); - - for entry in dir.flatten() { - let path = entry.path(); - if !path.is_dir() { - continue; - } - - let module_id = match entry.file_name().to_str() { - Some(id) => id.to_string(), - None => continue, - }; - - // Check status markers - if path.join(DISABLE_FILE_NAME).exists() { - info!("Module {} is disabled, skipping", module_id); - continue; - } - - if path.join(SKIP_MOUNT_FILE_NAME).exists() { - info!("Module {} has skip_mount, skipping", module_id); - continue; - } - - // Optional: verify module.prop exists - if !path.join("module.prop").exists() { - warn!("Module {} has no module.prop, skipping", module_id); - continue; - } - - info!("Module {} enabled", module_id); - enabled.push(module_id); - } - - Ok(enabled) -} - -/// Dual-directory version of mount_modules_systemlessly -/// -/// Parameters: -/// - metadata_dir: Metadata directory, stores module.prop, disable, skip_mount, etc. -/// - content_dir: Content directory, stores system/, vendor/ and other partition content (ext4 image mount point) -#[cfg(any(target_os = "linux", target_os = "android"))] -pub fn mount_modules_systemlessly(metadata_dir: &str, content_dir: &str) -> Result<()> { - info!("Scanning modules (dual-directory mode)"); - info!(" Metadata: {}", metadata_dir); - info!(" Content: {}", content_dir); - - // 1. Traverse metadata directory, collect enabled module IDs - let enabled_modules = collect_enabled_modules(metadata_dir)?; - - if enabled_modules.is_empty() { - info!("No enabled modules found"); - return Ok(()); - } - - info!("Found {} enabled module(s)", enabled_modules.len()); - - // 2. Initialize partition lowerdir lists - let partition = vec!["vendor", "product", "system_ext", "odm", "oem"]; - let mut system_lowerdir: Vec = Vec::new(); - let mut partition_lowerdir: HashMap> = HashMap::new(); - - for part in &partition { - partition_lowerdir.insert((*part).to_string(), Vec::new()); - } - - // 3. Read module content from content directory - for module_id in &enabled_modules { - let module_content_path = Path::new(content_dir).join(module_id); - - if !module_content_path.exists() { - warn!("Module {} has no content directory, skipping", module_id); - continue; - } - - info!("Processing module: {}", module_id); - - // Collect system partition - let system_path = module_content_path.join("system"); - if system_path.is_dir() { - system_lowerdir.push(system_path.display().to_string()); - info!(" + system/"); - } - - // Collect other partitions - for part in &partition { - let part_path = module_content_path.join(part); - if part_path.is_dir() - && let Some(v) = partition_lowerdir.get_mut(*part) - { - v.push(part_path.display().to_string()); - info!(" + {}/", part); - } - } - } - - // 4. Mount partitions - info!("Mounting partitions..."); - - if let Err(e) = mount_partition("system", &system_lowerdir) { - warn!("mount system failed: {e:#}"); - } - - for (k, v) in partition_lowerdir { - if let Err(e) = mount_partition(&k, &v) { - warn!("mount {k} failed: {e:#}"); - } - } - - info!("All partitions processed"); - Ok(()) -} - -#[cfg(not(any(target_os = "linux", target_os = "android")))] -pub fn mount_modules_systemlessly(_metadata_dir: &str, _content_dir: &str) -> Result<()> { - unimplemented!("mount_modules_systemlessly is only supported on Linux/Android") -} diff --git a/userspace/meta-overlayfs/src/xcp.rs b/userspace/meta-overlayfs/src/xcp.rs deleted file mode 100644 index 7593b776..00000000 --- a/userspace/meta-overlayfs/src/xcp.rs +++ /dev/null @@ -1,90 +0,0 @@ -use anyhow::{bail, Context, Result}; -use hole_punch::*; -use std::{ - fs::{File, OpenOptions}, - io::{Read, Seek, SeekFrom, Write}, - path::Path, -}; - -/// Handle the `xcp` command: copy sparse file with optional hole punching. -pub fn run(args: &[String]) -> Result<()> { - let mut positional: Vec<&str> = Vec::with_capacity(2); - let mut punch_hole = false; - - for arg in args { - match arg.as_str() { - "--punch-hole" => punch_hole = true, - "-h" | "--help" => { - print_usage(); - return Ok(()); - } - _ => positional.push(arg), - } - } - - if positional.len() < 2 { - print_usage(); - bail!("xcp requires source and destination paths"); - } - if positional.len() > 2 { - bail!("unexpected argument: {}", positional[2]); - } - - copy_sparse_file(positional[0], positional[1], punch_hole) -} - -fn print_usage() { - eprintln!("Usage: meta-overlayfs xcp [--punch-hole]"); -} - -// TODO: use libxcp to improve the speed if cross's MSRV is 1.70 -pub fn copy_sparse_file, Q: AsRef>( - src: P, - dst: Q, - punch_hole: bool, -) -> Result<()> { - let mut src_file = File::open(src.as_ref()) - .with_context(|| format!("failed to open {}", src.as_ref().display()))?; - let mut dst_file = OpenOptions::new() - .write(true) - .create(true) - .truncate(true) - .open(dst.as_ref()) - .with_context(|| format!("failed to open {}", dst.as_ref().display()))?; - - dst_file.set_len(src_file.metadata()?.len())?; - - let segments = src_file.scan_chunks()?; - for segment in segments { - if let SegmentType::Data = segment.segment_type { - let start = segment.start; - let end = segment.end + 1; - - src_file.seek(SeekFrom::Start(start))?; - dst_file.seek(SeekFrom::Start(start))?; - - let mut buffer = [0; 4096]; - let mut total_bytes_copied = 0; - - while total_bytes_copied < end - start { - let bytes_to_read = - std::cmp::min(buffer.len() as u64, end - start - total_bytes_copied); - let bytes_read = src_file.read(&mut buffer[..bytes_to_read as usize])?; - - if bytes_read == 0 { - break; - } - - if punch_hole && buffer[..bytes_read].iter().all(|&x| x == 0) { - dst_file.seek(SeekFrom::Current(bytes_read as i64))?; - total_bytes_copied += bytes_read as u64; - continue; - } - dst_file.write_all(&buffer[..bytes_read])?; - total_bytes_copied += bytes_read as u64; - } - } - } - - Ok(()) -}