authfs: add unit tests for ChunkedFileReader
Now that Rust tests can run with data:
- Add unit tests for ChunkedFileReader
- Update other tests to use test files, instead of include_bytes!
- Remove `impl ReadOnlyDataByChunk for &[u8]` since it's no longer use.
Test: atest authfs_device_test_src_lib
Bug: 178874539
Change-Id: Idee833a2fd86aa9d8b8550b574686ea56158f93f
diff --git a/authfs/Android.bp b/authfs/Android.bp
index 270be6e..3fc4504 100644
--- a/authfs/Android.bp
+++ b/authfs/Android.bp
@@ -49,4 +49,16 @@
name: "authfs_device_test_src_lib",
defaults: ["authfs_defaults"],
test_suites: ["device-tests"],
+ data: [
+ "testdata/input.4k",
+ "testdata/input.4k.fsv_sig",
+ "testdata/input.4k.merkle_dump",
+ "testdata/input.4k1",
+ "testdata/input.4k1.fsv_sig",
+ "testdata/input.4k1.merkle_dump",
+ "testdata/input.4m",
+ "testdata/input.4m.fsv_sig",
+ "testdata/input.4m.merkle_dump",
+ "testdata/input.4m.merkle_dump.bad",
+ ],
}
diff --git a/authfs/src/fsverity.rs b/authfs/src/fsverity.rs
index 29e15d4..306c9d9 100644
--- a/authfs/src/fsverity.rs
+++ b/authfs/src/fsverity.rs
@@ -211,89 +211,93 @@
mod tests {
use super::*;
use crate::auth::FakeAuthenticator;
- use crate::reader::ReadOnlyDataByChunk;
+ use crate::reader::{ChunkedFileReader, ReadOnlyDataByChunk};
use anyhow::Result;
+ use std::fs::File;
+ use std::io::Read;
+
+ type LocalFsverityChunkedFileReader =
+ FsverityChunkedFileReader<ChunkedFileReader, ChunkedFileReader>;
fn total_chunk_number(file_size: u64) -> u64 {
(file_size + 4095) / 4096
}
+ // Returns a reader with fs-verity verification and the file size.
+ fn new_reader_with_fsverity(
+ content_path: &str,
+ merkle_tree_path: &str,
+ signature_path: &str,
+ ) -> Result<(LocalFsverityChunkedFileReader, u64)> {
+ let file_reader = ChunkedFileReader::new(File::open(content_path)?)?;
+ let file_size = file_reader.len();
+ let merkle_tree = ChunkedFileReader::new(File::open(merkle_tree_path)?)?;
+ let mut sig = Vec::new();
+ let _ = File::open(signature_path)?.read_to_end(&mut sig)?;
+ let authenticator = FakeAuthenticator::always_succeed();
+ Ok((
+ FsverityChunkedFileReader::new(
+ &authenticator,
+ file_reader,
+ file_size,
+ sig,
+ merkle_tree,
+ )?,
+ file_size,
+ ))
+ }
+
#[test]
fn fsverity_verify_full_read_4k() -> Result<()> {
- let file = &include_bytes!("../testdata/input.4k")[..];
- let merkle_tree = &include_bytes!("../testdata/input.4k.merkle_dump")[..];
- let sig = include_bytes!("../testdata/input.4k.fsv_sig").to_vec();
- let authenticator = FakeAuthenticator::always_succeed();
- let verified_file = FsverityChunkedFileReader::new(
- &authenticator,
- file,
- file.len() as u64,
- sig,
- merkle_tree,
+ let (file_reader, file_size) = new_reader_with_fsverity(
+ "testdata/input.4k",
+ "testdata/input.4k.merkle_dump",
+ "testdata/input.4k.fsv_sig",
)?;
- for i in 0..total_chunk_number(file.len() as u64) {
+ for i in 0..total_chunk_number(file_size) {
let mut buf = [0u8; 4096];
- assert!(verified_file.read_chunk(i, &mut buf[..]).is_ok());
+ assert!(file_reader.read_chunk(i, &mut buf[..]).is_ok());
}
Ok(())
}
#[test]
fn fsverity_verify_full_read_4k1() -> Result<()> {
- let file = &include_bytes!("../testdata/input.4k1")[..];
- let merkle_tree = &include_bytes!("../testdata/input.4k1.merkle_dump")[..];
- let sig = include_bytes!("../testdata/input.4k1.fsv_sig").to_vec();
- let authenticator = FakeAuthenticator::always_succeed();
- let verified_file = FsverityChunkedFileReader::new(
- &authenticator,
- file,
- file.len() as u64,
- sig,
- merkle_tree,
+ let (file_reader, file_size) = new_reader_with_fsverity(
+ "testdata/input.4k1",
+ "testdata/input.4k1.merkle_dump",
+ "testdata/input.4k1.fsv_sig",
)?;
- for i in 0..total_chunk_number(file.len() as u64) {
+ for i in 0..total_chunk_number(file_size) {
let mut buf = [0u8; 4096];
- assert!(verified_file.read_chunk(i, &mut buf[..]).is_ok());
+ assert!(file_reader.read_chunk(i, &mut buf[..]).is_ok());
}
Ok(())
}
#[test]
fn fsverity_verify_full_read_4m() -> Result<()> {
- let file = &include_bytes!("../testdata/input.4m")[..];
- let merkle_tree = &include_bytes!("../testdata/input.4m.merkle_dump")[..];
- let sig = include_bytes!("../testdata/input.4m.fsv_sig").to_vec();
- let authenticator = FakeAuthenticator::always_succeed();
- let verified_file = FsverityChunkedFileReader::new(
- &authenticator,
- file,
- file.len() as u64,
- sig,
- merkle_tree,
+ let (file_reader, file_size) = new_reader_with_fsverity(
+ "testdata/input.4m",
+ "testdata/input.4m.merkle_dump",
+ "testdata/input.4m.fsv_sig",
)?;
- for i in 0..total_chunk_number(file.len() as u64) {
+ for i in 0..total_chunk_number(file_size) {
let mut buf = [0u8; 4096];
- assert!(verified_file.read_chunk(i, &mut buf[..]).is_ok());
+ assert!(file_reader.read_chunk(i, &mut buf[..]).is_ok());
}
Ok(())
}
#[test]
fn fsverity_verify_bad_merkle_tree() -> Result<()> {
- let file = &include_bytes!("../testdata/input.4m")[..];
- // First leaf node is corrupted.
- let merkle_tree = &include_bytes!("../testdata/input.4m.merkle_dump.bad")[..];
- let sig = include_bytes!("../testdata/input.4m.fsv_sig").to_vec();
- let authenticator = FakeAuthenticator::always_succeed();
- let verified_file = FsverityChunkedFileReader::new(
- &authenticator,
- file,
- file.len() as u64,
- sig,
- merkle_tree,
+ let (file_reader, _) = new_reader_with_fsverity(
+ "testdata/input.4m",
+ "testdata/input.4m.merkle_dump.bad", // First leaf node is corrupted.
+ "testdata/input.4m.fsv_sig",
)?;
// A lowest broken node (a 4K chunk that contains 128 sha256 hashes) will fail the read
@@ -302,22 +306,23 @@
let num_hashes = 4096 / 32;
let last_index = num_hashes;
for i in 0..last_index {
- assert!(verified_file.read_chunk(i, &mut buf[..]).is_err());
+ assert!(file_reader.read_chunk(i, &mut buf[..]).is_err());
}
- assert!(verified_file.read_chunk(last_index, &mut buf[..]).is_ok());
+ assert!(file_reader.read_chunk(last_index, &mut buf[..]).is_ok());
Ok(())
}
#[test]
fn invalid_signature() -> Result<()> {
let authenticator = FakeAuthenticator::always_fail();
- let file = &include_bytes!("../testdata/input.4m")[..];
- let merkle_tree = &include_bytes!("../testdata/input.4m.merkle_dump")[..];
+ let file_reader = ChunkedFileReader::new(File::open("testdata/input.4m")?)?;
+ let file_size = file_reader.len();
+ let merkle_tree = ChunkedFileReader::new(File::open("testdata/input.4m.merkle_dump")?)?;
let sig = include_bytes!("../testdata/input.4m.fsv_sig").to_vec();
assert!(FsverityChunkedFileReader::new(
&authenticator,
- file,
- file.len() as u64,
+ file_reader,
+ file_size,
sig,
merkle_tree
)
diff --git a/authfs/src/main.rs b/authfs/src/main.rs
index f0b5237..46e6fd8 100644
--- a/authfs/src/main.rs
+++ b/authfs/src/main.rs
@@ -120,9 +120,8 @@
}
fn new_config_local_unverified_file(file_path: &PathBuf) -> Result<FileConfig> {
- let file = File::open(file_path)?;
- let file_size = file.metadata()?.len();
- let file_reader = ChunkedFileReader::new(file)?;
+ let file_reader = ChunkedFileReader::new(File::open(file_path)?)?;
+ let file_size = file_reader.len();
Ok(FileConfig::LocalUnverifiedFile(file_reader, file_size))
}
diff --git a/authfs/src/reader.rs b/authfs/src/reader.rs
index 2d1b617..d365a41 100644
--- a/authfs/src/reader.rs
+++ b/authfs/src/reader.rs
@@ -54,6 +54,10 @@
let size = file.metadata()?.len();
Ok(ChunkedFileReader { file, size })
}
+
+ pub fn len(&self) -> u64 {
+ self.size
+ }
}
impl ReadOnlyDataByChunk for ChunkedFileReader {
@@ -65,55 +69,57 @@
}
}
-impl ReadOnlyDataByChunk for &[u8] {
- fn read_chunk(&self, chunk_index: u64, buf: &mut [u8]) -> Result<usize> {
- debug_assert!(buf.len() as u64 >= Self::CHUNK_SIZE);
- let chunk = &self.chunks(Self::CHUNK_SIZE as usize).nth(chunk_index as usize).unwrap();
- buf[..chunk.len()].copy_from_slice(&chunk);
- Ok(chunk.len())
- }
-}
-
#[cfg(test)]
mod tests {
use super::*;
+ use std::env::temp_dir;
- fn test_reading_more_than_4kb_data<T: ReadOnlyDataByChunk>(
- reader: T,
- data_size: u64,
- ) -> Result<()> {
+ #[test]
+ fn test_read_4k_file() -> Result<()> {
+ let file_reader = ChunkedFileReader::new(File::open("testdata/input.4k")?)?;
let mut buf = [0u8; 4096];
- assert_eq!(reader.read_chunk(0, &mut buf)?, 4096);
- let last_index = (data_size + 4095) / 4096 - 1;
- assert_eq!(reader.read_chunk(last_index, &mut buf)?, (data_size % 4096) as usize);
+ let size = file_reader.read_chunk(0, &mut buf)?;
+ assert_eq!(size, buf.len());
Ok(())
}
- // TODO(victorhsieh): test ChunkedFileReader once there is a way to access testdata in the test
- // environement.
-
#[test]
- fn test_read_in_memory_data() -> Result<()> {
- let data = &[1u8; 5000][..];
- test_reading_more_than_4kb_data(data, data.len() as u64)
- }
-
- #[test]
- #[should_panic]
- #[allow(unused_must_use)]
- fn test_read_in_memory_empty_data() {
- let data = &[][..]; // zero length slice
+ fn test_read_4k1_file() -> Result<()> {
+ let file_reader = ChunkedFileReader::new(File::open("testdata/input.4k1")?)?;
let mut buf = [0u8; 4096];
- data.read_chunk(0, &mut buf); // should panic
+ let size = file_reader.read_chunk(0, &mut buf)?;
+ assert_eq!(size, buf.len());
+ let size = file_reader.read_chunk(1, &mut buf)?;
+ assert_eq!(size, 1);
+ Ok(())
+ }
+
+ #[test]
+ fn test_read_4m_file() -> Result<()> {
+ let file_reader = ChunkedFileReader::new(File::open("testdata/input.4m")?)?;
+ for index in 0..file_reader.len() / 4096 {
+ let mut buf = [0u8; 4096];
+ let size = file_reader.read_chunk(index, &mut buf)?;
+ assert_eq!(size, buf.len());
+ }
+ Ok(())
}
#[test]
#[should_panic]
- #[allow(unused_must_use)]
fn test_read_beyond_file_size() {
- let data = &[1u8; 5000][..];
+ let file_reader = ChunkedFileReader::new(File::open("testdata/input.4k").unwrap()).unwrap();
let mut buf = [0u8; 4096];
- let last_index_plus_1 = (data.len() + 4095) / 4096;
- data.read_chunk(last_index_plus_1 as u64, &mut buf); // should panic
+ let _ = file_reader.read_chunk(1u64, &mut buf); // should panic
+ }
+
+ #[test]
+ #[should_panic]
+ fn test_read_empty_file() {
+ let mut temp_file = temp_dir();
+ temp_file.push("authfs_test_empty_file");
+ let file_reader = ChunkedFileReader::new(File::create(temp_file).unwrap()).unwrap();
+ let mut buf = [0u8; 4096];
+ let _ = file_reader.read_chunk(0, &mut buf); // should panic
}
}