authfs: Reorganize modules & rename flags/classes/vars
This change adjusts the module/directory layout to make it easier to
reason.
- File "transports" are now under file/. Common traits are defined in
file.rs.
- All fs-verity related modules are now under fsverity/. This includes
VerifiedFileReader, which implements traits in the file module to
provide transparent verification.
- Rename structs for better consistency. Drop "Chunked" for
simplicity.
ChunkedFileReader -> LocalFileReader
RemoteChunkedFileReader -> RemoteFileReader
FsverityChunkedFileReader -> VerifiedFileReader
- Move and rename get_local_service from remote_file.rs to file.rs.
Also, rename command line flags and related class/field names in main.rs
so that later the name of new read-writable file can fit in more consistently.
New layours:
src/
|-- auth.rs
|-- common.rs
|-- crypto.hpp
|-- crypto.rs
|-- file.rs
|-- file
| |-- local_file.rs
| `-- remote_file.rs
|-- fsverity.rs
|-- fsverity
| |-- builder.rs
| |-- common.rs
| |-- sys.rs
| |-- verifier.rs
| `-- editor.rs
|-- fusefs.rs
`-- main.rs
Bug: 171279640
Test: atest
Change-Id: Ib257a37df89c6b813f4d97978678db3483d28b57
diff --git a/authfs/src/file.rs b/authfs/src/file.rs
new file mode 100644
index 0000000..1d52631
--- /dev/null
+++ b/authfs/src/file.rs
@@ -0,0 +1,49 @@
+mod local_file;
+mod remote_file;
+
+pub use local_file::LocalFileReader;
+pub use remote_file::{RemoteFileReader, RemoteMerkleTreeReader};
+
+use std::io;
+
+use authfs_aidl_interface::aidl::com::android::virt::fs::IVirtFdService;
+use authfs_aidl_interface::binder::{get_interface, Strong};
+
+// TODO(victorhsieh): use remote binder.
+pub fn get_local_binder() -> Strong<dyn IVirtFdService::IVirtFdService> {
+ let service_name = "authfs_fd_server";
+ get_interface(&service_name).expect("Cannot reach authfs_fd_server binder service")
+}
+
+/// A trait for reading data by chunks. The data is assumed readonly and has fixed length. Chunks
+/// can be read by specifying the chunk index. Only the last chunk may have incomplete chunk size.
+pub trait ReadOnlyDataByChunk {
+ /// Read the `chunk_index`-th chunk to `buf`. Each slice/chunk has size `CHUNK_SIZE` except for
+ /// the last one, which can be an incomplete chunk. `buf` is currently required to be large
+ /// enough to hold a full chunk of data. Reading beyond the file size (including empty file)
+ /// will crash.
+ fn read_chunk(&self, chunk_index: u64, buf: &mut [u8]) -> io::Result<usize>;
+}
+
+/// A trait to write a buffer to the destination at a given offset. The implementation does not
+/// necessarily own or maintain the destination state.
+///
+/// NB: The trait is required in a member of `fusefs::AuthFs`, which is required to be Sync and
+/// immutable (this the member).
+pub trait RandomWrite {
+ /// Writes `buf` to the destination at `offset`. Returns the written size, which may not be the
+ /// full buffer.
+ fn write_at(&self, buf: &[u8], offset: u64) -> io::Result<usize>;
+
+ /// Writes the full `buf` to the destination at `offset`.
+ fn write_all_at(&self, buf: &[u8], offset: u64) -> io::Result<()> {
+ let mut input_offset = 0;
+ let mut output_offset = offset;
+ while input_offset < buf.len() {
+ let size = self.write_at(&buf[input_offset..], output_offset)?;
+ input_offset += size;
+ output_offset += size as u64;
+ }
+ Ok(())
+ }
+}
diff --git a/authfs/src/reader.rs b/authfs/src/file/local_file.rs
similarity index 64%
rename from authfs/src/reader.rs
rename to authfs/src/file/local_file.rs
index 0242afa..0692767 100644
--- a/authfs/src/reader.rs
+++ b/authfs/src/file/local_file.rs
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2020 The Android Open Source Project
+ * Copyright (C) 2021 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,24 +14,13 @@
* limitations under the License.
*/
-//! A module for reading data by chunks.
-
use std::fs::File;
use std::io::Result;
use std::os::unix::fs::FileExt;
+use super::ReadOnlyDataByChunk;
use crate::common::CHUNK_SIZE;
-/// A trait for reading data by chunks. The data is assumed readonly and has fixed length. Chunks
-/// can be read by specifying the chunk index. Only the last chunk may have incomplete chunk size.
-pub trait ReadOnlyDataByChunk {
- /// Read the `chunk_index`-th chunk to `buf`. Each slice/chunk has size `CHUNK_SIZE` except for
- /// the last one, which can be an incomplete chunk. `buf` is currently required to be large
- /// enough to hold a full chunk of data. Reading beyond the file size (including empty file)
- /// will crash.
- fn read_chunk(&self, chunk_index: u64, buf: &mut [u8]) -> Result<usize>;
-}
-
fn chunk_index_to_range(size: u64, chunk_index: u64) -> Result<(u64, u64)> {
let start = chunk_index * CHUNK_SIZE;
assert!(start < size);
@@ -40,16 +29,16 @@
}
/// A read-only file that can be read by chunks.
-pub struct ChunkedFileReader {
+pub struct LocalFileReader {
file: File,
size: u64,
}
-impl ChunkedFileReader {
- /// Creates a `ChunkedFileReader` to read from for the specified `path`.
- pub fn new(file: File) -> Result<ChunkedFileReader> {
+impl LocalFileReader {
+ /// Creates a `LocalFileReader` to read from for the specified `path`.
+ pub fn new(file: File) -> Result<LocalFileReader> {
let size = file.metadata()?.len();
- Ok(ChunkedFileReader { file, size })
+ Ok(LocalFileReader { file, size })
}
pub fn len(&self) -> u64 {
@@ -57,7 +46,7 @@
}
}
-impl ReadOnlyDataByChunk for ChunkedFileReader {
+impl ReadOnlyDataByChunk for LocalFileReader {
fn read_chunk(&self, chunk_index: u64, buf: &mut [u8]) -> Result<usize> {
debug_assert!(buf.len() as u64 >= CHUNK_SIZE);
let (start, end) = chunk_index_to_range(self.size, chunk_index)?;
@@ -73,7 +62,7 @@
#[test]
fn test_read_4k_file() -> Result<()> {
- let file_reader = ChunkedFileReader::new(File::open("testdata/input.4k")?)?;
+ let file_reader = LocalFileReader::new(File::open("testdata/input.4k")?)?;
let mut buf = [0u8; 4096];
let size = file_reader.read_chunk(0, &mut buf)?;
assert_eq!(size, buf.len());
@@ -82,7 +71,7 @@
#[test]
fn test_read_4k1_file() -> Result<()> {
- let file_reader = ChunkedFileReader::new(File::open("testdata/input.4k1")?)?;
+ let file_reader = LocalFileReader::new(File::open("testdata/input.4k1")?)?;
let mut buf = [0u8; 4096];
let size = file_reader.read_chunk(0, &mut buf)?;
assert_eq!(size, buf.len());
@@ -93,7 +82,7 @@
#[test]
fn test_read_4m_file() -> Result<()> {
- let file_reader = ChunkedFileReader::new(File::open("testdata/input.4m")?)?;
+ let file_reader = LocalFileReader::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)?;
@@ -105,7 +94,7 @@
#[test]
#[should_panic]
fn test_read_beyond_file_size() {
- let file_reader = ChunkedFileReader::new(File::open("testdata/input.4k").unwrap()).unwrap();
+ let file_reader = LocalFileReader::new(File::open("testdata/input.4k").unwrap()).unwrap();
let mut buf = [0u8; 4096];
let _ = file_reader.read_chunk(1u64, &mut buf); // should panic
}
@@ -115,7 +104,7 @@
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 file_reader = LocalFileReader::new(File::create(temp_file).unwrap()).unwrap();
let mut buf = [0u8; 4096];
let _ = file_reader.read_chunk(0, &mut buf); // should panic
}
diff --git a/authfs/src/remote_file.rs b/authfs/src/file/remote_file.rs
similarity index 82%
rename from authfs/src/remote_file.rs
rename to authfs/src/file/remote_file.rs
index ed7381c..b87891b 100644
--- a/authfs/src/remote_file.rs
+++ b/authfs/src/file/remote_file.rs
@@ -19,24 +19,14 @@
use std::io::Write;
use std::sync::{Arc, Mutex};
+use super::{RandomWrite, ReadOnlyDataByChunk};
use crate::common::CHUNK_SIZE;
-use crate::reader::ReadOnlyDataByChunk;
-use crate::writer::RandomWrite;
use authfs_aidl_interface::aidl::com::android::virt::fs::IVirtFdService;
use authfs_aidl_interface::binder::Strong;
type VirtFdService = Strong<dyn IVirtFdService::IVirtFdService>;
-pub mod server {
- // TODO(victorhsieh): use remote binder.
- pub fn get_local_service() -> super::VirtFdService {
- let service_name = "authfs_fd_server";
- authfs_aidl_interface::binder::get_interface(&service_name)
- .expect("Cannot reach authfs_fd_server binder service")
- }
-}
-
fn remote_read_chunk(
service: &Arc<Mutex<VirtFdService>>,
remote_fd: i32,
@@ -54,38 +44,38 @@
buf.write(&chunk)
}
-pub struct RemoteChunkedFileReader {
+pub struct RemoteFileReader {
// This needs to have Sync trait to be used in fuse::worker::start_message_loop.
service: Arc<Mutex<VirtFdService>>,
file_fd: i32,
}
-impl RemoteChunkedFileReader {
+impl RemoteFileReader {
pub fn new(service: Arc<Mutex<VirtFdService>>, file_fd: i32) -> Self {
- RemoteChunkedFileReader { service, file_fd }
+ RemoteFileReader { service, file_fd }
}
}
-impl ReadOnlyDataByChunk for RemoteChunkedFileReader {
+impl ReadOnlyDataByChunk for RemoteFileReader {
fn read_chunk(&self, chunk_index: u64, buf: &mut [u8]) -> io::Result<usize> {
remote_read_chunk(&self.service, self.file_fd, chunk_index, buf)
}
}
-pub struct RemoteFsverityMerkleTreeReader {
+pub struct RemoteMerkleTreeReader {
// This needs to be a Sync to be used in fuse::worker::start_message_loop.
// TODO(victorhsieh): change to Strong<> once binder supports it.
service: Arc<Mutex<VirtFdService>>,
file_fd: i32,
}
-impl RemoteFsverityMerkleTreeReader {
+impl RemoteMerkleTreeReader {
pub fn new(service: Arc<Mutex<VirtFdService>>, file_fd: i32) -> Self {
- RemoteFsverityMerkleTreeReader { service, file_fd }
+ RemoteMerkleTreeReader { service, file_fd }
}
}
-impl ReadOnlyDataByChunk for RemoteFsverityMerkleTreeReader {
+impl ReadOnlyDataByChunk for RemoteMerkleTreeReader {
fn read_chunk(&self, chunk_index: u64, mut buf: &mut [u8]) -> io::Result<usize> {
let offset = i64::try_from(chunk_index * CHUNK_SIZE)
.map_err(|_| io::Error::from_raw_os_error(libc::EOVERFLOW))?;
diff --git a/authfs/src/fsverity.rs b/authfs/src/fsverity.rs
index 37d96c1..1515574 100644
--- a/authfs/src/fsverity.rs
+++ b/authfs/src/fsverity.rs
@@ -16,8 +16,9 @@
mod builder;
mod common;
+mod editor;
mod sys;
mod verifier;
-pub use self::builder::MerkleLeaves;
-pub use self::verifier::FsverityChunkedFileReader;
+pub use editor::VerifiedFileEditor;
+pub use verifier::VerifiedFileReader;
diff --git a/authfs/src/writer.rs b/authfs/src/fsverity/editor.rs
similarity index 90%
rename from authfs/src/writer.rs
rename to authfs/src/fsverity/editor.rs
index 6706a08..fc4e101 100644
--- a/authfs/src/writer.rs
+++ b/authfs/src/fsverity/editor.rs
@@ -55,10 +55,10 @@
use std::io;
use std::sync::{Arc, RwLock};
+use super::builder::MerkleLeaves;
use crate::common::{ChunkedSizeIter, CHUNK_SIZE};
use crate::crypto::{CryptoError, Sha256Hash, Sha256Hasher};
-use crate::fsverity::MerkleLeaves;
-use crate::reader::ReadOnlyDataByChunk;
+use crate::file::{RandomWrite, ReadOnlyDataByChunk};
// Implement the conversion from `CryptoError` to `io::Error` just to avoid manual error type
// mapping below.
@@ -68,26 +68,6 @@
}
}
-/// A trait to write a buffer to the destination at a given offset. The implementation does not
-/// necessarily own or maintain the destination state.
-pub trait RandomWrite {
- /// Writes `buf` to the destination at `offset`. Returns the written size, which may not be the
- /// full buffer.
- fn write_at(&self, buf: &[u8], offset: u64) -> io::Result<usize>;
-
- /// Writes the full `buf` to the destination at `offset`.
- fn write_all_at(&self, buf: &[u8], offset: u64) -> io::Result<()> {
- let mut input_offset = 0;
- let mut output_offset = offset;
- while input_offset < buf.len() {
- let size = self.write_at(&buf[input_offset..], output_offset)?;
- input_offset += size;
- output_offset += size as u64;
- }
- Ok(())
- }
-}
-
/// VerifiedFileEditor provides an integrity layer to an underlying read-writable file, which may
/// not be stored in a trusted environment. Only new, empty files are currently supported.
pub struct VerifiedFileEditor<F: ReadOnlyDataByChunk + RandomWrite> {
@@ -247,18 +227,18 @@
use std::cell::RefCell;
use std::convert::TryInto;
- struct InMemoryWriter {
+ struct InMemoryEditor {
data: RefCell<Vec<u8>>,
fail_read: bool,
}
- impl InMemoryWriter {
- pub fn new() -> InMemoryWriter {
- InMemoryWriter { data: RefCell::new(Vec::new()), fail_read: false }
+ impl InMemoryEditor {
+ pub fn new() -> InMemoryEditor {
+ InMemoryEditor { data: RefCell::new(Vec::new()), fail_read: false }
}
}
- impl RandomWrite for InMemoryWriter {
+ impl RandomWrite for InMemoryEditor {
fn write_at(&self, buf: &[u8], offset: u64) -> io::Result<usize> {
let begin: usize =
offset.try_into().map_err(|e| io::Error::new(io::ErrorKind::Other, e))?;
@@ -271,7 +251,7 @@
}
}
- impl ReadOnlyDataByChunk for InMemoryWriter {
+ impl ReadOnlyDataByChunk for InMemoryEditor {
fn read_chunk(&self, chunk_index: u64, buf: &mut [u8]) -> io::Result<usize> {
debug_assert!(buf.len() as u64 >= CHUNK_SIZE);
@@ -296,7 +276,7 @@
#[test]
fn test_writer() -> Result<()> {
- let writer = InMemoryWriter::new();
+ let writer = InMemoryEditor::new();
let buf = [1; 4096];
assert_eq!(writer.data.borrow().len(), 0);
@@ -313,7 +293,7 @@
#[test]
fn test_verified_writer_no_write() -> Result<()> {
// Verify fs-verity hash without any write.
- let file = VerifiedFileEditor::new(InMemoryWriter::new());
+ let file = VerifiedFileEditor::new(InMemoryEditor::new());
assert_eq!(
file.calculate_fsverity_digest()?,
to_u8_vec("3d248ca542a24fc62d1c43b916eae5016878e2533c88238480b26128a1f1af95")
@@ -325,7 +305,7 @@
#[test]
fn test_verified_writer_from_zero() -> Result<()> {
// Verify a write of a full chunk.
- let file = VerifiedFileEditor::new(InMemoryWriter::new());
+ let file = VerifiedFileEditor::new(InMemoryEditor::new());
assert_eq!(file.write_at(&[1; 4096], 0)?, 4096);
assert_eq!(
file.calculate_fsverity_digest()?,
@@ -334,7 +314,7 @@
);
// Verify a write of across multiple chunks.
- let file = VerifiedFileEditor::new(InMemoryWriter::new());
+ let file = VerifiedFileEditor::new(InMemoryEditor::new());
assert_eq!(file.write_at(&[1; 4097], 0)?, 4097);
assert_eq!(
file.calculate_fsverity_digest()?,
@@ -343,7 +323,7 @@
);
// Verify another write of across multiple chunks.
- let file = VerifiedFileEditor::new(InMemoryWriter::new());
+ let file = VerifiedFileEditor::new(InMemoryEditor::new());
assert_eq!(file.write_at(&[1; 10000], 0)?, 10000);
assert_eq!(
file.calculate_fsverity_digest()?,
@@ -356,7 +336,7 @@
#[test]
fn test_verified_writer_unaligned() -> Result<()> {
// Verify small, unaligned write beyond EOF.
- let file = VerifiedFileEditor::new(InMemoryWriter::new());
+ let file = VerifiedFileEditor::new(InMemoryEditor::new());
assert_eq!(file.write_at(&[1; 5], 3)?, 5);
assert_eq!(
file.calculate_fsverity_digest()?,
@@ -365,7 +345,7 @@
);
// Verify bigger, unaligned write beyond EOF.
- let file = VerifiedFileEditor::new(InMemoryWriter::new());
+ let file = VerifiedFileEditor::new(InMemoryEditor::new());
assert_eq!(file.write_at(&[1; 6000], 4000)?, 6000);
assert_eq!(
file.calculate_fsverity_digest()?,
@@ -378,7 +358,7 @@
#[test]
fn test_verified_writer_with_hole() -> Result<()> {
// Verify an aligned write beyond EOF with holes.
- let file = VerifiedFileEditor::new(InMemoryWriter::new());
+ let file = VerifiedFileEditor::new(InMemoryEditor::new());
assert_eq!(file.write_at(&[1; 4096], 4096)?, 4096);
assert_eq!(
file.calculate_fsverity_digest()?,
@@ -387,7 +367,7 @@
);
// Verify an unaligned write beyond EOF with holes.
- let file = VerifiedFileEditor::new(InMemoryWriter::new());
+ let file = VerifiedFileEditor::new(InMemoryEditor::new());
assert_eq!(file.write_at(&[1; 5000], 6000)?, 5000);
assert_eq!(
file.calculate_fsverity_digest()?,
@@ -396,7 +376,7 @@
);
// Just another example with a small write.
- let file = VerifiedFileEditor::new(InMemoryWriter::new());
+ let file = VerifiedFileEditor::new(InMemoryEditor::new());
assert_eq!(file.write_at(&[1; 5], 16381)?, 5);
assert_eq!(
file.calculate_fsverity_digest()?,
@@ -408,7 +388,7 @@
#[test]
fn test_verified_writer_various_writes() -> Result<()> {
- let file = VerifiedFileEditor::new(InMemoryWriter::new());
+ let file = VerifiedFileEditor::new(InMemoryEditor::new());
assert_eq!(file.write_at(&[1; 2048], 0)?, 2048);
assert_eq!(file.write_at(&[1; 2048], 4096 + 2048)?, 2048);
assert_eq!(
@@ -448,7 +428,7 @@
#[test]
fn test_verified_writer_inconsistent_read() -> Result<()> {
- let file = VerifiedFileEditor::new(InMemoryWriter::new());
+ let file = VerifiedFileEditor::new(InMemoryEditor::new());
assert_eq!(file.write_at(&[1; 8192], 0)?, 8192);
// Replace the expected hash of the first/0-th chunk. An incomplete write will fail when it
@@ -478,7 +458,7 @@
#[test]
fn test_verified_writer_failed_read_back() -> Result<()> {
- let mut writer = InMemoryWriter::new();
+ let mut writer = InMemoryEditor::new();
writer.fail_read = true;
let file = VerifiedFileEditor::new(writer);
assert_eq!(file.write_at(&[1; 8192], 0)?, 8192);
diff --git a/authfs/src/fsverity/verifier.rs b/authfs/src/fsverity/verifier.rs
index fd108f5..4021ce1 100644
--- a/authfs/src/fsverity/verifier.rs
+++ b/authfs/src/fsverity/verifier.rs
@@ -22,7 +22,7 @@
use crate::auth::Authenticator;
use crate::common::{divide_roundup, CHUNK_SIZE};
use crate::crypto::{CryptoError, Sha256Hasher};
-use crate::reader::ReadOnlyDataByChunk;
+use crate::file::ReadOnlyDataByChunk;
const ZEROS: [u8; CHUNK_SIZE as usize] = [0u8; CHUNK_SIZE as usize];
@@ -125,21 +125,21 @@
Ok(formatted_digest)
}
-pub struct FsverityChunkedFileReader<F: ReadOnlyDataByChunk, M: ReadOnlyDataByChunk> {
+pub struct VerifiedFileReader<F: ReadOnlyDataByChunk, M: ReadOnlyDataByChunk> {
chunked_file: F,
file_size: u64,
merkle_tree: M,
root_hash: HashBuffer,
}
-impl<F: ReadOnlyDataByChunk, M: ReadOnlyDataByChunk> FsverityChunkedFileReader<F, M> {
+impl<F: ReadOnlyDataByChunk, M: ReadOnlyDataByChunk> VerifiedFileReader<F, M> {
pub fn new<A: Authenticator>(
authenticator: &A,
chunked_file: F,
file_size: u64,
sig: Vec<u8>,
merkle_tree: M,
- ) -> Result<FsverityChunkedFileReader<F, M>, FsverityError> {
+ ) -> Result<VerifiedFileReader<F, M>, FsverityError> {
let mut buf = [0u8; CHUNK_SIZE as usize];
let size = merkle_tree.read_chunk(0, &mut buf)?;
if buf.len() != size {
@@ -149,7 +149,7 @@
let formatted_digest = build_fsverity_formatted_digest(&root_hash, file_size)?;
let valid = authenticator.verify(&sig, &formatted_digest)?;
if valid {
- Ok(FsverityChunkedFileReader { chunked_file, file_size, merkle_tree, root_hash })
+ Ok(VerifiedFileReader { chunked_file, file_size, merkle_tree, root_hash })
} else {
Err(FsverityError::BadSignature)
}
@@ -157,7 +157,7 @@
}
impl<F: ReadOnlyDataByChunk, M: ReadOnlyDataByChunk> ReadOnlyDataByChunk
- for FsverityChunkedFileReader<F, M>
+ for VerifiedFileReader<F, M>
{
fn read_chunk(&self, chunk_index: u64, buf: &mut [u8]) -> io::Result<usize> {
debug_assert!(buf.len() as u64 >= CHUNK_SIZE);
@@ -176,13 +176,12 @@
mod tests {
use super::*;
use crate::auth::FakeAuthenticator;
- use crate::reader::{ChunkedFileReader, ReadOnlyDataByChunk};
+ use crate::file::{LocalFileReader, ReadOnlyDataByChunk};
use anyhow::Result;
use std::fs::File;
use std::io::Read;
- type LocalFsverityChunkedFileReader =
- FsverityChunkedFileReader<ChunkedFileReader, ChunkedFileReader>;
+ type LocalVerifiedFileReader = VerifiedFileReader<LocalFileReader, LocalFileReader>;
fn total_chunk_number(file_size: u64) -> u64 {
(file_size + 4095) / 4096
@@ -193,21 +192,15 @@
content_path: &str,
merkle_tree_path: &str,
signature_path: &str,
- ) -> Result<(LocalFsverityChunkedFileReader, u64)> {
- let file_reader = ChunkedFileReader::new(File::open(content_path)?)?;
+ ) -> Result<(LocalVerifiedFileReader, u64)> {
+ let file_reader = LocalFileReader::new(File::open(content_path)?)?;
let file_size = file_reader.len();
- let merkle_tree = ChunkedFileReader::new(File::open(merkle_tree_path)?)?;
+ let merkle_tree = LocalFileReader::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,
- )?,
+ VerifiedFileReader::new(&authenticator, file_reader, file_size, sig, merkle_tree)?,
file_size,
))
}
@@ -280,18 +273,12 @@
#[test]
fn invalid_signature() -> Result<()> {
let authenticator = FakeAuthenticator::always_fail();
- let file_reader = ChunkedFileReader::new(File::open("testdata/input.4m")?)?;
+ let file_reader = LocalFileReader::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 merkle_tree = LocalFileReader::new(File::open("testdata/input.4m.merkle_dump")?)?;
let sig = include_bytes!("../../testdata/input.4m.fsv_sig").to_vec();
- assert!(FsverityChunkedFileReader::new(
- &authenticator,
- file_reader,
- file_size,
- sig,
- merkle_tree
- )
- .is_err());
+ assert!(VerifiedFileReader::new(&authenticator, file_reader, file_size, sig, merkle_tree)
+ .is_err());
Ok(())
}
}
diff --git a/authfs/src/fusefs.rs b/authfs/src/fusefs.rs
index bbcb84f..bdf7cd8 100644
--- a/authfs/src/fusefs.rs
+++ b/authfs/src/fusefs.rs
@@ -30,27 +30,19 @@
use fuse::mount::MountOption;
use crate::common::{divide_roundup, ChunkedSizeIter, CHUNK_SIZE};
-use crate::fsverity::FsverityChunkedFileReader;
-use crate::reader::{ChunkedFileReader, ReadOnlyDataByChunk};
-use crate::remote_file::{RemoteChunkedFileReader, RemoteFsverityMerkleTreeReader};
+use crate::file::{LocalFileReader, ReadOnlyDataByChunk, RemoteFileReader, RemoteMerkleTreeReader};
+use crate::fsverity::VerifiedFileReader;
const DEFAULT_METADATA_TIMEOUT: std::time::Duration = Duration::from_secs(5);
pub type Inode = u64;
type Handle = u64;
-type RemoteFsverityChunkedFileReader =
- FsverityChunkedFileReader<RemoteChunkedFileReader, RemoteFsverityMerkleTreeReader>;
-
-// A debug only type where everything are stored as local files.
-type FileBackedFsverityChunkedFileReader =
- FsverityChunkedFileReader<ChunkedFileReader, ChunkedFileReader>;
-
pub enum FileConfig {
- LocalVerifiedFile(FileBackedFsverityChunkedFileReader, u64),
- LocalUnverifiedFile(ChunkedFileReader, u64),
- RemoteVerifiedFile(RemoteFsverityChunkedFileReader, u64),
- RemoteUnverifiedFile(RemoteChunkedFileReader, u64),
+ LocalVerifiedReadonlyFile(VerifiedFileReader<LocalFileReader, LocalFileReader>, u64),
+ LocalUnverifiedReadonlyFile(LocalFileReader, u64),
+ RemoteVerifiedReadonlyFile(VerifiedFileReader<RemoteFileReader, RemoteMerkleTreeReader>, u64),
+ RemoteUnverifiedReadonlyFile(RemoteFileReader, u64),
}
struct AuthFs {
@@ -177,10 +169,12 @@
// be static.
let inode = num.parse::<Inode>().map_err(|_| io::Error::from_raw_os_error(libc::ENOENT))?;
let st = match self.get_file_config(&inode)? {
- FileConfig::LocalVerifiedFile(_, file_size)
- | FileConfig::LocalUnverifiedFile(_, file_size)
- | FileConfig::RemoteUnverifiedFile(_, file_size)
- | FileConfig::RemoteVerifiedFile(_, file_size) => create_stat(inode, *file_size)?,
+ FileConfig::LocalVerifiedReadonlyFile(_, file_size)
+ | FileConfig::LocalUnverifiedReadonlyFile(_, file_size)
+ | FileConfig::RemoteUnverifiedReadonlyFile(_, file_size)
+ | FileConfig::RemoteVerifiedReadonlyFile(_, file_size) => {
+ create_stat(inode, *file_size)?
+ }
};
Ok(Entry {
inode,
@@ -199,10 +193,12 @@
) -> io::Result<(libc::stat64, Duration)> {
Ok((
match self.get_file_config(&inode)? {
- FileConfig::LocalVerifiedFile(_, file_size)
- | FileConfig::LocalUnverifiedFile(_, file_size)
- | FileConfig::RemoteUnverifiedFile(_, file_size)
- | FileConfig::RemoteVerifiedFile(_, file_size) => create_stat(inode, *file_size)?,
+ FileConfig::LocalVerifiedReadonlyFile(_, file_size)
+ | FileConfig::LocalUnverifiedReadonlyFile(_, file_size)
+ | FileConfig::RemoteUnverifiedReadonlyFile(_, file_size)
+ | FileConfig::RemoteVerifiedReadonlyFile(_, file_size) => {
+ create_stat(inode, *file_size)?
+ }
},
DEFAULT_METADATA_TIMEOUT,
))
@@ -215,15 +211,18 @@
flags: u32,
) -> io::Result<(Option<Self::Handle>, fuse::sys::OpenOptions)> {
// Since file handle is not really used in later operations (which use Inode directly),
- // return None as the handle..
+ // return None as the handle.
match self.get_file_config(&inode)? {
- FileConfig::LocalVerifiedFile(_, _) | FileConfig::RemoteVerifiedFile(_, _) => {
+ FileConfig::LocalVerifiedReadonlyFile(_, _)
+ | FileConfig::RemoteVerifiedReadonlyFile(_, _) => {
check_access_mode(flags, libc::O_RDONLY)?;
// Once verified, and only if verified, the file content can be cached. This is not
- // really needed for a local file, but is the behavior of RemoteVerifiedFile later.
+ // really needed for a local file, but is the behavior of RemoteVerifiedReadonlyFile
+ // later.
Ok((None, fuse::sys::OpenOptions::KEEP_CACHE))
}
- FileConfig::LocalUnverifiedFile(_, _) | FileConfig::RemoteUnverifiedFile(_, _) => {
+ FileConfig::LocalUnverifiedReadonlyFile(_, _)
+ | FileConfig::RemoteUnverifiedReadonlyFile(_, _) => {
check_access_mode(flags, libc::O_RDONLY)?;
// Do not cache the content. This type of file is supposed to be verified using
// dm-verity. The filesystem mount over dm-verity already is already cached, so use
@@ -245,16 +244,16 @@
_flags: u32,
) -> io::Result<usize> {
match self.get_file_config(&inode)? {
- FileConfig::LocalVerifiedFile(file, file_size) => {
+ FileConfig::LocalVerifiedReadonlyFile(file, file_size) => {
read_chunks(w, file, *file_size, offset, size)
}
- FileConfig::LocalUnverifiedFile(file, file_size) => {
+ FileConfig::LocalUnverifiedReadonlyFile(file, file_size) => {
read_chunks(w, file, *file_size, offset, size)
}
- FileConfig::RemoteVerifiedFile(file, file_size) => {
+ FileConfig::RemoteVerifiedReadonlyFile(file, file_size) => {
read_chunks(w, file, *file_size, offset, size)
}
- FileConfig::RemoteUnverifiedFile(file, file_size) => {
+ FileConfig::RemoteUnverifiedReadonlyFile(file, file_size) => {
read_chunks(w, file, *file_size, offset, size)
}
}
diff --git a/authfs/src/main.rs b/authfs/src/main.rs
index 05e4584..39482e3 100644
--- a/authfs/src/main.rs
+++ b/authfs/src/main.rs
@@ -38,17 +38,14 @@
mod auth;
mod common;
mod crypto;
+mod file;
mod fsverity;
mod fusefs;
-mod reader;
-mod remote_file;
-mod writer;
use auth::FakeAuthenticator;
-use fsverity::FsverityChunkedFileReader;
+use file::{LocalFileReader, RemoteFileReader, RemoteMerkleTreeReader};
+use fsverity::VerifiedFileReader;
use fusefs::{FileConfig, Inode};
-use reader::ChunkedFileReader;
-use remote_file::{RemoteChunkedFileReader, RemoteFsverityMerkleTreeReader};
#[derive(StructOpt)]
struct Args {
@@ -56,31 +53,31 @@
#[structopt(parse(from_os_str))]
mount_point: PathBuf,
- /// A verifiable read-only file. Can be multiple.
+ /// A read-only remote file with integrity check. Can be multiple.
///
/// For example, `--remote-verified-file 5:10:1234:/path/to/cert` tells the filesystem to
/// associate entry 5 with a remote file 10 of size 1234 bytes, and need to be verified against
/// the /path/to/cert.
- #[structopt(long, parse(try_from_str = parse_remote_verified_file_option))]
- remote_verified_file: Vec<RemoteVerifiedFileConfig>,
+ #[structopt(long, parse(try_from_str = parse_remote_ro_file_option))]
+ remote_ro_file: Vec<OptionRemoteRoFile>,
- /// An unverifiable read-only file. Can be multiple.
+ /// A read-only remote file without integrity check. Can be multiple.
///
/// For example, `--remote-unverified-file 5:10:1234` tells the filesystem to associate entry 5
/// with a remote file 10 of size 1234 bytes.
- #[structopt(long, parse(try_from_str = parse_remote_unverified_file_option))]
- remote_unverified_file: Vec<RemoteUnverifiedFileConfig>,
+ #[structopt(long, parse(try_from_str = parse_remote_ro_file_unverified_option))]
+ remote_ro_file_unverified: Vec<OptionRemoteRoFileUnverified>,
- /// Debug only. A readonly file to be protected by fs-verity. Can be multiple.
- #[structopt(long, parse(try_from_str = parse_local_verified_file_option))]
- local_verified_file: Vec<LocalVerifiedFileConfig>,
+ /// Debug only. A read-only local file with integrity check. Can be multiple.
+ #[structopt(long, parse(try_from_str = parse_local_file_ro_option))]
+ local_ro_file: Vec<OptionLocalFileRo>,
- /// Debug only. An unverified read-only file. Can be multiple.
- #[structopt(long, parse(try_from_str = parse_local_unverified_file_option))]
- local_unverified_file: Vec<LocalUnverifiedFileConfig>,
+ /// Debug only. A read-only local file without integrity check. Can be multiple.
+ #[structopt(long, parse(try_from_str = parse_local_ro_file_unverified_ro_option))]
+ local_ro_file_unverified: Vec<OptionLocalRoFileUnverified>,
}
-struct RemoteVerifiedFileConfig {
+struct OptionRemoteRoFile {
ino: Inode,
/// ID to refer to the remote file.
@@ -95,7 +92,7 @@
_certificate_path: PathBuf,
}
-struct RemoteUnverifiedFileConfig {
+struct OptionRemoteRoFileUnverified {
ino: Inode,
/// ID to refer to the remote file.
@@ -105,7 +102,7 @@
file_size: u64,
}
-struct LocalVerifiedFileConfig {
+struct OptionLocalFileRo {
ino: Inode,
/// Local path of the backing file.
@@ -122,19 +119,19 @@
_certificate_path: PathBuf,
}
-struct LocalUnverifiedFileConfig {
+struct OptionLocalRoFileUnverified {
ino: Inode,
/// Local path of the backing file.
file_path: PathBuf,
}
-fn parse_remote_verified_file_option(option: &str) -> Result<RemoteVerifiedFileConfig> {
+fn parse_remote_ro_file_option(option: &str) -> Result<OptionRemoteRoFile> {
let strs: Vec<&str> = option.split(':').collect();
if strs.len() != 4 {
bail!("Invalid option: {}", option);
}
- Ok(RemoteVerifiedFileConfig {
+ Ok(OptionRemoteRoFile {
ino: strs[0].parse::<Inode>()?,
remote_id: strs[1].parse::<i32>()?,
file_size: strs[2].parse::<u64>()?,
@@ -142,24 +139,24 @@
})
}
-fn parse_remote_unverified_file_option(option: &str) -> Result<RemoteUnverifiedFileConfig> {
+fn parse_remote_ro_file_unverified_option(option: &str) -> Result<OptionRemoteRoFileUnverified> {
let strs: Vec<&str> = option.split(':').collect();
if strs.len() != 3 {
bail!("Invalid option: {}", option);
}
- Ok(RemoteUnverifiedFileConfig {
+ Ok(OptionRemoteRoFileUnverified {
ino: strs[0].parse::<Inode>()?,
remote_id: strs[1].parse::<i32>()?,
file_size: strs[2].parse::<u64>()?,
})
}
-fn parse_local_verified_file_option(option: &str) -> Result<LocalVerifiedFileConfig> {
+fn parse_local_file_ro_option(option: &str) -> Result<OptionLocalFileRo> {
let strs: Vec<&str> = option.split(':').collect();
if strs.len() != 5 {
bail!("Invalid option: {}", option);
}
- Ok(LocalVerifiedFileConfig {
+ Ok(OptionLocalFileRo {
ino: strs[0].parse::<Inode>()?,
file_path: PathBuf::from(strs[1]),
merkle_tree_dump_path: PathBuf::from(strs[2]),
@@ -168,92 +165,85 @@
})
}
-fn parse_local_unverified_file_option(option: &str) -> Result<LocalUnverifiedFileConfig> {
+fn parse_local_ro_file_unverified_ro_option(option: &str) -> Result<OptionLocalRoFileUnverified> {
let strs: Vec<&str> = option.split(':').collect();
if strs.len() != 2 {
bail!("Invalid option: {}", option);
}
- Ok(LocalUnverifiedFileConfig {
+ Ok(OptionLocalRoFileUnverified {
ino: strs[0].parse::<Inode>()?,
file_path: PathBuf::from(strs[1]),
})
}
fn new_config_remote_verified_file(remote_id: i32, file_size: u64) -> Result<FileConfig> {
- let service = remote_file::server::get_local_service();
+ let service = file::get_local_binder();
let signature = service.readFsveritySignature(remote_id).context("Failed to read signature")?;
let service = Arc::new(Mutex::new(service));
let authenticator = FakeAuthenticator::always_succeed();
- Ok(FileConfig::RemoteVerifiedFile(
- FsverityChunkedFileReader::new(
+ Ok(FileConfig::RemoteVerifiedReadonlyFile(
+ VerifiedFileReader::new(
&authenticator,
- RemoteChunkedFileReader::new(Arc::clone(&service), remote_id),
+ RemoteFileReader::new(Arc::clone(&service), remote_id),
file_size,
signature,
- RemoteFsverityMerkleTreeReader::new(Arc::clone(&service), remote_id),
+ RemoteMerkleTreeReader::new(Arc::clone(&service), remote_id),
)?,
file_size,
))
}
fn new_config_remote_unverified_file(remote_id: i32, file_size: u64) -> Result<FileConfig> {
- let file_reader = RemoteChunkedFileReader::new(
- Arc::new(Mutex::new(remote_file::server::get_local_service())),
- remote_id,
- );
- Ok(FileConfig::RemoteUnverifiedFile(file_reader, file_size))
+ let file_reader =
+ RemoteFileReader::new(Arc::new(Mutex::new(file::get_local_binder())), remote_id);
+ Ok(FileConfig::RemoteUnverifiedReadonlyFile(file_reader, file_size))
}
-fn new_config_local_verified_file(
+fn new_config_local_ro_file(
protected_file: &PathBuf,
merkle_tree_dump: &PathBuf,
signature: &PathBuf,
) -> Result<FileConfig> {
let file = File::open(&protected_file)?;
let file_size = file.metadata()?.len();
- let file_reader = ChunkedFileReader::new(file)?;
- let merkle_tree_reader = ChunkedFileReader::new(File::open(merkle_tree_dump)?)?;
+ let file_reader = LocalFileReader::new(file)?;
+ let merkle_tree_reader = LocalFileReader::new(File::open(merkle_tree_dump)?)?;
let authenticator = FakeAuthenticator::always_succeed();
let mut sig = Vec::new();
let _ = File::open(signature)?.read_to_end(&mut sig)?;
- let file_reader = FsverityChunkedFileReader::new(
- &authenticator,
- file_reader,
- file_size,
- sig,
- merkle_tree_reader,
- )?;
- Ok(FileConfig::LocalVerifiedFile(file_reader, file_size))
+ let file_reader =
+ VerifiedFileReader::new(&authenticator, file_reader, file_size, sig, merkle_tree_reader)?;
+ Ok(FileConfig::LocalVerifiedReadonlyFile(file_reader, file_size))
}
-fn new_config_local_unverified_file(file_path: &PathBuf) -> Result<FileConfig> {
- let file_reader = ChunkedFileReader::new(File::open(file_path)?)?;
+fn new_config_local_ro_file_unverified(file_path: &PathBuf) -> Result<FileConfig> {
+ let file_reader = LocalFileReader::new(File::open(file_path)?)?;
let file_size = file_reader.len();
- Ok(FileConfig::LocalUnverifiedFile(file_reader, file_size))
+ Ok(FileConfig::LocalUnverifiedReadonlyFile(file_reader, file_size))
}
fn prepare_file_pool(args: &Args) -> Result<BTreeMap<Inode, FileConfig>> {
let mut file_pool = BTreeMap::new();
- for config in &args.remote_verified_file {
+ for config in &args.remote_ro_file {
file_pool.insert(
config.ino,
new_config_remote_verified_file(config.remote_id, config.file_size)?,
);
}
- for config in &args.remote_unverified_file {
+ for config in &args.remote_ro_file_unverified {
file_pool.insert(
config.ino,
new_config_remote_unverified_file(config.remote_id, config.file_size)?,
);
}
- for config in &args.local_verified_file {
+ for config in &args.local_ro_file {
file_pool.insert(
config.ino,
- new_config_local_verified_file(
+ new_config_local_ro_file(
&config.file_path,
&config.merkle_tree_dump_path,
&config.signature_path,
@@ -261,8 +251,8 @@
);
}
- for config in &args.local_unverified_file {
- file_pool.insert(config.ino, new_config_local_unverified_file(&config.file_path)?);
+ for config in &args.local_ro_file_unverified {
+ file_pool.insert(config.ino, new_config_local_ro_file_unverified(&config.file_path)?);
}
Ok(file_pool)
diff --git a/authfs/tools/device-test.sh b/authfs/tools/device-test.sh
index 5cf5f10..e85ad17 100755
--- a/authfs/tools/device-test.sh
+++ b/authfs/tools/device-test.sh
@@ -33,13 +33,13 @@
# certificate.
authfs \
${MOUNTPOINT} \
- --local-verified-file 2:input.4m:input.4m.merkle_dump:input.4m.fsv_sig:/dev/null \
- --local-verified-file 3:input.4k1:input.4k1.merkle_dump:input.4k1.fsv_sig:/dev/null \
- --local-verified-file 4:input.4k:input.4k.merkle_dump:input.4k.fsv_sig:/dev/null \
- --local-unverified-file 5:/system/bin/sh \
- --remote-unverified-file 6:9:${size} \
- --remote-verified-file 7:8:${size2}:/dev/null \
- --remote-verified-file 8:5:${size2}:/dev/null \
+ --local-ro-file 2:input.4m:input.4m.merkle_dump:input.4m.fsv_sig:/dev/null \
+ --local-ro-file 3:input.4k1:input.4k1.merkle_dump:input.4k1.fsv_sig:/dev/null \
+ --local-ro-file 4:input.4k:input.4k.merkle_dump:input.4k.fsv_sig:/dev/null \
+ --local-ro-file-unverified 5:/system/bin/sh \
+ --remote-ro-file-unverified 6:9:${size} \
+ --remote-ro-file 7:8:${size2}:/dev/null \
+ --remote-ro-file 8:5:${size2}:/dev/null \
&
sleep 0.1