mar/
lib.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 http://mozilla.org/MPL/2.0/. */
4
5//! This is a Rust implementation of the [Mozilla Archive (MAR) file format][1]
6//! used to deliver automatic updates to Firefox.  It includes both a library and
7//! a command-line tool for reading and writing MAR files.
8//!
9//! This code is subject to the terms of the Mozilla Public License, v. 2.0.
10//!
11//! [1]: https://wiki.mozilla.org/Software_Update:MAR
12
13#![warn(missing_docs)]
14
15use std::{
16    fs::File,
17    io::{self, BufReader, Cursor, ErrorKind, Read, Seek, SeekFrom},
18    path::Path,
19};
20
21use byteorder::{BigEndian, ReadBytesExt};
22use compression::CompressedRead;
23use read::{get_info, read_next_item};
24
25pub mod compression;
26pub mod extract;
27pub mod read;
28
29/// Metadata about an entire MAR file.
30pub struct MarFileInfo {
31    offset_to_index: u32,
32    #[allow(dead_code)]
33    has_signature_block: bool,
34    #[allow(dead_code)]
35    num_signatures: u32,
36    #[allow(dead_code)]
37    has_additional_blocks: bool,
38    #[allow(dead_code)]
39    offset_additional_blocks: u32,
40    #[allow(dead_code)]
41    num_additional_blocks: u32,
42}
43
44/// An entry in the MAR index.
45pub struct MarItem {
46    /// Position of the item within the archive file.
47    offset: u32,
48    /// Length of data in bytes.
49    pub length: u32,
50    /// File mode bits.
51    pub flags: u32,
52    /// File path.
53    pub name: String,
54}
55
56/// A high level interface to read the contents of a mar file.
57pub struct Mar<R> {
58    info: MarFileInfo,
59    buffer: R,
60}
61
62impl<R> Mar<R>
63where
64    R: Read + Seek,
65{
66    /// Creates a Mar instance from any seekable readable.
67    pub fn from_buffer(mut buffer: R) -> io::Result<Mar<R>> {
68        let info = get_info(&mut buffer)?;
69
70        Ok(Mar { info, buffer })
71    }
72}
73
74impl Mar<BufReader<File>> {
75    /// Creates a Mar instance from a local file path.
76    pub fn from_path<P: AsRef<Path>>(path: P) -> io::Result<Mar<BufReader<File>>> {
77        let buffer = BufReader::new(File::open(path)?);
78        Self::from_buffer(buffer)
79    }
80}
81
82impl<R> Mar<R>
83where
84    R: Read + Seek,
85{
86    /// Reads the contents of a file from this mar.
87    pub fn read<'a>(&'a mut self, item: &MarItem) -> io::Result<CompressedRead<'a, R>> {
88        self.buffer.seek(SeekFrom::Start(item.offset as u64))?;
89        CompressedRead::new(&mut self.buffer, item.length as u64)
90    }
91
92    /// Returns an Iterator to the list of files in this mar.
93    pub fn files(&mut self) -> io::Result<Files> {
94        self.buffer
95            .seek(SeekFrom::Start(self.info.offset_to_index as u64))?;
96
97        // Read the index into memory.
98        let size_of_index = self.buffer.read_u32::<BigEndian>()?;
99        let mut index = vec![0; size_of_index as usize];
100        self.buffer.read_exact(&mut index)?;
101
102        Ok(Files {
103            index: Cursor::new(index),
104        })
105    }
106}
107
108/// An iterator over the files in a mar.
109pub struct Files {
110    index: Cursor<Vec<u8>>,
111}
112
113impl Iterator for Files {
114    type Item = io::Result<MarItem>;
115
116    fn next(&mut self) -> Option<Self::Item> {
117        match read_next_item(&mut self.index) {
118            Ok(item) => Some(Ok(item)),
119            Err(e) => {
120                if e.kind() == ErrorKind::UnexpectedEof {
121                    None
122                } else {
123                    Some(Err(e))
124                }
125            }
126        }
127    }
128}