From 2073fa3d310587a04b9a7d3da1e87275e60ba2a8 Mon Sep 17 00:00:00 2001 From: SF-Zhou Date: Sun, 18 Jan 2026 20:41:05 +0800 Subject: [PATCH] bump version to v0.1.1 1. add read_exact option --- Cargo.lock | 2 +- Cargo.toml | 2 +- src/options.rs | 25 ++++++++++++++++++++++++- src/reader.rs | 34 ++++++++++++++++++++++++++++++++-- 4 files changed, 58 insertions(+), 5 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index dcc9f68..443fcb8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -81,7 +81,7 @@ dependencies = [ [[package]] name = "blkreader" -version = "0.1.0" +version = "0.1.1" dependencies = [ "blkmap", "blkpath", diff --git a/Cargo.toml b/Cargo.toml index ef7e3ec..b29308e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "blkreader" -version = "0.1.0" +version = "0.1.1" edition = "2021" authors = ["SF-Zhou"] description = "Read file data directly from block device using extent information" diff --git a/src/options.rs b/src/options.rs index e54d8dd..e31ab23 100644 --- a/src/options.rs +++ b/src/options.rs @@ -32,6 +32,16 @@ pub struct Options { /// using regular file I/O instead of direct block device I/O. /// This avoids the need for root privileges in such cases. pub allow_fallback: bool, + + /// Require reading the exact requested length. + /// + /// When enabled, the read operation will return an error if the amount + /// of data read is less than the requested buffer size. + /// This is similar to the behavior of [`std::io::Read::read_exact`]. + /// + /// When disabled, partial reads are allowed and the actual number of + /// bytes read is returned (similar to [`std::io::Read::read`]). + pub read_exact: bool, } impl Default for Options { @@ -41,6 +51,7 @@ impl Default for Options { fill_holes: false, zero_unwritten: false, allow_fallback: false, + read_exact: false, } } } @@ -77,6 +88,15 @@ impl Options { self.allow_fallback = allow; self } + + /// Enable or disable requiring exact read length. + /// + /// When enabled, the read will fail if less than the requested number of + /// bytes are read. When disabled, partial reads are allowed. + pub fn with_read_exact(mut self, exact: bool) -> Self { + self.read_exact = exact; + self + } } #[cfg(test)] @@ -90,6 +110,7 @@ mod tests { assert!(!opts.fill_holes); assert!(!opts.zero_unwritten); assert!(!opts.allow_fallback); + assert!(!opts.read_exact); } #[test] @@ -98,11 +119,13 @@ mod tests { .with_cache(false) .with_fill_holes(true) .with_zero_unwritten(true) - .with_allow_fallback(true); + .with_allow_fallback(true) + .with_read_exact(true); assert!(!opts.enable_cache); assert!(opts.fill_holes); assert!(opts.zero_unwritten); assert!(opts.allow_fallback); + assert!(opts.read_exact); } } diff --git a/src/reader.rs b/src/reader.rs index 749e52e..7de59b3 100644 --- a/src/reader.rs +++ b/src/reader.rs @@ -181,7 +181,14 @@ impl<'a> ReadContext<'a> { offset: u64, extents: Vec, ) -> io::Result { - let bytes_read = FileExt::read_at(self.file, buf, offset)?; + // Check if we read the exact requested length + let bytes_read = if self.options.read_exact { + self.file.read_exact_at(buf, offset)?; + buf.len() + } else { + self.file.read_at(buf, offset)? + }; + Ok(State::fallback(extents, bytes_read)) } @@ -305,6 +312,18 @@ impl<'a> ReadContext<'a> { } } + // Check if we read the exact requested length + if self.options.read_exact && bytes_read < buf.len() { + return Err(io::Error::new( + io::ErrorKind::UnexpectedEof, + format!( + "failed to fill entire buffer: expected {} bytes, got {} bytes", + buf.len(), + bytes_read + ), + )); + } + Ok(bytes_read) } } @@ -370,12 +389,14 @@ mod tests { .with_cache(false) .with_fill_holes(true) .with_zero_unwritten(true) - .with_allow_fallback(true); + .with_allow_fallback(true) + .with_read_exact(false); assert!(!opts.enable_cache); assert!(opts.fill_holes); assert!(opts.zero_unwritten); assert!(opts.allow_fallback); + assert!(!opts.read_exact); } #[test] @@ -416,4 +437,13 @@ mod tests { }]; assert!(!ctx.can_use_fallback(&extents, 0, 200)); } + + #[test] + fn test_read_exact_builder() { + let opts = Options::new().with_read_exact(false); + assert!(!opts.read_exact); + + let opts = opts.with_read_exact(true); + assert!(opts.read_exact); + } }