From 9474c0752680e209b2c97cc0b4d94bcc9947cd62 Mon Sep 17 00:00:00 2001 From: Anatol Belski Date: Fri, 17 Jul 2026 16:13:40 +0200 Subject: [PATCH] block: sparse: Add unit tests for write_zeroes and punch_hole fallback Exercise the EOPNOTSUPP fallback path using memfd, which shares the same backing as tmpfs and triggers the fallback without requiring privileges or a specific filesystem. Signed-off-by: Anatol Belski --- block/src/sparse.rs | 52 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 52 insertions(+) diff --git a/block/src/sparse.rs b/block/src/sparse.rs index 55787fcc6..2f7e13d0a 100644 --- a/block/src/sparse.rs +++ b/block/src/sparse.rs @@ -105,3 +105,55 @@ pub(crate) fn write_zeroes( Ok(()) } +#[cfg(test)] +mod tests { + use std::ffi::CString; + use std::fs::File; + use std::os::unix::fs::FileExt; + use std::os::unix::io::FromRawFd; + + use super::*; + + // Create a memfd backed file. memfd uses the same backing as tmpfs, + // so fallocate with ZERO_RANGE and PUNCH_HOLE returns EOPNOTSUPP, + // exercising the fallback path. + fn memfd_file() -> AlignedFile { + let name = CString::new("ch_sparse_test").unwrap(); + // SAFETY: memfd_create is a safe syscall that returns a new fd. + let fd = unsafe { libc::memfd_create(name.as_ptr(), 0) }; + assert!(fd >= 0, "memfd_create failed"); + // SAFETY: fd is a valid open file descriptor we just created. + let f = unsafe { File::from_raw_fd(fd) }; + AlignedFile::new(f, false) + } + + #[test] + fn write_zeroes_fallback() { + let mut f = memfd_file(); + f.file().write_all_at(&vec![0xAA; 4096], 0).unwrap(); + + write_zeroes(&mut f, false, 512, 1024).unwrap(); + + let mut buf = vec![0xFFu8; 4096]; + f.file().read_exact_at(&mut buf, 0).unwrap(); + + assert!(buf[..512].iter().all(|&b| b == 0xAA)); + assert!(buf[512..1536].iter().all(|&b| b == 0)); + assert!(buf[1536..].iter().all(|&b| b == 0xAA)); + } + + #[test] + fn punch_hole_fallback() { + let mut f = memfd_file(); + f.file().write_all_at(&vec![0xBB; 4096], 0).unwrap(); + + punch_hole(&mut f, false, 1024, 2048).unwrap(); + + let mut buf = vec![0xFFu8; 4096]; + f.file().read_exact_at(&mut buf, 0).unwrap(); + + assert!(buf[..1024].iter().all(|&b| b == 0xBB)); + assert!(buf[1024..3072].iter().all(|&b| b == 0)); + assert!(buf[3072..].iter().all(|&b| b == 0xBB)); + } +}