ext4_parser/
lib.rs

1// Copyright 2019 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5use 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                // Too small to even fit the first copy of the ext4 Super Block.
38                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    // macros
58    use vfs::{
59        assert_close, assert_event, assert_read, assert_read_dirents,
60        open_as_vmo_file_assert_content, open_get_proxy_assert, open_get_vmo_file_proxy_assert_ok,
61    };
62
63    use ext4_read_only::structs::MIN_EXT4_SIZE;
64    use fidl_fuchsia_io as fio;
65    use std::fs;
66    use vfs::directory::test_utils::{run_server_client, DirentsSameInodeBuilder};
67    use zx::Vmo;
68
69    #[fuchsia::test]
70    fn image_too_small() {
71        let vmo = Vmo::create(10).expect("VMO is created");
72        vmo.write(b"too small", 0).expect("VMO write() succeeds");
73        let buffer = FsSourceType::Vmo(vmo);
74
75        assert!(construct_fs(buffer).is_err(), "Expected failed parsing of VMO.");
76    }
77
78    #[fuchsia::test]
79    fn invalid_fs() {
80        let vmo = Vmo::create(MIN_EXT4_SIZE as u64).expect("VMO is created");
81        vmo.write(b"not ext4", 0).expect("VMO write() succeeds");
82        let buffer = FsSourceType::Vmo(vmo);
83
84        assert!(construct_fs(buffer).is_err(), "Expected failed parsing of VMO.");
85    }
86
87    #[fuchsia::test]
88    fn list_root() {
89        let data = fs::read("/pkg/data/nest.img").expect("Unable to read file");
90        let vmo = Vmo::create(data.len() as u64).expect("VMO is created");
91        vmo.write(data.as_slice(), 0).expect("VMO write() succeeds");
92        let buffer = FsSourceType::Vmo(vmo);
93
94        let tree = construct_fs(buffer).expect("construct_fs parses the vmo");
95
96        run_server_client(fio::OpenFlags::RIGHT_READABLE, tree, |root| async move {
97            {
98                let mut expected = DirentsSameInodeBuilder::new(fio::INO_UNKNOWN);
99                expected.add(fio::DirentType::Directory, b".");
100                expected.add(fio::DirentType::File, b"file1");
101                expected.add(fio::DirentType::Directory, b"inner");
102                expected.add(fio::DirentType::Directory, b"lost+found");
103
104                assert_read_dirents!(root, 1000, expected.into_vec());
105            }
106
107            let flags = fio::OpenFlags::RIGHT_READABLE | fio::OpenFlags::DESCRIBE;
108            let compare = "file1 contents.\n";
109            open_as_vmo_file_assert_content!(&root, flags, "file1", compare);
110
111            assert_close!(root);
112        });
113    }
114}