From a7c17caebdb2ea8708e10c391c9ca4f67d297e26 Mon Sep 17 00:00:00 2001 From: Moritz Bitsch Date: Mon, 14 Jun 2021 18:07:37 +0200 Subject: [PATCH] Initial import --- .gitignore | 2 + Cargo.toml | 10 ++++ src/lib.rs | 131 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 143 insertions(+) create mode 100644 .gitignore create mode 100644 Cargo.toml create mode 100644 src/lib.rs diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..96ef6c0 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +/target +Cargo.lock diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..853aa22 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,10 @@ +[package] +name = "e2size" +version = "0.1.0" +authors = ["Moritz Bitsch"] +edition = "2018" + +[dependencies] +byteorder = "1.4.3" +positioned-io = "0.2.2" +thiserror = "1.0.25" \ No newline at end of file diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000..e5f8905 --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,131 @@ +use byteorder::{ByteOrder, LittleEndian}; +use positioned_io::ReadAt; +use thiserror::Error; + +#[derive(Error, Debug)] +/// Errors returned by e2size +pub enum E2SizeError { + /// Error reading superblock + #[error("Can't read superblock")] + Read(#[from] std::io::Error), + + /// Bad magic value of superblock (corrupt fs?) + #[error("Invalid magic value (expected: 0xEF53 got: 0x{0:X})")] + BadMagic(u16), + + /// Blocksize was outside allowed range (corrupt fs?) + #[error("Invalid block size: {0}")] + InvalidBlockSize(u32), + + /// Filesystem outside seekable range (corrupt fs?) + #[error("Filesystem too Big")] + FilesystemTooBig, +} + +/// Retrieves the size of a ext2/3/4 filesystem in bytes +/// +/// Reads the superblock (1024 bytes at 1024 byte offset) from reader +pub fn e2size(reader: T) -> std::result::Result +where + T: ReadAt, +{ + let mut superblock = [0u8; 1024]; + reader.read_exact_at(1024, &mut superblock)?; + + let magic = LittleEndian::read_u16(&superblock[0x38..]); + if magic != 0xEF53 { + return Err(E2SizeError::BadMagic(magic)); + } + + let block_count = LittleEndian::read_u32(&superblock[0x4..]); + + let block_size_shift = LittleEndian::read_u32(&superblock[0x18..]); + let block_size = 1u64 + .checked_shl(10 + block_size_shift) + .ok_or_else(|| E2SizeError::InvalidBlockSize(block_size_shift))?; + + (block_size as u64) + .checked_mul(block_count as u64) + .ok_or_else(|| E2SizeError::FilesystemTooBig) +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + #[cfg(not(tarpaulin_include))] + fn valid_superblock() -> Result<(), E2SizeError> { + let mut superblock = [0u8; 2048]; + LittleEndian::write_u16(&mut superblock[1024 + 0x38..], 0xEF53); + LittleEndian::write_u32(&mut superblock[1024 + 0x4..], 256); + LittleEndian::write_u32(&mut superblock[1024 + 0x18..], 2); + + assert_eq!(e2size(&superblock[..])?, 1048576); + + Ok(()) + } + + #[test] + #[cfg(not(tarpaulin_include))] + fn huge_filesystem() -> Result<(), E2SizeError> { + let mut superblock = [0u8; 2048]; + LittleEndian::write_u16(&mut superblock[1024 + 0x38..], 0xEF53); + LittleEndian::write_u32(&mut superblock[1024 + 0x4..], 1 << 31); + LittleEndian::write_u32(&mut superblock[1024 + 0x18..], 22); + + assert_eq!(e2size(&superblock[..])?, 9223372036854775808); + + Ok(()) + } + + #[test] + #[cfg(not(tarpaulin_include))] + fn invalid_magic() -> Result<(), E2SizeError> { + let mut superblock = [0u8; 2048]; + LittleEndian::write_u16(&mut superblock[1024 + 0x38..], 0xEF52); + LittleEndian::write_u32(&mut superblock[1024 + 0x4..], 1); + LittleEndian::write_u32(&mut superblock[1024 + 0x18..], 0); + + match e2size(&superblock[..]) { + Err(E2SizeError::BadMagic(e)) => assert_eq!(e, 0xEF52), + _ => panic!("not reached!"), + }; + + Ok(()) + } + + #[test] + #[cfg(not(tarpaulin_include))] + fn invalid_block_size() -> Result<(), E2SizeError> { + let mut superblock = [0u8; 2048]; + LittleEndian::write_u16(&mut superblock[1024 + 0x38..], 0xEF53); + LittleEndian::write_u32(&mut superblock[1024 + 0x4..], 1); + LittleEndian::write_u32(&mut superblock[1024 + 0x18..], 54); + + match e2size(&superblock[..]) { + Err(E2SizeError::InvalidBlockSize(_)) => {} + Err(e) => panic!("not expected error: {}", e), + _ => panic!("not reached!"), + }; + + Ok(()) + } + + #[test] + #[cfg(not(tarpaulin_include))] + fn filesystem_too_big() -> Result<(), E2SizeError> { + let mut superblock = [0u8; 2048]; + LittleEndian::write_u16(&mut superblock[1024 + 0x38..], 0xEF53); + LittleEndian::write_u32(&mut superblock[1024 + 0x4..], 1 << 31); + LittleEndian::write_u32(&mut superblock[1024 + 0x18..], 23); + + match e2size(&superblock[..]) { + Err(E2SizeError::FilesystemTooBig) => {} + Err(e) => panic!("not expected error: {}", e), + _ => panic!("not reached!"), + }; + + Ok(()) + } +}