ksud: use sparse image to avoiding resize image. close #1220
This commit is contained in:
@@ -3,7 +3,7 @@ use crate::utils::*;
|
||||
use crate::{
|
||||
assets, defs, mount,
|
||||
restorecon::{restore_syscon, setsyscon},
|
||||
sepolicy,
|
||||
sepolicy, utils,
|
||||
};
|
||||
|
||||
use anyhow::{anyhow, bail, ensure, Context, Result};
|
||||
@@ -14,7 +14,7 @@ use log::{info, warn};
|
||||
use std::{
|
||||
collections::HashMap,
|
||||
env::var as env_var,
|
||||
fs::{remove_dir_all, remove_file, set_permissions, File, Permissions},
|
||||
fs::{remove_dir_all, remove_file, set_permissions, File, OpenOptions, Permissions},
|
||||
io::Cursor,
|
||||
path::{Path, PathBuf},
|
||||
process::{Command, Stdio},
|
||||
@@ -118,25 +118,6 @@ fn foreach_active_module(f: impl FnMut(&Path) -> Result<()>) -> Result<()> {
|
||||
foreach_module(true, f)
|
||||
}
|
||||
|
||||
fn get_minimal_image_size(img: &str) -> Result<u64> {
|
||||
check_image(img)?;
|
||||
|
||||
let output = Command::new("resize2fs")
|
||||
.args(["-P", img])
|
||||
.stdout(Stdio::piped())
|
||||
.output()?;
|
||||
|
||||
let output = String::from_utf8_lossy(&output.stdout);
|
||||
println!("- {}", output.trim());
|
||||
let regex = regex::Regex::new(r"filesystem: (\d+)")?;
|
||||
let result = regex
|
||||
.captures(&output)
|
||||
.ok_or(anyhow::anyhow!("regex not match"))?;
|
||||
let result = &result[1];
|
||||
let result = u64::from_str(result)?;
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
fn check_image(img: &str) -> Result<()> {
|
||||
let result = Command::new("e2fsck")
|
||||
.args(["-yf", img])
|
||||
@@ -157,31 +138,6 @@ fn check_image(img: &str) -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn grow_image_size(img: &str, extra_size: u64) -> Result<()> {
|
||||
let minimal_size = get_minimal_image_size(img)?; // the minimal size is in KB
|
||||
let target_size = minimal_size * 1024 + extra_size;
|
||||
|
||||
// check image
|
||||
check_image(img)?;
|
||||
|
||||
println!(
|
||||
"- Target image size: {}",
|
||||
humansize::format_size(target_size, humansize::DECIMAL)
|
||||
);
|
||||
let target_size = target_size / 1024 + 1024;
|
||||
info!("resize image to {target_size}K, minimal size is {minimal_size}K");
|
||||
let result = Command::new("resize2fs")
|
||||
.args([img, &format!("{target_size}K")])
|
||||
.stdout(Stdio::piped())
|
||||
.status()
|
||||
.with_context(|| format!("Failed to exec resize2fs {img}"))?;
|
||||
ensure!(result.success(), "Failed to resize2fs: {}", result);
|
||||
|
||||
check_image(img)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn load_sepolicy_rule() -> Result<()> {
|
||||
foreach_active_module(|path| {
|
||||
let rule_file = path.join("sepolicy.rule");
|
||||
@@ -370,18 +326,12 @@ fn _install_module(zip: &str) -> Result<()> {
|
||||
std::fs::remove_file(tmp_module_path)?;
|
||||
}
|
||||
|
||||
let default_reserve_size = 256 * 1024 * 1024;
|
||||
let zip_uncompressed_size = get_zip_uncompressed_size(zip)?;
|
||||
let grow_size = default_reserve_size + zip_uncompressed_size;
|
||||
|
||||
info!(
|
||||
"zip uncompressed size: {}",
|
||||
humansize::format_size(zip_uncompressed_size, humansize::DECIMAL)
|
||||
);
|
||||
info!(
|
||||
"grow size: {}",
|
||||
humansize::format_size(grow_size, humansize::DECIMAL)
|
||||
);
|
||||
|
||||
println!("- Preparing image");
|
||||
println!(
|
||||
@@ -389,14 +339,15 @@ fn _install_module(zip: &str) -> Result<()> {
|
||||
humansize::format_size(zip_uncompressed_size, humansize::DECIMAL)
|
||||
);
|
||||
|
||||
let sparse_image_size = 256 * (1 << 30); // 256G
|
||||
if !modules_img_exist && !modules_update_img_exist {
|
||||
// if no modules and modules_update, it is brand new installation, we should create a new img
|
||||
// create a tmp module img and mount it to modules_update
|
||||
info!("Creating brand new module image");
|
||||
File::create(tmp_module_img)
|
||||
.context("Failed to create ext4 image file")?
|
||||
.set_len(grow_size)
|
||||
.context("Failed to extend ext4 image")?;
|
||||
.set_len(sparse_image_size)
|
||||
.context("Failed to truncate ext4 image")?;
|
||||
|
||||
// format the img to ext4 filesystem
|
||||
let result = Command::new("mkfs.ext4")
|
||||
@@ -410,33 +361,45 @@ fn _install_module(zip: &str) -> Result<()> {
|
||||
"Failed to format ext4 image: {}",
|
||||
String::from_utf8(result.stderr).unwrap()
|
||||
);
|
||||
|
||||
check_image(tmp_module_img)?;
|
||||
} else if modules_update_img_exist {
|
||||
// modules_update.img exists, we should use it as tmp img
|
||||
info!("Using existing modules_update.img as tmp image");
|
||||
std::fs::copy(modules_update_img, tmp_module_img).with_context(|| {
|
||||
utils::copy_sparse_file(modules_update_img, tmp_module_img).with_context(|| {
|
||||
format!(
|
||||
"Failed to copy {} to {}",
|
||||
modules_update_img.display(),
|
||||
tmp_module_img
|
||||
)
|
||||
})?;
|
||||
// grow size of the tmp image
|
||||
grow_image_size(tmp_module_img, grow_size)?;
|
||||
} else {
|
||||
// modules.img exists, we should use it as tmp img
|
||||
info!("Using existing modules.img as tmp image");
|
||||
std::fs::copy(modules_img, tmp_module_img).with_context(|| {
|
||||
utils::copy_sparse_file(modules_img, tmp_module_img).with_context(|| {
|
||||
format!(
|
||||
"Failed to copy {} to {}",
|
||||
modules_img.display(),
|
||||
tmp_module_img
|
||||
)
|
||||
})?;
|
||||
// grow size of the tmp image
|
||||
grow_image_size(tmp_module_img, grow_size)?;
|
||||
|
||||
// legacy image, truncate it to new size.
|
||||
if std::fs::metadata(modules_img)?.len() < sparse_image_size {
|
||||
println!("- Truncate legacy image to new size");
|
||||
OpenOptions::new()
|
||||
.write(true)
|
||||
.open(tmp_module_img)
|
||||
.context("Failed to open ext4 image")?
|
||||
.set_len(sparse_image_size)
|
||||
.context("Failed to truncate ext4 image")?;
|
||||
|
||||
check_image(tmp_module_img)?;
|
||||
Command::new("resize2fs")
|
||||
.arg(tmp_module_img)
|
||||
.stdout(Stdio::piped())
|
||||
.status()?;
|
||||
}
|
||||
}
|
||||
check_image(tmp_module_img)?;
|
||||
|
||||
// ensure modules_update exists
|
||||
ensure_dir_exists(module_update_tmp_dir)?;
|
||||
@@ -473,7 +436,7 @@ fn _install_module(zip: &str) -> Result<()> {
|
||||
// all done, rename the tmp image to modules_update.img
|
||||
if std::fs::rename(tmp_module_img, defs::MODULE_UPDATE_IMG).is_err() {
|
||||
warn!("Rename image failed, try copy it.");
|
||||
std::fs::copy(tmp_module_img, defs::MODULE_UPDATE_IMG)
|
||||
utils::copy_sparse_file(tmp_module_img, defs::MODULE_UPDATE_IMG)
|
||||
.with_context(|| "Failed to copy image.".to_string())?;
|
||||
let _ = std::fs::remove_file(tmp_module_img);
|
||||
}
|
||||
@@ -513,14 +476,14 @@ where
|
||||
modules_update_img.display(),
|
||||
modules_update_tmp_img.display()
|
||||
);
|
||||
std::fs::copy(modules_update_img, modules_update_tmp_img)?;
|
||||
utils::copy_sparse_file(modules_update_img, modules_update_tmp_img)?;
|
||||
} else {
|
||||
info!(
|
||||
"copy {} to {}",
|
||||
modules_img.display(),
|
||||
modules_update_tmp_img.display()
|
||||
);
|
||||
std::fs::copy(modules_img, modules_update_tmp_img)?;
|
||||
utils::copy_sparse_file(modules_img, modules_update_tmp_img)?;
|
||||
}
|
||||
|
||||
// ensure modules_update dir exist
|
||||
@@ -534,7 +497,7 @@ where
|
||||
|
||||
if let Err(e) = std::fs::rename(modules_update_tmp_img, defs::MODULE_UPDATE_IMG) {
|
||||
warn!("Rename image failed: {e}, try copy it.");
|
||||
std::fs::copy(modules_update_tmp_img, defs::MODULE_UPDATE_IMG)
|
||||
utils::copy_sparse_file(modules_update_tmp_img, defs::MODULE_UPDATE_IMG)
|
||||
.with_context(|| "Failed to copy image.".to_string())?;
|
||||
let _ = std::fs::remove_file(modules_update_tmp_img);
|
||||
}
|
||||
|
||||
@@ -12,6 +12,9 @@ use std::fs::{set_permissions, Permissions};
|
||||
#[cfg(unix)]
|
||||
use std::os::unix::prelude::PermissionsExt;
|
||||
|
||||
use hole_punch::*;
|
||||
use std::io::{Read, Seek, SeekFrom};
|
||||
|
||||
pub fn ensure_clean_dir(dir: &str) -> Result<()> {
|
||||
let path = Path::new(dir);
|
||||
log::debug!("ensure_clean_dir: {}", path.display());
|
||||
@@ -182,3 +185,44 @@ pub fn get_tmp_path() -> &'static str {
|
||||
}
|
||||
""
|
||||
}
|
||||
|
||||
// TODO: use libxcp to improve the speed if cross's MSRV is 1.70
|
||||
pub fn copy_sparse_file<P: AsRef<Path>, Q: AsRef<Path>>(src: P, dst: Q) -> Result<()> {
|
||||
let mut src_file = File::open(src.as_ref())?;
|
||||
let mut dst_file = OpenOptions::new()
|
||||
.write(true)
|
||||
.create(true)
|
||||
.truncate(true)
|
||||
.open(dst.as_ref())?;
|
||||
|
||||
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;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
dst_file.write_all(&buffer[..bytes_read])?;
|
||||
total_bytes_copied += bytes_read as u64;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user