1use ext4_read_only::parser::Parser;
6use ext4_read_only::readers::{BlockDeviceReader, Reader, VmoReader};
7use ext4_read_only::structs::{self, MIN_EXT4_SIZE};
8use fidl::endpoints::ClientEnd;
9use fidl_fuchsia_hardware_block::BlockMarker;
10use log::error;
11use std::sync::Arc;
12
13pub enum FsSourceType {
14 BlockDevice(ClientEnd<BlockMarker>),
15 Vmo(zx::Vmo),
16}
17
18#[derive(Debug, PartialEq)]
19pub enum ConstructFsError {
20 VmoReadError(zx::Status),
21 ParsingError(structs::ParsingError),
22}
23
24pub fn construct_fs(
25 source: FsSourceType,
26) -> Result<Arc<vfs::directory::immutable::Simple>, ConstructFsError> {
27 let reader: Box<dyn Reader> = match source {
28 FsSourceType::BlockDevice(block_device) => {
29 Box::new(BlockDeviceReader::from_client_end(block_device).map_err(|e| {
30 error!("Error constructing file system: {}", e);
31 ConstructFsError::VmoReadError(zx::Status::IO_INVALID)
32 })?)
33 }
34 FsSourceType::Vmo(vmo) => {
35 let size = vmo.get_size().map_err(ConstructFsError::VmoReadError)?;
36 if size < MIN_EXT4_SIZE as u64 {
37 return Err(ConstructFsError::VmoReadError(zx::Status::NO_SPACE));
39 }
40
41 Box::new(VmoReader::new(Arc::new(vmo)))
42 }
43 };
44
45 let parser = Parser::new(reader);
46
47 match parser.build_fuchsia_tree() {
48 Ok(tree) => Ok(tree),
49 Err(e) => Err(ConstructFsError::ParsingError(e)),
50 }
51}
52
53#[cfg(test)]
54mod tests {
55 use super::{construct_fs, FsSourceType};
56
57 use ext4_read_only::structs::MIN_EXT4_SIZE;
58 use fidl_fuchsia_io as fio;
59 use fuchsia_fs::directory::{open_file, readdir, DirEntry, DirentKind};
60 use fuchsia_fs::file::read_to_string;
61 use std::fs;
62 use zx::Vmo;
63
64 #[fuchsia::test]
65 fn image_too_small() {
66 let vmo = Vmo::create(10).expect("VMO is created");
67 vmo.write(b"too small", 0).expect("VMO write() succeeds");
68 let buffer = FsSourceType::Vmo(vmo);
69
70 assert!(construct_fs(buffer).is_err(), "Expected failed parsing of VMO.");
71 }
72
73 #[fuchsia::test]
74 fn invalid_fs() {
75 let vmo = Vmo::create(MIN_EXT4_SIZE as u64).expect("VMO is created");
76 vmo.write(b"not ext4", 0).expect("VMO write() succeeds");
77 let buffer = FsSourceType::Vmo(vmo);
78
79 assert!(construct_fs(buffer).is_err(), "Expected failed parsing of VMO.");
80 }
81
82 #[fuchsia::test]
83 async fn list_root() {
84 let data = fs::read("/pkg/data/nest.img").expect("Unable to read file");
85 let vmo = Vmo::create(data.len() as u64).expect("VMO is created");
86 vmo.write(data.as_slice(), 0).expect("VMO write() succeeds");
87 let buffer = FsSourceType::Vmo(vmo);
88
89 let tree = construct_fs(buffer).expect("construct_fs parses the vmo");
90 let root = vfs::directory::serve(tree, fio::PERM_READABLE);
91
92 let expected = vec![
93 DirEntry { name: String::from("file1"), kind: DirentKind::File },
94 DirEntry { name: String::from("inner"), kind: DirentKind::Directory },
95 DirEntry { name: String::from("lost+found"), kind: DirentKind::Directory },
96 ];
97 assert_eq!(readdir(&root).await.unwrap(), expected);
98
99 let file = open_file(&root, "file1", fio::PERM_READABLE).await.unwrap();
100 assert_eq!(read_to_string(&file).await.unwrap(), "file1 contents.\n");
101 file.close().await.unwrap().map_err(zx::Status::from_raw).unwrap();
102 root.close().await.unwrap().map_err(zx::Status::from_raw).unwrap();
103 }
104}