Skip to main content

ext4_lib/
parser.rs

1/*
2 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3 *
4 * Copyright (c) 2012, 2010 Zheng Liu <lz@freebsd.org>
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 *    notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 *    notice, this list of conditions and the following disclaimer in the
14 *    documentation and/or other materials provided with the distribution.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26 * SUCH DAMAGE.
27 *
28 * $FreeBSD$
29 */
30
31// Copyright 2019 The Fuchsia Authors. All rights reserved.
32// Use of this source code is governed by a BSD-style license that can be
33// found in the LICENSE file.
34
35use crate::readers::Reader;
36use crate::structs::{
37    BlockGroupDesc32, BlockGroupDesc64, DirEntry2, DirEntryHeader, EntryType, Extent, ExtentHeader,
38    ExtentIndex, ExtentTreeNode, FIRST_BG_PADDING, INode, InvalidAddressErrorType, MIN_EXT4_SIZE,
39    MINIMUM_INODE_SIZE, ParseToStruct, ParsingError, ROOT_INODE_NUM, SuperBlock, XattrEntryHeader,
40    XattrHeader,
41};
42use once_cell::sync::OnceCell;
43use std::collections::BTreeMap;
44use std::mem::{size_of, size_of_val};
45use std::path::{Component, Path};
46use std::str;
47use zerocopy::byteorder::little_endian::U32 as LEU32;
48use zerocopy::{IntoBytes, SplitByteSlice};
49
50// Assuming/ensuring that we are on a 64bit system where u64 == usize.
51assert_eq_size!(u64, usize);
52
53pub struct Parser {
54    reader: Box<dyn Reader>,
55    super_block: OnceCell<SuperBlock>,
56}
57
58pub type XattrMap = BTreeMap<Vec<u8>, Vec<u8>>;
59
60/// An abstraction over an appendable buffer for reading out files.
61pub trait DataWriter {
62    /// Reserves `size` in the underlying buffer.
63    ///
64    /// Does not affect the write offset within the buffer.
65    fn reserve(&mut self, size: u64) -> Result<(), ParsingError>;
66
67    /// Appends `bytes` to the buffer.
68    ///
69    /// The internal write offset is incremented by `bytes.len()`. Partial
70    /// writes must return an error.
71    fn append(&mut self, bytes: &[u8]) -> Result<(), ParsingError>;
72
73    /// Pads the buffer to `target_size`.
74    ///
75    /// Moves the buffer's write offset _forward_ to `target_size` filling with
76    /// zeroes.
77    /// Returns an error if the write offset is larger than `target_size`.
78    fn pad_to(&mut self, target_size: u64) -> Result<(), ParsingError>;
79}
80
81impl DataWriter for Vec<u8> {
82    fn reserve(&mut self, size: u64) -> Result<(), ParsingError> {
83        let size: usize = size
84            .try_into()
85            .map_err(|_| ParsingError::Incompatible("Target size out of bounds".to_string()))?;
86        self.reserve(size);
87        Ok(())
88    }
89
90    fn append(&mut self, bytes: &[u8]) -> Result<(), ParsingError> {
91        self.extend_from_slice(bytes);
92        Ok(())
93    }
94
95    fn pad_to(&mut self, target_size: u64) -> Result<(), ParsingError> {
96        let target_size: usize = target_size
97            .try_into()
98            .map_err(|_| ParsingError::Incompatible("Target size out of bounds".to_string()))?;
99        let len = self.len();
100        if len > target_size {
101            return Err(ParsingError::NotSupported(format!(
102                "invalid target size {target_size} for {len}"
103            )));
104        }
105        self.resize(target_size, 0);
106        Ok(())
107    }
108}
109
110#[cfg(target_os = "fuchsia")]
111pub struct VmoWriter {
112    vmo: zx::Vmo,
113    stream: zx::Stream,
114}
115
116#[cfg(target_os = "fuchsia")]
117impl VmoWriter {
118    /// Creates a new `VmoWriter` backed by a VMO of `size` bytes.
119    pub fn new(size: u64) -> Result<Self, zx::Status> {
120        let vmo = zx::Vmo::create(size)?;
121        vmo.set_stream_size(0)?;
122        let stream = zx::Stream::create(
123            zx::StreamOptions::MODE_APPEND | zx::StreamOptions::MODE_WRITE,
124            &vmo,
125            0,
126        )?;
127        Ok(Self { vmo, stream })
128    }
129
130    /// Consumes the writer, returning the underlying VMO.
131    ///
132    /// The inner VMO is guaranteed to be the size given to [`VmoWriter::new`],
133    /// but its stream size is set to the amount of bytes written to it via the
134    /// [`DataWriter`] implementation.
135    pub fn into_vmo(self) -> zx::Vmo {
136        self.vmo
137    }
138}
139
140#[cfg(target_os = "fuchsia")]
141impl DataWriter for VmoWriter {
142    fn reserve(&mut self, size: u64) -> Result<(), ParsingError> {
143        if size > self.vmo.get_size().map_err(ParsingError::VmoError)? {
144            return Err(ParsingError::VmoError(zx::Status::NOT_SUPPORTED));
145        }
146        Ok(())
147    }
148
149    fn append(&mut self, bytes: &[u8]) -> Result<(), ParsingError> {
150        let written = self
151            .stream
152            .write(zx::StreamWriteOptions::empty(), bytes)
153            .map_err(ParsingError::VmoError)?;
154        if written != bytes.len() {
155            return Err(ParsingError::VmoError(zx::Status::IO_REFUSED));
156        }
157        Ok(())
158    }
159
160    fn pad_to(&mut self, target_size: u64) -> Result<(), ParsingError> {
161        let cur = self.vmo.get_stream_size().map_err(ParsingError::VmoError)?;
162        if cur > target_size {
163            return Err(ParsingError::NotSupported(format!(
164                "invalid target size {target_size} for {cur}"
165            )));
166        }
167        // Note: Given we created the VMO ourselves and we're ensuring we can
168        // only seek forward, we're guaranteed this is filled with 0s.
169        self.vmo.set_stream_size(target_size).map_err(ParsingError::VmoError)?;
170        Ok(())
171    }
172}
173
174/// Abstracts over block group descriptors of different size
175enum BlockGroupDescriptor {
176    BGD32(BlockGroupDesc32),
177    BGD64(BlockGroupDesc64),
178}
179
180impl BlockGroupDescriptor {
181    fn inode_table_block(&self) -> u64 {
182        match self {
183            Self::BGD32(bgd) => u64::from(bgd.ext2bgd_i_tables),
184            Self::BGD64(bgd) => {
185                u64::from(bgd.base.ext2bgd_i_tables) + (u64::from(bgd.ext4bgd_i_tables_hi) << 32)
186            }
187        }
188    }
189}
190
191/// EXT4 Parser
192///
193/// Takes in a `Reader` that is able to read arbitrary chunks of data from the filesystem image.
194///
195/// Basic use:
196/// let mut parser = Parser::new(VecReader::new(vec_of_u8));
197/// let tree = parser.build_fuchsia_tree()
198impl Parser {
199    pub fn new(reader: Box<dyn Reader>) -> Self {
200        Parser { reader, super_block: OnceCell::new() }
201    }
202
203    /// Returns the Super Block.
204    ///
205    /// If the super block has been parsed and saved before, return that.
206    /// Else, parse the super block and save it and return it.
207    ///
208    /// We never need to re-parse the super block in this read-only
209    /// implementation.
210    fn super_block(&self) -> Result<&SuperBlock, ParsingError> {
211        self.super_block.get_or_try_init(|| SuperBlock::parse(&self.reader))
212    }
213
214    /// Reads block size from the Super Block.
215    pub fn block_size(&self) -> Result<u64, ParsingError> {
216        self.super_block()?.block_size()
217    }
218
219    /// Reads full raw data from a given block number.
220    pub(crate) fn block(&self, block_number: u64) -> Result<Box<[u8]>, ParsingError> {
221        if block_number == 0 {
222            return Err(ParsingError::InvalidAddress(
223                InvalidAddressErrorType::Lower,
224                0,
225                FIRST_BG_PADDING,
226            ));
227        }
228        let block_size = self.block_size()?;
229        let address = block_number
230            .checked_mul(block_size)
231            .ok_or(ParsingError::BlockNumberOutOfBounds(block_number))?;
232
233        let mut data = vec![0u8; block_size.try_into().unwrap()];
234        self.reader.read(address, data.as_mut_slice()).map_err(Into::<ParsingError>::into)?;
235
236        Ok(data.into_boxed_slice())
237    }
238
239    /// Returns the address of the given `inode_number` within `self.reader`.
240    pub(crate) fn inode_addr(&self, inode_number: u32) -> Result<u64, ParsingError> {
241        if inode_number < 1 {
242            // INode number 0 is not allowed per ext4 spec.
243            return Err(ParsingError::InvalidInode(inode_number));
244        }
245        let sb = self.super_block()?;
246        let block_size = self.block_size()?;
247
248        // The first Block Group starts with:
249        // - 1024 byte padding
250        // - 1024 byte Super Block
251        // Then in the next block, there are many blocks worth of Block Group Descriptors.
252        // If the block size is 2048 bytes or larger, then the 1024 byte padding, and the
253        // Super Block both fit in the first block (0), and the Block Group Descriptors start
254        // at block 1.
255        //
256        // A 1024 byte block size means the padding takes block 0 and the Super Block takes
257        // block 1. This means the Block Group Descriptors start in block 2.
258        let bgd_table_offset = if block_size >= MIN_EXT4_SIZE {
259            // Padding and Super Block both fit in the first block, so offset to the next
260            // block.
261            block_size
262        } else {
263            // Block size is less than 2048. The only valid block size smaller than 2048 is 1024.
264            // Padding and Super Block take one block each, so offset to the third block.
265            block_size * 2
266        };
267
268        let bgd_offset = (inode_number - 1) as u64 / sb.e2fs_ipg.get() as u64
269            * sb.block_group_descriptor_size() as u64;
270        let bgd = if sb.is_64bit() {
271            BlockGroupDescriptor::BGD64(BlockGroupDesc64::from_reader_with_offset(
272                &self.reader,
273                bgd_table_offset + bgd_offset,
274            )?)
275        } else {
276            BlockGroupDescriptor::BGD32(BlockGroupDesc32::from_reader_with_offset(
277                &self.reader,
278                bgd_table_offset + bgd_offset,
279            )?)
280        };
281
282        // Offset could really be anywhere, and the Reader will enforce reading within the
283        // filesystem size. Not much can be checked here.
284        let inode_table_offset =
285            (inode_number - 1) as u64 % sb.e2fs_ipg.get() as u64 * sb.e2fs_inode_size.get() as u64;
286        let inode_addr = (bgd.inode_table_block() * block_size) + inode_table_offset;
287        if inode_addr < MIN_EXT4_SIZE {
288            return Err(ParsingError::InvalidAddress(
289                InvalidAddressErrorType::Lower,
290                inode_addr,
291                MIN_EXT4_SIZE,
292            ));
293        }
294        Ok(inode_addr)
295    }
296
297    /// Reads the INode at the given inode number.
298    pub fn inode(&self, inode_number: u32) -> Result<INode, ParsingError> {
299        INode::from_reader_with_offset(&self.reader, self.inode_addr(inode_number)?)
300    }
301
302    /// Helper function to get the root directory INode.
303    pub fn root_inode(&self) -> Result<INode, ParsingError> {
304        self.inode(ROOT_INODE_NUM)
305    }
306
307    /// Reads extent data from a leaf node into `writer`.
308    fn extent_data<W: DataWriter>(
309        &self,
310        extent: &Extent,
311        writer: &mut W,
312        mut allowance: u64,
313    ) -> Result<u64, ParsingError> {
314        let block_number = extent.target_block_num();
315        let block_count = extent.e_len.get() as u64;
316        let block_size = self.block_size()?;
317        let mut read_len;
318
319        writer.reserve(block_size * block_count)?;
320        let mut total_written = 0u64;
321
322        for i in 0..block_count {
323            let block_data = self.block(block_number + i as u64)?;
324            if allowance >= block_size {
325                read_len = block_size;
326            } else {
327                read_len = allowance;
328            }
329            let slice = &block_data[0..read_len.try_into().unwrap()];
330            writer.append(slice)?;
331            total_written += read_len;
332            allowance -= read_len;
333        }
334
335        Ok(total_written)
336    }
337
338    /// Reads the inode size and raw extent data for a regular file.  Fails if the provided inode is
339    /// not a regular file.
340    pub fn read_extents(&self, inode_num: u32) -> Result<(u64, Vec<Extent>), ParsingError> {
341        let inode = self.inode(inode_num)?;
342
343        // Make sure this is a regular file.
344        const IFMT: u16 = 0xf000;
345        const IFREG: u16 = 0x8000;
346        if u16::from(inode.e2di_mode) & IFMT != IFREG {
347            return Err(ParsingError::NotFile);
348        }
349
350        let root_extent_tree_node = inode.extent_tree_node()?;
351        let mut extents = Vec::new();
352
353        self.iterate_extents_in_tree(&root_extent_tree_node, &mut |extent| {
354            extents.push(extent.clone());
355            Ok(())
356        })?;
357
358        Ok((inode.size(), extents))
359    }
360
361    /// Reads extent data from a leaf node into `writer`.
362    ///
363    /// # Arguments
364    /// * `extent`: Extent from which to read data from.
365    /// * `writer`: `DataWriter` where data that is read is added.
366    /// * `allowance`: The maximum number of bytes to read from the extent. The
367    ///    given file allowance is updated on each call to track sizing for an
368    ///    entire extent tree.
369    fn read_extent_data<W: DataWriter>(
370        &self,
371        extent: &Extent,
372        writer: &mut W,
373        allowance: &mut u64,
374    ) -> Result<(), ParsingError> {
375        let extent_len = self.extent_data(extent, writer, *allowance)?;
376        if extent_len > *allowance {
377            return Err(ParsingError::ExtentUnexpectedLength(extent_len, *allowance));
378        }
379        *allowance -= extent_len;
380        Ok(())
381    }
382
383    /// Reads directory entries from an extent leaf node.
384    fn read_dir_entries(
385        &self,
386        extent: &Extent,
387        entries: &mut Vec<DirEntry2>,
388    ) -> Result<(), ParsingError> {
389        let block_size = self.block_size()?;
390        let target_block_offset = extent.target_block_num() * block_size;
391
392        // The `e2d_reclen` of the last entry will be large enough fill the
393        // remaining space of the block.
394        for block_index in 0..extent.e_len.get() {
395            let mut dir_entry_offset = 0u64;
396            while (dir_entry_offset + size_of::<DirEntryHeader>() as u64) < block_size {
397                let offset =
398                    dir_entry_offset + target_block_offset + (block_index as u64 * block_size);
399
400                let de_header = DirEntryHeader::from_reader_with_offset(&self.reader, offset)?;
401                let mut de = DirEntry2 {
402                    e2d_ino: de_header.e2d_ino,
403                    e2d_reclen: de_header.e2d_reclen,
404                    e2d_namlen: de_header.e2d_namlen,
405                    e2d_type: de_header.e2d_type,
406                    e2d_name: [0u8; 255],
407                };
408                self.reader.read(
409                    offset + size_of::<DirEntryHeader>() as u64,
410                    &mut de.e2d_name[..de.e2d_namlen as usize],
411                )?;
412
413                dir_entry_offset += de.e2d_reclen.get() as u64;
414
415                if de.e2d_ino.get() != 0 {
416                    entries.push(de);
417                }
418            }
419        }
420        Ok(())
421    }
422
423    /// Handles an extent tree leaf node by invoking `extent_handler` for each contained extent.
424    fn iterate_extents_in_leaf<B: SplitByteSlice, F: FnMut(&Extent) -> Result<(), ParsingError>>(
425        &self,
426        extent_tree_node: &ExtentTreeNode<B>,
427        extent_handler: &mut F,
428    ) -> Result<(), ParsingError> {
429        for e_index in 0..extent_tree_node.header.eh_ecount.get() {
430            let start = size_of::<Extent>() * e_index as usize;
431            let end = start + size_of::<Extent>() as usize;
432            let e = Extent::to_struct_ref(
433                &(extent_tree_node.entries)[start..end],
434                ParsingError::InvalidExtent(start as u64),
435            )?;
436
437            extent_handler(e)?;
438        }
439
440        Ok(())
441    }
442
443    /// Handles traversal down an extent tree.
444    pub(crate) fn iterate_extents_in_tree<
445        B: SplitByteSlice,
446        F: FnMut(&Extent) -> Result<(), ParsingError>,
447    >(
448        &self,
449        extent_tree_node: &ExtentTreeNode<B>,
450        extent_handler: &mut F,
451    ) -> Result<(), ParsingError> {
452        let block_size = self.block_size()?;
453
454        match extent_tree_node.header.eh_depth.get() {
455            0 => {
456                self.iterate_extents_in_leaf(extent_tree_node, extent_handler)?;
457            }
458            1..=4 => {
459                for e_index in 0..extent_tree_node.header.eh_ecount.get() {
460                    let start: usize = size_of::<Extent>() * e_index as usize;
461                    let end = start + size_of::<Extent>();
462                    let e = ExtentIndex::to_struct_ref(
463                        &(extent_tree_node.entries)[start..end],
464                        ParsingError::InvalidExtent(start as u64),
465                    )?;
466
467                    let next_level_offset = e.target_block_num() as u64 * block_size;
468
469                    let next_extent_header =
470                        ExtentHeader::from_reader_with_offset(&self.reader, next_level_offset)?;
471
472                    let entry_count = next_extent_header.eh_ecount.get() as usize;
473                    let entry_size = match next_extent_header.eh_depth.get() {
474                        0 => size_of::<Extent>(),
475                        _ => size_of::<ExtentIndex>(),
476                    };
477                    let node_size = size_of::<ExtentHeader>() + (entry_count * entry_size);
478
479                    let mut data = vec![0u8; node_size];
480                    self.reader.read(next_level_offset, data.as_mut_slice())?;
481
482                    let next_level_node = ExtentTreeNode::parse(data.as_slice())
483                        .ok_or(ParsingError::InvalidExtent(next_level_offset))?;
484
485                    self.iterate_extents_in_tree(&next_level_node, extent_handler)?;
486                }
487            }
488            _ => return Err(ParsingError::InvalidExtentHeader),
489        };
490
491        Ok(())
492    }
493
494    /// Lists directory entries from the directory that is the given Inode.
495    ///
496    /// Errors if the Inode does not map to a Directory.
497    pub fn entries_from_inode(&self, inode: &INode) -> Result<Vec<DirEntry2>, ParsingError> {
498        let root_extent_tree_node = inode.extent_tree_node()?;
499        let mut dir_entries = Vec::new();
500
501        self.iterate_extents_in_tree(&root_extent_tree_node, &mut |extent| {
502            self.read_dir_entries(extent, &mut dir_entries)
503        })?;
504
505        Ok(dir_entries)
506    }
507
508    /// Gets any DirEntry2 that isn't root.
509    ///
510    /// Root doesn't have a DirEntry2.
511    ///
512    /// When dynamic loading of files is supported, this is the required mechanism.
513    pub fn entry_at_path(&self, path: &Path) -> Result<DirEntry2, ParsingError> {
514        let root_inode = self.root_inode()?;
515        let root_entries = self.entries_from_inode(&root_inode)?;
516        let mut entry_map = DirEntry2::as_hash_map(root_entries)?;
517
518        let mut components = path.components().peekable();
519        let mut component = components.next();
520
521        while component != None {
522            match component {
523                Some(Component::RootDir) => {
524                    // Skip
525                }
526                Some(Component::Normal(name)) => {
527                    let name = name.to_str().ok_or(ParsingError::InvalidInputPath)?;
528                    if let Some(entry) = entry_map.remove(name) {
529                        if components.peek() == None {
530                            return Ok(entry);
531                        }
532                        match EntryType::from_u8(entry.e2d_type)? {
533                            EntryType::Directory => {
534                                let inode = self.inode(entry.e2d_ino.get())?;
535                                entry_map =
536                                    DirEntry2::as_hash_map(self.entries_from_inode(&inode)?)?;
537                            }
538                            _ => {
539                                break;
540                            }
541                        }
542                    }
543                }
544                _ => {
545                    break;
546                }
547            }
548            component = components.next();
549        }
550
551        match path.to_str() {
552            Some(s) => Err(ParsingError::PathNotFound(s.to_string())),
553            None => Err(ParsingError::PathNotFound(
554                "Bad path - was not able to convert into string".to_string(),
555            )),
556        }
557    }
558
559    /// Reads all raw data for a given inode into the given writer.
560    ///
561    /// For a file, this will write the file data. For a symlink,
562    /// this will write the symlink target.
563    pub fn read_data_into<W: DataWriter>(
564        &self,
565        inode_num: u32,
566        writer: &mut W,
567    ) -> Result<(), ParsingError> {
568        let inode = self.inode(inode_num)?;
569        let mut size_remaining = inode.size();
570        writer.reserve(size_remaining.try_into().unwrap())?;
571
572        // Check for symlink with inline data.
573        if u16::from(inode.e2di_mode) & 0xa000 != 0 && u32::from(inode.e2di_nblock) == 0 {
574            let inline_data = &inode.e2di_blocks[..inode.size().try_into().unwrap()];
575            writer.append(inline_data)?;
576            return Ok(());
577        }
578
579        let root_extent_tree_node = inode.extent_tree_node()?;
580        let mut extents = Vec::new();
581
582        self.iterate_extents_in_tree(&root_extent_tree_node, &mut |extent| {
583            extents.push(extent.clone());
584            Ok(())
585        })?;
586
587        let block_size = self.block_size()?;
588        let mut current_offset = 0u64;
589
590        // Summarized from https://www.kernel.org/doc/ols/2007/ols2007v2-pages-21-34.pdf,
591        // Section 2.2: Extent and ExtentHeader entries must be sorted by logical block number. This
592        // enforces that when the extent tree is traversed depth first that a list of extents sorted
593        // by logical block number is produced. This is a requirement to produce the proper ordering
594        // of bytes within `data` here.
595        for extent in extents {
596            let buffer_offset = extent.e_blk.get() as u64 * block_size;
597
598            // File may be sparse. Sparse files will have gaps
599            // between logical blocks. Fill in any gaps with zeros.
600            if buffer_offset > current_offset {
601                let gap = buffer_offset - current_offset;
602                size_remaining -= gap;
603                writer.pad_to(buffer_offset)?;
604                current_offset = buffer_offset;
605            }
606
607            let size_before = size_remaining;
608            self.read_extent_data(&extent, writer, &mut size_remaining)?;
609            let written = size_before - size_remaining;
610            current_offset += written;
611        }
612
613        // If there are zero pages at the end of the file, they won't appear in the extents list.
614        // Pad the data with zeroes to the full file length.
615        // TODO(https://fxbug.dev/42073237): Add a test for this behavior, once better test infra exists.
616        writer.pad_to(inode.size())?;
617
618        Ok(())
619    }
620
621    /// Reads all raw data for a given inode into a vector.
622    pub fn read_data(&self, inode_num: u32) -> Result<Vec<u8>, ParsingError> {
623        let mut data = Vec::new();
624        self.read_data_into(inode_num, &mut data)?;
625        Ok(data)
626    }
627
628    /// Progress through the entire directory tree starting from the given INode.
629    ///
630    /// If given the root directory INode, this will process through every directory entry in the
631    /// filesystem in a DFS manner.
632    ///
633    /// Takes in a closure that will be called for each entry found.
634    /// Closure should return `Ok(true)` in order to continue the process, otherwise the process
635    /// will stop.
636    ///
637    /// Returns Ok(true) if it has indexed its subtree successfully. Otherwise, if the receiver
638    /// chooses to cancel indexing early, an Ok(false) is returned and propagated up.
639    pub fn index<R, E>(&self, inode: INode, prefix: Vec<&str>, receiver: &mut R) -> Result<bool, E>
640    where
641        E: From<ParsingError>,
642        R: FnMut(&Parser, Vec<&str>, &DirEntry2) -> Result<bool, E>,
643    {
644        let entries = self.entries_from_inode(&inode)?;
645        for entry in entries {
646            let entry_name = entry.name()?;
647            if entry_name == "." || entry_name == ".." {
648                continue;
649            }
650            let mut name = Vec::new();
651            name.append(&mut prefix.clone());
652            name.push(entry_name);
653            if !receiver(self, name.clone(), &entry)? {
654                return Ok(false);
655            }
656            if EntryType::from_u8(entry.e2d_type)? == EntryType::Directory {
657                let inode = self.inode(entry.e2d_ino.get())?;
658                if !self.index(inode, name, receiver)? {
659                    return Ok(false);
660                }
661            }
662        }
663
664        Ok(true)
665    }
666
667    /// Returns the xattrs associated with `inode_number`.
668    pub fn inode_xattrs(&self, inode_number: u32) -> Result<XattrMap, ParsingError> {
669        let mut xattrs = BTreeMap::new();
670
671        let inode_addr = self.inode_addr(inode_number).expect("Couldn't get inode address");
672        let inode =
673            INode::from_reader_with_offset(&self.reader, inode_addr).expect("Failed reader");
674
675        let sb = self.super_block().expect("No super block for inode");
676        let xattr_magic_addr = inode_addr
677            + MINIMUM_INODE_SIZE
678            + u64::from(inode.e4di_extra_isize(sb).unwrap_or_default());
679
680        let mut magic = LEU32::ZERO;
681        self.reader.read(xattr_magic_addr, magic.as_mut_bytes()).expect("Failed to read xattr");
682        if magic.get() == Self::XATTR_MAGIC {
683            let first_entry = xattr_magic_addr + size_of_val(&magic) as u64;
684            self.read_xattr_entries_from_inode(
685                first_entry,
686                inode_addr + (sb.e2fs_inode_size.get() as u64),
687                &mut xattrs,
688            )?;
689        }
690
691        let block_number: u64 = inode.facl();
692        if block_number > 0 {
693            let block = self.block(block_number).expect("Couldn't find block");
694            Self::read_xattr_entries_from_block(&block, &mut xattrs)?;
695        }
696
697        Ok(xattrs)
698    }
699
700    const XATTR_ALIGNMENT: u64 = 4;
701    const XATTR_MAGIC: u32 = 0xea020000;
702
703    fn round_up_to_align(x: u64, align: u64) -> u64 {
704        let spare = x % align;
705        if spare > 0 { x.checked_add(align - spare).expect("Overflow when aligning") } else { x }
706    }
707
708    fn is_valid_xattr_entry_header(header: &XattrEntryHeader) -> bool {
709        !(header.e_name_len == 0
710            && header.e_name_index == 0
711            && header.e_value_offs.get() == 0
712            && header.e_value_inum.get() == 0)
713    }
714
715    fn xattr_prefix_for_name_index(header: &XattrEntryHeader) -> Vec<u8> {
716        match header.e_name_index {
717            1 => b"user.".to_vec(),
718            2 => b"system.posix_acl_access.".to_vec(),
719            3 => b"system.posix_acl_default.".to_vec(),
720            4 => b"trusted.".to_vec(),
721            6 => b"security.".to_vec(),
722            7 => b"system.".to_vec(),
723            8 => b"system.richacl".to_vec(),
724            _ => b"".to_vec(),
725        }
726    }
727
728    /// Reads all the xattr entries, stored in the inode, from `entries_addr` into `xattrs`.
729    fn read_xattr_entries_from_inode(
730        &self,
731        mut entries_addr: u64,
732        inode_end: u64,
733        xattrs: &mut XattrMap,
734    ) -> Result<(), ParsingError> {
735        let value_base_addr = entries_addr;
736        while entries_addr + (std::mem::size_of::<XattrEntryHeader>() as u64) < inode_end {
737            let head = XattrEntryHeader::from_reader_with_offset(&self.reader, entries_addr)?;
738            if !Self::is_valid_xattr_entry_header(&head) {
739                break;
740            }
741
742            let prefix = Self::xattr_prefix_for_name_index(&head);
743            let mut name = Vec::with_capacity(prefix.len() + head.e_name_len as usize);
744            name.extend_from_slice(&prefix);
745            name.resize(prefix.len() + head.e_name_len as usize, 0);
746
747            self.reader.read(
748                entries_addr + size_of::<XattrEntryHeader>() as u64,
749                &mut name[prefix.len()..],
750            )?;
751
752            let mut value = vec![0u8; head.e_value_size.get() as usize];
753            self.reader.read(value_base_addr + u64::from(head.e_value_offs), &mut value)?;
754            xattrs.insert(name, value);
755
756            entries_addr += size_of::<XattrEntryHeader>() as u64 + head.e_name_len as u64;
757            entries_addr = Self::round_up_to_align(entries_addr, Self::XATTR_ALIGNMENT);
758        }
759        Ok(())
760    }
761
762    /// Reads all the xattr entries, stored in the inode, from `entries_addr` into `xattrs`.
763    fn read_xattr_entries_from_block(
764        block: &[u8],
765        xattrs: &mut XattrMap,
766    ) -> Result<(), ParsingError> {
767        let head = XattrHeader::to_struct_ref(
768            &block[..std::mem::size_of::<XattrHeader>()],
769            ParsingError::Incompatible("Invalid XattrHeader".to_string()),
770        )?;
771
772        if head.e_magic.get() != Self::XATTR_MAGIC {
773            return Ok(());
774        }
775
776        let mut offset = Self::round_up_to_align(
777            std::mem::size_of::<XattrHeader>() as u64,
778            Self::XATTR_ALIGNMENT * 2,
779        ) as usize;
780
781        while offset + std::mem::size_of::<XattrEntryHeader>() < block.len() {
782            let head = XattrEntryHeader::to_struct_ref(
783                &block[offset..offset + std::mem::size_of::<XattrEntryHeader>()],
784                ParsingError::Incompatible("Invalid XattrEntryHeader".to_string()),
785            )?;
786
787            if !Self::is_valid_xattr_entry_header(&head) {
788                break;
789            }
790
791            let name_start = offset + std::mem::size_of::<XattrEntryHeader>();
792            let name_end = name_start + head.e_name_len as usize;
793            let mut name = Self::xattr_prefix_for_name_index(&head);
794            name.extend_from_slice(&block[name_start..name_end]);
795
796            let value_start = head.e_value_offs.get() as usize;
797            let value_end = value_start + head.e_value_size.get() as usize;
798            let value = block[value_start..value_end].to_vec();
799            xattrs.insert(name, value);
800
801            offset = Self::round_up_to_align(name_end as u64, 4) as usize;
802        }
803
804        Ok(())
805    }
806}
807
808#[cfg(test)]
809mod tests {
810    use crate::parser::Parser;
811    #[cfg(target_os = "fuchsia")]
812    use crate::parser::{DataWriter, VmoWriter};
813    use crate::readers::VecReader;
814    use crate::structs::{EntryType, ParsingError};
815    use maplit::hashmap;
816    use sha2::{Digest, Sha256};
817    #[cfg(target_os = "fuchsia")]
818    use std::assert_matches;
819    use std::collections::{HashMap, HashSet};
820    use std::path::Path;
821    use std::{fs, str};
822    use test_case::test_case;
823
824    #[fuchsia::test]
825    fn list_root_1_file() {
826        let data = fs::read("/pkg/data/1file.img").expect("Unable to read file");
827        let parser = Parser::new(Box::new(VecReader::new(data)));
828        assert!(parser.super_block().expect("Super Block").check_magic().is_ok());
829        let root_inode = parser.root_inode().expect("Parse INode");
830        let entries = parser.entries_from_inode(&root_inode).expect("List entries");
831        let mut expected_entries = vec!["file1", "lost+found", "..", "."];
832
833        for de in &entries {
834            assert_eq!(expected_entries.pop().unwrap(), de.name().unwrap());
835        }
836        assert_eq!(expected_entries.len(), 0);
837    }
838
839    #[test_case(
840        "/pkg/data/nest.img",
841        vec!["inner", "file1", "lost+found", "..", "."];
842        "fs with a single directory")]
843    #[test_case(
844        "/pkg/data/extents.img",
845        vec!["trailingzeropages", "a", "smallfile", "largefile", "sparsefile", "lost+found", "..", "."];
846        "fs with multiple files with multiple extents")]
847    fn list_root(ext4_path: &str, mut expected_entries: Vec<&str>) {
848        let data = fs::read(ext4_path).expect("Unable to read file");
849        let parser = Parser::new(Box::new(VecReader::new(data)));
850        assert!(parser.super_block().expect("Super Block").check_magic().is_ok());
851        let root_inode = parser.root_inode().expect("Parse INode");
852        let entries = parser.entries_from_inode(&root_inode).expect("List entries");
853
854        for de in &entries {
855            assert_eq!(expected_entries.pop().unwrap(), de.name().unwrap());
856        }
857        assert_eq!(expected_entries.len(), 0);
858    }
859
860    #[fuchsia::test]
861    fn get_from_path() {
862        let data = fs::read("/pkg/data/nest.img").expect("Unable to read file");
863        let parser = Parser::new(Box::new(VecReader::new(data)));
864        assert!(parser.super_block().expect("Super Block").check_magic().is_ok());
865
866        let entry = parser.entry_at_path(Path::new("/inner")).expect("Entry at path");
867        assert_eq!(entry.e2d_ino.get(), 12);
868        assert_eq!(entry.name().unwrap(), "inner");
869
870        let entry = parser.entry_at_path(Path::new("/inner/file2")).expect("Entry at path");
871        assert_eq!(entry.e2d_ino.get(), 17);
872        assert_eq!(entry.name().unwrap(), "file2");
873    }
874
875    #[fuchsia::test]
876    fn read_data() {
877        let data = fs::read("/pkg/data/1file.img").expect("Unable to read file");
878        let parser = Parser::new(Box::new(VecReader::new(data)));
879        assert!(parser.super_block().expect("Super Block").check_magic().is_ok());
880
881        let entry = parser.entry_at_path(Path::new("file1")).expect("Entry at path");
882        assert_eq!(entry.e2d_ino.get(), 15);
883        assert_eq!(entry.name().unwrap(), "file1");
884
885        let data = parser.read_data(entry.e2d_ino.into()).expect("File data");
886        let compare = "file1 contents.\n";
887        assert_eq!(data.len(), compare.len());
888        assert_eq!(str::from_utf8(data.as_slice()).expect("File data"), compare);
889    }
890
891    #[fuchsia::test]
892    fn fail_inode_zero() {
893        let data = fs::read("/pkg/data/1file.img").expect("Unable to read file");
894        let parser = Parser::new(Box::new(VecReader::new(data)));
895        assert!(parser.inode(0).is_err());
896    }
897
898    #[fuchsia::test]
899    fn index() {
900        let data = fs::read("/pkg/data/nest.img").expect("Unable to read file");
901        let parser = Parser::new(Box::new(VecReader::new(data)));
902        assert!(parser.super_block().expect("Super Block").check_magic().is_ok());
903
904        let mut count = 0;
905        let mut entries: HashSet<u32> = HashSet::new();
906        let root_inode = parser.root_inode().expect("Root inode");
907
908        parser
909            .index(root_inode, Vec::new(), &mut |_, _, entry| {
910                count += 1;
911
912                // Make sure each inode only appears once.
913                assert_ne!(entries.contains(&entry.e2d_ino.get()), true);
914                entries.insert(entry.e2d_ino.get());
915
916                Ok::<bool, ParsingError>(true)
917            })
918            .expect("Index");
919
920        assert_eq!(count, 4);
921    }
922
923    #[fuchsia::test]
924    fn xattr() {
925        let data = fs::read("/pkg/data/xattr.img").expect("Unable to read file");
926        let parser = Parser::new(Box::new(VecReader::new(data)));
927        assert!(parser.super_block().expect("Super Block").check_magic().is_ok());
928        let root_inode = parser.root_inode().expect("Root inode");
929        let mut found_files = HashSet::new();
930
931        parser
932            .index(root_inode, Vec::new(), &mut |_, _, entry| {
933                let name = entry.e2d_name;
934                let inode = entry.e2d_ino.get();
935                let attributes = parser.inode_xattrs(inode).expect("Extended attributes");
936                match name {
937                    name if &name[0..10] == b"lost+found" => {
938                        assert_eq!(attributes.len(), 0);
939                        found_files.insert("lost+found");
940                    }
941                    name if &name[0..5] == b"file1" => {
942                        assert_eq!(attributes.len(), 1);
943                        assert_eq!(attributes[&b"user.test".to_vec()], b"test value".to_vec());
944                        found_files.insert("file1");
945                    }
946                    name if &name[0..9] == b"file_many" => {
947                        assert_eq!(attributes.len(), 6);
948                        assert_eq!(
949                            attributes[&b"user.long".to_vec()],
950                            b"vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv".to_vec()
951                        );
952                        found_files.insert("file_many");
953                    }
954                    name if &name[0..6] == b"subdir" => {
955                        assert_eq!(attributes.len(), 1);
956                        assert_eq!(attributes[&b"user.type".to_vec()], b"dir".to_vec());
957                        found_files.insert("subdir");
958                    }
959                    name if &name[0..5] == b"file2" => {
960                        assert_eq!(attributes.len(), 2);
961                        assert_eq!(
962                            attributes[&b"user.test_one".to_vec()],
963                            b"test value 1".to_vec()
964                        );
965                        assert_eq!(
966                            attributes[&b"user.test_two".to_vec()],
967                            b"test value 2".to_vec()
968                        );
969                        found_files.insert("file2");
970                    }
971                    _ => {}
972                }
973                Ok::<bool, ParsingError>(true)
974            })
975            .expect("Index");
976
977        assert_eq!(found_files.len(), 5);
978    }
979
980    #[test_case(
981        "/pkg/data/extents.img",
982        hashmap!{
983            "largefile".to_string() => "de2cf635ae4e0e727f1e412f978001d6a70d2386dc798d4327ec8c77a8e4895d".to_string(),
984            "smallfile".to_string() => "5891b5b522d5df086d0ff0b110fbd9d21bb4fc7163af34d08286a2e846f6be03".to_string(),
985            "sparsefile".to_string() => "3f411e42c1417cd8845d7144679812be3e120318d843c8c6e66d8b2c47a700e9".to_string(),
986            "trailingzeropages".to_string() => "afc5cc689fd3cb8d00c147d60dc911a70d36b7afb03cc7f15de9c78a52be978d".to_string(),
987            "a/multi/dir/path/within/this/crowded/extents/test/img/empty".to_string() => "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855".to_string(),
988        },
989        vec!["a/multi/dir/path/within/this/crowded/extents/test/img", "lost+found"];
990        "fs with multiple files with multiple extents")]
991    #[test_case(
992        "/pkg/data/1file.img",
993        hashmap!{
994            "file1".to_string() => "6bc35bfb2ca96c75a1fecde205693c19a827d4b04e90ace330048f3e031487dd".to_string(),
995        },
996        vec!["lost+found"];
997        "fs with one small file")]
998    #[test_case(
999        "/pkg/data/nest.img",
1000        hashmap!{
1001            "file1".to_string() => "6bc35bfb2ca96c75a1fecde205693c19a827d4b04e90ace330048f3e031487dd".to_string(),
1002            "inner/file2".to_string() => "215ca145cbac95c9e2a6f5ff91ca1887c837b18e5f58fd2a7a16e2e5a3901e10".to_string(),
1003        },
1004        vec!["inner", "lost+found"];
1005        "fs with a single directory")]
1006    #[test_case(
1007        "/pkg/data/nest64.img",
1008        hashmap!{
1009            "file1".to_string() => "6bc35bfb2ca96c75a1fecde205693c19a827d4b04e90ace330048f3e031487dd".to_string(),
1010            "inner/file2".to_string() => "215ca145cbac95c9e2a6f5ff91ca1887c837b18e5f58fd2a7a16e2e5a3901e10".to_string(),
1011        },
1012        vec!["inner", "lost+found"];
1013        "fs with 64bit enabled and a single directory")]
1014    #[test_case(
1015        "/pkg/data/longdir.img",
1016        {
1017            let mut hash = HashMap::new();
1018            for i in 1..=1000 {
1019                hash.insert(i.to_string(), "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855".to_string());
1020            }
1021            hash
1022        },
1023        vec!["lost+found"];
1024        "fs with many entries in a directory")]
1025    fn check_data(
1026        ext4_path: &str,
1027        mut file_hashes: HashMap<String, String>,
1028        expected_dirs: Vec<&str>,
1029    ) {
1030        let data = fs::read(ext4_path).expect("Unable to read file");
1031        let parser = Parser::new(Box::new(VecReader::new(data)));
1032        assert!(parser.super_block().expect("Super Block").check_magic().is_ok());
1033
1034        let root_inode = parser.root_inode().expect("Root inode");
1035
1036        parser
1037            .index(root_inode, Vec::new(), &mut |my_self, path, entry| {
1038                let entry_type = EntryType::from_u8(entry.e2d_type).expect("Entry Type");
1039                let file_path = path.join("/");
1040
1041                match entry_type {
1042                    EntryType::RegularFile => {
1043                        let data = my_self.read_data(entry.e2d_ino.into()).expect("File data");
1044
1045                        let mut hasher = Sha256::new();
1046                        hasher.update(&data);
1047                        assert_eq!(
1048                            file_hashes.remove(&file_path).unwrap(),
1049                            hex::encode(hasher.finalize())
1050                        );
1051                    }
1052                    EntryType::Directory => {
1053                        let mut found = false;
1054
1055                        // These should be the only possible directories.
1056                        for expected_dir in expected_dirs.iter() {
1057                            if expected_dir.starts_with(&file_path) {
1058                                found = true;
1059                                break;
1060                            }
1061                        }
1062                        assert!(found, "Unexpected path {}", file_path);
1063                    }
1064                    _ => {
1065                        assert!(false, "No other types should exist in this image.");
1066                    }
1067                }
1068                Ok::<bool, ParsingError>(true)
1069            })
1070            .expect("Index");
1071        assert!(file_hashes.is_empty(), "Expected files were not found {:?}", file_hashes);
1072    }
1073
1074    #[cfg(target_os = "fuchsia")]
1075    #[test_case(true; "pad")]
1076    #[test_case(true; "no_pad")]
1077    #[fuchsia::test]
1078    fn vmo_writer_size_and_stream_size(pad: bool) {
1079        let size = zx::system_get_page_size() as u64;
1080        let mut writer = VmoWriter::new(size).expect("failed to create VmoWriter");
1081        writer.reserve(100).expect("reserve failed");
1082        let contents = b"hello world";
1083        writer.append(contents).expect("append failed");
1084        let expect_stream_size = if pad {
1085            let pad = 500;
1086            writer.pad_to(pad).expect("pad_to failed");
1087            pad
1088        } else {
1089            contents.len() as u64
1090        };
1091
1092        let vmo = writer.into_vmo();
1093        assert_eq!(vmo.get_size().expect("get_size failed"), size);
1094        assert_eq!(vmo.get_stream_size().expect("get_stream_size failed"), expect_stream_size);
1095    }
1096
1097    #[cfg(target_os = "fuchsia")]
1098    #[fuchsia::test]
1099    fn vmo_writer_write_or_reserve_past_size_fails() {
1100        let size = zx::system_get_page_size() as u64;
1101
1102        let mut writer = VmoWriter::new(size).expect("failed to create VmoWriter");
1103        assert_eq!(
1104            writer.reserve(size + 1),
1105            Err(ParsingError::VmoError(zx::Status::NOT_SUPPORTED))
1106        );
1107
1108        let mut writer = VmoWriter::new(size).expect("failed to create VmoWriter");
1109        let large_buffer = vec![0u8; (size + 1) as usize];
1110        assert_eq!(
1111            writer.append(&large_buffer),
1112            Err(ParsingError::VmoError(zx::Status::IO_REFUSED))
1113        );
1114
1115        let mut writer = VmoWriter::new(size).expect("failed to create VmoWriter");
1116        assert_eq!(writer.pad_to(size + 1), Err(ParsingError::VmoError(zx::Status::OUT_OF_RANGE)));
1117
1118        let mut writer = VmoWriter::new(size).expect("failed to create VmoWriter");
1119        writer.append(&vec![0u8; size as usize]).expect("append failed");
1120        assert_eq!(writer.append(b"a"), Err(ParsingError::VmoError(zx::Status::OUT_OF_RANGE)));
1121    }
1122
1123    #[cfg(target_os = "fuchsia")]
1124    #[fuchsia::test]
1125    fn vmo_writer_pad_to_lower_than_offset_fails() {
1126        let size = zx::system_get_page_size() as u64;
1127        let mut writer = VmoWriter::new(size).expect("failed to create VmoWriter");
1128        writer.append(b"hello world").expect("append failed");
1129        assert_matches!(writer.pad_to(5), Err(ParsingError::NotSupported(_)));
1130    }
1131
1132    #[cfg(target_os = "fuchsia")]
1133    #[fuchsia::test]
1134    fn vmo_writer_pad_to_sets_stream_size_and_zeroes() {
1135        let size = zx::system_get_page_size() as u64;
1136        let mut writer = VmoWriter::new(size).expect("failed to create VmoWriter");
1137        let contents = b"hello";
1138        writer.append(contents).expect("append failed");
1139        let pad: usize = 100;
1140        writer.pad_to(pad as u64).expect("pad_to failed");
1141
1142        let vmo = writer.into_vmo();
1143        assert_eq!(vmo.get_stream_size().expect("get_stream_size failed"), pad as u64);
1144
1145        let mut buf = vec![0u8; pad];
1146        vmo.read(&mut buf, 0).expect("vmo read failed");
1147        assert_eq!(&buf[0..contents.len()], b"hello");
1148        assert_eq!(&buf[contents.len()..pad], &vec![0u8; pad - contents.len()]);
1149    }
1150
1151    #[cfg(target_os = "fuchsia")]
1152    #[fuchsia::test]
1153    fn vmo_writer_with_no_writes_has_zero_stream_size() {
1154        let size = zx::system_get_page_size() as u64;
1155        let writer = VmoWriter::new(size).expect("failed to create VmoWriter");
1156        let vmo = writer.into_vmo();
1157        assert_eq!(vmo.get_size().expect("get_size"), size);
1158        assert_eq!(vmo.get_stream_size().expect("get_stream_size"), 0);
1159    }
1160
1161    #[cfg(target_os = "fuchsia")]
1162    #[test_case(true; "pad")]
1163    #[test_case(true; "no_pad")]
1164    #[fuchsia::test]
1165    fn vmo_writer_multiple_appends(pad: bool) {
1166        let size = zx::system_get_page_size() as u64;
1167        let mut writer = VmoWriter::new(size).expect("failed to create VmoWriter");
1168        let mut expect = vec![];
1169        for i in 0..5 {
1170            let b = format!("hello payload {i}");
1171            expect.extend_from_slice(b.as_bytes());
1172            writer.append(b.as_bytes()).expect("append");
1173            if pad {
1174                let pad = [0u8; 10];
1175                expect.extend_from_slice(&pad);
1176                writer.pad_to(expect.len() as u64).expect("pad");
1177            }
1178        }
1179
1180        let vmo = writer.into_vmo();
1181        let mut buf = vec![0u8; expect.len()];
1182        assert_eq!(vmo.get_stream_size().expect("get_stream_size"), expect.len() as u64);
1183        vmo.read(&mut buf, 0).expect("read");
1184        assert_eq!(buf, expect);
1185    }
1186}