From 06c8580788c39ef509c42514f576cfe58d9cad2a Mon Sep 17 00:00:00 2001 From: weishu Date: Thu, 20 Nov 2025 14:17:07 +0800 Subject: [PATCH] metaovl: Use xcp to copy image faster. --- userspace/meta-overlayfs/Cargo.toml | 1 + .../meta-overlayfs/metamodule/customize.sh | 3 +- .../meta-overlayfs/metamodule/metainstall.sh | 12 +-- userspace/meta-overlayfs/src/main.rs | 6 ++ userspace/meta-overlayfs/src/xcp.rs | 90 +++++++++++++++++++ 5 files changed, 104 insertions(+), 8 deletions(-) create mode 100644 userspace/meta-overlayfs/src/xcp.rs diff --git a/userspace/meta-overlayfs/Cargo.toml b/userspace/meta-overlayfs/Cargo.toml index 820ea2f5..7160c4fd 100644 --- a/userspace/meta-overlayfs/Cargo.toml +++ b/userspace/meta-overlayfs/Cargo.toml @@ -10,6 +10,7 @@ license = "GPL-3.0" 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"] } diff --git a/userspace/meta-overlayfs/metamodule/customize.sh b/userspace/meta-overlayfs/metamodule/customize.sh index a7cdcd37..128a5bd6 100644 --- a/userspace/meta-overlayfs/metamodule/customize.sh +++ b/userspace/meta-overlayfs/metamodule/customize.sh @@ -43,13 +43,12 @@ ui_print "- Architecture-specific binary installed successfully" # Create ext4 image for module content storage IMG_FILE="$MODPATH/modules.img" -MNT_DIR="$MODPATH/mnt" IMG_SIZE_MB=2048 EXISTING_IMG="/data/adb/modules/$MODID/modules.img" if [ -f "$EXISTING_IMG" ]; then ui_print "- Reusing modules image from previous install" - cp "$EXISTING_IMG" "$IMG_FILE" || \ + "$MODPATH/meta-overlayfs" xcp "$EXISTING_IMG" "$IMG_FILE" || \ abort "! Failed to copy existing modules image" else ui_print "- Creating 2GB ext4 image for module storage" diff --git a/userspace/meta-overlayfs/metamodule/metainstall.sh b/userspace/meta-overlayfs/metamodule/metainstall.sh index b3ec411c..337260dd 100644 --- a/userspace/meta-overlayfs/metamodule/metainstall.sh +++ b/userspace/meta-overlayfs/metamodule/metainstall.sh @@ -42,7 +42,7 @@ module_requires_overlay_move() { post_install_to_image() { ui_print "- Copying module content to image" - set_perm_recursive $MNT_DIR 0 0 0755 0644 + set_perm_recursive "$MNT_DIR" 0 0 0755 0644 MOD_IMG_DIR="$MNT_DIR/$MODID" mkdir -p "$MOD_IMG_DIR" @@ -58,11 +58,11 @@ post_install_to_image() { done # Set permissions - set_perm_recursive $MOD_IMG_DIR 0 0 0755 0644 - set_perm_recursive $MOD_IMG_DIR/system/bin 0 2000 0755 0755 - set_perm_recursive $MOD_IMG_DIR/system/xbin 0 2000 0755 0755 - set_perm_recursive $MOD_IMG_DIR/system/system_ext/bin 0 2000 0755 0755 - set_perm_recursive $MOD_IMG_DIR/system/vendor 0 2000 0755 0755 u:object_r:vendor_file:s0 + set_perm_recursive "$MOD_IMG_DIR" 0 0 0755 0644 + set_perm_recursive "$MOD_IMG_DIR/system/bin" 0 2000 0755 0755 + set_perm_recursive "$MOD_IMG_DIR/system/xbin" 0 2000 0755 0755 + set_perm_recursive "$MOD_IMG_DIR/system/system_ext/bin" 0 2000 0755 0755 + set_perm_recursive "$MOD_IMG_DIR/system/vendor" 0 2000 0755 0755 u:object_r:vendor_file:s0 ui_print "- Module content copied" } diff --git a/userspace/meta-overlayfs/src/main.rs b/userspace/meta-overlayfs/src/main.rs index 4100e2af..2d764f07 100644 --- a/userspace/meta-overlayfs/src/main.rs +++ b/userspace/meta-overlayfs/src/main.rs @@ -3,8 +3,14 @@ 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) diff --git a/userspace/meta-overlayfs/src/xcp.rs b/userspace/meta-overlayfs/src/xcp.rs new file mode 100644 index 00000000..7593b776 --- /dev/null +++ b/userspace/meta-overlayfs/src/xcp.rs @@ -0,0 +1,90 @@ +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(()) +}