apple_flat_package/
component_package.rs

1// This Source Code Form is subject to the terms of the Mozilla Public
2// License, v. 2.0. If a copy of the MPL was not distributed with this
3// file, You can obtain one at https://mozilla.org/MPL/2.0/.
4
5//! Interface to component packages, installable units within flat packages.
6
7use {
8    crate::{package_info::PackageInfo, PkgResult},
9    cpio_archive::ChainedCpioReader,
10    std::io::{Cursor, Read},
11};
12
13const GZIP_HEADER: [u8; 3] = [0x1f, 0x8b, 0x08];
14
15/// Attempt to decode the compressed content of an archive file.
16///
17/// The content can be compressed with various formats. This attempts to
18/// sniff them and apply an appropriate decompressor.
19fn decode_archive(data: Vec<u8>) -> PkgResult<Box<dyn Read>> {
20    if data.len() > 3 && data[0..3] == GZIP_HEADER {
21        Ok(Box::new(flate2::read::GzDecoder::new(Cursor::new(data))) as Box<dyn Read>)
22    } else {
23        Ok(Box::new(Cursor::new(data)) as Box<dyn Read>)
24    }
25}
26
27/// Type alias representing a generic reader for a cpio archive.
28pub type CpioReader = Box<ChainedCpioReader<Box<dyn Read>>>;
29
30fn cpio_reader(data: &[u8]) -> PkgResult<CpioReader> {
31    let decoder = decode_archive(data.to_vec())?;
32    Ok(cpio_archive::reader(decoder)?)
33}
34
35/// Read-only interface for a single *component package*.
36pub struct ComponentPackageReader {
37    bom: Option<Vec<u8>>,
38    package_info: Option<PackageInfo>,
39    payload: Option<Vec<u8>>,
40    scripts: Option<Vec<u8>>,
41}
42
43impl ComponentPackageReader {
44    /// Construct an instance with raw file data backing different files.
45    pub fn from_file_data(
46        bom: Option<Vec<u8>>,
47        package_info: Option<Vec<u8>>,
48        payload: Option<Vec<u8>>,
49        scripts: Option<Vec<u8>>,
50    ) -> PkgResult<Self> {
51        let package_info = if let Some(data) = package_info {
52            Some(PackageInfo::from_reader(Cursor::new(data))?)
53        } else {
54            None
55        };
56
57        Ok(Self {
58            bom,
59            package_info,
60            payload,
61            scripts,
62        })
63    }
64
65    /// Obtained the contents of the `Bom` file.
66    pub fn bom(&self) -> Option<&[u8]> {
67        self.bom.as_ref().map(|x| x.as_ref())
68    }
69
70    /// Obtain the parsed `PackageInfo` XML file.
71    pub fn package_info(&self) -> Option<&PackageInfo> {
72        self.package_info.as_ref()
73    }
74
75    /// Obtain a reader for the `Payload` cpio archive.
76    pub fn payload_reader(&self) -> PkgResult<Option<CpioReader>> {
77        if let Some(payload) = &self.payload {
78            Ok(Some(cpio_reader(payload)?))
79        } else {
80            Ok(None)
81        }
82    }
83
84    /// Obtain a reader for the `Scripts` cpio archive.
85    pub fn scripts_reader(&self) -> PkgResult<Option<CpioReader>> {
86        if let Some(data) = &self.scripts {
87            Ok(Some(cpio_reader(data)?))
88        } else {
89            Ok(None)
90        }
91    }
92}