ksud: reclaim sparse space when install/uninstall modules. close #1367

This commit is contained in:
weishu
2024-02-19 15:27:27 +08:00
parent e0802b0d15
commit e0e7058d14
3 changed files with 101 additions and 0 deletions

View File

@@ -231,3 +231,88 @@ pub fn copy_sparse_file<P: AsRef<Path>, Q: AsRef<Path>>(src: P, dst: Q) -> Resul
Ok(())
}
pub fn punch_hole(src: impl AsRef<Path>) -> Result<()> {
let mut src_file = OpenOptions::new().write(true).read(true).open(src)?;
let st = rustix::fs::fstat(&src_file)?;
let bufsz = st.st_blksize;
let mut buf = vec![0u8; bufsz as usize];
let mut ct = 0;
let mut hole_sz = 0;
let mut hole_start = 0;
let segments = src_file.scan_chunks()?;
for segment in segments {
if segment.segment_type != SegmentType::Data {
continue;
}
let mut off = segment.start;
let end = segment.end + 1;
while off < end {
let mut rsz = rustix::io::pread(&src_file, &mut buf, off)? as u64;
if rsz > 0 && rsz > end - off {
// exceed the end of the boundary
rsz = end - off;
}
if rsz == 0 {
break;
}
if buf.iter().all(|&x| x == 0) {
// the whole buf is zero, mark it as a hole
if hole_sz == 0 {
hole_start = off;
}
// for continuous zero, we can merge them into a bigger hole
hole_sz += rsz;
} else if hole_sz > 0 {
if let Err(e) = rustix::fs::fallocate(
&src_file,
rustix::fs::FallocateFlags::PUNCH_HOLE | rustix::fs::FallocateFlags::KEEP_SIZE,
hole_start,
hole_sz,
) {
log::warn!("Failed to punch hole: {:?}", e);
}
ct += hole_sz;
hole_sz = 0;
hole_start = 0;
}
off += rsz;
}
// if the last segment is a hole, we need to punch it
if hole_sz > 0 {
let mut alloc_sz = hole_sz;
if off >= end {
alloc_sz += st.st_blksize as u64;
}
if let Err(e) = rustix::fs::fallocate(
&src_file,
rustix::fs::FallocateFlags::PUNCH_HOLE | rustix::fs::FallocateFlags::KEEP_SIZE,
hole_start,
alloc_sz,
) {
log::warn!("Failed to punch hole: {:?}", e);
}
ct += hole_sz;
}
}
log::info!(
"Punched {} of hole",
humansize::format_size(ct, humansize::DECIMAL)
);
Ok(())
}